Commit 47d696a9 authored by Patrick Mochel's avatar Patrick Mochel

[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.
parent adc58de4
...@@ -186,9 +186,31 @@ static inline void pm_dev_idle(struct pm_dev *dev) {} ...@@ -186,9 +186,31 @@ static inline void pm_dev_idle(struct pm_dev *dev) {}
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
/*
* Callbacks for platform drivers to implement.
*/
extern void (*pm_idle)(void); extern void (*pm_idle)(void);
extern void (*pm_power_off)(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 device;
struct dev_pm_info { struct dev_pm_info {
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
* *
*/ */
#include <linux/suspend.h>
#include <linux/kobject.h> #include <linux/kobject.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/errno.h> #include <linux/errno.h>
...@@ -15,42 +16,114 @@ ...@@ -15,42 +16,114 @@
#include <linux/pm.h> #include <linux/pm.h>
int (*pm_power_down)(u32 state) = NULL;
static int standby(void)
static DECLARE_MUTEX(pm_sem);
static int pm_suspend_standby(void)
{ {
return 0; return 0;
} }
static int suspend(void) static int pm_suspend_mem(void)
{ {
return 0; return 0;
} }
static int hibernate(void) static int pm_suspend_disk(void)
{ {
return 0; return 0;
} }
#define decl_state(_name) \ #define decl_state(_name) \
{ .name = __stringify(_name), .fn = _name } { .name = __stringify(_name), .fn = pm_suspend_##_name }
struct pm_state { struct pm_state {
char * name; char * name;
int (*fn)(void); int (*fn)(void);
} pm_states[] = { } pm_states[] = {
decl_state(standby), [PM_SUSPEND_STANDBY] = decl_state(standby),
decl_state(suspend), [PM_SUSPEND_MEM] = decl_state(mem),
decl_state(hibernate), [PM_SUSPEND_DISK] = decl_state(disk),
{ NULL }, { 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) 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); decl_subsys(power,NULL,NULL);
...@@ -69,8 +142,8 @@ static struct subsys_attribute _name##_attr = { \ ...@@ -69,8 +142,8 @@ static struct subsys_attribute _name##_attr = { \
* state - control system power state. * state - control system power state.
* *
* show() returns what states are supported, which is hard-coded to * show() returns what states are supported, which is hard-coded to
* 'standby' (Power-On Suspend), 'suspend' (Suspend-to-RAM), and * 'standby' (Power-On Suspend), 'mem' (Suspend-to-RAM), and
* 'hibernate' (Suspend-to-Disk). * 'disk' (Suspend-to-Disk).
* *
* store() accepts one of those strings, translates it into the * store() accepts one of those strings, translates it into the
* proper enumerated value, and initiates a suspend transition. * 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) ...@@ -97,12 +170,13 @@ static ssize_t state_store(struct subsystem * s, const char * buf, size_t n)
*end = '\0'; *end = '\0';
for (state = &pm_states[0]; state; state++) { for (state = &pm_states[0]; state; state++) {
if (!strcmp(buf,state->name)) if (state->name && !strcmp(buf,state->name))
break; break;
} }
if (!state) if (state)
return -EINVAL; error = enter_state(state);
error = enter_state(state); else
error = -EINVAL;
return error ? error : n; return error ? error : n;
} }
......
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