From 805d410fb0dbd65e1a57a810858fa2491e75822d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 21 Dec 2012 00:36:39 +0100 Subject: ACPI: Separate adding ACPI device objects from probing ACPI drivers Split the ACPI namespace scanning for devices into two passes, such that struct acpi_device objects are registerd in the first pass without probing ACPI drivers and the drivers are probed against them directly in the second pass. There are two main reasons for doing that. First, the ACPI PCI root bridge driver's .add() routine, acpi_pci_root_add(), causes struct pci_dev objects to be created for all PCI devices under the given root bridge. Usually, there are corresponding ACPI device nodes in the ACPI namespace for some of those devices and therefore there should be "companion" struct acpi_device objects to attach those struct pci_dev objects to. These struct acpi_device objects should exist when the corresponding struct pci_dev objects are created, but that is only guaranteed during boot and not during hotplug. This leads to a number of functional differences between the boot and the hotplug cases which are not strictly necessary and make the code more complicated. For example, this forces the ACPI PCI root bridge driver to defer the registration of the just created struct pci_dev objects and to use a special .start() callback routine, acpi_pci_root_start(), to make sure that all of the "companion" struct acpi_device objects will be present at PCI devices registration time during hotplug. If those differences can be eliminated, we will be able to consolidate the boot and hotplug code paths for the enumeration and registration of PCI devices and to reduce the complexity of that code quite a bit. The second reason is that, in general, it should be possible to resolve conflicts of resources assigned by the BIOS to different devices represented by ACPI namespace nodes before any drivers bind to them and before they are attached to "companion" objects representing physical devices (such as struct pci_dev). However, for this purpose we first need to enumerate all ACPI device nodes in the given namespace scope. Signed-off-by: Rafael J. Wysocki Acked-by: Yinghai Lu Acked-by: Toshi Kani --- drivers/acpi/scan.c | 103 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 72 insertions(+), 31 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 53502d1bbf26..726f0d1ace4b 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -494,7 +494,8 @@ static int acpi_bus_match(struct device *dev, struct device_driver *drv) struct acpi_device *acpi_dev = to_acpi_device(dev); struct acpi_driver *acpi_drv = to_acpi_driver(drv); - return !acpi_match_device_ids(acpi_dev, acpi_drv->ids); + return acpi_dev->bus_ops.acpi_op_match + && !acpi_match_device_ids(acpi_dev, acpi_drv->ids); } static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env) @@ -1418,6 +1419,17 @@ static int acpi_bus_remove(struct acpi_device *dev, int rmdevice) return 0; } +/* + * acpi_hot_add_bind - Bind _ADR-based devices on hot-add. + * @device: ACPI device node to bind. + */ +static void acpi_hot_add_bind(struct acpi_device *device) +{ + if (device->flags.bus_address + && device->parent && device->parent->ops.bind) + device->parent->ops.bind(device); +} + static int acpi_add_single_object(struct acpi_device **child, acpi_handle handle, int type, unsigned long long sta, @@ -1490,13 +1502,8 @@ static int acpi_add_single_object(struct acpi_device **child, result = acpi_device_register(device); - /* - * Bind _ADR-Based Devices when hot add - */ - if (device->flags.bus_address) { - if (device->parent && device->parent->ops.bind) - device->parent->ops.bind(device); - } + if (device->bus_ops.acpi_op_match) + acpi_hot_add_bind(device); end: if (!result) { @@ -1522,6 +1529,7 @@ static void acpi_bus_add_power_resource(acpi_handle handle) struct acpi_bus_ops ops = { .acpi_op_add = 1, .acpi_op_start = 1, + .acpi_op_match = 1, }; struct acpi_device *device = NULL; @@ -1574,9 +1582,9 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl, void *context, void **return_value) { struct acpi_bus_ops *ops = context; + struct acpi_device *device = NULL; int type; unsigned long long sta; - struct acpi_device *device; acpi_status status; int result; @@ -1596,52 +1604,84 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl, return AE_CTRL_DEPTH; } - /* - * We may already have an acpi_device from a previous enumeration. If - * so, we needn't add it again, but we may still have to start it. - */ - device = NULL; acpi_bus_get_device(handle, &device); if (ops->acpi_op_add && !device) { - acpi_add_single_object(&device, handle, type, sta, ops); - /* Is the device a known good platform device? */ - if (device - && !acpi_match_device_ids(device, acpi_platform_device_ids)) - acpi_create_platform_device(device); - } + struct acpi_bus_ops add_ops = *ops; - if (!device) - return AE_CTRL_DEPTH; - - if (ops->acpi_op_start && !(ops->acpi_op_add)) { - status = acpi_start_single_object(device); - if (ACPI_FAILURE(status)) + add_ops.acpi_op_match = 0; + acpi_add_single_object(&device, handle, type, sta, &add_ops); + if (!device) return AE_CTRL_DEPTH; + + device->bus_ops.acpi_op_match = 1; + acpi_hot_add_bind(device); } if (!*return_value) *return_value = device; + return AE_OK; } +static acpi_status acpi_bus_probe_start(acpi_handle handle, u32 lvl, + void *context, void **not_used) +{ + struct acpi_bus_ops *ops = context; + acpi_status status = AE_OK; + struct acpi_device *device; + unsigned long long sta_not_used; + int type_not_used; + + /* + * Ignore errors ignored by acpi_bus_check_add() to avoid terminating + * namespace walks prematurely. + */ + if (acpi_bus_type_and_status(handle, &type_not_used, &sta_not_used)) + return AE_OK; + + if (acpi_bus_get_device(handle, &device)) + return AE_CTRL_DEPTH; + + if (ops->acpi_op_add) { + if (!acpi_match_device_ids(device, acpi_platform_device_ids)) { + /* This is a known good platform device. */ + acpi_create_platform_device(device); + } else if (device_attach(&device->dev)) { + status = AE_CTRL_DEPTH; + } + } else if (ops->acpi_op_start) { + if (ACPI_FAILURE(acpi_start_single_object(device))) + status = AE_CTRL_DEPTH; + } + return status; +} + static int acpi_bus_scan(acpi_handle handle, struct acpi_bus_ops *ops, struct acpi_device **child) { - acpi_status status; void *device = NULL; + acpi_status status; + int ret = -ENODEV; status = acpi_bus_check_add(handle, 0, ops, &device); if (ACPI_SUCCESS(status)) acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, acpi_bus_check_add, NULL, ops, &device); + if (!device) + goto out; + + ret = 0; + status = acpi_bus_probe_start(handle, 0, ops, NULL); + if (ACPI_SUCCESS(status)) + acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, + acpi_bus_probe_start, NULL, ops, NULL); + + out: if (child) *child = device; - if (device) - return 0; - else - return -ENODEV; + return ret; } /* @@ -1752,6 +1792,7 @@ static int acpi_bus_scan_fixed(void) memset(&ops, 0, sizeof(ops)); ops.acpi_op_add = 1; ops.acpi_op_start = 1; + ops.acpi_op_match = 1; /* * Enumerate all fixed-feature devices. -- cgit v1.2.3 From 92ef2a25c763338905dce8344a0584606f842920 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 21 Dec 2012 00:36:40 +0100 Subject: ACPI: Change the ordering of PCI root bridge driver registrarion Instead of running acpi_pci_root_init() from a separate subsys initcall, call it directly from acpi_scan_init() before scanning the ACPI namespace for the first time, so that the PCI root bridge driver's .add() routine, acpi_pci_root_start(), is always run before binding ACPI drivers or attaching "companion" device objects to struct acpi_device objects below the root bridge's device node in the ACPI namespace. The first, simpler reason for doing this is that it makes the situation during boot more similar to the situation during hotplug, in which the ACPI PCI root bridge driver is always present. The second reason is that acpi_pci_root_init() causes struct pci_dev objects to be created for all PCI devices below the bridge and these objects may be necessary for whatever is done with the other ACPI device nodes in that namespace scope. For example, devices created by acpi_create_platform_device() sometimes may need to be added to the device hierarchy as children of PCI bridges. For this purpose, however, the struct pci_dev objects representing those bridges need to exist before the platform devices in question are registered. Signed-off-by: Rafael J. Wysocki Acked-by: Yinghai Lu Acked-by: Toshi Kani --- drivers/acpi/internal.h | 1 + drivers/acpi/pci_root.c | 4 +--- drivers/acpi/scan.c | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 3c407cdc1ec1..e050254ae143 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -67,6 +67,7 @@ struct acpi_ec { extern struct acpi_ec *first_ec; +int acpi_pci_root_init(void); int acpi_ec_init(void); int acpi_ec_ecdt_probe(void); int acpi_boot_ec_enable(void); diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 7928d4dc7056..a233fe93dfac 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -699,7 +699,7 @@ static int acpi_pci_root_remove(struct acpi_device *device, int type) return 0; } -static int __init acpi_pci_root_init(void) +int __init acpi_pci_root_init(void) { acpi_hest_init(); @@ -712,5 +712,3 @@ static int __init acpi_pci_root_init(void) return 0; } - -subsys_initcall(acpi_pci_root_init); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 726f0d1ace4b..a7e81ac2cd0c 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1831,6 +1831,7 @@ int __init acpi_scan_init(void) } acpi_power_init(); + acpi_pci_root_init(); /* * Enumerate devices in the ACPI namespace. -- cgit v1.2.3 From 0fc300b0537c6a6a7f2b261b6c339dc498cd1702 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 21 Dec 2012 00:36:41 +0100 Subject: ACPI: Make acpi_bus_add() and acpi_bus_start() visibly different The current ACPI namespace scanning code suggests that acpi_bus_add() and acpi_bus_start() share some code. In fact, however, they are completely different code paths (except for the initial checks), so refactor the code to make that distinction visibly clear. Signed-off-by: Rafael J. Wysocki Acked-by: Yinghai Lu Acked-by: Toshi Kani --- drivers/acpi/scan.c | 63 +++++++++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 28 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index a7e81ac2cd0c..f3bcaf6c6bd4 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1623,10 +1623,9 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl, return AE_OK; } -static acpi_status acpi_bus_probe_start(acpi_handle handle, u32 lvl, - void *context, void **not_used) +static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, + void *not_used, void **ret_not_used) { - struct acpi_bus_ops *ops = context; acpi_status status = AE_OK; struct acpi_device *device; unsigned long long sta_not_used; @@ -1642,16 +1641,11 @@ static acpi_status acpi_bus_probe_start(acpi_handle handle, u32 lvl, if (acpi_bus_get_device(handle, &device)) return AE_CTRL_DEPTH; - if (ops->acpi_op_add) { - if (!acpi_match_device_ids(device, acpi_platform_device_ids)) { - /* This is a known good platform device. */ - acpi_create_platform_device(device); - } else if (device_attach(&device->dev)) { - status = AE_CTRL_DEPTH; - } - } else if (ops->acpi_op_start) { - if (ACPI_FAILURE(acpi_start_single_object(device))) - status = AE_CTRL_DEPTH; + if (!acpi_match_device_ids(device, acpi_platform_device_ids)) { + /* This is a known good platform device. */ + acpi_create_platform_device(device); + } else if (device_attach(&device->dev)) { + status = AE_CTRL_DEPTH; } return status; } @@ -1672,10 +1666,10 @@ static int acpi_bus_scan(acpi_handle handle, struct acpi_bus_ops *ops, goto out; ret = 0; - status = acpi_bus_probe_start(handle, 0, ops, NULL); + status = acpi_bus_device_attach(handle, 0, NULL, NULL); if (ACPI_SUCCESS(status)) acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, - acpi_bus_probe_start, NULL, ops, NULL); + acpi_bus_device_attach, NULL, NULL, NULL); out: if (child) @@ -1700,31 +1694,44 @@ int acpi_bus_add(struct acpi_device **child, struct acpi_device *parent, acpi_handle handle, int type) { - struct acpi_bus_ops ops; - - memset(&ops, 0, sizeof(ops)); - ops.acpi_op_add = 1; + struct acpi_bus_ops ops = { .acpi_op_add = 1, }; return acpi_bus_scan(handle, &ops, child); } EXPORT_SYMBOL(acpi_bus_add); -int acpi_bus_start(struct acpi_device *device) +static acpi_status acpi_bus_start_device(acpi_handle handle, u32 lvl, + void *not_used, void **ret_not_used) { - struct acpi_bus_ops ops; - int result; + struct acpi_device *device; + unsigned long long sta_not_used; + int type_not_used; + + /* + * Ignore errors ignored by acpi_bus_check_add() to avoid terminating + * namespace walks prematurely. + */ + if (acpi_bus_type_and_status(handle, &type_not_used, &sta_not_used)) + return AE_OK; + + if (acpi_bus_get_device(handle, &device)) + return AE_CTRL_DEPTH; + + return acpi_start_single_object(device); +} +int acpi_bus_start(struct acpi_device *device) +{ if (!device) return -EINVAL; - memset(&ops, 0, sizeof(ops)); - ops.acpi_op_start = 1; - - result = acpi_bus_scan(device->handle, &ops, NULL); + if (ACPI_SUCCESS(acpi_start_single_object(device))) + acpi_walk_namespace(ACPI_TYPE_ANY, device->handle, + ACPI_UINT32_MAX, acpi_bus_start_device, + NULL, NULL, NULL); acpi_update_all_gpes(); - - return result; + return 0; } EXPORT_SYMBOL(acpi_bus_start); -- cgit v1.2.3 From ca7b3c4f3d85bf7ec225eebb43b6af0a25499c6c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 21 Dec 2012 00:36:42 +0100 Subject: ACPI: Reduce the usage of struct acpi_bus_ops Objects of type struct acpi_bus_ops are currently used to pass information between different parts of the ACPI namespace scanning code, sometimes in quite convoluted ways. It turns out that that is not necessary in some cases, so simplify the code by reducing the utilization of struct acpi_bus_ops objects where clearly possible. Signed-off-by: Rafael J. Wysocki Acked-by: Yinghai Lu Acked-by: Toshi Kani --- drivers/acpi/scan.c | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index f3bcaf6c6bd4..8dde0e7f68d8 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1527,7 +1527,6 @@ end: static void acpi_bus_add_power_resource(acpi_handle handle) { struct acpi_bus_ops ops = { - .acpi_op_add = 1, .acpi_op_start = 1, .acpi_op_match = 1, }; @@ -1581,7 +1580,6 @@ static int acpi_bus_type_and_status(acpi_handle handle, int *type, static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl, void *context, void **return_value) { - struct acpi_bus_ops *ops = context; struct acpi_device *device = NULL; int type; unsigned long long sta; @@ -1605,11 +1603,13 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl, } acpi_bus_get_device(handle, &device); - if (ops->acpi_op_add && !device) { - struct acpi_bus_ops add_ops = *ops; + if (!device) { + struct acpi_bus_ops ops = { + .acpi_op_start = !!context, + .acpi_op_match = 0, + }; - add_ops.acpi_op_match = 0; - acpi_add_single_object(&device, handle, type, sta, &add_ops); + acpi_add_single_object(&device, handle, type, sta, &ops); if (!device) return AE_CTRL_DEPTH; @@ -1650,17 +1650,18 @@ static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, return status; } -static int acpi_bus_scan(acpi_handle handle, struct acpi_bus_ops *ops, +static int acpi_bus_scan(acpi_handle handle, bool start, struct acpi_device **child) { void *device = NULL; acpi_status status; int ret = -ENODEV; - status = acpi_bus_check_add(handle, 0, ops, &device); + status = acpi_bus_check_add(handle, 0, (void *)start, &device); if (ACPI_SUCCESS(status)) acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, - acpi_bus_check_add, NULL, ops, &device); + acpi_bus_check_add, NULL, (void *)start, + &device); if (!device) goto out; @@ -1694,9 +1695,7 @@ int acpi_bus_add(struct acpi_device **child, struct acpi_device *parent, acpi_handle handle, int type) { - struct acpi_bus_ops ops = { .acpi_op_add = 1, }; - - return acpi_bus_scan(handle, &ops, child); + return acpi_bus_scan(handle, false, child); } EXPORT_SYMBOL(acpi_bus_add); @@ -1794,12 +1793,10 @@ static int acpi_bus_scan_fixed(void) { int result = 0; struct acpi_device *device = NULL; - struct acpi_bus_ops ops; - - memset(&ops, 0, sizeof(ops)); - ops.acpi_op_add = 1; - ops.acpi_op_start = 1; - ops.acpi_op_match = 1; + struct acpi_bus_ops ops = { + .acpi_op_start = 1, + .acpi_op_match = 1, + }; /* * Enumerate all fixed-feature devices. @@ -1825,11 +1822,6 @@ static int acpi_bus_scan_fixed(void) int __init acpi_scan_init(void) { int result; - struct acpi_bus_ops ops; - - memset(&ops, 0, sizeof(ops)); - ops.acpi_op_add = 1; - ops.acpi_op_start = 1; result = bus_register(&acpi_bus_type); if (result) { @@ -1843,7 +1835,7 @@ int __init acpi_scan_init(void) /* * Enumerate devices in the ACPI namespace. */ - result = acpi_bus_scan(ACPI_ROOT_OBJECT, &ops, &acpi_root); + result = acpi_bus_scan(ACPI_ROOT_OBJECT, true, &acpi_root); if (!result) result = acpi_bus_scan_fixed(); -- cgit v1.2.3 From a2d06a1a0851fb3d7e775b9d878cdffb9e0300ee Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 21 Dec 2012 00:36:43 +0100 Subject: ACPI: Replace struct acpi_bus_ops with enum type Notice that one member of struct acpi_bus_ops, acpi_op_add, is not used anywhere any more and the relationship between its remaining members, acpi_op_match and acpi_op_start, is such that it doesn't make sense to set the latter without setting the former at the same time. Therefore, replace struct acpi_bus_ops with new a enum type, enum acpi_bus_add_type, with three values, ACPI_BUS_ADD_BASIC, ACPI_BUS_ADD_MATCH, ACPI_BUS_ADD_START, corresponding to both acpi_op_match and acpi_op_start unset, acpi_op_match set and acpi_op_start unset, and both acpi_op_match and acpi_op_start set, respectively. Signed-off-by: Rafael J. Wysocki Acked-by: Yinghai Lu Acked-by: Toshi Kani --- drivers/acpi/scan.c | 35 ++++++++++++----------------------- include/acpi/acpi_bus.h | 15 ++++++++------- 2 files changed, 20 insertions(+), 30 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 8dde0e7f68d8..7082d11ce5d2 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -494,7 +494,7 @@ static int acpi_bus_match(struct device *dev, struct device_driver *drv) struct acpi_device *acpi_dev = to_acpi_device(dev); struct acpi_driver *acpi_drv = to_acpi_driver(drv); - return acpi_dev->bus_ops.acpi_op_match + return acpi_dev->add_type >= ACPI_BUS_ADD_MATCH && !acpi_match_device_ids(acpi_dev, acpi_drv->ids); } @@ -580,7 +580,7 @@ static int acpi_device_probe(struct device * dev) ret = acpi_bus_driver_init(acpi_dev, acpi_drv); if (!ret) { - if (acpi_dev->bus_ops.acpi_op_start) + if (acpi_dev->add_type == ACPI_BUS_ADD_START) acpi_start_single_object(acpi_dev); if (acpi_drv->ops.notify) { @@ -1433,7 +1433,7 @@ static void acpi_hot_add_bind(struct acpi_device *device) static int acpi_add_single_object(struct acpi_device **child, acpi_handle handle, int type, unsigned long long sta, - struct acpi_bus_ops *ops) + enum acpi_bus_add_type add_type) { int result; struct acpi_device *device; @@ -1449,7 +1449,7 @@ static int acpi_add_single_object(struct acpi_device **child, device->device_type = type; device->handle = handle; device->parent = acpi_bus_get_parent(handle); - device->bus_ops = *ops; /* workround for not call .start */ + device->add_type = add_type; STRUCT_TO_INT(device->status) = sta; acpi_device_get_busid(device); @@ -1502,7 +1502,7 @@ static int acpi_add_single_object(struct acpi_device **child, result = acpi_device_register(device); - if (device->bus_ops.acpi_op_match) + if (device->add_type >= ACPI_BUS_ADD_MATCH) acpi_hot_add_bind(device); end: @@ -1526,16 +1526,12 @@ end: static void acpi_bus_add_power_resource(acpi_handle handle) { - struct acpi_bus_ops ops = { - .acpi_op_start = 1, - .acpi_op_match = 1, - }; struct acpi_device *device = NULL; acpi_bus_get_device(handle, &device); if (!device) acpi_add_single_object(&device, handle, ACPI_BUS_TYPE_POWER, - ACPI_STA_DEFAULT, &ops); + ACPI_STA_DEFAULT, ACPI_BUS_ADD_START); } static int acpi_bus_type_and_status(acpi_handle handle, int *type, @@ -1604,16 +1600,13 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl, acpi_bus_get_device(handle, &device); if (!device) { - struct acpi_bus_ops ops = { - .acpi_op_start = !!context, - .acpi_op_match = 0, - }; - - acpi_add_single_object(&device, handle, type, sta, &ops); + acpi_add_single_object(&device, handle, type, sta, + ACPI_BUS_ADD_BASIC); if (!device) return AE_CTRL_DEPTH; - device->bus_ops.acpi_op_match = 1; + device->add_type = context ? + ACPI_BUS_ADD_START : ACPI_BUS_ADD_MATCH; acpi_hot_add_bind(device); } @@ -1793,10 +1786,6 @@ static int acpi_bus_scan_fixed(void) { int result = 0; struct acpi_device *device = NULL; - struct acpi_bus_ops ops = { - .acpi_op_start = 1, - .acpi_op_match = 1, - }; /* * Enumerate all fixed-feature devices. @@ -1805,7 +1794,7 @@ static int acpi_bus_scan_fixed(void) result = acpi_add_single_object(&device, NULL, ACPI_BUS_TYPE_POWER_BUTTON, ACPI_STA_DEFAULT, - &ops); + ACPI_BUS_ADD_START); device_init_wakeup(&device->dev, true); } @@ -1813,7 +1802,7 @@ static int acpi_bus_scan_fixed(void) result = acpi_add_single_object(&device, NULL, ACPI_BUS_TYPE_SLEEP_BUTTON, ACPI_STA_DEFAULT, - &ops); + ACPI_BUS_ADD_START); } return result; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 016918c16c98..d5294798fdda 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -63,6 +63,13 @@ acpi_get_physical_device_location(acpi_handle handle, struct acpi_pld_info **pld #define ACPI_BUS_FILE_ROOT "acpi" extern struct proc_dir_entry *acpi_root_dir; +enum acpi_bus_add_type { + ACPI_BUS_ADD_BASIC = 0, + ACPI_BUS_ADD_MATCH, + ACPI_BUS_ADD_START, + ACPI_BUS_ADD_TYPE_COUNT +}; + enum acpi_bus_removal_type { ACPI_BUS_REMOVAL_NORMAL = 0, ACPI_BUS_REMOVAL_EJECT, @@ -95,12 +102,6 @@ typedef int (*acpi_op_bind) (struct acpi_device * device); typedef int (*acpi_op_unbind) (struct acpi_device * device); typedef void (*acpi_op_notify) (struct acpi_device * device, u32 event); -struct acpi_bus_ops { - u32 acpi_op_add:1; - u32 acpi_op_start:1; - u32 acpi_op_match:1; -}; - struct acpi_device_ops { acpi_op_add add; acpi_op_remove remove; @@ -284,7 +285,7 @@ struct acpi_device { struct acpi_driver *driver; void *driver_data; struct device dev; - struct acpi_bus_ops bus_ops; /* workaround for different code path for hotplug */ + enum acpi_bus_add_type add_type; /* how to handle adding */ enum acpi_bus_removal_type removal_type; /* indicate for different removal type */ u8 physical_node_count; struct list_head physical_node_list; -- cgit v1.2.3 From 4002bf384cc567a843a7d16c95db54a7d77b16c6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 21 Dec 2012 00:36:44 +0100 Subject: ACPI: Change the ordering of acpi_bus_check_add() If acpi_bus_check_add() is called for a handle already having an existing struct acpi_device object attached, it is not necessary to check the type and status of the device correspondig to it, so change the ordering of acpi_bus_check_add() to avoid that. Signed-off-by: Rafael J. Wysocki Acked-by: Yinghai Lu Acked-by: Toshi Kani --- drivers/acpi/scan.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 7082d11ce5d2..eb7ecb1f2032 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1582,6 +1582,10 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl, acpi_status status; int result; + acpi_bus_get_device(handle, &device); + if (device) + goto out; + result = acpi_bus_type_and_status(handle, &type, &sta); if (result) return AE_OK; @@ -1598,18 +1602,14 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl, return AE_CTRL_DEPTH; } - acpi_bus_get_device(handle, &device); - if (!device) { - acpi_add_single_object(&device, handle, type, sta, - ACPI_BUS_ADD_BASIC); - if (!device) - return AE_CTRL_DEPTH; + acpi_add_single_object(&device, handle, type, sta, ACPI_BUS_ADD_BASIC); + if (!device) + return AE_CTRL_DEPTH; - device->add_type = context ? - ACPI_BUS_ADD_START : ACPI_BUS_ADD_MATCH; - acpi_hot_add_bind(device); - } + device->add_type = context ? ACPI_BUS_ADD_START : ACPI_BUS_ADD_MATCH; + acpi_hot_add_bind(device); + out: if (!*return_value) *return_value = device; -- cgit v1.2.3 From 02f57c67a8677ae55dcdd256a2a7abaf41e4cc1f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 21 Dec 2012 00:36:46 +0100 Subject: ACPI: Remove acpi_start_single_object() and acpi_bus_start() The ACPI PCI root bridge driver was the only ACPI driver implementing the .start() callback, which isn't used by any ACPI drivers any more now. For this reason, acpi_start_single_object() has no purpose any more, so remove it and all references to it. Also remove acpi_bus_start_device(), whose only purpose was to call acpi_start_single_object(). Moreover, since after the removal of acpi_bus_start_device() the only purpose of acpi_bus_start() remains to call acpi_update_all_gpes(), move that into acpi_bus_add() and drop acpi_bus_start() too, remove its header from acpi_bus.h and update all of its former users accordingly. This change was previously proposed in a different from by Yinghai Lu. Signed-off-by: Rafael J. Wysocki Acked-by: Yinghai Lu Acked-by: Toshi Kani --- drivers/acpi/container.c | 16 ++------- drivers/acpi/scan.c | 66 +++++--------------------------------- drivers/pci/hotplug/acpiphp_glue.c | 4 +-- drivers/pci/hotplug/sgi_hotplug.c | 2 -- include/acpi/acpi_bus.h | 1 - 5 files changed, 12 insertions(+), 77 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index 811910b50b75..98b85e3e4781 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -139,24 +139,14 @@ static int container_device_add(struct acpi_device **device, acpi_handle handle) { acpi_handle phandle; struct acpi_device *pdev; - int result; - - - if (acpi_get_parent(handle, &phandle)) { - return -ENODEV; - } - if (acpi_bus_get_device(phandle, &pdev)) { + if (acpi_get_parent(handle, &phandle)) return -ENODEV; - } - if (acpi_bus_add(device, pdev, handle, ACPI_BUS_TYPE_DEVICE)) { + if (acpi_bus_get_device(phandle, &pdev)) return -ENODEV; - } - - result = acpi_bus_start(*device); - return result; + return acpi_bus_add(device, pdev, handle, ACPI_BUS_TYPE_DEVICE); } static void container_notify_cb(acpi_handle handle, u32 type, void *context) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index eb7ecb1f2032..6321cab4e5ff 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -571,7 +571,6 @@ static void acpi_device_remove_notify_handler(struct acpi_device *device) } static int acpi_bus_driver_init(struct acpi_device *, struct acpi_driver *); -static int acpi_start_single_object(struct acpi_device *); static int acpi_device_probe(struct device * dev) { struct acpi_device *acpi_dev = to_acpi_device(dev); @@ -580,9 +579,6 @@ static int acpi_device_probe(struct device * dev) ret = acpi_bus_driver_init(acpi_dev, acpi_drv); if (!ret) { - if (acpi_dev->add_type == ACPI_BUS_ADD_START) - acpi_start_single_object(acpi_dev); - if (acpi_drv->ops.notify) { ret = acpi_device_install_notify_handler(acpi_dev); if (ret) { @@ -761,24 +757,6 @@ acpi_bus_driver_init(struct acpi_device *device, struct acpi_driver *driver) return 0; } -static int acpi_start_single_object(struct acpi_device *device) -{ - int result = 0; - struct acpi_driver *driver; - - - if (!(driver = device->driver)) - return 0; - - if (driver->ops.start) { - result = driver->ops.start(device); - if (result && driver->ops.remove) - driver->ops.remove(device, ACPI_BUS_REMOVAL_NORMAL); - } - - return result; -} - /** * acpi_bus_register_driver - register a driver with the ACPI bus * @driver: driver being registered @@ -1673,59 +1651,31 @@ static int acpi_bus_scan(acpi_handle handle, bool start, } /* - * acpi_bus_add and acpi_bus_start + * acpi_bus_add * * scan a given ACPI tree and (probably recently hot-plugged) - * create and add or starts found devices. + * create and add found devices. * * If no devices were found -ENODEV is returned which does not * mean that this is a real error, there just have been no suitable * ACPI objects in the table trunk from which the kernel could create - * a device and add/start an appropriate driver. + * a device and add an appropriate driver. */ int acpi_bus_add(struct acpi_device **child, struct acpi_device *parent, acpi_handle handle, int type) { - return acpi_bus_scan(handle, false, child); -} -EXPORT_SYMBOL(acpi_bus_add); - -static acpi_status acpi_bus_start_device(acpi_handle handle, u32 lvl, - void *not_used, void **ret_not_used) -{ - struct acpi_device *device; - unsigned long long sta_not_used; - int type_not_used; - - /* - * Ignore errors ignored by acpi_bus_check_add() to avoid terminating - * namespace walks prematurely. - */ - if (acpi_bus_type_and_status(handle, &type_not_used, &sta_not_used)) - return AE_OK; - - if (acpi_bus_get_device(handle, &device)) - return AE_CTRL_DEPTH; - - return acpi_start_single_object(device); -} - -int acpi_bus_start(struct acpi_device *device) -{ - if (!device) - return -EINVAL; + int err; - if (ACPI_SUCCESS(acpi_start_single_object(device))) - acpi_walk_namespace(ACPI_TYPE_ANY, device->handle, - ACPI_UINT32_MAX, acpi_bus_start_device, - NULL, NULL, NULL); + err = acpi_bus_scan(handle, false, child); + if (err) + return err; acpi_update_all_gpes(); return 0; } -EXPORT_SYMBOL(acpi_bus_start); +EXPORT_SYMBOL(acpi_bus_add); int acpi_bus_trim(struct acpi_device *start, int rmdevice) { diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 3d6d4fd1e3c5..7e2bad4c2fcc 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -759,7 +759,6 @@ static int acpiphp_bus_add(struct acpiphp_func *func) -ret_val); goto acpiphp_bus_add_out; } - ret_val = acpi_bus_start(device); acpiphp_bus_add_out: return ret_val; @@ -1148,8 +1147,7 @@ static void handle_bridge_insertion(acpi_handle handle, u32 type) err("cannot add bridge to acpi list\n"); return; } - if (!acpiphp_configure_bridge(handle) && - !acpi_bus_start(device)) + if (!acpiphp_configure_bridge(handle)) add_bridge(handle); else err("cannot configure and start bridge\n"); diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c index f64ca92253da..20c960d5317d 100644 --- a/drivers/pci/hotplug/sgi_hotplug.c +++ b/drivers/pci/hotplug/sgi_hotplug.c @@ -457,8 +457,6 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot) ret, (int)(adr>>16), (int)(adr&0xffff)); /* try to continue on */ - } else { - acpi_bus_start(device); } } } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index d5294798fdda..6d47a584c0bc 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -362,7 +362,6 @@ int acpi_bus_add(struct acpi_device **child, struct acpi_device *parent, acpi_handle handle, int type); void acpi_bus_hot_remove_device(void *context); int acpi_bus_trim(struct acpi_device *start, int rmdevice); -int acpi_bus_start(struct acpi_device *device); acpi_status acpi_bus_get_ejd(acpi_handle handle, acpi_handle * ejd); int acpi_match_device_ids(struct acpi_device *device, const struct acpi_device_id *ids); -- cgit v1.2.3 From 636458de36f1fb4cdd318387d2f45604e451b17a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 21 Dec 2012 00:36:47 +0100 Subject: ACPI: Remove the arguments of acpi_bus_add() that are not used Notice that acpi_bus_add() uses only 2 of its 4 arguments and redefine its header to match the body. Update all of its callers as necessary and observe that this leads to quite a number of removed lines of code (Linus will like that). Add a kerneldoc comment documenting acpi_bus_add() and wonder how its callers make wrong assumptions about the second argument (make note to self to take care of that later). Signed-off-by: Rafael J. Wysocki Acked-by: Yinghai Lu Acked-by: Toshi Kani --- drivers/acpi/acpi_memhotplug.c | 19 +------------------ drivers/acpi/container.c | 16 +--------------- drivers/acpi/dock.c | 13 ++----------- drivers/acpi/processor_driver.c | 24 +----------------------- drivers/acpi/scan.c | 34 +++++++++++++++++++++------------- drivers/pci/hotplug/acpiphp_glue.c | 21 ++++----------------- drivers/pci/hotplug/sgi_hotplug.c | 3 +-- include/acpi/acpi_bus.h | 3 +-- 8 files changed, 32 insertions(+), 101 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index eb30e5ab4cab..d0a7da704d49 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c @@ -157,34 +157,17 @@ static int acpi_memory_get_device(acpi_handle handle, struct acpi_memory_device **mem_device) { - acpi_status status; - acpi_handle phandle; struct acpi_device *device = NULL; - struct acpi_device *pdevice = NULL; int result; - if (!acpi_bus_get_device(handle, &device) && device) goto end; - status = acpi_get_parent(handle, &phandle); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Cannot find acpi parent")); - return -EINVAL; - } - - /* Get the parent device */ - result = acpi_bus_get_device(phandle, &pdevice); - if (result) { - acpi_handle_warn(phandle, "Cannot get acpi bus device\n"); - return -EINVAL; - } - /* * Now add the notified device. This creates the acpi_device * and invokes .add function */ - result = acpi_bus_add(&device, pdevice, handle, ACPI_BUS_TYPE_DEVICE); + result = acpi_bus_add(handle, &device); if (result) { acpi_handle_warn(handle, "Cannot add acpi bus\n"); return -EINVAL; diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index 98b85e3e4781..0688f83bc436 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -135,20 +135,6 @@ static int acpi_container_remove(struct acpi_device *device, int type) return status; } -static int container_device_add(struct acpi_device **device, acpi_handle handle) -{ - acpi_handle phandle; - struct acpi_device *pdev; - - if (acpi_get_parent(handle, &phandle)) - return -ENODEV; - - if (acpi_bus_get_device(phandle, &pdev)) - return -ENODEV; - - return acpi_bus_add(device, pdev, handle, ACPI_BUS_TYPE_DEVICE); -} - static void container_notify_cb(acpi_handle handle, u32 type, void *context) { struct acpi_device *device = NULL; @@ -180,7 +166,7 @@ static void container_notify_cb(acpi_handle handle, u32 type, void *context) if (!ACPI_FAILURE(status) || device) break; - result = container_device_add(&device, handle); + result = acpi_bus_add(handle, &device); if (result) { acpi_handle_warn(handle, "Failed to add container\n"); break; diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index f32bd47b35e0..ff30582d2e1d 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -310,8 +310,6 @@ static int dock_present(struct dock_station *ds) static struct acpi_device * dock_create_acpi_device(acpi_handle handle) { struct acpi_device *device; - struct acpi_device *parent_device; - acpi_handle parent; int ret; if (acpi_bus_get_device(handle, &device)) { @@ -319,16 +317,9 @@ static struct acpi_device * dock_create_acpi_device(acpi_handle handle) * no device created for this object, * so we should create one. */ - acpi_get_parent(handle, &parent); - if (acpi_bus_get_device(parent, &parent_device)) - parent_device = NULL; - - ret = acpi_bus_add(&device, parent_device, handle, - ACPI_BUS_TYPE_DEVICE); - if (ret) { + ret = acpi_bus_add(handle, &device); + if (ret) pr_debug("error adding bus, %x\n", -ret); - return NULL; - } } return device; } diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index e83311bf1ebd..18b292e12b38 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -677,28 +677,6 @@ static int is_processor_present(acpi_handle handle) return 0; } -static -int acpi_processor_device_add(acpi_handle handle, struct acpi_device **device) -{ - acpi_handle phandle; - struct acpi_device *pdev; - - - if (acpi_get_parent(handle, &phandle)) { - return -ENODEV; - } - - if (acpi_bus_get_device(phandle, &pdev)) { - return -ENODEV; - } - - if (acpi_bus_add(device, pdev, handle, ACPI_BUS_TYPE_PROCESSOR)) { - return -ENODEV; - } - - return 0; -} - static void acpi_processor_hotplug_notify(acpi_handle handle, u32 event, void *data) { @@ -721,7 +699,7 @@ static void acpi_processor_hotplug_notify(acpi_handle handle, if (!acpi_bus_get_device(handle, &device)) break; - result = acpi_processor_device_add(handle, &device); + result = acpi_bus_add(handle, &device); if (result) { acpi_handle_err(handle, "Unable to add the device\n"); break; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 6321cab4e5ff..25095bf57bba 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1650,25 +1650,33 @@ static int acpi_bus_scan(acpi_handle handle, bool start, return ret; } -/* - * acpi_bus_add +/** + * acpi_bus_add - Add ACPI device node objects in a given namespace scope. + * @handle: Root of the namespace scope to scan. + * @ret: Location to store a return struct acpi_device pointer. + * + * Scan a given ACPI tree (probably recently hot-plugged) and create and add + * found devices. * - * scan a given ACPI tree and (probably recently hot-plugged) - * create and add found devices. + * If no devices were found, -ENODEV is returned, but it does not mean that + * there has been a real error. There just have been no suitable ACPI objects + * in the table trunk from which the kernel could create a device and add an + * appropriate driver. * - * If no devices were found -ENODEV is returned which does not - * mean that this is a real error, there just have been no suitable - * ACPI objects in the table trunk from which the kernel could create - * a device and add an appropriate driver. + * If 0 is returned, the memory location pointed to by @ret will be populated + * with a pointer to a struct acpi_device created while scanning the namespace. + * If @handle corresponds to a device node, that will be a pointer to the struct + * acpi_device object corresponding to @handle. Otherwise, it will be a pointer + * to a struct acpi_device corresponding to one of its descendants. + * + * If an error code is returned, NULL will be stored in the memory location + * pointed to by @ret. */ - -int -acpi_bus_add(struct acpi_device **child, - struct acpi_device *parent, acpi_handle handle, int type) +int acpi_bus_add(acpi_handle handle, struct acpi_device **ret) { int err; - err = acpi_bus_scan(handle, false, child); + err = acpi_bus_scan(handle, false, ret); if (err) return err; diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 7e2bad4c2fcc..dfc2df54b93a 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -734,15 +734,9 @@ static unsigned char acpiphp_max_busnr(struct pci_bus *bus) */ static int acpiphp_bus_add(struct acpiphp_func *func) { - acpi_handle phandle; - struct acpi_device *device, *pdevice; + struct acpi_device *device; int ret_val; - acpi_get_parent(func->handle, &phandle); - if (acpi_bus_get_device(phandle, &pdevice)) { - dbg("no parent device, assuming NULL\n"); - pdevice = NULL; - } if (!acpi_bus_get_device(func->handle, &device)) { dbg("bus exists... trim\n"); /* this shouldn't be in here, so remove @@ -752,8 +746,7 @@ static int acpiphp_bus_add(struct acpiphp_func *func) dbg("acpi_bus_trim return %x\n", ret_val); } - ret_val = acpi_bus_add(&device, pdevice, func->handle, - ACPI_BUS_TYPE_DEVICE); + ret_val = acpi_bus_add(func->handle, &device); if (ret_val) { dbg("error adding bus, %x\n", -ret_val); @@ -1129,8 +1122,7 @@ static int acpiphp_configure_bridge (acpi_handle handle) static void handle_bridge_insertion(acpi_handle handle, u32 type) { - struct acpi_device *device, *pdevice; - acpi_handle phandle; + struct acpi_device *device; if ((type != ACPI_NOTIFY_BUS_CHECK) && (type != ACPI_NOTIFY_DEVICE_CHECK)) { @@ -1138,12 +1130,7 @@ static void handle_bridge_insertion(acpi_handle handle, u32 type) return; } - acpi_get_parent(handle, &phandle); - if (acpi_bus_get_device(phandle, &pdevice)) { - dbg("no parent device, assuming NULL\n"); - pdevice = NULL; - } - if (acpi_bus_add(&device, pdevice, handle, ACPI_BUS_TYPE_DEVICE)) { + if (acpi_bus_add(handle, &device)) { err("cannot add bridge to acpi list\n"); return; } diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c index 20c960d5317d..801b58d1f78e 100644 --- a/drivers/pci/hotplug/sgi_hotplug.c +++ b/drivers/pci/hotplug/sgi_hotplug.c @@ -448,8 +448,7 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot) if (ACPI_SUCCESS(ret) && (adr>>16) == (slot->device_num + 1)) { - ret = acpi_bus_add(&device, pdevice, chandle, - ACPI_BUS_TYPE_DEVICE); + ret = acpi_bus_add(chandle, &device); if (ACPI_FAILURE(ret)) { printk(KERN_ERR "%s: acpi_bus_add " "failed (0x%x) for slot %d " diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 6d47a584c0bc..75c6b3f2250d 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -358,8 +358,7 @@ static inline int acpi_bus_generate_proc_event(struct acpi_device *device, u8 ty #endif int acpi_bus_register_driver(struct acpi_driver *driver); void acpi_bus_unregister_driver(struct acpi_driver *driver); -int acpi_bus_add(struct acpi_device **child, struct acpi_device *parent, - acpi_handle handle, int type); +int acpi_bus_add(acpi_handle handle, struct acpi_device **ret); void acpi_bus_hot_remove_device(void *context); int acpi_bus_trim(struct acpi_device *start, int rmdevice); acpi_status acpi_bus_get_ejd(acpi_handle handle, acpi_handle * ejd); -- cgit v1.2.3 From e3863094c6f9b2f980d6e7a5cad6b4d03a4dd579 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 21 Dec 2012 00:36:47 +0100 Subject: ACPI: Drop the second argument of acpi_bus_scan() After the removal of acpi_start_single_object() and acpi_bus_start() the second argument of acpi_bus_scan() is not necessary any more, so drop it and update acpi_bus_check_add() accordingly. Signed-off-by: Rafael J. Wysocki Acked-by: Yinghai Lu Acked-by: Toshi Kani --- drivers/acpi/scan.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 25095bf57bba..eb54f98bb728 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1551,8 +1551,8 @@ static int acpi_bus_type_and_status(acpi_handle handle, int *type, return 0; } -static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl, - void *context, void **return_value) +static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, + void *not_used, void **return_value) { struct acpi_device *device = NULL; int type; @@ -1584,7 +1584,7 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl, if (!device) return AE_CTRL_DEPTH; - device->add_type = context ? ACPI_BUS_ADD_START : ACPI_BUS_ADD_MATCH; + device->add_type = ACPI_BUS_ADD_START; acpi_hot_add_bind(device); out: @@ -1621,18 +1621,16 @@ static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, return status; } -static int acpi_bus_scan(acpi_handle handle, bool start, - struct acpi_device **child) +static int acpi_bus_scan(acpi_handle handle, struct acpi_device **child) { void *device = NULL; acpi_status status; int ret = -ENODEV; - status = acpi_bus_check_add(handle, 0, (void *)start, &device); + status = acpi_bus_check_add(handle, 0, NULL, &device); if (ACPI_SUCCESS(status)) acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, - acpi_bus_check_add, NULL, (void *)start, - &device); + acpi_bus_check_add, NULL, NULL, &device); if (!device) goto out; @@ -1676,7 +1674,7 @@ int acpi_bus_add(acpi_handle handle, struct acpi_device **ret) { int err; - err = acpi_bus_scan(handle, false, ret); + err = acpi_bus_scan(handle, ret); if (err) return err; @@ -1782,7 +1780,7 @@ int __init acpi_scan_init(void) /* * Enumerate devices in the ACPI namespace. */ - result = acpi_bus_scan(ACPI_ROOT_OBJECT, true, &acpi_root); + result = acpi_bus_scan(ACPI_ROOT_OBJECT, &acpi_root); if (!result) result = acpi_bus_scan_fixed(); -- cgit v1.2.3 From 209d3b1743c8187c67cc75dbe9fefbcd3121fba0 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 21 Dec 2012 00:36:48 +0100 Subject: ACPI: Replace ACPI device add_type field with a match_driver flag After the removal of the second argument of acpi_bus_scan() there is no difference between the ACPI_BUS_ADD_MATCH and ACPI_BUS_ADD_START add types, so the add_type field in struct acpi_device may be replaced with a single flag. Do that calling the flag match_driver. Signed-off-by: Rafael J. Wysocki Acked-by: Yinghai Lu Acked-by: Toshi Kani --- drivers/acpi/scan.c | 21 +++++++++------------ include/acpi/acpi_bus.h | 11 ++--------- 2 files changed, 11 insertions(+), 21 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index eb54f98bb728..e3aed481aed2 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -494,7 +494,7 @@ static int acpi_bus_match(struct device *dev, struct device_driver *drv) struct acpi_device *acpi_dev = to_acpi_device(dev); struct acpi_driver *acpi_drv = to_acpi_driver(drv); - return acpi_dev->add_type >= ACPI_BUS_ADD_MATCH + return acpi_dev->flags.match_driver && !acpi_match_device_ids(acpi_dev, acpi_drv->ids); } @@ -1410,8 +1410,7 @@ static void acpi_hot_add_bind(struct acpi_device *device) static int acpi_add_single_object(struct acpi_device **child, acpi_handle handle, int type, - unsigned long long sta, - enum acpi_bus_add_type add_type) + unsigned long long sta, bool match_driver) { int result; struct acpi_device *device; @@ -1427,7 +1426,6 @@ static int acpi_add_single_object(struct acpi_device **child, device->device_type = type; device->handle = handle; device->parent = acpi_bus_get_parent(handle); - device->add_type = add_type; STRUCT_TO_INT(device->status) = sta; acpi_device_get_busid(device); @@ -1478,9 +1476,10 @@ static int acpi_add_single_object(struct acpi_device **child, if ((result = acpi_device_set_context(device))) goto end; + device->flags.match_driver = match_driver; result = acpi_device_register(device); - if (device->add_type >= ACPI_BUS_ADD_MATCH) + if (device->flags.match_driver) acpi_hot_add_bind(device); end: @@ -1509,7 +1508,7 @@ static void acpi_bus_add_power_resource(acpi_handle handle) acpi_bus_get_device(handle, &device); if (!device) acpi_add_single_object(&device, handle, ACPI_BUS_TYPE_POWER, - ACPI_STA_DEFAULT, ACPI_BUS_ADD_START); + ACPI_STA_DEFAULT, true); } static int acpi_bus_type_and_status(acpi_handle handle, int *type, @@ -1580,11 +1579,11 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, return AE_CTRL_DEPTH; } - acpi_add_single_object(&device, handle, type, sta, ACPI_BUS_ADD_BASIC); + acpi_add_single_object(&device, handle, type, sta, false); if (!device) return AE_CTRL_DEPTH; - device->add_type = ACPI_BUS_ADD_START; + device->flags.match_driver = true; acpi_hot_add_bind(device); out: @@ -1749,16 +1748,14 @@ static int acpi_bus_scan_fixed(void) if ((acpi_gbl_FADT.flags & ACPI_FADT_POWER_BUTTON) == 0) { result = acpi_add_single_object(&device, NULL, ACPI_BUS_TYPE_POWER_BUTTON, - ACPI_STA_DEFAULT, - ACPI_BUS_ADD_START); + ACPI_STA_DEFAULT, true); device_init_wakeup(&device->dev, true); } if ((acpi_gbl_FADT.flags & ACPI_FADT_SLEEP_BUTTON) == 0) { result = acpi_add_single_object(&device, NULL, ACPI_BUS_TYPE_SLEEP_BUTTON, - ACPI_STA_DEFAULT, - ACPI_BUS_ADD_START); + ACPI_STA_DEFAULT, true); } return result; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 75c6b3f2250d..056cb0cd8eff 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -63,13 +63,6 @@ acpi_get_physical_device_location(acpi_handle handle, struct acpi_pld_info **pld #define ACPI_BUS_FILE_ROOT "acpi" extern struct proc_dir_entry *acpi_root_dir; -enum acpi_bus_add_type { - ACPI_BUS_ADD_BASIC = 0, - ACPI_BUS_ADD_MATCH, - ACPI_BUS_ADD_START, - ACPI_BUS_ADD_TYPE_COUNT -}; - enum acpi_bus_removal_type { ACPI_BUS_REMOVAL_NORMAL = 0, ACPI_BUS_REMOVAL_EJECT, @@ -150,7 +143,8 @@ struct acpi_device_flags { u32 power_manageable:1; u32 performance_manageable:1; u32 eject_pending:1; - u32 reserved:24; + u32 match_driver:1; + u32 reserved:23; }; /* File System */ @@ -285,7 +279,6 @@ struct acpi_device { struct acpi_driver *driver; void *driver_data; struct device dev; - enum acpi_bus_add_type add_type; /* how to handle adding */ enum acpi_bus_removal_type removal_type; /* indicate for different removal type */ u8 physical_node_count; struct list_head physical_node_list; -- cgit v1.2.3 From 0cd6ac52b333f66ee64e50ed216ec99231092dcd Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 21 Dec 2012 00:36:49 +0100 Subject: ACPI: Make acpi_bus_scan() and acpi_bus_add() take only one argument The callers of acpi_bus_add() usually assume that if it has succeeded, then a struct acpi_device object has been attached to the handle passed as the first argument. Unfortunately, however, this assumption is wrong, because acpi_bus_scan(), and acpi_bus_add() too as a result, may return a pointer to a different struct acpi_device object on success (it may be an object corresponding to one of the descendant ACPI nodes in the namespace scope below that handle). For this reason, the callers of acpi_bus_add() who care about whether or not a struct acpi_device object has been created for its first argument need to check that using acpi_bus_get_device() anyway, so the second argument of acpi_bus_add() is not really useful for them. The same observation applies to acpi_bus_scan() executed directly from acpi_scan_init(). Therefore modify the relevant callers of acpi_bus_add() to check the existence of the struct acpi_device in question with the help of acpi_bus_get_device() and drop the no longer necessary second argument of acpi_bus_add(). Accordingly, modify acpi_scan_init() to use acpi_bus_get_device() to get acpi_root and drop the no longer needed second argument of acpi_bus_scan(). Signed-off-by: Rafael J. Wysocki Acked-by: Yinghai Lu Acked-by: Toshi Kani --- drivers/acpi/acpi_memhotplug.c | 7 ++++++- drivers/acpi/container.c | 7 ++++++- drivers/acpi/dock.c | 4 +++- drivers/acpi/processor_driver.c | 8 ++++++-- drivers/acpi/scan.c | 38 +++++++++++--------------------------- drivers/pci/hotplug/acpiphp_glue.c | 19 +++++++++++-------- drivers/pci/hotplug/sgi_hotplug.c | 3 +-- include/acpi/acpi_bus.h | 2 +- 8 files changed, 45 insertions(+), 43 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index d0a7da704d49..327ab4459558 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c @@ -167,11 +167,16 @@ acpi_memory_get_device(acpi_handle handle, * Now add the notified device. This creates the acpi_device * and invokes .add function */ - result = acpi_bus_add(handle, &device); + result = acpi_bus_add(handle); if (result) { acpi_handle_warn(handle, "Cannot add acpi bus\n"); return -EINVAL; } + result = acpi_bus_get_device(handle, &device); + if (result) { + acpi_handle_warn(handle, "Missing device object\n"); + return -EINVAL; + } end: *mem_device = acpi_driver_data(device); diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index 0688f83bc436..f8fb2281f34a 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -166,11 +166,16 @@ static void container_notify_cb(acpi_handle handle, u32 type, void *context) if (!ACPI_FAILURE(status) || device) break; - result = acpi_bus_add(handle, &device); + result = acpi_bus_add(handle); if (result) { acpi_handle_warn(handle, "Failed to add container\n"); break; } + result = acpi_bus_get_device(handle, &device); + if (result) { + acpi_handle_warn(handle, "Missing device object\n"); + break; + } kobject_uevent(&device->dev.kobj, KOBJ_ONLINE); ost_code = ACPI_OST_SC_SUCCESS; diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index ff30582d2e1d..9e31b2bd93d3 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -317,9 +317,11 @@ static struct acpi_device * dock_create_acpi_device(acpi_handle handle) * no device created for this object, * so we should create one. */ - ret = acpi_bus_add(handle, &device); + ret = acpi_bus_add(handle); if (ret) pr_debug("error adding bus, %x\n", -ret); + + acpi_bus_get_device(handle, &device); } return device; } diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 18b292e12b38..0777663f443e 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -699,12 +699,16 @@ static void acpi_processor_hotplug_notify(acpi_handle handle, if (!acpi_bus_get_device(handle, &device)) break; - result = acpi_bus_add(handle, &device); + result = acpi_bus_add(handle); if (result) { acpi_handle_err(handle, "Unable to add the device\n"); break; } - + result = acpi_bus_get_device(handle, &device); + if (result) { + acpi_handle_err(handle, "Missing device object\n"); + break; + } ost_code = ACPI_OST_SC_SUCCESS; break; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index e3aed481aed2..3d44c705a3a2 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1620,37 +1620,27 @@ static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, return status; } -static int acpi_bus_scan(acpi_handle handle, struct acpi_device **child) +static int acpi_bus_scan(acpi_handle handle) { void *device = NULL; - acpi_status status; - int ret = -ENODEV; - status = acpi_bus_check_add(handle, 0, NULL, &device); - if (ACPI_SUCCESS(status)) + if (ACPI_SUCCESS(acpi_bus_check_add(handle, 0, NULL, &device))) acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, acpi_bus_check_add, NULL, NULL, &device); if (!device) - goto out; + return -ENODEV; - ret = 0; - status = acpi_bus_device_attach(handle, 0, NULL, NULL); - if (ACPI_SUCCESS(status)) + if (ACPI_SUCCESS(acpi_bus_device_attach(handle, 0, NULL, NULL))) acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, acpi_bus_device_attach, NULL, NULL, NULL); - out: - if (child) - *child = device; - - return ret; + return 0; } /** * acpi_bus_add - Add ACPI device node objects in a given namespace scope. * @handle: Root of the namespace scope to scan. - * @ret: Location to store a return struct acpi_device pointer. * * Scan a given ACPI tree (probably recently hot-plugged) and create and add * found devices. @@ -1659,21 +1649,12 @@ static int acpi_bus_scan(acpi_handle handle, struct acpi_device **child) * there has been a real error. There just have been no suitable ACPI objects * in the table trunk from which the kernel could create a device and add an * appropriate driver. - * - * If 0 is returned, the memory location pointed to by @ret will be populated - * with a pointer to a struct acpi_device created while scanning the namespace. - * If @handle corresponds to a device node, that will be a pointer to the struct - * acpi_device object corresponding to @handle. Otherwise, it will be a pointer - * to a struct acpi_device corresponding to one of its descendants. - * - * If an error code is returned, NULL will be stored in the memory location - * pointed to by @ret. */ -int acpi_bus_add(acpi_handle handle, struct acpi_device **ret) +int acpi_bus_add(acpi_handle handle) { int err; - err = acpi_bus_scan(handle, ret); + err = acpi_bus_scan(handle); if (err) return err; @@ -1777,8 +1758,11 @@ int __init acpi_scan_init(void) /* * Enumerate devices in the ACPI namespace. */ - result = acpi_bus_scan(ACPI_ROOT_OBJECT, &acpi_root); + result = acpi_bus_scan(ACPI_ROOT_OBJECT); + if (result) + return result; + result = acpi_bus_get_device(ACPI_ROOT_OBJECT, &acpi_root); if (!result) result = acpi_bus_scan_fixed(); diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index dfc2df54b93a..91b5ad875c53 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -746,14 +746,13 @@ static int acpiphp_bus_add(struct acpiphp_func *func) dbg("acpi_bus_trim return %x\n", ret_val); } - ret_val = acpi_bus_add(func->handle, &device); - if (ret_val) { - dbg("error adding bus, %x\n", - -ret_val); - goto acpiphp_bus_add_out; - } + ret_val = acpi_bus_add(func->handle); + if (!ret_val) + ret_val = acpi_bus_get_device(func->handle, &device); + + if (ret_val) + dbg("error adding bus, %x\n", -ret_val); -acpiphp_bus_add_out: return ret_val; } @@ -1130,10 +1129,14 @@ static void handle_bridge_insertion(acpi_handle handle, u32 type) return; } - if (acpi_bus_add(handle, &device)) { + if (acpi_bus_add(handle)) { err("cannot add bridge to acpi list\n"); return; } + if (acpi_bus_get_device(handle, &device)) { + err("ACPI device object missing\n"); + return; + } if (!acpiphp_configure_bridge(handle)) add_bridge(handle); else diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c index 801b58d1f78e..f3c419256d2a 100644 --- a/drivers/pci/hotplug/sgi_hotplug.c +++ b/drivers/pci/hotplug/sgi_hotplug.c @@ -412,7 +412,6 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot) if (SN_ACPI_BASE_SUPPORT() && ssdt) { unsigned long long adr; struct acpi_device *pdevice; - struct acpi_device *device; acpi_handle phandle; acpi_handle chandle = NULL; acpi_handle rethandle; @@ -448,7 +447,7 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot) if (ACPI_SUCCESS(ret) && (adr>>16) == (slot->device_num + 1)) { - ret = acpi_bus_add(chandle, &device); + ret = acpi_bus_add(chandle); if (ACPI_FAILURE(ret)) { printk(KERN_ERR "%s: acpi_bus_add " "failed (0x%x) for slot %d " diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 056cb0cd8eff..5e1d5a1b477f 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -351,7 +351,7 @@ static inline int acpi_bus_generate_proc_event(struct acpi_device *device, u8 ty #endif int acpi_bus_register_driver(struct acpi_driver *driver); void acpi_bus_unregister_driver(struct acpi_driver *driver); -int acpi_bus_add(acpi_handle handle, struct acpi_device **ret); +int acpi_bus_add(acpi_handle handle); void acpi_bus_hot_remove_device(void *context); int acpi_bus_trim(struct acpi_device *start, int rmdevice); acpi_status acpi_bus_get_ejd(acpi_handle handle, acpi_handle * ejd); -- cgit v1.2.3 From 3eec5f7a1955ead549020ec2f60235ea6a4a2252 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 23 Dec 2012 00:03:03 +0100 Subject: ACPI: Drop ACPI device .bind() and .unbind() callbacks Drop the .bind() and .unbind() that have no more users from struct acpi_device_ops and remove all of the code referring to them from drivers/acpi/scan.c. Signed-off-by: Rafael J. Wysocki Acked-by: Yinghai Lu Acked-by: Toshi Kani --- drivers/acpi/scan.c | 22 ---------------------- include/acpi/acpi_bus.h | 2 -- 2 files changed, 24 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 3d44c705a3a2..f8a0d0f7d197 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1385,29 +1385,11 @@ static int acpi_bus_remove(struct acpi_device *dev, int rmdevice) if (!rmdevice) return 0; - /* - * unbind _ADR-Based Devices when hot removal - */ - if (dev->flags.bus_address) { - if ((dev->parent) && (dev->parent->ops.unbind)) - dev->parent->ops.unbind(dev); - } acpi_device_unregister(dev, ACPI_BUS_REMOVAL_EJECT); return 0; } -/* - * acpi_hot_add_bind - Bind _ADR-based devices on hot-add. - * @device: ACPI device node to bind. - */ -static void acpi_hot_add_bind(struct acpi_device *device) -{ - if (device->flags.bus_address - && device->parent && device->parent->ops.bind) - device->parent->ops.bind(device); -} - static int acpi_add_single_object(struct acpi_device **child, acpi_handle handle, int type, unsigned long long sta, bool match_driver) @@ -1479,9 +1461,6 @@ static int acpi_add_single_object(struct acpi_device **child, device->flags.match_driver = match_driver; result = acpi_device_register(device); - if (device->flags.match_driver) - acpi_hot_add_bind(device); - end: if (!result) { acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); @@ -1584,7 +1563,6 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, return AE_CTRL_DEPTH; device->flags.match_driver = true; - acpi_hot_add_bind(device); out: if (!*return_value) diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 70647313a9bc..6e9a73e05916 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -99,8 +99,6 @@ struct acpi_device_ops { acpi_op_add add; acpi_op_remove remove; acpi_op_start start; - acpi_op_bind bind; - acpi_op_unbind unbind; acpi_op_notify notify; }; -- cgit v1.2.3 From f95988de06ea62ef5bd861f06e9ef56cea405ed1 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 7 Jan 2013 21:17:02 +0100 Subject: ACPI / scan: Treat power resources in a special way Commit 805d410 (ACPI: Separate adding ACPI device objects from probing ACPI drivers) introduced an ACPI power resources management regression, because it didn't ensure that the power resources driver bind to the struct acpi_device objects corresponding to power resources as soon as they were created. As a result, ACPI power management routines may attempt to access power resource objects before they are ready to use. To fix this problem, tell the acpi_add_single_object() in acpi_bus_check_add() to probe the driver for objects of type ACPI_BUS_TYPE_POWER. This fix has been verified to work on HP nx6325 where the problem was first observed. Signed-off-by: Rafael J. Wysocki Acked-by: Toshi Kani --- drivers/acpi/scan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index f8a0d0f7d197..e380345b643a 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1558,7 +1558,8 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, return AE_CTRL_DEPTH; } - acpi_add_single_object(&device, handle, type, sta, false); + acpi_add_single_object(&device, handle, type, sta, + type == ACPI_BUS_TYPE_POWER); if (!device) return AE_CTRL_DEPTH; -- cgit v1.2.3 From abe99210e0f624cea39f1dc375ba818b201c0d7f Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 11 Jan 2013 22:08:09 +0100 Subject: ACPI / scan: Fix check of device_attach() return value. Since device_attach() returns 1 on success (a driver has been bound to the device), the check against its return value in acpi_bus_device_attach() should modified to take that into accout. Make it so. [rjw: Subject and changelog.] Signed-off-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index e380345b643a..bac357da3f29 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1593,7 +1593,7 @@ static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, if (!acpi_match_device_ids(device, acpi_platform_device_ids)) { /* This is a known good platform device. */ acpi_create_platform_device(device); - } else if (device_attach(&device->dev)) { + } else if (device_attach(&device->dev) < 0) { status = AE_CTRL_DEPTH; } return status; -- cgit v1.2.3 From b17b537ac1429a609addb55bf985f5ebfcf4ae7b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 15 Jan 2013 13:23:44 +0100 Subject: ACPI / scan: Drop the second argument of acpi_device_unregister() Drop the second argument of acpi_device_unregister(), type, which is not used by that function. Signed-off-by: Rafael J. Wysocki Acked-by: Toshi Kani Acked-by: Yinghai Lu Acked-by: Yasuaki Ishimatsu --- drivers/acpi/scan.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index bac357da3f29..a26c09efc286 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -701,7 +701,7 @@ end: return result; } -static void acpi_device_unregister(struct acpi_device *device, int type) +static void acpi_device_unregister(struct acpi_device *device) { mutex_lock(&acpi_device_lock); if (device->parent) @@ -1385,7 +1385,7 @@ static int acpi_bus_remove(struct acpi_device *dev, int rmdevice) if (!rmdevice) return 0; - acpi_device_unregister(dev, ACPI_BUS_REMOVAL_EJECT); + acpi_device_unregister(dev); return 0; } @@ -1746,7 +1746,7 @@ int __init acpi_scan_init(void) result = acpi_bus_scan_fixed(); if (result) - acpi_device_unregister(acpi_root, ACPI_BUS_REMOVAL_NORMAL); + acpi_device_unregister(acpi_root); else acpi_update_all_gpes(); -- cgit v1.2.3 From ae281795ec92d35dd1631401829124acab965b1f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 15 Jan 2013 13:23:53 +0100 Subject: ACPI / scan: Drop the second argument of acpi_bus_trim() All callers of acpi_bus_trim() pass 1 (true) as the second argument of it, so remove that argument entirely and change acpi_bus_trim() to always behave as though it were 1. Signed-off-by: Rafael J. Wysocki Acked-by: Toshi Kani Acked-by: Yinghai Lu Acked-by: Yasuaki Ishimatsu --- drivers/acpi/dock.c | 2 +- drivers/acpi/scan.c | 16 ++++------------ drivers/pci/hotplug/acpiphp_glue.c | 4 ++-- drivers/pci/hotplug/sgi_hotplug.c | 2 +- include/acpi/acpi_bus.h | 2 +- 5 files changed, 9 insertions(+), 17 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 9e31b2bd93d3..4a56a8b2e51e 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -339,7 +339,7 @@ static void dock_remove_acpi_device(acpi_handle handle) int ret; if (!acpi_bus_get_device(handle, &device)) { - ret = acpi_bus_trim(device, 1); + ret = acpi_bus_trim(device); if (ret) pr_debug("error removing bus, %x\n", -ret); } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index a26c09efc286..d14ce446b469 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -133,7 +133,7 @@ void acpi_bus_hot_remove_device(void *context) ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Hot-removing device %s...\n", dev_name(&device->dev))); - if (acpi_bus_trim(device, 1)) { + if (acpi_bus_trim(device)) { printk(KERN_ERR PREFIX "Removing device failed\n"); goto err_out; @@ -1374,7 +1374,7 @@ static int acpi_device_set_context(struct acpi_device *device) return -ENODEV; } -static int acpi_bus_remove(struct acpi_device *dev, int rmdevice) +static int acpi_bus_remove(struct acpi_device *dev) { if (!dev) return -EINVAL; @@ -1382,9 +1382,6 @@ static int acpi_bus_remove(struct acpi_device *dev, int rmdevice) dev->removal_type = ACPI_BUS_REMOVAL_EJECT; device_release_driver(&dev->dev); - if (!rmdevice) - return 0; - acpi_device_unregister(dev); return 0; @@ -1642,7 +1639,7 @@ int acpi_bus_add(acpi_handle handle) } EXPORT_SYMBOL(acpi_bus_add); -int acpi_bus_trim(struct acpi_device *start, int rmdevice) +int acpi_bus_trim(struct acpi_device *start) { acpi_status status; struct acpi_device *parent, *child; @@ -1668,12 +1665,7 @@ int acpi_bus_trim(struct acpi_device *start, int rmdevice) acpi_get_parent(phandle, &phandle); child = parent; parent = parent->parent; - - if (level == 0) - err = acpi_bus_remove(child, rmdevice); - else - err = acpi_bus_remove(child, 1); - + err = acpi_bus_remove(child); continue; } diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 91b5ad875c53..22006f2d9dd5 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -742,7 +742,7 @@ static int acpiphp_bus_add(struct acpiphp_func *func) /* this shouldn't be in here, so remove * the bus then re-add it... */ - ret_val = acpi_bus_trim(device, 1); + ret_val = acpi_bus_trim(device); dbg("acpi_bus_trim return %x\n", ret_val); } @@ -772,7 +772,7 @@ static int acpiphp_bus_trim(acpi_handle handle) return retval; } - retval = acpi_bus_trim(device, 1); + retval = acpi_bus_trim(device); if (retval) err("cannot remove from acpi list\n"); diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c index f3c419256d2a..2e006ee5738b 100644 --- a/drivers/pci/hotplug/sgi_hotplug.c +++ b/drivers/pci/hotplug/sgi_hotplug.c @@ -535,7 +535,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot) ret = acpi_bus_get_device(chandle, &device); if (ACPI_SUCCESS(ret)) - acpi_bus_trim(device, 1); + acpi_bus_trim(device); } } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index efe5f746c561..566f1fdabbd2 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -348,7 +348,7 @@ int acpi_bus_register_driver(struct acpi_driver *driver); void acpi_bus_unregister_driver(struct acpi_driver *driver); int acpi_bus_add(acpi_handle handle); void acpi_bus_hot_remove_device(void *context); -int acpi_bus_trim(struct acpi_device *start, int rmdevice); +int acpi_bus_trim(struct acpi_device *start); acpi_status acpi_bus_get_ejd(acpi_handle handle, acpi_handle * ejd); int acpi_match_device_ids(struct acpi_device *device, const struct acpi_device_id *ids); -- cgit v1.2.3 From cecdb193c8d91a42d9489d00618cc3dfff92e55a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 15 Jan 2013 13:24:02 +0100 Subject: ACPI / scan: Change the implementation of acpi_bus_trim() The current acpi_bus_trim() implementation is not really straightforward and may be simplified significantly by using acpi_walk_namespace() with acpi_bus_remove() as a post-order callback. Observe that acpi_bus_remove(), as called by acpi_bus_trim(), cannot actually fail, because its first argument is guaranteed not to be NULL thanks to the acpi_bus_get_device() check in acpi_bus_trim(), so simply move the acpi_bus_get_device() check to acpi_bus_remove() and use acpi_walk_namespace() to execute it for every device under start->handle as a post-order callback. The, run it directly for start->handle itself. Signed-off-by: Rafael J. Wysocki Acked-by: Toshi Kani Acked-by: Yinghai Lu Acked-by: Yasuaki Ishimatsu --- drivers/acpi/scan.c | 64 +++++++++++++---------------------------------------- 1 file changed, 15 insertions(+), 49 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index d14ce446b469..1ee62bd25828 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1374,17 +1374,20 @@ static int acpi_device_set_context(struct acpi_device *device) return -ENODEV; } -static int acpi_bus_remove(struct acpi_device *dev) +static acpi_status acpi_bus_remove(acpi_handle handle, u32 lvl_not_used, + void *not_used, void **ret_not_used) { - if (!dev) - return -EINVAL; + struct acpi_device *dev = NULL; + + if (acpi_bus_get_device(handle, &dev)) + return AE_OK; dev->removal_type = ACPI_BUS_REMOVAL_EJECT; device_release_driver(&dev->dev); acpi_device_unregister(dev); - return 0; + return AE_OK; } static int acpi_add_single_object(struct acpi_device **child, @@ -1641,51 +1644,14 @@ EXPORT_SYMBOL(acpi_bus_add); int acpi_bus_trim(struct acpi_device *start) { - acpi_status status; - struct acpi_device *parent, *child; - acpi_handle phandle, chandle; - acpi_object_type type; - u32 level = 1; - int err = 0; - - parent = start; - phandle = start->handle; - child = chandle = NULL; - - while ((level > 0) && parent && (!err)) { - status = acpi_get_next_object(ACPI_TYPE_ANY, phandle, - chandle, &chandle); - - /* - * If this scope is exhausted then move our way back up. - */ - if (ACPI_FAILURE(status)) { - level--; - chandle = phandle; - acpi_get_parent(phandle, &phandle); - child = parent; - parent = parent->parent; - err = acpi_bus_remove(child); - continue; - } - - status = acpi_get_type(chandle, &type); - if (ACPI_FAILURE(status)) { - continue; - } - /* - * If there is a device corresponding to chandle then - * parse it (depth-first). - */ - if (acpi_bus_get_device(chandle, &child) == 0) { - level++; - phandle = chandle; - chandle = NULL; - parent = child; - } - continue; - } - return err; + /* + * Execute acpi_bus_remove() as a post-order callback to remove device + * nodes in the given namespace scope. + */ + acpi_walk_namespace(ACPI_TYPE_ANY, start->handle, ACPI_UINT32_MAX, NULL, + acpi_bus_remove, NULL, NULL); + acpi_bus_remove(start->handle, 0, NULL, NULL); + return 0; } EXPORT_SYMBOL_GPL(acpi_bus_trim); -- cgit v1.2.3 From 05404d8f7b5c831e1a2c24bb782f0fe8ea02354c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 15 Jan 2013 13:24:13 +0100 Subject: ACPI / scan: Add second pass to acpi_bus_trim() Make acpi_bus_trim() work in analogy with acpi_bus_scan() and carry out two passes such that ACPI drivers will be detached from device nodes being removed in the first pass and the device nodes themselves will be removed in the second pass. For this purpose split the driver unregistration out of acpi_bus_remove() into a new routine, acpi_bus_device_detach(), that will be executed by acpi_bus_trim() in the additional first pass as a post-order callback. This is necessary, because some ACPI drivers' .remove() routines unregister struct device objects associated with the ACPI device nodes being removed and that needs to happen while the ACPI device nodes are still around (for example, in case they need to be used for power management or similar things at that time). Signed-off-by: Rafael J. Wysocki Acked-by: Toshi Kani Acked-by: Yinghai Lu Acked-by: Yasuaki Ishimatsu --- drivers/acpi/scan.c | 46 ++++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 1ee62bd25828..d04d0b33656c 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1374,22 +1374,6 @@ static int acpi_device_set_context(struct acpi_device *device) return -ENODEV; } -static acpi_status acpi_bus_remove(acpi_handle handle, u32 lvl_not_used, - void *not_used, void **ret_not_used) -{ - struct acpi_device *dev = NULL; - - if (acpi_bus_get_device(handle, &dev)) - return AE_OK; - - dev->removal_type = ACPI_BUS_REMOVAL_EJECT; - device_release_driver(&dev->dev); - - acpi_device_unregister(dev); - - return AE_OK; -} - static int acpi_add_single_object(struct acpi_device **child, acpi_handle handle, int type, unsigned long long sta, bool match_driver) @@ -1642,8 +1626,38 @@ int acpi_bus_add(acpi_handle handle) } EXPORT_SYMBOL(acpi_bus_add); +static acpi_status acpi_bus_device_detach(acpi_handle handle, u32 lvl_not_used, + void *not_used, void **ret_not_used) +{ + struct acpi_device *device = NULL; + + if (!acpi_bus_get_device(handle, &device)) { + device->removal_type = ACPI_BUS_REMOVAL_EJECT; + device_release_driver(&device->dev); + } + return AE_OK; +} + +static acpi_status acpi_bus_remove(acpi_handle handle, u32 lvl_not_used, + void *not_used, void **ret_not_used) +{ + struct acpi_device *device = NULL; + + if (!acpi_bus_get_device(handle, &device)) + acpi_device_unregister(device); + + return AE_OK; +} + int acpi_bus_trim(struct acpi_device *start) { + /* + * Execute acpi_bus_device_detach() as a post-order callback to detach + * all ACPI drivers from the device nodes being removed. + */ + acpi_walk_namespace(ACPI_TYPE_ANY, start->handle, ACPI_UINT32_MAX, NULL, + acpi_bus_device_detach, NULL, NULL); + acpi_bus_device_detach(start->handle, 0, NULL, NULL); /* * Execute acpi_bus_remove() as a post-order callback to remove device * nodes in the given namespace scope. -- cgit v1.2.3 From 5993c4670ea2453ef5abb45b312f150e994e6eb9 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 11 Jan 2013 22:40:41 +0000 Subject: ACPI: update ej_event interface to take acpi_device Should use acpi_device pointer directly instead of use handle and get the device pointer again later. Signed-off-by: Yinghai Lu Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_memhotplug.c | 2 +- drivers/acpi/processor_driver.c | 2 +- drivers/acpi/scan.c | 14 ++++---------- include/acpi/acpi_bus.h | 2 +- 4 files changed, 7 insertions(+), 13 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index 327ab4459558..eaddb7a89c70 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c @@ -361,7 +361,7 @@ static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data) break; } - ej_event->handle = handle; + ej_event->device = device; ej_event->event = ACPI_NOTIFY_EJECT_REQUEST; acpi_os_hotplug_execute(acpi_bus_hot_remove_device, (void *)ej_event); diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 0777663f443e..a24ee43e06e4 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -733,7 +733,7 @@ static void acpi_processor_hotplug_notify(acpi_handle handle, break; } - ej_event->handle = handle; + ej_event->device = device; ej_event->event = ACPI_NOTIFY_EJECT_REQUEST; acpi_os_hotplug_execute(acpi_bus_hot_remove_device, (void *)ej_event); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index d04d0b33656c..388b59c096dc 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -116,20 +116,14 @@ static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); void acpi_bus_hot_remove_device(void *context) { struct acpi_eject_event *ej_event = (struct acpi_eject_event *) context; - struct acpi_device *device; - acpi_handle handle = ej_event->handle; + struct acpi_device *device = ej_event->device; + acpi_handle handle = device->handle; acpi_handle temp; struct acpi_object_list arg_list; union acpi_object arg; acpi_status status = AE_OK; u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */ - if (acpi_bus_get_device(handle, &device)) - goto err_out; - - if (!device) - goto err_out; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Hot-removing device %s...\n", dev_name(&device->dev))); @@ -215,7 +209,7 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, goto err; } - ej_event->handle = acpi_device->handle; + ej_event->device = acpi_device; if (acpi_device->flags.eject_pending) { /* event originated from ACPI eject notification */ ej_event->event = ACPI_NOTIFY_EJECT_REQUEST; @@ -223,7 +217,7 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, } else { /* event originated from user */ ej_event->event = ACPI_OST_EC_OSPM_EJECT; - (void) acpi_evaluate_hotplug_ost(ej_event->handle, + (void) acpi_evaluate_hotplug_ost(acpi_device->handle, ej_event->event, ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 566f1fdabbd2..567851b4f043 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -306,7 +306,7 @@ struct acpi_bus_event { }; struct acpi_eject_event { - acpi_handle handle; + struct acpi_device *device; u32 event; }; -- cgit v1.2.3 From bc9b6407bd6df3ab7189e5622816bbc11ae9d2d8 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 17 Jan 2013 14:11:05 +0100 Subject: ACPI / PM: Rework the handling of devices depending on power resources Commit 0090def6 (ACPI: Add interface to register/unregister device to/from power resources) made it possible to indicate to the ACPI core that if the given device depends on any power resources, then it should be resumed as soon as all of the power resources required by it to transition to the D0 power state have been turned on. Unfortunately, however, this was a mistake, because all devices depending on power resources should be treated this way (i.e. they should be resumed when all power resources required by their D0 state have been turned on) and for the majority of those devices the ACPI core can figure out by itself which (physical) devices depend on what power resources. For this reason, replace the code added by commit 0090def6 with a new, much more straightforward, mechanism that will be used internally by the ACPI core and remove all references to that code from kernel subsystems using ACPI. For the cases when there are (physical) devices that should be resumed whenever a not directly related ACPI device node goes into D0 as a result of power resources configuration changes, like in the SATA case, add two new routines, acpi_dev_pm_add_dependent() and acpi_dev_pm_remove_dependent(), allowing subsystems to manage such dependencies. Convert the SATA subsystem to use the new functions accordingly. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/device_pm.c | 56 ++++++++++++ drivers/acpi/internal.h | 1 + drivers/acpi/power.c | 219 +++++++++++++++++----------------------------- drivers/acpi/scan.c | 8 ++ drivers/ata/libata-acpi.c | 18 +--- drivers/pci/pci-acpi.c | 2 - include/acpi/acpi_bus.h | 9 +- 7 files changed, 158 insertions(+), 155 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 995019063f64..8be4b29e38aa 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -665,3 +665,59 @@ void acpi_dev_pm_detach(struct device *dev, bool power_off) } } EXPORT_SYMBOL_GPL(acpi_dev_pm_detach); + +/** + * acpi_dev_pm_add_dependent - Add physical device depending for PM. + * @handle: Handle of ACPI device node. + * @depdev: Device depending on that node for PM. + */ +void acpi_dev_pm_add_dependent(acpi_handle handle, struct device *depdev) +{ + struct acpi_device_physical_node *dep; + struct acpi_device *adev; + + if (!depdev || acpi_bus_get_device(handle, &adev)) + return; + + mutex_lock(&adev->physical_node_lock); + + list_for_each_entry(dep, &adev->power_dependent, node) + if (dep->dev == depdev) + goto out; + + dep = kzalloc(sizeof(*dep), GFP_KERNEL); + if (dep) { + dep->dev = depdev; + list_add_tail(&dep->node, &adev->power_dependent); + } + + out: + mutex_unlock(&adev->physical_node_lock); +} +EXPORT_SYMBOL_GPL(acpi_dev_pm_add_dependent); + +/** + * acpi_dev_pm_remove_dependent - Remove physical device depending for PM. + * @handle: Handle of ACPI device node. + * @depdev: Device depending on that node for PM. + */ +void acpi_dev_pm_remove_dependent(acpi_handle handle, struct device *depdev) +{ + struct acpi_device_physical_node *dep; + struct acpi_device *adev; + + if (!depdev || acpi_bus_get_device(handle, &adev)) + return; + + mutex_lock(&adev->physical_node_lock); + + list_for_each_entry(dep, &adev->power_dependent, node) + if (dep->dev == depdev) { + list_del(&dep->node); + kfree(dep); + break; + } + + mutex_unlock(&adev->physical_node_lock); +} +EXPORT_SYMBOL_GPL(acpi_dev_pm_remove_dependent); diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index e050254ae143..b79b4258bd6b 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -38,6 +38,7 @@ static inline void acpi_debugfs_init(void) { return; } Power Resource -------------------------------------------------------------------------- */ int acpi_power_init(void); +void acpi_power_add_remove_device(struct acpi_device *adev, bool add); int acpi_device_sleep_wake(struct acpi_device *dev, int enable, int sleep_state, int dev_state); int acpi_power_get_inferred_state(struct acpi_device *device, int *state); diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 6e7b9d523812..659386c4f0cb 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -83,31 +83,20 @@ static struct acpi_driver acpi_power_driver = { .drv.pm = &acpi_power_pm, }; -/* - * A power managed device - * A device may rely on multiple power resources. - * */ -struct acpi_power_managed_device { - struct device *dev; /* The physical device */ - acpi_handle *handle; -}; - -struct acpi_power_resource_device { - struct acpi_power_managed_device *device; - struct acpi_power_resource_device *next; +struct acpi_power_dependent_device { + struct list_head node; + struct acpi_device *adev; + struct work_struct work; }; struct acpi_power_resource { - struct acpi_device * device; + struct acpi_device *device; + struct list_head dependent; acpi_bus_id name; u32 system_level; u32 order; unsigned int ref_count; struct mutex resource_lock; - - /* List of devices relying on this power resource */ - struct acpi_power_resource_device *devices; - struct mutex devices_lock; }; static struct list_head acpi_power_resource_list; @@ -207,21 +196,30 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state) return 0; } -/* Resume the device when all power resources in _PR0 are on */ -static void acpi_power_on_device(struct acpi_power_managed_device *device) +static void acpi_power_resume_dependent(struct work_struct *work) { - struct acpi_device *acpi_dev; - acpi_handle handle = device->handle; + struct acpi_power_dependent_device *dep; + struct acpi_device_physical_node *pn; + struct acpi_device *adev; int state; - if (acpi_bus_get_device(handle, &acpi_dev)) + dep = container_of(work, struct acpi_power_dependent_device, work); + adev = dep->adev; + if (acpi_power_get_inferred_state(adev, &state)) return; - if(acpi_power_get_inferred_state(acpi_dev, &state)) + if (state > ACPI_STATE_D0) return; - if (state == ACPI_STATE_D0 && pm_runtime_suspended(device->dev)) - pm_request_resume(device->dev); + mutex_lock(&adev->physical_node_lock); + + list_for_each_entry(pn, &adev->physical_node_list, node) + pm_request_resume(pn->dev); + + list_for_each_entry(pn, &adev->power_dependent, node) + pm_request_resume(pn->dev); + + mutex_unlock(&adev->physical_node_lock); } static int __acpi_power_on(struct acpi_power_resource *resource) @@ -244,9 +242,7 @@ static int __acpi_power_on(struct acpi_power_resource *resource) static int acpi_power_on(acpi_handle handle) { int result = 0; - bool resume_device = false; struct acpi_power_resource *resource = NULL; - struct acpi_power_resource_device *device_list; result = acpi_power_get_context(handle, &resource); if (result) @@ -260,26 +256,17 @@ static int acpi_power_on(acpi_handle handle) resource->name)); } else { result = __acpi_power_on(resource); - if (result) + if (result) { resource->ref_count--; - else - resume_device = true; - } + } else { + struct acpi_power_dependent_device *dep; - mutex_unlock(&resource->resource_lock); - - if (!resume_device) - return result; - - mutex_lock(&resource->devices_lock); - - device_list = resource->devices; - while (device_list) { - acpi_power_on_device(device_list->device); - device_list = device_list->next; + list_for_each_entry(dep, &resource->dependent, node) + schedule_work(&dep->work); + } } - mutex_unlock(&resource->devices_lock); + mutex_unlock(&resource->resource_lock); return result; } @@ -357,119 +344,81 @@ static int acpi_power_on_list(struct acpi_handle_list *list) return result; } -static void __acpi_power_resource_unregister_device(struct device *dev, - acpi_handle res_handle) +static void acpi_power_add_dependent(acpi_handle rhandle, + struct acpi_device *adev) { - struct acpi_power_resource *resource = NULL; - struct acpi_power_resource_device *prev, *curr; + struct acpi_power_dependent_device *dep; + struct acpi_power_resource *resource; - if (acpi_power_get_context(res_handle, &resource)) + if (!rhandle || !adev || acpi_power_get_context(rhandle, &resource)) return; - mutex_lock(&resource->devices_lock); - prev = NULL; - curr = resource->devices; - while (curr) { - if (curr->device->dev == dev) { - if (!prev) - resource->devices = curr->next; - else - prev->next = curr->next; - - kfree(curr); - break; - } - - prev = curr; - curr = curr->next; - } - mutex_unlock(&resource->devices_lock); -} - -/* Unlink dev from all power resources in _PR0 */ -void acpi_power_resource_unregister_device(struct device *dev, acpi_handle handle) -{ - struct acpi_device *acpi_dev; - struct acpi_handle_list *list; - int i; + mutex_lock(&resource->resource_lock); - if (!dev || !handle) - return; + list_for_each_entry(dep, &resource->dependent, node) + if (dep->adev == adev) + goto out; - if (acpi_bus_get_device(handle, &acpi_dev)) - return; + dep = kzalloc(sizeof(*dep), GFP_KERNEL); + if (!dep) + goto out; - list = &acpi_dev->power.states[ACPI_STATE_D0].resources; + dep->adev = adev; + INIT_WORK(&dep->work, acpi_power_resume_dependent); + list_add_tail(&dep->node, &resource->dependent); - for (i = 0; i < list->count; i++) - __acpi_power_resource_unregister_device(dev, - list->handles[i]); + out: + mutex_unlock(&resource->resource_lock); } -EXPORT_SYMBOL_GPL(acpi_power_resource_unregister_device); -static int __acpi_power_resource_register_device( - struct acpi_power_managed_device *powered_device, acpi_handle handle) +static void acpi_power_remove_dependent(acpi_handle rhandle, + struct acpi_device *adev) { - struct acpi_power_resource *resource = NULL; - struct acpi_power_resource_device *power_resource_device; - int result; + struct acpi_power_dependent_device *dep; + struct acpi_power_resource *resource; + struct work_struct *work = NULL; - result = acpi_power_get_context(handle, &resource); - if (result) - return result; + if (!rhandle || !adev || acpi_power_get_context(rhandle, &resource)) + return; - power_resource_device = kzalloc( - sizeof(*power_resource_device), GFP_KERNEL); - if (!power_resource_device) - return -ENOMEM; + mutex_lock(&resource->resource_lock); - power_resource_device->device = powered_device; + list_for_each_entry(dep, &resource->dependent, node) + if (dep->adev == adev) { + list_del(&dep->node); + work = &dep->work; + break; + } - mutex_lock(&resource->devices_lock); - power_resource_device->next = resource->devices; - resource->devices = power_resource_device; - mutex_unlock(&resource->devices_lock); + mutex_unlock(&resource->resource_lock); - return 0; + if (work) { + cancel_work_sync(work); + kfree(dep); + } } -/* Link dev to all power resources in _PR0 */ -int acpi_power_resource_register_device(struct device *dev, acpi_handle handle) +void acpi_power_add_remove_device(struct acpi_device *adev, bool add) { - struct acpi_device *acpi_dev; - struct acpi_handle_list *list; - struct acpi_power_managed_device *powered_device; - int i, ret; + if (adev->power.flags.power_resources) { + struct acpi_device_power_state *ps; + int j; - if (!dev || !handle) - return -ENODEV; - - ret = acpi_bus_get_device(handle, &acpi_dev); - if (ret || !acpi_dev->power.flags.power_resources) - return -ENODEV; + ps = &adev->power.states[ACPI_STATE_D0]; + for (j = 0; j < ps->resources.count; j++) { + acpi_handle rhandle = ps->resources.handles[j]; - powered_device = kzalloc(sizeof(*powered_device), GFP_KERNEL); - if (!powered_device) - return -ENOMEM; - - powered_device->dev = dev; - powered_device->handle = handle; - - list = &acpi_dev->power.states[ACPI_STATE_D0].resources; - - for (i = 0; i < list->count; i++) { - ret = __acpi_power_resource_register_device(powered_device, - list->handles[i]); - - if (ret) { - acpi_power_resource_unregister_device(dev, handle); - break; + if (add) + acpi_power_add_dependent(rhandle, adev); + else + acpi_power_remove_dependent(rhandle, adev); } } - - return ret; } -EXPORT_SYMBOL_GPL(acpi_power_resource_register_device); + +/* -------------------------------------------------------------------------- + Device Power Management + -------------------------------------------------------------------------- */ /** * acpi_device_sleep_wake - execute _DSW (Device Sleep Wake) or (deprecated in @@ -623,10 +572,6 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev) return err; } -/* -------------------------------------------------------------------------- - Device Power Management - -------------------------------------------------------------------------- */ - int acpi_power_get_inferred_state(struct acpi_device *device, int *state) { int result = 0; @@ -725,7 +670,7 @@ static int acpi_power_add(struct acpi_device *device) resource->device = device; mutex_init(&resource->resource_lock); - mutex_init(&resource->devices_lock); + INIT_LIST_HEAD(&resource->dependent); strcpy(resource->name, device->pnp.bus_id); strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_POWER_CLASS); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 7d164a966b0d..c6d60910e8a8 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -633,6 +633,7 @@ static int acpi_device_register(struct acpi_device *device) INIT_LIST_HEAD(&device->wakeup_list); INIT_LIST_HEAD(&device->physical_node_list); mutex_init(&device->physical_node_lock); + INIT_LIST_HEAD(&device->power_dependent); new_bus_id = kzalloc(sizeof(struct acpi_device_bus_id), GFP_KERNEL); if (!new_bus_id) { @@ -706,8 +707,14 @@ static void acpi_device_unregister(struct acpi_device *device) acpi_detach_data(device->handle, acpi_bus_data_handler); + acpi_power_add_remove_device(device, false); acpi_device_remove_files(device); device_unregister(&device->dev); + /* + * Drop the reference counts of all power resources the device depends + * on and turn off the ones that have no more references. + */ + acpi_power_transition(device, ACPI_STATE_D3_COLD); } /* -------------------------------------------------------------------------- @@ -1441,6 +1448,7 @@ static int acpi_add_single_object(struct acpi_device **child, end: if (!result) { + acpi_power_add_remove_device(device, true); acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Adding %s [%s] parent %s\n", dev_name(&device->dev), diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index ef01ac07502e..6fc67f7efb22 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -1029,30 +1029,20 @@ static void ata_acpi_register_power_resource(struct ata_device *dev) { struct scsi_device *sdev = dev->sdev; acpi_handle handle; - struct device *device; handle = ata_dev_acpi_handle(dev); - if (!handle) - return; - - device = &sdev->sdev_gendev; - - acpi_power_resource_register_device(device, handle); + if (handle) + acpi_dev_pm_remove_dependent(handle, &sdev->sdev_gendev); } static void ata_acpi_unregister_power_resource(struct ata_device *dev) { struct scsi_device *sdev = dev->sdev; acpi_handle handle; - struct device *device; handle = ata_dev_acpi_handle(dev); - if (!handle) - return; - - device = &sdev->sdev_gendev; - - acpi_power_resource_unregister_device(device, handle); + if (handle) + acpi_dev_pm_remove_dependent(handle, &sdev->sdev_gendev); } void ata_acpi_bind(struct ata_device *dev) diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 42736e213f25..e407c61559ca 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -345,7 +345,6 @@ static void pci_acpi_setup(struct device *dev) acpi_pci_irq_add_prt(handle, pci_domain_nr(pci_dev->bus), bus); } - acpi_power_resource_register_device(dev, handle); if (acpi_bus_get_device(handle, &adev) || !adev->wakeup.flags.valid) return; @@ -368,7 +367,6 @@ static void pci_acpi_cleanup(struct device *dev) device_set_run_wake(dev, false); pci_acpi_remove_pm_notifier(adev); } - acpi_power_resource_unregister_device(dev, handle); if (pci_dev->subordinate) acpi_pci_irq_del_prt(pci_domain_nr(pci_dev->bus), diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 567851b4f043..29a1badfca55 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -279,6 +279,7 @@ struct acpi_device { struct list_head physical_node_list; struct mutex physical_node_lock; DECLARE_BITMAP(physical_node_id_bitmap, ACPI_MAX_PHYSICAL_NODE); + struct list_head power_dependent; }; static inline void *acpi_driver_data(struct acpi_device *d) @@ -334,8 +335,6 @@ int acpi_device_set_power(struct acpi_device *device, int state); int acpi_bus_update_power(acpi_handle handle, int *state_p); bool acpi_bus_power_manageable(acpi_handle handle); bool acpi_bus_can_wakeup(acpi_handle handle); -int acpi_power_resource_register_device(struct device *dev, acpi_handle handle); -void acpi_power_resource_unregister_device(struct device *dev, acpi_handle handle); #ifdef CONFIG_ACPI_PROC_EVENT int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data); int acpi_bus_generate_proc_event4(const char *class, const char *bid, u8 type, int data); @@ -414,6 +413,8 @@ acpi_status acpi_remove_pm_notifier(struct acpi_device *adev, int acpi_device_power_state(struct device *dev, struct acpi_device *adev, u32 target_state, int d_max_in, int *d_min_p); int acpi_pm_device_sleep_state(struct device *, int *, int); +void acpi_dev_pm_add_dependent(acpi_handle handle, struct device *depdev); +void acpi_dev_pm_remove_dependent(acpi_handle handle, struct device *depdev); #else static inline acpi_status acpi_add_pm_notifier(struct acpi_device *adev, acpi_notify_handler handler, @@ -443,6 +444,10 @@ static inline int acpi_pm_device_sleep_state(struct device *d, int *p, int m) { return __acpi_device_power_state(m, p); } +static inline void acpi_dev_pm_add_dependent(acpi_handle handle, + struct device *depdev) {} +static inline void acpi_dev_pm_remove_dependent(acpi_handle handle, + struct device *depdev) {} #endif #ifdef CONFIG_PM_RUNTIME -- cgit v1.2.3 From d43e167db44b37bb284dc72fff2c3b61bb155752 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 17 Jan 2013 14:11:05 +0100 Subject: ACPI / scan: More straightforward preparation of ACPI device objects Simplify the code preparing struct acpi_device objects for registration by removing useless code, moving different pieces of code into the functions they belong to and making a couple of int functions always returning 0 void. This also fixes a possible memory leak in ACPI device registration error code path by making acpi_device_register() detach data from device->handle if device_register() fails and prepares the scanning code for special-casing ACPI power resources (next patch). Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 178 ++++++++++++++++++---------------------------------- 1 file changed, 61 insertions(+), 117 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index c6d60910e8a8..02629a810c04 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -472,6 +472,7 @@ static void acpi_free_ids(struct acpi_device *device) kfree(id->id); kfree(id); } + kfree(device->pnp.unique_id); } static void acpi_device_release(struct device *dev) @@ -479,7 +480,6 @@ static void acpi_device_release(struct device *dev) struct acpi_device *acpi_dev = to_acpi_device(dev); acpi_free_ids(acpi_dev); - kfree(acpi_dev->pnp.unique_id); kfree(acpi_dev); } @@ -623,6 +623,18 @@ static int acpi_device_register(struct acpi_device *device) struct acpi_device_bus_id *acpi_device_bus_id, *new_bus_id; int found = 0; + if (device->handle) { + acpi_status status; + + status = acpi_attach_data(device->handle, acpi_bus_data_handler, + device); + if (ACPI_FAILURE(status)) { + acpi_handle_err(device->handle, + "Unable to attach device data\n"); + return -ENODEV; + } + } + /* * Linkage * ------- @@ -637,8 +649,9 @@ static int acpi_device_register(struct acpi_device *device) new_bus_id = kzalloc(sizeof(struct acpi_device_bus_id), GFP_KERNEL); if (!new_bus_id) { - printk(KERN_ERR PREFIX "Memory allocation error\n"); - return -ENOMEM; + pr_err(PREFIX "Memory allocation error\n"); + result = -ENOMEM; + goto err_detach; } mutex_lock(&acpi_device_lock); @@ -677,7 +690,7 @@ static int acpi_device_register(struct acpi_device *device) result = device_register(&device->dev); if (result) { dev_err(&device->dev, "Error registering device\n"); - goto end; + goto err; } result = acpi_device_setup_files(device); @@ -687,12 +700,16 @@ static int acpi_device_register(struct acpi_device *device) device->removal_type = ACPI_BUS_REMOVAL_NORMAL; return 0; -end: + + err: mutex_lock(&acpi_device_lock); if (device->parent) list_del(&device->node); list_del(&device->wakeup_list); mutex_unlock(&acpi_device_lock); + + err_detach: + acpi_detach_data(device->handle, acpi_bus_data_handler); return result; } @@ -857,12 +874,6 @@ void acpi_bus_data_handler(acpi_handle handle, void *context) return; } -static int acpi_bus_get_perf_flags(struct acpi_device *device) -{ - device->performance.state = ACPI_STATE_UNKNOWN; - return 0; -} - static acpi_status acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, struct acpi_device_wakeup *wakeup) @@ -1013,12 +1024,25 @@ static void acpi_bus_get_wakeup_device_flags(struct acpi_device *device) static void acpi_bus_add_power_resource(acpi_handle handle); -static int acpi_bus_get_power_flags(struct acpi_device *device) +static void acpi_bus_get_power_flags(struct acpi_device *device) { acpi_status status = 0; acpi_handle handle = NULL; u32 i = 0; + /* Power resources cannot be power manageable. */ + if (device->device_type == ACPI_BUS_TYPE_POWER) + return; + + /* Presence of _PS0|_PR0 indicates 'power manageable' */ + status = acpi_get_handle(device->handle, "_PS0", &handle); + if (ACPI_FAILURE(status)) { + status = acpi_get_handle(device->handle, "_PR0", &handle); + if (ACPI_FAILURE(status)) + return; + } + + device->flags.power_manageable = 1; /* * Power Management Flags @@ -1084,16 +1108,13 @@ static int acpi_bus_get_power_flags(struct acpi_device *device) device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible = 1; acpi_bus_init_power(device); - - return 0; } -static int acpi_bus_get_flags(struct acpi_device *device) +static void acpi_bus_get_flags(struct acpi_device *device) { acpi_status status = AE_OK; acpi_handle temp = NULL; - /* Presence of _STA indicates 'dynamic_status' */ status = acpi_get_handle(device->handle, "_STA", &temp); if (ACPI_SUCCESS(status)) @@ -1113,21 +1134,6 @@ static int acpi_bus_get_flags(struct acpi_device *device) if (ACPI_SUCCESS(status)) device->flags.ejectable = 1; } - - /* Power resources cannot be power manageable. */ - if (device->device_type == ACPI_BUS_TYPE_POWER) - return 0; - - /* Presence of _PS0|_PR0 indicates 'power manageable' */ - status = acpi_get_handle(device->handle, "_PS0", &temp); - if (ACPI_FAILURE(status)) - status = acpi_get_handle(device->handle, "_PR0", &temp); - if (ACPI_SUCCESS(status)) - device->flags.power_manageable = 1; - - /* TBD: Performance management */ - - return 0; } static void acpi_device_get_busid(struct acpi_device *device) @@ -1352,27 +1358,18 @@ static void acpi_device_set_id(struct acpi_device *device) } } -static int acpi_device_set_context(struct acpi_device *device) +static void acpi_init_device_object(struct acpi_device *device, + acpi_handle handle, + int type, unsigned long long sta) { - acpi_status status; - - /* - * Context - * ------- - * Attach this 'struct acpi_device' to the ACPI object. This makes - * resolutions from handle->device very efficient. Fixed hardware - * devices have no handles, so we skip them. - */ - if (!device->handle) - return 0; - - status = acpi_attach_data(device->handle, - acpi_bus_data_handler, device); - if (ACPI_SUCCESS(status)) - return 0; - - printk(KERN_ERR PREFIX "Error attaching device data\n"); - return -ENODEV; + INIT_LIST_HEAD(&device->pnp.ids); + device->device_type = type; + device->handle = handle; + device->parent = acpi_bus_get_parent(handle); + STRUCT_TO_INT(device->status) = sta; + acpi_device_get_busid(device); + acpi_device_set_id(device); + acpi_bus_get_flags(device); } static int acpi_add_single_object(struct acpi_device **child, @@ -1389,78 +1386,25 @@ static int acpi_add_single_object(struct acpi_device **child, return -ENOMEM; } - INIT_LIST_HEAD(&device->pnp.ids); - device->device_type = type; - device->handle = handle; - device->parent = acpi_bus_get_parent(handle); - STRUCT_TO_INT(device->status) = sta; - - acpi_device_get_busid(device); - - /* - * Flags - * ----- - * Note that we only look for object handles -- cannot evaluate objects - * until we know the device is present and properly initialized. - */ - result = acpi_bus_get_flags(device); - if (result) - goto end; - - /* - * Initialize Device - * ----------------- - * TBD: Synch with Core's enumeration/initialization process. - */ - acpi_device_set_id(device); - - /* - * Power Management - * ---------------- - */ - if (device->flags.power_manageable) { - result = acpi_bus_get_power_flags(device); - if (result) - goto end; - } - - /* - * Wakeup device management - *----------------------- - */ + acpi_init_device_object(device, handle, type, sta); + acpi_bus_get_power_flags(device); acpi_bus_get_wakeup_device_flags(device); - /* - * Performance Management - * ---------------------- - */ - if (device->flags.performance_manageable) { - result = acpi_bus_get_perf_flags(device); - if (result) - goto end; - } - - if ((result = acpi_device_set_context(device))) - goto end; - device->flags.match_driver = match_driver; result = acpi_device_register(device); - -end: - if (!result) { - acpi_power_add_remove_device(device, true); - acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Adding %s [%s] parent %s\n", dev_name(&device->dev), - (char *) buffer.pointer, - device->parent ? dev_name(&device->parent->dev) : - "(null)")); - kfree(buffer.pointer); - *child = device; - } else + if (result) { acpi_device_release(&device->dev); + return result; + } - return result; + acpi_power_add_remove_device(device, true); + acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Added %s [%s] parent %s\n", + dev_name(&device->dev), (char *) buffer.pointer, + device->parent ? dev_name(&device->parent->dev) : "(null)")); + kfree(buffer.pointer); + *child = device; + return 0; } #define ACPI_STA_DEFAULT (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | \ -- cgit v1.2.3 From 82c7d5efaadf99fb4a26500cd5b59b6fd7659772 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 17 Jan 2013 14:11:05 +0100 Subject: ACPI / scan: Treat power resources in a special way ACPI power resources need to be treated in a special way by the namespace scanning code, because they need to be ready to use as soon as they have been discovered (even before registering ACPI device nodes using them for power management). For this reason, it doesn't make sense to separate the preparation of struct acpi_device objects representing them in the device hierarchy from the creation of struct acpi_power_resource objects actually used for power resource manipulation. Accordingly, it doesn't make sense to define non-empty .add() and .remove() callbacks in the power resources "driver" (in fact, it is questionable whether or not it is useful to register such a "driver" at all). Rearrange the code in scan.c and power.c so that power resources are initialized entirely by one routine, acpi_add_power_resource(), that also prepares their struct acpi_device objects and registers them with the driver core, telling it to use a special release routine, acpi_release_power_resource(), for removing objects that represent power resources from memory. Make the ACPI namespace scanning code in scan.c always use acpi_add_power_resource() for preparing and registering objects that represent power resources. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/internal.h | 13 ++++ drivers/acpi/power.c | 157 ++++++++++++++++++++++-------------------------- drivers/acpi/scan.c | 47 ++++++--------- 3 files changed, 102 insertions(+), 115 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index b79b4258bd6b..ce6cb24de8c7 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -34,10 +34,23 @@ int acpi_debugfs_init(void); static inline void acpi_debugfs_init(void) { return; } #endif +/* -------------------------------------------------------------------------- + Device Node Initialization / Removal + -------------------------------------------------------------------------- */ +#define ACPI_STA_DEFAULT (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | \ + ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING) + +int acpi_device_register(struct acpi_device *device, + void (*release)(struct device *)); +void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, + int type, unsigned long long sta); +void acpi_free_ids(struct acpi_device *device); + /* -------------------------------------------------------------------------- Power Resource -------------------------------------------------------------------------- */ int acpi_power_init(void); +void acpi_add_power_resource(acpi_handle handle); void acpi_power_add_remove_device(struct acpi_device *adev, bool add); int acpi_device_sleep_wake(struct acpi_device *dev, int enable, int sleep_state, int dev_state); diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 659386c4f0cb..b12933fd2e56 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -58,8 +58,7 @@ ACPI_MODULE_NAME("power"); #define ACPI_POWER_RESOURCE_STATE_ON 0x01 #define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF -static int acpi_power_add(struct acpi_device *device); -static int acpi_power_remove(struct acpi_device *device, int type); +static inline int acpi_power_add(struct acpi_device *device) { return 0; } static const struct acpi_device_id power_device_ids[] = { {ACPI_POWER_HID, 0}, @@ -76,10 +75,7 @@ static struct acpi_driver acpi_power_driver = { .name = "power", .class = ACPI_POWER_CLASS, .ids = power_device_ids, - .ops = { - .add = acpi_power_add, - .remove = acpi_power_remove, - }, + .ops.add = acpi_power_add, .drv.pm = &acpi_power_pm, }; @@ -90,9 +86,9 @@ struct acpi_power_dependent_device { }; struct acpi_power_resource { - struct acpi_device *device; + struct acpi_device device; struct list_head dependent; - acpi_bus_id name; + char *name; u32 system_level; u32 order; unsigned int ref_count; @@ -105,28 +101,14 @@ static struct list_head acpi_power_resource_list; Power Resource Management -------------------------------------------------------------------------- */ -static int -acpi_power_get_context(acpi_handle handle, - struct acpi_power_resource **resource) +static struct acpi_power_resource *acpi_power_get_context(acpi_handle handle) { - int result = 0; - struct acpi_device *device = NULL; - - - if (!resource) - return -ENODEV; - - result = acpi_bus_get_device(handle, &device); - if (result) { - printk(KERN_WARNING PREFIX "Getting context [%p]\n", handle); - return result; - } + struct acpi_device *device; - *resource = acpi_driver_data(device); - if (!*resource) - return -ENODEV; + if (acpi_bus_get_device(handle, &device)) + return NULL; - return 0; + return container_of(device, struct acpi_power_resource, device); } static int acpi_power_get_state(acpi_handle handle, int *state) @@ -171,9 +153,9 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state) acpi_handle handle = list->handles[i]; int result; - result = acpi_power_get_context(handle, &resource); - if (result) - return result; + resource = acpi_power_get_context(handle); + if (!resource) + return -ENODEV; mutex_lock(&resource->resource_lock); @@ -226,12 +208,12 @@ static int __acpi_power_on(struct acpi_power_resource *resource) { acpi_status status = AE_OK; - status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL); + status = acpi_evaluate_object(resource->device.handle, "_ON", NULL, NULL); if (ACPI_FAILURE(status)) return -ENODEV; /* Update the power resource's _device_ power state */ - resource->device->power.state = ACPI_STATE_D0; + resource->device.power.state = ACPI_STATE_D0; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] turned on\n", resource->name)); @@ -242,11 +224,11 @@ static int __acpi_power_on(struct acpi_power_resource *resource) static int acpi_power_on(acpi_handle handle) { int result = 0; - struct acpi_power_resource *resource = NULL; + struct acpi_power_resource *resource; - result = acpi_power_get_context(handle, &resource); - if (result) - return result; + resource = acpi_power_get_context(handle); + if (!resource) + return -ENODEV; mutex_lock(&resource->resource_lock); @@ -275,11 +257,11 @@ static int acpi_power_off(acpi_handle handle) { int result = 0; acpi_status status = AE_OK; - struct acpi_power_resource *resource = NULL; + struct acpi_power_resource *resource; - result = acpi_power_get_context(handle, &resource); - if (result) - return result; + resource = acpi_power_get_context(handle); + if (!resource) + return -ENODEV; mutex_lock(&resource->resource_lock); @@ -297,12 +279,12 @@ static int acpi_power_off(acpi_handle handle) goto unlock; } - status = acpi_evaluate_object(resource->device->handle, "_OFF", NULL, NULL); + status = acpi_evaluate_object(resource->device.handle, "_OFF", NULL, NULL); if (ACPI_FAILURE(status)) { result = -ENODEV; } else { /* Update the power resource's _device_ power state */ - resource->device->power.state = ACPI_STATE_D3; + resource->device.power.state = ACPI_STATE_D3; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] turned off\n", @@ -350,7 +332,11 @@ static void acpi_power_add_dependent(acpi_handle rhandle, struct acpi_power_dependent_device *dep; struct acpi_power_resource *resource; - if (!rhandle || !adev || acpi_power_get_context(rhandle, &resource)) + if (!rhandle || !adev) + return; + + resource = acpi_power_get_context(rhandle); + if (!resource) return; mutex_lock(&resource->resource_lock); @@ -378,7 +364,11 @@ static void acpi_power_remove_dependent(acpi_handle rhandle, struct acpi_power_resource *resource; struct work_struct *work = NULL; - if (!rhandle || !adev || acpi_power_get_context(rhandle, &resource)) + if (!rhandle || !adev) + return; + + resource = acpi_power_get_context(rhandle); + if (!resource) return; mutex_lock(&resource->resource_lock); @@ -648,46 +638,53 @@ int acpi_power_transition(struct acpi_device *device, int state) return result; } -/* -------------------------------------------------------------------------- - Driver Interface - -------------------------------------------------------------------------- */ +static void acpi_release_power_resource(struct device *dev) +{ + struct acpi_device *device = to_acpi_device(dev); + struct acpi_power_resource *resource; + + acpi_free_ids(device); + resource = container_of(device, struct acpi_power_resource, device); + kfree(resource); +} -static int acpi_power_add(struct acpi_device *device) +void acpi_add_power_resource(acpi_handle handle) { - int result = 0, state; - acpi_status status = AE_OK; - struct acpi_power_resource *resource = NULL; + struct acpi_power_resource *resource; + struct acpi_device *device = NULL; union acpi_object acpi_object; struct acpi_buffer buffer = { sizeof(acpi_object), &acpi_object }; + acpi_status status; + int state, result = -ENODEV; + acpi_bus_get_device(handle, &device); + if (device) + return; - if (!device) - return -EINVAL; - - resource = kzalloc(sizeof(struct acpi_power_resource), GFP_KERNEL); + resource = kzalloc(sizeof(*resource), GFP_KERNEL); if (!resource) - return -ENOMEM; + return; - resource->device = device; + device = &resource->device; + acpi_init_device_object(device, handle, ACPI_BUS_TYPE_POWER, + ACPI_STA_DEFAULT); mutex_init(&resource->resource_lock); INIT_LIST_HEAD(&resource->dependent); - strcpy(resource->name, device->pnp.bus_id); + resource->name = device->pnp.bus_id; strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_POWER_CLASS); - device->driver_data = resource; /* Evalute the object to get the system level and resource order. */ - status = acpi_evaluate_object(device->handle, NULL, NULL, &buffer); - if (ACPI_FAILURE(status)) { - result = -ENODEV; - goto end; - } + status = acpi_evaluate_object(handle, NULL, NULL, &buffer); + if (ACPI_FAILURE(status)) + goto out; + resource->system_level = acpi_object.power_resource.system_level; resource->order = acpi_object.power_resource.resource_order; - result = acpi_power_get_state(device->handle, &state); + result = acpi_power_get_state(handle, &state); if (result) - goto end; + goto out; switch (state) { case ACPI_POWER_RESOURCE_STATE_ON: @@ -698,34 +695,24 @@ static int acpi_power_add(struct acpi_device *device) break; default: device->power.state = ACPI_STATE_UNKNOWN; - break; } printk(KERN_INFO PREFIX "%s [%s] (%s)\n", acpi_device_name(device), acpi_device_bid(device), state ? "on" : "off"); - end: + device->flags.match_driver = true; + result = acpi_device_register(device, acpi_release_power_resource); + + out: if (result) - kfree(resource); + acpi_release_power_resource(&device->dev); - return result; + return; } -static int acpi_power_remove(struct acpi_device *device, int type) -{ - struct acpi_power_resource *resource; - - if (!device) - return -EINVAL; - - resource = acpi_driver_data(device); - if (!resource) - return -EINVAL; - - kfree(resource); - - return 0; -} +/* -------------------------------------------------------------------------- + Driver Interface + -------------------------------------------------------------------------- */ #ifdef CONFIG_PM_SLEEP static int acpi_power_resume(struct device *dev) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 02629a810c04..952b08af91de 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -464,7 +464,7 @@ int acpi_match_device_ids(struct acpi_device *device, } EXPORT_SYMBOL(acpi_match_device_ids); -static void acpi_free_ids(struct acpi_device *device) +void acpi_free_ids(struct acpi_device *device) { struct acpi_hardware_id *id, *tmp; @@ -617,7 +617,8 @@ struct bus_type acpi_bus_type = { .uevent = acpi_device_uevent, }; -static int acpi_device_register(struct acpi_device *device) +int acpi_device_register(struct acpi_device *device, + void (*release)(struct device *)) { int result; struct acpi_device_bus_id *acpi_device_bus_id, *new_bus_id; @@ -686,7 +687,7 @@ static int acpi_device_register(struct acpi_device *device) if (device->parent) device->dev.parent = &device->parent->dev; device->dev.bus = &acpi_bus_type; - device->dev.release = &acpi_device_release; + device->dev.release = release; result = device_register(&device->dev); if (result) { dev_err(&device->dev, "Error registering device\n"); @@ -1022,18 +1023,12 @@ static void acpi_bus_get_wakeup_device_flags(struct acpi_device *device) "error in _DSW or _PSW evaluation\n")); } -static void acpi_bus_add_power_resource(acpi_handle handle); - static void acpi_bus_get_power_flags(struct acpi_device *device) { acpi_status status = 0; acpi_handle handle = NULL; u32 i = 0; - /* Power resources cannot be power manageable. */ - if (device->device_type == ACPI_BUS_TYPE_POWER) - return; - /* Presence of _PS0|_PR0 indicates 'power manageable' */ status = acpi_get_handle(device->handle, "_PS0", &handle); if (ACPI_FAILURE(status)) { @@ -1068,8 +1063,10 @@ static void acpi_bus_get_power_flags(struct acpi_device *device) int j; device->power.flags.power_resources = 1; - for (j = 0; j < ps->resources.count; j++) - acpi_bus_add_power_resource(ps->resources.handles[j]); + for (j = 0; j < ps->resources.count; j++) { + acpi_handle rhandle = ps->resources.handles[j]; + acpi_add_power_resource(rhandle); + } } /* Evaluate "_PSx" to see if we can do explicit sets */ @@ -1358,9 +1355,8 @@ static void acpi_device_set_id(struct acpi_device *device) } } -static void acpi_init_device_object(struct acpi_device *device, - acpi_handle handle, - int type, unsigned long long sta) +void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, + int type, unsigned long long sta) { INIT_LIST_HEAD(&device->pnp.ids); device->device_type = type; @@ -1391,7 +1387,7 @@ static int acpi_add_single_object(struct acpi_device **child, acpi_bus_get_wakeup_device_flags(device); device->flags.match_driver = match_driver; - result = acpi_device_register(device); + result = acpi_device_register(device, acpi_device_release); if (result) { acpi_device_release(&device->dev); return result; @@ -1407,19 +1403,6 @@ static int acpi_add_single_object(struct acpi_device **child, return 0; } -#define ACPI_STA_DEFAULT (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | \ - ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING) - -static void acpi_bus_add_power_resource(acpi_handle handle) -{ - struct acpi_device *device = NULL; - - acpi_bus_get_device(handle, &device); - if (!device) - acpi_add_single_object(&device, handle, ACPI_BUS_TYPE_POWER, - ACPI_STA_DEFAULT, true); -} - static int acpi_bus_type_and_status(acpi_handle handle, int *type, unsigned long long *sta) { @@ -1476,6 +1459,11 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, if (result) return AE_OK; + if (type == ACPI_BUS_TYPE_POWER) { + acpi_add_power_resource(handle); + return AE_OK; + } + if (!(sta & ACPI_STA_DEVICE_PRESENT) && !(sta & ACPI_STA_DEVICE_FUNCTIONING)) { struct acpi_device_wakeup wakeup; @@ -1488,8 +1476,7 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, return AE_CTRL_DEPTH; } - acpi_add_single_object(&device, handle, type, sta, - type == ACPI_BUS_TYPE_POWER); + acpi_add_single_object(&device, handle, type, sta, false); if (!device) return AE_CTRL_DEPTH; -- cgit v1.2.3 From 781d737c7466845035e5ce02885c7436b5278b90 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 17 Jan 2013 14:11:06 +0100 Subject: ACPI: Drop power resources driver The ACPI power resources driver is not very useful, because the only thing it really does is to restore the state of the power resources that were "on" before system suspend or hibernation, but that may be achieved in a different way. Drop the ACPI power resources driver entirely and add acpi_resume_power_resources() that will walk the list of all registered power resources during system resume and turn on the ones that were "on" before the preceding system suspend or hibernation. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/power.c | 91 +++++++++++++++++++--------------------------------- drivers/acpi/scan.c | 1 - drivers/acpi/sleep.c | 2 ++ drivers/acpi/sleep.h | 2 ++ 4 files changed, 37 insertions(+), 59 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index b12933fd2e56..29803857a2ef 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -58,27 +58,6 @@ ACPI_MODULE_NAME("power"); #define ACPI_POWER_RESOURCE_STATE_ON 0x01 #define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF -static inline int acpi_power_add(struct acpi_device *device) { return 0; } - -static const struct acpi_device_id power_device_ids[] = { - {ACPI_POWER_HID, 0}, - {"", 0}, -}; -MODULE_DEVICE_TABLE(acpi, power_device_ids); - -#ifdef CONFIG_PM_SLEEP -static int acpi_power_resume(struct device *dev); -#endif -static SIMPLE_DEV_PM_OPS(acpi_power_pm, NULL, acpi_power_resume); - -static struct acpi_driver acpi_power_driver = { - .name = "power", - .class = ACPI_POWER_CLASS, - .ids = power_device_ids, - .ops.add = acpi_power_add, - .drv.pm = &acpi_power_pm, -}; - struct acpi_power_dependent_device { struct list_head node; struct acpi_device *adev; @@ -87,6 +66,7 @@ struct acpi_power_dependent_device { struct acpi_power_resource { struct acpi_device device; + struct list_head list_node; struct list_head dependent; char *name; u32 system_level; @@ -95,7 +75,8 @@ struct acpi_power_resource { struct mutex resource_lock; }; -static struct list_head acpi_power_resource_list; +static LIST_HEAD(acpi_power_resource_list); +static DEFINE_MUTEX(power_resource_list_lock); /* -------------------------------------------------------------------------- Power Resource Management @@ -643,8 +624,13 @@ static void acpi_release_power_resource(struct device *dev) struct acpi_device *device = to_acpi_device(dev); struct acpi_power_resource *resource; - acpi_free_ids(device); resource = container_of(device, struct acpi_power_resource, device); + + mutex_lock(&power_resource_list_lock); + list_del(&resource->list_node); + mutex_unlock(&power_resource_list_lock); + + acpi_free_ids(device); kfree(resource); } @@ -677,14 +663,14 @@ void acpi_add_power_resource(acpi_handle handle) /* Evalute the object to get the system level and resource order. */ status = acpi_evaluate_object(handle, NULL, NULL, &buffer); if (ACPI_FAILURE(status)) - goto out; + goto err; resource->system_level = acpi_object.power_resource.system_level; resource->order = acpi_object.power_resource.resource_order; result = acpi_power_get_state(handle, &state); if (result) - goto out; + goto err; switch (state) { case ACPI_POWER_RESOURCE_STATE_ON: @@ -702,51 +688,40 @@ void acpi_add_power_resource(acpi_handle handle) device->flags.match_driver = true; result = acpi_device_register(device, acpi_release_power_resource); - - out: if (result) - acpi_release_power_resource(&device->dev); + goto err; + mutex_lock(&power_resource_list_lock); + list_add(&resource->list_node, &acpi_power_resource_list); + mutex_unlock(&power_resource_list_lock); return; -} -/* -------------------------------------------------------------------------- - Driver Interface - -------------------------------------------------------------------------- */ + err: + acpi_release_power_resource(&device->dev); +} -#ifdef CONFIG_PM_SLEEP -static int acpi_power_resume(struct device *dev) +#ifdef CONFIG_ACPI_SLEEP +void acpi_resume_power_resources(void) { - int result = 0, state; - struct acpi_device *device; struct acpi_power_resource *resource; - if (!dev) - return -EINVAL; + mutex_lock(&power_resource_list_lock); - device = to_acpi_device(dev); - resource = acpi_driver_data(device); - if (!resource) - return -EINVAL; - - mutex_lock(&resource->resource_lock); + list_for_each_entry(resource, &acpi_power_resource_list, list_node) { + int result, state; - result = acpi_power_get_state(device->handle, &state); - if (result) - goto unlock; + mutex_lock(&resource->resource_lock); - if (state == ACPI_POWER_RESOURCE_STATE_OFF && resource->ref_count) - result = __acpi_power_on(resource); + result = acpi_power_get_state(resource->device.handle, &state); + if (!result && state == ACPI_POWER_RESOURCE_STATE_OFF + && resource->ref_count) { + dev_info(&resource->device.dev, "Turning ON\n"); + __acpi_power_on(resource); + } - unlock: - mutex_unlock(&resource->resource_lock); + mutex_unlock(&resource->resource_lock); + } - return result; + mutex_unlock(&power_resource_list_lock); } #endif - -int __init acpi_power_init(void) -{ - INIT_LIST_HEAD(&acpi_power_resource_list); - return acpi_bus_register_driver(&acpi_power_driver); -} diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 952b08af91de..c7ea9c2649a4 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1636,7 +1636,6 @@ int __init acpi_scan_init(void) printk(KERN_ERR PREFIX "Could not register bus type\n"); } - acpi_power_init(); acpi_pci_root_init(); /* diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 2fcc67d34b11..4ef0328579cc 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -386,6 +386,8 @@ static void acpi_pm_finish(void) acpi_target_sleep_state = ACPI_STATE_S0; + acpi_resume_power_resources(); + /* If we were woken with the fixed power button, provide a small * hint to userspace in the form of a wakeup event on the fixed power * button device (if it can be found). diff --git a/drivers/acpi/sleep.h b/drivers/acpi/sleep.h index 74d59c8f4678..0143540a2519 100644 --- a/drivers/acpi/sleep.h +++ b/drivers/acpi/sleep.h @@ -6,3 +6,5 @@ extern void acpi_disable_wakeup_devices(u8 sleep_state); extern struct list_head acpi_wakeup_device_list; extern struct mutex acpi_device_lock; + +extern void acpi_resume_power_resources(void); -- cgit v1.2.3 From 0b224527323669c66e0a37ae05b04034bfcdce14 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 17 Jan 2013 14:11:06 +0100 Subject: ACPI / PM: Take order attribute of power resources into account ACPI power resources have an order attribute that should be taken into account when turning them on and off, but it is not used now. Modify the power resources management code to preserve the spec-compliant ordering of power resources that power states of devices depend on (analogous changes will be done separately for power resources used for wakeup). Signed-off-by: Rafael J. Wysocki --- drivers/acpi/internal.h | 2 + drivers/acpi/power.c | 161 ++++++++++++++++++++++++++---------------------- drivers/acpi/scan.c | 31 ++++++++-- include/acpi/acpi_bus.h | 2 +- 4 files changed, 118 insertions(+), 78 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index ce6cb24de8c7..e28068a765a9 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -50,6 +50,8 @@ void acpi_free_ids(struct acpi_device *device); Power Resource -------------------------------------------------------------------------- */ int acpi_power_init(void); +void acpi_power_resources_list_add(acpi_handle handle, struct list_head *list); +void acpi_power_resources_list_free(struct list_head *list); void acpi_add_power_resource(acpi_handle handle); void acpi_power_add_remove_device(struct acpi_device *adev, bool add); int acpi_device_sleep_wake(struct acpi_device *dev, diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 06ad05288af8..22a3d00d0359 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -75,6 +75,11 @@ struct acpi_power_resource { struct mutex resource_lock; }; +struct acpi_power_resource_entry { + struct list_head node; + struct acpi_power_resource *resource; +}; + static LIST_HEAD(acpi_power_resource_list); static DEFINE_MUTEX(power_resource_list_lock); @@ -92,6 +97,41 @@ static struct acpi_power_resource *acpi_power_get_context(acpi_handle handle) return container_of(device, struct acpi_power_resource, device); } +void acpi_power_resources_list_add(acpi_handle handle, struct list_head *list) +{ + struct acpi_power_resource *resource = acpi_power_get_context(handle); + struct acpi_power_resource_entry *entry; + + if (!resource || !list) + return; + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return; + + entry->resource = resource; + if (!list_empty(list)) { + struct acpi_power_resource_entry *e; + + list_for_each_entry(e, list, node) + if (e->resource->order > resource->order) { + list_add_tail(&entry->node, &e->node); + return; + } + } + list_add_tail(&entry->node, list); +} + +void acpi_power_resources_list_free(struct list_head *list) +{ + struct acpi_power_resource_entry *entry, *e; + + list_for_each_entry_safe(entry, e, list, node) { + list_del(&entry->node); + kfree(entry); + } +} + static int acpi_power_get_state(acpi_handle handle, int *state) { acpi_status status = AE_OK; @@ -119,31 +159,23 @@ static int acpi_power_get_state(acpi_handle handle, int *state) return 0; } -static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state) +static int acpi_power_get_list_state(struct list_head *list, int *state) { + struct acpi_power_resource_entry *entry; int cur_state; - int i = 0; if (!list || !state) return -EINVAL; /* The state of the list is 'on' IFF all resources are 'on'. */ - - for (i = 0; i < list->count; i++) { - struct acpi_power_resource *resource; - acpi_handle handle = list->handles[i]; + list_for_each_entry(entry, list, node) { + struct acpi_power_resource *resource = entry->resource; + acpi_handle handle = resource->device.handle; int result; - resource = acpi_power_get_context(handle); - if (!resource) - return -ENODEV; - mutex_lock(&resource->resource_lock); - result = acpi_power_get_state(handle, &cur_state); - mutex_unlock(&resource->resource_lock); - if (result) return result; @@ -155,7 +187,6 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state) cur_state ? "on" : "off")); *state = cur_state; - return 0; } @@ -199,14 +230,9 @@ static int __acpi_power_on(struct acpi_power_resource *resource) return 0; } -static int acpi_power_on(acpi_handle handle) +static int acpi_power_on(struct acpi_power_resource *resource) { - int result = 0; - struct acpi_power_resource *resource; - - resource = acpi_power_get_context(handle); - if (!resource) - return -ENODEV; + int result = 0;; mutex_lock(&resource->resource_lock); @@ -231,15 +257,10 @@ static int acpi_power_on(acpi_handle handle) return result; } -static int acpi_power_off(acpi_handle handle) +static int acpi_power_off(struct acpi_power_resource *resource) { - int result = 0; acpi_status status = AE_OK; - struct acpi_power_resource *resource; - - resource = acpi_power_get_context(handle); - if (!resource) - return -ENODEV; + int result = 0; mutex_lock(&resource->resource_lock); @@ -271,47 +292,48 @@ static int acpi_power_off(acpi_handle handle) return result; } -static void __acpi_power_off_list(struct acpi_handle_list *list, int num_res) +static int acpi_power_off_list(struct list_head *list) { - int i; + struct acpi_power_resource_entry *entry; + int result = 0; - for (i = num_res - 1; i >= 0 ; i--) - acpi_power_off(list->handles[i]); -} + list_for_each_entry_reverse(entry, list, node) { + result = acpi_power_off(entry->resource); + if (result) + goto err; + } + return 0; -static void acpi_power_off_list(struct acpi_handle_list *list) -{ - __acpi_power_off_list(list, list->count); + err: + list_for_each_entry_continue(entry, list, node) + acpi_power_on(entry->resource); + + return result; } -static int acpi_power_on_list(struct acpi_handle_list *list) +static int acpi_power_on_list(struct list_head *list) { + struct acpi_power_resource_entry *entry; int result = 0; - int i; - for (i = 0; i < list->count; i++) { - result = acpi_power_on(list->handles[i]); - if (result) { - __acpi_power_off_list(list, i); - break; - } + list_for_each_entry(entry, list, node) { + result = acpi_power_on(entry->resource); + if (result) + goto err; } + return 0; + + err: + list_for_each_entry_continue_reverse(entry, list, node) + acpi_power_off(entry->resource); return result; } -static void acpi_power_add_dependent(acpi_handle rhandle, +static void acpi_power_add_dependent(struct acpi_power_resource *resource, struct acpi_device *adev) { struct acpi_power_dependent_device *dep; - struct acpi_power_resource *resource; - - if (!rhandle || !adev) - return; - - resource = acpi_power_get_context(rhandle); - if (!resource) - return; mutex_lock(&resource->resource_lock); @@ -331,20 +353,12 @@ static void acpi_power_add_dependent(acpi_handle rhandle, mutex_unlock(&resource->resource_lock); } -static void acpi_power_remove_dependent(acpi_handle rhandle, +static void acpi_power_remove_dependent(struct acpi_power_resource *resource, struct acpi_device *adev) { struct acpi_power_dependent_device *dep; - struct acpi_power_resource *resource; struct work_struct *work = NULL; - if (!rhandle || !adev) - return; - - resource = acpi_power_get_context(rhandle); - if (!resource) - return; - mutex_lock(&resource->resource_lock); list_for_each_entry(dep, &resource->dependent, node) @@ -366,16 +380,16 @@ void acpi_power_add_remove_device(struct acpi_device *adev, bool add) { if (adev->power.flags.power_resources) { struct acpi_device_power_state *ps; - int j; + struct acpi_power_resource_entry *entry; ps = &adev->power.states[ACPI_STATE_D0]; - for (j = 0; j < ps->resources.count; j++) { - acpi_handle rhandle = ps->resources.handles[j]; + list_for_each_entry(entry, &ps->resources, node) { + struct acpi_power_resource *resource = entry->resource; if (add) - acpi_power_add_dependent(rhandle, adev); + acpi_power_add_dependent(resource, adev); else - acpi_power_remove_dependent(rhandle, adev); + acpi_power_remove_dependent(resource, adev); } } } @@ -539,7 +553,6 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev) int acpi_power_get_inferred_state(struct acpi_device *device, int *state) { int result = 0; - struct acpi_handle_list *list = NULL; int list_state = 0; int i = 0; @@ -551,8 +564,9 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state) * required for a given D-state are 'on'. */ for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) { - list = &device->power.states[i].resources; - if (list->count < 1) + struct list_head *list = &device->power.states[i].resources; + + if (list_empty(list)) continue; result = acpi_power_get_list_state(list, &list_state); @@ -571,9 +585,12 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state) int acpi_power_on_resources(struct acpi_device *device, int state) { - if (!device || state < ACPI_STATE_D0 || state > ACPI_STATE_D3) + if (!device || state < ACPI_STATE_D0 || state > ACPI_STATE_D3_COLD) return -EINVAL; + if (state == ACPI_STATE_D3_COLD) + return 0; + return acpi_power_on_list(&device->power.states[state].resources); } @@ -584,7 +601,7 @@ int acpi_power_transition(struct acpi_device *device, int state) if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD)) return -EINVAL; - if (device->power.state == state) + if (device->power.state == state || !device->flags.power_manageable) return 0; if ((device->power.state < ACPI_STATE_D0) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index c7ea9c2649a4..d557868c0081 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -475,11 +475,25 @@ void acpi_free_ids(struct acpi_device *device) kfree(device->pnp.unique_id); } +static void acpi_free_power_resources_lists(struct acpi_device *device) +{ + int i; + + if (!device->flags.power_manageable) + return; + + for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) { + struct acpi_device_power_state *ps = &device->power.states[i]; + acpi_power_resources_list_free(&ps->resources); + } +} + static void acpi_device_release(struct device *dev) { struct acpi_device *acpi_dev = to_acpi_device(dev); acpi_free_ids(acpi_dev); + acpi_free_power_resources_lists(acpi_dev); kfree(acpi_dev); } @@ -1055,17 +1069,22 @@ static void acpi_bus_get_power_flags(struct acpi_device *device) for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) { struct acpi_device_power_state *ps = &device->power.states[i]; char object_name[5] = { '_', 'P', 'R', '0' + i, '\0' }; + struct acpi_handle_list resources; + INIT_LIST_HEAD(&ps->resources); /* Evaluate "_PRx" to se if power resources are referenced */ acpi_evaluate_reference(device->handle, object_name, NULL, - &ps->resources); - if (ps->resources.count) { + &resources); + if (resources.count) { int j; device->power.flags.power_resources = 1; - for (j = 0; j < ps->resources.count; j++) { - acpi_handle rhandle = ps->resources.handles[j]; + for (j = 0; j < resources.count; j++) { + acpi_handle rhandle = resources.handles[j]; + acpi_add_power_resource(rhandle); + acpi_power_resources_list_add(rhandle, + &ps->resources); } } @@ -1079,7 +1098,7 @@ static void acpi_bus_get_power_flags(struct acpi_device *device) * State is valid if there are means to put the device into it. * D3hot is only valid if _PR3 present. */ - if (ps->resources.count || + if (resources.count || (ps->flags.explicit_set && i < ACPI_STATE_D3_HOT)) { ps->flags.valid = 1; ps->flags.os_accessible = 1; @@ -1089,6 +1108,8 @@ static void acpi_bus_get_power_flags(struct acpi_device *device) ps->latency = -1; /* Unknown - driver assigned */ } + INIT_LIST_HEAD(&device->power.states[ACPI_STATE_D3_COLD].resources); + /* Set defaults for D0 and D3 states (always valid) */ device->power.states[ACPI_STATE_D0].flags.valid = 1; device->power.states[ACPI_STATE_D0].power = 100; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 29a1badfca55..32dc679d2c71 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -199,7 +199,7 @@ struct acpi_device_power_state { } flags; int power; /* % Power (compared to D0) */ int latency; /* Dx->D0 time (microseconds) */ - struct acpi_handle_list resources; /* Power resources referenced */ + struct list_head resources; /* Power resources referenced */ }; struct acpi_device_power { -- cgit v1.2.3 From 993cbe595dda731471a07f4f65575fadedc570dc Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 17 Jan 2013 14:11:06 +0100 Subject: ACPI / PM: Take order attribute of wakeup power resources into account ACPI power resources have an order attribute that should be taken into account when turning them on and off, but it is not used now. Modify the power resources management code to preserve the spec-compliant ordering of wakeup power resources. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/power.c | 44 ++++++++++++++++---------------------------- drivers/acpi/scan.c | 26 ++++++++++++++++---------- include/acpi/acpi_bus.h | 2 +- 3 files changed, 33 insertions(+), 39 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 22a3d00d0359..242feca231eb 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -469,7 +469,7 @@ int acpi_device_sleep_wake(struct acpi_device *dev, */ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state) { - int i, err = 0; + int err = 0; if (!dev || !dev->wakeup.flags.valid) return -EINVAL; @@ -479,24 +479,17 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state) if (dev->wakeup.prepare_count++) goto out; - /* Open power resource */ - for (i = 0; i < dev->wakeup.resources.count; i++) { - int ret = acpi_power_on(dev->wakeup.resources.handles[i]); - if (ret) { - printk(KERN_ERR PREFIX "Transition power state\n"); - dev->wakeup.flags.valid = 0; - err = -ENODEV; - goto err_out; - } + err = acpi_power_on_list(&dev->wakeup.resources); + if (err) { + dev_err(&dev->dev, "Cannot turn wakeup power resources on\n"); + dev->wakeup.flags.valid = 0; + } else { + /* + * Passing 3 as the third argument below means the device may be + * put into arbitrary power state afterward. + */ + err = acpi_device_sleep_wake(dev, 1, sleep_state, 3); } - - /* - * Passing 3 as the third argument below means the device may be placed - * in arbitrary power state afterwards. - */ - err = acpi_device_sleep_wake(dev, 1, sleep_state, 3); - - err_out: if (err) dev->wakeup.prepare_count = 0; @@ -513,7 +506,7 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state) */ int acpi_disable_wakeup_device_power(struct acpi_device *dev) { - int i, err = 0; + int err = 0; if (!dev || !dev->wakeup.flags.valid) return -EINVAL; @@ -534,15 +527,10 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev) if (err) goto out; - /* Close power resource */ - for (i = 0; i < dev->wakeup.resources.count; i++) { - int ret = acpi_power_off(dev->wakeup.resources.handles[i]); - if (ret) { - printk(KERN_ERR PREFIX "Transition power state\n"); - dev->wakeup.flags.valid = 0; - err = -ENODEV; - goto out; - } + err = acpi_power_off_list(&dev->wakeup.resources); + if (err) { + dev_err(&dev->dev, "Cannot turn wakeup power resources off\n"); + dev->wakeup.flags.valid = 0; } out: diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index d557868c0081..e4ac46a9c664 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -479,6 +479,9 @@ static void acpi_free_power_resources_lists(struct acpi_device *device) { int i; + if (device->wakeup.flags.valid) + acpi_power_resources_list_free(&device->wakeup.resources); + if (!device->flags.power_manageable) return; @@ -902,6 +905,8 @@ acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, if (!wakeup) return AE_BAD_PARAMETER; + INIT_LIST_HEAD(&wakeup->resources); + /* _PRW */ status = acpi_evaluate_object(handle, "_PRW", NULL, &buffer); if (ACPI_FAILURE(status)) { @@ -948,19 +953,17 @@ acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, } wakeup->sleep_state = element->integer.value; - if ((package->package.count - 2) > ACPI_MAX_HANDLES) { - status = AE_NO_MEMORY; - goto out; - } - wakeup->resources.count = package->package.count - 2; - for (i = 0; i < wakeup->resources.count; i++) { - element = &(package->package.elements[i + 2]); + for (i = 2; i < package->package.count; i++) { + acpi_handle rhandle; + + element = &(package->package.elements[i]); if (element->type != ACPI_TYPE_LOCAL_REFERENCE) { status = AE_BAD_DATA; goto out; } - - wakeup->resources.handles[i] = element->reference.handle; + rhandle = element->reference.handle; + acpi_add_power_resource(rhandle); + acpi_power_resources_list_add(rhandle, &wakeup->resources); } acpi_setup_gpe_for_wake(handle, wakeup->gpe_device, wakeup->gpe_number); @@ -1018,6 +1021,7 @@ static void acpi_bus_get_wakeup_device_flags(struct acpi_device *device) status = acpi_bus_extract_wakeup_device_power_package(device->handle, &device->wakeup); if (ACPI_FAILURE(status)) { + acpi_power_resources_list_free(&device->wakeup.resources); ACPI_EXCEPTION((AE_INFO, status, "Extracting _PRW package")); return; } @@ -1491,9 +1495,11 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, acpi_handle temp; status = acpi_get_handle(handle, "_PRW", &temp); - if (ACPI_SUCCESS(status)) + if (ACPI_SUCCESS(status)) { acpi_bus_extract_wakeup_device_power_package(handle, &wakeup); + acpi_power_resources_list_free(&wakeup.resources); + } return AE_CTRL_DEPTH; } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 32dc679d2c71..a272c3156999 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -242,7 +242,7 @@ struct acpi_device_wakeup { acpi_handle gpe_device; u64 gpe_number; u64 sleep_state; - struct acpi_handle_list resources; + struct list_head resources; struct acpi_device_wakeup_flags flags; int prepare_count; }; -- cgit v1.2.3 From f33ce568366ab61b5685bae07306e40f17beb943 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 17 Jan 2013 14:11:06 +0100 Subject: ACPI / scan: Move power state initialization to a separate routine To reduce indentation level and improve code readability, move the initialization code related to device power states from acpi_bus_get_power_flags() to a new routine, acpi_bus_init_power_state(). Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 87 ++++++++++++++++++++++++++++------------------------- 1 file changed, 46 insertions(+), 41 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index e4ac46a9c664..10c98ff6b026 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1041,6 +1041,50 @@ static void acpi_bus_get_wakeup_device_flags(struct acpi_device *device) "error in _DSW or _PSW evaluation\n")); } +static void acpi_bus_init_power_state(struct acpi_device *device, int state) +{ + struct acpi_device_power_state *ps = &device->power.states[state]; + char object_name[5] = { '_', 'P', 'R', '0' + state, '\0' }; + struct acpi_handle_list resources; + acpi_handle handle; + acpi_status status; + + INIT_LIST_HEAD(&ps->resources); + + /* Evaluate "_PRx" to se if power resources are referenced */ + acpi_evaluate_reference(device->handle, object_name, NULL, &resources); + if (resources.count) { + int j; + + device->power.flags.power_resources = 1; + for (j = 0; j < resources.count; j++) { + acpi_handle rhandle = resources.handles[j]; + + acpi_add_power_resource(rhandle); + acpi_power_resources_list_add(rhandle, &ps->resources); + } + } + + /* Evaluate "_PSx" to see if we can do explicit sets */ + object_name[2] = 'S'; + status = acpi_get_handle(device->handle, object_name, &handle); + if (ACPI_SUCCESS(status)) + ps->flags.explicit_set = 1; + + /* + * State is valid if there are means to put the device into it. + * D3hot is only valid if _PR3 present. + */ + if (resources.count + || (ps->flags.explicit_set && state < ACPI_STATE_D3_HOT)) { + ps->flags.valid = 1; + ps->flags.os_accessible = 1; + } + + ps->power = -1; /* Unknown - driver assigned */ + ps->latency = -1; /* Unknown - driver assigned */ +} + static void acpi_bus_get_power_flags(struct acpi_device *device) { acpi_status status = 0; @@ -1070,47 +1114,8 @@ static void acpi_bus_get_power_flags(struct acpi_device *device) /* * Enumerate supported power management states */ - for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) { - struct acpi_device_power_state *ps = &device->power.states[i]; - char object_name[5] = { '_', 'P', 'R', '0' + i, '\0' }; - struct acpi_handle_list resources; - - INIT_LIST_HEAD(&ps->resources); - /* Evaluate "_PRx" to se if power resources are referenced */ - acpi_evaluate_reference(device->handle, object_name, NULL, - &resources); - if (resources.count) { - int j; - - device->power.flags.power_resources = 1; - for (j = 0; j < resources.count; j++) { - acpi_handle rhandle = resources.handles[j]; - - acpi_add_power_resource(rhandle); - acpi_power_resources_list_add(rhandle, - &ps->resources); - } - } - - /* Evaluate "_PSx" to see if we can do explicit sets */ - object_name[2] = 'S'; - status = acpi_get_handle(device->handle, object_name, &handle); - if (ACPI_SUCCESS(status)) - ps->flags.explicit_set = 1; - - /* - * State is valid if there are means to put the device into it. - * D3hot is only valid if _PR3 present. - */ - if (resources.count || - (ps->flags.explicit_set && i < ACPI_STATE_D3_HOT)) { - ps->flags.valid = 1; - ps->flags.os_accessible = 1; - } - - ps->power = -1; /* Unknown - driver assigned */ - ps->latency = -1; /* Unknown - driver assigned */ - } + for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) + acpi_bus_init_power_state(device, i); INIT_LIST_HEAD(&device->power.states[ACPI_STATE_D3_COLD].resources); -- cgit v1.2.3 From 8bc5053bcdff09a6d1c6a61a79a9014884aa0a14 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 17 Jan 2013 14:11:07 +0100 Subject: ACPI / scan: Remove unnecessary initialization of local variables The local variables in acpi_bus_get_power_flags() need not be initialized upfront, so change the code accordingly. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 10c98ff6b026..8da315418d94 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1087,9 +1087,9 @@ static void acpi_bus_init_power_state(struct acpi_device *device, int state) static void acpi_bus_get_power_flags(struct acpi_device *device) { - acpi_status status = 0; - acpi_handle handle = NULL; - u32 i = 0; + acpi_status status; + acpi_handle handle; + u32 i; /* Presence of _PS0|_PR0 indicates 'power manageable' */ status = acpi_get_handle(device->handle, "_PS0", &handle); -- cgit v1.2.3 From ef85bdbec444b42775a18580c6bfe1307a63ef0f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 17 Jan 2013 14:11:07 +0100 Subject: ACPI / scan: Consolidate extraction of power resources lists The lists of ACPI power resources are currently extracted in two different ways, one for wakeup power resources and one for power resources that device power states depend on. There is no reason why it should be done differently in those two cases, so introduce a common routine for extracting power resources lists from data returned by AML, acpi_extract_power_resources(), and make the namespace scanning code use it for both wakeup and device power states power resources. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/internal.h | 4 +++- drivers/acpi/power.c | 32 ++++++++++++++++++++++++++++++- drivers/acpi/scan.c | 51 ++++++++++++++++++++----------------------------- 3 files changed, 55 insertions(+), 32 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index e28068a765a9..c35435e3d760 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -50,8 +50,10 @@ void acpi_free_ids(struct acpi_device *device); Power Resource -------------------------------------------------------------------------- */ int acpi_power_init(void); -void acpi_power_resources_list_add(acpi_handle handle, struct list_head *list); void acpi_power_resources_list_free(struct list_head *list); +acpi_status acpi_extract_power_resources(union acpi_object *package, + unsigned int start, + struct list_head *list); void acpi_add_power_resource(acpi_handle handle); void acpi_power_add_remove_device(struct acpi_device *adev, bool add); int acpi_device_sleep_wake(struct acpi_device *dev, diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 242feca231eb..4b93c97aff9f 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -97,7 +97,8 @@ static struct acpi_power_resource *acpi_power_get_context(acpi_handle handle) return container_of(device, struct acpi_power_resource, device); } -void acpi_power_resources_list_add(acpi_handle handle, struct list_head *list) +static void acpi_power_resources_list_add(acpi_handle handle, + struct list_head *list) { struct acpi_power_resource *resource = acpi_power_get_context(handle); struct acpi_power_resource_entry *entry; @@ -132,6 +133,35 @@ void acpi_power_resources_list_free(struct list_head *list) } } +acpi_status acpi_extract_power_resources(union acpi_object *package, + unsigned int start, + struct list_head *list) +{ + acpi_status status = AE_OK; + unsigned int i; + + for (i = start; i < package->package.count; i++) { + union acpi_object *element = &package->package.elements[i]; + acpi_handle rhandle; + + if (element->type != ACPI_TYPE_LOCAL_REFERENCE) { + status = AE_BAD_DATA; + break; + } + rhandle = element->reference.handle; + if (!rhandle) { + status = AE_NULL_ENTRY; + break; + } + acpi_add_power_resource(rhandle); + acpi_power_resources_list_add(rhandle, list); + } + if (ACPI_FAILURE(status)) + acpi_power_resources_list_free(list); + + return status; +} + static int acpi_power_get_state(acpi_handle handle, int *state) { acpi_status status = AE_OK; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 8da315418d94..d80df969f64a 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -900,7 +900,6 @@ acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, union acpi_object *package = NULL; union acpi_object *element = NULL; acpi_status status; - int i = 0; if (!wakeup) return AE_BAD_PARAMETER; @@ -953,18 +952,9 @@ acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, } wakeup->sleep_state = element->integer.value; - for (i = 2; i < package->package.count; i++) { - acpi_handle rhandle; - - element = &(package->package.elements[i]); - if (element->type != ACPI_TYPE_LOCAL_REFERENCE) { - status = AE_BAD_DATA; - goto out; - } - rhandle = element->reference.handle; - acpi_add_power_resource(rhandle); - acpi_power_resources_list_add(rhandle, &wakeup->resources); - } + status = acpi_extract_power_resources(package, 2, &wakeup->resources); + if (ACPI_FAILURE(status)) + goto out; acpi_setup_gpe_for_wake(handle, wakeup->gpe_device, wakeup->gpe_number); @@ -1021,7 +1011,6 @@ static void acpi_bus_get_wakeup_device_flags(struct acpi_device *device) status = acpi_bus_extract_wakeup_device_power_package(device->handle, &device->wakeup); if (ACPI_FAILURE(status)) { - acpi_power_resources_list_free(&device->wakeup.resources); ACPI_EXCEPTION((AE_INFO, status, "Extracting _PRW package")); return; } @@ -1044,30 +1033,32 @@ static void acpi_bus_get_wakeup_device_flags(struct acpi_device *device) static void acpi_bus_init_power_state(struct acpi_device *device, int state) { struct acpi_device_power_state *ps = &device->power.states[state]; - char object_name[5] = { '_', 'P', 'R', '0' + state, '\0' }; - struct acpi_handle_list resources; + char pathname[5] = { '_', 'P', 'R', '0' + state, '\0' }; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; acpi_handle handle; acpi_status status; INIT_LIST_HEAD(&ps->resources); - /* Evaluate "_PRx" to se if power resources are referenced */ - acpi_evaluate_reference(device->handle, object_name, NULL, &resources); - if (resources.count) { - int j; - - device->power.flags.power_resources = 1; - for (j = 0; j < resources.count; j++) { - acpi_handle rhandle = resources.handles[j]; - - acpi_add_power_resource(rhandle); - acpi_power_resources_list_add(rhandle, &ps->resources); + /* Evaluate "_PRx" to get referenced power resources */ + status = acpi_evaluate_object(device->handle, pathname, NULL, &buffer); + if (ACPI_SUCCESS(status)) { + union acpi_object *package = buffer.pointer; + + if (buffer.length && package + && package->type == ACPI_TYPE_PACKAGE + && package->package.count) { + status = acpi_extract_power_resources(package, 0, + &ps->resources); + if (ACPI_SUCCESS(status)) + device->power.flags.power_resources = 1; } + ACPI_FREE(buffer.pointer); } /* Evaluate "_PSx" to see if we can do explicit sets */ - object_name[2] = 'S'; - status = acpi_get_handle(device->handle, object_name, &handle); + pathname[2] = 'S'; + status = acpi_get_handle(device->handle, pathname, &handle); if (ACPI_SUCCESS(status)) ps->flags.explicit_set = 1; @@ -1075,7 +1066,7 @@ static void acpi_bus_init_power_state(struct acpi_device *device, int state) * State is valid if there are means to put the device into it. * D3hot is only valid if _PR3 present. */ - if (resources.count + if (!list_empty(&ps->resources) || (ps->flags.explicit_set && state < ACPI_STATE_D3_HOT)) { ps->flags.valid = 1; ps->flags.os_accessible = 1; -- cgit v1.2.3 From e88c9c603b2ad0cd0fbe90afedba3f1becbbeb79 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 17 Jan 2013 14:11:07 +0100 Subject: ACPI: Take power resource initialization errors into account Some ACPI power resource initialization errors, like memory allocation errors, are not taken into account appropriately in some cases, which may lead to a device having an incomplete list of power resources that one of its power states depends on, for one example. Rework the power resource initialization and namespace scanning code so that power resource initialization errors are treated more seriously. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/internal.h | 7 +++--- drivers/acpi/power.c | 44 +++++++++++++++++++++----------------- drivers/acpi/scan.c | 57 ++++++++++++++++++++++--------------------------- 3 files changed, 53 insertions(+), 55 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index c35435e3d760..8a6c67c9da42 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -51,10 +51,9 @@ void acpi_free_ids(struct acpi_device *device); -------------------------------------------------------------------------- */ int acpi_power_init(void); void acpi_power_resources_list_free(struct list_head *list); -acpi_status acpi_extract_power_resources(union acpi_object *package, - unsigned int start, - struct list_head *list); -void acpi_add_power_resource(acpi_handle handle); +int acpi_extract_power_resources(union acpi_object *package, unsigned int start, + struct list_head *list); +int acpi_add_power_resource(acpi_handle handle); void acpi_power_add_remove_device(struct acpi_device *adev, bool add); int acpi_device_sleep_wake(struct acpi_device *dev, int enable, int sleep_state, int dev_state); diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 4b93c97aff9f..1600f753fafe 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -97,18 +97,18 @@ static struct acpi_power_resource *acpi_power_get_context(acpi_handle handle) return container_of(device, struct acpi_power_resource, device); } -static void acpi_power_resources_list_add(acpi_handle handle, - struct list_head *list) +static int acpi_power_resources_list_add(acpi_handle handle, + struct list_head *list) { struct acpi_power_resource *resource = acpi_power_get_context(handle); struct acpi_power_resource_entry *entry; if (!resource || !list) - return; + return -EINVAL; entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (!entry) - return; + return -ENOMEM; entry->resource = resource; if (!list_empty(list)) { @@ -117,10 +117,11 @@ static void acpi_power_resources_list_add(acpi_handle handle, list_for_each_entry(e, list, node) if (e->resource->order > resource->order) { list_add_tail(&entry->node, &e->node); - return; + return 0; } } list_add_tail(&entry->node, list); + return 0; } void acpi_power_resources_list_free(struct list_head *list) @@ -133,33 +134,37 @@ void acpi_power_resources_list_free(struct list_head *list) } } -acpi_status acpi_extract_power_resources(union acpi_object *package, - unsigned int start, - struct list_head *list) +int acpi_extract_power_resources(union acpi_object *package, unsigned int start, + struct list_head *list) { - acpi_status status = AE_OK; unsigned int i; + int err = 0; for (i = start; i < package->package.count; i++) { union acpi_object *element = &package->package.elements[i]; acpi_handle rhandle; if (element->type != ACPI_TYPE_LOCAL_REFERENCE) { - status = AE_BAD_DATA; + err = -ENODATA; break; } rhandle = element->reference.handle; if (!rhandle) { - status = AE_NULL_ENTRY; + err = -ENODEV; break; } - acpi_add_power_resource(rhandle); - acpi_power_resources_list_add(rhandle, list); + err = acpi_add_power_resource(rhandle); + if (err) + break; + + err = acpi_power_resources_list_add(rhandle, list); + if (err) + break; } - if (ACPI_FAILURE(status)) + if (err) acpi_power_resources_list_free(list); - return status; + return err; } static int acpi_power_get_state(acpi_handle handle, int *state) @@ -662,7 +667,7 @@ static void acpi_release_power_resource(struct device *dev) kfree(resource); } -void acpi_add_power_resource(acpi_handle handle) +int acpi_add_power_resource(acpi_handle handle) { struct acpi_power_resource *resource; struct acpi_device *device = NULL; @@ -673,11 +678,11 @@ void acpi_add_power_resource(acpi_handle handle) acpi_bus_get_device(handle, &device); if (device) - return; + return 0; resource = kzalloc(sizeof(*resource), GFP_KERNEL); if (!resource) - return; + return -ENOMEM; device = &resource->device; acpi_init_device_object(device, handle, ACPI_BUS_TYPE_POWER, @@ -712,10 +717,11 @@ void acpi_add_power_resource(acpi_handle handle) mutex_lock(&power_resource_list_lock); list_add(&resource->list_node, &acpi_power_resource_list); mutex_unlock(&power_resource_list_lock); - return; + return 0; err: acpi_release_power_resource(&device->dev); + return result; } #ifdef CONFIG_ACPI_SLEEP diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index d80df969f64a..0b6a6b4febd6 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -892,17 +892,17 @@ void acpi_bus_data_handler(acpi_handle handle, void *context) return; } -static acpi_status -acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, - struct acpi_device_wakeup *wakeup) +static int acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, + struct acpi_device_wakeup *wakeup) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *package = NULL; union acpi_object *element = NULL; acpi_status status; + int err = -ENODATA; if (!wakeup) - return AE_BAD_PARAMETER; + return -EINVAL; INIT_LIST_HEAD(&wakeup->resources); @@ -910,29 +910,25 @@ acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, status = acpi_evaluate_object(handle, "_PRW", NULL, &buffer); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PRW")); - return status; + return err; } package = (union acpi_object *)buffer.pointer; - if (!package || (package->package.count < 2)) { - status = AE_BAD_DATA; + if (!package || package->package.count < 2) goto out; - } element = &(package->package.elements[0]); - if (!element) { - status = AE_BAD_DATA; + if (!element) goto out; - } + if (element->type == ACPI_TYPE_PACKAGE) { if ((element->package.count < 2) || (element->package.elements[0].type != ACPI_TYPE_LOCAL_REFERENCE) - || (element->package.elements[1].type != ACPI_TYPE_INTEGER)) { - status = AE_BAD_DATA; + || (element->package.elements[1].type != ACPI_TYPE_INTEGER)) goto out; - } + wakeup->gpe_device = element->package.elements[0].reference.handle; wakeup->gpe_number = @@ -941,27 +937,24 @@ acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, wakeup->gpe_device = NULL; wakeup->gpe_number = element->integer.value; } else { - status = AE_BAD_DATA; goto out; } element = &(package->package.elements[1]); - if (element->type != ACPI_TYPE_INTEGER) { - status = AE_BAD_DATA; + if (element->type != ACPI_TYPE_INTEGER) goto out; - } + wakeup->sleep_state = element->integer.value; - status = acpi_extract_power_resources(package, 2, &wakeup->resources); - if (ACPI_FAILURE(status)) + err = acpi_extract_power_resources(package, 2, &wakeup->resources); + if (err) goto out; acpi_setup_gpe_for_wake(handle, wakeup->gpe_device, wakeup->gpe_number); out: kfree(buffer.pointer); - - return status; + return err; } static void acpi_bus_set_run_wake_flags(struct acpi_device *device) @@ -1001,17 +994,17 @@ static void acpi_bus_get_wakeup_device_flags(struct acpi_device *device) { acpi_handle temp; acpi_status status = 0; - int psw_error; + int err; /* Presence of _PRW indicates wake capable */ status = acpi_get_handle(device->handle, "_PRW", &temp); if (ACPI_FAILURE(status)) return; - status = acpi_bus_extract_wakeup_device_power_package(device->handle, - &device->wakeup); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Extracting _PRW package")); + err = acpi_bus_extract_wakeup_device_power_package(device->handle, + &device->wakeup); + if (err) { + dev_err(&device->dev, "_PRW evaluation error: %d\n", err); return; } @@ -1024,8 +1017,8 @@ static void acpi_bus_get_wakeup_device_flags(struct acpi_device *device) * So it is necessary to call _DSW object first. Only when it is not * present will the _PSW object used. */ - psw_error = acpi_device_sleep_wake(device, 0, 0, 0); - if (psw_error) + err = acpi_device_sleep_wake(device, 0, 0, 0); + if (err) ACPI_DEBUG_PRINT((ACPI_DB_INFO, "error in _DSW or _PSW evaluation\n")); } @@ -1048,9 +1041,9 @@ static void acpi_bus_init_power_state(struct acpi_device *device, int state) if (buffer.length && package && package->type == ACPI_TYPE_PACKAGE && package->package.count) { - status = acpi_extract_power_resources(package, 0, - &ps->resources); - if (ACPI_SUCCESS(status)) + int err = acpi_extract_power_resources(package, 0, + &ps->resources); + if (!err) device->power.flags.power_resources = 1; } ACPI_FREE(buffer.pointer); -- cgit v1.2.3 From 0596a52b8357b25185e06af32973225baeb7196a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 17 Jan 2013 14:11:07 +0100 Subject: ACPI: Use system level attribute of wakeup power resources The system level attribute of ACPI power resources is the lowest system sleep level (S0, S2 etc.) in which the given resource can be "on" (ACPI 5.0, Section 7.1). On the other hand, wakeup power resources have to be "on" for devices depending on them to be able to signal wakeup. Therefore devices cannot wake up the system from sleep states higher than the minimum of the system level attributes of their wakeup power resources. Use the wakeup power resources' system level values to get the deepest system sleep state (highest system sleep level) the given device can wake up the system from. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/internal.h | 1 + drivers/acpi/power.c | 14 ++++++++++++++ drivers/acpi/scan.c | 11 +++++++++++ 3 files changed, 26 insertions(+) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 8a6c67c9da42..07f61dbd8136 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -55,6 +55,7 @@ int acpi_extract_power_resources(union acpi_object *package, unsigned int start, struct list_head *list); int acpi_add_power_resource(acpi_handle handle); void acpi_power_add_remove_device(struct acpi_device *adev, bool add); +int acpi_power_min_system_level(struct list_head *list); int acpi_device_sleep_wake(struct acpi_device *dev, int enable, int sleep_state, int dev_state); int acpi_power_get_inferred_state(struct acpi_device *device, int *state); diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 1600f753fafe..089a7c39348f 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -429,6 +429,20 @@ void acpi_power_add_remove_device(struct acpi_device *adev, bool add) } } +int acpi_power_min_system_level(struct list_head *list) +{ + struct acpi_power_resource_entry *entry; + int system_level = 5; + + list_for_each_entry(entry, list, node) { + struct acpi_power_resource *resource = entry->resource; + + if (system_level > resource->system_level) + system_level = resource->system_level; + } + return system_level; +} + /* -------------------------------------------------------------------------- Device Power Management -------------------------------------------------------------------------- */ diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 0b6a6b4febd6..1fc57a349a3c 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -950,6 +950,17 @@ static int acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, if (err) goto out; + if (!list_empty(&wakeup->resources)) { + int sleep_state; + + sleep_state = acpi_power_min_system_level(&wakeup->resources); + if (sleep_state < wakeup->sleep_state) { + acpi_handle_warn(handle, "Overriding _PRW sleep state " + "(S%d) by S%d from power resources\n", + (int)wakeup->sleep_state, sleep_state); + wakeup->sleep_state = sleep_state; + } + } acpi_setup_gpe_for_wake(handle, wakeup->gpe_device, wakeup->gpe_number); out: -- cgit v1.2.3 From b8bd759acd05281abf88cddef30c57313c109697 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 19 Jan 2013 01:27:35 +0100 Subject: ACPI / scan: Drop acpi_bus_add() and use acpi_bus_scan() instead The only difference between acpi_bus_scan() and acpi_bus_add() is the invocation of acpi_update_all_gpes() in the latter which in fact is unnecessary, because acpi_update_all_gpes() has already been called by acpi_scan_init() and the way it is implemented guarantees the next invocations of it to do nothing. For this reason, drop acpi_bus_add() and make all its callers use acpi_bus_scan() directly instead of it. Additionally, rearrange the code in acpi_scan_init() slightly to improve the visibility of the acpi_update_all_gpes() call in there. Signed-off-by: Rafael J. Wysocki Acked-by: Yinghai Lu --- drivers/acpi/acpi_memhotplug.c | 2 +- drivers/acpi/container.c | 2 +- drivers/acpi/dock.c | 2 +- drivers/acpi/processor_driver.c | 2 +- drivers/acpi/scan.c | 54 ++++++++++++++++---------------------- drivers/pci/hotplug/acpiphp_glue.c | 4 +-- drivers/pci/hotplug/sgi_hotplug.c | 4 +-- include/acpi/acpi_bus.h | 2 +- 8 files changed, 31 insertions(+), 41 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index eaddb7a89c70..15ea22fc1f5e 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c @@ -167,7 +167,7 @@ acpi_memory_get_device(acpi_handle handle, * Now add the notified device. This creates the acpi_device * and invokes .add function */ - result = acpi_bus_add(handle); + result = acpi_bus_scan(handle); if (result) { acpi_handle_warn(handle, "Cannot add acpi bus\n"); return -EINVAL; diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index f8fb2281f34a..cc79d3e53a39 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -166,7 +166,7 @@ static void container_notify_cb(acpi_handle handle, u32 type, void *context) if (!ACPI_FAILURE(status) || device) break; - result = acpi_bus_add(handle); + result = acpi_bus_scan(handle); if (result) { acpi_handle_warn(handle, "Failed to add container\n"); break; diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 4a56a8b2e51e..420d24fc9388 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -317,7 +317,7 @@ static struct acpi_device * dock_create_acpi_device(acpi_handle handle) * no device created for this object, * so we should create one. */ - ret = acpi_bus_add(handle); + ret = acpi_bus_scan(handle); if (ret) pr_debug("error adding bus, %x\n", -ret); diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index a24ee43e06e4..9c5929a17d3a 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -699,7 +699,7 @@ static void acpi_processor_hotplug_notify(acpi_handle handle, if (!acpi_bus_get_device(handle, &device)) break; - result = acpi_bus_add(handle); + result = acpi_bus_scan(handle); if (result) { acpi_handle_err(handle, "Unable to add the device\n"); break; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 388b59c096dc..7c43bdc36abc 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1577,26 +1577,8 @@ static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, return status; } -static int acpi_bus_scan(acpi_handle handle) -{ - void *device = NULL; - - if (ACPI_SUCCESS(acpi_bus_check_add(handle, 0, NULL, &device))) - acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, - acpi_bus_check_add, NULL, NULL, &device); - - if (!device) - return -ENODEV; - - if (ACPI_SUCCESS(acpi_bus_device_attach(handle, 0, NULL, NULL))) - acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, - acpi_bus_device_attach, NULL, NULL, NULL); - - return 0; -} - /** - * acpi_bus_add - Add ACPI device node objects in a given namespace scope. + * acpi_bus_scan - Add ACPI device node objects in a given namespace scope. * @handle: Root of the namespace scope to scan. * * Scan a given ACPI tree (probably recently hot-plugged) and create and add @@ -1607,18 +1589,24 @@ static int acpi_bus_scan(acpi_handle handle) * in the table trunk from which the kernel could create a device and add an * appropriate driver. */ -int acpi_bus_add(acpi_handle handle) +int acpi_bus_scan(acpi_handle handle) { - int err; + void *device = NULL; - err = acpi_bus_scan(handle); - if (err) - return err; + if (ACPI_SUCCESS(acpi_bus_check_add(handle, 0, NULL, &device))) + acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, + acpi_bus_check_add, NULL, NULL, &device); + + if (!device) + return -ENODEV; + + if (ACPI_SUCCESS(acpi_bus_device_attach(handle, 0, NULL, NULL))) + acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, + acpi_bus_device_attach, NULL, NULL, NULL); - acpi_update_all_gpes(); return 0; } -EXPORT_SYMBOL(acpi_bus_add); +EXPORT_SYMBOL(acpi_bus_scan); static acpi_status acpi_bus_device_detach(acpi_handle handle, u32 lvl_not_used, void *not_used, void **ret_not_used) @@ -1708,13 +1696,15 @@ int __init acpi_scan_init(void) return result; result = acpi_bus_get_device(ACPI_ROOT_OBJECT, &acpi_root); - if (!result) - result = acpi_bus_scan_fixed(); - if (result) + return result; + + result = acpi_bus_scan_fixed(); + if (result) { acpi_device_unregister(acpi_root); - else - acpi_update_all_gpes(); + return result; + } - return result; + acpi_update_all_gpes(); + return 0; } diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 22006f2d9dd5..9e2b1f6dbe41 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -746,7 +746,7 @@ static int acpiphp_bus_add(struct acpiphp_func *func) dbg("acpi_bus_trim return %x\n", ret_val); } - ret_val = acpi_bus_add(func->handle); + ret_val = acpi_bus_scan(func->handle); if (!ret_val) ret_val = acpi_bus_get_device(func->handle, &device); @@ -1129,7 +1129,7 @@ static void handle_bridge_insertion(acpi_handle handle, u32 type) return; } - if (acpi_bus_add(handle)) { + if (acpi_bus_scan(handle)) { err("cannot add bridge to acpi list\n"); return; } diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c index 2e006ee5738b..ae606b3e991e 100644 --- a/drivers/pci/hotplug/sgi_hotplug.c +++ b/drivers/pci/hotplug/sgi_hotplug.c @@ -447,9 +447,9 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot) if (ACPI_SUCCESS(ret) && (adr>>16) == (slot->device_num + 1)) { - ret = acpi_bus_add(chandle); + ret = acpi_bus_scan(chandle); if (ACPI_FAILURE(ret)) { - printk(KERN_ERR "%s: acpi_bus_add " + printk(KERN_ERR "%s: acpi_bus_scan " "failed (0x%x) for slot %d " "func %d\n", __func__, ret, (int)(adr>>16), diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 567851b4f043..2c722deb2490 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -346,7 +346,7 @@ static inline int acpi_bus_generate_proc_event(struct acpi_device *device, u8 ty #endif int acpi_bus_register_driver(struct acpi_driver *driver); void acpi_bus_unregister_driver(struct acpi_driver *driver); -int acpi_bus_add(acpi_handle handle); +int acpi_bus_scan(acpi_handle handle); void acpi_bus_hot_remove_device(void *context); int acpi_bus_trim(struct acpi_device *start); acpi_status acpi_bus_get_ejd(acpi_handle handle, acpi_handle * ejd); -- cgit v1.2.3 From 13176bbf183c82281a0e65519780ffebff5abc9d Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 17 Jan 2013 09:59:33 +0000 Subject: ACPI: add support for CSRT table Core System Resources Table (CSRT) is a proprietary ACPI table that contains resources for certain devices that are not found in the DSDT table. Typically a shared DMA controller might be found here. This patch adds support for this table. We go through all entries in the table and make platform devices of them. The resources from the table are passed with the platform device. There is one special resource in the table and it is the DMA request line base and number of request lines. This information might be needed by the DMA controller driver as it needs to map the ACPI DMA request line number to the actual request line understood by the hardware. This range is passed as IORESOURCE_DMA resource. Signed-off-by: Andy Shevchenko Signed-off-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki --- drivers/acpi/Makefile | 1 + drivers/acpi/csrt.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/acpi/internal.h | 1 + drivers/acpi/scan.c | 1 + 4 files changed, 162 insertions(+) create mode 100644 drivers/acpi/csrt.c (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 4ee2e753306a..474fcfeba66c 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -38,6 +38,7 @@ acpi-y += processor_core.o acpi-y += ec.o acpi-$(CONFIG_ACPI_DOCK) += dock.o acpi-y += pci_root.o pci_link.o pci_irq.o +acpi-y += csrt.o acpi-y += acpi_platform.o acpi-y += power.o acpi-y += event.o diff --git a/drivers/acpi/csrt.c b/drivers/acpi/csrt.c new file mode 100644 index 000000000000..5c15a91faf0b --- /dev/null +++ b/drivers/acpi/csrt.c @@ -0,0 +1,159 @@ +/* + * Support for Core System Resources Table (CSRT) + * + * Copyright (C) 2013, Intel Corporation + * Authors: Mika Westerberg + * Andy Shevchenko + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define pr_fmt(fmt) "ACPI: CSRT: " fmt + +#include +#include +#include +#include +#include +#include + +ACPI_MODULE_NAME("CSRT"); + +static int __init acpi_csrt_parse_shared_info(struct platform_device *pdev, + const struct acpi_csrt_group *grp) +{ + const struct acpi_csrt_shared_info *si; + struct resource res[3]; + size_t nres; + int ret; + + memset(res, 0, sizeof(res)); + nres = 0; + + si = (const struct acpi_csrt_shared_info *)&grp[1]; + /* + * The peripherals that are listed on CSRT typically support only + * 32-bit addresses so we only use the low part of MMIO base for + * now. + */ + if (!si->mmio_base_high && si->mmio_base_low) { + /* + * There is no size of the memory resource in shared_info + * so we assume that it is 4k here. + */ + res[nres].start = si->mmio_base_low; + res[nres].end = res[0].start + SZ_4K - 1; + res[nres++].flags = IORESOURCE_MEM; + } + + if (si->gsi_interrupt) { + int irq = acpi_register_gsi(NULL, si->gsi_interrupt, + si->interrupt_mode, + si->interrupt_polarity); + res[nres].start = irq; + res[nres].end = irq; + res[nres++].flags = IORESOURCE_IRQ; + } + + if (si->base_request_line || si->num_handshake_signals) { + /* + * We pass the driver a DMA resource describing the range + * of request lines the device supports. + */ + res[nres].start = si->base_request_line; + res[nres].end = res[nres].start + si->num_handshake_signals - 1; + res[nres++].flags = IORESOURCE_DMA; + } + + ret = platform_device_add_resources(pdev, res, nres); + if (ret) { + if (si->gsi_interrupt) + acpi_unregister_gsi(si->gsi_interrupt); + return ret; + } + + return 0; +} + +static int __init +acpi_csrt_parse_resource_group(const struct acpi_csrt_group *grp) +{ + struct platform_device *pdev; + char vendor[5], name[16]; + int ret, i; + + vendor[0] = grp->vendor_id; + vendor[1] = grp->vendor_id >> 8; + vendor[2] = grp->vendor_id >> 16; + vendor[3] = grp->vendor_id >> 24; + vendor[4] = '\0'; + + if (grp->shared_info_length != sizeof(struct acpi_csrt_shared_info)) + return -ENODEV; + + snprintf(name, sizeof(name), "%s%04X", vendor, grp->device_id); + pdev = platform_device_alloc(name, PLATFORM_DEVID_AUTO); + if (!pdev) + return -ENOMEM; + + /* Add resources based on the shared info */ + ret = acpi_csrt_parse_shared_info(pdev, grp); + if (ret) + goto fail; + + ret = platform_device_add(pdev); + if (ret) + goto fail; + + for (i = 0; i < pdev->num_resources; i++) + dev_dbg(&pdev->dev, "%pR\n", &pdev->resource[i]); + + return 0; + +fail: + platform_device_put(pdev); + return ret; +} + +/* + * CSRT or Core System Resources Table is a proprietary ACPI table + * introduced by Microsoft. This table can contain devices that are not in + * the system DSDT table. In particular DMA controllers might be described + * here. + * + * We present these devices as normal platform devices that don't have ACPI + * IDs or handle. The platform device name will be something like + * ..auto for example: INTL9C06.0.auto. + */ +void __init acpi_csrt_init(void) +{ + struct acpi_csrt_group *grp, *end; + struct acpi_table_csrt *csrt; + acpi_status status; + int ret; + + status = acpi_get_table(ACPI_SIG_CSRT, 0, + (struct acpi_table_header **)&csrt); + if (ACPI_FAILURE(status)) { + if (status != AE_NOT_FOUND) + pr_warn("failed to get the CSRT table\n"); + return; + } + + pr_debug("parsing CSRT table for devices\n"); + + grp = (struct acpi_csrt_group *)(csrt + 1); + end = (struct acpi_csrt_group *)((void *)csrt + csrt->header.length); + + while (grp < end) { + ret = acpi_csrt_parse_resource_group(grp); + if (ret) { + pr_warn("error in parsing resource group: %d\n", ret); + return; + } + + grp = (struct acpi_csrt_group *)((void *)grp + grp->length); + } +} diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index e050254ae143..4d2e4bf5f88d 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -26,6 +26,7 @@ int init_acpi_device_notify(void); int acpi_scan_init(void); int acpi_sysfs_init(void); +void acpi_csrt_init(void); #ifdef CONFIG_DEBUG_FS extern struct dentry *acpi_debugfs_dir; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 7d164a966b0d..a85b4080c3ca 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1699,6 +1699,7 @@ int __init acpi_scan_init(void) acpi_power_init(); acpi_pci_root_init(); + acpi_csrt_init(); /* * Enumerate devices in the ACPI namespace. -- cgit v1.2.3 From e375325ce55eb841ccda54a4472cf3b0139ea5f2 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 18 Jan 2013 13:46:01 +0000 Subject: ACPI / platform: create LPSS clocks if Lynxpoint devices are found during scan Intel Lynxpoint LPSS peripheral drivers depend on LPSS clock tree being created in order to function properly. The clock tree is exposed as a platform driver that binds to a device named 'clk-lpt'. To support this we modify the acpi_create_platform_device() to take one additional parameter called flags. This is passed from acpi_platform_device_ids[] array when acpi_create_platform_device() is called. We then introduce a new flag ACPI_PLATFORM_CLK which is used to tell acpi_create_platform_device() to create the platform clocks as well. Finally we set the ACPI_PLATFORM_CLK flags for all the Lynxpoint LPSS devices and make sure that when this flag is set we create the corresponding clock tree platform device. Signed-off-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_platform.c | 25 ++++++++++++++++++++++++- drivers/acpi/internal.h | 6 +++++- drivers/acpi/scan.c | 22 ++++++++++++---------- 3 files changed, 41 insertions(+), 12 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index 5554aea4bb0e..2d1fb4c21605 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -21,9 +22,25 @@ ACPI_MODULE_NAME("platform"); +static int acpi_create_platform_clks(struct acpi_device *adev) +{ + static struct platform_device *pdev; + + /* Create Lynxpoint LPSS clocks */ + if (!pdev && !strncmp(acpi_device_hid(adev), "INT33C", 6)) { + pdev = platform_device_register_simple("clk-lpt", -1, NULL, 0); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + } + + return 0; +} + /** * acpi_create_platform_device - Create platform device for ACPI device node * @adev: ACPI device node to create a platform device for. + * @flags: ACPI_PLATFORM_* flags that affect the creation of the platform + * devices. * * Check if the given @adev can be represented as a platform device and, if * that's the case, create and register a platform device, populate its common @@ -31,7 +48,8 @@ ACPI_MODULE_NAME("platform"); * * Name of the platform device will be the same as @adev's. */ -struct platform_device *acpi_create_platform_device(struct acpi_device *adev) +struct platform_device *acpi_create_platform_device(struct acpi_device *adev, + unsigned long flags) { struct platform_device *pdev = NULL; struct acpi_device *acpi_parent; @@ -41,6 +59,11 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) struct resource *resources; int count; + if ((flags & ACPI_PLATFORM_CLK) && acpi_create_platform_clks(adev)) { + dev_err(&adev->dev, "failed to create clocks\n"); + return NULL; + } + /* If the ACPI node already has a physical device attached, skip it. */ if (adev->physical_node_count) return NULL; diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 4d2e4bf5f88d..4b68373473de 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -100,6 +100,10 @@ static inline void suspend_nvs_restore(void) {} -------------------------------------------------------------------------- */ struct platform_device; -struct platform_device *acpi_create_platform_device(struct acpi_device *adev); +/* Flags for acpi_create_platform_device */ +#define ACPI_PLATFORM_CLK BIT(0) + +struct platform_device *acpi_create_platform_device(struct acpi_device *adev, + unsigned long flags); #endif /* _ACPI_INTERNAL_H_ */ diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index a85b4080c3ca..fe171aa7a98c 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -38,14 +38,14 @@ static const struct acpi_device_id acpi_platform_device_ids[] = { { "PNP0D40" }, /* Haswell LPSS devices */ - { "INT33C0", 0 }, - { "INT33C1", 0 }, - { "INT33C2", 0 }, - { "INT33C3", 0 }, - { "INT33C4", 0 }, - { "INT33C5", 0 }, - { "INT33C6", 0 }, - { "INT33C7", 0 }, + { "INT33C0", ACPI_PLATFORM_CLK }, + { "INT33C1", ACPI_PLATFORM_CLK }, + { "INT33C2", ACPI_PLATFORM_CLK }, + { "INT33C3", ACPI_PLATFORM_CLK }, + { "INT33C4", ACPI_PLATFORM_CLK }, + { "INT33C5", ACPI_PLATFORM_CLK }, + { "INT33C6", ACPI_PLATFORM_CLK }, + { "INT33C7", ACPI_PLATFORM_CLK }, { } }; @@ -1553,6 +1553,7 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, void *not_used, void **ret_not_used) { + const struct acpi_device_id *id; acpi_status status = AE_OK; struct acpi_device *device; unsigned long long sta_not_used; @@ -1568,9 +1569,10 @@ static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, if (acpi_bus_get_device(handle, &device)) return AE_CTRL_DEPTH; - if (!acpi_match_device_ids(device, acpi_platform_device_ids)) { + id = __acpi_match_device(device, acpi_platform_device_ids); + if (id) { /* This is a known good platform device. */ - acpi_create_platform_device(device); + acpi_create_platform_device(device, id->driver_data); } else if (device_attach(&device->dev) < 0) { status = AE_CTRL_DEPTH; } -- cgit v1.2.3 From cf860be639d86ed77af179c925085ae0721ae602 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 24 Jan 2013 12:49:49 +0100 Subject: ACPI / scan: Prevent device add uevents from racing with user space ACPI core adds sysfs device files after the given devices have been registered with device_register(), which is not appropriate, because it may lead to race conditions with user space tools using those files. Fix the problem by delaying the KOBJ_ADD uevent for ACPI devices until after all of the devices' sysfs files have been created. This also fixes a use-after-free in acpi_device_unregister(). Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman --- drivers/acpi/internal.h | 5 +++-- drivers/acpi/power.c | 3 ++- drivers/acpi/scan.c | 20 +++++++++++++++----- 3 files changed, 20 insertions(+), 8 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 1f004f35bc67..c5a61cd6c1a5 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -40,10 +40,11 @@ static inline void acpi_debugfs_init(void) { return; } #define ACPI_STA_DEFAULT (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | \ ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING) -int acpi_device_register(struct acpi_device *device, - void (*release)(struct device *)); +int acpi_device_add(struct acpi_device *device, + void (*release)(struct device *)); void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, int type, unsigned long long sta); +void acpi_device_add_finalize(struct acpi_device *device); void acpi_free_ids(struct acpi_device *device); /* -------------------------------------------------------------------------- diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 6db261c237d4..3f16dd4db23e 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -721,13 +721,14 @@ int acpi_add_power_resource(acpi_handle handle) acpi_device_bid(device), state ? "on" : "off"); device->flags.match_driver = true; - result = acpi_device_register(device, acpi_release_power_resource); + result = acpi_device_add(device, acpi_release_power_resource); if (result) goto err; mutex_lock(&power_resource_list_lock); list_add(&resource->list_node, &acpi_power_resource_list); mutex_unlock(&power_resource_list_lock); + acpi_device_add_finalize(device); return 0; err: diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 1fc57a349a3c..8b3b18846c8c 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -634,8 +634,8 @@ struct bus_type acpi_bus_type = { .uevent = acpi_device_uevent, }; -int acpi_device_register(struct acpi_device *device, - void (*release)(struct device *)) +int acpi_device_add(struct acpi_device *device, + void (*release)(struct device *)) { int result; struct acpi_device_bus_id *acpi_device_bus_id, *new_bus_id; @@ -705,7 +705,7 @@ int acpi_device_register(struct acpi_device *device, device->dev.parent = &device->parent->dev; device->dev.bus = &acpi_bus_type; device->dev.release = release; - result = device_register(&device->dev); + result = device_add(&device->dev); if (result) { dev_err(&device->dev, "Error registering device\n"); goto err; @@ -744,12 +744,13 @@ static void acpi_device_unregister(struct acpi_device *device) acpi_power_add_remove_device(device, false); acpi_device_remove_files(device); - device_unregister(&device->dev); + device_del(&device->dev); /* * Drop the reference counts of all power resources the device depends * on and turn off the ones that have no more references. */ acpi_power_transition(device, ACPI_STATE_D3_COLD); + put_device(&device->dev); } /* -------------------------------------------------------------------------- @@ -1391,6 +1392,14 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, acpi_device_get_busid(device); acpi_device_set_id(device); acpi_bus_get_flags(device); + device_initialize(&device->dev); + dev_set_uevent_suppress(&device->dev, true); +} + +void acpi_device_add_finalize(struct acpi_device *device) +{ + dev_set_uevent_suppress(&device->dev, false); + kobject_uevent(&device->dev.kobj, KOBJ_ADD); } static int acpi_add_single_object(struct acpi_device **child, @@ -1412,13 +1421,14 @@ static int acpi_add_single_object(struct acpi_device **child, acpi_bus_get_wakeup_device_flags(device); device->flags.match_driver = match_driver; - result = acpi_device_register(device, acpi_device_release); + result = acpi_device_add(device, acpi_device_release); if (result) { acpi_device_release(&device->dev); return result; } acpi_power_add_remove_device(device, true); + acpi_device_add_finalize(device); acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Added %s [%s] parent %s\n", dev_name(&device->dev), (char *) buffer.pointer, -- cgit v1.2.3 From 836aedb1414d4724b2ec68dd19810960c593720c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 24 Jan 2013 12:49:59 +0100 Subject: ACPI / PM: Expose power states of ACPI devices to user space Make it possible to retrieve the current power state of a device with ACPI power management from user space via sysfs by adding two new attributes, power_state and real_power_state, to the sysfs directory associated with the struct acpi_device object representing the device's ACPI node. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman --- .../ABI/testing/sysfs-devices-power_state | 20 +++++++++ .../ABI/testing/sysfs-devices-real_power_state | 23 ++++++++++ drivers/acpi/scan.c | 49 +++++++++++++++++++++- 3 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 Documentation/ABI/testing/sysfs-devices-power_state create mode 100644 Documentation/ABI/testing/sysfs-devices-real_power_state (limited to 'drivers/acpi/scan.c') diff --git a/Documentation/ABI/testing/sysfs-devices-power_state b/Documentation/ABI/testing/sysfs-devices-power_state new file mode 100644 index 000000000000..7ad9546748f0 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-power_state @@ -0,0 +1,20 @@ +What: /sys/devices/.../power_state +Date: January 2013 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../power_state attribute is only present for + device objects representing ACPI device nodes that provide power + management methods. + + If present, it contains a string representing the current ACPI + power state of the given device node. Its possible values, + "D0", "D1", "D2", "D3hot", and "D3cold", reflect the power state + names defined by the ACPI specification (ACPI 4 and above). + + If the device node uses shared ACPI power resources, this state + determines a list of power resources required not to be turned + off. However, some power resources needed by the device node in + higher-power (lower-number) states may also be ON because of + some other devices using them at the moment. + + This attribute is read-only. diff --git a/Documentation/ABI/testing/sysfs-devices-real_power_state b/Documentation/ABI/testing/sysfs-devices-real_power_state new file mode 100644 index 000000000000..8b3527c82a7d --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-real_power_state @@ -0,0 +1,23 @@ +What: /sys/devices/.../real_power_state +Date: January 2013 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../real_power_state attribute is only present + for device objects representing ACPI device nodes that provide + power management methods and use ACPI power resources for power + management. + + If present, it contains a string representing the real ACPI + power state of the given device node as returned by the _PSC + control method or inferred from the configuration of power + resources. Its possible values, "D0", "D1", "D2", "D3hot", and + "D3cold", reflect the power state names defined by the ACPI + specification (ACPI 4 and above). + + In some situations the value of this attribute may be different + from the value of the /sys/devices/.../power_state attribute for + the same device object. If that happens, some shared power + resources used by the device node are only ON because of some + other devices using them at the moment. + + This attribute is read-only. diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 8b3b18846c8c..9761d589f3f5 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -178,6 +178,32 @@ err_out: } EXPORT_SYMBOL(acpi_bus_hot_remove_device); +static ssize_t real_power_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct acpi_device *adev = to_acpi_device(dev); + int state; + int ret; + + ret = acpi_device_get_power(adev, &state); + if (ret) + return ret; + + return sprintf(buf, "%s\n", acpi_power_state_string(state)); +} + +static DEVICE_ATTR(real_power_state, 0444, real_power_state_show, NULL); + +static ssize_t power_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct acpi_device *adev = to_acpi_device(dev); + + return sprintf(buf, "%s\n", acpi_power_state_string(adev->power.state)); +} + +static DEVICE_ATTR(power_state, 0444, power_state_show, NULL); + static ssize_t acpi_eject_store(struct device *d, struct device_attribute *attr, const char *buf, size_t count) @@ -369,8 +395,22 @@ static int acpi_device_setup_files(struct acpi_device *dev) * hot-removal function from userland. */ status = acpi_get_handle(dev->handle, "_EJ0", &temp); - if (ACPI_SUCCESS(status)) + if (ACPI_SUCCESS(status)) { result = device_create_file(&dev->dev, &dev_attr_eject); + if (result) + return result; + } + + if (dev->flags.power_manageable) { + result = device_create_file(&dev->dev, &dev_attr_power_state); + if (result) + return result; + + if (dev->power.flags.power_resources) + result = device_create_file(&dev->dev, + &dev_attr_real_power_state); + } + end: return result; } @@ -380,6 +420,13 @@ static void acpi_device_remove_files(struct acpi_device *dev) acpi_status status; acpi_handle temp; + if (dev->flags.power_manageable) { + device_remove_file(&dev->dev, &dev_attr_power_state); + if (dev->power.flags.power_resources) + device_remove_file(&dev->dev, + &dev_attr_real_power_state); + } + /* * If device has _STR, remove 'description' file */ -- cgit v1.2.3 From b1c0f99bfb89cd9b42e3119ab822a8102fa87909 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 24 Jan 2013 12:50:09 +0100 Subject: ACPI / PM: Expose current status of ACPI power resources Since ACPI power resources are going to be used more extensively on new hardware platforms, it becomes necessary for user space (powertop in particular) to observe some properties of those resources for diagnostics purposes. For this reason, expose the current status of each ACPI power resource to user space via sysfs by adding a new resource_in_use attribute to the sysfs directory representing the given power resource. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman --- .../ABI/testing/sysfs-devices-resource_in_use | 12 ++++++++++ drivers/acpi/power.c | 26 +++++++++++++++++++++- drivers/acpi/scan.c | 3 +++ include/acpi/acpi_bus.h | 1 + 4 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 Documentation/ABI/testing/sysfs-devices-resource_in_use (limited to 'drivers/acpi/scan.c') diff --git a/Documentation/ABI/testing/sysfs-devices-resource_in_use b/Documentation/ABI/testing/sysfs-devices-resource_in_use new file mode 100644 index 000000000000..b4a3bc5922a3 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-resource_in_use @@ -0,0 +1,12 @@ +What: /sys/devices/.../resource_in_use +Date: January 2013 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../resource_in_use attribute is only present + for device objects representing ACPI power resources. + + If present, it contains a number (0 or 1) representing the + current status of the given power resource (0 means that the + resource is not in use and therefore it has been turned off). + + This attribute is read-only. diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 3f16dd4db23e..946720a4db57 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -87,6 +87,12 @@ static DEFINE_MUTEX(power_resource_list_lock); Power Resource Management -------------------------------------------------------------------------- */ +static inline +struct acpi_power_resource *to_power_resource(struct acpi_device *device) +{ + return container_of(device, struct acpi_power_resource, device); +} + static struct acpi_power_resource *acpi_power_get_context(acpi_handle handle) { struct acpi_device *device; @@ -94,7 +100,7 @@ static struct acpi_power_resource *acpi_power_get_context(acpi_handle handle) if (acpi_bus_get_device(handle, &device)) return NULL; - return container_of(device, struct acpi_power_resource, device); + return to_power_resource(device); } static int acpi_power_resources_list_add(acpi_handle handle, @@ -678,6 +684,21 @@ static void acpi_release_power_resource(struct device *dev) kfree(resource); } +static ssize_t acpi_power_in_use_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + struct acpi_power_resource *resource; + + resource = to_power_resource(to_acpi_device(dev)); + return sprintf(buf, "%u\n", !!resource->ref_count); +} +static DEVICE_ATTR(resource_in_use, 0444, acpi_power_in_use_show, NULL); + +static void acpi_power_sysfs_remove(struct acpi_device *device) +{ + device_remove_file(&device->dev, &dev_attr_resource_in_use); +} + int acpi_add_power_resource(acpi_handle handle) { struct acpi_power_resource *resource; @@ -725,6 +746,9 @@ int acpi_add_power_resource(acpi_handle handle) if (result) goto err; + if (!device_create_file(&device->dev, &dev_attr_resource_in_use)) + device->remove = acpi_power_sysfs_remove; + mutex_lock(&power_resource_list_lock); list_add(&resource->list_node, &acpi_power_resource_list); mutex_unlock(&power_resource_list_lock); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 9761d589f3f5..9801837876b7 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -791,6 +791,9 @@ static void acpi_device_unregister(struct acpi_device *device) acpi_power_add_remove_device(device, false); acpi_device_remove_files(device); + if (device->remove) + device->remove(device); + device_del(&device->dev); /* * Drop the reference counts of all power resources the device depends diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index fca1b9cb27d9..aef56a9f4e70 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -280,6 +280,7 @@ struct acpi_device { struct mutex physical_node_lock; DECLARE_BITMAP(physical_node_id_bitmap, ACPI_MAX_PHYSICAL_NODE); struct list_head power_dependent; + void (*remove)(struct acpi_device *); }; static inline void *acpi_driver_data(struct acpi_device *d) -- cgit v1.2.3 From 668192b678201d2fff27c6cc76bb003c1ec4a52a Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 21 Jan 2013 13:20:48 -0800 Subject: PCI: acpiphp: Move host bridge hotplug to pci_root.c The acpiphp driver is confusing because it contains partial support for PCI host bridge hotplug as well as support for hotplug of PCI devices. This patch moves the host bridge hot-add support to pci_root.c and adds hot-remove support in pci_root.c. How to test it: if sci_emu patch is applied, find out root bus number to ACPI root name mapping from dmesg or /sys. To remove root bus: echo "\_SB.PCIB 3" > /sys/kernel/debug/acpi/sci_notify To add back root bus: echo "\_SB.PCIB 1" > /sys/kernel/debug/acpi/sci_notify Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- drivers/acpi/internal.h | 1 + drivers/acpi/pci_root.c | 124 +++++++++++++++++++++++++++++++++++++ drivers/acpi/scan.c | 3 + drivers/pci/hotplug/acpiphp_glue.c | 59 +++++------------- 4 files changed, 143 insertions(+), 44 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index e050254ae143..0f24148a2b2a 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -68,6 +68,7 @@ struct acpi_ec { extern struct acpi_ec *first_ec; int acpi_pci_root_init(void); +void acpi_pci_root_hp_init(void); int acpi_ec_init(void); int acpi_ec_ecdt_probe(void); int acpi_boot_ec_enable(void); diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 471b2dcb1c67..1389811aa21b 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -673,3 +673,127 @@ int __init acpi_pci_root_init(void) return 0; } +/* Support root bridge hotplug */ + +static void handle_root_bridge_insertion(acpi_handle handle) +{ + struct acpi_device *device; + + if (!acpi_bus_get_device(handle, &device)) { + printk(KERN_DEBUG "acpi device exists...\n"); + return; + } + + if (acpi_bus_scan(handle)) + printk(KERN_ERR "cannot add bridge to acpi list\n"); +} + +static void handle_root_bridge_removal(struct acpi_device *device) +{ + struct acpi_eject_event *ej_event; + + ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL); + if (!ej_event) { + /* Inform firmware the hot-remove operation has error */ + (void) acpi_evaluate_hotplug_ost(device->handle, + ACPI_NOTIFY_EJECT_REQUEST, + ACPI_OST_SC_NON_SPECIFIC_FAILURE, + NULL); + return; + } + + ej_event->device = device; + ej_event->event = ACPI_NOTIFY_EJECT_REQUEST; + + acpi_bus_hot_remove_device(ej_event); +} + +static void _handle_hotplug_event_root(struct work_struct *work) +{ + struct acpi_pci_root *root; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER }; + struct acpi_hp_work *hp_work; + acpi_handle handle; + u32 type; + + hp_work = container_of(work, struct acpi_hp_work, work); + handle = hp_work->handle; + type = hp_work->type; + + root = acpi_pci_find_root(handle); + + acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + + switch (type) { + case ACPI_NOTIFY_BUS_CHECK: + /* bus enumerate */ + printk(KERN_DEBUG "%s: Bus check notify on %s\n", __func__, + (char *)buffer.pointer); + if (!root) + handle_root_bridge_insertion(handle); + + break; + + case ACPI_NOTIFY_DEVICE_CHECK: + /* device check */ + printk(KERN_DEBUG "%s: Device check notify on %s\n", __func__, + (char *)buffer.pointer); + if (!root) + handle_root_bridge_insertion(handle); + break; + + case ACPI_NOTIFY_EJECT_REQUEST: + /* request device eject */ + printk(KERN_DEBUG "%s: Device eject notify on %s\n", __func__, + (char *)buffer.pointer); + if (root) + handle_root_bridge_removal(root->device); + break; + default: + printk(KERN_WARNING "notify_handler: unknown event type 0x%x for %s\n", + type, (char *)buffer.pointer); + break; + } + + kfree(hp_work); /* allocated in handle_hotplug_event_bridge */ + kfree(buffer.pointer); +} + +static void handle_hotplug_event_root(acpi_handle handle, u32 type, + void *context) +{ + alloc_acpi_hp_work(handle, type, context, + _handle_hotplug_event_root); +} + +static acpi_status __init +find_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv) +{ + char objname[64]; + struct acpi_buffer buffer = { .length = sizeof(objname), + .pointer = objname }; + int *count = (int *)context; + + if (!acpi_is_root_bridge(handle)) + return AE_OK; + + (*count)++; + + acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + + acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + handle_hotplug_event_root, NULL); + printk(KERN_DEBUG "acpi root: %s notify handler installed\n", objname); + + return AE_OK; +} + +void __init acpi_pci_root_hp_init(void) +{ + int num = 0; + + acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, find_root_bridges, NULL, &num, NULL); + + printk(KERN_DEBUG "Found %d acpi root devices\n", num); +} diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 7c43bdc36abc..bc2f33790e83 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1706,5 +1706,8 @@ int __init acpi_scan_init(void) } acpi_update_all_gpes(); + + acpi_pci_root_hp_init(); + return 0; } diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index bf338d2ff371..c4a6301009f2 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -543,10 +543,13 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) acpi_status status; acpi_handle handle = bridge->handle; - status = acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + if (bridge->type != BRIDGE_TYPE_HOST) { + status = acpi_remove_notify_handler(handle, + ACPI_SYSTEM_NOTIFY, handle_hotplug_event_bridge); - if (ACPI_FAILURE(status)) - err("failed to remove notify handler\n"); + if (ACPI_FAILURE(status)) + err("failed to remove notify handler\n"); + } if ((bridge->type != BRIDGE_TYPE_HOST) && ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func)) { @@ -630,9 +633,6 @@ static void remove_bridge(struct acpi_pci_root *root) bridge = acpiphp_handle_to_bridge(handle); if (bridge) cleanup_bridge(bridge); - else - acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, - handle_hotplug_event_bridge); } static int power_on_slot(struct acpiphp_slot *slot) @@ -1123,18 +1123,12 @@ static void acpiphp_sanitize_bus(struct pci_bus *bus) } /* Program resources in newly inserted bridge */ -static int acpiphp_configure_bridge (acpi_handle handle) +static int acpiphp_configure_p2p_bridge(acpi_handle handle) { - struct pci_bus *bus; + struct pci_dev *pdev = acpi_get_pci_dev(handle); + struct pci_bus *bus = pdev->subordinate; - if (acpi_is_root_bridge(handle)) { - struct acpi_pci_root *root = acpi_pci_find_root(handle); - bus = root->bus; - } else { - struct pci_dev *pdev = acpi_get_pci_dev(handle); - bus = pdev->subordinate; - pci_dev_put(pdev); - } + pci_dev_put(pdev); pci_bus_size_bridges(bus); pci_bus_assign_resources(bus); @@ -1144,7 +1138,7 @@ static int acpiphp_configure_bridge (acpi_handle handle) return 0; } -static void handle_bridge_insertion(acpi_handle handle, u32 type) +static void handle_p2p_bridge_insertion(acpi_handle handle, u32 type) { struct acpi_device *device; @@ -1162,8 +1156,8 @@ static void handle_bridge_insertion(acpi_handle handle, u32 type) err("ACPI device object missing\n"); return; } - if (!acpiphp_configure_bridge(handle)) - add_bridge(handle); + if (!acpiphp_configure_p2p_bridge(handle)) + add_p2p_bridge(handle); else err("cannot configure and start bridge\n"); @@ -1221,7 +1215,7 @@ static void _handle_hotplug_event_bridge(struct work_struct *work) if (acpi_bus_get_device(handle, &device)) { /* This bridge must have just been physically inserted */ - handle_bridge_insertion(handle, type); + handle_p2p_bridge_insertion(handle, type); goto out; } @@ -1396,21 +1390,6 @@ static void handle_hotplug_event_func(acpi_handle handle, u32 type, alloc_acpi_hp_work(handle, type, context, _handle_hotplug_event_func); } -static acpi_status -find_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv) -{ - int *count = (int *)context; - - if (!acpi_is_root_bridge(handle)) - return AE_OK; - - (*count)++; - acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, - handle_hotplug_event_bridge, NULL); - - return AE_OK ; -} - static struct acpi_pci_driver acpi_pci_hp_driver = { .add = add_bridge, .remove = remove_bridge, @@ -1421,15 +1400,7 @@ static struct acpi_pci_driver acpi_pci_hp_driver = { */ int __init acpiphp_glue_init(void) { - int num = 0; - - acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, find_root_bridges, NULL, &num, NULL); - - if (num <= 0) - return -1; - else - acpi_pci_register_driver(&acpi_pci_hp_driver); + acpi_pci_register_driver(&acpi_pci_hp_driver); return 0; } -- cgit v1.2.3 From bfee26dba0f373ebe4e6f0b293d078b02f9f7f69 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 26 Jan 2013 00:27:44 +0100 Subject: ACPI / scan: Make it clear that acpi_bus_trim() cannot fail Since acpi_bus_trim() cannot fail, change its definition to a void function, so that its callers don't check the return value in vain and update the callers. Signed-off-by: Rafael J. Wysocki Acked-by: Yinghai Lu Acked-by: Yasuaki Ishimatsu Acked-by: Toshi Kani --- drivers/acpi/dock.c | 8 ++------ drivers/acpi/scan.c | 12 +++--------- drivers/pci/hotplug/acpiphp_glue.c | 10 +++------- include/acpi/acpi_bus.h | 2 +- 4 files changed, 9 insertions(+), 23 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 420d24fc9388..78648f811049 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -336,13 +336,9 @@ static struct acpi_device * dock_create_acpi_device(acpi_handle handle) static void dock_remove_acpi_device(acpi_handle handle) { struct acpi_device *device; - int ret; - if (!acpi_bus_get_device(handle, &device)) { - ret = acpi_bus_trim(device); - if (ret) - pr_debug("error removing bus, %x\n", -ret); - } + if (!acpi_bus_get_device(handle, &device)) + acpi_bus_trim(device); } /** diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 7c43bdc36abc..d37f290be44c 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -127,13 +127,8 @@ void acpi_bus_hot_remove_device(void *context) ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Hot-removing device %s...\n", dev_name(&device->dev))); - if (acpi_bus_trim(device)) { - printk(KERN_ERR PREFIX - "Removing device failed\n"); - goto err_out; - } - - /* device has been freed */ + acpi_bus_trim(device); + /* Device node has been released. */ device = NULL; /* power off device */ @@ -1631,7 +1626,7 @@ static acpi_status acpi_bus_remove(acpi_handle handle, u32 lvl_not_used, return AE_OK; } -int acpi_bus_trim(struct acpi_device *start) +void acpi_bus_trim(struct acpi_device *start) { /* * Execute acpi_bus_device_detach() as a post-order callback to detach @@ -1647,7 +1642,6 @@ int acpi_bus_trim(struct acpi_device *start) acpi_walk_namespace(ACPI_TYPE_ANY, start->handle, ACPI_UINT32_MAX, NULL, acpi_bus_remove, NULL, NULL); acpi_bus_remove(start->handle, 0, NULL, NULL); - return 0; } EXPORT_SYMBOL_GPL(acpi_bus_trim); diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 9e2b1f6dbe41..d1a6f4a25da8 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -742,8 +742,7 @@ static int acpiphp_bus_add(struct acpiphp_func *func) /* this shouldn't be in here, so remove * the bus then re-add it... */ - ret_val = acpi_bus_trim(device); - dbg("acpi_bus_trim return %x\n", ret_val); + acpi_bus_trim(device); } ret_val = acpi_bus_scan(func->handle); @@ -772,11 +771,8 @@ static int acpiphp_bus_trim(acpi_handle handle) return retval; } - retval = acpi_bus_trim(device); - if (retval) - err("cannot remove from acpi list\n"); - - return retval; + acpi_bus_trim(device); + return 0; } static void acpiphp_set_acpi_region(struct acpiphp_slot *slot) diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 2c722deb2490..da7fb61ba868 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -348,7 +348,7 @@ int acpi_bus_register_driver(struct acpi_driver *driver); void acpi_bus_unregister_driver(struct acpi_driver *driver); int acpi_bus_scan(acpi_handle handle); void acpi_bus_hot_remove_device(void *context); -int acpi_bus_trim(struct acpi_device *start); +void acpi_bus_trim(struct acpi_device *start); acpi_status acpi_bus_get_ejd(acpi_handle handle, acpi_handle * ejd); int acpi_match_device_ids(struct acpi_device *device, const struct acpi_device_id *ids); -- cgit v1.2.3 From 51fac8388a0325a43f0ae67453ece2c373e2ec28 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 24 Jan 2013 00:24:48 +0100 Subject: ACPI: Remove useless type argument of driver .remove() operation The second argument of ACPI driver .remove() operation is only used by the ACPI processor driver and the value passed to that driver through it is always available from the given struct acpi_device object's removal_type field. For this reason, the second ACPI driver .remove() argument is in fact useless, so drop it. Signed-off-by: Rafael J. Wysocki Reviewed-by: Jiang Liu Acked-by: Toshi Kani Acked-by: Yinghai Lu --- arch/ia64/hp/common/aml_nfw.c | 2 +- arch/x86/platform/olpc/olpc-xo15-sci.c | 2 +- drivers/acpi/ac.c | 4 ++-- drivers/acpi/acpi_memhotplug.c | 4 ++-- drivers/acpi/acpi_pad.c | 3 +-- drivers/acpi/battery.c | 2 +- drivers/acpi/button.c | 4 ++-- drivers/acpi/container.c | 4 ++-- drivers/acpi/ec.c | 2 +- drivers/acpi/fan.c | 4 ++-- drivers/acpi/hed.c | 2 +- drivers/acpi/pci_link.c | 4 ++-- drivers/acpi/pci_root.c | 4 ++-- drivers/acpi/processor_driver.c | 6 +++--- drivers/acpi/sbs.c | 6 +++--- drivers/acpi/sbshc.c | 4 ++-- drivers/acpi/scan.c | 5 ++--- drivers/acpi/thermal.c | 4 ++-- drivers/acpi/video.c | 4 ++-- drivers/char/hpet.c | 2 +- drivers/char/sonypi.c | 2 +- drivers/hwmon/acpi_power_meter.c | 2 +- drivers/hwmon/asus_atk0110.c | 4 ++-- drivers/i2c/busses/i2c-scmi.c | 2 +- drivers/input/misc/atlas_btns.c | 2 +- drivers/platform/x86/asus-laptop.c | 2 +- drivers/platform/x86/classmate-laptop.c | 10 +++++----- drivers/platform/x86/eeepc-laptop.c | 2 +- drivers/platform/x86/fujitsu-laptop.c | 4 ++-- drivers/platform/x86/fujitsu-tablet.c | 2 +- drivers/platform/x86/hp_accel.c | 2 +- drivers/platform/x86/ideapad-laptop.c | 2 +- drivers/platform/x86/intel_menlow.c | 2 +- drivers/platform/x86/panasonic-laptop.c | 4 ++-- drivers/platform/x86/sony-laptop.c | 4 ++-- drivers/platform/x86/topstar-laptop.c | 2 +- drivers/platform/x86/toshiba_acpi.c | 4 ++-- drivers/platform/x86/toshiba_bluetooth.c | 4 ++-- drivers/platform/x86/wmi.c | 4 ++-- drivers/platform/x86/xo15-ebook.c | 2 +- drivers/staging/quickstart/quickstart.c | 2 +- drivers/video/backlight/apple_bl.c | 2 +- drivers/xen/xen-acpi-pad.c | 3 +-- include/acpi/acpi_bus.h | 2 +- 44 files changed, 70 insertions(+), 73 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/arch/ia64/hp/common/aml_nfw.c b/arch/ia64/hp/common/aml_nfw.c index 6192f7188654..916ffe770bcf 100644 --- a/arch/ia64/hp/common/aml_nfw.c +++ b/arch/ia64/hp/common/aml_nfw.c @@ -191,7 +191,7 @@ static int aml_nfw_add(struct acpi_device *device) return aml_nfw_add_global_handler(); } -static int aml_nfw_remove(struct acpi_device *device, int type) +static int aml_nfw_remove(struct acpi_device *device) { return aml_nfw_remove_global_handler(); } diff --git a/arch/x86/platform/olpc/olpc-xo15-sci.c b/arch/x86/platform/olpc/olpc-xo15-sci.c index 2fdca25905ae..fef7d0ba7e3a 100644 --- a/arch/x86/platform/olpc/olpc-xo15-sci.c +++ b/arch/x86/platform/olpc/olpc-xo15-sci.c @@ -195,7 +195,7 @@ err_sysfs: return r; } -static int xo15_sci_remove(struct acpi_device *device, int type) +static int xo15_sci_remove(struct acpi_device *device) { acpi_disable_gpe(NULL, xo15_sci_gpe); acpi_remove_gpe_handler(NULL, xo15_sci_gpe, xo15_sci_gpe_handler); diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index d5fdd36190cc..6d5bf649196d 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -60,7 +60,7 @@ static int acpi_ac_open_fs(struct inode *inode, struct file *file); #endif static int acpi_ac_add(struct acpi_device *device); -static int acpi_ac_remove(struct acpi_device *device, int type); +static int acpi_ac_remove(struct acpi_device *device); static void acpi_ac_notify(struct acpi_device *device, u32 event); static const struct acpi_device_id ac_device_ids[] = { @@ -337,7 +337,7 @@ static int acpi_ac_resume(struct device *dev) } #endif -static int acpi_ac_remove(struct acpi_device *device, int type) +static int acpi_ac_remove(struct acpi_device *device) { struct acpi_ac *ac = NULL; diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index 03d18f290118..94c823b25138 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c @@ -54,7 +54,7 @@ MODULE_LICENSE("GPL"); #define MEMORY_POWER_OFF_STATE 2 static int acpi_memory_device_add(struct acpi_device *device); -static int acpi_memory_device_remove(struct acpi_device *device, int type); +static int acpi_memory_device_remove(struct acpi_device *device); static const struct acpi_device_id memory_device_ids[] = { {ACPI_MEMORY_DEVICE_HID, 0}, @@ -415,7 +415,7 @@ static int acpi_memory_device_add(struct acpi_device *device) return result; } -static int acpi_memory_device_remove(struct acpi_device *device, int type) +static int acpi_memory_device_remove(struct acpi_device *device) { struct acpi_memory_device *mem_device = NULL; int result; diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c index 16fa979f7180..31de1043eea0 100644 --- a/drivers/acpi/acpi_pad.c +++ b/drivers/acpi/acpi_pad.c @@ -482,8 +482,7 @@ static int acpi_pad_add(struct acpi_device *device) return 0; } -static int acpi_pad_remove(struct acpi_device *device, - int type) +static int acpi_pad_remove(struct acpi_device *device) { mutex_lock(&isolated_cpus_lock); acpi_pad_idle_cpus(0); diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 7efaeaa53b88..c5cd5b5513e6 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -1111,7 +1111,7 @@ fail: return result; } -static int acpi_battery_remove(struct acpi_device *device, int type) +static int acpi_battery_remove(struct acpi_device *device) { struct acpi_battery *battery = NULL; diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index f0d936b65e37..86c7d5445c38 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -75,7 +75,7 @@ static const struct acpi_device_id button_device_ids[] = { MODULE_DEVICE_TABLE(acpi, button_device_ids); static int acpi_button_add(struct acpi_device *device); -static int acpi_button_remove(struct acpi_device *device, int type); +static int acpi_button_remove(struct acpi_device *device); static void acpi_button_notify(struct acpi_device *device, u32 event); #ifdef CONFIG_PM_SLEEP @@ -433,7 +433,7 @@ static int acpi_button_add(struct acpi_device *device) return error; } -static int acpi_button_remove(struct acpi_device *device, int type) +static int acpi_button_remove(struct acpi_device *device) { struct acpi_button *button = acpi_driver_data(device); diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index cc79d3e53a39..cc0bf4613e0d 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -52,7 +52,7 @@ MODULE_DESCRIPTION("ACPI container driver"); MODULE_LICENSE("GPL"); static int acpi_container_add(struct acpi_device *device); -static int acpi_container_remove(struct acpi_device *device, int type); +static int acpi_container_remove(struct acpi_device *device); static const struct acpi_device_id container_device_ids[] = { {"ACPI0004", 0}, @@ -125,7 +125,7 @@ static int acpi_container_add(struct acpi_device *device) return 0; } -static int acpi_container_remove(struct acpi_device *device, int type) +static int acpi_container_remove(struct acpi_device *device) { acpi_status status = AE_OK; struct acpi_container *pc = NULL; diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 354007d490d1..d45b2871d33b 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -852,7 +852,7 @@ static int acpi_ec_add(struct acpi_device *device) return ret; } -static int acpi_ec_remove(struct acpi_device *device, int type) +static int acpi_ec_remove(struct acpi_device *device) { struct acpi_ec *ec; struct acpi_ec_query_handler *handler, *tmp; diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index 3bd6a54702d6..f815da82c765 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -45,7 +45,7 @@ MODULE_DESCRIPTION("ACPI Fan Driver"); MODULE_LICENSE("GPL"); static int acpi_fan_add(struct acpi_device *device); -static int acpi_fan_remove(struct acpi_device *device, int type); +static int acpi_fan_remove(struct acpi_device *device); static const struct acpi_device_id fan_device_ids[] = { {"PNP0C0B", 0}, @@ -172,7 +172,7 @@ static int acpi_fan_add(struct acpi_device *device) return result; } -static int acpi_fan_remove(struct acpi_device *device, int type) +static int acpi_fan_remove(struct acpi_device *device) { struct thermal_cooling_device *cdev = acpi_driver_data(device); diff --git a/drivers/acpi/hed.c b/drivers/acpi/hed.c index a0cc796932f7..13b1d39d7cdf 100644 --- a/drivers/acpi/hed.c +++ b/drivers/acpi/hed.c @@ -70,7 +70,7 @@ static int acpi_hed_add(struct acpi_device *device) return 0; } -static int acpi_hed_remove(struct acpi_device *device, int type) +static int acpi_hed_remove(struct acpi_device *device) { hed_handle = NULL; return 0; diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index a12808259dfb..b6592797f5b2 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -54,7 +54,7 @@ ACPI_MODULE_NAME("pci_link"); #define ACPI_PCI_LINK_MAX_POSSIBLE 16 static int acpi_pci_link_add(struct acpi_device *device); -static int acpi_pci_link_remove(struct acpi_device *device, int type); +static int acpi_pci_link_remove(struct acpi_device *device); static const struct acpi_device_id link_device_ids[] = { {"PNP0C0F", 0}, @@ -766,7 +766,7 @@ static void irqrouter_resume(void) } } -static int acpi_pci_link_remove(struct acpi_device *device, int type) +static int acpi_pci_link_remove(struct acpi_device *device) { struct acpi_pci_link *link; diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 22a8458b4ec9..8890775e8b25 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -46,7 +46,7 @@ ACPI_MODULE_NAME("pci_root"); #define ACPI_PCI_ROOT_CLASS "pci_bridge" #define ACPI_PCI_ROOT_DEVICE_NAME "PCI Root Bridge" static int acpi_pci_root_add(struct acpi_device *device); -static int acpi_pci_root_remove(struct acpi_device *device, int type); +static int acpi_pci_root_remove(struct acpi_device *device); #define ACPI_PCIE_REQ_SUPPORT (OSC_EXT_PCI_CONFIG_SUPPORT \ | OSC_ACTIVE_STATE_PWR_SUPPORT \ @@ -627,7 +627,7 @@ end: return result; } -static int acpi_pci_root_remove(struct acpi_device *device, int type) +static int acpi_pci_root_remove(struct acpi_device *device) { acpi_status status; acpi_handle handle; diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 9c5929a17d3a..c5d2fd85dbe0 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -81,7 +81,7 @@ MODULE_DESCRIPTION("ACPI Processor Driver"); MODULE_LICENSE("GPL"); static int acpi_processor_add(struct acpi_device *device); -static int acpi_processor_remove(struct acpi_device *device, int type); +static int acpi_processor_remove(struct acpi_device *device); static void acpi_processor_notify(struct acpi_device *device, u32 event); static acpi_status acpi_processor_hotadd_init(struct acpi_processor *pr); static int acpi_processor_handle_eject(struct acpi_processor *pr); @@ -610,7 +610,7 @@ err_free_pr: return result; } -static int acpi_processor_remove(struct acpi_device *device, int type) +static int acpi_processor_remove(struct acpi_device *device) { struct acpi_processor *pr = NULL; @@ -623,7 +623,7 @@ static int acpi_processor_remove(struct acpi_device *device, int type) if (pr->id >= nr_cpu_ids) goto free; - if (type == ACPI_BUS_REMOVAL_EJECT) { + if (device->removal_type == ACPI_BUS_REMOVAL_EJECT) { if (acpi_processor_handle_eject(pr)) return -EINVAL; } diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index ff0740e0a9c2..e523245643ac 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -130,7 +130,7 @@ struct acpi_sbs { #define to_acpi_sbs(x) container_of(x, struct acpi_sbs, charger) -static int acpi_sbs_remove(struct acpi_device *device, int type); +static int acpi_sbs_remove(struct acpi_device *device); static int acpi_battery_get_state(struct acpi_battery *battery); static inline int battery_scale(int log) @@ -949,11 +949,11 @@ static int acpi_sbs_add(struct acpi_device *device) acpi_smbus_register_callback(sbs->hc, acpi_sbs_callback, sbs); end: if (result) - acpi_sbs_remove(device, 0); + acpi_sbs_remove(device); return result; } -static int acpi_sbs_remove(struct acpi_device *device, int type) +static int acpi_sbs_remove(struct acpi_device *device) { struct acpi_sbs *sbs; int id; diff --git a/drivers/acpi/sbshc.c b/drivers/acpi/sbshc.c index cf6129a8af7c..b78bc605837e 100644 --- a/drivers/acpi/sbshc.c +++ b/drivers/acpi/sbshc.c @@ -33,7 +33,7 @@ struct acpi_smb_hc { }; static int acpi_smbus_hc_add(struct acpi_device *device); -static int acpi_smbus_hc_remove(struct acpi_device *device, int type); +static int acpi_smbus_hc_remove(struct acpi_device *device); static const struct acpi_device_id sbs_device_ids[] = { {"ACPI0001", 0}, @@ -296,7 +296,7 @@ static int acpi_smbus_hc_add(struct acpi_device *device) extern void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit); -static int acpi_smbus_hc_remove(struct acpi_device *device, int type) +static int acpi_smbus_hc_remove(struct acpi_device *device) { struct acpi_smb_hc *hc; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index bc8077f173da..0989b323e65f 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -577,8 +577,7 @@ static int acpi_device_probe(struct device * dev) ret = acpi_device_install_notify_handler(acpi_dev); if (ret) { if (acpi_drv->ops.remove) - acpi_drv->ops.remove(acpi_dev, - acpi_dev->removal_type); + acpi_drv->ops.remove(acpi_dev); return ret; } } @@ -600,7 +599,7 @@ static int acpi_device_remove(struct device * dev) if (acpi_drv->ops.notify) acpi_device_remove_notify_handler(acpi_dev); if (acpi_drv->ops.remove) - acpi_drv->ops.remove(acpi_dev, acpi_dev->removal_type); + acpi_drv->ops.remove(acpi_dev); } acpi_dev->driver = NULL; acpi_dev->driver_data = NULL; diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 506fbd4b5733..da079d4e0baa 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -97,7 +97,7 @@ module_param(psv, int, 0644); MODULE_PARM_DESC(psv, "Disable or override all passive trip points."); static int acpi_thermal_add(struct acpi_device *device); -static int acpi_thermal_remove(struct acpi_device *device, int type); +static int acpi_thermal_remove(struct acpi_device *device); static void acpi_thermal_notify(struct acpi_device *device, u32 event); static const struct acpi_device_id thermal_device_ids[] = { @@ -1111,7 +1111,7 @@ end: return result; } -static int acpi_thermal_remove(struct acpi_device *device, int type) +static int acpi_thermal_remove(struct acpi_device *device) { struct acpi_thermal *tz = NULL; diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index ac9a69cd45f5..5be60ad8381f 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -88,7 +88,7 @@ module_param(use_bios_initial_backlight, bool, 0644); static int register_count = 0; static int acpi_video_bus_add(struct acpi_device *device); -static int acpi_video_bus_remove(struct acpi_device *device, int type); +static int acpi_video_bus_remove(struct acpi_device *device); static void acpi_video_bus_notify(struct acpi_device *device, u32 event); static const struct acpi_device_id video_device_ids[] = { @@ -1740,7 +1740,7 @@ static int acpi_video_bus_add(struct acpi_device *device) return error; } -static int acpi_video_bus_remove(struct acpi_device *device, int type) +static int acpi_video_bus_remove(struct acpi_device *device) { struct acpi_video_bus *video = NULL; diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index fe6d4be48296..e3f9a99b8522 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -1041,7 +1041,7 @@ static int hpet_acpi_add(struct acpi_device *device) return hpet_alloc(&data); } -static int hpet_acpi_remove(struct acpi_device *device, int type) +static int hpet_acpi_remove(struct acpi_device *device) { /* XXX need to unregister clocksource, dealloc mem, etc */ return -EINVAL; diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index d780295a1473..6386a98e43c1 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -1142,7 +1142,7 @@ static int sonypi_acpi_add(struct acpi_device *device) return 0; } -static int sonypi_acpi_remove(struct acpi_device *device, int type) +static int sonypi_acpi_remove(struct acpi_device *device) { sonypi_acpi_device = NULL; return 0; diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index 1672e2a5db46..6351aba8819c 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -911,7 +911,7 @@ exit: return res; } -static int acpi_power_meter_remove(struct acpi_device *device, int type) +static int acpi_power_meter_remove(struct acpi_device *device) { struct acpi_power_meter_resource *resource; diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c index 56dbcfb3e301..b25c64302cbc 100644 --- a/drivers/hwmon/asus_atk0110.c +++ b/drivers/hwmon/asus_atk0110.c @@ -190,7 +190,7 @@ struct atk_acpi_input_buf { }; static int atk_add(struct acpi_device *device); -static int atk_remove(struct acpi_device *device, int type); +static int atk_remove(struct acpi_device *device); static void atk_print_sensor(struct atk_data *data, union acpi_object *obj); static int atk_read_value(struct atk_sensor_data *sensor, u64 *value); static void atk_free_sensors(struct atk_data *data); @@ -1416,7 +1416,7 @@ out: return err; } -static int atk_remove(struct acpi_device *device, int type) +static int atk_remove(struct acpi_device *device) { struct atk_data *data = device->driver_data; dev_dbg(&device->dev, "removing...\n"); diff --git a/drivers/i2c/busses/i2c-scmi.c b/drivers/i2c/busses/i2c-scmi.c index 6aafa3d88ff0..c447e8d40b78 100644 --- a/drivers/i2c/busses/i2c-scmi.c +++ b/drivers/i2c/busses/i2c-scmi.c @@ -406,7 +406,7 @@ err: return -EIO; } -static int acpi_smbus_cmi_remove(struct acpi_device *device, int type) +static int acpi_smbus_cmi_remove(struct acpi_device *device) { struct acpi_smbus_cmi *smbus_cmi = acpi_driver_data(device); diff --git a/drivers/input/misc/atlas_btns.c b/drivers/input/misc/atlas_btns.c index 26f13131639a..5d4402365a52 100644 --- a/drivers/input/misc/atlas_btns.c +++ b/drivers/input/misc/atlas_btns.c @@ -121,7 +121,7 @@ static int atlas_acpi_button_add(struct acpi_device *device) return err; } -static int atlas_acpi_button_remove(struct acpi_device *device, int type) +static int atlas_acpi_button_remove(struct acpi_device *device) { acpi_status status; diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index fcde4e528819..d9f9a0dbc6f3 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -1910,7 +1910,7 @@ fail_platform: return result; } -static int asus_acpi_remove(struct acpi_device *device, int type) +static int asus_acpi_remove(struct acpi_device *device) { struct asus_laptop *asus = acpi_driver_data(device); diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index c87ff16873f9..36e5e6c13db4 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -432,7 +432,7 @@ failed_sensitivity: return error; } -static int cmpc_accel_remove_v4(struct acpi_device *acpi, int type) +static int cmpc_accel_remove_v4(struct acpi_device *acpi) { struct input_dev *inputdev; struct cmpc_accel *accel; @@ -668,7 +668,7 @@ failed_file: return error; } -static int cmpc_accel_remove(struct acpi_device *acpi, int type) +static int cmpc_accel_remove(struct acpi_device *acpi) { struct input_dev *inputdev; struct cmpc_accel *accel; @@ -753,7 +753,7 @@ static int cmpc_tablet_add(struct acpi_device *acpi) cmpc_tablet_idev_init); } -static int cmpc_tablet_remove(struct acpi_device *acpi, int type) +static int cmpc_tablet_remove(struct acpi_device *acpi) { return cmpc_remove_acpi_notify_device(acpi); } @@ -1000,7 +1000,7 @@ out_bd: return retval; } -static int cmpc_ipml_remove(struct acpi_device *acpi, int type) +static int cmpc_ipml_remove(struct acpi_device *acpi) { struct ipml200_dev *ipml; @@ -1079,7 +1079,7 @@ static int cmpc_keys_add(struct acpi_device *acpi) cmpc_keys_idev_init); } -static int cmpc_keys_remove(struct acpi_device *acpi, int type) +static int cmpc_keys_remove(struct acpi_device *acpi) { return cmpc_remove_acpi_notify_device(acpi); } diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 528e9495458d..98935f945f53 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -1501,7 +1501,7 @@ fail_platform: return result; } -static int eeepc_acpi_remove(struct acpi_device *device, int type) +static int eeepc_acpi_remove(struct acpi_device *device) { struct eeepc_laptop *eeepc = acpi_driver_data(device); diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index c4c1a5444b38..1c9386e7c58c 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -733,7 +733,7 @@ err_stop: return result; } -static int acpi_fujitsu_remove(struct acpi_device *device, int type) +static int acpi_fujitsu_remove(struct acpi_device *device) { struct fujitsu_t *fujitsu = acpi_driver_data(device); struct input_dev *input = fujitsu->input; @@ -938,7 +938,7 @@ err_stop: return result; } -static int acpi_fujitsu_hotkey_remove(struct acpi_device *device, int type) +static int acpi_fujitsu_hotkey_remove(struct acpi_device *device) { struct fujitsu_hotkey_t *fujitsu_hotkey = acpi_driver_data(device); struct input_dev *input = fujitsu_hotkey->input; diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c index 174ca01c4aa7..570926c10014 100644 --- a/drivers/platform/x86/fujitsu-tablet.c +++ b/drivers/platform/x86/fujitsu-tablet.c @@ -431,7 +431,7 @@ static int acpi_fujitsu_add(struct acpi_device *adev) return 0; } -static int acpi_fujitsu_remove(struct acpi_device *adev, int type) +static int acpi_fujitsu_remove(struct acpi_device *adev) { free_irq(fujitsu.irq, fujitsu_interrupt); release_region(fujitsu.io_base, fujitsu.io_length); diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c index 18d74f29dcb2..e64a7a870d42 100644 --- a/drivers/platform/x86/hp_accel.c +++ b/drivers/platform/x86/hp_accel.c @@ -337,7 +337,7 @@ static int lis3lv02d_add(struct acpi_device *device) return ret; } -static int lis3lv02d_remove(struct acpi_device *device, int type) +static int lis3lv02d_remove(struct acpi_device *device) { if (!device) return -EINVAL; diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 64bfb30a52e9..17f00b8dc5cb 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -834,7 +834,7 @@ platform_failed: return ret; } -static int ideapad_acpi_remove(struct acpi_device *adevice, int type) +static int ideapad_acpi_remove(struct acpi_device *adevice) { struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); int i; diff --git a/drivers/platform/x86/intel_menlow.c b/drivers/platform/x86/intel_menlow.c index 3271ac85115e..d6cfc1558c2f 100644 --- a/drivers/platform/x86/intel_menlow.c +++ b/drivers/platform/x86/intel_menlow.c @@ -200,7 +200,7 @@ static int intel_menlow_memory_add(struct acpi_device *device) } -static int intel_menlow_memory_remove(struct acpi_device *device, int type) +static int intel_menlow_memory_remove(struct acpi_device *device) { struct thermal_cooling_device *cdev = acpi_driver_data(device); diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index 8e8caa767d6a..4add9a31bf60 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -176,7 +176,7 @@ enum SINF_BITS { SINF_NUM_BATTERIES = 0, /* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */ static int acpi_pcc_hotkey_add(struct acpi_device *device); -static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type); +static int acpi_pcc_hotkey_remove(struct acpi_device *device); static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event); static const struct acpi_device_id pcc_device_ids[] = { @@ -663,7 +663,7 @@ static int __init acpi_pcc_init(void) return 0; } -static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type) +static int acpi_pcc_hotkey_remove(struct acpi_device *device) { struct pcc_acpi *pcc = acpi_driver_data(device); diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index b8ad71f7863f..ceb41eff4230 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -2740,7 +2740,7 @@ outwalk: return result; } -static int sony_nc_remove(struct acpi_device *device, int type) +static int sony_nc_remove(struct acpi_device *device) { struct sony_nc_value *item; @@ -4111,7 +4111,7 @@ found: * ACPI driver * *****************/ -static int sony_pic_remove(struct acpi_device *device, int type) +static int sony_pic_remove(struct acpi_device *device) { struct sony_pic_ioport *io, *tmp_io; struct sony_pic_irq *irq, *tmp_irq; diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c index d727bfee89a6..4ab618c63b45 100644 --- a/drivers/platform/x86/topstar-laptop.c +++ b/drivers/platform/x86/topstar-laptop.c @@ -157,7 +157,7 @@ add_err: return -ENODEV; } -static int acpi_topstar_remove(struct acpi_device *device, int type) +static int acpi_topstar_remove(struct acpi_device *device) { struct topstar_hkey *tps_hkey = acpi_driver_data(device); diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index c2727895794c..904476b2fa8f 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -1118,7 +1118,7 @@ static int toshiba_acpi_setup_backlight(struct toshiba_acpi_dev *dev) return 0; } -static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type) +static int toshiba_acpi_remove(struct acpi_device *acpi_dev) { struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); @@ -1250,7 +1250,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) return 0; error: - toshiba_acpi_remove(acpi_dev, 0); + toshiba_acpi_remove(acpi_dev); return ret; } diff --git a/drivers/platform/x86/toshiba_bluetooth.c b/drivers/platform/x86/toshiba_bluetooth.c index e95be0b74859..74dd01ae343b 100644 --- a/drivers/platform/x86/toshiba_bluetooth.c +++ b/drivers/platform/x86/toshiba_bluetooth.c @@ -32,7 +32,7 @@ MODULE_LICENSE("GPL"); static int toshiba_bt_rfkill_add(struct acpi_device *device); -static int toshiba_bt_rfkill_remove(struct acpi_device *device, int type); +static int toshiba_bt_rfkill_remove(struct acpi_device *device); static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event); static const struct acpi_device_id bt_device_ids[] = { @@ -122,7 +122,7 @@ static int toshiba_bt_rfkill_add(struct acpi_device *device) return result; } -static int toshiba_bt_rfkill_remove(struct acpi_device *device, int type) +static int toshiba_bt_rfkill_remove(struct acpi_device *device) { /* clean up */ return 0; diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 42a4dcc25f92..e4ac38aca580 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -92,7 +92,7 @@ module_param(debug_dump_wdg, bool, 0444); MODULE_PARM_DESC(debug_dump_wdg, "Dump available WMI interfaces [0/1]"); -static int acpi_wmi_remove(struct acpi_device *device, int type); +static int acpi_wmi_remove(struct acpi_device *device); static int acpi_wmi_add(struct acpi_device *device); static void acpi_wmi_notify(struct acpi_device *device, u32 event); @@ -917,7 +917,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event) } } -static int acpi_wmi_remove(struct acpi_device *device, int type) +static int acpi_wmi_remove(struct acpi_device *device) { acpi_remove_address_space_handler(device->handle, ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c index 16d340c3b852..4b1377bd5944 100644 --- a/drivers/platform/x86/xo15-ebook.c +++ b/drivers/platform/x86/xo15-ebook.c @@ -150,7 +150,7 @@ static int ebook_switch_add(struct acpi_device *device) return error; } -static int ebook_switch_remove(struct acpi_device *device, int type) +static int ebook_switch_remove(struct acpi_device *device) { struct ebook_switch *button = acpi_driver_data(device); diff --git a/drivers/staging/quickstart/quickstart.c b/drivers/staging/quickstart/quickstart.c index cac320738142..adb8da564cf6 100644 --- a/drivers/staging/quickstart/quickstart.c +++ b/drivers/staging/quickstart/quickstart.c @@ -296,7 +296,7 @@ fail_config: return ret; } -static int quickstart_acpi_remove(struct acpi_device *device, int type) +static int quickstart_acpi_remove(struct acpi_device *device) { acpi_status status; struct quickstart_acpi *quickstart; diff --git a/drivers/video/backlight/apple_bl.c b/drivers/video/backlight/apple_bl.c index f088d4c07381..d84329676689 100644 --- a/drivers/video/backlight/apple_bl.c +++ b/drivers/video/backlight/apple_bl.c @@ -196,7 +196,7 @@ static int apple_bl_add(struct acpi_device *dev) return 0; } -static int apple_bl_remove(struct acpi_device *dev, int type) +static int apple_bl_remove(struct acpi_device *dev) { backlight_device_unregister(apple_backlight_device); diff --git a/drivers/xen/xen-acpi-pad.c b/drivers/xen/xen-acpi-pad.c index da39191e7278..c763479ed85e 100644 --- a/drivers/xen/xen-acpi-pad.c +++ b/drivers/xen/xen-acpi-pad.c @@ -140,8 +140,7 @@ static int acpi_pad_add(struct acpi_device *device) return 0; } -static int acpi_pad_remove(struct acpi_device *device, - int type) +static int acpi_pad_remove(struct acpi_device *device) { mutex_lock(&xen_cpu_lock); xen_acpi_pad_idle_cpus(0); diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 2c722deb2490..7714a1c2bbc5 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -89,7 +89,7 @@ struct acpi_device; */ typedef int (*acpi_op_add) (struct acpi_device * device); -typedef int (*acpi_op_remove) (struct acpi_device * device, int type); +typedef int (*acpi_op_remove) (struct acpi_device * device); typedef int (*acpi_op_start) (struct acpi_device * device); typedef void (*acpi_op_notify) (struct acpi_device * device, u32 event); -- cgit v1.2.3 From c511cc1990bbc263c3f8a2ef4d7d613a3b40ffe2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 27 Jan 2013 21:17:29 +0100 Subject: ACPI / scan: Make namespace scanning and trimming mutually exclusive There is no guarantee that acpi_bus_scan() and acpi_bus_trim() will not be run in parallel for the same scope of the ACPI namespace, which may lead to a great deal of confusion, so introduce a new mutex to prevent that from happening. Signed-off-by: Rafael J. Wysocki Acked-by: Yinghai Lu --- drivers/acpi/scan.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index d37f290be44c..43754655c156 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -52,6 +52,7 @@ static const struct acpi_device_id acpi_platform_device_ids[] = { static LIST_HEAD(acpi_device_list); static LIST_HEAD(acpi_bus_id_list); +static DEFINE_MUTEX(acpi_scan_lock); DEFINE_MUTEX(acpi_device_lock); LIST_HEAD(acpi_wakeup_device_list); @@ -1587,19 +1588,22 @@ static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, int acpi_bus_scan(acpi_handle handle) { void *device = NULL; + int error = 0; + + mutex_lock(&acpi_scan_lock); if (ACPI_SUCCESS(acpi_bus_check_add(handle, 0, NULL, &device))) acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, acpi_bus_check_add, NULL, NULL, &device); if (!device) - return -ENODEV; - - if (ACPI_SUCCESS(acpi_bus_device_attach(handle, 0, NULL, NULL))) + error = -ENODEV; + else if (ACPI_SUCCESS(acpi_bus_device_attach(handle, 0, NULL, NULL))) acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, acpi_bus_device_attach, NULL, NULL, NULL); - return 0; + mutex_unlock(&acpi_scan_lock); + return error; } EXPORT_SYMBOL(acpi_bus_scan); @@ -1628,6 +1632,8 @@ static acpi_status acpi_bus_remove(acpi_handle handle, u32 lvl_not_used, void acpi_bus_trim(struct acpi_device *start) { + mutex_lock(&acpi_scan_lock); + /* * Execute acpi_bus_device_detach() as a post-order callback to detach * all ACPI drivers from the device nodes being removed. @@ -1642,6 +1648,8 @@ void acpi_bus_trim(struct acpi_device *start) acpi_walk_namespace(ACPI_TYPE_ANY, start->handle, ACPI_UINT32_MAX, NULL, acpi_bus_remove, NULL, NULL); acpi_bus_remove(start->handle, 0, NULL, NULL); + + mutex_unlock(&acpi_scan_lock); } EXPORT_SYMBOL_GPL(acpi_bus_trim); -- cgit v1.2.3 From 2c0d4fe0189ae5e29fd9602d5b83f3b2b169bd1b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 29 Jan 2013 13:57:20 +0100 Subject: ACPI / scan: Make scanning of fixed devices follow the general scheme Make acpi_bus_scan_fixed() use device_attach() directly to attach drivers, if any, to the fixed devices in analogy with how acpi_bus_scan() works, which allows the last argument of acpi_add_single_object() to be dropped and the manipulation of the flags.match_driver bit to be moved to acpi_init_device_object() and acpi_device_add_finalize(). After these changes all of the functions for the initialization and registration of struct acpi_device objects work in the same way for all of them. Signed-off-by: Rafael J. Wysocki Acked-by: Yinghai Lu --- drivers/acpi/scan.c | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index a4a2595b6d88..b206ce5e1faf 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1441,19 +1441,21 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, acpi_device_get_busid(device); acpi_device_set_id(device); acpi_bus_get_flags(device); + device->flags.match_driver = false; device_initialize(&device->dev); dev_set_uevent_suppress(&device->dev, true); } void acpi_device_add_finalize(struct acpi_device *device) { + device->flags.match_driver = true; dev_set_uevent_suppress(&device->dev, false); kobject_uevent(&device->dev.kobj, KOBJ_ADD); } static int acpi_add_single_object(struct acpi_device **child, acpi_handle handle, int type, - unsigned long long sta, bool match_driver) + unsigned long long sta) { int result; struct acpi_device *device; @@ -1469,7 +1471,6 @@ static int acpi_add_single_object(struct acpi_device **child, acpi_bus_get_power_flags(device); acpi_bus_get_wakeup_device_flags(device); - device->flags.match_driver = match_driver; result = acpi_device_add(device, acpi_device_release); if (result) { acpi_device_release(&device->dev); @@ -1562,12 +1563,10 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, return AE_CTRL_DEPTH; } - acpi_add_single_object(&device, handle, type, sta, false); + acpi_add_single_object(&device, handle, type, sta); if (!device) return AE_CTRL_DEPTH; - device->flags.match_driver = true; - out: if (!*return_value) *return_value = device; @@ -1679,25 +1678,39 @@ EXPORT_SYMBOL_GPL(acpi_bus_trim); static int acpi_bus_scan_fixed(void) { int result = 0; - struct acpi_device *device = NULL; /* * Enumerate all fixed-feature devices. */ - if ((acpi_gbl_FADT.flags & ACPI_FADT_POWER_BUTTON) == 0) { + if (!(acpi_gbl_FADT.flags & ACPI_FADT_POWER_BUTTON)) { + struct acpi_device *device = NULL; + result = acpi_add_single_object(&device, NULL, ACPI_BUS_TYPE_POWER_BUTTON, - ACPI_STA_DEFAULT, true); + ACPI_STA_DEFAULT); + if (result) + return result; + + result = device_attach(&device->dev); + if (result < 0) + return result; + device_init_wakeup(&device->dev, true); } - if ((acpi_gbl_FADT.flags & ACPI_FADT_SLEEP_BUTTON) == 0) { + if (!(acpi_gbl_FADT.flags & ACPI_FADT_SLEEP_BUTTON)) { + struct acpi_device *device = NULL; + result = acpi_add_single_object(&device, NULL, ACPI_BUS_TYPE_SLEEP_BUTTON, - ACPI_STA_DEFAULT, true); + ACPI_STA_DEFAULT); + if (result) + return result; + + result = device_attach(&device->dev); } - return result; + return result < 0 ? result : 0; } int __init acpi_scan_init(void) -- cgit v1.2.3 From ca589f9469641916f4f9bd6a820012a27102ef63 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 30 Jan 2013 14:27:29 +0100 Subject: ACPI / scan: Introduce struct acpi_scan_handler Introduce struct acpi_scan_handler for representing objects that will do configuration tasks depending on ACPI device nodes' hardware IDs (HIDs). Currently, those tasks are done either directly by the ACPI namespace scanning code or by ACPI device drivers designed specifically for this purpose. None of the above is desirable, however, because doing that directly in the namespace scanning code makes that code overly complicated and difficult to follow and doing that in "special" device drivers leads to a great deal of confusion about their role and to confusing interactions with the driver core (for example, sysfs directories are created for those drivers, but they are completely unnecessary and only increase the kernel's memory footprint in vain). Signed-off-by: Rafael J. Wysocki Acked-by: Yinghai Lu Acked-by: Yasuaki Ishimatsu Acked-by: Toshi Kani --- Documentation/acpi/scan_handlers.txt | 77 ++++++++++++++++++++++++++++++++++++ drivers/acpi/scan.c | 60 ++++++++++++++++++++++++---- include/acpi/acpi_bus.h | 14 +++++++ 3 files changed, 144 insertions(+), 7 deletions(-) create mode 100644 Documentation/acpi/scan_handlers.txt (limited to 'drivers/acpi/scan.c') diff --git a/Documentation/acpi/scan_handlers.txt b/Documentation/acpi/scan_handlers.txt new file mode 100644 index 000000000000..3246ccf15992 --- /dev/null +++ b/Documentation/acpi/scan_handlers.txt @@ -0,0 +1,77 @@ +ACPI Scan Handlers + +Copyright (C) 2012, Intel Corporation +Author: Rafael J. Wysocki + +During system initialization and ACPI-based device hot-add, the ACPI namespace +is scanned in search of device objects that generally represent various pieces +of hardware. This causes a struct acpi_device object to be created and +registered with the driver core for every device object in the ACPI namespace +and the hierarchy of those struct acpi_device objects reflects the namespace +layout (i.e. parent device objects in the namespace are represented by parent +struct acpi_device objects and analogously for their children). Those struct +acpi_device objects are referred to as "device nodes" in what follows, but they +should not be confused with struct device_node objects used by the Device Trees +parsing code (although their role is analogous to the role of those objects). + +During ACPI-based device hot-remove device nodes representing pieces of hardware +being removed are unregistered and deleted. + +The core ACPI namespace scanning code in drivers/acpi/scan.c carries out basic +initialization of device nodes, such as retrieving common configuration +information from the device objects represented by them and populating them with +appropriate data, but some of them require additional handling after they have +been registered. For example, if the given device node represents a PCI host +bridge, its registration should cause the PCI bus under that bridge to be +enumerated and PCI devices on that bus to be registered with the driver core. +Similarly, if the device node represents a PCI interrupt link, it is necessary +to configure that link so that the kernel can use it. + +Those additional configuration tasks usually depend on the type of the hardware +component represented by the given device node which can be determined on the +basis of the device node's hardware ID (HID). They are performed by objects +called ACPI scan handlers represented by the following structure: + +struct acpi_scan_handler { + const struct acpi_device_id *ids; + struct list_head list_node; + int (*attach)(struct acpi_device *dev, const struct acpi_device_id *id); + void (*detach)(struct acpi_device *dev); +}; + +where ids is the list of IDs of device nodes the given handler is supposed to +take care of, list_node is the hook to the global list of ACPI scan handlers +maintained by the ACPI core and the .attach() and .detach() callbacks are +executed, respectively, after registration of new device nodes and before +unregistration of device nodes the handler attached to previously. + +The namespace scanning function, acpi_bus_scan(), first registers all of the +device nodes in the given namespace scope with the driver core. Then, it tries +to match a scan handler against each of them using the ids arrays of the +available scan handlers. If a matching scan handler is found, its .attach() +callback is executed for the given device node. If that callback returns 1, +that means that the handler has claimed the device node and is now responsible +for carrying out any additional configuration tasks related to it. It also will +be responsible for preparing the device node for unregistration in that case. +The device node's handler field is then populated with the address of the scan +handler that has claimed it. + +If the .attach() callback returns 0, it means that the device node is not +interesting to the given scan handler and may be matched against the next scan +handler in the list. If it returns a (negative) error code, that means that +the namespace scan should be terminated due to a serious error. The error code +returned should then reflect the type of the error. + +The namespace trimming function, acpi_bus_trim(), first executes .detach() +callbacks from the scan handlers of all device nodes in the given namespace +scope (if they have scan handlers). Next, it unregisters all of the device +nodes in that scope. + +ACPI scan handlers can be added to the list maintained by the ACPI core with the +help of the acpi_scan_add_handler() function taking a pointer to the new scan +handler as an argument. The order in which scan handlers are added to the list +is the order in which they are matched against device nodes during namespace +scans. + +All scan handles must be added to the list before acpi_bus_scan() is run for the +first time and they cannot be removed from it. diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index fe9f2c926663..1453cd0672fb 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -53,6 +53,7 @@ static const struct acpi_device_id acpi_platform_device_ids[] = { static LIST_HEAD(acpi_device_list); static LIST_HEAD(acpi_bus_id_list); static DEFINE_MUTEX(acpi_scan_lock); +static LIST_HEAD(acpi_scan_handlers_list); DEFINE_MUTEX(acpi_device_lock); LIST_HEAD(acpi_wakeup_device_list); @@ -62,6 +63,15 @@ struct acpi_device_bus_id{ struct list_head node; }; +int acpi_scan_add_handler(struct acpi_scan_handler *handler) +{ + if (!handler || !handler->attach) + return -EINVAL; + + list_add_tail(&handler->list_node, &acpi_scan_handlers_list); + return 0; +} + /* * Creates hid/cid(s) string needed for modalias and uevent * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get: @@ -1570,20 +1580,42 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, return AE_OK; } +static int acpi_scan_attach_handler(struct acpi_device *device) +{ + struct acpi_scan_handler *handler; + int ret = 0; + + list_for_each_entry(handler, &acpi_scan_handlers_list, list_node) { + const struct acpi_device_id *id; + + id = __acpi_match_device(device, handler->ids); + if (!id) + continue; + + ret = handler->attach(device, id); + if (ret > 0) { + device->handler = handler; + break; + } else if (ret < 0) { + break; + } + } + return ret; +} + static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, void *not_used, void **ret_not_used) { const struct acpi_device_id *id; - acpi_status status = AE_OK; struct acpi_device *device; unsigned long long sta_not_used; - int type_not_used; + int ret; /* * Ignore errors ignored by acpi_bus_check_add() to avoid terminating * namespace walks prematurely. */ - if (acpi_bus_type_and_status(handle, &type_not_used, &sta_not_used)) + if (acpi_bus_type_and_status(handle, &ret, &sta_not_used)) return AE_OK; if (acpi_bus_get_device(handle, &device)) @@ -1593,10 +1625,15 @@ static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, if (id) { /* This is a known good platform device. */ acpi_create_platform_device(device, id->driver_data); - } else if (device_attach(&device->dev) < 0) { - status = AE_CTRL_DEPTH; + return AE_OK; } - return status; + + ret = acpi_scan_attach_handler(device); + if (ret) + return ret > 0 ? AE_OK : AE_CTRL_DEPTH; + + ret = device_attach(&device->dev); + return ret >= 0 ? AE_OK : AE_CTRL_DEPTH; } /** @@ -1639,8 +1676,17 @@ static acpi_status acpi_bus_device_detach(acpi_handle handle, u32 lvl_not_used, struct acpi_device *device = NULL; if (!acpi_bus_get_device(handle, &device)) { + struct acpi_scan_handler *dev_handler = device->handler; + device->removal_type = ACPI_BUS_REMOVAL_EJECT; - device_release_driver(&device->dev); + if (dev_handler) { + if (dev_handler->detach) + dev_handler->detach(device); + + device->handler = NULL; + } else { + device_release_driver(&device->dev); + } } return AE_OK; } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index ad0a86ac5cce..41850cb21730 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -83,6 +83,18 @@ enum acpi_bus_device_type { struct acpi_driver; struct acpi_device; +/* + * ACPI Scan Handler + * ----------------- + */ + +struct acpi_scan_handler { + const struct acpi_device_id *ids; + struct list_head list_node; + int (*attach)(struct acpi_device *dev, const struct acpi_device_id *id); + void (*detach)(struct acpi_device *dev); +}; + /* * ACPI Driver * ----------- @@ -269,6 +281,7 @@ struct acpi_device { struct acpi_device_wakeup wakeup; struct acpi_device_perf performance; struct acpi_device_dir dir; + struct acpi_scan_handler *handler; struct acpi_driver *driver; void *driver_data; struct device dev; @@ -382,6 +395,7 @@ int acpi_bus_receive_event(struct acpi_bus_event *event); static inline int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data) { return 0; } #endif +int acpi_scan_add_handler(struct acpi_scan_handler *handler); int acpi_bus_register_driver(struct acpi_driver *driver); void acpi_bus_unregister_driver(struct acpi_driver *driver); int acpi_bus_scan(acpi_handle handle); -- cgit v1.2.3 From 4daeaf68379f75dedd120582add5206c7c5ad72e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 30 Jan 2013 14:27:37 +0100 Subject: ACPI / PCI: Make PCI IRQ link driver use struct acpi_scan_handler Make the ACPI PCI IRQ link driver use struct acpi_scan_handler for representing the object used to set up ACPI interrupt links and to remove data structures used for this purpose before unregistering the corresponding ACPI device nodes. This simplifies the code slightly and reduces the kernel's memory footprint by avoiding the registration of a struct device_driver object with the driver core and creation of its sysfs directory which is unnecessary. Signed-off-by: Rafael J. Wysocki Acked-by: Yinghai Lu Acked-by: Toshi Kani --- drivers/acpi/internal.h | 1 + drivers/acpi/pci_link.c | 47 ++++++++++++++--------------------------------- drivers/acpi/scan.c | 1 + 3 files changed, 16 insertions(+), 33 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 8310ba010176..e5a65217e480 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -26,6 +26,7 @@ int init_acpi_device_notify(void); int acpi_scan_init(void); void acpi_pci_root_init(void); +void acpi_pci_link_init(void); int acpi_sysfs_init(void); void acpi_csrt_init(void); diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index b6592797f5b2..ab764ed34a50 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -53,23 +53,19 @@ ACPI_MODULE_NAME("pci_link"); #define ACPI_PCI_LINK_FILE_STATUS "state" #define ACPI_PCI_LINK_MAX_POSSIBLE 16 -static int acpi_pci_link_add(struct acpi_device *device); -static int acpi_pci_link_remove(struct acpi_device *device); +static int acpi_pci_link_add(struct acpi_device *device, + const struct acpi_device_id *not_used); +static void acpi_pci_link_remove(struct acpi_device *device); static const struct acpi_device_id link_device_ids[] = { {"PNP0C0F", 0}, {"", 0}, }; -MODULE_DEVICE_TABLE(acpi, link_device_ids); -static struct acpi_driver acpi_pci_link_driver = { - .name = "pci_link", - .class = ACPI_PCI_LINK_CLASS, +static struct acpi_scan_handler pci_link_handler = { .ids = link_device_ids, - .ops = { - .add = acpi_pci_link_add, - .remove = acpi_pci_link_remove, - }, + .attach = acpi_pci_link_add, + .detach = acpi_pci_link_remove, }; /* @@ -692,7 +688,8 @@ int acpi_pci_link_free_irq(acpi_handle handle) Driver Interface -------------------------------------------------------------------------- */ -static int acpi_pci_link_add(struct acpi_device *device) +static int acpi_pci_link_add(struct acpi_device *device, + const struct acpi_device_id *not_used) { int result; struct acpi_pci_link *link; @@ -746,7 +743,7 @@ static int acpi_pci_link_add(struct acpi_device *device) if (result) kfree(link); - return result; + return result < 0 ? result : 1; } static int acpi_pci_link_resume(struct acpi_pci_link *link) @@ -766,7 +763,7 @@ static void irqrouter_resume(void) } } -static int acpi_pci_link_remove(struct acpi_device *device) +static void acpi_pci_link_remove(struct acpi_device *device) { struct acpi_pci_link *link; @@ -777,7 +774,6 @@ static int acpi_pci_link_remove(struct acpi_device *device) mutex_unlock(&acpi_link_lock); kfree(link); - return 0; } /* @@ -874,20 +870,10 @@ static struct syscore_ops irqrouter_syscore_ops = { .resume = irqrouter_resume, }; -static int __init irqrouter_init_ops(void) -{ - if (!acpi_disabled && !acpi_noirq) - register_syscore_ops(&irqrouter_syscore_ops); - - return 0; -} - -device_initcall(irqrouter_init_ops); - -static int __init acpi_pci_link_init(void) +void __init acpi_pci_link_init(void) { if (acpi_noirq) - return 0; + return; if (acpi_irq_balance == -1) { /* no command line switch: enable balancing in IOAPIC mode */ @@ -896,11 +882,6 @@ static int __init acpi_pci_link_init(void) else acpi_irq_balance = 0; } - - if (acpi_bus_register_driver(&acpi_pci_link_driver) < 0) - return -ENODEV; - - return 0; + register_syscore_ops(&irqrouter_syscore_ops); + acpi_scan_add_handler(&pci_link_handler); } - -subsys_initcall(acpi_pci_link_init); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 1453cd0672fb..c2821699bc49 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1774,6 +1774,7 @@ int __init acpi_scan_init(void) } acpi_pci_root_init(); + acpi_pci_link_init(); acpi_csrt_init(); /* -- cgit v1.2.3 From 141a297bd02e8ddc5ab625cc3a1a5926b1ff929a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 30 Jan 2013 14:27:40 +0100 Subject: ACPI / platform: Use struct acpi_scan_handler for creating devices Currently, the ACPI namespace scanning code creates platform device objects for ACPI device nodes whose IDs match the contents of the acpi_platform_device_ids[] table. However, this adds a superfluous special case into acpi_bus_device_attach() and makes it more difficult to follow than it has to be. It also will make it more difficult to implement removal code for those platform device objects in the future. For the above reasons, introduce a struct acpi_scan_handler object for creating platform devices and move the code related to that from acpi_bus_device_attach() to the .attach() callback of that object. Also move the acpi_platform_device_ids[] table to acpi_platform.c. Signed-off-by: Rafael J. Wysocki Acked-by: Yinghai Lu Acked-by: Yasuaki Ishimatsu Acked-by: Toshi Kani Reviewed-by: Mika Westerberg Tested-by: Mika Westerberg --- drivers/acpi/acpi_platform.c | 59 +++++++++++++++++++++++++++++++++++--------- drivers/acpi/internal.h | 7 +----- drivers/acpi/scan.c | 30 +--------------------- 3 files changed, 50 insertions(+), 46 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index 2d1fb4c21605..26fce4b8a632 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -22,6 +22,30 @@ ACPI_MODULE_NAME("platform"); +/* Flags for acpi_create_platform_device */ +#define ACPI_PLATFORM_CLK BIT(0) + +/* + * The following ACPI IDs are known to be suitable for representing as + * platform devices. + */ +static const struct acpi_device_id acpi_platform_device_ids[] = { + + { "PNP0D40" }, + + /* Haswell LPSS devices */ + { "INT33C0", ACPI_PLATFORM_CLK }, + { "INT33C1", ACPI_PLATFORM_CLK }, + { "INT33C2", ACPI_PLATFORM_CLK }, + { "INT33C3", ACPI_PLATFORM_CLK }, + { "INT33C4", ACPI_PLATFORM_CLK }, + { "INT33C5", ACPI_PLATFORM_CLK }, + { "INT33C6", ACPI_PLATFORM_CLK }, + { "INT33C7", ACPI_PLATFORM_CLK }, + + { } +}; + static int acpi_create_platform_clks(struct acpi_device *adev) { static struct platform_device *pdev; @@ -39,8 +63,7 @@ static int acpi_create_platform_clks(struct acpi_device *adev) /** * acpi_create_platform_device - Create platform device for ACPI device node * @adev: ACPI device node to create a platform device for. - * @flags: ACPI_PLATFORM_* flags that affect the creation of the platform - * devices. + * @id: ACPI device ID used to match @adev. * * Check if the given @adev can be represented as a platform device and, if * that's the case, create and register a platform device, populate its common @@ -48,9 +71,10 @@ static int acpi_create_platform_clks(struct acpi_device *adev) * * Name of the platform device will be the same as @adev's. */ -struct platform_device *acpi_create_platform_device(struct acpi_device *adev, - unsigned long flags) +static int acpi_create_platform_device(struct acpi_device *adev, + const struct acpi_device_id *id) { + unsigned long flags = id->driver_data; struct platform_device *pdev = NULL; struct acpi_device *acpi_parent; struct platform_device_info pdevinfo; @@ -59,25 +83,28 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev, struct resource *resources; int count; - if ((flags & ACPI_PLATFORM_CLK) && acpi_create_platform_clks(adev)) { - dev_err(&adev->dev, "failed to create clocks\n"); - return NULL; + if (flags & ACPI_PLATFORM_CLK) { + int ret = acpi_create_platform_clks(adev); + if (ret) { + dev_err(&adev->dev, "failed to create clocks\n"); + return ret; + } } /* If the ACPI node already has a physical device attached, skip it. */ if (adev->physical_node_count) - return NULL; + return 0; INIT_LIST_HEAD(&resource_list); count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); if (count <= 0) - return NULL; + return 0; resources = kmalloc(count * sizeof(struct resource), GFP_KERNEL); if (!resources) { dev_err(&adev->dev, "No memory for resources\n"); acpi_dev_free_resource_list(&resource_list); - return NULL; + return -ENOMEM; } count = 0; list_for_each_entry(rentry, &resource_list, node) @@ -123,5 +150,15 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev, } kfree(resources); - return pdev; + return 1; +} + +static struct acpi_scan_handler platform_handler = { + .ids = acpi_platform_device_ids, + .attach = acpi_create_platform_device, +}; + +void __init acpi_platform_init(void) +{ + acpi_scan_add_handler(&platform_handler); } diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index e5a65217e480..0d1397dc7003 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -27,6 +27,7 @@ int init_acpi_device_notify(void); int acpi_scan_init(void); void acpi_pci_root_init(void); void acpi_pci_link_init(void); +void acpi_platform_init(void); int acpi_sysfs_init(void); void acpi_csrt_init(void); @@ -119,10 +120,4 @@ static inline void suspend_nvs_restore(void) {} -------------------------------------------------------------------------- */ struct platform_device; -/* Flags for acpi_create_platform_device */ -#define ACPI_PLATFORM_CLK BIT(0) - -struct platform_device *acpi_create_platform_device(struct acpi_device *adev, - unsigned long flags); - #endif /* _ACPI_INTERNAL_H_ */ diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index c2821699bc49..a849d2430dff 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -29,27 +29,6 @@ extern struct acpi_device *acpi_root; static const char *dummy_hid = "device"; -/* - * The following ACPI IDs are known to be suitable for representing as - * platform devices. - */ -static const struct acpi_device_id acpi_platform_device_ids[] = { - - { "PNP0D40" }, - - /* Haswell LPSS devices */ - { "INT33C0", ACPI_PLATFORM_CLK }, - { "INT33C1", ACPI_PLATFORM_CLK }, - { "INT33C2", ACPI_PLATFORM_CLK }, - { "INT33C3", ACPI_PLATFORM_CLK }, - { "INT33C4", ACPI_PLATFORM_CLK }, - { "INT33C5", ACPI_PLATFORM_CLK }, - { "INT33C6", ACPI_PLATFORM_CLK }, - { "INT33C7", ACPI_PLATFORM_CLK }, - - { } -}; - static LIST_HEAD(acpi_device_list); static LIST_HEAD(acpi_bus_id_list); static DEFINE_MUTEX(acpi_scan_lock); @@ -1606,7 +1585,6 @@ static int acpi_scan_attach_handler(struct acpi_device *device) static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, void *not_used, void **ret_not_used) { - const struct acpi_device_id *id; struct acpi_device *device; unsigned long long sta_not_used; int ret; @@ -1621,13 +1599,6 @@ static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, if (acpi_bus_get_device(handle, &device)) return AE_CTRL_DEPTH; - id = __acpi_match_device(device, acpi_platform_device_ids); - if (id) { - /* This is a known good platform device. */ - acpi_create_platform_device(device, id->driver_data); - return AE_OK; - } - ret = acpi_scan_attach_handler(device); if (ret) return ret > 0 ? AE_OK : AE_CTRL_DEPTH; @@ -1775,6 +1746,7 @@ int __init acpi_scan_init(void) acpi_pci_root_init(); acpi_pci_link_init(); + acpi_platform_init(); acpi_csrt_init(); /* -- cgit v1.2.3 From 0613e1f7fd98a0cef2a7add1368a87cdd86a1106 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 31 Jan 2013 20:54:05 +0100 Subject: ACPI / scan: Fix acpi_bus_get_device() check in acpi_match_device() Since acpi_bus_get_device() returns int and not acpi_status, change acpi_match_device() so that it doesn't apply ACPI_FAILURE() to the return value of acpi_bus_get_device(). Signed-off-by: Rafael J. Wysocki Acked-by: Yasuaki Ishimatsu Acked-by: Yinghai Lu Reviewed-by: Mika Westerberg --- drivers/acpi/scan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 43754655c156..a4224727f09c 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -444,9 +444,9 @@ const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids, const struct device *dev) { struct acpi_device *adev; + acpi_handle handle = ACPI_HANDLE(dev); - if (!ids || !ACPI_HANDLE(dev) - || ACPI_FAILURE(acpi_bus_get_device(ACPI_HANDLE(dev), &adev))) + if (!ids || !handle || acpi_bus_get_device(handle, &adev)) return NULL; return __acpi_match_device(adev, ids); -- cgit v1.2.3 From 456de893ea1a693dc266c5464a6d857bfed0875f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 31 Jan 2013 20:57:40 +0100 Subject: ACPI / scan: Clean up acpi_bus_get_parent() Make acpi_bus_get_parent() more straightforward and remove an unnecessary local variable ret from it. Signed-off-by: Rafael J. Wysocki Acked-by: Yasuaki Ishimatsu Acked-by: Yinghai Lu Reviewed-by: Mika Westerberg --- drivers/acpi/scan.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index a849d2430dff..360f1338749b 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -871,29 +871,23 @@ EXPORT_SYMBOL(acpi_bus_unregister_driver); -------------------------------------------------------------------------- */ static struct acpi_device *acpi_bus_get_parent(acpi_handle handle) { + struct acpi_device *device = NULL; acpi_status status; - int ret; - struct acpi_device *device; /* * Fixed hardware devices do not appear in the namespace and do not * have handles, but we fabricate acpi_devices for them, so we have * to deal with them specially. */ - if (handle == NULL) + if (!handle) return acpi_root; do { status = acpi_get_parent(handle, &handle); - if (status == AE_NULL_ENTRY) - return NULL; if (ACPI_FAILURE(status)) - return acpi_root; - - ret = acpi_bus_get_device(handle, &device); - if (ret == 0) - return device; - } while (1); + return status == AE_NULL_ENTRY ? NULL : acpi_root; + } while (acpi_bus_get_device(handle, &device)); + return device; } acpi_status -- cgit v1.2.3 From b3785492268f9f3cdaa9722facb84b266dcf8bf6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 1 Feb 2013 23:43:02 +0100 Subject: ACPI / PM: Do not power manage devices in unknown initial states In general, for ACPI device power management to work, the initial power states of devices must be known (otherwise, we wouldn't be able to keep track of power resources, for example). Hence, if it is impossible to determine the initial ACPI power states of some devices, they can't be regarded as power-manageable using ACPI. For this reason, modify acpi_bus_get_power_flags() to clear the power_manageable flag if acpi_bus_init_power() fails and add some extra fallback code to acpi_bus_init_power() to cover broken BIOSes that provide _PS0/_PS3 without _PSC for some devices. Verified to work on my HP nx6325 that has this problem. Signed-off-by: Rafael J. Wysocki Tested-by: Peter Wu --- drivers/acpi/device_pm.c | 6 ++++++ drivers/acpi/scan.c | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 3ef075b71870..164d609d7c9e 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -330,6 +330,12 @@ int acpi_bus_init_power(struct acpi_device *device) result = acpi_dev_pm_explicit_set(device, state); if (result) return result; + } else if (state == ACPI_STATE_UNKNOWN) { + /* No power resources and missing _PSC? Try to force D0. */ + state = ACPI_STATE_D0; + result = acpi_dev_pm_explicit_set(device, state); + if (result) + return result; } device->power.state = state; return 0; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 9801837876b7..f75f25c2e455 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1180,7 +1180,10 @@ static void acpi_bus_get_power_flags(struct acpi_device *device) device->power.flags.power_resources) device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible = 1; - acpi_bus_init_power(device); + if (acpi_bus_init_power(device)) { + acpi_free_power_resources_lists(device); + device->flags.power_manageable = 0; + } } static void acpi_bus_get_flags(struct acpi_device *device) -- cgit v1.2.3 From 87b85b3c8a4ac286d41a1c6419014b7562e4663b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 6 Feb 2013 13:05:22 +0100 Subject: ACPI / scan: Follow priorities of IDs when matching scan handlers The IDs of ACPI device nodes stored in their pnp.ids member arrays are sorted by decreasing priority (i.e. the highest-priority ID is the first entry). This means that when matching scan handlers to device nodes, the namespace scanning code should walk the list of scan handlers for each device node ID instead of walking the list of device node IDs for each handler (the latter causes the first handler matching any of the device node IDs to be chosen, although there may be another handler matching an ID of a higher priority which should be preferred). Make the code follow this observation. This change has been suggested and justified by Toshi Kani. Signed-off-by: Rafael J. Wysocki Acked-by: Toshi Kani --- drivers/acpi/scan.c | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 360f1338749b..688b7f7c23dd 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1553,26 +1553,42 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, return AE_OK; } -static int acpi_scan_attach_handler(struct acpi_device *device) +static int acpi_scan_do_attach_handler(struct acpi_device *device, char *id) { struct acpi_scan_handler *handler; - int ret = 0; list_for_each_entry(handler, &acpi_scan_handlers_list, list_node) { - const struct acpi_device_id *id; + const struct acpi_device_id *devid; - id = __acpi_match_device(device, handler->ids); - if (!id) - continue; + for (devid = handler->ids; devid->id[0]; devid++) { + int ret; - ret = handler->attach(device, id); - if (ret > 0) { - device->handler = handler; - break; - } else if (ret < 0) { - break; + if (strcmp((char *)devid->id, id)) + continue; + + ret = handler->attach(device, devid); + if (ret > 0) { + device->handler = handler; + return ret; + } else if (ret < 0) { + return ret; + } } } + return 0; +} + +static int acpi_scan_attach_handler(struct acpi_device *device) +{ + struct acpi_hardware_id *hwid; + int ret = 0; + + list_for_each_entry(hwid, &device->pnp.ids, list) { + ret = acpi_scan_do_attach_handler(device, hwid->id); + if (ret) + break; + + } return ret; } -- cgit v1.2.3 From ce7685ad764f070a9234df6a12a17eb92471d126 Mon Sep 17 00:00:00 2001 From: Toshi Kani Date: Thu, 7 Feb 2013 12:50:53 +0100 Subject: ACPI: sysfs eject support for ACPI scan handlers Changed sysfs eject, acpi_eject_store(), so that it doesn't return error codes for devices nodes with ACPI scan handlers attached and no ACPI drivers. [rjw: Changelog] Signed-off-by: Toshi Kani Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 688b7f7c23dd..95547efcffe0 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -203,7 +203,7 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, return -EINVAL; } #ifndef FORCE_EJECT - if (acpi_device->driver == NULL) { + if (!acpi_device->driver && !acpi_device->handler) { ret = -ENODEV; goto err; } -- cgit v1.2.3 From 5f27ee8e1261e47ec2e9dbebf17e87e55f568d75 Mon Sep 17 00:00:00 2001 From: Toshi Kani Date: Thu, 7 Feb 2013 21:19:13 +0000 Subject: ACPI: Unbind ACPI drv when probe failed When acpi_device_install_notify_handler() failed in acpi_device_probe(), it calls acpi_drv->ops.remove() and fails the probe. However, the ACPI driver is left bound to the acpi_device. Fix it by clearing the driver and driver_data fields. Signed-off-by: Toshi Kani Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 95547efcffe0..bf6e2c26f52d 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -627,6 +627,8 @@ static int acpi_device_probe(struct device * dev) if (ret) { if (acpi_drv->ops.remove) acpi_drv->ops.remove(acpi_dev); + acpi_dev->driver = NULL; + acpi_dev->driver_data = NULL; return ret; } } -- cgit v1.2.3 From 38475b3be1517a16d263b0b04dae862bf7027d48 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 8 Feb 2013 23:52:23 +0100 Subject: ACPI / scan: Remove useless #ifndef from acpi_eject_store() Since the FORCE_EJECT symbol is never defined, the #ifndef FORCE_EJECT in acpi_eject_store() is always true, so drop it. Signed-off-by: Rafael J. Wysocki Acked-by: Yasuaki Ishimatsu Tested-by: Yasuaki Ishimatsu Reviewed-by: Toshi Kani Tested-by: Toshi Kani --- drivers/acpi/scan.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index bf6e2c26f52d..5bc2641fba8a 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -202,12 +202,10 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, if ((!count) || (buf[0] != '1')) { return -EINVAL; } -#ifndef FORCE_EJECT if (!acpi_device->driver && !acpi_device->handler) { ret = -ENODEV; goto err; } -#endif status = acpi_get_type(acpi_device->handle, &type); if (ACPI_FAILURE(status) || (!acpi_device->flags.ejectable)) { ret = -ENODEV; -- cgit v1.2.3 From 737f1a9f808280c481681b1f46254fd67023ec2f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 8 Feb 2013 23:52:39 +0100 Subject: ACPI / scan: Make container driver use struct acpi_scan_handler Make the ACPI container driver use struct acpi_scan_handler for representing the object used to initialize ACPI containers and remove the ACPI driver structure used previously and the data structures created by it, since in fact they were not used for any purpose. This simplifies the code and reduces the kernel's memory footprint by avoiding the registration of a struct device_driver object with the driver core and creation of its sysfs directory which is unnecessary. In addition to that, make the namespace walk callback used for installing the notify handlers for ACPI containers more straightforward. This change includes fixes from Toshi Kani. Signed-off-by: Rafael J. Wysocki Acked-by: Yinghai Lu Acked-by: Yasuaki Ishimatsu Tested-by: Yasuaki Ishimatsu Reviewed-by: Toshi Kani Tested-by: Toshi Kani --- drivers/acpi/Kconfig | 2 +- drivers/acpi/container.c | 167 ++++++++++------------------------------------- drivers/acpi/internal.h | 5 ++ drivers/acpi/scan.c | 1 + 4 files changed, 43 insertions(+), 132 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 38c5078da11d..78105b3a5262 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -337,7 +337,7 @@ config X86_PM_TIMER systems require this timer. config ACPI_CONTAINER - tristate "Container and Module Devices (EXPERIMENTAL)" + bool "Container and Module Devices (EXPERIMENTAL)" depends on EXPERIMENTAL default (ACPI_HOTPLUG_MEMORY || ACPI_HOTPLUG_CPU || ACPI_HOTPLUG_IO) help diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index cc0bf4613e0d..9053e86e9904 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -38,42 +38,31 @@ #define PREFIX "ACPI: " -#define ACPI_CONTAINER_DEVICE_NAME "ACPI container device" -#define ACPI_CONTAINER_CLASS "container" - -#define INSTALL_NOTIFY_HANDLER 1 -#define UNINSTALL_NOTIFY_HANDLER 2 - #define _COMPONENT ACPI_CONTAINER_COMPONENT ACPI_MODULE_NAME("container"); -MODULE_AUTHOR("Anil S Keshavamurthy"); -MODULE_DESCRIPTION("ACPI container driver"); -MODULE_LICENSE("GPL"); - -static int acpi_container_add(struct acpi_device *device); -static int acpi_container_remove(struct acpi_device *device); - static const struct acpi_device_id container_device_ids[] = { {"ACPI0004", 0}, {"PNP0A05", 0}, {"PNP0A06", 0}, {"", 0}, }; -MODULE_DEVICE_TABLE(acpi, container_device_ids); -static struct acpi_driver acpi_container_driver = { - .name = "container", - .class = ACPI_CONTAINER_CLASS, +static int container_device_attach(struct acpi_device *device, + const struct acpi_device_id *not_used) +{ + /* + * FIXME: This is necessary, so that acpi_eject_store() doesn't return + * -ENODEV for containers. + */ + return 1; +} + +static struct acpi_scan_handler container_device_handler = { .ids = container_device_ids, - .ops = { - .add = acpi_container_add, - .remove = acpi_container_remove, - }, + .attach = container_device_attach, }; -/*******************************************************************/ - static int is_device_present(acpi_handle handle) { acpi_handle temp; @@ -92,49 +81,6 @@ static int is_device_present(acpi_handle handle) return ((sta & ACPI_STA_DEVICE_PRESENT) == ACPI_STA_DEVICE_PRESENT); } -static bool is_container_device(const char *hid) -{ - const struct acpi_device_id *container_id; - - for (container_id = container_device_ids; - container_id->id[0]; container_id++) { - if (!strcmp((char *)container_id->id, hid)) - return true; - } - - return false; -} - -/*******************************************************************/ -static int acpi_container_add(struct acpi_device *device) -{ - struct acpi_container *container; - - container = kzalloc(sizeof(struct acpi_container), GFP_KERNEL); - if (!container) - return -ENOMEM; - - container->handle = device->handle; - strcpy(acpi_device_name(device), ACPI_CONTAINER_DEVICE_NAME); - strcpy(acpi_device_class(device), ACPI_CONTAINER_CLASS); - device->driver_data = container; - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device <%s> bid <%s>\n", - acpi_device_name(device), acpi_device_bid(device))); - - return 0; -} - -static int acpi_container_remove(struct acpi_device *device) -{ - acpi_status status = AE_OK; - struct acpi_container *pc = NULL; - - pc = acpi_driver_data(device); - kfree(pc); - return status; -} - static void container_notify_cb(acpi_handle handle, u32 type, void *context) { struct acpi_device *device = NULL; @@ -199,84 +145,43 @@ static void container_notify_cb(acpi_handle handle, u32 type, void *context) return; } -static acpi_status -container_walk_namespace_cb(acpi_handle handle, - u32 lvl, void *context, void **rv) +static bool is_container(acpi_handle handle) { - char *hid = NULL; struct acpi_device_info *info; - acpi_status status; - int *action = context; - - status = acpi_get_object_info(handle, &info); - if (ACPI_FAILURE(status)) { - return AE_OK; - } + bool ret = false; - if (info->valid & ACPI_VALID_HID) - hid = info->hardware_id.string; + if (ACPI_FAILURE(acpi_get_object_info(handle, &info))) + return false; - if (hid == NULL) { - goto end; - } - - if (!is_container_device(hid)) - goto end; + if (info->valid & ACPI_VALID_HID) { + const struct acpi_device_id *id; - switch (*action) { - case INSTALL_NOTIFY_HANDLER: - acpi_install_notify_handler(handle, - ACPI_SYSTEM_NOTIFY, - container_notify_cb, NULL); - break; - case UNINSTALL_NOTIFY_HANDLER: - acpi_remove_notify_handler(handle, - ACPI_SYSTEM_NOTIFY, - container_notify_cb); - break; - default: - break; + for (id = container_device_ids; id->id[0]; id++) { + ret = !strcmp((char *)id->id, info->hardware_id.string); + if (ret) + break; + } } - - end: kfree(info); - - return AE_OK; + return ret; } -static int __init acpi_container_init(void) +static acpi_status acpi_container_register_notify_handler(acpi_handle handle, + u32 lvl, void *ctxt, + void **retv) { - int result = 0; - int action = INSTALL_NOTIFY_HANDLER; - - result = acpi_bus_register_driver(&acpi_container_driver); - if (result < 0) { - return (result); - } - - /* register notify handler to every container device */ - acpi_walk_namespace(ACPI_TYPE_DEVICE, - ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, - container_walk_namespace_cb, NULL, &action, NULL); + if (is_container(handle)) + acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + container_notify_cb, NULL); - return (0); + return AE_OK; } -static void __exit acpi_container_exit(void) +void __init acpi_container_init(void) { - int action = UNINSTALL_NOTIFY_HANDLER; - - - acpi_walk_namespace(ACPI_TYPE_DEVICE, - ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, - container_walk_namespace_cb, NULL, &action, NULL); + acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, + acpi_container_register_notify_handler, NULL, + NULL, NULL); - acpi_bus_unregister_driver(&acpi_container_driver); - - return; + acpi_scan_add_handler(&container_device_handler); } - -module_init(acpi_container_init); -module_exit(acpi_container_exit); diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 0d1397dc7003..79092328cf06 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -30,6 +30,11 @@ void acpi_pci_link_init(void); void acpi_platform_init(void); int acpi_sysfs_init(void); void acpi_csrt_init(void); +#ifdef CONFIG_ACPI_CONTAINER +void acpi_container_init(void); +#else +static inline void acpi_container_init(void) {} +#endif #ifdef CONFIG_DEBUG_FS extern struct dentry *acpi_debugfs_dir; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 5bc2641fba8a..a48b6e92f9f8 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1758,6 +1758,7 @@ int __init acpi_scan_init(void) acpi_pci_link_init(); acpi_platform_init(); acpi_csrt_init(); + acpi_container_init(); /* * Enumerate devices in the ACPI namespace. -- cgit v1.2.3 From f058cdf4cf3e5181172455f90fc73f2127b6ddf8 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 9 Feb 2013 15:29:11 +0100 Subject: ACPI / scan: Make acpi_bus_hot_remove_device() acquire the scan lock The ACPI scan lock has been introduced to prevent acpi_bus_scan() and acpi_bus_trim() from running in parallel with each other for overlapping ACPI namespace scopes. However, it is not sufficient to do that, because if acpi_bus_scan() is run (for an overlapping namespace scope) right after the acpi_bus_trim() in acpi_bus_hot_remove_device(), the subsequent eject will remove devices without removing the corresponding struct acpi_device objects (and possibly companion "physical" device objects). Therefore acpi_bus_hot_remove_device() has to acquire the scan lock before carrying out the bus trimming and hold it through the evaluation of _EJ0, so make that happen. Signed-off-by: Rafael J. Wysocki Acked-by: Toshi Kani --- drivers/acpi/scan.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index a48b6e92f9f8..75fb14fc19e8 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -95,6 +95,8 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha } static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); +static void __acpi_bus_trim(struct acpi_device *start); + /** * acpi_bus_hot_remove_device: hot-remove a device and its children * @context: struct acpi_eject_event pointer (freed in this func) @@ -114,10 +116,12 @@ void acpi_bus_hot_remove_device(void *context) acpi_status status = AE_OK; u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */ + mutex_lock(&acpi_scan_lock); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Hot-removing device %s...\n", dev_name(&device->dev))); - acpi_bus_trim(device); + __acpi_bus_trim(device); /* Device node has been released. */ device = NULL; @@ -146,18 +150,14 @@ void acpi_bus_hot_remove_device(void *context) status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL); if (ACPI_FAILURE(status)) { if (status != AE_NOT_FOUND) - printk(KERN_WARNING PREFIX - "Eject device failed\n"); - goto err_out; - } + acpi_handle_warn(handle, "Eject failed\n"); - kfree(context); - return; + /* Tell the firmware the hot-remove operation has failed. */ + acpi_evaluate_hotplug_ost(handle, ej_event->event, + ost_code, NULL); + } -err_out: - /* Inform firmware the hot-remove operation has completed w/ error */ - (void) acpi_evaluate_hotplug_ost(handle, - ej_event->event, ost_code, NULL); + mutex_unlock(&acpi_scan_lock); kfree(context); return; } @@ -1683,10 +1683,8 @@ static acpi_status acpi_bus_remove(acpi_handle handle, u32 lvl_not_used, return AE_OK; } -void acpi_bus_trim(struct acpi_device *start) +static void __acpi_bus_trim(struct acpi_device *start) { - mutex_lock(&acpi_scan_lock); - /* * Execute acpi_bus_device_detach() as a post-order callback to detach * all ACPI drivers from the device nodes being removed. @@ -1701,7 +1699,12 @@ void acpi_bus_trim(struct acpi_device *start) acpi_walk_namespace(ACPI_TYPE_ANY, start->handle, ACPI_UINT32_MAX, NULL, acpi_bus_remove, NULL, NULL); acpi_bus_remove(start->handle, 0, NULL, NULL); +} +void acpi_bus_trim(struct acpi_device *start) +{ + mutex_lock(&acpi_scan_lock); + __acpi_bus_trim(start); mutex_unlock(&acpi_scan_lock); } EXPORT_SYMBOL_GPL(acpi_bus_trim); -- cgit v1.2.3 From 0aa120a0138398d6597350f4c1dcb46d14be1a0b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 9 Feb 2013 15:29:20 +0100 Subject: ACPI / scan: Full transition to D3cold in acpi_device_unregister() In order to drop reference counts of all power resources used by an ACPI device node being removed, acpi_device_unregister() calls acpi_power_transition(device, ACPI_STATE_D3_COLD), which effectively transitions the device node into D3cold if it uses any power resources. However, for some device nodes it may not be appropriate to remove power from them entirely before putting them into D3hot before. On the other hand, executing _PS3 for devices that don't use power resources before removing them shouldn't really hurt. In fact, that is done by acpi_bus_hot_remove_device(), but this is not the right place to do it, because the bus trimming may have caused power to be removed from the device node in question already before. For these reasons, make acpi_device_unregister() carry out full power-off transition for all device nodes supporting that and remove the direct evaluation of _PS3 from acpi_bus_hot_remove_device(). Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 75fb14fc19e8..c7676ee8eca4 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -125,12 +125,6 @@ void acpi_bus_hot_remove_device(void *context) /* Device node has been released. */ device = NULL; - /* power off device */ - status = acpi_evaluate_object(handle, "_PS3", NULL, NULL); - if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) - printk(KERN_WARNING PREFIX - "Power-off device failed\n"); - if (ACPI_SUCCESS(acpi_get_handle(handle, "_LCK", &temp))) { arg_list.count = 1; arg_list.pointer = &arg; @@ -780,10 +774,11 @@ static void acpi_device_unregister(struct acpi_device *device) device_del(&device->dev); /* - * Drop the reference counts of all power resources the device depends - * on and turn off the ones that have no more references. + * Transition the device to D3cold to drop the reference counts of all + * power resources the device depends on and turn off the ones that have + * no more references. */ - acpi_power_transition(device, ACPI_STATE_D3_COLD); + acpi_device_set_power(device, ACPI_STATE_D3_COLD); put_device(&device->dev); } -- cgit v1.2.3 From 3757b94802fb65d8f696597a74053cf21738da0b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 13 Feb 2013 14:36:47 +0100 Subject: ACPI / hotplug: Fix concurrency issues and memory leaks This changeset is aimed at fixing a few different but related problems in the ACPI hotplug infrastructure. First of all, since notify handlers may be run in parallel with acpi_bus_scan(), acpi_bus_trim() and acpi_bus_hot_remove_device() and some of them are installed for ACPI handles that have no struct acpi_device objects attached (i.e. before those objects are created), those notify handlers have to take acpi_scan_lock to prevent races from taking place (e.g. a struct acpi_device is found to be present for the given ACPI handle, but right after that it is removed by acpi_bus_trim() running in parallel to the given notify handler). Moreover, since some of them call acpi_bus_scan() and acpi_bus_trim(), this leads to the conclusion that acpi_scan_lock should be acquired by the callers of these two funtions rather by these functions themselves. For these reasons, make all notify handlers that can handle device addition and eject events take acpi_scan_lock and remove the acpi_scan_lock locking from acpi_bus_scan() and acpi_bus_trim(). Accordingly, update all of their users to make sure that they are always called under acpi_scan_lock. Furthermore, since eject operations are carried out asynchronously with respect to the notify events that trigger them, with the help of acpi_bus_hot_remove_device(), even if notify handlers take the ACPI scan lock, it still is possible that, for example, acpi_bus_trim() will run between acpi_bus_hot_remove_device() and the notify handler that scheduled its execution and that acpi_bus_trim() will remove the device node passed to acpi_bus_hot_remove_device() for ejection. In that case, the struct acpi_device object obtained by acpi_bus_hot_remove_device() will be invalid and not-so-funny things will ensue. To protect agaist that, make the users of acpi_bus_hot_remove_device() run get_device() on ACPI device node objects that are about to be passed to it and make acpi_bus_hot_remove_device() run put_device() on them and check if their ACPI handles are not NULL (make acpi_device_unregister() clear the device nodes' ACPI handles for that check to work). Finally, observe that acpi_os_hotplug_execute() actually can fail, in which case its caller ought to free memory allocated for the context object to prevent leaks from happening. It also needs to run put_device() on the device node that it ran get_device() on previously in that case. Modify the code accordingly. Signed-off-by: Rafael J. Wysocki Acked-by: Yinghai Lu --- drivers/acpi/acpi_memhotplug.c | 56 ++++++++++++++++++++----------- drivers/acpi/container.c | 12 ++++--- drivers/acpi/dock.c | 19 +++++++++-- drivers/acpi/processor_driver.c | 24 +++++++++---- drivers/acpi/scan.c | 69 ++++++++++++++++++++++++++------------ drivers/pci/hotplug/acpiphp_glue.c | 6 ++++ drivers/pci/hotplug/sgi_hotplug.c | 5 ++- include/acpi/acpi_bus.h | 3 ++ 8 files changed, 139 insertions(+), 55 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index 94c823b25138..034d3e72aa92 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c @@ -153,14 +153,16 @@ acpi_memory_get_device_resources(struct acpi_memory_device *mem_device) return 0; } -static int -acpi_memory_get_device(acpi_handle handle, - struct acpi_memory_device **mem_device) +static int acpi_memory_get_device(acpi_handle handle, + struct acpi_memory_device **mem_device) { struct acpi_device *device = NULL; - int result; + int result = 0; - if (!acpi_bus_get_device(handle, &device) && device) + acpi_scan_lock_acquire(); + + acpi_bus_get_device(handle, &device); + if (device) goto end; /* @@ -169,23 +171,28 @@ acpi_memory_get_device(acpi_handle handle, */ result = acpi_bus_scan(handle); if (result) { - acpi_handle_warn(handle, "Cannot add acpi bus\n"); - return -EINVAL; + acpi_handle_warn(handle, "ACPI namespace scan failed\n"); + result = -EINVAL; + goto out; } result = acpi_bus_get_device(handle, &device); if (result) { acpi_handle_warn(handle, "Missing device object\n"); - return -EINVAL; + result = -EINVAL; + goto out; } - end: + end: *mem_device = acpi_driver_data(device); if (!(*mem_device)) { dev_err(&device->dev, "driver data not found\n"); - return -ENODEV; + result = -ENODEV; + goto out; } - return 0; + out: + acpi_scan_lock_release(); + return result; } static int acpi_memory_check_device(struct acpi_memory_device *mem_device) @@ -305,6 +312,7 @@ static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data) struct acpi_device *device; struct acpi_eject_event *ej_event = NULL; u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */ + acpi_status status; switch (event) { case ACPI_NOTIFY_BUS_CHECK: @@ -327,29 +335,40 @@ static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data) ACPI_DEBUG_PRINT((ACPI_DB_INFO, "\nReceived EJECT REQUEST notification for device\n")); + status = AE_ERROR; + acpi_scan_lock_acquire(); + if (acpi_bus_get_device(handle, &device)) { acpi_handle_err(handle, "Device doesn't exist\n"); - break; + goto unlock; } mem_device = acpi_driver_data(device); if (!mem_device) { acpi_handle_err(handle, "Driver Data is NULL\n"); - break; + goto unlock; } ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL); if (!ej_event) { pr_err(PREFIX "No memory, dropping EJECT\n"); - break; + goto unlock; } + get_device(&device->dev); ej_event->device = device; ej_event->event = ACPI_NOTIFY_EJECT_REQUEST; - acpi_os_hotplug_execute(acpi_bus_hot_remove_device, - (void *)ej_event); + /* The eject is carried out asynchronously. */ + status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device, + ej_event); + if (ACPI_FAILURE(status)) { + put_device(&device->dev); + kfree(ej_event); + } - /* eject is performed asynchronously */ - return; + unlock: + acpi_scan_lock_release(); + if (ACPI_SUCCESS(status)) + return; default: ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unsupported event [0x%x]\n", event)); @@ -360,7 +379,6 @@ static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data) /* Inform firmware that the hotplug operation has completed */ (void) acpi_evaluate_hotplug_ost(handle, event, ost_code, NULL); - return; } static void acpi_memory_device_free(struct acpi_memory_device *mem_device) diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index 4cc2937cc022..5523ba7d764d 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -88,6 +88,8 @@ static void container_notify_cb(acpi_handle handle, u32 type, void *context) acpi_status status; u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */ + acpi_scan_lock_acquire(); + switch (type) { case ACPI_NOTIFY_BUS_CHECK: /* Fall through */ @@ -103,7 +105,7 @@ static void container_notify_cb(acpi_handle handle, u32 type, void *context) /* device exist and this is a remove request */ device->flags.eject_pending = 1; kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); - return; + goto out; } break; } @@ -130,18 +132,20 @@ static void container_notify_cb(acpi_handle handle, u32 type, void *context) if (!acpi_bus_get_device(handle, &device) && device) { device->flags.eject_pending = 1; kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); - return; + goto out; } break; default: /* non-hotplug event; possibly handled by other handler */ - return; + goto out; } /* Inform firmware that the hotplug operation has completed */ (void) acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL); - return; + + out: + acpi_scan_lock_release(); } static bool is_container(acpi_handle handle) diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 68d720af71ed..4fdea381ef21 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -744,7 +744,9 @@ static void acpi_dock_deferred_cb(void *context) { struct dock_data *data = context; + acpi_scan_lock_acquire(); dock_notify(data->handle, data->event, data->ds); + acpi_scan_lock_release(); kfree(data); } @@ -757,20 +759,31 @@ static int acpi_dock_notifier_call(struct notifier_block *this, if (event != ACPI_NOTIFY_BUS_CHECK && event != ACPI_NOTIFY_DEVICE_CHECK && event != ACPI_NOTIFY_EJECT_REQUEST) return 0; + + acpi_scan_lock_acquire(); + list_for_each_entry(dock_station, &dock_stations, sibling) { if (dock_station->handle == handle) { struct dock_data *dd; + acpi_status status; dd = kmalloc(sizeof(*dd), GFP_KERNEL); if (!dd) - return 0; + break; + dd->handle = handle; dd->event = event; dd->ds = dock_station; - acpi_os_hotplug_execute(acpi_dock_deferred_cb, dd); - return 0 ; + status = acpi_os_hotplug_execute(acpi_dock_deferred_cb, + dd); + if (ACPI_FAILURE(status)) + kfree(dd); + + break; } } + + acpi_scan_lock_release(); return 0; } diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index c5d2fd85dbe0..cbf1f122666b 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -683,8 +683,11 @@ static void acpi_processor_hotplug_notify(acpi_handle handle, struct acpi_device *device = NULL; struct acpi_eject_event *ej_event = NULL; u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */ + acpi_status status; int result; + acpi_scan_lock_acquire(); + switch (event) { case ACPI_NOTIFY_BUS_CHECK: case ACPI_NOTIFY_DEVICE_CHECK: @@ -733,25 +736,32 @@ static void acpi_processor_hotplug_notify(acpi_handle handle, break; } + get_device(&device->dev); ej_event->device = device; ej_event->event = ACPI_NOTIFY_EJECT_REQUEST; - acpi_os_hotplug_execute(acpi_bus_hot_remove_device, - (void *)ej_event); - - /* eject is performed asynchronously */ - return; + /* The eject is carried out asynchronously. */ + status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device, + ej_event); + if (ACPI_FAILURE(status)) { + put_device(&device->dev); + kfree(ej_event); + break; + } + goto out; default: ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unsupported event [0x%x]\n", event)); /* non-hotplug event; possibly handled by other handler */ - return; + goto out; } /* Inform firmware that the hotplug operation has completed */ (void) acpi_evaluate_hotplug_ost(handle, event, ost_code, NULL); - return; + + out: + acpi_scan_lock_release(); } static acpi_status is_processor_device(acpi_handle handle) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index c7676ee8eca4..d16a94ef0baf 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -42,6 +42,18 @@ struct acpi_device_bus_id{ struct list_head node; }; +void acpi_scan_lock_acquire(void) +{ + mutex_lock(&acpi_scan_lock); +} +EXPORT_SYMBOL_GPL(acpi_scan_lock_acquire); + +void acpi_scan_lock_release(void) +{ + mutex_unlock(&acpi_scan_lock); +} +EXPORT_SYMBOL_GPL(acpi_scan_lock_release); + int acpi_scan_add_handler(struct acpi_scan_handler *handler) { if (!handler || !handler->attach) @@ -95,8 +107,6 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha } static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); -static void __acpi_bus_trim(struct acpi_device *start); - /** * acpi_bus_hot_remove_device: hot-remove a device and its children * @context: struct acpi_eject_event pointer (freed in this func) @@ -107,7 +117,7 @@ static void __acpi_bus_trim(struct acpi_device *start); */ void acpi_bus_hot_remove_device(void *context) { - struct acpi_eject_event *ej_event = (struct acpi_eject_event *) context; + struct acpi_eject_event *ej_event = context; struct acpi_device *device = ej_event->device; acpi_handle handle = device->handle; acpi_handle temp; @@ -118,11 +128,19 @@ void acpi_bus_hot_remove_device(void *context) mutex_lock(&acpi_scan_lock); + /* If there is no handle, the device node has been unregistered. */ + if (!device->handle) { + dev_dbg(&device->dev, "ACPI handle missing\n"); + put_device(&device->dev); + goto out; + } + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Hot-removing device %s...\n", dev_name(&device->dev))); - __acpi_bus_trim(device); - /* Device node has been released. */ + acpi_bus_trim(device); + /* Device node has been unregistered. */ + put_device(&device->dev); device = NULL; if (ACPI_SUCCESS(acpi_get_handle(handle, "_LCK", &temp))) { @@ -151,6 +169,7 @@ void acpi_bus_hot_remove_device(void *context) ost_code, NULL); } + out: mutex_unlock(&acpi_scan_lock); kfree(context); return; @@ -212,6 +231,7 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, goto err; } + get_device(&acpi_device->dev); ej_event->device = acpi_device; if (acpi_device->flags.eject_pending) { /* event originated from ACPI eject notification */ @@ -224,7 +244,11 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, ej_event->event, ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); } - acpi_os_hotplug_execute(acpi_bus_hot_remove_device, (void *)ej_event); + status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device, ej_event); + if (ACPI_FAILURE(status)) { + put_device(&acpi_device->dev); + kfree(ej_event); + } err: return ret; } @@ -779,6 +803,7 @@ static void acpi_device_unregister(struct acpi_device *device) * no more references. */ acpi_device_set_power(device, ACPI_STATE_D3_COLD); + device->handle = NULL; put_device(&device->dev); } @@ -1623,14 +1648,14 @@ static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, * there has been a real error. There just have been no suitable ACPI objects * in the table trunk from which the kernel could create a device and add an * appropriate driver. + * + * Must be called under acpi_scan_lock. */ int acpi_bus_scan(acpi_handle handle) { void *device = NULL; int error = 0; - mutex_lock(&acpi_scan_lock); - if (ACPI_SUCCESS(acpi_bus_check_add(handle, 0, NULL, &device))) acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, acpi_bus_check_add, NULL, NULL, &device); @@ -1641,7 +1666,6 @@ int acpi_bus_scan(acpi_handle handle) acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, acpi_bus_device_attach, NULL, NULL, NULL); - mutex_unlock(&acpi_scan_lock); return error; } EXPORT_SYMBOL(acpi_bus_scan); @@ -1678,7 +1702,13 @@ static acpi_status acpi_bus_remove(acpi_handle handle, u32 lvl_not_used, return AE_OK; } -static void __acpi_bus_trim(struct acpi_device *start) +/** + * acpi_bus_trim - Remove ACPI device node and all of its descendants + * @start: Root of the ACPI device nodes subtree to remove. + * + * Must be called under acpi_scan_lock. + */ +void acpi_bus_trim(struct acpi_device *start) { /* * Execute acpi_bus_device_detach() as a post-order callback to detach @@ -1695,13 +1725,6 @@ static void __acpi_bus_trim(struct acpi_device *start) acpi_bus_remove, NULL, NULL); acpi_bus_remove(start->handle, 0, NULL, NULL); } - -void acpi_bus_trim(struct acpi_device *start) -{ - mutex_lock(&acpi_scan_lock); - __acpi_bus_trim(start); - mutex_unlock(&acpi_scan_lock); -} EXPORT_SYMBOL_GPL(acpi_bus_trim); static int acpi_bus_scan_fixed(void) @@ -1758,23 +1781,27 @@ int __init acpi_scan_init(void) acpi_csrt_init(); acpi_container_init(); + mutex_lock(&acpi_scan_lock); /* * Enumerate devices in the ACPI namespace. */ result = acpi_bus_scan(ACPI_ROOT_OBJECT); if (result) - return result; + goto out; result = acpi_bus_get_device(ACPI_ROOT_OBJECT, &acpi_root); if (result) - return result; + goto out; result = acpi_bus_scan_fixed(); if (result) { acpi_device_unregister(acpi_root); - return result; + goto out; } acpi_update_all_gpes(); - return 0; + + out: + mutex_unlock(&acpi_scan_lock); + return result; } diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index d1a6f4a25da8..a951c22921d1 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -1218,6 +1218,8 @@ static void _handle_hotplug_event_bridge(struct work_struct *work) handle = hp_work->handle; type = hp_work->type; + acpi_scan_lock_acquire(); + if (acpi_bus_get_device(handle, &device)) { /* This bridge must have just been physically inserted */ handle_bridge_insertion(handle, type); @@ -1295,6 +1297,7 @@ static void _handle_hotplug_event_bridge(struct work_struct *work) } out: + acpi_scan_lock_release(); kfree(hp_work); /* allocated in handle_hotplug_event_bridge */ } @@ -1341,6 +1344,8 @@ static void _handle_hotplug_event_func(struct work_struct *work) func = (struct acpiphp_func *)context; + acpi_scan_lock_acquire(); + switch (type) { case ACPI_NOTIFY_BUS_CHECK: /* bus re-enumerate */ @@ -1371,6 +1376,7 @@ static void _handle_hotplug_event_func(struct work_struct *work) break; } + acpi_scan_lock_release(); kfree(hp_work); /* allocated in handle_hotplug_event_func */ } diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c index ae606b3e991e..574421bc2fa6 100644 --- a/drivers/pci/hotplug/sgi_hotplug.c +++ b/drivers/pci/hotplug/sgi_hotplug.c @@ -425,6 +425,7 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot) pdevice = NULL; } + acpi_scan_lock_acquire(); /* * Walk the rootbus node's immediate children looking for * the slot's device node(s). There can be more than @@ -458,6 +459,7 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot) } } } + acpi_scan_lock_release(); } /* Call the driver for the new device */ @@ -508,6 +510,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot) /* Get the rootbus node pointer */ phandle = PCI_CONTROLLER(slot->pci_bus)->acpi_handle; + acpi_scan_lock_acquire(); /* * Walk the rootbus node's immediate children looking for * the slot's device node(s). There can be more than @@ -538,7 +541,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot) acpi_bus_trim(device); } } - + acpi_scan_lock_release(); } /* Free the SN resources assigned to the Linux device.*/ diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 41850cb21730..227ba7dc293d 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -395,6 +395,9 @@ int acpi_bus_receive_event(struct acpi_bus_event *event); static inline int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data) { return 0; } #endif + +void acpi_scan_lock_acquire(void); +void acpi_scan_lock_release(void); int acpi_scan_add_handler(struct acpi_scan_handler *handler); int acpi_bus_register_driver(struct acpi_driver *driver); void acpi_bus_unregister_driver(struct acpi_driver *driver); -- cgit v1.2.3 From ab1a2e038ff2336502e95ec6492c0364a9fc70d0 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Sat, 19 Jan 2013 00:07:42 +0800 Subject: ACPI / PCI: Make pci_slot built-in only, not a module As discussed in thread at https://patchwork.kernel.org/patch/1946851/, there's no value in supporting CONFIG_ACPI_PCI_SLOT=m any more. So change Kconfig and code to only support building pci_slot as built-in driver. Signed-off-by: Jiang Liu Signed-off-by: Bjorn Helgaas --- drivers/acpi/Kconfig | 5 +---- drivers/acpi/internal.h | 5 +++++ drivers/acpi/pci_slot.c | 13 +------------ drivers/acpi/scan.c | 1 + 4 files changed, 8 insertions(+), 16 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 38c5078da11d..27fde5e8d31a 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -308,7 +308,7 @@ config ACPI_DEBUG_FUNC_TRACE is about half of the penalty and is rarely useful. config ACPI_PCI_SLOT - tristate "PCI slot detection driver" + bool "PCI slot detection driver" depends on SYSFS default n help @@ -317,9 +317,6 @@ config ACPI_PCI_SLOT i.e., segment/bus/device/function tuples, with physical slots in the system. If you are unsure, say N. - To compile this driver as a module, choose M here: - the module will be called pci_slot. - config X86_PM_TIMER bool "Power Management Timer Support" if EXPERT depends on X86 diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index e050254ae143..7374cfc5917a 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -67,6 +67,11 @@ struct acpi_ec { extern struct acpi_ec *first_ec; +#ifdef CONFIG_ACPI_PCI_SLOT +void acpi_pci_slot_init(void); +#else +static inline void acpi_pci_slot_init(void) { } +#endif int acpi_pci_root_init(void); int acpi_ec_init(void); int acpi_ec_ecdt_probe(void); diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c index d22585f21aeb..a7d7e7710f9e 100644 --- a/drivers/acpi/pci_slot.c +++ b/drivers/acpi/pci_slot.c @@ -330,19 +330,8 @@ static struct dmi_system_id acpi_pci_slot_dmi_table[] __initdata = { {} }; -static int __init -acpi_pci_slot_init(void) +void __init acpi_pci_slot_init(void) { dmi_check_system(acpi_pci_slot_dmi_table); acpi_pci_register_driver(&acpi_pci_slot_driver); - return 0; } - -static void __exit -acpi_pci_slot_exit(void) -{ - acpi_pci_unregister_driver(&acpi_pci_slot_driver); -} - -module_init(acpi_pci_slot_init); -module_exit(acpi_pci_slot_exit); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 7c43bdc36abc..236e476b09c9 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1687,6 +1687,7 @@ int __init acpi_scan_init(void) acpi_power_init(); acpi_pci_root_init(); + acpi_pci_slot_init(); /* * Enumerate devices in the ACPI namespace. -- cgit v1.2.3 From b5d667eb392ed901fc7ae76869c7a130559e193c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 23 Feb 2013 23:15:21 +0100 Subject: ACPI / PM: Take unusual configurations of power resources into account Commit d2e5f0c (ACPI / PCI: Rework the setup and cleanup of device wakeup) moved the initial disabling of system wakeup for PCI devices into a place where it can actually work and that exposed a hidden old issue with crap^Wunusual system designs where the same power resources are used for both wakeup power and device power control at run time. Namely, say there is one power resource such that the ACPI power state D0 of a PCI device depends on that power resource (i.e. the device is in D0 when that power resource is "on") and it is used as a wakeup power resource for the same device. Then, calling acpi_pci_sleep_wake(pci_dev, false) for the device in question will cause the reference counter of that power resource to drop to 0, which in turn will cause it to be turned off. As a result, the device will go into D3cold at that point, although it should have stayed in D0. As it turns out, that happens to USB controllers on some laptops and USB becomes unusable on those machines as a result, which is a major regression from v3.8. To fix this problem, (1) increment the reference counters of wakup power resources during their initialization if they are "on" initially, (2) prevent acpi_disable_wakeup_device_power() from decrementing the reference counters of wakeup power resources that were not enabled for wakeup power previously, and (3) prevent acpi_enable_wakeup_device_power() from incrementing the reference counters of wakeup power resources that already are enabled for wakeup power. In addition to that, if it is impossible to determine the initial states of wakeup power resources, avoid enabling wakeup for devices whose wakeup power depends on those power resources. Reported-by: Dave Jones Reported-by: Fabio Baltieri Tested-by: Fabio Baltieri Signed-off-by: Rafael J. Wysocki --- drivers/acpi/internal.h | 2 +- drivers/acpi/power.c | 112 ++++++++++++++++++++++++++++++++++++------------ drivers/acpi/scan.c | 9 +++- 3 files changed, 94 insertions(+), 29 deletions(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index c5a61cd6c1a5..6306d2ecb428 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -56,7 +56,7 @@ int acpi_extract_power_resources(union acpi_object *package, unsigned int start, struct list_head *list); int acpi_add_power_resource(acpi_handle handle); void acpi_power_add_remove_device(struct acpi_device *adev, bool add); -int acpi_power_min_system_level(struct list_head *list); +int acpi_power_wakeup_list_init(struct list_head *list, int *system_level); int acpi_device_sleep_wake(struct acpi_device *dev, int enable, int sleep_state, int dev_state); int acpi_power_get_inferred_state(struct acpi_device *device, int *state); diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index b820528a5fa3..34f5ef11d427 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -73,6 +73,7 @@ struct acpi_power_resource { u32 system_level; u32 order; unsigned int ref_count; + bool wakeup_enabled; struct mutex resource_lock; }; @@ -272,11 +273,9 @@ static int __acpi_power_on(struct acpi_power_resource *resource) return 0; } -static int acpi_power_on(struct acpi_power_resource *resource) +static int acpi_power_on_unlocked(struct acpi_power_resource *resource) { - int result = 0;; - - mutex_lock(&resource->resource_lock); + int result = 0; if (resource->ref_count++) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, @@ -293,9 +292,16 @@ static int acpi_power_on(struct acpi_power_resource *resource) schedule_work(&dep->work); } } + return result; +} - mutex_unlock(&resource->resource_lock); +static int acpi_power_on(struct acpi_power_resource *resource) +{ + int result; + mutex_lock(&resource->resource_lock); + result = acpi_power_on_unlocked(resource); + mutex_unlock(&resource->resource_lock); return result; } @@ -313,17 +319,15 @@ static int __acpi_power_off(struct acpi_power_resource *resource) return 0; } -static int acpi_power_off(struct acpi_power_resource *resource) +static int acpi_power_off_unlocked(struct acpi_power_resource *resource) { int result = 0; - mutex_lock(&resource->resource_lock); - if (!resource->ref_count) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] already off", resource->name)); - goto unlock; + return 0; } if (--resource->ref_count) { @@ -335,10 +339,16 @@ static int acpi_power_off(struct acpi_power_resource *resource) if (result) resource->ref_count++; } + return result; +} - unlock: - mutex_unlock(&resource->resource_lock); +static int acpi_power_off(struct acpi_power_resource *resource) +{ + int result; + mutex_lock(&resource->resource_lock); + result = acpi_power_off_unlocked(resource); + mutex_unlock(&resource->resource_lock); return result; } @@ -521,18 +531,35 @@ void acpi_power_add_remove_device(struct acpi_device *adev, bool add) } } -int acpi_power_min_system_level(struct list_head *list) +int acpi_power_wakeup_list_init(struct list_head *list, int *system_level_p) { struct acpi_power_resource_entry *entry; int system_level = 5; list_for_each_entry(entry, list, node) { struct acpi_power_resource *resource = entry->resource; + acpi_handle handle = resource->device.handle; + int result; + int state; + mutex_lock(&resource->resource_lock); + + result = acpi_power_get_state(handle, &state); + if (result) { + mutex_unlock(&resource->resource_lock); + return result; + } + if (state == ACPI_POWER_RESOURCE_STATE_ON) { + resource->ref_count++; + resource->wakeup_enabled = true; + } if (system_level > resource->system_level) system_level = resource->system_level; + + mutex_unlock(&resource->resource_lock); } - return system_level; + *system_level_p = system_level; + return 0; } /* -------------------------------------------------------------------------- @@ -610,6 +637,7 @@ int acpi_device_sleep_wake(struct acpi_device *dev, */ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state) { + struct acpi_power_resource_entry *entry; int err = 0; if (!dev || !dev->wakeup.flags.valid) @@ -620,17 +648,31 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state) if (dev->wakeup.prepare_count++) goto out; - err = acpi_power_on_list(&dev->wakeup.resources); - if (err) { - dev_err(&dev->dev, "Cannot turn wakeup power resources on\n"); - dev->wakeup.flags.valid = 0; - } else { - /* - * Passing 3 as the third argument below means the device may be - * put into arbitrary power state afterward. - */ - err = acpi_device_sleep_wake(dev, 1, sleep_state, 3); + list_for_each_entry(entry, &dev->wakeup.resources, node) { + struct acpi_power_resource *resource = entry->resource; + + mutex_lock(&resource->resource_lock); + + if (!resource->wakeup_enabled) { + err = acpi_power_on_unlocked(resource); + if (!err) + resource->wakeup_enabled = true; + } + + mutex_unlock(&resource->resource_lock); + + if (err) { + dev_err(&dev->dev, + "Cannot turn wakeup power resources on\n"); + dev->wakeup.flags.valid = 0; + goto out; + } } + /* + * Passing 3 as the third argument below means the device may be + * put into arbitrary power state afterward. + */ + err = acpi_device_sleep_wake(dev, 1, sleep_state, 3); if (err) dev->wakeup.prepare_count = 0; @@ -647,6 +689,7 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state) */ int acpi_disable_wakeup_device_power(struct acpi_device *dev) { + struct acpi_power_resource_entry *entry; int err = 0; if (!dev || !dev->wakeup.flags.valid) @@ -668,10 +711,25 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev) if (err) goto out; - err = acpi_power_off_list(&dev->wakeup.resources); - if (err) { - dev_err(&dev->dev, "Cannot turn wakeup power resources off\n"); - dev->wakeup.flags.valid = 0; + list_for_each_entry(entry, &dev->wakeup.resources, node) { + struct acpi_power_resource *resource = entry->resource; + + mutex_lock(&resource->resource_lock); + + if (resource->wakeup_enabled) { + err = acpi_power_off_unlocked(resource); + if (!err) + resource->wakeup_enabled = false; + } + + mutex_unlock(&resource->resource_lock); + + if (err) { + dev_err(&dev->dev, + "Cannot turn wakeup power resources off\n"); + dev->wakeup.flags.valid = 0; + break; + } } out: diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index f75f25c2e455..560b05566f3b 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1004,7 +1004,14 @@ static int acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, if (!list_empty(&wakeup->resources)) { int sleep_state; - sleep_state = acpi_power_min_system_level(&wakeup->resources); + err = acpi_power_wakeup_list_init(&wakeup->resources, + &sleep_state); + if (err) { + acpi_handle_warn(handle, "Retrieving current states " + "of wakeup power resources failed\n"); + acpi_power_resources_list_free(&wakeup->resources); + goto out; + } if (sleep_state < wakeup->sleep_state) { acpi_handle_warn(handle, "Overriding _PRW sleep state " "(S%d) by S%d from power resources\n", -- cgit v1.2.3