diff options
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/Kconfig | 7 | ||||
-rw-r--r-- | drivers/acpi/Makefile | 1 | ||||
-rw-r--r-- | drivers/acpi/acpi_i2c.c | 2 | ||||
-rw-r--r-- | drivers/acpi/acpi_lpss.c | 292 | ||||
-rw-r--r-- | drivers/acpi/acpi_memhotplug.c | 328 | ||||
-rw-r--r-- | drivers/acpi/acpi_platform.c | 40 | ||||
-rw-r--r-- | drivers/acpi/apei/cper.c | 2 | ||||
-rw-r--r-- | drivers/acpi/container.c | 150 | ||||
-rw-r--r-- | drivers/acpi/internal.h | 21 | ||||
-rw-r--r-- | drivers/acpi/pci_root.c | 85 | ||||
-rw-r--r-- | drivers/acpi/power.c | 2 | ||||
-rw-r--r-- | drivers/acpi/processor_idle.c | 13 | ||||
-rw-r--r-- | drivers/acpi/processor_perflib.c | 4 | ||||
-rw-r--r-- | drivers/acpi/scan.c | 531 | ||||
-rw-r--r-- | drivers/acpi/sleep.c | 8 | ||||
-rw-r--r-- | drivers/acpi/sysfs.c | 66 | ||||
-rw-r--r-- | drivers/acpi/video_detect.c | 25 |
17 files changed, 906 insertions, 671 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 92ed9692c47e..8fcae6df0a42 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -334,7 +334,7 @@ config X86_PM_TIMER config ACPI_CONTAINER bool "Container and Module Devices" - default (ACPI_HOTPLUG_MEMORY || ACPI_HOTPLUG_CPU || ACPI_HOTPLUG_IO) + default (ACPI_HOTPLUG_MEMORY || ACPI_HOTPLUG_CPU) help This driver supports ACPI Container and Module devices (IDs ACPI0004, PNP0A05, and PNP0A06). @@ -345,9 +345,8 @@ config ACPI_CONTAINER the module will be called container. config ACPI_HOTPLUG_MEMORY - tristate "Memory Hotplug" + bool "Memory Hotplug" depends on MEMORY_HOTPLUG - default n help This driver supports ACPI memory hotplug. The driver fields notifications on ACPI memory devices (PNP0C80), @@ -396,7 +395,7 @@ config ACPI_CUSTOM_METHOD config ACPI_BGRT bool "Boottime Graphics Resource Table support" - depends on EFI + depends on EFI && X86 help This driver adds support for exposing the ACPI Boottime Graphics Resource Table, which allows the operating system to obtain diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 474fcfeba66c..ecb743bf05a5 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -39,6 +39,7 @@ 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-$(CONFIG_X86_INTEL_LPSS) += acpi_lpss.o acpi-y += acpi_platform.o acpi-y += power.o acpi-y += event.o diff --git a/drivers/acpi/acpi_i2c.c b/drivers/acpi/acpi_i2c.c index 82045e3f5cac..a82c7626aa9b 100644 --- a/drivers/acpi/acpi_i2c.c +++ b/drivers/acpi/acpi_i2c.c @@ -90,7 +90,7 @@ void acpi_i2c_register_devices(struct i2c_adapter *adapter) acpi_handle handle; acpi_status status; - handle = ACPI_HANDLE(&adapter->dev); + handle = ACPI_HANDLE(adapter->dev.parent); if (!handle) return; diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c new file mode 100644 index 000000000000..b1c95422ce74 --- /dev/null +++ b/drivers/acpi/acpi_lpss.c @@ -0,0 +1,292 @@ +/* + * ACPI support for Intel Lynxpoint LPSS. + * + * Copyright (C) 2013, Intel Corporation + * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> + * Rafael J. Wysocki <rafael.j.wysocki@intel.com> + * + * 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. + */ + +#include <linux/acpi.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/platform_data/clk-lpss.h> +#include <linux/pm_runtime.h> + +#include "internal.h" + +ACPI_MODULE_NAME("acpi_lpss"); + +#define LPSS_CLK_SIZE 0x04 +#define LPSS_LTR_SIZE 0x18 + +/* Offsets relative to LPSS_PRIVATE_OFFSET */ +#define LPSS_GENERAL 0x08 +#define LPSS_GENERAL_LTR_MODE_SW BIT(2) +#define LPSS_SW_LTR 0x10 +#define LPSS_AUTO_LTR 0x14 + +struct lpss_device_desc { + bool clk_required; + const char *clk_parent; + bool ltr_required; + unsigned int prv_offset; +}; + +struct lpss_private_data { + void __iomem *mmio_base; + resource_size_t mmio_size; + struct clk *clk; + const struct lpss_device_desc *dev_desc; +}; + +static struct lpss_device_desc lpt_dev_desc = { + .clk_required = true, + .clk_parent = "lpss_clk", + .prv_offset = 0x800, + .ltr_required = true, +}; + +static struct lpss_device_desc lpt_sdio_dev_desc = { + .prv_offset = 0x1000, + .ltr_required = true, +}; + +static const struct acpi_device_id acpi_lpss_device_ids[] = { + /* Lynxpoint LPSS devices */ + { "INT33C0", (unsigned long)&lpt_dev_desc }, + { "INT33C1", (unsigned long)&lpt_dev_desc }, + { "INT33C2", (unsigned long)&lpt_dev_desc }, + { "INT33C3", (unsigned long)&lpt_dev_desc }, + { "INT33C4", (unsigned long)&lpt_dev_desc }, + { "INT33C5", (unsigned long)&lpt_dev_desc }, + { "INT33C6", (unsigned long)&lpt_sdio_dev_desc }, + { "INT33C7", }, + + { } +}; + +static int is_memory(struct acpi_resource *res, void *not_used) +{ + struct resource r; + return !acpi_dev_resource_memory(res, &r); +} + +/* LPSS main clock device. */ +static struct platform_device *lpss_clk_dev; + +static inline void lpt_register_clock_device(void) +{ + lpss_clk_dev = platform_device_register_simple("clk-lpt", -1, NULL, 0); +} + +static int register_device_clock(struct acpi_device *adev, + struct lpss_private_data *pdata) +{ + const struct lpss_device_desc *dev_desc = pdata->dev_desc; + + if (!lpss_clk_dev) + lpt_register_clock_device(); + + if (!dev_desc->clk_parent || !pdata->mmio_base + || pdata->mmio_size < dev_desc->prv_offset + LPSS_CLK_SIZE) + return -ENODATA; + + pdata->clk = clk_register_gate(NULL, dev_name(&adev->dev), + dev_desc->clk_parent, 0, + pdata->mmio_base + dev_desc->prv_offset, + 0, 0, NULL); + if (IS_ERR(pdata->clk)) + return PTR_ERR(pdata->clk); + + clk_register_clkdev(pdata->clk, NULL, dev_name(&adev->dev)); + return 0; +} + +static int acpi_lpss_create_device(struct acpi_device *adev, + const struct acpi_device_id *id) +{ + struct lpss_device_desc *dev_desc; + struct lpss_private_data *pdata; + struct resource_list_entry *rentry; + struct list_head resource_list; + int ret; + + dev_desc = (struct lpss_device_desc *)id->driver_data; + if (!dev_desc) + return acpi_create_platform_device(adev, id); + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + INIT_LIST_HEAD(&resource_list); + ret = acpi_dev_get_resources(adev, &resource_list, is_memory, NULL); + if (ret < 0) + goto err_out; + + list_for_each_entry(rentry, &resource_list, node) + if (resource_type(&rentry->res) == IORESOURCE_MEM) { + pdata->mmio_size = resource_size(&rentry->res); + pdata->mmio_base = ioremap(rentry->res.start, + pdata->mmio_size); + pdata->dev_desc = dev_desc; + break; + } + + acpi_dev_free_resource_list(&resource_list); + + if (dev_desc->clk_required) { + ret = register_device_clock(adev, pdata); + if (ret) { + /* + * Skip the device, but don't terminate the namespace + * scan. + */ + kfree(pdata); + return 0; + } + } + + adev->driver_data = pdata; + ret = acpi_create_platform_device(adev, id); + if (ret > 0) + return ret; + + adev->driver_data = NULL; + + err_out: + kfree(pdata); + return ret; +} + +static int lpss_reg_read(struct device *dev, unsigned int reg, u32 *val) +{ + struct acpi_device *adev; + struct lpss_private_data *pdata; + unsigned long flags; + int ret; + + ret = acpi_bus_get_device(ACPI_HANDLE(dev), &adev); + if (WARN_ON(ret)) + return ret; + + spin_lock_irqsave(&dev->power.lock, flags); + if (pm_runtime_suspended(dev)) { + ret = -EAGAIN; + goto out; + } + pdata = acpi_driver_data(adev); + if (WARN_ON(!pdata || !pdata->mmio_base)) { + ret = -ENODEV; + goto out; + } + *val = readl(pdata->mmio_base + pdata->dev_desc->prv_offset + reg); + + out: + spin_unlock_irqrestore(&dev->power.lock, flags); + return ret; +} + +static ssize_t lpss_ltr_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + u32 ltr_value = 0; + unsigned int reg; + int ret; + + reg = strcmp(attr->attr.name, "auto_ltr") ? LPSS_SW_LTR : LPSS_AUTO_LTR; + ret = lpss_reg_read(dev, reg, <r_value); + if (ret) + return ret; + + return snprintf(buf, PAGE_SIZE, "%08x\n", ltr_value); +} + +static ssize_t lpss_ltr_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 ltr_mode = 0; + char *outstr; + int ret; + + ret = lpss_reg_read(dev, LPSS_GENERAL, <r_mode); + if (ret) + return ret; + + outstr = (ltr_mode & LPSS_GENERAL_LTR_MODE_SW) ? "sw" : "auto"; + return sprintf(buf, "%s\n", outstr); +} + +static DEVICE_ATTR(auto_ltr, S_IRUSR, lpss_ltr_show, NULL); +static DEVICE_ATTR(sw_ltr, S_IRUSR, lpss_ltr_show, NULL); +static DEVICE_ATTR(ltr_mode, S_IRUSR, lpss_ltr_mode_show, NULL); + +static struct attribute *lpss_attrs[] = { + &dev_attr_auto_ltr.attr, + &dev_attr_sw_ltr.attr, + &dev_attr_ltr_mode.attr, + NULL, +}; + +static struct attribute_group lpss_attr_group = { + .attrs = lpss_attrs, + .name = "lpss_ltr", +}; + +static int acpi_lpss_platform_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct platform_device *pdev = to_platform_device(data); + struct lpss_private_data *pdata; + struct acpi_device *adev; + const struct acpi_device_id *id; + int ret = 0; + + id = acpi_match_device(acpi_lpss_device_ids, &pdev->dev); + if (!id || !id->driver_data) + return 0; + + if (acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev)) + return 0; + + pdata = acpi_driver_data(adev); + if (!pdata || !pdata->mmio_base || !pdata->dev_desc->ltr_required) + return 0; + + if (pdata->mmio_size < pdata->dev_desc->prv_offset + LPSS_LTR_SIZE) { + dev_err(&pdev->dev, "MMIO size insufficient to access LTR\n"); + return 0; + } + + if (action == BUS_NOTIFY_ADD_DEVICE) + ret = sysfs_create_group(&pdev->dev.kobj, &lpss_attr_group); + else if (action == BUS_NOTIFY_DEL_DEVICE) + sysfs_remove_group(&pdev->dev.kobj, &lpss_attr_group); + + return ret; +} + +static struct notifier_block acpi_lpss_nb = { + .notifier_call = acpi_lpss_platform_notify, +}; + +static struct acpi_scan_handler lpss_handler = { + .ids = acpi_lpss_device_ids, + .attach = acpi_lpss_create_device, +}; + +void __init acpi_lpss_init(void) +{ + if (!lpt_clk_init()) { + bus_register_notifier(&platform_bus_type, &acpi_lpss_nb); + acpi_scan_add_handler(&lpss_handler); + } +} diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index da1f82b445e0..5e6301e94920 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c @@ -1,5 +1,7 @@ /* - * Copyright (C) 2004 Intel Corporation <naveen.b.s@intel.com> + * Copyright (C) 2004, 2013 Intel Corporation + * Author: Naveen B S <naveen.b.s@intel.com> + * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> * * All rights reserved. * @@ -25,14 +27,10 @@ * ranges. */ -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/types.h> -#include <linux/memory_hotplug.h> -#include <linux/slab.h> #include <linux/acpi.h> -#include <acpi/acpi_drivers.h> +#include <linux/memory_hotplug.h> + +#include "internal.h" #define ACPI_MEMORY_DEVICE_CLASS "memory" #define ACPI_MEMORY_DEVICE_HID "PNP0C80" @@ -44,32 +42,28 @@ #define PREFIX "ACPI:memory_hp:" ACPI_MODULE_NAME("acpi_memhotplug"); -MODULE_AUTHOR("Naveen B S <naveen.b.s@intel.com>"); -MODULE_DESCRIPTION("Hotplug Mem Driver"); -MODULE_LICENSE("GPL"); /* Memory Device States */ #define MEMORY_INVALID_STATE 0 #define MEMORY_POWER_ON_STATE 1 #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); +static int acpi_memory_device_add(struct acpi_device *device, + const struct acpi_device_id *not_used); +static void acpi_memory_device_remove(struct acpi_device *device); static const struct acpi_device_id memory_device_ids[] = { {ACPI_MEMORY_DEVICE_HID, 0}, {"", 0}, }; -MODULE_DEVICE_TABLE(acpi, memory_device_ids); -static struct acpi_driver acpi_memory_device_driver = { - .name = "acpi_memhotplug", - .class = ACPI_MEMORY_DEVICE_CLASS, +static struct acpi_scan_handler memory_device_handler = { .ids = memory_device_ids, - .ops = { - .add = acpi_memory_device_add, - .remove = acpi_memory_device_remove, - }, + .attach = acpi_memory_device_add, + .detach = acpi_memory_device_remove, + .hotplug = { + .enabled = true, + }, }; struct acpi_memory_info { @@ -79,7 +73,6 @@ struct acpi_memory_info { unsigned short caching; /* memory cache attribute */ unsigned short write_protect; /* memory read/write attribute */ unsigned int enabled:1; - unsigned int failed:1; }; struct acpi_memory_device { @@ -153,48 +146,6 @@ 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) -{ - struct acpi_device *device = NULL; - int result = 0; - - acpi_scan_lock_acquire(); - - acpi_bus_get_device(handle, &device); - if (device) - goto end; - - /* - * Now add the notified device. This creates the acpi_device - * and invokes .add function - */ - result = acpi_bus_scan(handle); - if (result) { - 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"); - result = -EINVAL; - goto out; - } - - end: - *mem_device = acpi_driver_data(device); - if (!(*mem_device)) { - dev_err(&device->dev, "driver data not found\n"); - result = -ENODEV; - goto out; - } - - out: - acpi_scan_lock_release(); - return result; -} - static int acpi_memory_check_device(struct acpi_memory_device *mem_device) { unsigned long long current_status; @@ -249,13 +200,11 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) * returns -EEXIST. If add_memory() returns the other error, it * means that this memory block is not used by the kernel. */ - if (result && result != -EEXIST) { - info->failed = 1; + if (result && result != -EEXIST) continue; - } - if (!result) - info->enabled = 1; + info->enabled = 1; + /* * Add num_enable even if add_memory() returns -EEXIST, so the * device is bound to this driver. @@ -286,16 +235,8 @@ static int acpi_memory_remove_memory(struct acpi_memory_device *mem_device) nid = acpi_get_node(mem_device->device->handle); list_for_each_entry_safe(info, n, &mem_device->res_list, list) { - if (info->failed) - /* The kernel does not use this memory block */ - continue; - if (!info->enabled) - /* - * The kernel uses this memory block, but it may be not - * managed by us. - */ - return -EBUSY; + continue; if (nid < 0) nid = memory_add_physaddr_to_nid(info->start_addr); @@ -310,95 +251,21 @@ static int acpi_memory_remove_memory(struct acpi_memory_device *mem_device) return result; } -static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data) -{ - struct acpi_memory_device *mem_device; - 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: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "\nReceived BUS CHECK notification for device\n")); - /* Fall Through */ - case ACPI_NOTIFY_DEVICE_CHECK: - if (event == ACPI_NOTIFY_DEVICE_CHECK) - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "\nReceived DEVICE CHECK notification for device\n")); - if (acpi_memory_get_device(handle, &mem_device)) { - acpi_handle_err(handle, "Cannot find driver data\n"); - break; - } - - ost_code = ACPI_OST_SC_SUCCESS; - break; - - case ACPI_NOTIFY_EJECT_REQUEST: - 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"); - goto unlock; - } - mem_device = acpi_driver_data(device); - if (!mem_device) { - acpi_handle_err(handle, "Driver Data is NULL\n"); - goto unlock; - } - - ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL); - if (!ej_event) { - pr_err(PREFIX "No memory, dropping EJECT\n"); - goto unlock; - } - - get_device(&device->dev); - ej_event->device = device; - ej_event->event = ACPI_NOTIFY_EJECT_REQUEST; - /* 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); - } - - unlock: - acpi_scan_lock_release(); - if (ACPI_SUCCESS(status)) - return; - default: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Unsupported event [0x%x]\n", event)); - - /* non-hotplug event; possibly handled by other handler */ - return; - } - - /* Inform firmware that the hotplug operation has completed */ - (void) acpi_evaluate_hotplug_ost(handle, event, ost_code, NULL); -} - static void acpi_memory_device_free(struct acpi_memory_device *mem_device) { if (!mem_device) return; acpi_memory_free_device_resources(mem_device); + mem_device->device->driver_data = NULL; kfree(mem_device); } -static int acpi_memory_device_add(struct acpi_device *device) +static int acpi_memory_device_add(struct acpi_device *device, + const struct acpi_device_id *not_used) { + struct acpi_memory_device *mem_device; int result; - struct acpi_memory_device *mem_device = NULL; - if (!device) return -EINVAL; @@ -423,147 +290,36 @@ static int acpi_memory_device_add(struct acpi_device *device) /* Set the device state */ mem_device->state = MEMORY_POWER_ON_STATE; - pr_debug("%s\n", acpi_device_name(device)); + result = acpi_memory_check_device(mem_device); + if (result) { + acpi_memory_device_free(mem_device); + return 0; + } - if (!acpi_memory_check_device(mem_device)) { - /* call add_memory func */ - result = acpi_memory_enable_device(mem_device); - if (result) { - dev_err(&device->dev, - "Error in acpi_memory_enable_device\n"); - acpi_memory_device_free(mem_device); - } + result = acpi_memory_enable_device(mem_device); + if (result) { + dev_err(&device->dev, "acpi_memory_enable_device() error\n"); + acpi_memory_device_free(mem_device); + return -ENODEV; } - return result; + + dev_dbg(&device->dev, "Memory device configured by ACPI\n"); + return 1; } -static int acpi_memory_device_remove(struct acpi_device *device) +static void acpi_memory_device_remove(struct acpi_device *device) { - struct acpi_memory_device *mem_device = NULL; - int result; + struct acpi_memory_device *mem_device; if (!device || !acpi_driver_data(device)) - return -EINVAL; + return; mem_device = acpi_driver_data(device); - - result = acpi_memory_remove_memory(mem_device); - if (result) - return result; - + acpi_memory_remove_memory(mem_device); acpi_memory_device_free(mem_device); - - return 0; -} - -/* - * Helper function to check for memory device - */ -static acpi_status is_memory_device(acpi_handle handle) -{ - char *hardware_id; - acpi_status status; - struct acpi_device_info *info; - - status = acpi_get_object_info(handle, &info); - if (ACPI_FAILURE(status)) - return status; - - if (!(info->valid & ACPI_VALID_HID)) { - kfree(info); - return AE_ERROR; - } - - hardware_id = info->hardware_id.string; - if ((hardware_id == NULL) || - (strcmp(hardware_id, ACPI_MEMORY_DEVICE_HID))) - status = AE_ERROR; - - kfree(info); - return status; -} - -static acpi_status -acpi_memory_register_notify_handler(acpi_handle handle, - u32 level, void *ctxt, void **retv) -{ - acpi_status status; - - - status = is_memory_device(handle); - if (ACPI_FAILURE(status)) - return AE_OK; /* continue */ - - status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, - acpi_memory_device_notify, NULL); - /* continue */ - return AE_OK; -} - -static acpi_status -acpi_memory_deregister_notify_handler(acpi_handle handle, - u32 level, void *ctxt, void **retv) -{ - acpi_status status; - - - status = is_memory_device(handle); - if (ACPI_FAILURE(status)) - return AE_OK; /* continue */ - - status = acpi_remove_notify_handler(handle, - ACPI_SYSTEM_NOTIFY, - acpi_memory_device_notify); - - return AE_OK; /* continue */ -} - -static int __init acpi_memory_device_init(void) -{ - int result; - acpi_status status; - - - result = acpi_bus_register_driver(&acpi_memory_device_driver); - - if (result < 0) - return -ENODEV; - - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, - acpi_memory_register_notify_handler, NULL, - NULL, NULL); - - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "walk_namespace failed")); - acpi_bus_unregister_driver(&acpi_memory_device_driver); - return -ENODEV; - } - - return 0; } -static void __exit acpi_memory_device_exit(void) +void __init acpi_memory_hotplug_init(void) { - acpi_status status; - - - /* - * Adding this to un-install notification handlers for all the device - * handles. - */ - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, - acpi_memory_deregister_notify_handler, NULL, - NULL, NULL); - - if (ACPI_FAILURE(status)) - ACPI_EXCEPTION((AE_INFO, status, "walk_namespace failed")); - - acpi_bus_unregister_driver(&acpi_memory_device_driver); - - return; + acpi_scan_add_handler_with_hotplug(&memory_device_handler, "memory"); } - -module_init(acpi_memory_device_init); -module_exit(acpi_memory_device_exit); diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index 26fce4b8a632..fafec5ddf17f 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -22,9 +22,6 @@ 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. @@ -33,33 +30,9 @@ 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; - - /* 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. @@ -71,10 +44,9 @@ static int acpi_create_platform_clks(struct acpi_device *adev) * * Name of the platform device will be the same as @adev's. */ -static int acpi_create_platform_device(struct acpi_device *adev, - const struct acpi_device_id *id) +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; @@ -83,14 +55,6 @@ static int acpi_create_platform_device(struct acpi_device *adev, struct resource *resources; int count; - 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 0; diff --git a/drivers/acpi/apei/cper.c b/drivers/acpi/apei/cper.c index 1e5d8a40101e..fefc2ca7cc3e 100644 --- a/drivers/acpi/apei/cper.c +++ b/drivers/acpi/apei/cper.c @@ -405,7 +405,7 @@ int apei_estatus_check(const struct acpi_hest_generic_status *estatus) return rc; data_len = estatus->data_length; gdata = (struct acpi_hest_generic_data *)(estatus + 1); - while (data_len > sizeof(*gdata)) { + while (data_len >= sizeof(*gdata)) { gedata_len = gdata->error_data_length; if (gedata_len > data_len - sizeof(*gdata)) return -EINVAL; diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index 5523ba7d764d..f9f8a08827fa 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -1,12 +1,12 @@ /* - * acpi_container.c - ACPI Generic Container Driver - * ($Revision: ) + * container.c - ACPI Generic Container Driver * * Copyright (C) 2004 Anil S Keshavamurthy (anil.s.keshavamurthy@intel.com) * Copyright (C) 2004 Keiichiro Tokunaga (tokunaga.keiich@jp.fujitsu.com) * Copyright (C) 2004 Motoyuki Ito (motoyuki@soft.fujitsu.com) - * Copyright (C) 2004 Intel Corp. * Copyright (C) 2004 FUJITSU LIMITED + * Copyright (C) 2004, 2013 Intel Corp. + * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -26,14 +26,9 @@ * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/types.h> #include <linux/acpi.h> -#include <acpi/acpi_bus.h> -#include <acpi/acpi_drivers.h> + +#include "internal.h" #define PREFIX "ACPI: " @@ -50,141 +45,20 @@ static const struct acpi_device_id container_device_ids[] = { 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. - */ + /* This is necessary for container hotplug to work. */ return 1; } -static struct acpi_scan_handler container_device_handler = { +static struct acpi_scan_handler container_handler = { .ids = container_device_ids, .attach = container_device_attach, + .hotplug = { + .enabled = true, + .mode = AHM_CONTAINER, + }, }; -static int is_device_present(acpi_handle handle) -{ - acpi_handle temp; - acpi_status status; - unsigned long long sta; - - - status = acpi_get_handle(handle, "_STA", &temp); - if (ACPI_FAILURE(status)) - return 1; /* _STA not found, assume device present */ - - status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); - if (ACPI_FAILURE(status)) - return 0; /* Firmware error */ - - return ((sta & ACPI_STA_DEVICE_PRESENT) == ACPI_STA_DEVICE_PRESENT); -} - -static void container_notify_cb(acpi_handle handle, u32 type, void *context) -{ - struct acpi_device *device = NULL; - int result; - int present; - 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 */ - case ACPI_NOTIFY_DEVICE_CHECK: - pr_debug("Container driver received %s event\n", - (type == ACPI_NOTIFY_BUS_CHECK) ? - "ACPI_NOTIFY_BUS_CHECK" : "ACPI_NOTIFY_DEVICE_CHECK"); - - present = is_device_present(handle); - status = acpi_bus_get_device(handle, &device); - if (!present) { - if (ACPI_SUCCESS(status)) { - /* device exist and this is a remove request */ - device->flags.eject_pending = 1; - kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); - goto out; - } - break; - } - - if (!ACPI_FAILURE(status) || device) - break; - - result = acpi_bus_scan(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; - break; - - case ACPI_NOTIFY_EJECT_REQUEST: - if (!acpi_bus_get_device(handle, &device) && device) { - device->flags.eject_pending = 1; - kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); - goto out; - } - break; - - default: - /* non-hotplug event; possibly handled by other handler */ - goto out; - } - - /* Inform firmware that the hotplug operation has completed */ - (void) acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL); - - out: - acpi_scan_lock_release(); -} - -static bool is_container(acpi_handle handle) -{ - struct acpi_device_info *info; - bool ret = false; - - if (ACPI_FAILURE(acpi_get_object_info(handle, &info))) - return false; - - if (info->valid & ACPI_VALID_HID) { - const struct acpi_device_id *id; - - for (id = container_device_ids; id->id[0]; id++) { - ret = !strcmp((char *)id->id, info->hardware_id.string); - if (ret) - break; - } - } - kfree(info); - return ret; -} - -static acpi_status acpi_container_register_notify_handler(acpi_handle handle, - u32 lvl, void *ctxt, - void **retv) -{ - if (is_container(handle)) - acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, - container_notify_cb, NULL); - - return AE_OK; -} - void __init acpi_container_init(void) { - acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, - acpi_container_register_notify_handler, NULL, - NULL, NULL); - - acpi_scan_add_handler(&container_device_handler); + acpi_scan_add_handler_with_hotplug(&container_handler, "container"); } diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 3c94a732b4b3..6f1afd9118c8 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -41,6 +41,17 @@ void acpi_container_init(void); #else static inline void acpi_container_init(void) {} #endif +#ifdef CONFIG_ACPI_HOTPLUG_MEMORY +void acpi_memory_hotplug_init(void); +#else +static inline void acpi_memory_hotplug_init(void) {} +#endif + +void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug, + const char *name); +int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler, + const char *hotplug_profile_name); +void acpi_scan_hotplug_enabled(struct acpi_hotplug_profile *hotplug, bool val); #ifdef CONFIG_DEBUG_FS extern struct dentry *acpi_debugfs_dir; @@ -48,6 +59,11 @@ int acpi_debugfs_init(void); #else static inline void acpi_debugfs_init(void) { return; } #endif +#ifdef CONFIG_X86_INTEL_LPSS +void acpi_lpss_init(void); +#else +static inline void acpi_lpss_init(void) {} +#endif /* -------------------------------------------------------------------------- Device Node Initialization / Removal @@ -60,7 +76,7 @@ int acpi_device_add(struct acpi_device *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); +void acpi_free_pnp_ids(struct acpi_device_pnp *pnp); /* -------------------------------------------------------------------------- Power Resource @@ -131,4 +147,7 @@ static inline void suspend_nvs_restore(void) {} -------------------------------------------------------------------------- */ struct platform_device; +int acpi_create_platform_device(struct acpi_device *adev, + const struct acpi_device_id *id); + #endif /* _ACPI_INTERNAL_H_ */ diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 0ac546d5e53f..6ae5e440436e 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -415,7 +415,6 @@ static int acpi_pci_root_add(struct acpi_device *device, struct acpi_pci_root *root; struct acpi_pci_driver *driver; u32 flags, base_flags; - bool is_osc_granted = false; root = kzalloc(sizeof(struct acpi_pci_root), GFP_KERNEL); if (!root) @@ -476,6 +475,30 @@ static int acpi_pci_root_add(struct acpi_device *device, flags = base_flags = OSC_PCI_SEGMENT_GROUPS_SUPPORT; acpi_pci_osc_support(root, flags); + /* + * TBD: Need PCI interface for enumeration/configuration of roots. + */ + + mutex_lock(&acpi_pci_root_lock); + list_add_tail(&root->node, &acpi_pci_roots); + mutex_unlock(&acpi_pci_root_lock); + + /* + * Scan the Root Bridge + * -------------------- + * Must do this prior to any attempt to bind the root device, as the + * PCI namespace does not get created until this call is made (and + * thus the root bridge's pci_dev does not exist). + */ + root->bus = pci_acpi_scan_root(root); + if (!root->bus) { + printk(KERN_ERR PREFIX + "Bus %04x:%02x not present in PCI namespace\n", + root->segment, (unsigned int)root->secondary.start); + result = -ENODEV; + goto out_del_root; + } + /* Indicate support for various _OSC capabilities. */ if (pci_ext_cfg_avail()) flags |= OSC_EXT_PCI_CONFIG_SUPPORT; @@ -494,6 +517,7 @@ static int acpi_pci_root_add(struct acpi_device *device, flags = base_flags; } } + if (!pcie_ports_disabled && (flags & ACPI_PCIE_REQ_SUPPORT) == ACPI_PCIE_REQ_SUPPORT) { flags = OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL @@ -514,54 +538,28 @@ static int acpi_pci_root_add(struct acpi_device *device, status = acpi_pci_osc_control_set(device->handle, &flags, OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL); if (ACPI_SUCCESS(status)) { - is_osc_granted = true; dev_info(&device->dev, "ACPI _OSC control (0x%02x) granted\n", flags); + if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) { + /* + * We have ASPM control, but the FADT indicates + * that it's unsupported. Clear it. + */ + pcie_clear_aspm(root->bus); + } } else { - is_osc_granted = false; dev_info(&device->dev, "ACPI _OSC request failed (%s), " "returned control mask: 0x%02x\n", acpi_format_exception(status), flags); + pr_info("ACPI _OSC control for PCIe not granted, " + "disabling ASPM\n"); + pcie_no_aspm(); } } else { dev_info(&device->dev, - "Unable to request _OSC control " - "(_OSC support mask: 0x%02x)\n", flags); - } - - /* - * TBD: Need PCI interface for enumeration/configuration of roots. - */ - - mutex_lock(&acpi_pci_root_lock); - list_add_tail(&root->node, &acpi_pci_roots); - mutex_unlock(&acpi_pci_root_lock); - - /* - * Scan the Root Bridge - * -------------------- - * Must do this prior to any attempt to bind the root device, as the - * PCI namespace does not get created until this call is made (and - * thus the root bridge's pci_dev does not exist). - */ - root->bus = pci_acpi_scan_root(root); - if (!root->bus) { - printk(KERN_ERR PREFIX - "Bus %04x:%02x not present in PCI namespace\n", - root->segment, (unsigned int)root->secondary.start); - result = -ENODEV; - goto out_del_root; - } - - /* ASPM setting */ - if (is_osc_granted) { - if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) - pcie_clear_aspm(root->bus); - } else { - pr_info("ACPI _OSC control for PCIe not granted, " - "disabling ASPM\n"); - pcie_no_aspm(); + "Unable to request _OSC control " + "(_OSC support mask: 0x%02x)\n", flags); } pci_acpi_add_bus_pm_notifier(device, root->bus); @@ -646,6 +644,7 @@ static void handle_root_bridge_insertion(acpi_handle handle) static void handle_root_bridge_removal(struct acpi_device *device) { + acpi_status status; struct acpi_eject_event *ej_event; ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL); @@ -661,7 +660,9 @@ static void handle_root_bridge_removal(struct acpi_device *device) ej_event->device = device; ej_event->event = ACPI_NOTIFY_EJECT_REQUEST; - acpi_bus_hot_remove_device(ej_event); + status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device, ej_event); + if (ACPI_FAILURE(status)) + kfree(ej_event); } static void _handle_hotplug_event_root(struct work_struct *work) @@ -676,8 +677,9 @@ static void _handle_hotplug_event_root(struct work_struct *work) handle = hp_work->handle; type = hp_work->type; - root = acpi_pci_find_root(handle); + acpi_scan_lock_acquire(); + root = acpi_pci_find_root(handle); acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); switch (type) { @@ -711,6 +713,7 @@ static void _handle_hotplug_event_root(struct work_struct *work) break; } + acpi_scan_lock_release(); kfree(hp_work); /* allocated in handle_hotplug_event_bridge */ kfree(buffer.pointer); } diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 34f5ef11d427..0481b1b1d2aa 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -824,7 +824,7 @@ static void acpi_release_power_resource(struct device *dev) list_del(&resource->list_node); mutex_unlock(&power_resource_list_lock); - acpi_free_ids(device); + acpi_free_pnp_ids(&device->pnp); kfree(resource); } diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index fc95308e9a11..ee255c60bdac 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -66,7 +66,8 @@ module_param(latency_factor, uint, 0644); static DEFINE_PER_CPU(struct cpuidle_device *, acpi_cpuidle_device); -static struct acpi_processor_cx *acpi_cstate[CPUIDLE_STATE_MAX]; +static DEFINE_PER_CPU(struct acpi_processor_cx * [CPUIDLE_STATE_MAX], + acpi_cstate); static int disabled_by_idle_boot_param(void) { @@ -722,7 +723,7 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { struct acpi_processor *pr; - struct acpi_processor_cx *cx = acpi_cstate[index]; + struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu); pr = __this_cpu_read(processors); @@ -745,7 +746,7 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev, */ static int acpi_idle_play_dead(struct cpuidle_device *dev, int index) { - struct acpi_processor_cx *cx = acpi_cstate[index]; + struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu); ACPI_FLUSH_CPU_CACHE(); @@ -775,7 +776,7 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { struct acpi_processor *pr; - struct acpi_processor_cx *cx = acpi_cstate[index]; + struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu); pr = __this_cpu_read(processors); @@ -833,7 +834,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { struct acpi_processor *pr; - struct acpi_processor_cx *cx = acpi_cstate[index]; + struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu); pr = __this_cpu_read(processors); @@ -960,7 +961,7 @@ static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr, !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED)) continue; #endif - acpi_cstate[count] = cx; + per_cpu(acpi_cstate[count], dev->cpu) = cx; count++; if (count == CPUIDLE_STATE_MAX) diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c index 53e7ac9403a7..e854582f29a6 100644 --- a/drivers/acpi/processor_perflib.c +++ b/drivers/acpi/processor_perflib.c @@ -465,7 +465,7 @@ static int acpi_processor_get_performance_states(struct acpi_processor *pr) return result; } -static int acpi_processor_get_performance_info(struct acpi_processor *pr) +int acpi_processor_get_performance_info(struct acpi_processor *pr) { int result = 0; acpi_status status = AE_OK; @@ -509,7 +509,7 @@ static int acpi_processor_get_performance_info(struct acpi_processor *pr) #endif return result; } - +EXPORT_SYMBOL_GPL(acpi_processor_get_performance_info); int acpi_processor_notify_smm(struct module *calling_module) { acpi_status status; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 5e7e991717d7..d7f3c8b66fcf 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -63,6 +63,19 @@ int acpi_scan_add_handler(struct acpi_scan_handler *handler) return 0; } +int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler, + const char *hotplug_profile_name) +{ + int error; + + error = acpi_scan_add_handler(handler); + if (error) + return error; + + acpi_sysfs_add_hotplug_profile(&handler->hotplug, hotplug_profile_name); + 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: @@ -107,32 +120,20 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha } static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); -/** - * acpi_bus_hot_remove_device: hot-remove a device and its children - * @context: struct acpi_eject_event pointer (freed in this func) - * - * Hot-remove a device and its children. This function frees up the - * memory space passed by arg context, so that the caller may call - * this function asynchronously through acpi_os_hotplug_execute(). - */ -void acpi_bus_hot_remove_device(void *context) +static int acpi_scan_hot_remove(struct acpi_device *device) { - struct acpi_eject_event *ej_event = context; - struct acpi_device *device = ej_event->device; acpi_handle handle = device->handle; - acpi_handle temp; + acpi_handle not_used; 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 */ - - mutex_lock(&acpi_scan_lock); + acpi_status status; + unsigned long long sta; /* If there is no handle, the device node has been unregistered. */ - if (!device->handle) { + if (!handle) { dev_dbg(&device->dev, "ACPI handle missing\n"); put_device(&device->dev); - goto out; + return -EINVAL; } ACPI_DEBUG_PRINT((ACPI_DB_INFO, @@ -143,7 +144,7 @@ void acpi_bus_hot_remove_device(void *context) put_device(&device->dev); device = NULL; - if (ACPI_SUCCESS(acpi_get_handle(handle, "_LCK", &temp))) { + if (ACPI_SUCCESS(acpi_get_handle(handle, "_LCK", ¬_used))) { arg_list.count = 1; arg_list.pointer = &arg; arg.type = ACPI_TYPE_INTEGER; @@ -161,18 +162,205 @@ 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) - acpi_handle_warn(handle, "Eject failed\n"); + if (status == AE_NOT_FOUND) { + return -ENODEV; + } else { + acpi_handle_warn(handle, "Eject failed (0x%x)\n", + status); + return -EIO; + } + } - /* Tell the firmware the hot-remove operation has failed. */ - acpi_evaluate_hotplug_ost(handle, ej_event->event, - ost_code, NULL); + /* + * Verify if eject was indeed successful. If not, log an error + * message. No need to call _OST since _EJ0 call was made OK. + */ + status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); + if (ACPI_FAILURE(status)) { + acpi_handle_warn(handle, + "Status check after eject failed (0x%x)\n", status); + } else if (sta & ACPI_STA_DEVICE_ENABLED) { + acpi_handle_warn(handle, + "Eject incomplete - status 0x%llx\n", sta); + } + + return 0; +} + +static void acpi_bus_device_eject(void *context) +{ + acpi_handle handle = context; + struct acpi_device *device = NULL; + struct acpi_scan_handler *handler; + u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; + + mutex_lock(&acpi_scan_lock); + + acpi_bus_get_device(handle, &device); + if (!device) + goto err_out; + + handler = device->handler; + if (!handler || !handler->hotplug.enabled) { + ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED; + goto err_out; + } + acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST, + ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); + if (handler->hotplug.mode == AHM_CONTAINER) { + device->flags.eject_pending = true; + kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); + } else { + int error; + + get_device(&device->dev); + error = acpi_scan_hot_remove(device); + if (error) + goto err_out; } out: mutex_unlock(&acpi_scan_lock); - kfree(context); return; + + err_out: + acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST, ost_code, + NULL); + goto out; +} + +static void acpi_scan_bus_device_check(acpi_handle handle, u32 ost_source) +{ + struct acpi_device *device = NULL; + u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; + int error; + + mutex_lock(&acpi_scan_lock); + + acpi_bus_get_device(handle, &device); + if (device) { + dev_warn(&device->dev, "Attempt to re-insert\n"); + goto out; + } + acpi_evaluate_hotplug_ost(handle, ost_source, + ACPI_OST_SC_INSERT_IN_PROGRESS, NULL); + error = acpi_bus_scan(handle); + if (error) { + acpi_handle_warn(handle, "Namespace scan failure\n"); + goto out; + } + error = acpi_bus_get_device(handle, &device); + if (error) { + acpi_handle_warn(handle, "Missing device node object\n"); + goto out; + } + ost_code = ACPI_OST_SC_SUCCESS; + if (device->handler && device->handler->hotplug.mode == AHM_CONTAINER) + kobject_uevent(&device->dev.kobj, KOBJ_ONLINE); + + out: + acpi_evaluate_hotplug_ost(handle, ost_source, ost_code, NULL); + mutex_unlock(&acpi_scan_lock); +} + +static void acpi_scan_bus_check(void *context) +{ + acpi_scan_bus_device_check((acpi_handle)context, + ACPI_NOTIFY_BUS_CHECK); +} + +static void acpi_scan_device_check(void *context) +{ + acpi_scan_bus_device_check((acpi_handle)context, + ACPI_NOTIFY_DEVICE_CHECK); +} + +static void acpi_hotplug_unsupported(acpi_handle handle, u32 type) +{ + u32 ost_status; + + switch (type) { + case ACPI_NOTIFY_BUS_CHECK: + acpi_handle_debug(handle, + "ACPI_NOTIFY_BUS_CHECK event: unsupported\n"); + ost_status = ACPI_OST_SC_INSERT_NOT_SUPPORTED; + break; + case ACPI_NOTIFY_DEVICE_CHECK: + acpi_handle_debug(handle, + "ACPI_NOTIFY_DEVICE_CHECK event: unsupported\n"); + ost_status = ACPI_OST_SC_INSERT_NOT_SUPPORTED; + break; + case ACPI_NOTIFY_EJECT_REQUEST: + acpi_handle_debug(handle, + "ACPI_NOTIFY_EJECT_REQUEST event: unsupported\n"); + ost_status = ACPI_OST_SC_EJECT_NOT_SUPPORTED; + break; + default: + /* non-hotplug event; possibly handled by other handler */ + return; + } + + acpi_evaluate_hotplug_ost(handle, type, ost_status, NULL); +} + +static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data) +{ + acpi_osd_exec_callback callback; + struct acpi_scan_handler *handler = data; + acpi_status status; + + if (!handler->hotplug.enabled) + return acpi_hotplug_unsupported(handle, type); + + switch (type) { + case ACPI_NOTIFY_BUS_CHECK: + acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n"); + callback = acpi_scan_bus_check; + break; + case ACPI_NOTIFY_DEVICE_CHECK: + acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n"); + callback = acpi_scan_device_check; + break; + case ACPI_NOTIFY_EJECT_REQUEST: + acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n"); + callback = acpi_bus_device_eject; + break; + default: + /* non-hotplug event; possibly handled by other handler */ + return; + } + status = acpi_os_hotplug_execute(callback, handle); + if (ACPI_FAILURE(status)) + acpi_evaluate_hotplug_ost(handle, type, + ACPI_OST_SC_NON_SPECIFIC_FAILURE, + NULL); +} + +/** + * acpi_bus_hot_remove_device: hot-remove a device and its children + * @context: struct acpi_eject_event pointer (freed in this func) + * + * Hot-remove a device and its children. This function frees up the + * memory space passed by arg context, so that the caller may call + * this function asynchronously through acpi_os_hotplug_execute(). + */ +void acpi_bus_hot_remove_device(void *context) +{ + struct acpi_eject_event *ej_event = context; + struct acpi_device *device = ej_event->device; + acpi_handle handle = device->handle; + int error; + + mutex_lock(&acpi_scan_lock); + + error = acpi_scan_hot_remove(device); + if (error && handle) + acpi_evaluate_hotplug_ost(handle, ej_event->event, + ACPI_OST_SC_NON_SPECIFIC_FAILURE, + NULL); + + mutex_unlock(&acpi_scan_lock); + kfree(context); } EXPORT_SYMBOL(acpi_bus_hot_remove_device); @@ -206,51 +394,61 @@ static ssize_t acpi_eject_store(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - int ret = count; - acpi_status status; - acpi_object_type type = 0; struct acpi_device *acpi_device = to_acpi_device(d); struct acpi_eject_event *ej_event; + acpi_object_type not_used; + acpi_status status; + u32 ost_source; + int ret; - if ((!count) || (buf[0] != '1')) { + if (!count || buf[0] != '1') return -EINVAL; - } - if (!acpi_device->driver && !acpi_device->handler) { - ret = -ENODEV; - goto err; - } - status = acpi_get_type(acpi_device->handle, &type); - if (ACPI_FAILURE(status) || (!acpi_device->flags.ejectable)) { - ret = -ENODEV; - goto err; - } - ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL); - if (!ej_event) { - ret = -ENOMEM; - goto err; - } + if ((!acpi_device->handler || !acpi_device->handler->hotplug.enabled) + && !acpi_device->driver) + return -ENODEV; + + status = acpi_get_type(acpi_device->handle, ¬_used); + if (ACPI_FAILURE(status) || !acpi_device->flags.ejectable) + return -ENODEV; + + mutex_lock(&acpi_scan_lock); - get_device(&acpi_device->dev); - ej_event->device = acpi_device; if (acpi_device->flags.eject_pending) { - /* event originated from ACPI eject notification */ - ej_event->event = ACPI_NOTIFY_EJECT_REQUEST; + /* ACPI eject notification event. */ + ost_source = ACPI_NOTIFY_EJECT_REQUEST; acpi_device->flags.eject_pending = 0; } else { - /* event originated from user */ - ej_event->event = ACPI_OST_EC_OSPM_EJECT; - (void) acpi_evaluate_hotplug_ost(acpi_device->handle, - ej_event->event, ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); + /* Eject initiated by user space. */ + ost_source = ACPI_OST_EC_OSPM_EJECT; } - + ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL); + if (!ej_event) { + ret = -ENOMEM; + goto err_out; + } + acpi_evaluate_hotplug_ost(acpi_device->handle, ost_source, + ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); + ej_event->device = acpi_device; + ej_event->event = ost_source; + get_device(&acpi_device->dev); status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device, ej_event); if (ACPI_FAILURE(status)) { put_device(&acpi_device->dev); kfree(ej_event); + ret = status == AE_NO_MEMORY ? -ENOMEM : -EAGAIN; + goto err_out; } -err: + ret = count; + + out: + mutex_unlock(&acpi_scan_lock); return ret; + + err_out: + acpi_evaluate_hotplug_ost(acpi_device->handle, ost_source, + ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL); + goto out; } static DEVICE_ATTR(eject, 0200, NULL, acpi_eject_store); @@ -376,7 +574,7 @@ static int acpi_device_setup_files(struct acpi_device *dev) goto end; } - if (dev->flags.bus_address) + if (dev->pnp.type.bus_address) result = device_create_file(&dev->dev, &dev_attr_adr); if (dev->pnp.unique_id) result = device_create_file(&dev->dev, &dev_attr_uid); @@ -449,7 +647,7 @@ static void acpi_device_remove_files(struct acpi_device *dev) if (dev->pnp.unique_id) device_remove_file(&dev->dev, &dev_attr_uid); - if (dev->flags.bus_address) + if (dev->pnp.type.bus_address) device_remove_file(&dev->dev, &dev_attr_adr); device_remove_file(&dev->dev, &dev_attr_modalias); device_remove_file(&dev->dev, &dev_attr_hid); @@ -512,17 +710,6 @@ int acpi_match_device_ids(struct acpi_device *device, } EXPORT_SYMBOL(acpi_match_device_ids); -void acpi_free_ids(struct acpi_device *device) -{ - struct acpi_hardware_id *id, *tmp; - - list_for_each_entry_safe(id, tmp, &device->pnp.ids, list) { - kfree(id->id); - kfree(id); - } - kfree(device->pnp.unique_id); -} - static void acpi_free_power_resources_lists(struct acpi_device *device) { int i; @@ -543,7 +730,7 @@ static void acpi_device_release(struct device *dev) { struct acpi_device *acpi_dev = to_acpi_device(dev); - acpi_free_ids(acpi_dev); + acpi_free_pnp_ids(&acpi_dev->pnp); acpi_free_power_resources_lists(acpi_dev); kfree(acpi_dev); } @@ -1256,19 +1443,17 @@ static void acpi_device_get_busid(struct acpi_device *device) } /* - * acpi_bay_match - see if a device is an ejectable driver bay + * acpi_bay_match - see if an acpi object is an ejectable driver bay * * If an acpi object is ejectable and has one of the ACPI ATA methods defined, * then we can safely call it an ejectable drive bay */ -static int acpi_bay_match(struct acpi_device *device){ +static int acpi_bay_match(acpi_handle handle) +{ acpi_status status; - acpi_handle handle; acpi_handle tmp; acpi_handle phandle; - handle = device->handle; - status = acpi_get_handle(handle, "_EJ0", &tmp); if (ACPI_FAILURE(status)) return -ENODEV; @@ -1292,12 +1477,12 @@ static int acpi_bay_match(struct acpi_device *device){ } /* - * acpi_dock_match - see if a device has a _DCK method + * acpi_dock_match - see if an acpi object has a _DCK method */ -static int acpi_dock_match(struct acpi_device *device) +static int acpi_dock_match(acpi_handle handle) { acpi_handle tmp; - return acpi_get_handle(device->handle, "_DCK", &tmp); + return acpi_get_handle(handle, "_DCK", &tmp); } const char *acpi_device_hid(struct acpi_device *device) @@ -1312,7 +1497,7 @@ const char *acpi_device_hid(struct acpi_device *device) } EXPORT_SYMBOL(acpi_device_hid); -static void acpi_add_id(struct acpi_device *device, const char *dev_id) +static void acpi_add_id(struct acpi_device_pnp *pnp, const char *dev_id) { struct acpi_hardware_id *id; @@ -1326,7 +1511,8 @@ static void acpi_add_id(struct acpi_device *device, const char *dev_id) return; } - list_add_tail(&id->list, &device->pnp.ids); + list_add_tail(&id->list, &pnp->ids); + pnp->type.hardware_id = 1; } /* @@ -1334,7 +1520,7 @@ static void acpi_add_id(struct acpi_device *device, const char *dev_id) * lacks the SMBUS01 HID and the methods do not have the necessary "_" * prefix. Work around this. */ -static int acpi_ibm_smbus_match(struct acpi_device *device) +static int acpi_ibm_smbus_match(acpi_handle handle) { acpi_handle h_dummy; struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL}; @@ -1344,7 +1530,7 @@ static int acpi_ibm_smbus_match(struct acpi_device *device) return -ENODEV; /* Look for SMBS object */ - result = acpi_get_name(device->handle, ACPI_SINGLE_NAME, &path); + result = acpi_get_name(handle, ACPI_SINGLE_NAME, &path); if (result) return result; @@ -1355,48 +1541,50 @@ static int acpi_ibm_smbus_match(struct acpi_device *device) /* Does it have the necessary (but misnamed) methods? */ result = -ENODEV; - if (ACPI_SUCCESS(acpi_get_handle(device->handle, "SBI", &h_dummy)) && - ACPI_SUCCESS(acpi_get_handle(device->handle, "SBR", &h_dummy)) && - ACPI_SUCCESS(acpi_get_handle(device->handle, "SBW", &h_dummy))) + if (ACPI_SUCCESS(acpi_get_handle(handle, "SBI", &h_dummy)) && + ACPI_SUCCESS(acpi_get_handle(handle, "SBR", &h_dummy)) && + ACPI_SUCCESS(acpi_get_handle(handle, "SBW", &h_dummy))) result = 0; out: kfree(path.pointer); return result; } -static void acpi_device_set_id(struct acpi_device *device) +static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp, + int device_type) { acpi_status status; struct acpi_device_info *info; struct acpi_pnp_device_id_list *cid_list; int i; - switch (device->device_type) { + switch (device_type) { case ACPI_BUS_TYPE_DEVICE: - if (ACPI_IS_ROOT_DEVICE(device)) { - acpi_add_id(device, ACPI_SYSTEM_HID); + if (handle == ACPI_ROOT_OBJECT) { + acpi_add_id(pnp, ACPI_SYSTEM_HID); break; } - status = acpi_get_object_info(device->handle, &info); + status = acpi_get_object_info(handle, &info); if (ACPI_FAILURE(status)) { - printk(KERN_ERR PREFIX "%s: Error reading device info\n", __func__); + pr_err(PREFIX "%s: Error reading device info\n", + __func__); return; } if (info->valid & ACPI_VALID_HID) - acpi_add_id(device, info->hardware_id.string); + acpi_add_id(pnp, info->hardware_id.string); if (info->valid & ACPI_VALID_CID) { cid_list = &info->compatible_id_list; for (i = 0; i < cid_list->count; i++) - acpi_add_id(device, cid_list->ids[i].string); + acpi_add_id(pnp, cid_list->ids[i].string); } if (info->valid & ACPI_VALID_ADR) { - device->pnp.bus_address = info->address; - device->flags.bus_address = 1; + pnp->bus_address = info->address; + pnp->type.bus_address = 1; } if (info->valid & ACPI_VALID_UID) - device->pnp.unique_id = kstrdup(info->unique_id.string, + pnp->unique_id = kstrdup(info->unique_id.string, GFP_KERNEL); kfree(info); @@ -1405,40 +1593,50 @@ static void acpi_device_set_id(struct acpi_device *device) * Some devices don't reliably have _HIDs & _CIDs, so add * synthetic HIDs to make sure drivers can find them. */ - if (acpi_is_video_device(device)) - acpi_add_id(device, ACPI_VIDEO_HID); - else if (ACPI_SUCCESS(acpi_bay_match(device))) - acpi_add_id(device, ACPI_BAY_HID); - else if (ACPI_SUCCESS(acpi_dock_match(device))) - acpi_add_id(device, ACPI_DOCK_HID); - else if (!acpi_ibm_smbus_match(device)) - acpi_add_id(device, ACPI_SMBUS_IBM_HID); - else if (list_empty(&device->pnp.ids) && - ACPI_IS_ROOT_DEVICE(device->parent)) { - acpi_add_id(device, ACPI_BUS_HID); /* \_SB, LNXSYBUS */ - strcpy(device->pnp.device_name, ACPI_BUS_DEVICE_NAME); - strcpy(device->pnp.device_class, ACPI_BUS_CLASS); + if (acpi_is_video_device(handle)) + acpi_add_id(pnp, ACPI_VIDEO_HID); + else if (ACPI_SUCCESS(acpi_bay_match(handle))) + acpi_add_id(pnp, ACPI_BAY_HID); + else if (ACPI_SUCCESS(acpi_dock_match(handle))) + acpi_add_id(pnp, ACPI_DOCK_HID); + else if (!acpi_ibm_smbus_match(handle)) + acpi_add_id(pnp, ACPI_SMBUS_IBM_HID); + else if (list_empty(&pnp->ids) && handle == ACPI_ROOT_OBJECT) { + acpi_add_id(pnp, ACPI_BUS_HID); /* \_SB, LNXSYBUS */ + strcpy(pnp->device_name, ACPI_BUS_DEVICE_NAME); + strcpy(pnp->device_class, ACPI_BUS_CLASS); } break; case ACPI_BUS_TYPE_POWER: - acpi_add_id(device, ACPI_POWER_HID); + acpi_add_id(pnp, ACPI_POWER_HID); break; case ACPI_BUS_TYPE_PROCESSOR: - acpi_add_id(device, ACPI_PROCESSOR_OBJECT_HID); + acpi_add_id(pnp, ACPI_PROCESSOR_OBJECT_HID); break; case ACPI_BUS_TYPE_THERMAL: - acpi_add_id(device, ACPI_THERMAL_HID); + acpi_add_id(pnp, ACPI_THERMAL_HID); break; case ACPI_BUS_TYPE_POWER_BUTTON: - acpi_add_id(device, ACPI_BUTTON_HID_POWERF); + acpi_add_id(pnp, ACPI_BUTTON_HID_POWERF); break; case ACPI_BUS_TYPE_SLEEP_BUTTON: - acpi_add_id(device, ACPI_BUTTON_HID_SLEEPF); + acpi_add_id(pnp, ACPI_BUTTON_HID_SLEEPF); break; } } +void acpi_free_pnp_ids(struct acpi_device_pnp *pnp) +{ + struct acpi_hardware_id *id, *tmp; + + list_for_each_entry_safe(id, tmp, &pnp->ids, list) { + kfree(id->id); + kfree(id); + } + kfree(pnp->unique_id); +} + void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, int type, unsigned long long sta) { @@ -1448,7 +1646,7 @@ void acpi_init_device_object(struct acpi_device *device, acpi_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_set_pnp_ids(handle, &device->pnp, type); acpi_bus_get_flags(device); device->flags.match_driver = false; device_initialize(&device->dev); @@ -1536,6 +1734,75 @@ static int acpi_bus_type_and_status(acpi_handle handle, int *type, return 0; } +static bool acpi_scan_handler_matching(struct acpi_scan_handler *handler, + char *idstr, + const struct acpi_device_id **matchid) +{ + const struct acpi_device_id *devid; + + for (devid = handler->ids; devid->id[0]; devid++) + if (!strcmp((char *)devid->id, idstr)) { + if (matchid) + *matchid = devid; + + return true; + } + + return false; +} + +static struct acpi_scan_handler *acpi_scan_match_handler(char *idstr, + const struct acpi_device_id **matchid) +{ + struct acpi_scan_handler *handler; + + list_for_each_entry(handler, &acpi_scan_handlers_list, list_node) + if (acpi_scan_handler_matching(handler, idstr, matchid)) + return handler; + + return NULL; +} + +void acpi_scan_hotplug_enabled(struct acpi_hotplug_profile *hotplug, bool val) +{ + if (!!hotplug->enabled == !!val) + return; + + mutex_lock(&acpi_scan_lock); + + hotplug->enabled = val; + + mutex_unlock(&acpi_scan_lock); +} + +static void acpi_scan_init_hotplug(acpi_handle handle, int type) +{ + struct acpi_device_pnp pnp = {}; + struct acpi_hardware_id *hwid; + struct acpi_scan_handler *handler; + + INIT_LIST_HEAD(&pnp.ids); + acpi_set_pnp_ids(handle, &pnp, type); + + if (!pnp.type.hardware_id) + return; + + /* + * This relies on the fact that acpi_install_notify_handler() will not + * install the same notify handler routine twice for the same handle. + */ + list_for_each_entry(hwid, &pnp.ids, list) { + handler = acpi_scan_match_handler(hwid->id, NULL); + if (handler) { + acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + acpi_hotplug_notify_cb, handler); + break; + } + } + + acpi_free_pnp_ids(&pnp); +} + static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, void *not_used, void **return_value) { @@ -1558,6 +1825,8 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, return AE_OK; } + acpi_scan_init_hotplug(handle, type); + if (!(sta & ACPI_STA_DEVICE_PRESENT) && !(sta & ACPI_STA_DEVICE_FUNCTIONING)) { struct acpi_device_wakeup wakeup; @@ -1583,42 +1852,26 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, return AE_OK; } -static int acpi_scan_do_attach_handler(struct acpi_device *device, char *id) +static int acpi_scan_attach_handler(struct acpi_device *device) { - struct acpi_scan_handler *handler; + struct acpi_hardware_id *hwid; + int ret = 0; - list_for_each_entry(handler, &acpi_scan_handlers_list, list_node) { + list_for_each_entry(hwid, &device->pnp.ids, list) { const struct acpi_device_id *devid; + struct acpi_scan_handler *handler; - for (devid = handler->ids; devid->id[0]; devid++) { - int ret; - - if (strcmp((char *)devid->id, id)) - continue; - + handler = acpi_scan_match_handler(hwid->id, &devid); + if (handler) { ret = handler->attach(device, devid); if (ret > 0) { device->handler = handler; - return ret; + break; } else if (ret < 0) { - return ret; + break; } } } - 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; } @@ -1788,9 +2041,11 @@ int __init acpi_scan_init(void) acpi_pci_root_init(); acpi_pci_link_init(); acpi_platform_init(); + acpi_lpss_init(); acpi_csrt_init(); acpi_container_init(); acpi_pci_slot_init(); + acpi_memory_hotplug_init(); mutex_lock(&acpi_scan_lock); /* diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 24213033fbae..9c1a435d10e6 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -193,6 +193,14 @@ static struct dmi_system_id __initdata acpisleep_dmi_table[] = { }, { .callback = init_nvs_nosave, + .ident = "Sony Vaio VGN-FW21M", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FW21M"), + }, + }, + { + .callback = init_nvs_nosave, .ident = "Sony Vaio VPCEB17FX", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index 41c0504470db..fcae5fa2e1b3 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -7,6 +7,8 @@ #include <linux/moduleparam.h> #include <acpi/acpi_drivers.h> +#include "internal.h" + #define _COMPONENT ACPI_SYSTEM_COMPONENT ACPI_MODULE_NAME("sysfs"); @@ -249,6 +251,7 @@ module_param_call(acpica_version, NULL, param_get_acpica_version, NULL, 0444); static LIST_HEAD(acpi_table_attr_list); static struct kobject *tables_kobj; static struct kobject *dynamic_tables_kobj; +static struct kobject *hotplug_kobj; struct acpi_table_attr { struct bin_attribute attr; @@ -716,6 +719,67 @@ acpi_show_profile(struct device *dev, struct device_attribute *attr, static const struct device_attribute pm_profile_attr = __ATTR(pm_profile, S_IRUGO, acpi_show_profile, NULL); +static ssize_t hotplug_enabled_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct acpi_hotplug_profile *hotplug = to_acpi_hotplug_profile(kobj); + + return sprintf(buf, "%d\n", hotplug->enabled); +} + +static ssize_t hotplug_enabled_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t size) +{ + struct acpi_hotplug_profile *hotplug = to_acpi_hotplug_profile(kobj); + unsigned int val; + + if (kstrtouint(buf, 10, &val) || val > 1) + return -EINVAL; + + acpi_scan_hotplug_enabled(hotplug, val); + return size; +} + +static struct kobj_attribute hotplug_enabled_attr = + __ATTR(enabled, S_IRUGO | S_IWUSR, hotplug_enabled_show, + hotplug_enabled_store); + +static struct attribute *hotplug_profile_attrs[] = { + &hotplug_enabled_attr.attr, + NULL +}; + +static struct kobj_type acpi_hotplug_profile_ktype = { + .sysfs_ops = &kobj_sysfs_ops, + .default_attrs = hotplug_profile_attrs, +}; + +void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug, + const char *name) +{ + int error; + + if (!hotplug_kobj) + goto err_out; + + kobject_init(&hotplug->kobj, &acpi_hotplug_profile_ktype); + error = kobject_set_name(&hotplug->kobj, "%s", name); + if (error) + goto err_out; + + hotplug->kobj.parent = hotplug_kobj; + error = kobject_add(&hotplug->kobj, hotplug_kobj, NULL); + if (error) + goto err_out; + + kobject_uevent(&hotplug->kobj, KOBJ_ADD); + return; + + err_out: + pr_err(PREFIX "Unable to add hotplug profile '%s'\n", name); +} + int __init acpi_sysfs_init(void) { int result; @@ -723,6 +787,8 @@ int __init acpi_sysfs_init(void) result = acpi_tables_sysfs_init(); if (result) return result; + + hotplug_kobj = kobject_create_and_add("hotplug", acpi_kobj); result = sysfs_create_file(acpi_kobj, &pm_profile_attr.attr); return result; } diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 4ac2593234e7..66f67626f02e 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -67,40 +67,37 @@ acpi_backlight_cap_match(acpi_handle handle, u32 level, void *context, return 0; } -/* Returns true if the device is a video device which can be handled by - * video.ko. +/* Returns true if the ACPI object is a video device which can be + * handled by video.ko. * The device will get a Linux specific CID added in scan.c to * identify the device as an ACPI graphics device * Be aware that the graphics device may not be physically present * Use acpi_video_get_capabilities() to detect general ACPI video * capabilities of present cards */ -long acpi_is_video_device(struct acpi_device *device) +long acpi_is_video_device(acpi_handle handle) { acpi_handle h_dummy; long video_caps = 0; - if (!device) - return 0; - /* Is this device able to support video switching ? */ - if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOD", &h_dummy)) || - ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOS", &h_dummy))) + if (ACPI_SUCCESS(acpi_get_handle(handle, "_DOD", &h_dummy)) || + ACPI_SUCCESS(acpi_get_handle(handle, "_DOS", &h_dummy))) video_caps |= ACPI_VIDEO_OUTPUT_SWITCHING; /* Is this device able to retrieve a video ROM ? */ - if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_ROM", &h_dummy))) + if (ACPI_SUCCESS(acpi_get_handle(handle, "_ROM", &h_dummy))) video_caps |= ACPI_VIDEO_ROM_AVAILABLE; /* Is this device able to configure which video head to be POSTed ? */ - if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_VPO", &h_dummy)) && - ACPI_SUCCESS(acpi_get_handle(device->handle, "_GPD", &h_dummy)) && - ACPI_SUCCESS(acpi_get_handle(device->handle, "_SPD", &h_dummy))) + if (ACPI_SUCCESS(acpi_get_handle(handle, "_VPO", &h_dummy)) && + ACPI_SUCCESS(acpi_get_handle(handle, "_GPD", &h_dummy)) && + ACPI_SUCCESS(acpi_get_handle(handle, "_SPD", &h_dummy))) video_caps |= ACPI_VIDEO_DEVICE_POSTING; /* Only check for backlight functionality if one of the above hit. */ if (video_caps) - acpi_walk_namespace(ACPI_TYPE_DEVICE, device->handle, + acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, ACPI_UINT32_MAX, acpi_backlight_cap_match, NULL, &video_caps, NULL); @@ -127,7 +124,7 @@ find_video(acpi_handle handle, u32 lvl, void *context, void **rv) if (!dev) return AE_OK; pci_dev_put(dev); - *cap |= acpi_is_video_device(acpi_dev); + *cap |= acpi_is_video_device(handle); } return AE_OK; } |