mirror of
https://github.com/Fishwaldo/linux-bl808.git
synced 2025-06-08 07:35:04 +00:00
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:
commit
4d578573b8
5 changed files with 110 additions and 39 deletions
|
@ -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) {
|
||||||
|
/* 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
|
* Get the device's power state either directly (via _PSC) or
|
||||||
* indirectly (via power resources).
|
* indirectly (via power resources).
|
||||||
*/
|
*/
|
||||||
if (device->power.flags.power_resources) {
|
if (device->power.flags.explicit_get) {
|
||||||
result = acpi_power_get_inferred_state(device, state);
|
unsigned long long psc;
|
||||||
if (result)
|
acpi_status status = acpi_evaluate_integer(device->handle,
|
||||||
return result;
|
"_PSC", NULL, &psc);
|
||||||
} else if (device->power.flags.explicit_get) {
|
|
||||||
status = acpi_evaluate_integer(device->handle, "_PSC",
|
|
||||||
NULL, &psc);
|
|
||||||
if (ACPI_FAILURE(status))
|
if (ACPI_FAILURE(status))
|
||||||
return -ENODEV;
|
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",
|
result = psc;
|
||||||
device->pnp.bus_id, *state));
|
}
|
||||||
|
/* 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;
|
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 */
|
/* 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)
|
||||||
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)
|
||||||
* 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)
|
||||||
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.");
|
||||||
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)
|
||||||
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)
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
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)
|
||||||
/* 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_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);
|
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)
|
||||||
* 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)
|
||||||
|
|
||||||
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)
|
||||||
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;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue