summaryrefslogtreecommitdiffstats
path: root/drivers/acpi/scan.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi/scan.c')
-rw-r--r--drivers/acpi/scan.c976
1 files changed, 489 insertions, 487 deletions
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index c88be6c..daee749 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -29,29 +29,10 @@ 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", 0 },
- { "INT33C1", 0 },
- { "INT33C2", 0 },
- { "INT33C3", 0 },
- { "INT33C4", 0 },
- { "INT33C5", 0 },
- { "INT33C6", 0 },
- { "INT33C7", 0 },
-
- { }
-};
-
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);
@@ -61,6 +42,27 @@ 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)
+ 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:
@@ -115,39 +117,32 @@ 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_eject_event *ej_event = context;
+ 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;
+ mutex_lock(&acpi_scan_lock);
- if (!device)
- goto err_out;
+ /* 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)));
- if (acpi_bus_trim(device, 1)) {
- printk(KERN_ERR PREFIX
- "Removing device failed\n");
- goto err_out;
- }
-
- /* device has been freed */
+ acpi_bus_trim(device);
+ /* Device node has been unregistered. */
+ put_device(&device->dev);
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;
@@ -167,23 +162,46 @@ 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);
+ out:
+ mutex_unlock(&acpi_scan_lock);
kfree(context);
return;
}
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)
@@ -197,12 +215,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 == NULL) {
+ 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;
@@ -215,7 +231,8 @@ acpi_eject_store(struct device *d, struct device_attribute *attr,
goto err;
}
- ej_event->handle = acpi_device->handle;
+ 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;
@@ -223,11 +240,15 @@ 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);
}
- 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;
}
@@ -375,8 +396,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;
}
@@ -386,6 +421,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
*/
@@ -454,9 +496,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);
@@ -470,7 +512,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;
@@ -478,6 +520,23 @@ static void acpi_free_ids(struct acpi_device *device)
kfree(id->id);
kfree(id);
}
+ kfree(device->pnp.unique_id);
+}
+
+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;
+
+ 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)
@@ -485,7 +544,7 @@ 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);
+ acpi_free_power_resources_lists(acpi_dev);
kfree(acpi_dev);
}
@@ -494,7 +553,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->flags.match_driver
+ && !acpi_match_device_ids(acpi_dev, acpi_drv->ids);
}
static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env)
@@ -570,7 +630,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);
@@ -579,15 +638,13 @@ 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)
- acpi_start_single_object(acpi_dev);
-
if (acpi_drv->ops.notify) {
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);
+ acpi_dev->driver = NULL;
+ acpi_dev->driver_data = NULL;
return ret;
}
}
@@ -609,7 +666,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;
@@ -626,12 +683,25 @@ struct bus_type acpi_bus_type = {
.uevent = acpi_device_uevent,
};
-static int acpi_device_register(struct acpi_device *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;
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
* -------
@@ -642,11 +712,13 @@ 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) {
- 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);
@@ -681,11 +753,11 @@ 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;
- result = device_register(&device->dev);
+ device->dev.release = release;
+ result = device_add(&device->dev);
if (result) {
dev_err(&device->dev, "Error registering device\n");
- goto end;
+ goto err;
}
result = acpi_device_setup_files(device);
@@ -695,16 +767,20 @@ 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;
}
-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)
@@ -715,8 +791,20 @@ static void acpi_device_unregister(struct acpi_device *device, int type)
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);
+ if (device->remove)
+ device->remove(device);
+
+ device_del(&device->dev);
+ /*
+ * 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_device_set_power(device, ACPI_STATE_D3_COLD);
+ device->handle = NULL;
+ put_device(&device->dev);
}
/* --------------------------------------------------------------------------
@@ -760,24 +848,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
@@ -821,29 +891,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
@@ -877,52 +941,43 @@ 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)
+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 i = 0;
+ int err = -ENODATA;
if (!wakeup)
- return AE_BAD_PARAMETER;
+ return -EINVAL;
+
+ INIT_LIST_HEAD(&wakeup->resources);
/* _PRW */
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 =
@@ -931,38 +986,35 @@ 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;
- if ((package->package.count - 2) > ACPI_MAX_HANDLES) {
- status = AE_NO_MEMORY;
+ err = acpi_extract_power_resources(package, 2, &wakeup->resources);
+ if (err)
goto out;
- }
- wakeup->resources.count = package->package.count - 2;
- for (i = 0; i < wakeup->resources.count; i++) {
- element = &(package->package.elements[i + 2]);
- if (element->type != ACPI_TYPE_LOCAL_REFERENCE) {
- status = AE_BAD_DATA;
- goto out;
- }
- wakeup->resources.handles[i] = element->reference.handle;
- }
+ 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:
kfree(buffer.pointer);
-
- return status;
+ return err;
}
static void acpi_bus_set_run_wake_flags(struct acpi_device *device)
@@ -1002,17 +1054,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;
}
@@ -1025,20 +1077,73 @@ 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"));
}
-static void acpi_bus_add_power_resource(acpi_handle handle);
+static void acpi_bus_init_power_state(struct acpi_device *device, int state)
+{
+ struct acpi_device_power_state *ps = &device->power.states[state];
+ 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 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) {
+ int err = acpi_extract_power_resources(package, 0,
+ &ps->resources);
+ if (!err)
+ device->power.flags.power_resources = 1;
+ }
+ ACPI_FREE(buffer.pointer);
+ }
+
+ /* Evaluate "_PSx" to see if we can do explicit sets */
+ pathname[2] = 'S';
+ status = acpi_get_handle(device->handle, pathname, &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 (!list_empty(&ps->resources)
+ || (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 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;
+ acpi_status status;
+ acpi_handle handle;
+ u32 i;
+
+ /* 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
@@ -1053,40 +1158,10 @@ static int 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' };
-
- /* Evaluate "_PRx" to se if power resources are referenced */
- acpi_evaluate_reference(device->handle, object_name, NULL,
- &ps->resources);
- if (ps->resources.count) {
- 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 (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++)
+ acpi_bus_init_power_state(device, i);
- /* 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 (ps->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 */
- }
+ 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;
@@ -1103,17 +1178,17 @@ static int 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);
-
- return 0;
+ if (acpi_bus_init_power(device)) {
+ acpi_free_power_resources_lists(device);
+ device->flags.power_manageable = 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))
@@ -1133,21 +1208,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)
@@ -1372,56 +1432,32 @@ static void acpi_device_set_id(struct acpi_device *device)
}
}
-static int acpi_device_set_context(struct acpi_device *device)
+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);
+ device->flags.match_driver = false;
+ device_initialize(&device->dev);
+ dev_set_uevent_suppress(&device->dev, true);
}
-static int acpi_bus_remove(struct acpi_device *dev, int rmdevice)
+void acpi_device_add_finalize(struct acpi_device *device)
{
- if (!dev)
- return -EINVAL;
-
- dev->removal_type = ACPI_BUS_REMOVAL_EJECT;
- device_release_driver(&dev->dev);
-
- 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;
+ 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,
- struct acpi_bus_ops *ops)
+ unsigned long long sta)
{
int result;
struct acpi_device *device;
@@ -1433,102 +1469,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);
- device->bus_ops = *ops; /* workround for not call .start */
- 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;
-
- 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);
- }
-
-end:
- if (!result) {
- 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
+ result = acpi_device_add(device, acpi_device_release);
+ if (result) {
acpi_device_release(&device->dev);
+ return result;
+ }
- return result;
-}
-
-#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_bus_ops ops = {
- .acpi_op_add = 1,
- .acpi_op_start = 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_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,
+ device->parent ? dev_name(&device->parent->dev) : "(null)"));
+ kfree(buffer.pointer);
+ *child = device;
+ return 0;
}
static int acpi_bus_type_and_status(acpi_handle handle, int *type,
@@ -1570,218 +1529,248 @@ 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_bus_ops *ops = context;
+ struct acpi_device *device = NULL;
int type;
unsigned long long sta;
- struct acpi_device *device;
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;
+ 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;
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;
}
- /*
- * 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);
- }
-
+ acpi_add_single_object(&device, handle, type, sta);
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))
- return AE_CTRL_DEPTH;
- }
-
+ out:
if (!*return_value)
*return_value = device;
+
return AE_OK;
}
-static int acpi_bus_scan(acpi_handle handle, struct acpi_bus_ops *ops,
- struct acpi_device **child)
+static int acpi_scan_do_attach_handler(struct acpi_device *device, char *id)
{
- acpi_status status;
- void *device = NULL;
+ struct acpi_scan_handler *handler;
- 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);
+ list_for_each_entry(handler, &acpi_scan_handlers_list, list_node) {
+ const struct acpi_device_id *devid;
- if (child)
- *child = device;
+ for (devid = handler->ids; devid->id[0]; devid++) {
+ int ret;
- if (device)
- return 0;
- else
- return -ENODEV;
-}
+ if (strcmp((char *)devid->id, id))
+ continue;
-/*
- * acpi_bus_add and acpi_bus_start
- *
- * scan a given ACPI tree and (probably recently hot-plugged)
- * create and add or starts 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.
- */
+ ret = handler->attach(device, devid);
+ if (ret > 0) {
+ device->handler = handler;
+ return ret;
+ } else if (ret < 0) {
+ return ret;
+ }
+ }
+ }
+ return 0;
+}
-int
-acpi_bus_add(struct acpi_device **child,
- struct acpi_device *parent, acpi_handle handle, int type)
+static int acpi_scan_attach_handler(struct acpi_device *device)
{
- struct acpi_bus_ops ops;
+ struct acpi_hardware_id *hwid;
+ int ret = 0;
- memset(&ops, 0, sizeof(ops));
- ops.acpi_op_add = 1;
+ list_for_each_entry(hwid, &device->pnp.ids, list) {
+ ret = acpi_scan_do_attach_handler(device, hwid->id);
+ if (ret)
+ break;
- return acpi_bus_scan(handle, &ops, child);
+ }
+ return ret;
}
-EXPORT_SYMBOL(acpi_bus_add);
-int acpi_bus_start(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)
{
- struct acpi_bus_ops ops;
- int result;
-
- if (!device)
- return -EINVAL;
+ struct acpi_device *device;
+ unsigned long long sta_not_used;
+ int ret;
- memset(&ops, 0, sizeof(ops));
- ops.acpi_op_start = 1;
+ /*
+ * Ignore errors ignored by acpi_bus_check_add() to avoid terminating
+ * namespace walks prematurely.
+ */
+ if (acpi_bus_type_and_status(handle, &ret, &sta_not_used))
+ return AE_OK;
- result = acpi_bus_scan(device->handle, &ops, NULL);
+ if (acpi_bus_get_device(handle, &device))
+ return AE_CTRL_DEPTH;
- acpi_update_all_gpes();
+ ret = acpi_scan_attach_handler(device);
+ if (ret)
+ return ret > 0 ? AE_OK : AE_CTRL_DEPTH;
- return result;
+ ret = device_attach(&device->dev);
+ return ret >= 0 ? AE_OK : AE_CTRL_DEPTH;
}
-EXPORT_SYMBOL(acpi_bus_start);
-int acpi_bus_trim(struct acpi_device *start, int rmdevice)
+/**
+ * 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
+ * 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.
+ *
+ * Must be called under acpi_scan_lock.
+ */
+int acpi_bus_scan(acpi_handle handle)
{
- acpi_status status;
- struct acpi_device *parent, *child;
- acpi_handle phandle, chandle;
- acpi_object_type type;
- u32 level = 1;
- int err = 0;
+ void *device = NULL;
+ int error = 0;
- parent = start;
- phandle = start->handle;
- child = chandle = 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);
- while ((level > 0) && parent && (!err)) {
- status = acpi_get_next_object(ACPI_TYPE_ANY, phandle,
- chandle, &chandle);
+ if (!device)
+ 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);
- /*
- * 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;
-
- if (level == 0)
- err = acpi_bus_remove(child, rmdevice);
- else
- err = acpi_bus_remove(child, 1);
+ return error;
+}
+EXPORT_SYMBOL(acpi_bus_scan);
- continue;
- }
+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;
- 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;
+ if (!acpi_bus_get_device(handle, &device)) {
+ struct acpi_scan_handler *dev_handler = device->handler;
+
+ device->removal_type = ACPI_BUS_REMOVAL_EJECT;
+ if (dev_handler) {
+ if (dev_handler->detach)
+ dev_handler->detach(device);
+
+ device->handler = NULL;
+ } else {
+ device_release_driver(&device->dev);
}
- continue;
}
- return err;
+ 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;
+}
+
+/**
+ * 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
+ * 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.
+ */
+ 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);
}
EXPORT_SYMBOL_GPL(acpi_bus_trim);
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;
/*
* 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,
- &ops);
+ 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,
- &ops);
+ 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)
{
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) {
@@ -1789,20 +1778,33 @@ int __init acpi_scan_init(void)
printk(KERN_ERR PREFIX "Could not register bus type\n");
}
- acpi_power_init();
+ acpi_pci_root_init();
+ acpi_pci_link_init();
+ acpi_platform_init();
+ acpi_csrt_init();
+ acpi_container_init();
+ mutex_lock(&acpi_scan_lock);
/*
* Enumerate devices in the ACPI namespace.
*/
- result = acpi_bus_scan(ACPI_ROOT_OBJECT, &ops, &acpi_root);
-
- if (!result)
- result = acpi_bus_scan_fixed();
+ result = acpi_bus_scan(ACPI_ROOT_OBJECT);
+ if (result)
+ goto out;
+ result = acpi_bus_get_device(ACPI_ROOT_OBJECT, &acpi_root);
if (result)
- acpi_device_unregister(acpi_root, ACPI_BUS_REMOVAL_NORMAL);
- else
- acpi_update_all_gpes();
+ goto out;
+
+ result = acpi_bus_scan_fixed();
+ if (result) {
+ acpi_device_unregister(acpi_root);
+ goto out;
+ }
+ acpi_update_all_gpes();
+
+ out:
+ mutex_unlock(&acpi_scan_lock);
return result;
}
OpenPOWER on IntegriCloud