linux驱动程序之电源管理之Run-time PM 详解(4)

Run-time PM.

每个device或者bus都会向run-time PM core注册3个callback

struct dev_pm_ops {

...

int (*runtime_suspend)(struct device *dev);

int (*runtime_resume)(struct device *dev);

int (*runtime_idle)(struct device *dev);

...

};

每个device或者bus都会有2个计数器,一个是device的usage counter,一个是device的active状态的children个数。

当这个device的两个counter都减少为0的时候。

run-time PM core就会去调用runtime_idle函数,但是这里的idle函数可不是当前device的idle函数。

代码如下:

if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle) {

spin_unlock_irq(&dev->power.lock);

dev->bus->pm->runtime_idle(dev);

spin_lock_irq(&dev->power.lock);

} else if (dev->type && dev->type->pm && dev->type->pm->runtime_idle) {

spin_unlock_irq(&dev->power.lock);

dev->type->pm->runtime_idle(dev);

spin_lock_irq(&dev->power.lock);

} else if (dev->class && dev->class->pm

&& dev->class->pm->runtime_idle) {

spin_unlock_irq(&dev->power.lock);

dev->class->pm->runtime_idle(dev);

spin_lock_irq(&dev->power.lock);

}

按照dev->bus, dev->type, dev->class的顺序去调用。

大家会问了,那runtime_suspend函数什么时候调用?

runtime_suspend函数不会被RPM core主动去调用,一般情况下是在bus,或者class的idle函数里去调用。

例如:

static int xxx_runtime_idle(struct device *dev)

{

return pm_schedule_suspend(dev, 50);

}

pm_schedule_suspend函数会去调用device里的suspend函数,调用顺序代码如下:

if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) {

spin_unlock_irq(&dev->power.lock);

retval = dev->bus->pm->runtime_suspend(dev);

spin_lock_irq(&dev->power.lock);

dev->power.runtime_error = retval;

} else if (dev->type && dev->type->pm

&& dev->type->pm->runtime_suspend) {

spin_unlock_irq(&dev->power.lock);

retval = dev->type->pm->runtime_suspend(dev);

spin_lock_irq(&dev->power.lock);

dev->power.runtime_error = retval;

} else if (dev->class && dev->class->pm

&& dev->class->pm->runtime_suspend) {

spin_unlock_irq(&dev->power.lock);

retval = dev->class->pm->runtime_suspend(dev);

spin_lock_irq(&dev->power.lock);

dev->power.runtime_error = retval;

} else {

retval = -ENOSYS;

}

发现了吧,和idle顺序是一模一样哒。当然肯定也会有不一样了,否则runtime_suspend函数没存在意义了。在跑完此dev的bus or type or class的suspend函数以后。紧接着会做一个巨艰巨的任务,就是

if (parent && !parent->power.ignore_children) {

spin_unlock_irq(&dev->power.lock);

pm_request_idle(parent);

spin_lock_irq(&dev->power.lock);

}

会去调用当前这个device的parent device的idle函数!!!

之后会去递归的往上层调用。为啥会这么做呢???

其实RPM机制总体来说就是管理总线结构和主次设备结构的电源。

假如一个bus上有2个device。这个bus首先会有一个bus_type,其次还会有一个代表bus的device(谁说bus不是device了!)首先命名以下,bus device叫做Bdev, 两个bus上的子device是dev1, dev2。dev1,dev2是Bdev的子设备,也就是说dev1,dev2的parent是Bdev。

其中bus_type里会有一套runtime_pm的三个callback,Bdev自身还有另一套runtime_pm的三个callback。

当dev1的两个counter都为零了,就会调用bus_type里的runtime_idle,一般情况下这个idle会调用pm_runtime_suspend,仅按照上面的介绍,就会调用这个bus_type里的runtime_suspend call back。之后是不是就该是最重要的那一步了?pm_request_idle(parent);pm_request_idle里的一系列操作会首先判断parent的两个counter是否为零了,因为dev2还活着呢,所以条件不满足,返回!

当dev2也来这么一套之后,再调用pm_request_idle(parent);的时候,Bdev里的runtime_idle就能跑啦。

总结一下,bus_type的runtime_系列回调函数是用来处理bus上的device函数的。而bus自己的device的函数是用来处理自己的。

因为体系结构的因素,bus套bus的情况,最后就会形成一个device大树。runtime这套机制就可以从根到树顶都能管理得到。比如:I2C device挂在I2C bus上,I2C bus controller是PCI的一个设备,因为挂在PCI上。这个PCI bus一般还是在南桥上,然后再通过南桥在跑到北桥PCI上。。。。是不是块疯了。。。。但是有这么个递归电源管理。一切搞定。

说完了睡流程了。还有醒流程。

当device调用完suspend函数后,这个device就处于了一个suspended状态。当某个device被唤醒后,就会调用pm_runtime_get_sync类似的函数。这个函数做了啥捏?通过上述的睡过程,有点脑子的人就能想出醒流程,反着来呗!!!必须从大树顶往下跑,才能最后让根伸出来。代码如下:

if (!parent && dev->parent) {

/*

* Increment the parent‘s resume counter and resume it if

* necessary.

*/

parent = dev->parent;

spin_unlock(&dev->power.lock);

pm_runtime_get_noresume(parent);

spin_lock(&parent->power.lock);

/*

* We can resume if the parent‘s run-time PM is disabled or it

* is set to ignore children.

*/

if (!parent->power.disable_depth

&& !parent->power.ignore_children) {

__pm_runtime_resume(parent, false);

if (parent->power.runtime_status != RPM_ACTIVE)

retval = -EBUSY;

}

spin_unlock(&parent->power.lock);

spin_lock(&dev->power.lock);

if (retval)

goto out;

goto repeat;

}

首先跑这个device的parent的resume函数。之后

if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) {

spin_unlock_irq(&dev->power.lock);

retval = dev->bus->pm->runtime_resume(dev);

spin_lock_irq(&dev->power.lock);

dev->power.runtime_error = retval;

} else if (dev->type && dev->type->pm

&& dev->type->pm->runtime_resume) {

spin_unlock_irq(&dev->power.lock);

retval = dev->type->pm->runtime_resume(dev);

spin_lock_irq(&dev->power.lock);

dev->power.runtime_error = retval;

} else if (dev->class && dev->class->pm

&& dev->class->pm->runtime_resume) {

spin_unlock_irq(&dev->power.lock);

retval = dev->class->pm->runtime_resume(dev);

spin_lock_irq(&dev->power.lock);

dev->power.runtime_error = retval;

} else {

retval = -ENOSYS;

}

跑的是bus的resume函数。通过这个函数进行递归,直到递归到树顶后,树顶的resume就开始run了,run完一个往下面继续传,直到我们的这一连串device的resume函数都跑完,我们的device就算醒了。

RPM常用接口如下:

void pm_runtime_init(struct device *dev);

- initialize the device run-time PM fields in ‘struct dev_pm_info‘

void pm_runtime_remove(struct device *dev);

- make sure that the run-time PM of the device will be disabled after

removing the device from device hierarchy

int pm_runtime_idle(struct device *dev);

- execute the subsystem-level idle callback for the device; returns 0 on

success or error code on failure, where -EINPROGRESS means that

->runtime_idle() is already being executed

int pm_runtime_suspend(struct device *dev);

- execute the subsystem-level suspend callback for the device; returns 0 on

success, 1 if the device‘s run-time PM status was already ‘suspended‘, or

error code on failure, where -EAGAIN or -EBUSY means it is safe to attempt

to suspend the device again in future

int pm_runtime_resume(struct device *dev);

- execute the subsystem-level resume callback for the device; returns 0 on

success, 1 if the device‘s run-time PM status was already ‘active‘ or

error code on failure, where -EAGAIN means it may be safe to attempt to

resume the device again in future, but ‘power.runtime_error‘ should be

checked additionally

int pm_request_idle(struct device *dev);

- submit a request to execute the subsystem-level idle callback for the

device (the request is represented by a work item in pm_wq); returns 0 on

success or error code if the request has not been queued up

int pm_schedule_suspend(struct device *dev, unsigned int delay);

- schedule the execution of the subsystem-level suspend callback for the

device in future, where ‘delay‘ is the time to wait before queuing up a

suspend work item in pm_wq, in milliseconds (if ‘delay‘ is zero, the work

item is queued up immediately); returns 0 on success, 1 if the device‘s PM

run-time status was already ‘suspended‘, or error code if the request

hasn‘t been scheduled (or queued up if ‘delay‘ is 0); if the execution of

->runtime_suspend() is already scheduled and not yet expired, the new

value of ‘delay‘ will be used as the time to wait

int pm_request_resume(struct device *dev);

- submit a request to execute the subsystem-level resume callback for the

device (the request is represented by a work item in pm_wq); returns 0 on

success, 1 if the device‘s run-time PM status was already ‘active‘, or

error code if the request hasn‘t been queued up

void pm_runtime_get_noresume(struct device *dev);

- increment the device‘s usage counter

int pm_runtime_get(struct device *dev);

- increment the device‘s usage counter, run pm_request_resume(dev) and

return its result

int pm_runtime_get_sync(struct device *dev);

- increment the device‘s usage counter, run pm_runtime_resume(dev) and

return its result

void pm_runtime_put_noidle(struct device *dev);

- decrement the device‘s usage counter

int pm_runtime_put(struct device *dev);

- decrement the device‘s usage counter, run pm_request_idle(dev) and return

its result

int pm_runtime_put_sync(struct device *dev);

- decrement the device‘s usage counter, run pm_runtime_idle(dev) and return

its result

void pm_runtime_enable(struct device *dev);

- enable the run-time PM helper functions to run the device bus type‘s

run-time PM callbacks described in Section 2

int pm_runtime_disable(struct device *dev);

- prevent the run-time PM helper functions from running subsystem-level

run-time PM callbacks for the device, make sure that all of the pending

run-time PM operations on the device are either completed or canceled;

returns 1 if there was a resume request pending and it was necessary to

execute the subsystem-level resume callback for the device to satisfy that

request, otherwise 0 is returned

void pm_suspend_ignore_children(struct device *dev, bool enable);

- set/unset the power.ignore_children flag of the device

int pm_runtime_set_active(struct device *dev);

- clear the device‘s ‘power.runtime_error‘ flag, set the device‘s run-time

PM status to ‘active‘ and update its parent‘s counter of ‘active‘

children as appropriate (it is only valid to use this function if

‘power.runtime_error‘ is set or ‘power.disable_depth‘ is greater than

zero); it will fail and return error code if the device has a parent

which is not active and the ‘power.ignore_children‘ flag of which is unset

void pm_runtime_set_suspended(struct device *dev);

- clear the device‘s ‘power.runtime_error‘ flag, set the device‘s run-time

PM status to ‘suspended‘ and update its parent‘s counter of ‘active‘

children as appropriate (it is only valid to use this function if

‘power.runtime_error‘ is set or ‘power.disable_depth‘ is greater than

zero)

bool pm_runtime_suspended(struct device *dev);

- return true if the device‘s runtime PM status is ‘suspended‘, or false

otherwise

void pm_runtime_allow(struct device *dev);

- set the power.runtime_auto flag for the device and decrease its usage

counter (used by the /sys/devices/.../power/control interface to

effectively allow the device to be power managed at run time)

void pm_runtime_forbid(struct device *dev);

- unset the power.runtime_auto flag for the device and increase its usage

counter (used by the /sys/devices/.../power/control interface to

effectively prevent the device from being power managed at run time)

可以中断里跑的接口:

pm_request_idle()

pm_schedule_suspend()

pm_request_resume()

pm_runtime_get_noresume()

pm_runtime_get()

pm_runtime_put_noidle()

pm_runtime_put()

pm_suspend_ignore_children()

pm_runtime_set_active()

pm_runtime_set_suspended()

pm_runtime_enable()

时间: 2024-10-14 17:06:40

linux驱动程序之电源管理之Run-time PM 详解(4)的相关文章

linux驱动程序之电源管理之linux的电源管理架构(3)

设备电源管理 Copyright (c) 2010 Rafael J. Wysocki<[email protected]>, Novell Inc. Copyright (c) 2010 Alan Stern[email protected] ************************************************************* 本文由DroidPhone翻译于2011.8.5 ***************************************

linux驱动程序之电源管理 之linux休眠与唤醒(2)

在Linux中,休眠主要分三个主要的步骤:(1)冻结用户态进程和内核态任务:(2)调用注册的设备的suspend的回调函数:(3)按照注册顺序休眠核心设备和使CPU进入休眠态.       冻结进程是内核把进程列表中所有的进程的状态都设置为停止,并且保存下所有进程的上下文.当这些进程被解冻的时候,他们是不知道自己被冻结过的,只是简单的继续执行.如何让Linux进入休眠呢?用户可以通过读写sys文件/sys /power/state 是实现控制系统进入休眠.比如: # echo standby >

linux驱动程序之电源管理之标准linux休眠与唤醒机制分析(一)

1. Based on linux2.6.32,  only for mem(SDR) 2. 有兴趣请先参考阅读: 电源管理方案APM和ACPI比较.doc Linux系统的休眠与唤醒简介.doc 3. 本文先研究标准linux的休眠与唤醒,android对这部分的增改在另一篇文章中讨论 4. 基于手上的一个项目来讨论,这里只讨论共性的地方 虽然linux支持三种省电模式:standby.suspend to ram.suspend to disk,但是在使用电池供电的手持设备上,几乎所有的方案

linux驱动程序之电源管理之新版linux系统设备架构中关于电源管理方式的变更

新版linux系统设备架构中关于电源管理方式的变更 based on linux-2.6.32 一.设备模型各数据结构中电源管理的部分 linux的设备模型通过诸多结构体来联合描述,如struct device,struct device_type,struct class, struct device_driver,struct bus_type等. @kernel/include/linux/devices.h中有这几中结构体的定义,这里只列出和PM有关的项,其余查看源码: struct d

linux驱动程序之电源管理之regulator机制流程 (1)

电源管理芯片可以为多设备供电,且这些设备电压电流有所同.为这些设备提供的稳压器代码模型即为regulator. 下面通过下面三个过程分析regulartor供电机制: 1.分析regulator结构体 2.regulator 注册过程 3.设备使用regulator过程 一.分析regulator结构体 Regulator模块用于控制系统中某些设备的电压/电流供应.在嵌入式系统(尤其是手机)中,控制耗电量很重要,直接影响到电池的续航时间.所以,如果系统中某一个模块暂时不需要使用,就可以通过reg

linux驱动程序之电源管理之标准linux休眠和唤醒机制分析(二)

三.pm_test属性文件读写 int pm_test_level = TEST_NONE; static const char * const  pm_tests[__TEST_AFTER_LAST] = { [TEST_NONE] = "none", [TEST_CORE] = "core", [TEST_CPUS] = "processors", [TEST_PLATFORM] = "platform", [TEST_D

Linux上命令的使用格式和基础命令详解

一.Linux上命令的使用格式 命令行提示符详解: 用户通过终端的命令行接口来控制操作系统,登陆后如下: [[email protected] ~]# root: 当前登录的用户 @:分隔符 localhost: 当前主机的主机名,非完整格式:此处的完整格式为:localhost.localdomain [[email protected] ~]# hostname localhost.localdomain ~:用户当前所在的目录(current directory),也称为工作目录(work

Linux IO模式及 select、poll、epoll详解

注:本文是对众多博客的学习和总结,可能存在理解错误.请带着怀疑的眼光,同时如果有错误希望能指出. 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案是不同的.所以先限定一下本文的上下文. 本文讨论的背景是Linux环境下的network IO. 一 概念说明 在进行解释之前,首先要说明几个概念: - 用户空间和内核空间 - 进程切换 - 进程的阻塞 - 文件描述符 - 缓存 I/O 用户空间与内核空间 现在操作系统都是采用虚拟存储器,那么对32

第131讲:Hadoop集群管理工具均衡器Balancer 实战详解学习笔记

第131讲:Hadoop集群管理工具均衡器Balancer 实战详解学习笔记 为什么需要均衡器呢? 随着集群运行,具体hdfs各个数据存储节点上的block可能分布得越来越不均衡,会导致运行作业时降低mapreduce的本地性. 分布式计算中精髓性的一名话:数据不动代码动.降低本地性对性能的影响是致使的,而且不能充分利用集群的资源,因为导致任务计算会集中在部分datanode上,更易导致故障. balancer是hadoop的一个守护进程.会将block从忙的datanode移动到闲的datan