ACPI / dock: Use callback pointers from devices' ACPI hotplug contexts
Instead of requiring a set of special dock operations to be registered
via register_hotplug_dock_device() for each ACPI dock device, it is
much more straightforward to use callback pointers from the devices'
hotplug contexts if available.
For this reason, modify dock_hotplug_event() to use callback pointers
from the hotplug contexts of ACPI devices and fall back to using the
special dock operarions only if those callbacks are missing. Also
make the ACPI-based PCI hotplug (ACPIPHP) subsystem set the .fixup
callback pointer in the hotplug contexts of devices handled by it to
a new function, acpiphp_post_dock_fixup(), so that the dock station
driver can use the callbacks from those contexts instead of special
dock operations registered via register_hotplug_dock_device().
Along with the above changes drop the ACPIPHP's dock operations that
are not necessary any more.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c
index 8c3967c..78c4ee7 100644
--- a/drivers/acpi/dock.c
+++ b/drivers/acpi/dock.c
@@ -185,9 +185,38 @@
static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event,
enum dock_callback_type cb_type)
{
+ struct acpi_device *adev = dd->adev;
acpi_notify_handler cb = NULL;
bool run = false;
+ acpi_lock_hp_context();
+
+ if (!adev->hp)
+ goto no_context;
+
+ if (cb_type == DOCK_CALL_FIXUP) {
+ void (*fixup)(struct acpi_device *);
+
+ fixup = adev->hp->fixup;
+ if (fixup) {
+ acpi_unlock_hp_context();
+ fixup(adev);
+ return;
+ }
+ } else {
+ int (*notify)(struct acpi_device *, u32);
+
+ notify = adev->hp->event;
+ if (notify) {
+ acpi_unlock_hp_context();
+ notify(adev, event);
+ return;
+ }
+ }
+
+ no_context:
+ acpi_unlock_hp_context();
+
mutex_lock(&hotplug_lock);
if (dd->hp_context) {
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index cd88672..15865ae 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -60,6 +60,7 @@
static DEFINE_MUTEX(bridge_mutex);
static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type);
+static void acpiphp_post_dock_fixup(struct acpi_device *adev);
static void acpiphp_sanitize_bus(struct pci_bus *bus);
static void acpiphp_set_hpp_values(struct pci_bus *bus);
static void hotplug_event(u32 type, struct acpiphp_context *context);
@@ -80,7 +81,8 @@
return NULL;
context->refcount = 1;
- acpi_set_hp_context(adev, &context->hp, acpiphp_hotplug_event, NULL);
+ acpi_set_hp_context(adev, &context->hp, acpiphp_hotplug_event,
+ acpiphp_post_dock_fixup);
return context;
}
@@ -130,6 +132,27 @@
kref_put(&bridge->ref, free_bridge);
}
+static struct acpiphp_context *acpiphp_grab_context(struct acpi_device *adev)
+{
+ struct acpiphp_context *context;
+
+ acpi_lock_hp_context();
+ context = acpiphp_get_context(adev);
+ if (!context || context->func.parent->is_going_away) {
+ acpi_unlock_hp_context();
+ return NULL;
+ }
+ get_bridge(context->func.parent);
+ acpiphp_put_context(context);
+ acpi_unlock_hp_context();
+ return context;
+}
+
+static void acpiphp_let_context_go(struct acpiphp_context *context)
+{
+ put_bridge(context->func.parent);
+}
+
static void free_bridge(struct kref *kref)
{
struct acpiphp_context *context;
@@ -164,28 +187,29 @@
acpi_unlock_hp_context();
}
-/*
- * the _DCK method can do funny things... and sometimes not
- * hah-hah funny.
+/**
+ * acpiphp_post_dock_fixup - Post-dock fixups for PCI devices.
+ * @adev: ACPI device object corresponding to a PCI device.
*
- * TBD - figure out a way to only call fixups for
- * systems that require them.
+ * TBD - figure out a way to only call fixups for systems that require them.
*/
-static void post_dock_fixups(acpi_handle not_used, u32 event, void *data)
+static void acpiphp_post_dock_fixup(struct acpi_device *adev)
{
- struct acpiphp_context *context = data;
- struct pci_bus *bus = context->func.slot->bus;
+ struct acpiphp_context *context = acpiphp_grab_context(adev);
+ struct pci_bus *bus;
u32 buses;
- if (!bus->self)
+ if (!context)
return;
+ bus = context->func.slot->bus;
+ if (!bus->self)
+ goto out;
+
/* fixup bad _DCK function that rewrites
* secondary bridge on slot
*/
- pci_read_config_dword(bus->self,
- PCI_PRIMARY_BUS,
- &buses);
+ pci_read_config_dword(bus->self, PCI_PRIMARY_BUS, &buses);
if (((buses >> 8) & 0xff) != bus->busn_res.start) {
buses = (buses & 0xff000000)
@@ -194,24 +218,11 @@
| ((unsigned int)(bus->busn_res.end) << 16);
pci_write_config_dword(bus->self, PCI_PRIMARY_BUS, buses);
}
+
+ out:
+ acpiphp_let_context_go(context);
}
-static void dock_event(acpi_handle handle, u32 type, void *data)
-{
- struct acpi_device *adev;
-
- adev = acpi_bus_get_acpi_device(handle);
- if (adev) {
- acpiphp_hotplug_event(adev, type);
- acpi_bus_put_acpi_device(adev);
- }
-}
-
-static const struct acpi_dock_ops acpiphp_dock_ops = {
- .fixup = post_dock_fixups,
- .handler = dock_event,
-};
-
/* Check whether the PCI device is managed by native PCIe hotplug driver */
static bool device_is_managed_by_native_pciehp(struct pci_dev *pdev)
{
@@ -241,20 +252,6 @@
return true;
}
-static void acpiphp_dock_init(void *data)
-{
- struct acpiphp_context *context = data;
-
- get_bridge(context->func.parent);
-}
-
-static void acpiphp_dock_release(void *data)
-{
- struct acpiphp_context *context = data;
-
- put_bridge(context->func.parent);
-}
-
/**
* acpiphp_add_context - Add ACPIPHP context to an ACPI device object.
* @handle: ACPI handle of the object to add a context to.
@@ -300,15 +297,18 @@
newfunc = &context->func;
newfunc->function = function;
newfunc->parent = bridge;
+ acpi_unlock_hp_context();
- if (acpi_has_method(handle, "_EJ0"))
+ /*
+ * If this is a dock device, its _EJ0 should be executed by the dock
+ * notify handler after calling _DCK.
+ */
+ if (!is_dock_device(adev) && acpi_has_method(handle, "_EJ0"))
newfunc->flags = FUNC_HAS_EJ0;
if (acpi_has_method(handle, "_STA"))
newfunc->flags |= FUNC_HAS_STA;
- acpi_unlock_hp_context();
-
/* search for objects that share the same slot */
list_for_each_entry(slot, &bridge->slots, node)
if (slot->device == device)
@@ -369,18 +369,6 @@
&val, 60*1000))
slot->flags |= SLOT_ENABLED;
- if (is_dock_device(adev)) {
- /* we don't want to call this device's _EJ0
- * because we want the dock notify handler
- * to call it after it calls _DCK
- */
- newfunc->flags &= ~FUNC_HAS_EJ0;
- if (register_hotplug_dock_device(handle,
- &acpiphp_dock_ops, context,
- acpiphp_dock_init, acpiphp_dock_release))
- pr_debug("failed to register dock device\n");
- }
-
return AE_OK;
}
@@ -411,11 +399,9 @@
list_for_each_entry(func, &slot->funcs, sibling) {
struct acpi_device *adev = func_to_acpi_device(func);
- if (is_dock_device(adev))
- unregister_hotplug_dock_device(adev->handle);
-
acpi_lock_hp_context();
adev->hp->event = NULL;
+ adev->hp->fixup = NULL;
acpi_unlock_hp_context();
}
slot->flags |= SLOT_IS_GOING_AWAY;
@@ -851,19 +837,12 @@
{
struct acpiphp_context *context;
- acpi_lock_hp_context();
- context = acpiphp_get_context(adev);
- if (!context || context->func.parent->is_going_away) {
- acpi_unlock_hp_context();
+ context = acpiphp_grab_context(adev);
+ if (!context)
return -ENODATA;
- }
- get_bridge(context->func.parent);
- acpiphp_put_context(context);
- acpi_unlock_hp_context();
hotplug_event(type, context);
-
- put_bridge(context->func.parent);
+ acpiphp_let_context_go(context);
return 0;
}