Commit f5268fce authored by Patrick Mochel's avatar Patrick Mochel

Merge osdl.org:/home/mochel/src/kernel/linux-2.5-virgin

into osdl.org:/home/mochel/src/kernel/linux-2.5-power
parents be974925 0e59015e
......@@ -76,6 +76,7 @@ int device_pm_add(struct device * dev)
pr_debug("PM: Adding info for %s:%s\n",
dev->bus ? dev->bus->name : "No Bus", dev->kobj.name);
atomic_set(&dev->power.pm_users,0);
down(&dpm_sem);
list_add_tail(&dev->power.entry,&dpm_active);
device_pm_set_parent(dev,dev->parent);
......
......@@ -186,9 +186,36 @@ 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,
};
struct pm_ops {
int (*prepare)(u32 state);
int (*enter)(u32 state);
int (*finish)(u32 state);
};
extern void pm_set_ops(struct pm_ops *);
extern int pm_suspend(u32 state);
/*
* Device power management
*/
struct device;
struct dev_pm_info {
......
......@@ -8,6 +8,7 @@
*
*/
#include <linux/suspend.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/errno.h>
......@@ -15,42 +16,194 @@
#include <linux/pm.h>
static DECLARE_MUTEX(pm_sem);
static int standby(void)
static struct pm_ops * pm_ops = NULL;
/**
* pm_set_ops - Set the global power method table.
* @ops: Pointer to ops structure.
*/
void pm_set_ops(struct pm_ops * ops)
{
return 0;
down(&pm_sem);
pm_ops = ops;
up(&pm_sem);
}
static int suspend(void)
/**
* pm_suspend_standby - Enter 'standby' state.
*
* 'standby' is also known as 'Power-On Suspend'. Here, we power down
* devices, disable interrupts, and enter the state.
*/
static int pm_suspend_standby(void)
{
return 0;
int error = 0;
unsigned long flags;
if (!pm_ops || !pm_ops->enter)
return -EPERM;
if ((error = device_pm_power_down(PM_SUSPEND_STANDBY)))
goto Done;
local_irq_save(flags);
error = pm_ops->enter(PM_SUSPEND_STANDBY);
local_irq_restore(flags);
device_pm_power_up();
Done:
return error;
}
static int hibernate(void)
/**
* pm_suspend_mem - Enter suspend-to-RAM state.
*
* Identical to pm_suspend_standby() - we power down devices, disable
* interrupts, and enter the low-power state.
*/
static int pm_suspend_mem(void)
{
int error = 0;
unsigned long flags;
if (!pm_ops || !pm_ops->enter)
return -EPERM;
if ((error = device_pm_power_down(PM_SUSPEND_STANDBY)))
goto Done;
local_irq_save(flags);
error = pm_ops->enter(PM_SUSPEND_STANDBY);
local_irq_restore(flags);
device_pm_power_up();
Done:
return error;
}
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 enter_state(struct pm_state * state)
/**
* suspend_prepare - Do prep work before entering low-power state.
* @state: State we're entering.
*
* This is common code that is called for each state that we're
* entering. Allocate a console, stop all processes, then make sure
* the platform can enter the requested state.
*/
static int suspend_prepare(u32 state)
{
return state->fn();
int error = 0;
pm_prepare_console();
if (freeze_processes()) {
error = -EAGAIN;
goto Thaw;
}
if (pm_ops && pm_ops->prepare) {
if ((error = pm_ops->prepare(state)))
goto Thaw;
}
Done:
pm_restore_console();
return error;
Thaw:
thaw_processes();
goto Done;
}
/**
* suspend_finish - Do final work before exiting suspend sequence.
* @state: State we're coming out of.
*
* Call platform code to clean up, restart processes, and free the
* console that we've allocated.
*/
static void suspend_finish(u32 state)
{
if (pm_ops && pm_ops->finish)
pm_ops->finish(state);
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(u32 state)
{
int error;
struct pm_state * s = &pm_states[state];
if (down_trylock(&pm_sem))
return -EBUSY;
if ((error = suspend_prepare(state)))
goto Unlock;
if ((error = device_pm_suspend(state)))
goto Finish;
error = s->fn();
device_pm_resume();
Finish:
suspend_finish(state);
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(state);
return -EINVAL;
}
decl_subsys(power,NULL,NULL);
......@@ -69,8 +222,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.
......@@ -87,22 +240,25 @@ static ssize_t state_show(struct subsystem * subsys, char * buf)
return (s - buf);
}
static ssize_t state_store(struct subsystem * s, const char * buf, size_t n)
static ssize_t state_store(struct subsystem * subsys, const char * buf, size_t n)
{
struct pm_state * state;
u32 state;
struct pm_state * s;
int error;
char * end = strchr(buf,'\n');
if (end)
*end = '\0';
for (state = &pm_states[0]; state; state++) {
if (!strcmp(buf,state->name))
for (state = 0; state < PM_SUSPEND_MAX; state++) {
s = &pm_states[state];
if (s->name && !strcmp(buf,s->name))
break;
}
if (!state)
return -EINVAL;
error = enter_state(state);
if (s)
error = enter_state(state);
else
error = -EINVAL;
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