arm linux 电源管理 platform 挂起函数,arm电源管理(2)--core function(转载)

这部分说明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,

};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值