第04课第1节_runtime_PM框架

         Linux系统有上图所示的两种电源管理模型,一种是系统睡眠模型,控制系统里所有设备统统睡眠或者运行;runtime电源管理模型,在系统运行的过程中单独控制某个设备睡眠或者唤醒。在嵌入式系统里面,只有这两种电源管理模型有机地结合在一起,才能最大限度地省电。本节课讲解runtime电源管理模型。

 

 

 runtime PM (Runtime Power Management)

1. runtime PM流程

怎样动态地打开或关闭设备的电源(在系统运行的情况下)?最简单的方法:

在驱动程序里,在open函数中打开电源,在close函数中关闭电源。

 

上述方法有一个缺点: 多个APP使用该设备时可能造成干扰

解决方法:给驱动添加使用计数值: 当该计数大于0时打开电源, 等于0时关闭电源

 

一般打开与关闭电源的操作会在ioctrl函数中执行。

runtime PM只是提供辅助函数, 比如:

1. 增加计数/减少计数

2. 使能runtime pm

至于怎么使用,由驱动程序和应用程序决定。

 

 

内核驱动示例: drivers\input\misc\bma150.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类型的结构体bma150_pm(driver结构体的成员)里提供了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

           

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

           

            // 成功时,给父亲的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);

       

        wake_up_all(&dev->power.wait_queue);

 

 

 

以上睡眠流程中只调用到了runtime_idle,并没有调用到runtime_suspend,那runtime_suspend是怎么被调用的呢?分析bma150.c可知。

bma150.c : i2c_bus_type -> pm_generic_runtime_idle -> pm_runtime_suspend -> __pm_runtime_suspend(dev, 0);

           -> rpm_suspend(dev, rpmflags);  

如果设备不提供runtime_idle, 则最终会调用runtime_suspend。

 

 

 

 

 

如何使用runtime PM:

  1. 驱动程序提供接口, APP来调用。比如在bma150.c驱动程序中,在open和close函数中分别有pm_runtime_get_sync/ pm_runtime_put_sync函数,APP调用open/close函数即可实现电源的开关。
  2. 直接操作/sys/devices/.../power/control这个文件:

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

猜你喜欢

转载自blog.csdn.net/hahaha_2017/article/details/81208094