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()

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

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

    设备电源管理 Copyright (c) 2010 Rafael J. Wysocki<rjw@sisk.pl>, Novell Inc. Copyright (c) 2010 Alan ...

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

    新版linux系统设备架构中关于电源管理方式的变更 based on linux-2.6.32 一.设备模型各数据结构中电源管理的部分 linux的设备模型通过诸多结构体来联合描述,如struct d ...

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

    1. Based on linux2.6.32,  only for mem(SDR) 2. 有兴趣请先参考阅读: 电源管理方案APM和ACPI比较.doc Linux系统的休眠与唤醒简介.doc 3 ...

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

    在Linux中,休眠主要分三个主要的步骤:(1)冻结用户态进程和内核态任务:(2)调用注册的设备的suspend的回调函数:(3)按照注册顺序休眠核心设备和使CPU进入休眠态.       冻结进程是 ...

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

    电源管理芯片可以为多设备供电,且这些设备电压电流有所同.为这些设备提供的稳压器代码模型即为regulator. 下面通过下面三个过程分析regulartor供电机制: 1.分析regulator结构体 ...

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

    三.pm_test属性文件读写 int pm_test_level = TEST_NONE; static const char * const  pm_tests[__TEST_AFTER_LAST ...

  7. I/O模型之二:Linux IO模式及 select、poll、epoll详解

    目录: <I/O模型之一:Unix的五种I/O模型> <I/O模型之二:Linux IO模式及 select.poll.epoll详解> <I/O模型之三:两种高性能 I ...

  8. Linux NFS服务器的安装与配置方法(图文详解)

    这篇文章主要介绍了Linux NFS服务器的安装与配置方法(图文详解),需要的朋友可以参考下(http://xb.xcjl0834.com) 一.NFS服务简介 NFS 是Network File S ...

  9. (转)Linux下select, poll和epoll IO模型的详解

    Linux下select, poll和epoll IO模型的详解 原文:http://blog.csdn.net/tianmohust/article/details/6677985 一).Epoll ...

随机推荐

  1. 添加数据时候获取自增的ID

    create database dbDemo go use dbDemo go create table tdstudent { id int primary key identity(1,1), n ...

  2. php中utf8 与utf-8 与utf8 无BOM

    utf8 与utf-8 相信很多程序员刚开始也会有这样的疑惑,如题,我也是.    其实,他们可以这样来区分.    一.在php和html中设置编码,请尽量统一写成“UTF-8”,这才是标准写法,而 ...

  3. ubuntu14.04配置lnmp

    看到了一片讲解ubuntu下安装lnmp的文章,跟着一步步的来,竟然很顺利的成功了,将文章复制如下,原著勿怪 一.操作步骤 1.安装Nginx sudo apt-get install update ...

  4. hdu 1427 dfs

    速算24点 题意:随机给你四张牌,包括 A(1),2,3,4,5,6,7,8,9,10,J(11),Q(12),K(13).要求只用'+','-','*','/'运算符以及括号改变运算 顺序,使得最终 ...

  5. 在Linux系详解Linux bash中的变量

    (大讲台:国内首个it在线教育混合式自适应学习) 统中进行日常运维或者是编写脚本时,变量是再熟悉不过的了,但这些变量都有哪些类型,具体的用法又有哪些差异呢?本文整理分享给大家: 一.bash变量类型: ...

  6. python 批量修改图片大小

    一个文件夹下面有好多图片格式是jpg大小是1920*1080,把它们处理成1280*720并按原先图片的名保存在另一路径下这里首先要找到给定路径下所有的图片文件,然后在修改图片文件的大小,这里用到PI ...

  7. 问题分享:ActiveX component can't create object: "MSComDlg.CommonDialog"

    问题描述: 修改一个前辈的代码,在我自己电脑上面运行的很好,但是放到要用户电脑(win7 x64)上面却跑不了,报个如题的错误. 查了下是COMDLG32.OCX的问题,用到控件的地方是: Dim o ...

  8. c++中的static关键字

    1.在函数体中,一个被声明为静态的变量在这一函数被调用的过程中维持其值不变. 2.在模块内(但在函数外),比如在某一个C源文件内,一个被声明为静态的变量可以被该模块内的所有函数调用,但不能被模块外的函 ...

  9. web前端开发分享-css,js进阶篇

    一,css进阶篇: 等css哪些事儿看了两三遍之后,需要对看过的知识综合应用,这时候需要大量的实践 经验, 简单的想法:把qq首页全屏另存为jpg然后通过ps工具切图结合css转换成html,有无 从 ...

  10. 安装完QQ必须要删除掉的几个恐怖文件

    安装完QQ必须要删除掉的几个恐怖文件 感谢 QQ很可怕 的投递 很多关注自己电脑硬件温度的朋友,一般都懂得去查看什么进程占用CPU较高,可能发现过有这么几个进程的CPU占用会有时莫名其妙的非常之高,它 ...