Linux的源代码里,大部分都属于设备驱动程序的代码,因此,大多数电源管理(PM)的代码也是存在于驱动程序当中。很多驱动程序可能只做了少量的工作,另外一些,例如使用电池供电的硬件平台(移动电话等)则会在电源管理上做了大量的工作。 这份文档对驱动程序如何与系统的电源管理部分交互做了一个大概的描述,尤其是关联到驱动程序核心中的模型和接口的共享,建议从事驱动程序相关领域的人通过本文档可以了解相关的背景知识。 设备电源管理的两种模型
===================================
驱动程序可以使用其中一种模型来使设备进入低功耗状态:
1. 系统睡眠模型:
驱动程序作为一部分,跟随系统级别的低功耗状态,就像”suspend”(也叫做”suspend-to-RA
M”),或者对于有硬盘的系统,可以进入”hibernation”(也叫做”suspend-to-disk”)。
这种情况下,驱动程序,总线,设备类驱动一起,通过各种特定于设备的suspend和resume方法,清晰地关闭硬件设备和各个软件子系统,然后在数据不被丢失的情况下重新激活硬件设备。
有些驱动程序可以管理硬件的唤醒事件,这些事件可以让系统离开低功耗状态。这一特性可以通过相应的/sys/devices/…/power /wakeup文件来开启和关闭(对于Ethernet驱动程序,ethtool通过ioctl接口达到同样的目的);使能该功能可能会导致额外的功耗, 但他让整个系统有更多的机会进入低功耗状态。
2. Runtime 电源管理模型:
这种模型允许设备在系统运行阶段进入低功耗状态,原则上,他可以独立于其他的电源管理活动。不过,通常设备之间不能单独进行控制(例如,父设备不能 进入suspend,除非他的所有子设备已经进入suspend状态)。此外,依据不同的总线类型,可能必须做出一些特别的操作来达到目的。如果设备在系 统运行阶段进入了低功耗状态,在系统级别的电源状态迁移时(suspend或hibernation)就必须做出特别的处理。
正因为这个原因,不仅仅设备驱动程序本身,相应的子系统(bus type,device type,device class)驱动程序和电源管理核心也会被卷入到rumtime电源管理的工作中来。比如当系统睡眠时,以上的各模块必须互相合作来实现各种多样的 suspend和resume方法,以便让硬件进入低功耗状态,唤醒后继续提供服务而不丢失数据。
对于低功耗状态的定义,我们没有太多可以说的,因为他们通常特定于系统,甚至特定于某个设备。如果在系统运行状态,足够多的设备进入了低功耗状态, 这时的效果其实和进入了系统级别的低功耗状态非常相像。这样一些驱动程序可以利用rumtime电源管理让系统进入一种类似深度省电的状态。
大多数进入suspend状态的设备会停止所有的I/O操作:不会有DMA或者IRQ请求(需要唤醒系统的除外),不会有数据的读写,不再接受上层驱动的请求。这对于不同的总线和平台会有不同的要求。
关于硬件唤醒事件的一些例子:由RTC发起的闹钟,网络数据包的到达,键盘或者鼠标的活动,媒体的插入或移除(PCMCIA,MMC/SD,USB,等等)。
进入系统睡眠状态的接口
===================================================
内核为各个子系统(bus type,device type, device class)和驱动程序提供了相应的编程接口,以便它们参与它们所关心的设备的电源管理。这些接口覆盖了系统级别的睡眠和runtime级别的管理。
设备电源管理操作
===================================================
子系统和驱动程序的设备电源管理操作,都定义在dev_pm_ops结构中:cd架
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | 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_noirq)(struct device *dev); int(*resume_noirq)(struct device *dev); int(*freeze_noirq)(wcdlstruct 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); }; |
| |
这个结构在include/linux/pm.h中定义,它们的作用将会在接下来进行描述。现在,我们只要记住,最后三个方法是专门用于rumtime pm的,其他的则用于系统级别的电源状态迁移。
某些子系统中,依然存在所谓“过时的”或“传统的”电源管理操作接口,这种方式不会使用到dev_pm_ops结构,而且只适用于系统级别的电源管理方法,这边文章里将不会对它进行说明,如果要了解的话请直接查看内核的源代码。
子系统级别(Subsystem-Level)方法
————————————————
设备进入suspend和resume的关键方法在bus_type结构、device_type结构和class结构的pm成员中,他是一个 dev_pm_ops结构的指针。多数情况下,这些都是那些具体总线的体系结构(例如PCI或USB或某个设备类别和设备类)的维护者们来关注的部分。
总线驱动会适当地实现这些方法以供硬件和驱动程序使用它们;因为PCI和USB有不同的工作方式。只有少数人会编写subsystem-level的驱动程序;大多数的设备驱动程序是建立
在各种特定总线架构的代码之上。
有关这些调用,稍后会进行更详尽的描述;它们将会顺着父子形式的设备模型树,一个设备一个设备地被调用。
/sys/devices/…/power/wakeup files
————————————————-
设备模型中的所有设备都有两个标志来控制唤醒事件(可使得设备或系统退出低功耗状态)。设两个标志位由总线或者设备驱动用 device_set_wakeup_capable()和device_set_wakeup_enable()来初始化,它们在 include/linux/pm_wakeup.h中定义。
“can_wakeup”标志表示设备(或驱动)物理上支持唤醒事件,device_set_wakeup_capable()函数会影响该标 志。”should_wakeup”标志控制设备是否应该尝试启用他的唤醒机制。device_set_wakeup_enable()会影响该标志。大 部分的驱动程序不会主动修改它们的值。大多数设备的should_wakeup的初始值都被设为false,也有例外,比如电源键、键盘和由 ethtool设置了wake-on-LAN功能的网卡。