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
This commit is contained in:
Коммит
4d578573b8
|
@ -182,41 +182,66 @@ EXPORT_SYMBOL(acpi_bus_get_private_data);
|
|||
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)
|
||||
{
|
||||
int result = 0;
|
||||
acpi_status status = 0;
|
||||
unsigned long long psc = 0;
|
||||
int result = ACPI_STATE_UNKNOWN;
|
||||
|
||||
if (!device || !state)
|
||||
return -EINVAL;
|
||||
|
||||
*state = ACPI_STATE_UNKNOWN;
|
||||
if (!device->flags.power_manageable) {
|
||||
/* TBD: Non-recursive algorithm for walking up hierarchy. */
|
||||
*state = device->parent ?
|
||||
device->parent->power.state : ACPI_STATE_D0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
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 (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;
|
||||
*state = (int)psc;
|
||||
}
|
||||
} else {
|
||||
/* TBD: Non-recursive algorithm for walking up hierarchy. */
|
||||
*state = device->parent ?
|
||||
device->parent->power.state : ACPI_STATE_D0;
|
||||
}
|
||||
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is D%d\n",
|
||||
device->pnp.bus_id, *state));
|
||||
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;
|
||||
|
||||
out:
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is %s\n",
|
||||
device->pnp.bus_id, state_string(*state)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -234,13 +259,14 @@ static int __acpi_bus_set_power(struct acpi_device *device, int state)
|
|||
/* Make sure this is a valid target state */
|
||||
|
||||
if (state == device->power.state) {
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at D%d\n",
|
||||
state));
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at %s\n",
|
||||
state_string(state)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
if (device->parent && (state < device->parent->power.state)) {
|
||||
|
@ -294,13 +320,13 @@ static int __acpi_bus_set_power(struct acpi_device *device, int state)
|
|||
end:
|
||||
if (result)
|
||||
printk(KERN_WARNING PREFIX
|
||||
"Device [%s] failed to transition to D%d\n",
|
||||
device->pnp.bus_id, state);
|
||||
"Device [%s] failed to transition to %s\n",
|
||||
device->pnp.bus_id, state_string(state));
|
||||
else {
|
||||
device->power.state = state;
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
||||
"Device [%s] transitioned to D%d\n",
|
||||
device->pnp.bus_id, state));
|
||||
"Device [%s] transitioned to %s\n",
|
||||
device->pnp.bus_id, state_string(state)));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -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
|
||||
* 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;
|
||||
if (list->count < 1)
|
||||
continue;
|
||||
|
|
|
@ -1567,6 +1567,7 @@ static int acpi_bus_scan_fixed(void)
|
|||
ACPI_BUS_TYPE_POWER_BUTTON,
|
||||
ACPI_STA_DEFAULT,
|
||||
&ops);
|
||||
device_init_wakeup(&device->dev, true);
|
||||
}
|
||||
|
||||
if ((acpi_gbl_FADT.flags & ACPI_FADT_SLEEP_BUTTON) == 0) {
|
||||
|
|
|
@ -57,6 +57,7 @@ MODULE_PARM_DESC(gts, "Enable evaluation of _GTS on suspend.");
|
|||
MODULE_PARM_DESC(bfs, "Enable evaluation of _BFS on resume".);
|
||||
|
||||
static u8 sleep_states[ACPI_S_STATE_COUNT];
|
||||
static bool pwr_btn_event_pending;
|
||||
|
||||
static void acpi_sleep_tts_switch(u32 acpi_state)
|
||||
{
|
||||
|
@ -184,6 +185,14 @@ static int acpi_pm_prepare(void)
|
|||
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.
|
||||
*
|
||||
|
@ -192,6 +201,7 @@ static int acpi_pm_prepare(void)
|
|||
*/
|
||||
static void acpi_pm_finish(void)
|
||||
{
|
||||
struct device *pwr_btn_dev;
|
||||
u32 acpi_state = acpi_target_sleep_state;
|
||||
|
||||
acpi_ec_unblock_transactions();
|
||||
|
@ -209,6 +219,23 @@ static void acpi_pm_finish(void)
|
|||
acpi_set_firmware_waking_vector((acpi_physical_address) 0);
|
||||
|
||||
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)
|
|||
/* ACPI 3.0 specs (P62) says that it's the responsibility
|
||||
* of the OSPM to clear the status bit [ implying that the
|
||||
* 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_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
|
||||
|
@ -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.
|
||||
*/
|
||||
if (acpi_target_sleep_state == ACPI_STATE_S0 ||
|
||||
(device_may_wakeup(dev) &&
|
||||
adev->wakeup.sleep_state <= acpi_target_sleep_state)) {
|
||||
(device_may_wakeup(dev) && adev->wakeup.flags.valid &&
|
||||
adev->wakeup.sleep_state >= acpi_target_sleep_state)) {
|
||||
acpi_status status;
|
||||
|
||||
acpi_method[3] = 'W';
|
||||
|
|
|
@ -910,14 +910,17 @@ static inline int cmos_poweroff(struct device *dev)
|
|||
|
||||
static u32 rtc_handler(void *context)
|
||||
{
|
||||
struct device *dev = context;
|
||||
|
||||
pm_wakeup_event(dev, 0);
|
||||
acpi_clear_event(ACPI_EVENT_RTC);
|
||||
acpi_disable_event(ACPI_EVENT_RTC, 0);
|
||||
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
|
||||
* be disabled. Only when the RTC alarm is set will it be enabled.
|
||||
|
@ -950,7 +953,7 @@ cmos_wake_setup(struct device *dev)
|
|||
if (acpi_disabled)
|
||||
return;
|
||||
|
||||
rtc_wake_setup();
|
||||
rtc_wake_setup(dev);
|
||||
acpi_rtc_info.wake_on = rtc_wake_on;
|
||||
acpi_rtc_info.wake_off = rtc_wake_off;
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче