• 4、runtime电源管理模式(内核文档runtime_pm.txt有详细描述)


    系统睡眠模型是让整个系统休眠,而runtime是在系统正常工作的时候单独控制某个设备休眠和唤醒

    1. runtime PM流程
    怎样动态地打开或关闭设备的电源?最简单的方法:
    在驱动程序里,在open函数中打开电源,在close函数中关闭电源

    上述方法有一个缺点: 多个APP使用该设备时可能造成干扰
    解决方法:给驱动添加使用计数值: 当该计数大于0时打开电源, 等于0时关闭电源

    在应用程序运行过程中通过ioctl来关闭和启动电源

    ioctl: alsa

    runtime PM只是提供辅助函数, 比如:(内核文档runtime_pm.txt详细介绍了)
    1. 增加计数/减少计数
    2. 使能runtime pm电源管理

    内核驱动示例: driversinputmiscma150.c
    pm_runtime_enable (在probe函数中)/ pm_runtime_disable (在remove函数中): 使能/禁止runtime PM, 修改disable_depth变量
    pm_runtime_get_sync(在open函数中) / pm_runtime_put_sync (在close函数中): 增加/减小计数值, 并且让设备处于resume或suspend状态

    在dev_pm_ops里提供3个回调函数: runtime_suspend, runtime_resume, runtime_idle

    流程分析:
    pm_runtime_get_sync
      __pm_runtime_resume(dev, RPM_GET_PUT);
        atomic_inc(&dev->power.usage_count); // 增加使用计数
        rpm_resume(dev, rpmflags); // resume重新运行设备
          if (dev->power.disable_depth > 0) retval = -EACCES; // 该变量初值为1,要使用runtime PM, 要先pm_runtime_enable去修改disable_depth 变量
          if (!dev->power.timer_autosuspends) // 为防止设备频繁地开关,可以设置timer_autosuspends,会等待一会,如果这段时间没动作才关闭,防止关闭后又马上打开
            pm_runtime_deactivate_timer(dev);//设置这段等待时间,上面的判读应该是是否启用该功能
          if (dev->power.runtime_status == RPM_ACTIVE) { // 如果设备已经是RPM_ACTIVE,就没必要再次resume,直接返回
          // 如果设备处于RPM_RESUMING/RPM_SUSPENDING, 等待该操作完成
          // Increment the parent's usage counter and resume it if necessary(如果有父亲,就唤醒它,并增加它的计数)
          // resume设备本身: 前面4个函数被称为 subsystem-level callback(子系统级别的回调函数)
          callback = dev->pm_domain->ops.runtime_resume; 或
          callback = dev->type->pm->runtime_resume; 或
          callback = dev->class->pm->runtime_resume; 或
          callback = dev->bus->pm->runtime_resume; 或

          callback = dev->driver->pm->runtime_resume;(设备级别的回调函数)
          retval = rpm_callback(callback, dev);//调用runtime_resume
          // 成功时,给父亲的child_count加1
          if (parent)
            atomic_inc(&parent->power.child_count);
          // 唤醒其他进程
          wake_up_all(&dev->power.wait_queue);
          // 如果resume失败, 让设备进入idle状态
          if (!retval)
            rpm_idle(dev, RPM_ASYNC);

    pm_runtime_put_sync
      __pm_runtime_idle(dev, RPM_GET_PUT);//先进入空闲状态
        atomic_dec_and_test(&dev->power.usage_count) // 减小使用计数
        rpm_idle(dev, rpmflags); // 让设备进入idle状态
          rpm_check_suspend_allowed(dev); // 检查是否允许设备进入suspend状态
            if (dev->power.disable_depth > 0) //失败
            if (atomic_read(&dev->power.usage_count) > 0) // 当前的使用计数不是0,失败,表示当前有要用在使用
            if (!pm_children_suspended(dev)) // 如果的孩子不全部处于suspended(休眠), 失败
        if (dev->power.runtime_status != RPM_ACTIVE) // 如果设备本来就不处于RPM_ACTIVE,直接返回

        // 调用idle回调函数: 前4个是subsystem-level callback
        callback = dev->pm_domain->ops.runtime_idle; 或
        callback = dev->type->pm->runtime_idle; 或
        callback = dev->class->pm->runtime_idle; 或
        callback = dev->bus->pm->runtime_idle; 或
      
        callback = dev->driver->pm->runtime_idle;

        __rpm_callback(callback, dev);//调用runtime_idle

        wake_up_all(&dev->power.wait_queue);

    bma150.c : i2c_bus_type(dev->bus总线结构体) -> pm_generic_runtime_idle -> pm_runtime_suspend ->__pm_runtime_suspend(dev, 0);-> rpm_suspend(dev, rpmflags);
    如果设备不提供runtime_idle, 则最终会调用runtime_suspend

    如何使用runtime PM:
    1. 驱动程序提供接口,比如在open和close提供pm_runtime_get_sync, APP来调用open和close
    2. echo on > /sys/devices/.../power/control // 导致control_store -> pm_runtime_forbid(dev); :
                                  atomic_inc(&dev->power.usage_count);
                                  rpm_resume(dev, 0);//唤醒

    echo auto > /sys/devices/.../power/control // 导致control_store -> pm_runtime_allow(dev); :
                                atomic_dec_and_test(&dev->power.usage_count)
                                rpm_idle(dev, RPM_AUTO);//进入idle状态休眠

    control_store函数在drivers/base/power/sysfs.c中 

    2. 修改驱动程序和使用
    怎么修改?
    参考内核驱动示例: driversinputmiscma150.c
    2.1 在dev_pm_ops里提供3个回调函数: runtime_suspend, runtime_resume, runtime_idle
    2.2 对于runtime PM,默认状态下设备的状态是suspended, 如果硬件上它是运行状态,需要调用pm_runtime_set_active()来修改它的状态然后调用pm_runtime_enable()来使能runtime PM ,其使能后也是suspended休眠状态,所有需要设置状态为active
    一般是在probe函数里调用上述函数
    2.3 在对应的系统调用接口里调用: pm_runtime_get_sync / pm_runtime_put_sync : 增加/减小计数值, 并且让设备处于resume或suspend状态
    2.4 在remove函数里调用pm_runtime_disable()

    前提: 配置内核支持runtime PM
    make menuconfig
      Power management options --->
        [*] Run-time PM core functionality


    使用:
    1.
    echo on > /sys/devices/platform/mylcd/power/control(使用计数会加一)
    echo auto > /sys/devices/platform/mylcd/power/control(使用计数会减一)

    2. 在对应的系统调用接口里调用: pm_runtime_get_sync / pm_runtime_put_sync,那么就不用执行上面1中的休眠唤醒了

    (我们执行fb_test /dev/fb0的时候其也执行了open和close,所有屏幕一闪而过)
    3. autosuspend: 如果不想让设备频繁地开、关,可以使用autosuspend功能
    驱动里: 执行pm_runtime_use_autosuspend来设置启动autosuspend功能,
    put设备时, 执行这2个函数:
    pm_runtime_mark_last_busy(&lcd_dev.dev);(更新power.last_busy的状态)
    pm_runtime_put_sync_autosuspend(&lcd_dev.dev);(根据power.last_busy的状态启动定时器,一段时间后如果没有再次打开动作才休眠)

    (pm_runtime_get_sync不需要改,open的时候应该马上上电运行)

    用户空间, 执行以下命令设置时间值:
    echo 2000 > /sys/devices/platform/mylcd/power/autosuspend_delay_ms


    资料:
    http://blog.csdn.net/bingqingsuimeng/article/category/1228414
    http://os.chinaunix.net/a2006/0519/1002/000001002210.shtml
    http://www.ednchina.com/ART_44010_29_0_TA_eeda337e_3.HTM?jumpto=view_welcomead_1408610592294

    好文:
    http://blog.csdn.net/bingqingsuimeng/article/details/7935414
    http://blog.csdn.net/bingqingsuimeng/article/details/8197912
    http://blog.sina.com.cn/s/blog_a6559d920101i52j.html

  • 相关阅读:
    [日常] Go-逐行读取文本信息
    [日常] nginx的错误日志error_log设置
    [日常] nginx记录post数据
    [PHP] PHP在CLI环境下的错误日志
    [PHP] 2018年终总结
    [MySQL] INFORMATION_SCHEMA 数据库包含所有表的字段
    前端吐槽的后端接口那些事
    读《猫力乱步》 | 如果你走得够远,你也能有那么多故事
    js获取隐藏元素宽高的方法
    RequireJS使用注意地方
  • 原文地址:https://www.cnblogs.com/liusiluandzhangkun/p/8977059.html
Copyright © 2020-2023  润新知