]> git.hungrycats.org Git - linux/commitdiff
[power] Add flag to control suspend-to-disk behavior.
authorPatrick Mochel <mochel@osdl.org>
Tue, 19 Aug 2003 08:26:30 +0000 (01:26 -0700)
committerPatrick Mochel <mochel@osdl.org>
Tue, 19 Aug 2003 08:26:30 +0000 (01:26 -0700)
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.

drivers/acpi/sleep/main.c
include/linux/pm.h
kernel/power/main.c

index 92c6840f6c2327e6f49a8370f89087276f271174..5011051e6a94bf1066fcb72ac52e3e5eb6f722a2 100644 (file)
@@ -165,9 +165,13 @@ static int __init acpi_sleep_init(void)
                        sleep_states[i] = 1;
                        printk(" S%d", i);
                }
-               if (i == ACPI_STATE_S4 && acpi_gbl_FACS->S4bios_f) {
-                       sleep_states[i] = 1;
-                       printk(" S4bios");
+               if (i == ACPI_STATE_S4) {
+                       if (acpi_gbl_FACS->S4bios_f) {
+                               sleep_states[i] = 1;
+                               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");
index 15b3f5efa69295b152cd5df967f83c07b3f7db73..70282a7943b1e819e0a7f15420816c94c5a6dc80 100644 (file)
@@ -201,7 +201,17 @@ enum {
        PM_SUSPEND_MAX,
 };
 
+enum {
+       PM_DISK_FIRMWARE = 1,
+       PM_DISK_PLATFORM,
+       PM_DISK_SHUTDOWN,
+       PM_DISK_REBOOT,
+       PM_DISK_MAX,
+};
+
+
 struct pm_ops {
+       u32     pm_disk_mode;
        int (*prepare)(u32 state);
        int (*enter)(u32 state);
        int (*finish)(u32 state);
index 71648de7aa81a7267c3e3d5f84ae911274aa2571..98c16b847144772d74788b790af4b759ad877918 100644 (file)
@@ -20,6 +20,9 @@ static DECLARE_MUTEX(pm_sem);
 
 static struct pm_ops * pm_ops = NULL;
 
+static u32 pm_disk_mode = PM_DISK_SHUTDOWN;
+
+
 /**
  *     pm_set_ops - Set the global power method table. 
  *     @ops:   Pointer to ops structure.
@@ -29,6 +32,8 @@ void pm_set_ops(struct pm_ops * ops)
 {
        down(&pm_sem);
        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);
 }
 
@@ -224,6 +229,74 @@ static struct subsys_attribute _name##_attr = {    \
        .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.
  *
@@ -251,10 +324,6 @@ static ssize_t state_store(struct subsystem * subsys, const char * buf, size_t n
        u32 state;
        struct pm_state * s;
        int error;
-       char * end = strchr(buf,'\n');
-       
-       if (end)
-               *end = '\0';
 
        for (state = 0; state < PM_SUSPEND_MAX; state++) {
                s = &pm_states[state];
@@ -272,6 +341,7 @@ power_attr(state);
 
 static struct attribute * g[] = {
        &state_attr.attr,
+       &disk_attr.attr,
        NULL,
 };