diff options
55 files changed, 535 insertions, 423 deletions
diff --git a/Documentation/acpi/enumeration.txt b/Documentation/acpi/enumeration.txt index 54469bc81b1..94a65613188 100644 --- a/Documentation/acpi/enumeration.txt +++ b/Documentation/acpi/enumeration.txt @@ -63,8 +63,8 @@ from ACPI tables. Currently the kernel is not able to automatically determine from which ACPI device it should make the corresponding platform device so we need to add the ACPI device explicitly to acpi_platform_device_ids list defined in -drivers/acpi/scan.c. This limitation is only for the platform devices, SPI -and I2C devices are created automatically as described below. +drivers/acpi/acpi_platform.c. This limitation is only for the platform +devices, SPI and I2C devices are created automatically as described below. SPI serial bus support ~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Documentation/acpi/scan_handlers.txt b/Documentation/acpi/scan_handlers.txt new file mode 100644 index 00000000000..3246ccf1599 --- /dev/null +++ b/Documentation/acpi/scan_handlers.txt @@ -0,0 +1,77 @@ +ACPI Scan Handlers + +Copyright (C) 2012, Intel Corporation +Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> + +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/arch/ia64/hp/common/aml_nfw.c b/arch/ia64/hp/common/aml_nfw.c index 6192f718865..916ffe770bc 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 2fdca25905a..fef7d0ba7e3 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/Kconfig b/drivers/acpi/Kconfig index 38c5078da11..78105b3a526 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/ac.c b/drivers/acpi/ac.c index d5fdd36190c..6d5bf649196 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 03d18f29011..034d3e72aa9 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}, @@ -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) @@ -415,7 +433,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 16fa979f718..31de1043eea 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/acpi_platform.c b/drivers/acpi/acpi_platform.c index 2d1fb4c2160..26fce4b8a63 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/battery.c b/drivers/acpi/battery.c index 7efaeaa53b8..c5cd5b5513e 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 f0d936b65e3..86c7d5445c3 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 cc79d3e53a3..5523ba7d764 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -34,46 +34,34 @@ #include <linux/acpi.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> -#include <acpi/container.h> #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, int type); - 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 +80,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, int type) -{ - 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; @@ -143,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 */ @@ -158,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; } @@ -185,98 +132,59 @@ 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 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; + bool ret = false; - status = acpi_get_object_info(handle, &info); - if (ACPI_FAILURE(status)) { - return AE_OK; - } - - if (info->valid & ACPI_VALID_HID) - hid = info->hardware_id.string; - - if (hid == NULL) { - goto end; - } + if (ACPI_FAILURE(a |