ACPI / dock: Use callback pointers from devices' ACPI hotplug contexts
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Fri, 21 Feb 2014 00:10:18 +0000 (01:10 +0100)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Fri, 21 Feb 2014 00:10:18 +0000 (01:10 +0100)
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>
drivers/acpi/dock.c
drivers/pci/hotplug/acpiphp_glue.c

index 8c3967cb4830b3dc81a5d7e04cbb023a01fb7a7a..78c4ee7a422ebc2673f245f091bc04fa9e650f19 100644 (file)
@@ -185,9 +185,38 @@ static void dock_release_hotplug(struct dock_dependent_device *dd)
 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) {
index cd886725c42e7a9c9b048c08f5736f2429447678..15865aeebbb53d06f0f8f1ad46987a71ebb520cc 100644 (file)
@@ -60,6 +60,7 @@ static LIST_HEAD(bridge_list);
 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 @@ static struct acpiphp_context *acpiphp_init_context(struct acpi_device *adev)
                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 @@ static inline void put_bridge(struct acpiphp_bridge *bridge)
        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 @@ static void free_bridge(struct kref *kref)
        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 @@ static void post_dock_fixups(acpi_handle not_used, u32 event, void *data)
                        | ((unsigned int)(bus->busn_res.end) << 16);
                pci_write_config_dword(bus->self, PCI_PRIMARY_BUS, buses);
        }
-}
 
-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);
-       }
+ out:
+       acpiphp_let_context_go(context);
 }
 
-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 @@ static bool device_is_managed_by_native_pciehp(struct pci_dev *pdev)
        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 @@ static acpi_status acpiphp_add_context(acpi_handle handle, u32 lvl, void *data,
        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 @@ static acpi_status acpiphp_add_context(acpi_handle handle, u32 lvl, void *data,
                                       &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 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge)
                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 @@ static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type)
 {
        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;
 }