Commit e227e152 authored by Len Brown's avatar Len Brown Committed by Len Brown

[ACPI] ACPI bus support for wakeup GPE (David Shaohua Li)

http://bugzilla.kernel.org/show_bug.cgi?id=1415
parent 5ceb4762
......@@ -288,6 +288,86 @@ acpi_power_off_device (
return_VALUE(0);
}
/*
* Prepare a wakeup device, two steps (Ref ACPI 2.0:P229):
* 1. Power on the power resources required for the wakeup device
* 2. Enable _PSW (power state wake) for the device if present
*/
int acpi_enable_wakeup_device_power (struct acpi_device *dev)
{
union acpi_object arg = {ACPI_TYPE_INTEGER};
struct acpi_object_list arg_list = {1, &arg};
acpi_status status = AE_OK;
int i;
int ret = 0;
ACPI_FUNCTION_TRACE("acpi_enable_wakeup_device_power");
if (!dev || !dev->wakeup.flags.valid)
return -1;
arg.integer.value = 1;
/* Open power resource */
for (i = 0; i < dev->wakeup.resources.count; i++) {
ret = acpi_power_on(dev->wakeup.resources.handles[i]);
if (ret) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Error transition power state\n"));
dev->wakeup.flags.valid = 0;
return -1;
}
}
/* Execute PSW */
status = acpi_evaluate_object(dev->handle, "_PSW", &arg_list, NULL);
if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluate _PSW\n"));
dev->wakeup.flags.valid = 0;
ret = -1;
}
return ret;
}
/*
* Shutdown a wakeup device, counterpart of above method
* 1. Disable _PSW (power state wake)
* 2. Shutdown down the power resources
*/
int acpi_disable_wakeup_device_power (struct acpi_device *dev)
{
union acpi_object arg = {ACPI_TYPE_INTEGER};
struct acpi_object_list arg_list = {1, &arg};
acpi_status status = AE_OK;
int i;
int ret = 0;
ACPI_FUNCTION_TRACE("acpi_disable_wakeup_device_power");
if (!dev || !dev->wakeup.flags.valid)
return -1;
arg.integer.value = 0;
/* Execute PSW */
status = acpi_evaluate_object(dev->handle, "_PSW", &arg_list, NULL);
if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluate _PSW\n"));
dev->wakeup.flags.valid = 0;
return -1;
}
/* Close power resource */
for (i = 0; i < dev->wakeup.resources.count; i++) {
ret = acpi_power_off_device(dev->wakeup.resources.handles[i]);
if (ret) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Error transition power state\n"));
dev->wakeup.flags.valid = 0;
return -1;
}
}
return ret;
}
/* --------------------------------------------------------------------------
Device Power Management
......
......@@ -23,7 +23,8 @@ extern struct acpi_device *acpi_root;
#define ACPI_BUS_DEVICE_NAME "System Bus"
static LIST_HEAD(acpi_device_list);
static spinlock_t acpi_device_lock = SPIN_LOCK_UNLOCKED;
spinlock_t acpi_device_lock = SPIN_LOCK_UNLOCKED;
LIST_HEAD(acpi_wakeup_device_list);
static void acpi_device_release(struct kobject * kobj)
{
......@@ -115,9 +116,6 @@ acpi_bus_get_power_flags (
status = acpi_get_handle(device->handle, "_IRC", &handle);
if (ACPI_SUCCESS(status))
device->power.flags.inrush_current = 1;
status = acpi_get_handle(device->handle, "_PRW", &handle);
if (ACPI_SUCCESS(status))
device->power.flags.wake_capable = 1;
/*
* Enumerate supported power management states
......@@ -163,6 +161,125 @@ acpi_bus_get_power_flags (
return 0;
}
static int
acpi_match_ids (
struct acpi_device *device,
char *ids)
{
int error = 0;
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
if (device->flags.hardware_id)
if (strstr(ids, device->pnp.hardware_id))
goto Done;
if (device->flags.compatible_ids) {
struct acpi_compatible_id_list *cid_list = device->pnp.cid_list;
int i;
/* compare multiple _CID entries against driver ids */
for (i = 0; i < cid_list->count; i++)
{
if (strstr(ids, cid_list->id[i].value))
goto Done;
}
}
error = -ENOENT;
Done:
if (buffer.pointer)
acpi_os_free(buffer.pointer);
return error;
}
static acpi_status
acpi_bus_extract_wakeup_device_power_package (
struct acpi_device *device,
union acpi_object *package)
{
int i = 0;
union acpi_object *element = NULL;
if (!device || !package || (package->package.count < 2))
return AE_BAD_PARAMETER;
element = &(package->package.elements[0]);
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))
return AE_BAD_DATA;
device->wakeup.gpe_device = element->package.elements[0].reference.handle;
device->wakeup.gpe_number = (u32)element->package.elements[1].integer.value;
}else if (element->type == ACPI_TYPE_INTEGER) {
device->wakeup.gpe_number = element->integer.value;
}else
return AE_BAD_DATA;
element = &(package->package.elements[1]);
if (element->type != ACPI_TYPE_INTEGER) {
return AE_BAD_DATA;
}
device->wakeup.sleep_state = element->integer.value;
if ((package->package.count - 2) > ACPI_MAX_HANDLES) {
return AE_NO_MEMORY;
}
device->wakeup.resources.count = package->package.count - 2;
for (i=0; i < device->wakeup.resources.count; i++) {
element = &(package->package.elements[i + 2]);
if (element->type != ACPI_TYPE_ANY ) {
return AE_BAD_DATA;
}
device->wakeup.resources.handles[i] = element->reference.handle;
}
return AE_OK;
}
static int
acpi_bus_get_wakeup_device_flags (
struct acpi_device *device)
{
acpi_status status = 0;
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
union acpi_object *package = NULL;
ACPI_FUNCTION_TRACE("acpi_bus_get_wakeup_flags");
/* _PRW */
status = acpi_evaluate_object(device->handle, "_PRW", NULL, &buffer);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PRW\n"));
goto end;
}
package = (union acpi_object *) buffer.pointer;
status = acpi_bus_extract_wakeup_device_power_package(device, package);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error extracting _PRW package\n"));
goto end;
}
acpi_os_free(buffer.pointer);
device->wakeup.flags.valid = 1;
/* Power button, Lid switch always enable wakeup*/
if (!acpi_match_ids(device, "PNP0C0D,PNP0C0C,PNP0C0E"))
device->wakeup.flags.run_wake = 1;
/* TBD: lock */
INIT_LIST_HEAD(&device->wakeup_list);
spin_lock(&acpi_device_lock);
list_add_tail(&device->wakeup_list, &acpi_wakeup_device_list);
spin_unlock(&acpi_device_lock);
end:
if (ACPI_FAILURE(status))
device->flags.wake_capable = 0;
return 0;
}
/* --------------------------------------------------------------------------
Performance Management
......@@ -195,30 +312,7 @@ acpi_bus_match (
struct acpi_device *device,
struct acpi_driver *driver)
{
int error = 0;
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
if (device->flags.hardware_id)
if (strstr(driver->ids, device->pnp.hardware_id))
goto Done;
if (device->flags.compatible_ids) {
struct acpi_compatible_id_list *cid_list = device->pnp.cid_list;
int i;
/* compare multiple _CID entries against driver ids */
for (i = 0; i < cid_list->count; i++)
{
if (strstr(driver->ids, cid_list->id[i].value))
goto Done;
}
}
error = -ENOENT;
Done:
if (buffer.pointer)
acpi_os_free(buffer.pointer);
return error;
return acpi_match_ids(device, driver->ids);
}
......@@ -469,6 +563,11 @@ acpi_bus_get_flags (
if (ACPI_SUCCESS(status))
device->flags.power_manageable = 1;
/* Presence of _PRW indicates wake capable */
status = acpi_get_handle(device->handle, "_PRW", &temp);
if (ACPI_SUCCESS(status))
device->flags.wake_capable = 1;
/* TBD: Peformance management */
return_VALUE(0);
......@@ -740,6 +839,16 @@ acpi_bus_add (
goto end;
}
/*
* Wakeup device management
*-----------------------
*/
if (device->flags.wake_capable) {
result = acpi_bus_get_wakeup_device_flags(device);
if (result)
goto end;
}
/*
* Performance Management
* ----------------------
......
......@@ -157,7 +157,8 @@ struct acpi_device_flags {
u32 suprise_removal_ok:1;
u32 power_manageable:1;
u32 performance_manageable:1;
u32 reserved:21;
u32 wake_capable:1; /* Wakeup(_PRW) supported? */
u32 reserved:20;
};
......@@ -203,10 +204,8 @@ struct acpi_device_power_flags {
u32 explicit_get:1; /* _PSC present? */
u32 power_resources:1; /* Power resources */
u32 inrush_current:1; /* Serialize Dx->D0 */
u32 wake_capable:1; /* Wakeup supported? */
u32 wake_enabled:1; /* Enabled for wakeup */
u32 power_removed:1; /* Optimize Dx->D0 */
u32 reserved:26;
u32 reserved:28;
};
struct acpi_device_power_state {
......@@ -250,6 +249,25 @@ struct acpi_device_perf {
struct acpi_device_perf_state *states;
};
/* Wakeup Management */
struct acpi_device_wakeup_flags {
u8 valid:1; /* Can successfully enable wakeup? */
u8 run_wake:1; /* Run-Wake GPE devices */
};
struct acpi_device_wakeup_state {
u8 enabled:1;
u8 active:1;
};
struct acpi_device_wakeup {
acpi_handle gpe_device;
acpi_integer gpe_number;;
acpi_integer sleep_state;
struct acpi_handle_list resources;
struct acpi_device_wakeup_state state;
struct acpi_device_wakeup_flags flags;
};
/* Device */
......@@ -258,11 +276,13 @@ struct acpi_device {
struct acpi_device *parent;
struct list_head children;
struct list_head node;
struct list_head wakeup_list;
struct list_head g_list;
struct acpi_device_status status;
struct acpi_device_flags flags;
struct acpi_device_pnp pnp;
struct acpi_device_power power;
struct acpi_device_wakeup wakeup;
struct acpi_device_perf performance;
struct acpi_device_dir dir;
struct acpi_device_ops ops;
......
......@@ -81,7 +81,8 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_device *device, int domain, int b
-------------------------------------------------------------------------- */
#ifdef CONFIG_ACPI_POWER
int acpi_enable_wakeup_device_power (struct acpi_device *dev);
int acpi_disable_wakeup_device_power (struct acpi_device *dev);
int acpi_power_get_inferred_state (struct acpi_device *device);
int acpi_power_transition (struct acpi_device *device, int state);
#endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment