Commit 4db33583 authored by Patrick Mochel's avatar Patrick Mochel

[power] Add flag to control suspend-to-disk behavior.

Suspend-to-disk can be handled in numerous ways, some we have control over,
and others we don't. The biggest difference is whether or not the firmware
is responsible for entering a low-power state or if the platform driver is.
The two modes are incompatible, so we enable the platform driver tell the 
PM core when they register their pm_ops (via the ->pm_disk_mode) field. 

If the firmware is responsible, then it will also write memory to disk, 
while the kernel is otherwise responsible. However, a user may choose to 
use the in-kernel suspend mechanism, even if the system supports only 
the firmware mechanism. Instead of entering a low-power state, the system
will turn off (or reboot for testing). 

A sysfs file -- /sys/power/disk -- is available to set the mode to one of:

  'firmware' 
  'platform'
  'shutdown'
  'reboot'

The latter two are settable any time, and assume that one is using swsusp. 
The other two are only settable to what the platform supports.
parent 7f1ba7ac
...@@ -165,9 +165,13 @@ static int __init acpi_sleep_init(void) ...@@ -165,9 +165,13 @@ static int __init acpi_sleep_init(void)
sleep_states[i] = 1; sleep_states[i] = 1;
printk(" S%d", i); printk(" S%d", i);
} }
if (i == ACPI_STATE_S4 && acpi_gbl_FACS->S4bios_f) { if (i == ACPI_STATE_S4) {
if (acpi_gbl_FACS->S4bios_f) {
sleep_states[i] = 1; sleep_states[i] = 1;
printk(" S4bios"); printk(" S4bios");
acpi_pm_ops.pm_disk_mode = PM_DISK_FIRMWARE;
} else if (sleep_states[i])
acpi_pm_ops.pm_disk_mode = PM_DISK_PLATFORM;
} }
} }
printk(")\n"); printk(")\n");
......
...@@ -201,7 +201,17 @@ enum { ...@@ -201,7 +201,17 @@ enum {
PM_SUSPEND_MAX, PM_SUSPEND_MAX,
}; };
enum {
PM_DISK_FIRMWARE = 1,
PM_DISK_PLATFORM,
PM_DISK_SHUTDOWN,
PM_DISK_REBOOT,
PM_DISK_MAX,
};
struct pm_ops { struct pm_ops {
u32 pm_disk_mode;
int (*prepare)(u32 state); int (*prepare)(u32 state);
int (*enter)(u32 state); int (*enter)(u32 state);
int (*finish)(u32 state); int (*finish)(u32 state);
......
...@@ -20,6 +20,9 @@ static DECLARE_MUTEX(pm_sem); ...@@ -20,6 +20,9 @@ static DECLARE_MUTEX(pm_sem);
static struct pm_ops * pm_ops = NULL; static struct pm_ops * pm_ops = NULL;
static u32 pm_disk_mode = PM_DISK_SHUTDOWN;
/** /**
* pm_set_ops - Set the global power method table. * pm_set_ops - Set the global power method table.
* @ops: Pointer to ops structure. * @ops: Pointer to ops structure.
...@@ -29,6 +32,8 @@ void pm_set_ops(struct pm_ops * ops) ...@@ -29,6 +32,8 @@ void pm_set_ops(struct pm_ops * ops)
{ {
down(&pm_sem); down(&pm_sem);
pm_ops = ops; pm_ops = ops;
if (ops->pm_disk_mode && ops->pm_disk_mode < PM_DISK_MAX)
pm_disk_mode = ops->pm_disk_mode;
up(&pm_sem); up(&pm_sem);
} }
...@@ -224,6 +229,74 @@ static struct subsys_attribute _name##_attr = { \ ...@@ -224,6 +229,74 @@ static struct subsys_attribute _name##_attr = { \
.store = _name##_store, \ .store = _name##_store, \
} }
static char * pm_disk_modes[] = {
[PM_DISK_FIRMWARE] = "firmware",
[PM_DISK_PLATFORM] = "platform",
[PM_DISK_SHUTDOWN] = "shutdown",
[PM_DISK_REBOOT] = "reboot",
};
/**
* disk - Control suspend-to-disk mode
*
* Suspend-to-disk can be handled in several ways. The greatest
* distinction is who writes memory to disk - the firmware or the OS.
* If the firmware does it, we assume that it also handles suspending
* the system.
* If the OS does it, then we have three options for putting the system
* to sleep - using the platform driver (e.g. ACPI or other PM registers),
* powering off the system or rebooting the system (for testing).
*
* The system will support either 'firmware' or 'platform', and that is
* known a priori (and encoded in pm_ops). But, the user may choose
* 'shutdown' or 'reboot' as alternatives.
*
* show() will display what the mode is currently set to.
* store() will accept one of
*
* 'firmware'
* 'platform'
* 'shutdown'
* 'reboot'
*
* It will only change to 'firmware' or 'platform' if the system
* supports it (as determined from pm_ops->pm_disk_mode).
*/
static ssize_t disk_show(struct subsystem * subsys, char * buf)
{
return sprintf(buf,"%s\n",pm_disk_modes[pm_disk_mode]);
}
static ssize_t disk_store(struct subsystem * s, const char * buf, size_t n)
{
int i;
u32 mode = 0;
for (i = PM_DISK_FIRMWARE; i < PM_DISK_MAX; i++) {
if (!strcmp(buf,pm_disk_modes[i])) {
mode = i;
break;
}
}
if (mode) {
if (mode == PM_DISK_SHUTDOWN || mode == PM_DISK_REBOOT)
pm_disk_mode = mode;
else {
if (pm_ops && (mode == pm_ops->pm_disk_mode))
pm_disk_mode = mode;
else
return -EINVAL;
}
return n;
}
return -EINVAL;
}
power_attr(disk);
/** /**
* state - control system power state. * state - control system power state.
* *
...@@ -251,10 +324,6 @@ static ssize_t state_store(struct subsystem * subsys, const char * buf, size_t n ...@@ -251,10 +324,6 @@ static ssize_t state_store(struct subsystem * subsys, const char * buf, size_t n
u32 state; u32 state;
struct pm_state * s; struct pm_state * s;
int error; int error;
char * end = strchr(buf,'\n');
if (end)
*end = '\0';
for (state = 0; state < PM_SUSPEND_MAX; state++) { for (state = 0; state < PM_SUSPEND_MAX; state++) {
s = &pm_states[state]; s = &pm_states[state];
...@@ -272,6 +341,7 @@ power_attr(state); ...@@ -272,6 +341,7 @@ power_attr(state);
static struct attribute * g[] = { static struct attribute * g[] = {
&state_attr.attr, &state_attr.attr,
&disk_attr.attr,
NULL, NULL,
}; };
......
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