From 47d696a99c502d7f75d3cf77ec1feb21ffc0ac9f Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Mon, 18 Aug 2003 01:31:59 -0700 Subject: [PATCH] [power] Improve suspend functions. - Implement pm_suspend(), which is callable from anywhere in the kernel, and takes one of PM_SUSPEND_STANDBY PM_SUSPEND_MEM PM_SUSPEND_DISK and enters the appropriate state. - Change sysfs file to look for "standby" "mem" "disk" for what state to enter (rather than 'suspend' and 'hibernate' for the latter two). - Add pm_sem to block multiple suspend sequences happening at once. - Allocate a console and stop processes from common code before entering state. - Add pm_power_down() callback for platform drivers to implement. Will be called to actually enter the low-power state. --- include/linux/pm.h | 22 ++++++++++ kernel/power/main.c | 102 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 110 insertions(+), 14 deletions(-) diff --git a/include/linux/pm.h b/include/linux/pm.h index e4c795f71cea1..a5e4d115d1722 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -186,9 +186,31 @@ static inline void pm_dev_idle(struct pm_dev *dev) {} #endif /* CONFIG_PM */ + +/* + * Callbacks for platform drivers to implement. + */ extern void (*pm_idle)(void); extern void (*pm_power_off)(void); +enum { + PM_SUSPEND_ON, + PM_SUSPEND_STANDBY, + PM_SUSPEND_MEM, + PM_SUSPEND_DISK, + PM_SUSPEND_MAX, +}; + +extern int (*pm_power_down)(u32 state); + + +extern int pm_suspend(u32 state); + + +/* + * Device power management + */ + struct device; struct dev_pm_info { diff --git a/kernel/power/main.c b/kernel/power/main.c index 6039369648151..79301a45ea8ae 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -8,6 +8,7 @@ * */ +#include #include #include #include @@ -15,42 +16,114 @@ #include +int (*pm_power_down)(u32 state) = NULL; -static int standby(void) + +static DECLARE_MUTEX(pm_sem); + + +static int pm_suspend_standby(void) { return 0; } -static int suspend(void) +static int pm_suspend_mem(void) { return 0; } -static int hibernate(void) +static int pm_suspend_disk(void) { return 0; } #define decl_state(_name) \ - { .name = __stringify(_name), .fn = _name } + { .name = __stringify(_name), .fn = pm_suspend_##_name } struct pm_state { char * name; int (*fn)(void); } pm_states[] = { - decl_state(standby), - decl_state(suspend), - decl_state(hibernate), + [PM_SUSPEND_STANDBY] = decl_state(standby), + [PM_SUSPEND_MEM] = decl_state(mem), + [PM_SUSPEND_DISK] = decl_state(disk), { NULL }, }; +static int suspend_prepare(void) +{ + int error = 0; + + pm_prepare_console(); + + if (freeze_processes()) { + thaw_processes(); + error = -EAGAIN; + goto Done; + } + + Done: + pm_restore_console(); + return error; +} + +static void suspend_finish(void) +{ + thaw_processes(); + pm_restore_console(); +} + + +/** + * enter_state - Do common work of entering low-power state. + * @state: pm_state structure for state we're entering. + * + * Make sure we're the only ones trying to enter a sleep state. Fail + * if someone has beat us to it, since we don't want anything weird to + * happen when we wake up. + * Then, do the setup for suspend, enter the state, and cleaup (after + * we've woken up). + */ + static int enter_state(struct pm_state * state) { - return state->fn(); + int error; + + if (down_trylock(&pm_sem)) + return -EBUSY; + + if (!pm_power_down) { + error = -EPERM; + goto Unlock; + } + + if ((error = suspend_prepare())) + return error; + + error = state->fn(); + suspend_finish(); + Unlock: + up(&pm_sem); + return error; } +/** + * pm_suspend - Externally visible function for suspending system. + * @state: Enumarted value of state to enter. + * + * Determine whether or not value is within range, get state + * structure, and enter (above). + */ + +int pm_suspend(u32 state) +{ + if (state > PM_SUSPEND_ON && state < PM_SUSPEND_MAX) + return enter_state(&pm_states[state]); + return -EINVAL; +} + decl_subsys(power,NULL,NULL); @@ -69,8 +142,8 @@ static struct subsys_attribute _name##_attr = { \ * state - control system power state. * * show() returns what states are supported, which is hard-coded to - * 'standby' (Power-On Suspend), 'suspend' (Suspend-to-RAM), and - * 'hibernate' (Suspend-to-Disk). + * 'standby' (Power-On Suspend), 'mem' (Suspend-to-RAM), and + * 'disk' (Suspend-to-Disk). * * store() accepts one of those strings, translates it into the * proper enumerated value, and initiates a suspend transition. @@ -97,12 +170,13 @@ static ssize_t state_store(struct subsystem * s, const char * buf, size_t n) *end = '\0'; for (state = &pm_states[0]; state; state++) { - if (!strcmp(buf,state->name)) + if (state->name && !strcmp(buf,state->name)) break; } - if (!state) - return -EINVAL; - error = enter_state(state); + if (state) + error = enter_state(state); + else + error = -EINVAL; return error ? error : n; } -- 2.53.0