linux dpm机制分析(上)

  • Post author:
  • Post category:linux



1      DPM介绍


1.1        Dpm:

设备电源管理,device  power  management

主要功能:系统在对外设下电前,需要做一些准备工作,保存一些设备的必要信息,以便在系统退出低功耗状态时能够恢复这些信息,使本设备在上电后能像睡眠前那样正常工作。驱动程序作为一部分,跟随系统级别的低功耗状态。


1.2       系统睡眠模式


System Sleep model:


Suspend:保存到ram中,suspend-to-RAM,也就是我们使用并关注的睡眠模式

Hibernation:保存到硬盘中,suspend-to-disk


2      设备电源管理操作

子系统和驱动程序的设备电源管理操作,都定义在dev_pm_ops结构中,这个结构在include/linux/pm.h中定义:


struct

dev_pm_ops {

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

void (*complete)(struct device *dev);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

};

驱动程序使用的主要由以下8种:

Prepare:做suspend前的准备工作,返回后本设备下不能再注册子设备,后续可跟@suspend(), @freeze(), @poweroff()中的一种回调;其作为dpm众多回调函数中的一个,在执行保存操作时最先执行,优先级最高;

Complete:执行prepare的逆操作,

Suspend:回调各设备的suspend回调函数,保存各设备必要信息,在系统唤醒时能恢复这些信息使该设备能实现睡眠前的功能。

suspend_late:晚于其他设备进行suspend操作,还可以保存suspend阶段没有保存的一些信息;

suspend_noirq: 阶段发生在IRQ被禁止之后,这意味着该回调运行期间,驱动程序的中断处理代码不会被调用。回调方法可以保存上一阶段没有保存的寄存器并最终让设备进入相应的低功耗状态;该回调函数优先级最低,最后执行保存工作,

Resume:回调各设备的resume回调函数,恢复suspend保存的一些必要信息

resume_early:先于其他设备进行恢复操作;对应suspend_late

resume_noirq:唤醒阶段的第一阶段,执行suspend_noirq的逆操作

设备进入suspend和resume的关键方法在bus_type结构、device_type结构和class结构的pm成员中,他是一个dev_pm_ops结构的指针。 1      系统电源管理的各个阶段

suspend和resume是分阶段完成的。在进入下一个阶段之前,都需要为每个设备调用属于本阶段的回调函数。不是所有的总线和设备类都会支持所有这些回调,也不是所有的驱动程序都要使用这些回调。有些阶段需要冻结进程后,解冻进程前执行。此外,*_noirq阶段需要在IRQ被关闭的情况下执行。

2.1      系统挂起(suspend)

当系统进入standby或sleep状态时,需要经历以下阶段:

prepare,suspend,suspend_late, suspend_noirq。

(1)    prepare阶段主要是通过阻止新设备注册来防止竟态的发生; prepare回调返回后,该设备的下面将不可以注册新的子设备。回调方法也会让设备或驱动为将要到来的系统电源状态迁移做出准备。

(2)    suspend阶段由suspend回调实现,它停止设备的一切I/O操作。它同时也可以保存设备的寄存器,依据设备所属的总线类型,让设备进入合适的低功耗状态。


以上两个回调主要由低功耗接口dpm_suspend_start()来实现。

(3)    Suspend_late顾名思义,是在其他设备suspend之后,再进行suspend操作,也就是说如果设备注册了本回调意味着本设备的suspend要晚于其他设备的suspend操作

(4)    suspend_noirq阶段发生在IRQ被禁止之后,这意味着该回调运行期间,驱动程序的中断处理代码不会被调用。回调方法可以保存上一阶段没有保存的寄存器并最终让设备进入相应的低功耗状态。大多数子系统(subsystem)和驱动程序不需要实现这一回调。不过,某些允许设备共享中断向量的总线类型,例如PCI,通常需要这一回调;否则,当本设备已经进入低功耗时另一个与他共享中断的设备感知中断的发生,驱动程序将会发生错误。


以上两个回调主要由低功耗接口dpm_suspend_end()来实现。

这些阶段结束后,驱动程序必须停止所有的I/O事务(DMA,IRQs),保存足够的状态信息以便它们能被重新初始化或回复之前的状态(按应将的需要而定),然后让设备进入低功耗状态。很多平台上,它们会关闭某些时钟(只在一个核上使用的设备需要在回调函数中添加关钟和开钟操作);有时还会关闭电源或者是降低电压,V7R2由M3来统一执行下电操作。

只要这些回调中的一个返回错误,系统不会进入所述的低功耗状态,而是由pm的核心对已经suspend的设备发起resume动作进行回退。

2.2     唤醒(resume)

当系统退出standby或sleep状态时,需要经历以下阶段:

resume_noirq,resume_early,resume,complete。


(1)

resume_noirq回调方法应该执行所有在中断处理程序被调用前的必须动作。这通常意味着撤销suspend_noirq阶段所做的动作。如果总线类型允许共享中断向量,例如PCI,该回调方法应该使设备和驱动能够识别自身是否是中断源,如果是,还要能正确地处理。例如,对于PCI总线,bus->pm.resume_noirq()让设备进入全电源状态(PCI中称作D0),并回复设备的标准配置寄存器。然后,调用设备驱动程序的 ->pm.resume_noirq()方法来执行特定于设备的动作。


(2)

resume_early执行在其他设备恢复信息之前,早于其他设备的恢复,suspend_late的逆操作


以上两个回调主要由低功耗接口dpm_resume_start()来实现。


(3)

resume回调方法让设备回到他的工作状态,以便它能执行正常的I/O。这通常等同于执行suspend阶段的撤销工作。


(4)

complete阶段撤销prepare阶段所做出的动作。不过请注意,新设备可能在resume回调返回后立刻被注册,而不必等到complete阶段完成。


以上两个回调主要由低功耗接口dpm_resume_end()来实现。

这些阶段结束后,驱动应该和suspend之前一样:I/O能通过DMA或IRQs执行,相应的时钟被打开。

多数阶段使用bus、type和class的回调(也就是定义在dev->bus->pm,dev->type->pm和dev->class->pm中)。

当然也可以直接使用device结构体中的ops。