Commit 4d578573 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'pm-acpi' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull some left-over PM patches from Rafael J. Wysocki.

* 'pm-acpi' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  ACPI / PM: Make acpi_pm_device_sleep_state() follow the specification
  ACPI / PM: Make __acpi_bus_get_power() cover D3cold correctly
  ACPI / PM: Fix error messages in drivers/acpi/bus.c
  rtc-cmos / PM: report wakeup event on ACPI RTC alarm
  ACPI / PM: Generate wakeup events on fixed power button
parents 68e3e926 dbe9a2ed
...@@ -182,41 +182,66 @@ EXPORT_SYMBOL(acpi_bus_get_private_data); ...@@ -182,41 +182,66 @@ EXPORT_SYMBOL(acpi_bus_get_private_data);
Power Management Power Management
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
static const char *state_string(int state)
{
switch (state) {
case ACPI_STATE_D0:
return "D0";
case ACPI_STATE_D1:
return "D1";
case ACPI_STATE_D2:
return "D2";
case ACPI_STATE_D3_HOT:
return "D3hot";
case ACPI_STATE_D3_COLD:
return "D3";
default:
return "(unknown)";
}
}
static int __acpi_bus_get_power(struct acpi_device *device, int *state) static int __acpi_bus_get_power(struct acpi_device *device, int *state)
{ {
int result = 0; int result = ACPI_STATE_UNKNOWN;
acpi_status status = 0;
unsigned long long psc = 0;
if (!device || !state) if (!device || !state)
return -EINVAL; return -EINVAL;
*state = ACPI_STATE_UNKNOWN; if (!device->flags.power_manageable) {
if (device->flags.power_manageable) {
/*
* Get the device's power state either directly (via _PSC) or
* indirectly (via power resources).
*/
if (device->power.flags.power_resources) {
result = acpi_power_get_inferred_state(device, state);
if (result)
return result;
} else if (device->power.flags.explicit_get) {
status = acpi_evaluate_integer(device->handle, "_PSC",
NULL, &psc);
if (ACPI_FAILURE(status))
return -ENODEV;
*state = (int)psc;
}
} else {
/* TBD: Non-recursive algorithm for walking up hierarchy. */ /* TBD: Non-recursive algorithm for walking up hierarchy. */
*state = device->parent ? *state = device->parent ?
device->parent->power.state : ACPI_STATE_D0; device->parent->power.state : ACPI_STATE_D0;
goto out;
}
/*
* Get the device's power state either directly (via _PSC) or
* indirectly (via power resources).
*/
if (device->power.flags.explicit_get) {
unsigned long long psc;
acpi_status status = acpi_evaluate_integer(device->handle,
"_PSC", NULL, &psc);
if (ACPI_FAILURE(status))
return -ENODEV;
result = psc;
}
/* The test below covers ACPI_STATE_UNKNOWN too. */
if (result <= ACPI_STATE_D2) {
; /* Do nothing. */
} else if (device->power.flags.power_resources) {
int error = acpi_power_get_inferred_state(device, &result);
if (error)
return error;
} else if (result == ACPI_STATE_D3_HOT) {
result = ACPI_STATE_D3;
} }
*state = result;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is D%d\n", out:
device->pnp.bus_id, *state)); ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is %s\n",
device->pnp.bus_id, state_string(*state)));
return 0; return 0;
} }
...@@ -234,13 +259,14 @@ static int __acpi_bus_set_power(struct acpi_device *device, int state) ...@@ -234,13 +259,14 @@ static int __acpi_bus_set_power(struct acpi_device *device, int state)
/* Make sure this is a valid target state */ /* Make sure this is a valid target state */
if (state == device->power.state) { if (state == device->power.state) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at D%d\n", ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at %s\n",
state)); state_string(state)));
return 0; return 0;
} }
if (!device->power.states[state].flags.valid) { if (!device->power.states[state].flags.valid) {
printk(KERN_WARNING PREFIX "Device does not support D%d\n", state); printk(KERN_WARNING PREFIX "Device does not support %s\n",
state_string(state));
return -ENODEV; return -ENODEV;
} }
if (device->parent && (state < device->parent->power.state)) { if (device->parent && (state < device->parent->power.state)) {
...@@ -294,13 +320,13 @@ static int __acpi_bus_set_power(struct acpi_device *device, int state) ...@@ -294,13 +320,13 @@ static int __acpi_bus_set_power(struct acpi_device *device, int state)
end: end:
if (result) if (result)
printk(KERN_WARNING PREFIX printk(KERN_WARNING PREFIX
"Device [%s] failed to transition to D%d\n", "Device [%s] failed to transition to %s\n",
device->pnp.bus_id, state); device->pnp.bus_id, state_string(state));
else { else {
device->power.state = state; device->power.state = state;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Device [%s] transitioned to D%d\n", "Device [%s] transitioned to %s\n",
device->pnp.bus_id, state)); device->pnp.bus_id, state_string(state)));
} }
return result; return result;
......
...@@ -631,7 +631,7 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state) ...@@ -631,7 +631,7 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state)
* We know a device's inferred power state when all the resources * We know a device's inferred power state when all the resources
* required for a given D-state are 'on'. * required for a given D-state are 'on'.
*/ */
for (i = ACPI_STATE_D0; i < ACPI_STATE_D3_HOT; i++) { for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) {
list = &device->power.states[i].resources; list = &device->power.states[i].resources;
if (list->count < 1) if (list->count < 1)
continue; continue;
......
...@@ -1567,6 +1567,7 @@ static int acpi_bus_scan_fixed(void) ...@@ -1567,6 +1567,7 @@ static int acpi_bus_scan_fixed(void)
ACPI_BUS_TYPE_POWER_BUTTON, ACPI_BUS_TYPE_POWER_BUTTON,
ACPI_STA_DEFAULT, ACPI_STA_DEFAULT,
&ops); &ops);
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) == 0) {
......
...@@ -57,6 +57,7 @@ MODULE_PARM_DESC(gts, "Enable evaluation of _GTS on suspend."); ...@@ -57,6 +57,7 @@ MODULE_PARM_DESC(gts, "Enable evaluation of _GTS on suspend.");
MODULE_PARM_DESC(bfs, "Enable evaluation of _BFS on resume".); MODULE_PARM_DESC(bfs, "Enable evaluation of _BFS on resume".);
static u8 sleep_states[ACPI_S_STATE_COUNT]; static u8 sleep_states[ACPI_S_STATE_COUNT];
static bool pwr_btn_event_pending;
static void acpi_sleep_tts_switch(u32 acpi_state) static void acpi_sleep_tts_switch(u32 acpi_state)
{ {
...@@ -184,6 +185,14 @@ static int acpi_pm_prepare(void) ...@@ -184,6 +185,14 @@ static int acpi_pm_prepare(void)
return error; return error;
} }
static int find_powerf_dev(struct device *dev, void *data)
{
struct acpi_device *device = to_acpi_device(dev);
const char *hid = acpi_device_hid(device);
return !strcmp(hid, ACPI_BUTTON_HID_POWERF);
}
/** /**
* acpi_pm_finish - Instruct the platform to leave a sleep state. * acpi_pm_finish - Instruct the platform to leave a sleep state.
* *
...@@ -192,6 +201,7 @@ static int acpi_pm_prepare(void) ...@@ -192,6 +201,7 @@ static int acpi_pm_prepare(void)
*/ */
static void acpi_pm_finish(void) static void acpi_pm_finish(void)
{ {
struct device *pwr_btn_dev;
u32 acpi_state = acpi_target_sleep_state; u32 acpi_state = acpi_target_sleep_state;
acpi_ec_unblock_transactions(); acpi_ec_unblock_transactions();
...@@ -209,6 +219,23 @@ static void acpi_pm_finish(void) ...@@ -209,6 +219,23 @@ static void acpi_pm_finish(void)
acpi_set_firmware_waking_vector((acpi_physical_address) 0); acpi_set_firmware_waking_vector((acpi_physical_address) 0);
acpi_target_sleep_state = ACPI_STATE_S0; acpi_target_sleep_state = ACPI_STATE_S0;
/* If we were woken with the fixed power button, provide a small
* hint to userspace in the form of a wakeup event on the fixed power
* button device (if it can be found).
*
* We delay the event generation til now, as the PM layer requires
* timekeeping to be running before we generate events. */
if (!pwr_btn_event_pending)
return;
pwr_btn_event_pending = false;
pwr_btn_dev = bus_find_device(&acpi_bus_type, NULL, NULL,
find_powerf_dev);
if (pwr_btn_dev) {
pm_wakeup_event(pwr_btn_dev, 0);
put_device(pwr_btn_dev);
}
} }
/** /**
...@@ -298,9 +325,23 @@ static int acpi_suspend_enter(suspend_state_t pm_state) ...@@ -298,9 +325,23 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
/* ACPI 3.0 specs (P62) says that it's the responsibility /* ACPI 3.0 specs (P62) says that it's the responsibility
* of the OSPM to clear the status bit [ implying that the * of the OSPM to clear the status bit [ implying that the
* POWER_BUTTON event should not reach userspace ] * POWER_BUTTON event should not reach userspace ]
*
* However, we do generate a small hint for userspace in the form of
* a wakeup event. We flag this condition for now and generate the
* event later, as we're currently too early in resume to be able to
* generate wakeup events.
*/ */
if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3)) if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3)) {
acpi_clear_event(ACPI_EVENT_POWER_BUTTON); acpi_event_status pwr_btn_status;
acpi_get_event_status(ACPI_EVENT_POWER_BUTTON, &pwr_btn_status);
if (pwr_btn_status & ACPI_EVENT_FLAG_SET) {
acpi_clear_event(ACPI_EVENT_POWER_BUTTON);
/* Flag for later */
pwr_btn_event_pending = true;
}
}
/* /*
* Disable and clear GPE status before interrupt is enabled. Some GPEs * Disable and clear GPE status before interrupt is enabled. Some GPEs
...@@ -730,8 +771,8 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p) ...@@ -730,8 +771,8 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p)
* can wake the system. _S0W may be valid, too. * can wake the system. _S0W may be valid, too.
*/ */
if (acpi_target_sleep_state == ACPI_STATE_S0 || if (acpi_target_sleep_state == ACPI_STATE_S0 ||
(device_may_wakeup(dev) && (device_may_wakeup(dev) && adev->wakeup.flags.valid &&
adev->wakeup.sleep_state <= acpi_target_sleep_state)) { adev->wakeup.sleep_state >= acpi_target_sleep_state)) {
acpi_status status; acpi_status status;
acpi_method[3] = 'W'; acpi_method[3] = 'W';
......
...@@ -910,14 +910,17 @@ static inline int cmos_poweroff(struct device *dev) ...@@ -910,14 +910,17 @@ static inline int cmos_poweroff(struct device *dev)
static u32 rtc_handler(void *context) static u32 rtc_handler(void *context)
{ {
struct device *dev = context;
pm_wakeup_event(dev, 0);
acpi_clear_event(ACPI_EVENT_RTC); acpi_clear_event(ACPI_EVENT_RTC);
acpi_disable_event(ACPI_EVENT_RTC, 0); acpi_disable_event(ACPI_EVENT_RTC, 0);
return ACPI_INTERRUPT_HANDLED; return ACPI_INTERRUPT_HANDLED;
} }
static inline void rtc_wake_setup(void) static inline void rtc_wake_setup(struct device *dev)
{ {
acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL); acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, dev);
/* /*
* After the RTC handler is installed, the Fixed_RTC event should * After the RTC handler is installed, the Fixed_RTC event should
* be disabled. Only when the RTC alarm is set will it be enabled. * be disabled. Only when the RTC alarm is set will it be enabled.
...@@ -950,7 +953,7 @@ cmos_wake_setup(struct device *dev) ...@@ -950,7 +953,7 @@ cmos_wake_setup(struct device *dev)
if (acpi_disabled) if (acpi_disabled)
return; return;
rtc_wake_setup(); rtc_wake_setup(dev);
acpi_rtc_info.wake_on = rtc_wake_on; acpi_rtc_info.wake_on = rtc_wake_on;
acpi_rtc_info.wake_off = rtc_wake_off; acpi_rtc_info.wake_off = rtc_wake_off;
......
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