From edf5bf34d40804fbef32f240a79b74ffc69a658b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 21 Feb 2014 01:10:18 +0100 Subject: [PATCH] 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 --- drivers/acpi/dock.c | 29 +++++++ drivers/pci/hotplug/acpiphp_glue.c | 119 ++++++++++++----------------- 2 files changed, 78 insertions(+), 70 deletions(-) diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 8c3967cb4830..78c4ee7a422e 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -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) { diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index cd886725c42e..15865aeebbb5 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -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; } -- GitLab