Commit 32885332 authored by Patrick Mochel's avatar Patrick Mochel

[acpi] Convert sleep code to new PM infrastructure.

- Define and fill acpi_pm_ops, and register it on startup with the PM core.

- Fill methods with only ACPI-specific code, leaving device power down, etc
  up to the PM core. 
parent e3c27d65
...@@ -16,274 +16,124 @@ ...@@ -16,274 +16,124 @@
#include <acpi/acpi_drivers.h> #include <acpi/acpi_drivers.h>
#include "sleep.h" #include "sleep.h"
#define _COMPONENT ACPI_SYSTEM_COMPONENT
ACPI_MODULE_NAME ("sleep")
u8 sleep_states[ACPI_S_STATE_COUNT]; u8 sleep_states[ACPI_S_STATE_COUNT];
extern void do_suspend_lowlevel_s4bios(int); extern void do_suspend_lowlevel_s4bios(int);
extern void do_suspend_lowlevel(int); extern void do_suspend_lowlevel(int);
/** static u32 acpi_suspend_states[] = {
* acpi_system_restore_state - OS-specific restoration of state [PM_SUSPEND_ON] = ACPI_STATE_S0,
* @state: sleep state we're exiting [PM_SUSPEND_STANDBY] = ACPI_STATE_S1,
* [PM_SUSPEND_MEM] = ACPI_STATE_S3,
* Note that if we're coming back from S4, the memory image should have already [PM_SUSPEND_DISK] = ACPI_STATE_S4,
* been loaded from the disk and is already in place. (Otherwise how else would we };
* be here?).
*/
acpi_status
acpi_system_restore_state (
u32 state)
{
/* restore processor state
* We should only be here if we're coming back from STR or STD.
* And, in the case of the latter, the memory image should have already
* been loaded from disk.
*/
if (state > ACPI_STATE_S1)
acpi_restore_state_mem();
/* wait for power to come back */
mdelay(10);
/* turn all the devices back on */
device_resume(RESUME_POWER_ON);
/* enable interrupts once again */
ACPI_ENABLE_IRQS();
/* restore device context */
device_resume(RESUME_RESTORE_STATE);
if (dmi_broken & BROKEN_INIT_AFTER_S1) {
printk("Broken toshiba laptop -> kicking interrupts\n");
init_8259A(0);
}
return AE_OK;
}
/** /**
* acpi_system_save_state - save OS specific state and power down devices * acpi_pm_prepare - Do preliminary suspend work.
* @state: sleep state we're entering. * @state: suspend state we're entering.
* *
* This handles saving all context to memory, and possibly disk.
* First, we call to the device driver layer to save device state.
* Once we have that, we save whatevery processor and kernel state we
* need to memory.
*/ */
acpi_status
acpi_system_save_state(
u32 state)
{
int error = 0;
/* Send notification to devices that they will be suspended.
* If any device or driver cannot make the transition, either up
* or down, we'll get an error back.
*/
error = device_suspend(state, SUSPEND_NOTIFY);
if (error)
return AE_ERROR;
if (state < ACPI_STATE_S5) {
/* Tell devices to stop I/O and actually save their state. static int acpi_pm_prepare(u32 state)
* It is theoretically possible that something could fail, {
* so handle that gracefully.. int error = 0;
*/ u32 acpi_state = acpi_suspend_states[state];
error = device_suspend(state, SUSPEND_SAVE_STATE);
if (error) {
/* tell devices to restore state if they have
* it saved and to start taking I/O requests.
*/
device_resume(RESUME_RESTORE_STATE);
return error;
}
/* flush caches */
ACPI_FLUSH_CPU_CACHE();
/* Do arch specific saving of state. */
if (state > ACPI_STATE_S1) {
error = acpi_save_state_mem();
if (!error && (state == ACPI_STATE_S4)) if (!sleep_states[acpi_state])
error = acpi_save_state_disk(); return -EPERM;
if (error) { /* do we have a wakeup address for S2 and S3? */
device_resume(RESUME_RESTORE_STATE); /* Here, we support only S4BIOS, those we set the wakeup address */
return error; /* S4OS is only supported for now via swsusp.. */
} if (state == PM_SUSPEND_MEM || state == PM_SUSPEND_DISK) {
} if (!acpi_wakeup_address)
return -EFAULT;
acpi_set_firmware_waking_vector(
(acpi_physical_address) acpi_wakeup_address);
} }
/* disable interrupts ACPI_FLUSH_CPU_CACHE();
* Note that acpi_suspend -- our caller -- will do this once we return.
* But, we want it done early, so we don't get any suprises during
* the device suspend sequence.
*/
ACPI_DISABLE_IRQS();
/* Unconditionally turn off devices. /* Do arch specific saving of state. */
* Obvious if we enter a sleep state. if (state > PM_SUSPEND_STANDBY) {
* If entering S5 (soft off), this should put devices in a if ((error = acpi_save_state_mem()))
* quiescent state. goto Err;
*/ }
error = device_suspend(state, SUSPEND_POWER_DOWN);
/* We're pretty screwed if we got an error from this. acpi_enter_sleep_state_prep(acpi_state);
* We try to recover by simply calling our own restore_state
* function; see above for definition.
*
* If it's S5 though, go through with it anyway..
*/
if (error && state != ACPI_STATE_S5)
acpi_system_restore_state(state);
return error ? AE_ERROR : AE_OK; return 0;
Err:
acpi_set_firmware_waking_vector(0);
return error;
} }
/**************************************************************************** static int acpi_pm_enter(u32 state)
*
* FUNCTION: acpi_system_suspend
*
* PARAMETERS: %state: Sleep state to enter.
*
* RETURN: acpi_status, whether or not we successfully entered and
* exited sleep.
*
* DESCRIPTION: Perform OS-specific action to enter sleep state.
* This is the final step in going to sleep, per spec. If we
* know we're coming back (i.e. not entering S5), we save the
* processor flags. [ We'll have to save and restore them anyway,
* so we use the arch-agnostic save_flags and restore_flags
* here.] We then set the place to return to in arch-specific
* globals using arch_set_return_point. Finally, we call the
* ACPI function to write the proper values to I/O ports.
*
****************************************************************************/
acpi_status
acpi_system_suspend(
u32 state)
{ {
acpi_status status = AE_ERROR; acpi_status status = AE_OK;
unsigned long flags = 0; unsigned long flags = 0;
ACPI_FLUSH_CPU_CACHE();
local_irq_save(flags); local_irq_save(flags);
switch (state) switch (state)
{ {
case ACPI_STATE_S1: case PM_SUSPEND_STANDBY:
barrier(); barrier();
status = acpi_enter_sleep_state(state); status = acpi_enter_sleep_state(acpi_suspend_states[state]);
break; break;
case ACPI_STATE_S2: case PM_SUSPEND_MEM:
case ACPI_STATE_S3:
do_suspend_lowlevel(0); do_suspend_lowlevel(0);
break; break;
case ACPI_STATE_S4: case PM_SUSPEND_DISK:
do_suspend_lowlevel_s4bios(0); do_suspend_lowlevel_s4bios(0);
break; break;
default: default:
printk(KERN_WARNING PREFIX "don't know how to handle %d state.\n", state); return -EINVAL;
break;
} }
local_irq_restore(flags); local_irq_restore(flags);
printk(KERN_DEBUG "Back to C!\n"); printk(KERN_DEBUG "Back to C!\n");
return status; return ACPI_SUCCESS(status) ? 0 : -EFAULT;
} }
static int acpi_pm_finish(u32 state)
/**
* acpi_suspend - OS-agnostic system suspend/resume support (S? states)
* @state: state we're entering
*
*/
acpi_status
acpi_suspend (
u32 state)
{ {
acpi_status status;
/* Suspend is hard to get right on SMP. */
if (num_online_cpus() != 1)
return AE_ERROR;
/* get out if state is invalid */
if (state < ACPI_STATE_S1 || state > ACPI_STATE_S5)
return AE_ERROR;
/* Since we handle S4OS via a different path (swsusp), give up if no s4bios. */
if (state == ACPI_STATE_S4 && !acpi_gbl_FACS->S4bios_f)
return AE_ERROR;
pm_prepare_console();
/*
* TBD: S1 can be done without device_suspend. Make a CONFIG_XX
* to handle however when S1 failed without device_suspend.
*/
if (freeze_processes()) {
status = AE_ERROR;
goto Done;
}
/* do we have a wakeup address for S2 and S3? */
/* Here, we support only S4BIOS, those we set the wakeup address */
/* S4OS is only supported for now via swsusp.. */
if (state == ACPI_STATE_S2 || state == ACPI_STATE_S3 || state == ACPI_STATE_S4) {
if (!acpi_wakeup_address)
return AE_ERROR;
acpi_set_firmware_waking_vector((acpi_physical_address) acpi_wakeup_address);
}
status = acpi_system_save_state(state);
if (!ACPI_SUCCESS(status))
return status;
acpi_enter_sleep_state_prep(state);
/* disable interrupts and flush caches */
ACPI_DISABLE_IRQS();
ACPI_FLUSH_CPU_CACHE();
/* perform OS-specific sleep actions */
status = acpi_system_suspend(state);
/* Even if we failed to go to sleep, all of the devices are in an suspended
* mode. So, we run these unconditionaly to make sure we have a usable system
* no matter what.
*/
acpi_leave_sleep_state(state); acpi_leave_sleep_state(state);
acpi_system_restore_state(state);
/* make sure interrupts are enabled */ /* restore processor state
ACPI_ENABLE_IRQS(); * We should only be here if we're coming back from STR or STD.
* And, in the case of the latter, the memory image should have already
* been loaded from disk.
*/
if (state > ACPI_STATE_S1)
acpi_restore_state_mem();
/* reset firmware waking vector */ /* reset firmware waking vector */
acpi_set_firmware_waking_vector((acpi_physical_address) 0); acpi_set_firmware_waking_vector((acpi_physical_address) 0);
Done: if (dmi_broken & BROKEN_INIT_AFTER_S1) {
thaw_processes(); printk("Broken toshiba laptop -> kicking interrupts\n");
pm_restore_console(); init_8259A(0);
return status; }
return 0;
} }
static struct pm_ops acpi_pm_ops = {
.prepare = acpi_pm_prepare,
.enter = acpi_pm_enter,
.finish = acpi_pm_finish,
};
static int __init acpi_sleep_init(void) static int __init acpi_sleep_init(void)
{ {
int i = 0; int i = 0;
ACPI_FUNCTION_TRACE("acpi_system_add_fs");
if (acpi_disabled) if (acpi_disabled)
return_VALUE(0); return 0;
printk(KERN_INFO PREFIX "(supports"); printk(KERN_INFO PREFIX "(supports");
for (i=0; i<ACPI_S_STATE_COUNT; i++) { for (i=0; i<ACPI_S_STATE_COUNT; i++) {
...@@ -301,7 +151,8 @@ static int __init acpi_sleep_init(void) ...@@ -301,7 +151,8 @@ static int __init acpi_sleep_init(void)
} }
printk(")\n"); printk(")\n");
return_VALUE(0); pm_set_ops(&acpi_pm_ops);
return 0;
} }
late_initcall(acpi_sleep_init); late_initcall(acpi_sleep_init);
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