一:runtime机制说明 转载:http://blog.csdn.net/linux_devices_driver/article/details/38092115 何为runtime机制?也就是系统在非睡眠状态,设备在空闲时可以进入runtime suspend状态同时不依赖系统wake_lock机制,非空闲时执行runtime resume使得设备进入正常工作状态。 主要代码放在Runtime.c (drivers\base\power)中,同时附带的Runtime_pm.txt (documentation\power)有详细 说明。要使得设备可以进入runtime_idle与runtime_suspend必须满足device的2个参数usage_count与child_count同时为0。 1:操作usage_count参数通常在HOST控制器驱动中,使用辅助runtime函数来完成。 2:操作child_count通常由子设备来完成父设备child_count的增加与减少。child_count可以理解该设备活跃的子设备的个数。 通常由子设备睡后来让父设备进入休眠,依次递归进行。
二:下面重点分析一下rpm_suspend与rpm_resume static int rpm_suspend(struct device *dev, int rpmflags) __releases(&dev->power.lock) __acquires(&dev->power.lock) { int (*callback)(struct device *); struct device *parent = NULL; struct rpm_qos_data qos; int retval; trace_rpm_suspend(dev, rpmflags); repeat: retval = rpm_check_suspend_allowed(dev); //在这里检查usage_count与child_count,当非0存在时将使得该函数直接return。 if (retval < 0) ; /* Conditions are wrong. */ /* Synchronous suspends are not allowed in the RPM_RESUMING state. */ else if (dev->power.runtime_status == RPM_RESUMING && !(rpmflags & RPM_ASYNC)) retval = -EAGAIN; if (retval) goto out; ---------- /* Carry out an asynchronous or a synchronous suspend. */ if (rpmflags & RPM_ASYNC) { //异步情况,提交一个工作队列后退出 dev->power.request = (rpmflags & RPM_AUTO) ? RPM_REQ_AUTOSUSPEND : RPM_REQ_SUSPEND;//当该suspend发起者支持auto suspend时rpmflags为RPM_REQ_AUTOSUSPEND,通常auto带有delay time时延。 if (!dev->power.request_pending) { dev->power.request_pending = true; queue_work(pm_wq, &dev->power.work); } goto out; } -------------- /* 下面是选择回调函数,如果设备是挂在platform下面的,将执行pm_generic_runtime_suspend()后执行device runtime suspend callback 这里的callback也就是我们在驱动里面注册的idle,suspend,resume callback; static const struct dev_pm_ops platform_dev_pm_ops = { .runtime_suspend = pm_generic_runtime_suspend, .runtime_resume = pm_generic_runtime_resume, .runtime_idle = pm_generic_runtime_idle, USE_PLATFORM_PM_SLEEP_OPS }; int pm_generic_runtime_suspend(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int ret; ret = pm && pm->runtime_suspend ? pm->runtime_suspend(dev) : 0; return ret; } */ if (dev->pm_domain) callback = dev->pm_domain->ops.runtime_suspend; else if (dev->type && dev->type->pm) callback = dev->type->pm->runtime_suspend; else if (dev->class && dev->class->pm) callback = dev->class->pm->runtime_suspend; else if (dev->bus && dev->bus->pm) callback = dev->bus->pm->runtime_suspend; else callback = NULL; if (!callback && dev->driver && dev->driver->pm) callback = dev->driver->pm->runtime_suspend; retval = rpm_callback(callback, dev);//执行回调函数。 if (retval) goto fail; no_callback: __update_runtime_status(dev, RPM_SUSPENDED); pm_runtime_deactivate_timer(dev); if (dev->parent) { parent = dev->parent; atomic_add_unless(&parent->power.child_count, -1, 0); }//如果该设备父设备存在,由于上面该设备已经执行到runtime suspend callback了,那么这时将父设备的子设备减一。 ---------- /* Maybe the parent is now able to suspend. */ if (parent && !parent->power.ignore_children && !dev->power.irq_safe) { spin_unlock(&dev->power.lock); spin_lock(&parent->power.lock); rpm_idle(parent, RPM_ASYNC); spin_unlock(&parent->power.lock); spin_lock(&dev->power.lock); }//尝试让父设备进入idle。 ------------- }
static int rpm_resume(struct device *dev, int rpmflags) __releases(&dev->power.lock) __acquires(&dev->power.lock) { int (*callback)(struct device *); struct device *parent = NULL; int retval = 0; trace_rpm_resume(dev, rpmflags); repeat: if (dev->power.runtime_error) retval = -EINVAL; else if (dev->power.disable_depth > 0)//进行条件判断,disable_depth为禁止runtime,在pm_runtime_enable()时会置为0; retval = -EACCES; if (retval) goto out; -------- if (dev->power.runtime_status == RPM_ACTIVE) {//当前状态为active时直接推出; retval = 1; goto out; } --------- /* Carry out an asynchronous or a synchronous resume. */ if (rpmflags & RPM_ASYNC) { dev->power.request = RPM_REQ_RESUME; if (!dev->power.request_pending) { dev->power.request_pending = true; queue_work(pm_wq, &dev->power.work);//异步情况,提交一个工作队列后退出 } retval = 0; goto out; } if (!parent && dev->parent) { /* * Increment the parent's usage counter and resume it if * necessary. Not needed if dev is irq-safe; then the * parent is permanently resumed. */ parent = dev->parent; if (dev->power.irq_safe) goto skip_parent; spin_unlock(&dev->power.lock); pm_runtime_get_noresume(parent);//在该设备resume前要先resume父设备,There Increment the parent's usage counter; spin_lock(&parent->power.lock); /* * We can resume if the parent's runtime PM is disabled or it * is set to ignore children. */ if (!parent->power.disable_depth && !parent->power.ignore_children) { rpm_resume(parent, 0); //resume父设备 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; } skip_parent: if (dev->power.no_callbacks) goto no_callback; /* Assume success. */ dev->power.suspend_time = ktime_set(0, 0); dev->power.max_time_suspended_ns = -1; __update_runtime_status(dev, RPM_RESUMING); /*选择同时执行设备resume callback*/ if (dev->pm_domain) callback = dev->pm_domain->ops.runtime_resume; else if (dev->type && dev->type->pm) callback = dev->type->pm->runtime_resume; else if (dev->class && dev->class->pm) callback = dev->class->pm->runtime_resume; else if (dev->bus && dev->bus->pm) callback = dev->bus->pm->runtime_resume; else callback = NULL; if (!callback && dev->driver && dev->driver->pm) callback = dev->driver->pm->runtime_resume; retval = rpm_callback(callback, dev); if (retval) { __update_runtime_status(dev, RPM_SUSPENDED); pm_runtime_cancel_pending(dev); } else { no_callback: __update_runtime_status(dev, RPM_ACTIVE); if (parent) atomic_inc(&parent->power.child_count);//该设备唤醒后增加父设备的子设备活跃数 child_count+1; } wake_up_all(&dev->power.wait_queue); if (!retval) rpm_idle(dev, RPM_ASYNC); out: if (parent && !dev->power.irq_safe) { spin_unlock_irq(&dev->power.lock); pm_runtime_put(parent); //减去刚才增加的the parent's usage counter; spin_lock_irq(&dev->power.lock); } trace_rpm_return_int(dev, _THIS_IP_, retval); return retval; } RPM机制总体来说就是管理总线结构和主次设备结构的电源,因为体系结构的因素,device套device的情况,最后就会形成一个device大树。runtime这套机制就可以从根到树顶都能管理得到。 通过上面分析我们知道当休眠时,先由子设备睡眠再让父设备休眠,递归进行,由下而上休眠。当唤醒时,是由上而下,递归唤醒。 三:实例分析 1:以msm_sdcc.c为例分析使用runtime辅助函数来实现device runtime功能: 在sdio数据读写完成后会调用到msmsdcc_disable(),之前会调用到msmsdcc_enable()。 以此来实现runtime功能。 static int msmsdcc_disable(struct mmc_host *mmc) { --------- if (host->plat->disable_runtime_pm) return -ENOTSUPP; rc = pm_runtime_put_sync(mmc->parent); //使用runtime辅助函数,先将usage_count减一,再让设备进入idle --------- } static inline int pm_runtime_put_sync(struct device *dev) { return __pm_runtime_idle(dev, RPM_GET_PUT); } int pm_generic_runtime_idle(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; if (pm && pm->runtime_idle) { int ret = pm->runtime_idle(dev);//调用设备idle的callback if (ret) return ret; } pm_runtime_suspend(dev);//发起runtime suspend动作 return 0; } static int msmsdcc_enable(struct mmc_host *mmc) { -------- rc = pm_runtime_get_sync(dev);//使用runtime辅助函数,先将usage_count加一,再让设备进入resume -------- } static inline int pm_runtime_get_sync(struct device *dev) { return __pm_runtime_resume(dev, RPM_GET_PUT); }
2:分析一段关于usb runtime的log: <4>[ 22.281829] rpm_suspend name=usb1 usage_count=0 child_count=0 //device name为usb1 父设备名为msm_hsic_host,进入rpm_suspend <4>[ 22.281860] rpm_suspend name=usb1 parent_name=msm_hsic_host p_usage_count=0 p_child_count=1 //在这里usb还没有进入suspend,故打印出的父设备的活跃子设备为1; <4>[ 22.281890] rpm_suspend dev parent's child_count -1 //msm_hsic_host 的child_count -1; <4>[ 22.281890] rpm_suspend name=usb1 usage_count=0 child_count=0 <4>[ 22.281921] rpm_suspend name=usb1 parent_name=msm_hsic_host p_usage_count=0 p_child_count=0 //msm_hsic_host 的child_count为0; <4>[ 22.281951] rpm_suspend try dev parent going to sleep //让父设备进入休眠 <4>[ 22.281951] rpm_suspend name=usb1 usage_count=0 child_count=0 <4>[ 22.281982] rpm_suspend name=usb1 parent_name=msm_hsic_host p_usage_count=0 p_child_count=0 <4>[ 22.282135] rpm_suspend name=msm_hsic_host usage_count=0 child_count=0 //msm_hsic_host进入rpm_suspend,由上面发起 <4>[ 22.282165] rpm_suspend name=msm_hsic_host parent_name=platform p_usage_count=0 p_child_count=6 //msm_hsic_host的父设备还有6个活跃的子设备 <4>[ 22.282165] rpm_suspend dev parent's child_count -1 //msm_hsic_host已经休眠,让父设备的活跃的子设备减一; <4>[ 22.282196] rpm_suspend name=msm_hsic_host usage_count=0 child_count=0 <4>[ 22.282226] rpm_suspend name=msm_hsic_host parent_name=platform p_usage_count=0 p_child_count=5 <4>[ 22.282226] rpm_suspend try dev parent going to sleep //试图让platform进入休眠 <4>[ 22.282257] rpm_suspend name=msm_hsic_host usage_count=0 child_count=0 <4>[ 22.282257] rpm_suspend name=msm_hsic_host parent_name=platform p_usage_count=0 p_child_count=5 <4>[ 22.635589] rpm_resume name=msm_hsic_host usage_count=1 child_count=0 //设备名为msm_hsic_host的进入rpm_resume,usage_count=1是由发起者增加的。 <4>[ 22.635620] rpm_resume name=msm_hsic_host parent_name=platform p_usage_count=0 p_child_count=5 <4>[ 22.635650] rpm_resume add parent usage count <4>[ 22.635650] rpm_resume name=msm_hsic_host usage_count=1 child_count=0 <4>[ 22.635650] rpm_resume name=msm_hsic_host parent_name=platform p_usage_count=1 p_child_count=5 <4>[ 22.635772] rpm_resume name=msm_hsic_host usage_count=0 child_count=0 <4>[ 22.635772] rpm_resume name=msm_hsic_host parent_name=platform p_usage_count=1 p_child_count=5 <4>[ 22.635803] rpm_resume add parent child_count +1 <4>[ 22.635803] rpm_resume name=msm_hsic_host usage_count=0 child_count=0 <4>[ 22.635833] rpm_resume name=msm_hsic_host parent_name=platform p_usage_count=1 p_child_count=6 <4>[ 22.635833] rpm_resume over name=msm_hsic_host usage_count=0 child_count=0 <4>[ 22.635864] rpm_resume over name=msm_hsic_host parent_name=platform p_usage_count=0 p_child_count=6 <4>[ 22.635864] rpm_resume name=usb1 usage_count=1 child_count=0 //设备为usb1进入rpm_resume <4>[ 22.635894] rpm_resume name=usb1 parent_name=msm_hsic_host p_usage_count=0 p_child_count=0 <4>[ 22.635894] rpm_resume add parent usage count <4>[ 22.635894] rpm_resume name=usb1 usage_count=1 child_count=0 <4>[ 22.635925] rpm_resume name=usb1 parent_name=msm_hsic_host p_usage_count=1 p_child_count=0 <4>[ 22.671600] rpm_resume name=usb1 usage_count=1 child_count=0 <4>[ 22.671630] rpm_resume name=usb1 parent_name=msm_hsic_host p_usage_count=1 p_child_count=0 <4>[ 22.671630] rpm_resume add parent child_count +1 <4>[ 22.671661] rpm_resume name=usb1 usage_count=1 child_count=0 <4>[ 22.671661] rpm_resume name=usb1 parent_name=msm_hsic_host p_usage_count=1 p_child_count=1 <4>[ 22.671691] rpm_resume over name=usb1 usage_count=1 child_count=0 <4>[ 22.671691] rpm_resume over name=usb1 parent_name=msm_hsic_host p_usage_count=0 p_child_count=1 上面log充分说明了runtime_suspend是从子设备到父设备,递归休眠的,而runtime_resume则是相反的过程。 深入理解runtime pm机制,将会为调试设备不能进入runtime suspend打下坚实基础,通常linux中通讯总线都会采用runtime 电源管理机制,当I/O非busy时自动休眠,也就是通讯没有数据交互的时候的总线自动进入suspend,电源管理采用不用则停的方略,以此来减少功耗。 通常AP通过USB来外挂第三方MODEM,肯定会遇见休眠问题。由于usb通信速率能达到480Mbps,而sdio 100Mbps hs uart 4M,很明显usb通信很有优势,尤其在4G越来越普及的情况下。现在就是越来越来的AP+MODEM方案通过usb来通信的。 后续有空再结合数据收发详尽分析usb休眠机制,Linux usb suspend机制。 转载请注明原创地址,谢谢。 |
|