From 1e2380cd144f6a9619f72f80ad9a93268f63b8dc Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 16 Feb 2014 01:51:01 +0100 Subject: [PATCH 01/13] ACPI / dock: Dispatch dock notifications from the global notify handler The ACPI dock station code carries out an extra namespace scan before the main one in order to find and register all of the dock device objects. Then, it registers a notify handler for each of them for handling dock events. However, dock device objects need not be scanned for upfront. They very well can be enumerated and registered during the first phase of the main namespace scan, before attaching scan handlers and ACPI drivers to ACPI device objects. Then, the dependent devices can be added to the in the second phase. That makes it possible to drop the extra namespace scan, so do it. Moreover, it is not necessary to register notify handlers for all of the dock stations' namespace nodes, becuase notifications may be dispatched from the global notify handler for them. Do that and drop two functions used for dock notify handling, acpi_dock_deferred_cb() and dock_notify_handler(), that aren't necessary any more. Finally, some dock station objects have _HID objects matching the ACPI container scan handler which causes it to claim those objects and try to handle their hotplug, but that is not a good idea, because those objects have their own special hotplug handling anyway. For this reason, the hotplug_notify flag should not be set for ACPI device objects representing dock stations and the container scan handler should be made ignore those objects, so make that happen. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/container.c | 3 + drivers/acpi/dock.c | 173 ++++++++++----------------------------- drivers/acpi/internal.h | 11 ++- drivers/acpi/scan.c | 36 +++++++- include/acpi/acpi_bus.h | 3 +- 5 files changed, 91 insertions(+), 135 deletions(-) diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index 0b6ae6eb5c4a..9c35765ac5e9 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -68,6 +68,9 @@ static int container_device_attach(struct acpi_device *adev, struct device *dev; int ret; + if (adev->flags.is_dock_station) + return 0; + cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); if (!cdev) return -ENOMEM; diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 5bfd769fc91f..44c6e6c0d545 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -103,8 +103,7 @@ enum dock_callback_type { * * Add the dependent device to the dock's dependent device list. */ -static int __init -add_dock_dependent_device(struct dock_station *ds, acpi_handle handle) +static int add_dock_dependent_device(struct dock_station *ds, acpi_handle handle) { struct dock_dependent_device *dd; @@ -218,6 +217,17 @@ static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event, dock_release_hotplug(dd); } +static struct dock_station *find_dock_station(acpi_handle handle) +{ + struct dock_station *ds; + + list_for_each_entry(ds, &dock_stations, sibling) + if (ds->handle == handle) + return ds; + + return NULL; +} + /** * find_dock_dependent_device - get a device dependent on this dock * @ds: the dock station @@ -238,33 +248,19 @@ find_dock_dependent_device(struct dock_station *ds, acpi_handle handle) return NULL; } +void register_dock_dependent_device(struct acpi_device *adev, + acpi_handle dshandle) +{ + struct dock_station *ds = find_dock_station(dshandle); + acpi_handle handle = adev->handle; + + if (ds && !find_dock_dependent_device(ds, handle)) + add_dock_dependent_device(ds, handle); +} + /***************************************************************************** * Dock functions * *****************************************************************************/ -static int __init is_battery(acpi_handle handle) -{ - struct acpi_device_info *info; - int ret = 1; - - if (!ACPI_SUCCESS(acpi_get_object_info(handle, &info))) - return 0; - if (!(info->valid & ACPI_VALID_HID)) - ret = 0; - else - ret = !strcmp("PNP0C0A", info->hardware_id.string); - - kfree(info); - return ret; -} - -/* Check whether ACPI object is an ejectable battery or disk bay */ -static bool __init is_ejectable_bay(acpi_handle handle) -{ - if (acpi_has_method(handle, "_EJ0") && is_battery(handle)) - return true; - - return acpi_bay_match(handle); -} /** * is_dock_device - see if a device is on a dock station @@ -598,20 +594,23 @@ static int handle_eject_request(struct dock_station *ds, u32 event) } /** - * dock_notify - act upon an acpi dock notification - * @ds: dock station - * @event: the acpi event + * dock_notify - Handle ACPI dock notification. + * @adev: Dock station's ACPI device object. + * @event: Event code. * * If we are notified to dock, then check to see if the dock is * present and then dock. Notify all drivers of the dock event, * and then hotplug and devices that may need hotplugging. */ -static void dock_notify(struct dock_station *ds, u32 event) +int dock_notify(struct acpi_device *adev, u32 event) { - acpi_handle handle = ds->handle; - struct acpi_device *adev = NULL; + acpi_handle handle = adev->handle; + struct dock_station *ds = find_dock_station(handle); int surprise_removal = 0; + if (!ds) + return -ENODEV; + /* * According to acpi spec 3.0a, if a DEVICE_CHECK notification * is sent and _DCK is present, it is assumed to mean an undock @@ -632,7 +631,6 @@ static void dock_notify(struct dock_station *ds, u32 event) switch (event) { case ACPI_NOTIFY_BUS_CHECK: case ACPI_NOTIFY_DEVICE_CHECK: - acpi_bus_get_device(handle, &adev); if (!dock_in_progress(ds) && !acpi_device_enumerated(adev)) { begin_dock(ds); dock(ds); @@ -662,49 +660,8 @@ static void dock_notify(struct dock_station *ds, u32 event) else dock_event(ds, event, UNDOCK_EVENT); break; - default: - acpi_handle_err(handle, "Unknown dock event %d\n", event); } -} - -static void acpi_dock_deferred_cb(void *data, u32 event) -{ - acpi_scan_lock_acquire(); - dock_notify(data, event); - acpi_scan_lock_release(); -} - -static void dock_notify_handler(acpi_handle handle, u32 event, void *data) -{ - if (event != ACPI_NOTIFY_BUS_CHECK && event != ACPI_NOTIFY_DEVICE_CHECK - && event != ACPI_NOTIFY_EJECT_REQUEST) - return; - - acpi_hotplug_execute(acpi_dock_deferred_cb, data, event); -} - -/** - * find_dock_devices - find devices on the dock station - * @handle: the handle of the device we are examining - * @lvl: unused - * @context: the dock station private data - * @rv: unused - * - * This function is called by acpi_walk_namespace. It will - * check to see if an object has an _EJD method. If it does, then it - * will see if it is dependent on the dock station. - */ -static acpi_status __init find_dock_devices(acpi_handle handle, u32 lvl, - void *context, void **rv) -{ - struct dock_station *ds = context; - acpi_handle ejd = NULL; - - acpi_bus_get_ejd(handle, &ejd); - if (ejd == ds->handle) - add_dock_dependent_device(ds, handle); - - return AE_OK; + return 0; } /* @@ -803,23 +760,22 @@ static struct attribute_group dock_attribute_group = { }; /** - * dock_add - add a new dock station - * @handle: the dock station handle + * acpi_dock_add - Add a new dock station + * @adev: Dock station ACPI device object. * - * allocated and initialize a new dock station device. Find all devices - * that are on the dock station, and register for dock event notifications. + * allocated and initialize a new dock station device. */ -static int __init dock_add(acpi_handle handle) +void acpi_dock_add(struct acpi_device *adev) { struct dock_station *dock_station, ds = { NULL, }; + acpi_handle handle = adev->handle; struct platform_device *dd; - acpi_status status; int ret; dd = platform_device_register_data(NULL, "dock", dock_station_count, &ds, sizeof(ds)); if (IS_ERR(dd)) - return PTR_ERR(dd); + return; dock_station = dd->dev.platform_data; @@ -837,33 +793,24 @@ static int __init dock_add(acpi_handle handle) dock_station->flags |= DOCK_IS_DOCK; if (acpi_ata_match(handle)) dock_station->flags |= DOCK_IS_ATA; - if (is_battery(handle)) + if (acpi_device_is_battery(handle)) dock_station->flags |= DOCK_IS_BAT; ret = sysfs_create_group(&dd->dev.kobj, &dock_attribute_group); if (ret) goto err_unregister; - /* Find dependent devices */ - acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, find_dock_devices, NULL, - dock_station, NULL); - /* add the dock station as a device dependent on itself */ ret = add_dock_dependent_device(dock_station, handle); if (ret) goto err_rmgroup; - status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, - dock_notify_handler, dock_station); - if (ACPI_FAILURE(status)) { - ret = -ENODEV; - goto err_rmgroup; - } - dock_station_count++; list_add(&dock_station->sibling, &dock_stations); - return 0; + adev->flags.is_dock_station = true; + dev_info(&adev->dev, "ACPI dock station (docks/bays count: %d)\n", + dock_station_count); + return; err_rmgroup: remove_dock_dependent_devices(dock_station); @@ -871,38 +818,4 @@ static int __init dock_add(acpi_handle handle) err_unregister: platform_device_unregister(dd); acpi_handle_err(handle, "%s encountered error %d\n", __func__, ret); - return ret; -} - -/** - * find_dock_and_bay - look for dock stations and bays - * @handle: acpi handle of a device - * @lvl: unused - * @context: unused - * @rv: unused - * - * This is called by acpi_walk_namespace to look for dock stations and bays. - */ -static acpi_status __init -find_dock_and_bay(acpi_handle handle, u32 lvl, void *context, void **rv) -{ - if (acpi_dock_match(handle) || is_ejectable_bay(handle)) - dock_add(handle); - - return AE_OK; -} - -void __init acpi_dock_init(void) -{ - /* look for dock stations and bays */ - acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, find_dock_and_bay, NULL, NULL, NULL); - - if (!dock_station_count) { - pr_info(PREFIX "No dock devices found.\n"); - return; - } - - pr_info(PREFIX "%s: %d docks/bays found\n", - ACPI_DOCK_DRIVER_DESCRIPTION, dock_station_count); } diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 143d5df5ec32..00e3220febda 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -37,9 +37,15 @@ void acpi_container_init(void); static inline void acpi_container_init(void) {} #endif #ifdef CONFIG_ACPI_DOCK -void acpi_dock_init(void); +void register_dock_dependent_device(struct acpi_device *adev, + acpi_handle dshandle); +int dock_notify(struct acpi_device *adev, u32 event); +void acpi_dock_add(struct acpi_device *adev); #else -static inline void acpi_dock_init(void) {} +static inline void register_dock_dependent_device(struct acpi_device *adev, + acpi_handle dshandle) {} +static inline int dock_notify(struct acpi_device *adev, u32 event) { return -ENODEV; } +static inline void acpi_dock_add(struct acpi_device *adev) {} #endif #ifdef CONFIG_ACPI_HOTPLUG_MEMORY void acpi_memory_hotplug_init(void); @@ -91,6 +97,7 @@ void acpi_free_pnp_ids(struct acpi_device_pnp *pnp); int acpi_bind_one(struct device *dev, struct acpi_device *adev); int acpi_unbind_one(struct device *dev); bool acpi_device_is_present(struct acpi_device *adev); +bool acpi_device_is_battery(acpi_handle handle); /* -------------------------------------------------------------------------- Power Resource diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 8bb48bfab1df..ec12d970d78d 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -487,7 +487,9 @@ void acpi_device_hotplug(void *data, u32 src) if (adev->handle == INVALID_ACPI_HANDLE) goto err_out; - if (adev->flags.hotplug_notify) { + if (adev->flags.is_dock_station) { + error = dock_notify(adev, src); + } else if (adev->flags.hotplug_notify) { error = acpi_generic_hotplug_event(adev, src); if (error == -EPERM) { ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED; @@ -1660,6 +1662,29 @@ bool acpi_bay_match(acpi_handle handle) return acpi_ata_match(phandle); } +bool acpi_device_is_battery(acpi_handle handle) +{ + struct acpi_device_info *info; + bool ret = false; + + if (!ACPI_SUCCESS(acpi_get_object_info(handle, &info))) + return false; + + if (info->valid & ACPI_VALID_HID) + ret = !strcmp("PNP0C0A", info->hardware_id.string); + + kfree(info); + return ret; +} + +static bool is_ejectable_bay(acpi_handle handle) +{ + if (acpi_has_method(handle, "_EJ0") && acpi_device_is_battery(handle)) + return true; + + return acpi_bay_match(handle); +} + /* * acpi_dock_match - see if an acpi object has a _DCK method */ @@ -1964,6 +1989,10 @@ static void acpi_scan_init_hotplug(struct acpi_device *adev) { struct acpi_hardware_id *hwid; + if (acpi_dock_match(adev->handle) || is_ejectable_bay(adev->handle)) { + acpi_dock_add(adev); + return; + } list_for_each_entry(hwid, &adev->pnp.ids, list) { struct acpi_scan_handler *handler; @@ -2035,8 +2064,12 @@ static int acpi_scan_attach_handler(struct acpi_device *device) static void acpi_bus_attach(struct acpi_device *device) { struct acpi_device *child; + acpi_handle ejd; int ret; + if (ACPI_SUCCESS(acpi_bus_get_ejd(device->handle, &ejd))) + register_dock_dependent_device(device, ejd); + acpi_bus_get_status(device); /* Skip devices that are not present. */ if (!acpi_device_is_present(device)) { @@ -2189,7 +2222,6 @@ int __init acpi_scan_init(void) acpi_cmos_rtc_init(); acpi_container_init(); acpi_memory_hotplug_init(); - acpi_dock_init(); mutex_lock(&acpi_scan_lock); /* diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 32f90c7bcb03..8fb297b5307c 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -201,7 +201,8 @@ struct acpi_device_flags { u32 visited:1; u32 no_hotplug:1; u32 hotplug_notify:1; - u32 reserved:23; + u32 is_dock_station:1; + u32 reserved:22; }; /* File System */ From b43109fa466e6e29091b3e62e6a6c8a0bd099beb Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 16 Feb 2014 00:09:34 +0100 Subject: [PATCH 02/13] ACPI / dock: Pass ACPI device pointer to acpi_device_is_battery() Since we already know what the device's PNP IDs are when acpi_device_is_battery() is called, it is not necessary to run acpi_get_object_info() for the device in that function. Instead, if acpi_device_is_battery() is passed a pointer to a struct acpi_device object, it can use the list of PNP IDs from that object, so make that happen and modify the function's header accordingly Signed-off-by: Rafael J. Wysocki --- drivers/acpi/dock.c | 2 +- drivers/acpi/internal.h | 2 +- drivers/acpi/scan.c | 24 +++++++++++------------- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 44c6e6c0d545..edeb72b619fb 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -793,7 +793,7 @@ void acpi_dock_add(struct acpi_device *adev) dock_station->flags |= DOCK_IS_DOCK; if (acpi_ata_match(handle)) dock_station->flags |= DOCK_IS_ATA; - if (acpi_device_is_battery(handle)) + if (acpi_device_is_battery(adev)) dock_station->flags |= DOCK_IS_BAT; ret = sysfs_create_group(&dd->dev.kobj, &dock_attribute_group); diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 00e3220febda..4d081fc1aa24 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -97,7 +97,7 @@ void acpi_free_pnp_ids(struct acpi_device_pnp *pnp); int acpi_bind_one(struct device *dev, struct acpi_device *adev); int acpi_unbind_one(struct device *dev); bool acpi_device_is_present(struct acpi_device *adev); -bool acpi_device_is_battery(acpi_handle handle); +bool acpi_device_is_battery(struct acpi_device *adev); /* -------------------------------------------------------------------------- Power Resource diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index ec12d970d78d..518aae461a00 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1662,24 +1662,22 @@ bool acpi_bay_match(acpi_handle handle) return acpi_ata_match(phandle); } -bool acpi_device_is_battery(acpi_handle handle) +bool acpi_device_is_battery(struct acpi_device *adev) { - struct acpi_device_info *info; - bool ret = false; + struct acpi_hardware_id *hwid; - if (!ACPI_SUCCESS(acpi_get_object_info(handle, &info))) - return false; + list_for_each_entry(hwid, &adev->pnp.ids, list) + if (!strcmp("PNP0C0A", hwid->id)) + return true; - if (info->valid & ACPI_VALID_HID) - ret = !strcmp("PNP0C0A", info->hardware_id.string); - - kfree(info); - return ret; + return false; } -static bool is_ejectable_bay(acpi_handle handle) +static bool is_ejectable_bay(struct acpi_device *adev) { - if (acpi_has_method(handle, "_EJ0") && acpi_device_is_battery(handle)) + acpi_handle handle = adev->handle; + + if (acpi_has_method(handle, "_EJ0") && acpi_device_is_battery(adev)) return true; return acpi_bay_match(handle); @@ -1989,7 +1987,7 @@ static void acpi_scan_init_hotplug(struct acpi_device *adev) { struct acpi_hardware_id *hwid; - if (acpi_dock_match(adev->handle) || is_ejectable_bay(adev->handle)) { + if (acpi_dock_match(adev->handle) || is_ejectable_bay(adev)) { acpi_dock_add(adev); return; } From af8874491de6f03237df5e20d87324cbbe226f19 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 16 Feb 2014 00:09:47 +0100 Subject: [PATCH 03/13] ACPI / dock: Associate dock platform devices with ACPI device objects To allow user space to check which ACPI device object the dock station is represented by, make acpi_dock_add() indicate to platform_device_register_full() which ACPI device object should be the companion of the new platform device. This also ensures that the ACPI device object in question will not go away while the dock platform device is present (which is always). Signed-off-by: Rafael J. Wysocki --- drivers/acpi/dock.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index edeb72b619fb..3199ce950e05 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -768,12 +768,18 @@ static struct attribute_group dock_attribute_group = { void acpi_dock_add(struct acpi_device *adev) { struct dock_station *dock_station, ds = { NULL, }; + struct platform_device_info pdevinfo; acpi_handle handle = adev->handle; struct platform_device *dd; int ret; - dd = platform_device_register_data(NULL, "dock", dock_station_count, - &ds, sizeof(ds)); + memset(&pdevinfo, 0, sizeof(pdevinfo)); + pdevinfo.name = "dock"; + pdevinfo.id = dock_station_count; + pdevinfo.acpi_node.companion = adev; + pdevinfo.data = &ds; + pdevinfo.size_data = sizeof(ds); + dd = platform_device_register_full(&pdevinfo); if (IS_ERR(dd)) return; From d7c7c0256b936901eb7a70fbc6d674a6bdfd463e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 21 Feb 2014 01:08:42 +0100 Subject: [PATCH 04/13] ACPI / hotplug / PCI: Do not clear event callback pointer for docks After recent changes adding dock station handling to the ACPI hotplug core, it is not necessary to clear the .event() pointer in the ACPIPHP device hotplug context for dock stations any more, so don't do that. Signed-off-by: Rafael J. Wysocki --- drivers/pci/hotplug/acpiphp_glue.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 91276f9fe268..db81412cacb0 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -304,13 +304,6 @@ static acpi_status register_slot(acpi_handle handle, u32 lvl, void *data, if (acpi_has_method(handle, "_STA")) newfunc->flags |= FUNC_HAS_STA; - /* - * Dock stations' notify handler should be used for dock devices instead - * of the common one, so clear hp.event in their contexts. - */ - if (acpi_has_method(handle, "_DCK")) - context->hp.event = NULL; - acpi_unlock_hp_context(); /* search for objects that share the same slot */ From 59b42fa01fe2d84f3c3f28ee6f25510820ace35b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 21 Feb 2014 01:08:51 +0100 Subject: [PATCH 05/13] ACPI / hotplug: Add .fixup() callback to struct acpi_hotplug_context In order for the ACPI dock station code to be able to use the callbacks pointed to by the ACPI device objects' hotplug contexts add a .fixup() callback pointer to struct acpi_hotplug_context. That callback will be useful to handle PCI devices located in dock stations. Signed-off-by: Rafael J. Wysocki --- drivers/pci/hotplug/acpiphp_glue.c | 2 +- include/acpi/acpi_bus.h | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index db81412cacb0..4228c67ceffe 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -80,7 +80,7 @@ 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); + acpi_set_hp_context(adev, &context->hp, acpiphp_hotplug_event, NULL); return context; } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 8fb297b5307c..007fe99e29f5 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -144,6 +144,7 @@ struct acpi_scan_handler { struct acpi_hotplug_context { struct acpi_device *self; int (*event)(struct acpi_device *, u32); + void (*fixup)(struct acpi_device *); }; /* @@ -366,10 +367,12 @@ static inline void acpi_set_device_status(struct acpi_device *adev, u32 sta) static inline void acpi_set_hp_context(struct acpi_device *adev, struct acpi_hotplug_context *hp, - int (*event)(struct acpi_device *, u32)) + int (*event)(struct acpi_device *, u32), + void (*fixup)(struct acpi_device *)) { hp->self = adev; hp->event = event; + hp->fixup = fixup; adev->hp = hp; } From 3b52b21fa1f44c8956e21dfba645eda959111b5e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 21 Feb 2014 01:10:09 +0100 Subject: [PATCH 06/13] ACPI / dock: Use ACPI device object pointers instead of ACPI handles Rework the ACPI dock station driver to store ACPI device object pointers instead of ACPI handles in its internal data structures. The purpose is moslty to make subsequent simplifications possible, but also this allows the overall code size to be reduced slightly. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/dock.c | 104 ++++++++++++----------------- drivers/pci/hotplug/acpiphp_glue.c | 6 +- include/acpi/acpi_drivers.h | 4 +- 3 files changed, 47 insertions(+), 67 deletions(-) diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 3199ce950e05..8c3967cb4830 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -72,7 +72,7 @@ static DEFINE_MUTEX(hotplug_lock); struct dock_dependent_device { struct list_head list; - acpi_handle handle; + struct acpi_device *adev; const struct acpi_dock_ops *hp_ops; void *hp_context; unsigned int hp_refcount; @@ -98,12 +98,13 @@ enum dock_callback_type { *****************************************************************************/ /** * add_dock_dependent_device - associate a device with the dock station - * @ds: The dock station - * @handle: handle of the dependent device + * @ds: Dock station. + * @adev: Dependent ACPI device object. * * Add the dependent device to the dock's dependent device list. */ -static int add_dock_dependent_device(struct dock_station *ds, acpi_handle handle) +static int add_dock_dependent_device(struct dock_station *ds, + struct acpi_device *adev) { struct dock_dependent_device *dd; @@ -111,7 +112,7 @@ static int add_dock_dependent_device(struct dock_station *ds, acpi_handle handle if (!dd) return -ENOMEM; - dd->handle = handle; + dd->adev = adev; INIT_LIST_HEAD(&dd->list); list_add_tail(&dd->list, &ds->dependent_devices); @@ -212,7 +213,7 @@ static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event, return; if (cb) - cb(dd->handle, event, dd->hp_context); + cb(dd->adev->handle, event, dd->hp_context); dock_release_hotplug(dd); } @@ -231,18 +232,18 @@ static struct dock_station *find_dock_station(acpi_handle handle) /** * find_dock_dependent_device - get a device dependent on this dock * @ds: the dock station - * @handle: the acpi_handle of the device we want + * @adev: ACPI device object to find. * * iterate over the dependent device list for this dock. If the * dependent device matches the handle, return. */ static struct dock_dependent_device * -find_dock_dependent_device(struct dock_station *ds, acpi_handle handle) +find_dock_dependent_device(struct dock_station *ds, struct acpi_device *adev) { struct dock_dependent_device *dd; list_for_each_entry(dd, &ds->dependent_devices, list) - if (handle == dd->handle) + if (adev == dd->adev) return dd; return NULL; @@ -252,10 +253,9 @@ void register_dock_dependent_device(struct acpi_device *adev, acpi_handle dshandle) { struct dock_station *ds = find_dock_station(dshandle); - acpi_handle handle = adev->handle; - if (ds && !find_dock_dependent_device(ds, handle)) - add_dock_dependent_device(ds, handle); + if (ds && !find_dock_dependent_device(ds, adev)) + add_dock_dependent_device(ds, adev); } /***************************************************************************** @@ -264,24 +264,24 @@ void register_dock_dependent_device(struct acpi_device *adev, /** * is_dock_device - see if a device is on a dock station - * @handle: acpi handle of the device + * @adev: ACPI device object to check. * * If this device is either the dock station itself, * or is a device dependent on the dock station, then it * is a dock device */ -int is_dock_device(acpi_handle handle) +int is_dock_device(struct acpi_device *adev) { struct dock_station *dock_station; if (!dock_station_count) return 0; - if (acpi_dock_match(handle)) + if (acpi_dock_match(adev->handle)) return 1; list_for_each_entry(dock_station, &dock_stations, sibling) - if (find_dock_dependent_device(dock_station, handle)) + if (find_dock_dependent_device(dock_station, adev)) return 1; return 0; @@ -308,43 +308,6 @@ static int dock_present(struct dock_station *ds) return 0; } -/** - * dock_create_acpi_device - add new devices to acpi - * @handle - handle of the device to add - * - * This function will create a new acpi_device for the given - * handle if one does not exist already. This should cause - * acpi to scan for drivers for the given devices, and call - * matching driver's add routine. - */ -static void dock_create_acpi_device(acpi_handle handle) -{ - struct acpi_device *device = NULL; - int ret; - - acpi_bus_get_device(handle, &device); - if (!acpi_device_enumerated(device)) { - ret = acpi_bus_scan(handle); - if (ret) - pr_debug("error adding bus, %x\n", -ret); - } -} - -/** - * dock_remove_acpi_device - remove the acpi_device struct from acpi - * @handle - the handle of the device to remove - * - * Tell acpi to remove the acpi_device. This should cause any loaded - * driver to have it's remove routine called. - */ -static void dock_remove_acpi_device(acpi_handle handle) -{ - struct acpi_device *device; - - if (!acpi_bus_get_device(handle, &device)) - acpi_bus_trim(device); -} - /** * hot_remove_dock_devices - Remove dock station devices. * @ds: Dock station. @@ -362,7 +325,7 @@ static void hot_remove_dock_devices(struct dock_station *ds) dock_hotplug_event(dd, ACPI_NOTIFY_EJECT_REQUEST, false); list_for_each_entry_reverse(dd, &ds->dependent_devices, list) - dock_remove_acpi_device(dd->handle); + acpi_bus_trim(dd->adev); } /** @@ -388,12 +351,20 @@ static void hotplug_dock_devices(struct dock_station *ds, u32 event) dock_hotplug_event(dd, event, DOCK_CALL_HANDLER); /* - * Now make sure that an acpi_device is created for each dependent - * device. That will cause scan handlers to be attached to device - * objects or acpi_drivers to be stopped/started if they are present. + * Check if all devices have been enumerated already. If not, run + * acpi_bus_scan() for them and that will cause scan handlers to be + * attached to device objects or acpi_drivers to be stopped/started if + * they are present. */ - list_for_each_entry(dd, &ds->dependent_devices, list) - dock_create_acpi_device(dd->handle); + list_for_each_entry(dd, &ds->dependent_devices, list) { + struct acpi_device *adev = dd->adev; + + if (!acpi_device_enumerated(adev)) { + int ret = acpi_bus_scan(adev->handle); + if (ret) + dev_dbg(&adev->dev, "scan error %d\n", -ret); + } + } } static void dock_event(struct dock_station *ds, u32 event, int num) @@ -514,6 +485,7 @@ int register_hotplug_dock_device(acpi_handle handle, { struct dock_dependent_device *dd; struct dock_station *dock_station; + struct acpi_device *adev; int ret = -EINVAL; if (WARN_ON(!context)) @@ -522,6 +494,10 @@ int register_hotplug_dock_device(acpi_handle handle, if (!dock_station_count) return -ENODEV; + ret = acpi_bus_get_device(handle, &adev); + if (ret) + return ret; + /* * make sure this handle is for a device dependent on the dock, * this would include the dock station itself @@ -532,7 +508,7 @@ int register_hotplug_dock_device(acpi_handle handle, * separately, so there are two 'dock stations' which need the * ops */ - dd = find_dock_dependent_device(dock_station, handle); + dd = find_dock_dependent_device(dock_station, adev); if (dd && !dock_init_hotplug(dd, ops, context, init, release)) ret = 0; } @@ -549,12 +525,16 @@ void unregister_hotplug_dock_device(acpi_handle handle) { struct dock_dependent_device *dd; struct dock_station *dock_station; + struct acpi_device *adev; if (!dock_station_count) return; + if (acpi_bus_get_device(handle, &adev)) + return; + list_for_each_entry(dock_station, &dock_stations, sibling) { - dd = find_dock_dependent_device(dock_station, handle); + dd = find_dock_dependent_device(dock_station, adev); if (dd) dock_release_hotplug(dd); } @@ -807,7 +787,7 @@ void acpi_dock_add(struct acpi_device *adev) goto err_unregister; /* add the dock station as a device dependent on itself */ - ret = add_dock_dependent_device(dock_station, handle); + ret = add_dock_dependent_device(dock_station, adev); if (ret) goto err_rmgroup; diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index d3d2cc6bb40a..cd886725c42e 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -334,7 +334,7 @@ static acpi_status acpiphp_add_context(acpi_handle handle, u32 lvl, void *data, * by the native PCIe hotplug (PCIeHP), becuase that code is supposed to * expose slots to user space in those cases. */ - if ((acpi_pci_check_ejectable(pbus, handle) || is_dock_device(handle)) + if ((acpi_pci_check_ejectable(pbus, handle) || is_dock_device(adev)) && !(pdev && device_is_managed_by_native_pciehp(pdev))) { unsigned long long sun; int retval; @@ -369,7 +369,7 @@ static acpi_status acpiphp_add_context(acpi_handle handle, u32 lvl, void *data, &val, 60*1000)) slot->flags |= SLOT_ENABLED; - if (is_dock_device(handle)) { + 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 @@ -411,7 +411,7 @@ 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->handle)) + if (is_dock_device(adev)) unregister_hotplug_dock_device(adev->handle); acpi_lock_hp_context(); diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index b124fdb26046..d6c98b9cbe38 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h @@ -116,7 +116,7 @@ struct acpi_dock_ops { }; #ifdef CONFIG_ACPI_DOCK -extern int is_dock_device(acpi_handle handle); +extern int is_dock_device(struct acpi_device *adev); extern int register_hotplug_dock_device(acpi_handle handle, const struct acpi_dock_ops *ops, void *context, @@ -124,7 +124,7 @@ extern int register_hotplug_dock_device(acpi_handle handle, void (*release)(void *)); extern void unregister_hotplug_dock_device(acpi_handle handle); #else -static inline int is_dock_device(acpi_handle handle) +static inline int is_dock_device(struct acpi_device *adev) { return 0; } From edf5bf34d40804fbef32f240a79b74ffc69a658b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 21 Feb 2014 01:10:18 +0100 Subject: [PATCH 07/13] 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 | 121 ++++++++++++----------------- 2 files changed, 79 insertions(+), 71 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); } + + 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 @@ 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; } From be27b3dcb02335ec093b81053fc8c84b32d3106e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 21 Feb 2014 01:10:27 +0100 Subject: [PATCH 08/13] ACPI / dock: Add .uevent() callback to struct acpi_hotplug_context In order to avoid the need to register special ACPI dock operations for SATA devices add a .uevent() callback pointer to struct acpi_hotplug_context and make dock_hotplug_event() use that callback if available. Also rename the existing .event() callback in struct acpi_hotplug_context to .notify() to avoid possible confusion in the future. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/dock.c | 11 ++++++++++- drivers/acpi/scan.c | 8 ++++---- drivers/pci/hotplug/acpiphp_glue.c | 8 ++++---- include/acpi/acpi_bus.h | 9 ++++++--- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 78c4ee7a422e..a88fad9ff234 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -203,10 +203,19 @@ static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event, fixup(adev); return; } + } else if (cb_type == DOCK_CALL_UEVENT) { + void (*uevent)(struct acpi_device *, u32); + + uevent = adev->hp->uevent; + if (uevent) { + acpi_unlock_hp_context(); + uevent(adev, event); + return; + } } else { int (*notify)(struct acpi_device *, u32); - notify = adev->hp->event; + notify = adev->hp->notify; if (notify) { acpi_unlock_hp_context(); notify(adev, event); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 518aae461a00..e7d951032f85 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -496,17 +496,17 @@ void acpi_device_hotplug(void *data, u32 src) goto err_out; } } else { - int (*event)(struct acpi_device *, u32); + int (*notify)(struct acpi_device *, u32); acpi_lock_hp_context(); - event = adev->hp ? adev->hp->event : NULL; + notify = adev->hp ? adev->hp->notify : NULL; acpi_unlock_hp_context(); /* * There may be additional notify handlers for device objects * without the .event() callback, so ignore them here. */ - if (event) - error = event(adev, src); + if (notify) + error = notify(adev, src); else goto out; } diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 15865aeebbb5..a92b8b017901 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -59,7 +59,7 @@ static LIST_HEAD(bridge_list); static DEFINE_MUTEX(bridge_mutex); -static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type); +static int acpiphp_hotplug_notify(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); @@ -81,7 +81,7 @@ 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, + acpi_set_hp_context(adev, &context->hp, acpiphp_hotplug_notify, NULL, acpiphp_post_dock_fixup); return context; } @@ -400,7 +400,7 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) struct acpi_device *adev = func_to_acpi_device(func); acpi_lock_hp_context(); - adev->hp->event = NULL; + adev->hp->notify = NULL; adev->hp->fixup = NULL; acpi_unlock_hp_context(); } @@ -833,7 +833,7 @@ static void hotplug_event(u32 type, struct acpiphp_context *context) put_bridge(bridge); } -static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type) +static int acpiphp_hotplug_notify(struct acpi_device *adev, u32 type) { struct acpiphp_context *context; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 007fe99e29f5..738278606677 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -143,7 +143,8 @@ struct acpi_scan_handler { struct acpi_hotplug_context { struct acpi_device *self; - int (*event)(struct acpi_device *, u32); + int (*notify)(struct acpi_device *, u32); + void (*uevent)(struct acpi_device *, u32); void (*fixup)(struct acpi_device *); }; @@ -367,11 +368,13 @@ static inline void acpi_set_device_status(struct acpi_device *adev, u32 sta) static inline void acpi_set_hp_context(struct acpi_device *adev, struct acpi_hotplug_context *hp, - int (*event)(struct acpi_device *, u32), + int (*notify)(struct acpi_device *, u32), + void (*uevent)(struct acpi_device *, u32), void (*fixup)(struct acpi_device *)) { hp->self = adev; - hp->event = event; + hp->notify = notify; + hp->uevent = uevent; hp->fixup = fixup; adev->hp = hp; } From 5d5132059a1f652de9dc2d62a8ff15561e648d11 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 22 Feb 2014 00:48:31 +0100 Subject: [PATCH 09/13] ACPI / ATA: Add hotplug contexts to ACPI companions of SATA devices Modify the SATA subsystem to add hotplug contexts to ACPI companions of SATA devices and ports instead of registering special ACPI dock operations using register_hotplug_dock_device(). That change will allow the entire code handling those special ACPI dock operations to be dropped in the next commit. Signed-off-by: Rafael J. Wysocki Reviewed-by: Aaron Lu Acked-by: Tejun Heo --- drivers/acpi/scan.c | 11 ++++++ drivers/ata/libata-acpi.c | 72 ++++++++++++++++++++++++--------------- include/acpi/acpi_bus.h | 5 +++ 3 files changed, 61 insertions(+), 27 deletions(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index e7d951032f85..f6bcc24f73dc 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -71,6 +71,17 @@ void acpi_unlock_hp_context(void) mutex_unlock(&acpi_hp_context_lock); } +void acpi_initialize_hp_context(struct acpi_device *adev, + struct acpi_hotplug_context *hp, + int (*notify)(struct acpi_device *, u32), + void (*uevent)(struct acpi_device *, u32)) +{ + acpi_lock_hp_context(); + acpi_set_hp_context(adev, hp, notify, uevent, NULL); + acpi_unlock_hp_context(); +} +EXPORT_SYMBOL_GPL(acpi_initialize_hp_context); + int acpi_scan_add_handler(struct acpi_scan_handler *handler) { if (!handler || !handler->attach) diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index 9e69a5308693..acb95dffdb6a 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -38,6 +38,16 @@ static void ata_acpi_clear_gtf(struct ata_device *dev) dev->gtf_cache = NULL; } +struct ata_acpi_hotplug_context { + struct acpi_hotplug_context hp; + union { + struct ata_port *ap; + struct ata_device *dev; + } data; +}; + +#define ata_hotplug_data(context) (container_of((context), struct ata_acpi_hotplug_context, hp)->data) + /** * ata_dev_acpi_handle - provide the acpi_handle for an ata_device * @dev: the acpi_handle returned will correspond to this device @@ -121,18 +131,17 @@ static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device *dev, ata_port_wait_eh(ap); } -static void ata_acpi_dev_notify_dock(acpi_handle handle, u32 event, void *data) +static int ata_acpi_dev_notify_dock(struct acpi_device *adev, u32 event) { - struct ata_device *dev = data; - + struct ata_device *dev = ata_hotplug_data(adev->hp).dev; ata_acpi_handle_hotplug(dev->link->ap, dev, event); + return 0; } -static void ata_acpi_ap_notify_dock(acpi_handle handle, u32 event, void *data) +static int ata_acpi_ap_notify_dock(struct acpi_device *adev, u32 event) { - struct ata_port *ap = data; - - ata_acpi_handle_hotplug(ap, NULL, event); + ata_acpi_handle_hotplug(ata_hotplug_data(adev->hp).ap, NULL, event); + return 0; } static void ata_acpi_uevent(struct ata_port *ap, struct ata_device *dev, @@ -154,31 +163,23 @@ static void ata_acpi_uevent(struct ata_port *ap, struct ata_device *dev, } } -static void ata_acpi_ap_uevent(acpi_handle handle, u32 event, void *data) +static void ata_acpi_ap_uevent(struct acpi_device *adev, u32 event) { - ata_acpi_uevent(data, NULL, event); + ata_acpi_uevent(ata_hotplug_data(adev->hp).ap, NULL, event); } -static void ata_acpi_dev_uevent(acpi_handle handle, u32 event, void *data) +static void ata_acpi_dev_uevent(struct acpi_device *adev, u32 event) { - struct ata_device *dev = data; + struct ata_device *dev = ata_hotplug_data(adev->hp).dev; ata_acpi_uevent(dev->link->ap, dev, event); } -static const struct acpi_dock_ops ata_acpi_dev_dock_ops = { - .handler = ata_acpi_dev_notify_dock, - .uevent = ata_acpi_dev_uevent, -}; - -static const struct acpi_dock_ops ata_acpi_ap_dock_ops = { - .handler = ata_acpi_ap_notify_dock, - .uevent = ata_acpi_ap_uevent, -}; - /* bind acpi handle to pata port */ void ata_acpi_bind_port(struct ata_port *ap) { struct acpi_device *host_companion = ACPI_COMPANION(ap->host->dev); + struct acpi_device *adev; + struct ata_acpi_hotplug_context *context; if (libata_noacpi || ap->flags & ATA_FLAG_ACPI_SATA || !host_companion) return; @@ -188,9 +189,17 @@ void ata_acpi_bind_port(struct ata_port *ap) if (ata_acpi_gtm(ap, &ap->__acpi_init_gtm) == 0) ap->pflags |= ATA_PFLAG_INIT_GTM_VALID; - /* we might be on a docking station */ - register_hotplug_dock_device(ACPI_HANDLE(&ap->tdev), - &ata_acpi_ap_dock_ops, ap, NULL, NULL); + adev = ACPI_COMPANION(&ap->tdev); + if (!adev || adev->hp) + return; + + context = kzalloc(sizeof(*context), GFP_KERNEL); + if (!context) + return; + + context->data.ap = ap; + acpi_initialize_hp_context(adev, &context->hp, ata_acpi_ap_notify_dock, + ata_acpi_ap_uevent); } void ata_acpi_bind_dev(struct ata_device *dev) @@ -198,7 +207,8 @@ void ata_acpi_bind_dev(struct ata_device *dev) struct ata_port *ap = dev->link->ap; struct acpi_device *port_companion = ACPI_COMPANION(&ap->tdev); struct acpi_device *host_companion = ACPI_COMPANION(ap->host->dev); - struct acpi_device *parent; + struct acpi_device *parent, *adev; + struct ata_acpi_hotplug_context *context; u64 adr; /* @@ -221,9 +231,17 @@ void ata_acpi_bind_dev(struct ata_device *dev) } acpi_preset_companion(&dev->tdev, parent, adr); + adev = ACPI_COMPANION(&dev->tdev); + if (!adev || adev->hp) + return; - register_hotplug_dock_device(ata_dev_acpi_handle(dev), - &ata_acpi_dev_dock_ops, dev, NULL, NULL); + context = kzalloc(sizeof(*context), GFP_KERNEL); + if (!context) + return; + + context->data.dev = dev; + acpi_initialize_hp_context(adev, &context->hp, ata_acpi_dev_notify_dock, + ata_acpi_dev_uevent); } /** diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 738278606677..fb23a7b6c919 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -379,6 +379,11 @@ static inline void acpi_set_hp_context(struct acpi_device *adev, adev->hp = hp; } +void acpi_initialize_hp_context(struct acpi_device *adev, + struct acpi_hotplug_context *hp, + int (*notify)(struct acpi_device *, u32), + void (*uevent)(struct acpi_device *, u32)); + /* acpi_device.dev.bus == &acpi_bus_type */ extern struct bus_type acpi_bus_type; From 2f16817d87dcd3fc79e0320f6f0bd186b1ae6184 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 21 Feb 2014 01:11:30 +0100 Subject: [PATCH 10/13] ACPI / dock: Drop struct acpi_dock_ops and all code related to it Since struct acpi_dock_ops and the code handling it don't have any users any more after the previous changes, drop that structure and the code related to it altogether. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/dock.c | 167 +----------------------------------- include/acpi/acpi_drivers.h | 22 ----- 2 files changed, 2 insertions(+), 187 deletions(-) diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index a88fad9ff234..f6fd325078b3 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -68,15 +68,10 @@ struct dock_station { }; static LIST_HEAD(dock_stations); static int dock_station_count; -static DEFINE_MUTEX(hotplug_lock); struct dock_dependent_device { struct list_head list; struct acpi_device *adev; - const struct acpi_dock_ops *hp_ops; - void *hp_context; - unsigned int hp_refcount; - void (*hp_release)(void *); }; #define DOCK_DOCKING 0x00000001 @@ -129,70 +124,15 @@ static void remove_dock_dependent_devices(struct dock_station *ds) } } -/** - * dock_init_hotplug - Initialize a hotplug device on a docking station. - * @dd: Dock-dependent device. - * @ops: Dock operations to attach to the dependent device. - * @context: Data to pass to the @ops callbacks and @release. - * @init: Optional initialization routine to run after setting up context. - * @release: Optional release routine to run on removal. - */ -static int dock_init_hotplug(struct dock_dependent_device *dd, - const struct acpi_dock_ops *ops, void *context, - void (*init)(void *), void (*release)(void *)) -{ - int ret = 0; - - mutex_lock(&hotplug_lock); - if (WARN_ON(dd->hp_context)) { - ret = -EEXIST; - } else { - dd->hp_refcount = 1; - dd->hp_ops = ops; - dd->hp_context = context; - dd->hp_release = release; - if (init) - init(context); - } - mutex_unlock(&hotplug_lock); - return ret; -} - -/** - * dock_release_hotplug - Decrement hotplug reference counter of dock device. - * @dd: Dock-dependent device. - * - * Decrement the reference counter of @dd and if 0, detach its hotplug - * operations from it, reset its context pointer and run the optional release - * routine if present. - */ -static void dock_release_hotplug(struct dock_dependent_device *dd) -{ - mutex_lock(&hotplug_lock); - if (dd->hp_context && !--dd->hp_refcount) { - void (*release)(void *) = dd->hp_release; - void *context = dd->hp_context; - - dd->hp_ops = NULL; - dd->hp_context = NULL; - dd->hp_release = NULL; - if (release) - release(context); - } - mutex_unlock(&hotplug_lock); -} - 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; + goto out; if (cb_type == DOCK_CALL_FIXUP) { void (*fixup)(struct acpi_device *); @@ -223,37 +163,8 @@ static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event, } } - no_context: + out: acpi_unlock_hp_context(); - - mutex_lock(&hotplug_lock); - - if (dd->hp_context) { - run = true; - dd->hp_refcount++; - if (dd->hp_ops) { - switch (cb_type) { - case DOCK_CALL_FIXUP: - cb = dd->hp_ops->fixup; - break; - case DOCK_CALL_UEVENT: - cb = dd->hp_ops->uevent; - break; - default: - cb = dd->hp_ops->handler; - } - } - } - - mutex_unlock(&hotplug_lock); - - if (!run) - return; - - if (cb) - cb(dd->adev->handle, event, dd->hp_context); - - dock_release_hotplug(dd); } static struct dock_station *find_dock_station(acpi_handle handle) @@ -505,80 +416,6 @@ static int dock_in_progress(struct dock_station *ds) return 0; } -/** - * register_hotplug_dock_device - register a hotplug function - * @handle: the handle of the device - * @ops: handlers to call after docking - * @context: device specific data - * @init: Optional initialization routine to run after registration - * @release: Optional release routine to run on unregistration - * - * If a driver would like to perform a hotplug operation after a dock - * event, they can register an acpi_notifiy_handler to be called by - * the dock driver after _DCK is executed. - */ -int register_hotplug_dock_device(acpi_handle handle, - const struct acpi_dock_ops *ops, void *context, - void (*init)(void *), void (*release)(void *)) -{ - struct dock_dependent_device *dd; - struct dock_station *dock_station; - struct acpi_device *adev; - int ret = -EINVAL; - - if (WARN_ON(!context)) - return -EINVAL; - - if (!dock_station_count) - return -ENODEV; - - ret = acpi_bus_get_device(handle, &adev); - if (ret) - return ret; - - /* - * make sure this handle is for a device dependent on the dock, - * this would include the dock station itself - */ - list_for_each_entry(dock_station, &dock_stations, sibling) { - /* - * An ATA bay can be in a dock and itself can be ejected - * separately, so there are two 'dock stations' which need the - * ops - */ - dd = find_dock_dependent_device(dock_station, adev); - if (dd && !dock_init_hotplug(dd, ops, context, init, release)) - ret = 0; - } - - return ret; -} -EXPORT_SYMBOL_GPL(register_hotplug_dock_device); - -/** - * unregister_hotplug_dock_device - remove yourself from the hotplug list - * @handle: the acpi handle of the device - */ -void unregister_hotplug_dock_device(acpi_handle handle) -{ - struct dock_dependent_device *dd; - struct dock_station *dock_station; - struct acpi_device *adev; - - if (!dock_station_count) - return; - - if (acpi_bus_get_device(handle, &adev)) - return; - - list_for_each_entry(dock_station, &dock_stations, sibling) { - dd = find_dock_dependent_device(dock_station, adev); - if (dd) - dock_release_hotplug(dd); - } -} -EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device); - /** * handle_eject_request - handle an undock request checking for error conditions * diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index d6c98b9cbe38..d504613bbf80 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h @@ -109,36 +109,14 @@ void pci_acpi_crs_quirks(void); /*-------------------------------------------------------------------------- Dock Station -------------------------------------------------------------------------- */ -struct acpi_dock_ops { - acpi_notify_handler fixup; - acpi_notify_handler handler; - acpi_notify_handler uevent; -}; #ifdef CONFIG_ACPI_DOCK extern int is_dock_device(struct acpi_device *adev); -extern int register_hotplug_dock_device(acpi_handle handle, - const struct acpi_dock_ops *ops, - void *context, - void (*init)(void *), - void (*release)(void *)); -extern void unregister_hotplug_dock_device(acpi_handle handle); #else static inline int is_dock_device(struct acpi_device *adev) { return 0; } -static inline int register_hotplug_dock_device(acpi_handle handle, - const struct acpi_dock_ops *ops, - void *context, - void (*init)(void *), - void (*release)(void *)) -{ - return -ENODEV; -} -static inline void unregister_hotplug_dock_device(acpi_handle handle) -{ -} #endif /* CONFIG_ACPI_DOCK */ #endif /*__ACPI_DRIVERS_H__*/ From f311e1c4a6ea2347c8e167525f8a0fb9af9125ed Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 21 Feb 2014 01:11:38 +0100 Subject: [PATCH 11/13] ACPI / dock: Drop remove_dock_dependent_devices() Since remove_dock_dependent_devices() is only called from acpi_dock_add() and it only is called if the add_dock_dependent_device() adding the dock station to its own list of dependent devices has failed, it is not really necessary, because the dock station's list of dependent devices is guaranteed to be empty at that point. Drop it, then. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/dock.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index f6fd325078b3..d9baeae09d31 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -114,16 +114,6 @@ static int add_dock_dependent_device(struct dock_station *ds, return 0; } -static void remove_dock_dependent_devices(struct dock_station *ds) -{ - struct dock_dependent_device *dd, *aux; - - list_for_each_entry_safe(dd, aux, &ds->dependent_devices, list) { - list_del(&dd->list); - kfree(dd); - } -} - static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event, enum dock_callback_type cb_type) { @@ -674,8 +664,8 @@ void acpi_dock_add(struct acpi_device *adev) return; err_rmgroup: - remove_dock_dependent_devices(dock_station); sysfs_remove_group(&dd->dev.kobj, &dock_attribute_group); + err_unregister: platform_device_unregister(dd); acpi_handle_err(handle, "%s encountered error %d\n", __func__, ret); From 8cc2568124ad599a60024d5b410ba812a0b0917a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 21 Feb 2014 01:11:46 +0100 Subject: [PATCH 12/13] ACPI / dock: Update copyright notice Update the copyright notice of the ACPI dock driver to reflect the fact that substantial changes have been made to it recently. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/dock.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index d9baeae09d31..a7bd3002dbbc 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -1,7 +1,9 @@ /* * dock.c - ACPI dock station driver * - * Copyright (C) 2006 Kristen Carlson Accardi + * Copyright (C) 2006, 2014, Intel Corp. + * Author: Kristen Carlson Accardi + * Rafael J. Wysocki * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * From 1e3bcb596c6b1cf6db93f8f506e2de260e771bad Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 3 Mar 2014 00:40:38 +0100 Subject: [PATCH 13/13] ACPI / hotplug: Rework deferred execution of acpi_device_hotplug() Since the only function executed by acpi_hotplug_execute() is acpi_device_hotplug() and it only is called by the ACPI core, simplify its definition so that it only takes two arguments, the ACPI device object pointer and event code, rename it to acpi_hotplug_schedule() and move its header from acpi_bus.h to the ACPI core's internal header file internal.h. Modify the definition of acpi_device_hotplug() so that its first argument is an ACPI device object pointer and modify the definition of struct acpi_hp_work accordingly. Signed-off-by: Rafael J. Wysocki Acked-by: Toshi Kani --- drivers/acpi/bus.c | 2 +- drivers/acpi/internal.h | 3 ++- drivers/acpi/osl.c | 14 ++++++-------- drivers/acpi/scan.c | 6 ++---- include/acpi/acpi_bus.h | 4 ---- 5 files changed, 11 insertions(+), 18 deletions(-) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index e61e7b8a2eaf..afe6f9a919c1 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -400,7 +400,7 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data) case ACPI_NOTIFY_BUS_CHECK: case ACPI_NOTIFY_DEVICE_CHECK: case ACPI_NOTIFY_EJECT_REQUEST: - status = acpi_hotplug_execute(acpi_device_hotplug, adev, type); + status = acpi_hotplug_schedule(adev, type); if (ACPI_SUCCESS(status)) return; default: diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 4d081fc1aa24..957391306cbf 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -78,8 +78,9 @@ void acpi_lpss_init(void); static inline void acpi_lpss_init(void) {} #endif +acpi_status acpi_hotplug_schedule(struct acpi_device *adev, u32 src); bool acpi_queue_hotplug_work(struct work_struct *work); -void acpi_device_hotplug(void *data, u32 src); +void acpi_device_hotplug(struct acpi_device *adev, u32 src); bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent); /* -------------------------------------------------------------------------- diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index fc1aa7909690..afb4be566940 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -1168,8 +1168,7 @@ void acpi_os_wait_events_complete(void) struct acpi_hp_work { struct work_struct work; - acpi_hp_callback func; - void *data; + struct acpi_device *adev; u32 src; }; @@ -1178,25 +1177,24 @@ static void acpi_hotplug_work_fn(struct work_struct *work) struct acpi_hp_work *hpw = container_of(work, struct acpi_hp_work, work); acpi_os_wait_events_complete(); - hpw->func(hpw->data, hpw->src); + acpi_device_hotplug(hpw->adev, hpw->src); kfree(hpw); } -acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src) +acpi_status acpi_hotplug_schedule(struct acpi_device *adev, u32 src) { struct acpi_hp_work *hpw; ACPI_DEBUG_PRINT((ACPI_DB_EXEC, - "Scheduling function [%p(%p, %u)] for deferred execution.\n", - func, data, src)); + "Scheduling hotplug event (%p, %u) for deferred execution.\n", + adev, src)); hpw = kmalloc(sizeof(*hpw), GFP_KERNEL); if (!hpw) return AE_NO_MEMORY; INIT_WORK(&hpw->work, acpi_hotplug_work_fn); - hpw->func = func; - hpw->data = data; + hpw->adev = adev; hpw->src = src; /* * We can't run hotplug code in kacpid_wq/kacpid_notify_wq etc., because diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index f6bcc24f73dc..eb7a1ff224e7 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -481,10 +481,9 @@ static int acpi_generic_hotplug_event(struct acpi_device *adev, u32 type) return -EINVAL; } -void acpi_device_hotplug(void *data, u32 src) +void acpi_device_hotplug(struct acpi_device *adev, u32 src) { u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; - struct acpi_device *adev = data; int error = -ENODEV; lock_device_hotplug(); @@ -579,8 +578,7 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, return -ENODEV; get_device(&acpi_device->dev); - status = acpi_hotplug_execute(acpi_device_hotplug, acpi_device, - ACPI_OST_EC_OSPM_EJECT); + status = acpi_hotplug_schedule(acpi_device, ACPI_OST_EC_OSPM_EJECT); if (ACPI_SUCCESS(status)) return count; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index fb23a7b6c919..660f5056a37f 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -455,10 +455,6 @@ static inline bool acpi_device_enumerated(struct acpi_device *adev) return adev && adev->flags.initialized && adev->flags.visited; } -typedef void (*acpi_hp_callback)(void *data, u32 src); - -acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src); - /** * module_acpi_driver(acpi_driver) - Helper macro for registering an ACPI driver * @__acpi_driver: acpi_driver struct