1. 电源管理概述:为什么需要电源管理?

大家好,我是老李。做嵌入式Linux内核开发十几年了,今天咱们聊聊电源管理。

你可能会问:为什么非要搞电源管理?

说白了,就两个原因:省电散热

我刚开始做嵌入式那会儿,遇到一个手持设备项目。电池容量就那么大,客户要求待机一周。不加电源管理?那是不可能的。后来我学乖了,每个项目一开始就把电源管理框架搭好。

你想想看,手机、平板、IoT设备,哪个不是靠电池活着?就算插电的设备,发热问题也够你头疼的。我见过一个路由器项目,因为没做好CPU调频,夏天直接过热重启。嗯,那场面挺尴尬的。

Linux电源管理子系统整体架构

Linux的电源管理,不是一两个驱动就能搞定的。它是一个完整的子系统。我习惯把它分成几个层次来看:

核心思想:谁用谁管理,不用就休眠。能省则省,能降则降。

下面这张图,是我自己画的架构图。你看一眼,心里就有数了。

Linux电源管理子系统整体架构 用户空间 / 应用层 sysfs接口 | 电源管理工具 (powertop, cpupower) | Android wakelock PM Core (电源管理核心层) Suspend/Resume 系统休眠/唤醒框架 PM QOS 服务质量约束管理 Runtime PM 运行时电源管理 设备驱动层 (Device Drivers) CPUFreq (CPU频率调节) 动态调频 / 调压 CPUIdle (CPU空闲管理) C-State / 睡眠模式 硬件抽象层 / SoC底层 Devfreq (设备频率调节) | 时钟门控 | 电源域 | 稳压器

各模块是干什么的?

咱们从上往下捋一遍。

PM Core — 电源管理核心

这是整个子系统的大脑。它负责协调所有电源管理动作。比如系统要休眠了,PM Core会通知每个驱动:兄弟,准备睡觉了。驱动收到信号,保存状态、关掉外设。

我遇到过最坑的事,就是某个驱动在suspend回调里没处理好,导致系统睡下去就醒不过来。排查了三天,最后发现是GPIO没配置成唤醒源。嗯,这种坑踩过一次就记住了。

PM QOS — 服务质量约束

这个模块说白了就是讨价还价。比如你在放视频,GPU说:我要高频,不然卡顿。CPU说:我也要高频,不然解码跟不上。PM QOS就负责收集这些需求,然后给一个折中方案。

举个例子:

// 某个驱动要求CPU最低频率不低于1.2GHz
pm_qos_add_request(&req, PM_QOS_CPU_FREQ_MIN, 1200000);

这样系统就不会为了省电把频率降得太低,导致用户体验变差。

Runtime PM — 运行时电源管理

这是我最喜欢的模块。它让设备在不用的时候自动休眠,用的时候自动唤醒。不需要用户插手。

比如一个USB鼠标,你不动它几秒钟,它自动进入低功耗模式。你一动,它立刻醒来。整个过程对用户完全透明。

我的经验:Runtime PM的难点在于唤醒延迟。有些设备从休眠到就绪需要几十毫秒。如果用户操作频繁,频繁唤醒反而更耗电。所以要根据使用场景调整休眠策略。

Suspend/Resume — 系统休眠与唤醒

这个大家应该不陌生。手机按一下电源键,屏幕灭了,系统进入休眠。再按一下,瞬间亮起。这就是Suspend/Resume在干活。

Linux支持多种休眠模式:

模式 省电程度 唤醒延迟 典型场景
Suspend-to-Idle 毫秒级 手机待机
Suspend-to-RAM (STR) 百毫秒级 笔记本合盖
Suspend-to-Disk (STD) 秒级 台式机休眠

注意:移植Suspend/Resume时,一定要确认SoC的唤醒源配置。我曾经在一个项目里,因为RTC唤醒没配好,设备睡下去就再也醒不来了。最后只能拔电池。

CPUFreq — CPU频率调节

CPU是耗电大户。CPUFreq就是根据负载动态调整CPU频率和电压。负载高就升频,负载低就降频。

常见的调频策略:

  • performance:一直跑最高频,性能优先
  • powersave:一直跑最低频,省电优先
  • ondemand:根据负载动态调整,比较激进
  • interactive:Android常用的策略,响应快
  • schedutil:基于调度器负载的调频,比较精准

我个人习惯用schedutil。它跟调度器配合得好,不会出现「明明CPU很闲,频率还很高」的情况。

CPUIdle — CPU空闲管理

CPU没事干的时候,不能让它空转。CPUIdle负责让CPU进入各种睡眠状态(C-State)。

C-State越深,省电越多,但唤醒延迟也越大。比如:

  • C0:运行状态
  • C1:暂停(HALT)
  • C2:停止时钟
  • C3:关闭PLL,电压降低

嗯,这里要注意:不是C-State越深越好。如果中断频繁,CPU刚睡下就被唤醒,来回切换反而更耗电。所以要根据中断频率选择合适的C-State。

Devfreq — 设备频率调节

Devfreq是CPUFreq的「兄弟」。它负责管理非CPU设备的频率,比如GPU、DDR、总线等。

举个例子:

// 注册一个devfreq设备,使用simple_ondemand策略
struct devfreq *df = devfreq_add_device(dev, &my_devfreq_governor, NULL);

我在一个平板项目里,用Devfreq动态调整GPU频率。玩游戏时GPU跑满,看网页时降到最低。电池续航直接提升了30%。

总结一下

Linux电源管理子系统,说白了就是一套「能省则省」的机制。PM Core是大脑,PM QOS是协调员,Runtime PM是自动开关,Suspend/Resume是总开关,CPUFreq和CPUIdle管CPU,Devfreq管其他设备。

移植的时候,我的建议是:先跑通Suspend/Resume,再搞Runtime PM,最后优化CPUFreq和CPUIdle。一步一步来,别想一口吃成胖子。

好了,这一章就聊到这儿。下一章咱们开始动手,看看怎么把电源管理框架移植到你的板子上。


专注资料整理