s3c2410 电源管理(2)--core function
R.wen
这部分说明kernel里面的电源管理的核心函数
这部分的代码在/kernel/power目录中
1. 我们在(1)中看到apm_suspend()调用以下这个函数, 我们就从这里开始
typedef int __bitwise suspend_state_t;
#define PM_SUSPEND_ON ((__force suspend_state_t) 0)
#define PM_SUSPEND_STANDBY ((__force suspend_state_t) 1)
#define PM_SUSPEND_MEM ((__force suspend_state_t) 3)
#define PM_SUSPEND_DISK ((__force suspend_state_t) 4)
#define PM_SUSPEND_MAX ((__force suspend_state_t) 5)
/**
* 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).
*/
//注意这里的注释, Externally visible function for suspending system.
int pm_suspend(suspend_state_t state)
{
//arm apm传入的是PM_SUSPEND_MEM
if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)
return enter_state(state);
return -EINVAL;
}
/**
* 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(suspend_state_t state)
{
int error;
//获得锁, 参见注释
if (down_trylock(&pm_sem))
return -EBUSY;
//挂起磁盘的请求, 不是我我们的请求
if (state == PM_SUSPEND_DISK) {
error = pm_suspend_disk();
goto Unlock;
}
//prepare阶段
pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
if ((error = suspend_prepare(state)))
goto Unlock;
//进入阶段
pr_debug("PM: Entering %s sleep\n", pm_states[state]);
error = suspend_enter(state);
//完成挂起, 恢复状态
pr_debug("PM: Finishing wakeup.\n");
suspend_finish(state);
Unlock:
up(&pm_sem);
return error;
}
2.1 准备阶段, 为状态变换做准备
/**
* 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(suspend_state_t state)
{
int error = 0;
unsigned int free_pages;
if (!pm_ops || !pm_ops->enter)
return -EPERM;
pm_prepare_console();
disable_nonboot_cpus();
if (num_online_cpus() != 1) {
error = -EPERM;
goto Enable_cpu;
}
//进程处理
if (freeze_processes()) {
error = -EAGAIN;
goto Thaw;
}
//内存处理
if ((free_pages = nr_free_pages()) < FREE_PAGE_NUMBER) {
pr_debug("PM: free some memory\n");
shrink_all_memory(FREE_PAGE_NUMBER - free_pages);
if (nr_free_pages() < FREE_PAGE_NUMBER) {
error = -ENOMEM;
printk(KERN_ERR "PM: No enough memory\n");
goto Thaw;
}
}
//调用体系结构相关的函数, 这是在系统初始化的时候注册的.
if (pm_ops->prepare) {
if ((error = pm_ops->prepare(state)))
goto Thaw;
}
//挂起设备
if ((error = device_suspend(PMSG_SUSPEND))) {
printk(KERN_ERR "Some devices failed to suspend\n");
goto Finish;
}
return 0;
Finish:
if (pm_ops->finish)
pm_ops->finish(state);
Thaw:
thaw_processes();
Enable_cpu:
enable_nonboot_cpus();
pm_restore_console();
return error;
}
2.2挂起设备
/**
* device_suspend - Save state and stop all devices in system.
* @state: Power state to put each device in.
*
* Walk the dpm_active list, call ->suspend() for each device, and move
* it to dpm_off.
* Check the return value for each. If it returns 0, then we move the
* the device to the dpm_off list. If it returns -EAGAIN, we move it to
* the dpm_off_irq list. If we get a different error, try and back out.
*
* If we hit a failure with any of the devices, call device_resume()
* above to bring the suspended devices back to life.
*
*/
int device_suspend(pm_message_t state)
{
int error = 0;
down(&dpm_sem);
down(&dpm_list_sem);
//遍历设备链表, 当一个设备被注册进系统时, 它同时会被加入到这个dpm_active队列中
while (!list_empty(&dpm_active) && error == 0) {
struct list_head * entry = dpm_active.prev;
struct device * dev = to_device(entry);
get_device(dev);
up(&dpm_list_sem);
//挂起这个设备
error = suspend_device(dev, state);
down(&dpm_list_sem);
/* Check if the device got removed */
//加入off队列, 用于以后唤醒
if (!list_empty(&dev->power.entry)) {
/* Move it to the dpm_off or dpm_off_irq list */
if (!error) {
list_del(&dev->power.entry);
list_add(&dev->power.entry, &dpm_off);
} else if (error == -EAGAIN) {
list_del(&dev->power.entry);
list_add(&dev->power.entry, &dpm_off_irq);
error = 0;
}
}
if (error)
printk(KERN_ERR "Could not suspend device %s: "
"error %d\n", kobject_name(&dev->kobj), error);
put_device(dev);
}
up(&dpm_list_sem);
if (error) { //出错了! 恢复原来的状态
/* we failed... before resuming, bring back devices from
* dpm_off_irq list back to main dpm_off list, we do want
* to call resume() on them, in case they partially suspended
* despite returning -EAGAIN
*/
while (!list_empty(&dpm_off_irq)) {
struct list_head * entry = dpm_off_irq.next;
list_del(entry);
list_add(entry, &dpm_off);
}
dpm_resume();
}
up(&dpm_sem);
return error;
}
/**
* suspend_device - Save state of one device.
* @dev: Device.
* @state: Power state device is entering.
*/
int suspend_device(struct device * dev, pm_message_t state)
{
int error = 0;
down(&dev->sem);
if (dev->power.power_state.event) {
dev_dbg(dev, "PM: suspend %d-->%d\n",
dev->power.power_state.event, state.event);
}
if (dev->power.pm_parent
&& dev->power.pm_parent->power.power_state.event) {
dev_err(dev,
"PM: suspend %d->%d, parent %s already %d\n",
dev->power.power_state.event, state.event,
dev->power.pm_parent->bus_id,
dev->power.pm_parent->power.power_state.event);
}
//保留原来的状态
dev->power.prev_state = dev->power.power_state;
if (dev->bus && dev->bus->suspend && !dev->power.power_state.event) {
dev_dbg(dev, "suspending\n");
//执行BUS的suspend, bus的suspend再去执行dev的suspend
error = dev->bus->suspend(dev, state);
}
up(&dev->sem);
return error;
}
为了说明它说如何调用bus的suspend的, 这里插入一段设备的注册过程的描述:
static int __init s3c_arch_init(void)
{
int ret;
// do the correct init for cpu
if (cpu == NULL)
panic("s3c_arch_init: NULL cpu\n");
ret = (cpu->init)();
if (ret != 0)
return ret;
//这个board是全局变量, 就是下面的smdk2440_board
if (board != NULL) {
struct platform_device **ptr = board->devices;
int i;
for (i = 0; i < board->devices_count; i++, ptr++) {
//这个就是注册设备的函数, bus为platform
ret = platform_device_register(*ptr);
if (ret) {
printk(KERN_ERR "s3c24xx: failed to add board device %s (%d) @%
p\n", (*ptr)->name, ret, *ptr);
}
}
/* mask any error, we may not need all these board
* devices */
ret = 0;
}
return ret;
}
// 定义在mach-smdk2440.c
static struct platform_device *smdk2440_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c,
&s3c_device_iis,
};
static struct s3c24xx_board smdk2440_board __initdata = {
.devices = smdk2440_devices,
.devices_count = ARRAY_SIZE(smdk2440_devices)
};
我们看到, 就是这个platform_device_register()将上面数组中的设备(这些设备在devs.c中定义)注册进
platform bus中去的.
/**
* platform_device_register - add a platform-level device
* @pdev: platform device we're adding
*
*/
int platform_device_register(struct platform_device * pdev)
{
int i, ret = 0;
if (!pdev)
return -EINVAL;
if (!pdev->dev.parent)
pdev->dev.parent = &platform_bus;
//这个dev bus被初始化为platform_bus_type, 我们只关心这里
pdev->dev.bus = &platform_bus_type;
if (pdev->id != -1)
snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%u", pdev->name, pdev->id);
else
strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);
for (i = 0; i < pdev->num_resources; i++) {
struct resource *p, *r = &pdev->resource[i];
if (r->name == NULL)
r->name = pdev->dev.bus_id;
p = r->parent;
if (!p) {
if (r->flags & IORESOURCE_MEM)
p = &iomem_resource;
else if (r->flags & IORESOURCE_IO)
p = &ioport_resource;
}
if (p && request_resource(p, r)) {
printk(KERN_ERR
"%s: failed to claim resource %d\n",
pdev->dev.bus_id, i);
ret = -EBUSY;
goto failed;
}
}
pr_debug("Registering platform device '%s'. Parent at %s\n",
pdev->dev.bus_id, pdev->dev.parent->bus_id);
ret = device_register(&pdev->dev);
if (ret == 0)
return ret;
failed:
while (--i >= 0)
if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO))
release_resource(&pdev->resource[i]);
return ret;
}
再接着看看这个结构:
struct bus_type platform_bus_type = {
.name = "platform",
.match = platform_match,
//下面两个就是电源管理用的函数
.suspend = platform_suspend,
.resume = platform_resume,
};
我们在这里就可以清楚的看到, 它是会调用设备驱动的suspend实现的.
所以说, 系统挂起是, 设备也应该做相应的工作, 由于设备的特殊性, 这些就是留在设备里面来实现了.
static int platform_suspend(struct device * dev, pm_message_t state)
{
int ret = 0;
if (dev->driver && dev->driver->suspend) {
ret = dev->driver->suspend(dev, state, SUSPEND_DISABLE);
if (ret == 0)
ret = dev->driver->suspend(dev, state, SUSPEND_SAVE_STATE);
if (ret == 0)
ret = dev->driver->suspend(dev, state, SUSPEND_POWER_DOWN);
}
return ret;
}
3. enter阶段,
完成了prepare阶段后, 就是enter阶段了,即是进入了状态变换阶段了.
这就是:
static int suspend_enter(suspend_state_t state)
{
int error = 0;
unsigned long flags;
local_irq_save(flags);
if ((error = device_power_down(PMSG_SUSPEND))) {
printk(KERN_ERR "Some devices failed to power down\n");
goto Done;
}
error = pm_ops->enter(state);
device_power_up();
Done:
local_irq_restore(flags);
return error;
}
我们看到,所有的工作都在pm_ops->enter(state)中去做了.
它完成了suspend/resume的状态转换.
struct pm_ops {
suspend_disk_method_t pm_disk_mode;
int (*prepare)(suspend_state_t state);
int (*enter)(suspend_state_t state);
int (*finish)(suspend_state_t state);
};
这个结构在系统初始化是会初始化, 且每个体系结构的pm_os是不同的,
如s3c24xx的为:
/*
* Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk.
*/
static struct pm_ops s3c2410_pm_ops = {
.pm_disk_mode = PM_DISK_FIRMWARE,
.prepare = s3c2410_pm_prepare,
.enter = s3c2410_pm_enter,
.finish = s3c2410_pm_finish,
};
定义在arch/arm/mach-s3c2410/pm.c中.
我们在下一节再细看这个pm的实现.
4. finish阶段
/**
* 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. This is not called for suspend-to-disk.
*/
我们看到, 这里是enter_state的逆操作.
static void suspend_finish(suspend_state_t state)
{
device_resume();
if (pm_ops && pm_ops->finish)
pm_ops->finish(state); //体系相关的操作
thaw_processes();
enable_nonboot_cpus();
pm_restore_console();
}
5. 系统resume
/**
* device_resume - Restore state of each device in system.
*
* Walk the dpm_off list, remove each entry, resume the device,
* then add it to the dpm_active list.
*/
void device_resume(void)
{
down(&dpm_sem);
dpm_resume();
up(&dpm_sem);
}
void dpm_resume(void)
{
down(&dpm_list_sem);
while(!list_empty(&dpm_off)) { //在device_suspend()入列的dev
struct list_head * entry = dpm_off.next;
struct device * dev = to_device(entry);
get_device(dev);
list_del_init(entry);
list_add_tail(entry, &dpm_active);
up(&dpm_list_sem);
if (!dev->power.prev_state.event)
resume_device(dev); //对每个设备
down(&dpm_list_sem);
put_device(dev);
}
up(&dpm_list_sem);
}
/**
* resume_device - Restore state for one device.
* @dev: Device.
*
*/
int resume_device(struct device * dev)
{
int error = 0;
down(&dev->sem);
if (dev->power.pm_parent
&& dev->power.pm_parent->power.power_state.event) {
dev_err(dev, "PM: resume from %d, parent %s still %d\n",
dev->power.power_state.event,
dev->power.pm_parent->bus_id,
dev->power.pm_parent->power.power_state.event);
}
if (dev->bus && dev->bus->resume) {
dev_dbg(dev,"resuming\n");
error = dev->bus->resume(dev); //bus的resume, 相对应我们说的bus的suspend
}
up(&dev->sem);
return error;
}
6. 体系相关的操作,
到这里, 我们只是剩下如下这些函数操作没说了, 这是真正执行硬件指令的操作.
/*
* Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk.
*/
static struct pm_ops s3c2410_pm_ops = {
.pm_disk_mode = PM_DISK_FIRMWARE,
.prepare = s3c2410_pm_prepare,
.enter = s3c2410_pm_enter,
.finish = s3c2410_pm_finish,
};