From 4db335837b40105b914d6eab47785638dfa65059 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Tue, 19 Aug 2003 01:26:30 -0700 Subject: [PATCH] [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. --- drivers/acpi/sleep/main.c | 10 +++-- include/linux/pm.h | 10 +++++ kernel/power/main.c | 78 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 91 insertions(+), 7 deletions(-) diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index 92c6840f6c232..5011051e6a94b 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c @@ -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"); diff --git a/include/linux/pm.h b/include/linux/pm.h index 15b3f5efa6929..70282a7943b1e 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -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); diff --git a/kernel/power/main.c b/kernel/power/main.c index 71648de7aa81a..98c16b8471447 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -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, }; -- 2.53.0