资讯动态

ARMv9/v8电源管理架构全解析及功耗调试实战

发布时间:2026/9/16 6:31:34 来源:尧图企业网站定制
做ARM平台系统的人多半都经历过这种场景产品经理甩过来一张功耗测试报告说整机待机电流比竞品高了20mA让你赶紧排查。你打开powertop一看CPU明明在idle里待了90%的时间可耗电就是下不去。问题往往不在某一个点而在整条电源管理链路上——状态没真正进去、进去了被频繁唤醒、某个外设没关联到正确的电源域任何一个环节出问题功耗账都算不平。到了ARMv9时代安全架构变更之后电源管理的边界又多了一层很多以前靠经验能定位的问题现在变得更容易踩坑。这篇文章我把ARMv9/v8的电源管理系统架构完整梳理一遍从底层的WFI指令、DVFS调频调压、Power Gating机制到设备树里的idle states配置、PSCI与ATF固件、以及ARMv9新增的CCA/RME对功耗的影响最后再给出一套可落地的调试方法。适合做系统软件、BSP移植、功耗调优的工程师也适合刚接触ARM底层、想搞清楚“CPU睡眠到底发生了什么”的嵌入式开发者。1. 先把账算清电源管理到底在管哪些“电”1.1 动态功耗、静态功耗和系统清单搞电源管理首先得知道功耗是怎么产生的。SoC内部主要有两类功耗动态功耗和静态功耗。动态功耗的公式是 P C·V²·f。C是电容负载V是工作电压f是时钟频率。这个公式说明三件事电压的影响是平方级的所以降电压的收益远大于降频率频率降低能省电但省得有限真正想要“大幅度省电”核心思路是尽快把电压降下来或者直接把电源关掉。这也是为什么现代处理器都强调“Race to Idle”——活儿快点干完然后赶紧进入低功耗状态而不是慢悠悠地干活。静态功耗则来自漏电流。在28nm时代漏电占比还不算突出但到7nm、5nm以及更先进工艺下晶体管尺寸越来越小阈值电压越来越低漏电流占比明显上升。静态功耗的特点是只要芯片还通着电它就在耗电不管CPU有没有在跑。所以对于先进工艺的SoC光靠降频降电压还不够必须把不用的模块直接断电才能真正压住待机功耗。把视角拉到整机层面功耗来源通常是这样的AP侧CPU/GPU动态和静态都有DDR内存自刷新和读写状态差距很大显示链路屏幕一亮功耗就上去基带、WiFi、蓝牙等通信模块传感器Hub、Always-on Processor常开处理器各种PMIC自身的静态损耗、LDO空载损耗做功耗调优时我习惯先把整机的功耗拆成“开关状态”和“运行负载”两张表。开关状态决定哪些模块还在供电运行负载决定正在运行的模块跑多快。电源管理系统架构要解决的核心问题就是这两张表的自动编排和切换。1.2 电源域、时钟域、电压域的划分逻辑理解了功耗来源再看硬件侧的划分。一个SoC内部不是一整块铁板而是被切分成很多个电源域Power Domain、电压域Voltage Domain和时钟域Clock Domain。这三者经常被混在一起说实际是完全不同的概念。时钟域Clock Domain关注的是逻辑是否有时钟在跳。一个模块的时钟可以被关掉Clock Gating关掉之后该模块不再翻转信号动态功耗归零但静态功耗还在寄存器的值也能保持。电压域Voltage Domain是同一路电源轨覆盖的模块集合。做DVFS时一个电压域内的模块必须一起升降压不能一部分1.0V、一部分0.8V。不同电压域之间交互需要电平转换器Level Shifter电压域被断电时需要隔离单元Isolation Cell来防止输出信号倒灌。电源域Power Domain是可以独立切断电源的区域。电源域断电后内部所有逻辑状态全部丢失静态功耗也归零但代价是恢复时需要重新上电、复位、加载固件或恢复上下文延迟很大。举个例子一个典型的移动SoC会把CPU cluster A、CPU cluster B、GPU、NPU、ISP、Modem分别划成不同的电源域。其中CPU cluster内部还可能再细分每个core一个电源域、cluster公共的L2缓存一个电源域。这就是为什么CPUIdle状态可以有那么多层——WFI时可能只关core的时钟再深一层可以断掉core的电源再深一层可以把整个cluster的电源都断掉。设备树里的power-domains属性就是在描述这种归属关系。比如某个外设要挂到某个电源域下内核的PM Runtime框架才能通过genpdGeneric Power Domain驱动去控制它的开关。这块配置一旦出错就会出现“模块没在跑但功耗很高因为它的电源域根本没关过”这种诡异问题。2. 三大底层机制WFI、DVFS与电源闸控2.1 WFI/WFE与CPU挂起的第一道门ARM架构里CPU进入低功耗最基础的入口是WFIWait For Interrupt和WFEWait For Event两条指令。WFI会让CPU停止执行指令进入一种“等待中断”的状态。任何使能的中断到达后CPU会恢复执行。注意WFI本身并不承诺一定会关闭时钟或电源——它更像是一个“暂停”指令更深的省电效果取决于硬件如何响应WFI事件。在ARMv8/v9的Linux实现里CPU进入idle的路径大致是cpuidle框架选择state - arch_cpu_idle - cpu_do_idle - wfi最终落在这条指令上。WFE则是等待事件Event语义上和WFI有微妙区别。WFE被唤醒后不会自动重新检查条件通常用于自旋锁场景。WFE的进入退出延迟比WFI更小但省电效果也相对弱一些所以在Linux里WFE一般出现在同步原语中而不是CPUIdle的主路径上。这里要说一个常见误区很多人以为执行WFI就是“睡眠”了其实远远不够。硬件上WFI只是一个触发条件SoC的电源管理单元PMU接收到WFI事件后还要结合当前的状态配置决定要不要关时钟、要不要断电源、要不要先隔离输出。这套动作由硬件状态机完成软件能控制的只是事先设置好允许进入哪个idle state。2.2 DVFS调频调压的物理逻辑DVFSDynamic Voltage and Frequency Scaling是目前最常用的在线省电手段。它的核心思想很简单任务量大的时候提高频率和电压任务量小的时候降下来。但为什么要“频率和电压一起调”这里有一个物理约束。CMOS电路要正常工作晶体管的开关时间必须满足时钟周期的要求。频率越高意味着一个时钟周期越短晶体管必须在更短时间内完成充电放电这就需要更高的电压来驱动。举个具体的例子某款CPU运行在2.4GHz时可能需要1.0V电压运行在1.8GHz时0.85V就够用。如果你在2.4GHz频率下只给0.85VCPU计算会直接出错甚至在某些情况下不明不白地死机。所以DVFS的软件框架里频率和电压必须作为一个组合来管理这个组合在Linux里叫做OPPOperating Performance Point。一组OPP就是一个合法的电压频率配对SoC厂商会把所有经过验证的OPP写进设备树或者固件表里系统运行时只在这些点之间切换不允许随意组合。Linux里的cpufreq框架负责这件事。用户态或内核态的governor根据CPU负载决定目标频率底层驱动再通过clk framework改时钟频率、通过regulator framework改供电电压。调频调压的顺序也有讲究升频时必须先升压、再升频否则高频率低电压的过渡态可能出错降频时要反过来先降频、再降压这样能保证中间时刻有足够的电压余量。这个顺序在开发板上可以直接用示波器抓电压轨看顺序搞反了会在电压波形上看到明显的毛刺。governor的选择也很重要。老牌的ondemand按采样周期看负载处理突发任务时来回跳跃比较明显。ARM平台现在更推荐schedutil它直接利用调度器的PELT负载信号来调频响应更快配合EASEnergy Aware Scheduling可以在大小核之间做功耗感知的任务调度。我实测过很多场景schedutil在交互类负载上的功耗比ondemand能省5%-10%这在小电池设备上是很可观的。2.3 Power Gating与Clock Gating两种节电方式的区别如果说DVFS是“调节阀”那么Clock Gating和Power Gating就是“关阀门”和“关总闸”。Clock Gating只是把模块的时钟停掉。模块内部的状态、寄存器内容全部保留恢复时只要重新打开时钟就能立刻继续工作几乎没开销。代价是静态漏电依然存在电压也依然施加在电路上。在CPUIdle状态设计里浅睡眠例如WFI Clock Gating就是这一类它的优势是进出快适合处理器只在短时间内没事做的场景。Power Gating则是真正把电源切断。省电效果最彻底但代价也非常大断电后内部状态全部丢失重新上电后需要复位、重新加载固件或恢复上下文退出延迟可能在几十微秒到几毫秒不等。Power Gating的工程难点在于“断”和“复”这两个瞬间。断电前需要把模块的输出隔离Isolation防止断电模块输出悬空信号干扰还在工作的模块需要把关键上下文保存到不会掉电的地方比如DDR或者专用的retention寄存器。上电后还需要按正确的时序解除隔离、释放复位、恢复上下文。CPU Hotplug就是Power Gating理念的一种体现。当一个CPU core执行offline流程时内核通过PSCI通知固件把这个core的电源断开online时再上电。很多人不理解为什么offline一个CPU要那么久有时候几百毫秒就是因为背后是完整的电源域通断流程而不是一条指令能搞定的事。这里有个容易忽略的验证点你怎么确认一个电源域真的断了软件上不能只看日志说“powered off”最好接一个电流探头看该电源轨的电流曲线。我之前排查过一个“宣称进入了idle但整机电流居高不下”的问题最后就是靠测量发现某个GPU的电源域根本没断原因是最早加载GPU驱动的进程没有调用Runtime PM的put接口导致genpd的引用计数一直没归零。3. CPUIdle与系统挂起状态机怎么设计和验证3.1 设备树里的cpu-idle-states到底怎么填CPUIdle框架是Linux内核中管理CPU低功耗状态的核心组件。系统里每个CPU可以有多个idle state按深度排序state0通常是WFIstate1可能加了Clock Gatingstate2可能把CPU core的电源域直接断掉state3可能连cluster的L2都一起断电。每次CPU进入idle时governor会根据预测的空闲时长、唤醒延迟要求选择一个合适的state进入。设备树中的cpu-idle-states节点负责把这套状态配置告诉内核。常见的配置差不多是下面这个样子cpus { cpu0 { compatible arm,cortex-a78; reg 0x0; enable-method psci; cpu-idle-states cpu_sleep_0, cpu_sleep_1; capacity-dmips-mhz 1024; }; cpu-idle-states { entry-method psci; cpu_sleep_0: cpu-sleep-0 { compatible arm,idle-state; local-timer-stop; arm,psci-suspend-param 0x00010000; entry-latency-us 40; exit-latency-us 80; min-residency-us 200; status okay; }; cpu_sleep_1: cpu-sleep-1 { compatible arm,idle-state; local-timer-stop; arm,psci-suspend-param 0x00110000; entry-latency-us 100; exit-latency-us 200; min-residency-us 600; status okay; }; }; };这些字段的含义我在实际调试中总结过一套理解方式。entry-latency-us是进入这个状态需要的时间包括保存上下文、隔离输出等exit-latency-us是从状态唤醒到CPU能跑代码的时间min-residency-us是进入这个状态至少要待多久才划算。governor的判断逻辑就是如果预计空闲时间小于entryexit的开销还不如不睡如果预计空闲时间略大于exit但小于min-residency那睡浅一点更合适。arm,psci-suspend-param的值是最容易搞错的。它其实是一个按位编码的power state参数低4位或低8位代表state type比如standby还是power down高位可能包含affinity levelCPU层面还是cluster层面、上下文是否保留等信息。这个编码没有统一标准必须和固件TF-A或其他EL3实现里的解析逻辑严格对应。Linux只负责把这个值原样传给PSCI_CPU_SUSPEND接口解释权在固件侧。local-timer-stop这个属性也很关键。它表示进入这个idle state后CPU的local timer会停止计时。如果不设这个属性内核会认为local timer依然能唤醒CPU那么在处理器睡眠时定时器中断来不了整个内核的时钟管理就乱了。3.2 从CPUIdle到Suspend-to-RAM的完整链路CPUIdle解决的是“CPU没事干”的场景系统挂起Suspend-to-RAM / Suspend-to-Idle则解决“整机想睡觉”的场景。两者的区别是系统挂起时不只是CPU进idle而是所有设备、总线、中断控制器都要配合进入低功耗状态。在Linux下执行suspend的流程可以简单概括成下面几条路径用户写入echo mem /sys/power/state内核进入suspend路径冻结用户态进程把线程都停住逐个调用设备的suspend回调设备驱动把硬件设置成低功耗状态关闭非boot CPU让它们走CPUHotplug流程最后剩下的boot CPU执行PSCI_SYSTEM_SUSPEND把整个系统交给固件固件和硬件进入最终的睡眠状态DDR进入自刷新模式等待唤醒源RTC、按键、网络唤醒包等触发中断这条链路单看每一步都不复杂难在牵一发动全身。任何一个设备的suspend回调如果阻塞了、或者某个设备没注册到suspend链路上系统挂起就可能失败或者发虚。我调试中最常遇见的场景是系统“看似”挂了电流也降了但唤醒后发现网络模块还在运行因为它注册的是runtime PM而不是system suspend。唤醒路径同样重要。系统挂起后GIC通用中断控制器必须有独立的always-on电源域否则无法接收中断并唤醒boot CPU。各种唤醒源的中断要使能好wakeup_source要在设备驱动里正确注册否则内核可能会问“这个设备没被标记为唤醒源你为什么不让我睡”。唤醒的延迟也是一个关键指标。手机从按下电源键到屏幕亮起理想情况应该在100ms-200ms左右。这个时间不仅包括硬件上电时序还包括固件恢复上下文、内核恢复设备、显示链路重新初始化的时间。ARMv9时代如果还牵扯到Realm环境的恢复这个时间预算还需要重新评估。4. ARMv8到ARMv9PSCI、ATF与新增的安全边界4.1 PSCI接口电源管理在EL3的“操作系统”CPU的电源管理不能只靠Linux内核自己操作寄存器。ARM在标准里定义了一个协议叫PSCIPower State Coordination Interface它规范了操作系统和固件之间的电源管理调用接口。为什么需要这么一层因为CPU的电源状态、集群的电源状态、整个系统的电源状态本质上控制在EL3固件手里。Linux内核运行在EL1没有权限直接摆弄这些电源寄存器必须通过SMC指令陷入EL3去请求固件执行。PSCI主要接口我用下面的表整理一下接口名作用典型调用时机PSCI_VERSION查询PSCI版本内核启动早期PSCI_CPU_SUSPEND让指定CPU进入低功耗状态CPUIdle或CPU hotplugPSCI_CPU_OFF关闭指定CPU关核、系统挂起PSCI_CPU_ON启动指定CPU开核、唤醒PSCI_AFFINITY_INFO查询CPU/cluster状态某些固件调试PSCI_SYSTEM_SUSPEND整机挂起Suspend-to-RAMPSCI_SYSTEM_OFF系统关机关机流程PSCI_SYSTEM_RESET系统复位重启流程Linux内核通过psci_ops结构体封装这些调用代码路径在drivers/firmware/psci/和arch/arm64/kernel/psci.c。实际工作中我们不需要经常去改PSCI层代码但排查问题时必须看得懂固件和内核之间的交互。比如一个CPU无法进入深度idle我会先在TF-A侧打开PSCI日志看内核发来的suspend参数是什么再对比固件的解析逻辑很多疑难杂症最后都出在参数编码不一致上。PSCI的实现通常放在ATFARM Trusted Firmware即TF-A里。TF-A运行在EL3是ARMv8之后安全启动和运行时固件的地基。它负责启动后续的BL32如OP-TEE、BL33UEFI或内核也负责处理PSCI。如果你使用的平台是自定义固件而不是标准TF-A那PSCI的power state编码解释方式完全可以不一样这也是很多Soc内部资料里需要专门文档说明的部分。4.2 ARMv9的CCA/RME给电源管理带来的新问题ARMv9引入的CCAConfidential Compute Architecture是这几年ARM在架构层面最大的一次变化。CCA的核心是RMERealm Management Extension它新增了一个被称为Realm的安全执行环境。简单理解原来ARMv8的世界划分是EL0/1/2下的Normal World和Secure WorldCCA在Normal World里又划出一块隔离区域称为Realm用来跑机密计算负载连Hypervisor和OS都看不到Realm内部的内存和状态。这个新世界对电源管理的影响主要体现在三个方面。第一多了一层固件。RME引入了RMMRealm Management Monitor运行在R-EL2。正常世界和Realm世界之间的切换需要经过RMM这意味着原来从内核到EL3的一次PSCI调用在某些场景下可能会涉及RMM的参与和状态保存。多一次固件切换就多一份时间和功耗开销。第二Realm状态的保存恢复变得更加复杂。CPU进入深度idle或系统挂起时如果有Realm正在运行它的寄存器上下文、内存加密状态、Granule状态都需要考虑。系统恢复时RMM需要重新建立Realm的运行环境。这个过程的延迟如果设计得不好会直接影响唤醒速度和待机功耗。第三安全边界扩大了调试变得困难。Realm内存对Normal World是隔离的这就意味着以前可以在内核里直接读内存看状态的调试方法不再适用。排查Realm相关功耗问题时只能借助RMM和TF-A提供的接口诊断链路长了不是一点半点。我在一个启用了RME的平台上做过对比测试在完全相同的负载下开启RME之后系统空载电流比关闭RME时高了20mA-30mA唤醒延迟增加了大概几十微秒。这个损耗对于大核平台也许无所谓但对于电池供电的移动设备20mA可不是一个小数字。所以做ARMv9平台功耗设计时CCA带来的额外功耗预算必须在方案早期就考虑进去否则后面调优非常被动。5. 实操演示配置、调参与功耗排查5.1 一份可复现的设备树电源管理配置下面给出一份更完整的设备树配置片段涵盖CPU idle states、CPU频率的OPP表、以及一个外设关联到电源域的例子。注意频率和电压数值只是示例必须按你的实际SoC手册替换。/ { cpus { #address-cells 1; #size-cells 0; cpu0: cpu0 { compatible arm,cortex-a78; reg 0x0; device_type cpu; enable-method psci; operating-points-v2 cpu0_opp_table; cpu-idle-states cpu_sleep_0, cpu_sleep_1; power-domains cpu_cluster_pd; }; cpu0_opp_table: opp-table-0 { compatible operating-points-v2; opp-shared; opp-300000000 { opp-hz /bits/ 64 300000000; opp-microvolt 750000; }; opp-1800000000 { opp-hz /bits/ 64 1800000000; opp-microvolt 850000; }; opp-2400000000 { opp-hz /bits/ 64 2400000000; opp-microvolt 1000000; }; }; }; cpu-idle-states { entry-method psci; cpu_sleep_0: cpu-sleep-0 { compatible arm,idle-state; local-timer-stop; arm,psci-suspend-param 0x00000001; entry-latency-us 30; exit-latency-us 50; min-residency-us 150; }; cpu_sleep_1: cpu-sleep-1 { compatible arm,idle-state; local-timer-stop; arm,psci-suspend-param 0x00100001; entry-latency-us 80; exit-latency-us 200; min-residency-us 500; }; }; /* 外设挂到某个电源域下 */ soc { ethernet... { compatible ...; reg ...; power-domains eth_pd; wakeup-source; }; }; power-domains { cpu_cluster_pd: cpu-cluster-pd { #power-domain-cells 0; domain-idle-states cluster_sleep_0; }; eth_pd: eth-pd { #power-domain-cells 0; }; }; };有几个细节要特别留意。第一个OPP表里voltage-v2的字段是opp-microvolt一个OPP可以配置三个电压分别对应normal、min、max我在这里只写了一个实际要按需求补全。第二个如果多个CPU core共享同一个频率域需要标记opp-shared否则调度器可能误以为每个core可以独立变频导致选频逻辑错乱。第三个power-domains引用关系必须和固件侧的电源域编号一致否则内核调用genpd开关时会报错或者固件收到一个根本不认识的电源域ID。配置完设备树可以用下面的命令验证当前生效的完整配置# 查看CPU支持的idle state cat /sys/devices/system/cpu/cpu0/cpuidle/state*/name cat /sys/devices/system/cpu/cpu0/cpuidle/state*/usage cat /sys/devices/system/cpu/cpu0/cpuidle/state*/time # 查看CPU频率和OPP cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq # 查看genpd状态 ls /sys/kernel/debug/pm_genpd/如果state0的usage始终为0说明CPU根本没有进入idle通常是被某段内核代码持锁或者被实时任务抢占了如果state2的usage始终为0多半是该状态的latency配置过高governor评估后认为不划算所以从来不选它。5.2 用powertop、ftrace和电流探头定位功耗问题软件调参和配置只是第一步真正要验证功耗效果必须把工具链配齐。我常用的调试组合是powertop做粗筛ftrace做细查电流探头/示波器做最终确认。powertop是最容易上手的工具。运行powertop --debug会扫描系统中的设备、中断和内核线程给出每个组件的大致功耗估算和唤醒次数排名。它还能一键开启所有设备的自动省电策略也就是powertop --auto-tune。这个命令适合快速定位“哪个外设在频繁唤醒系统”这类问题。注意powertop的功耗估算只是一个粗糙的模型不能替代真实电流测量但它对排序查找问题是足够高效的。ftrace在电源管理调试中的价值被很多人低估。比如要抓CPUIdle的进入退出时间可以这样# 查看CPUIdle相关事件 trace-cmd record -e cpuidle:* -e cpu_frequency:* -e power:* sleep 10 trace-cmd report | head -50这段命令会记录10秒内CPUIdle状态切换、CPU频率变化、电源域开关的完整事件流。从report里能清楚看到每个CPU在什么时间进入了哪个idle state、停留了多久、被什么中断唤醒。如果发现某CPU在极短时间内反复进出深度idle那就是“睡不沉”的典型表现通常和中断风暴或者governor配置有关。电流探头是功耗调优的裁判。建议至少准备一个能测到mA级别精度的电流探头卡在整机供电的主电源轨上。测量时先把系统稳定在桌面状态记录基准电流然后分别测CPU满载、浅idle、深idle、suspend几种状态的电流。对比设备树配置修改前后的电流差异能非常直观地判断改动是否有效。有一次我为了定位“某外设空载泄漏1mA”的问题就是通过电流探头加逐项断电最后发现是PMIC上某根GPIO默认状态配错导致一路LDO始终处于使能状态。这种问题用软件日志几乎是看不出来的必须靠硬件测量才能收口。6. 常见坑与排查技巧汇总6.1 睡不醒、反复醒、醒不来三类典型故障电源管理的问题最终都会表现为三类症状睡不醒、反复醒、醒不来。下面这张表是我这几年调试经验的浓缩遇到问题可以对号入座。症状可能原因排查命令/手段系统无法进入suspend有设备suspend回调返回错误有进程持有wakelockcat /sys/kernel/debug/wakeup_sourcescat /sys/power/wake_lock系统suspend后很快自动唤醒某设备中断使能且没被屏蔽wakeup_source频繁触发查看/sys/kernel/debug/wakeup_sources的active_count抓ftrace的irq事件CPU进入深度idle后无法被唤醒local-timer-stop属性配置不当唤醒中断没有路由到该CPU电源域isolate没做好检查GIC中断路由配置抓PSCI日志测量唤醒中断是否到达进入idle但电流没降电源域引用计数不为0时钟门控没生效外设还挂在电源域上检查/sys/kernel/debug/pm_genpd逐项卸载外设驱动测电流唤醒延迟过高退出idle后固件恢复上下文耗时DDR频率从低到高恢复慢Realm上下文恢复用ftrace的power:事件和irq:事件标定时间线对比关闭RME的延迟6.2 几个只有做底层才容易踩的坑再分享几个我踩过的坑都是网上文档很少写、但实际开发中很常见的问题。第一个坑是内核和固件对PSCI power state编码的理解不一致。不同SoC的TF-A实现里power state的位域划分可能完全不同。有的固件用低4位表示power down层级有的用低8位有的把affinity level放在bit16有的放在bit24。内核设备树里填的arm,psci-suspend-param必须和固件对齐。我遇到过开发者从别家平台抄了一份设备树idle states配得漂漂亮亮但CPU就是不进深度睡眠查了三天最后发现是固件只认识0x100,内核一直在传0x10000。第二个坑是runtime PM的autosuspend超时时间设置不合理。设备不再工作时驱动通过pm_runtime_put_sync让设备进入idle但genpd会等待一段延迟再真正关电源。如果autosuspend设得太长比如10秒而设备每5秒就用一次那电源域永远等不到关闭的时机功耗就白白浪费了。这个值要根据设备的实际使用节奏来调不是越大越好也不是设成0最好。第三个坑是不同idle state之间的“连贯性”验证。CPUIdle的state层级设计是有依赖顺序的比如进入state2之前可能必须先进入state1完成某个保存动作但设备树只是平铺地描述每个state的延迟参数不负责描述依赖关系。这个依赖关系必须在固件的PSCI实现里保证。所以改idle states时至少要把state0到最深state都实际跑一遍记录每层的电流波形验证退出唤醒是否每次都成功。不能只看state2配置好看就认为它会生效中间哪个环节断了最深处那个state会变成一个永远进不去的摆设。还有一个经常忽略的是FIQ和Secure中断对电源管理的影响。在ARMv8/v9体系里EL3和安全世界的定时器、中断如果处理不当会频繁唤醒正在idle的CPU。而且这类唤醒很多时候在内核日志里看不到因为中断被固件直接处理了。排查这类问题需要在TF-A里打开FIQ日志或者用硬件的performance counter统计secure中断次数否则你根本猜不到是哪里来的唤醒源。调试ARM电源管理系统最大的体会是不要只盯着软件框架看。pinctrl配置、电源轨的电容大小、PMIC的默认上电状态、固件的PSCI实现任何一个环节不匹配都会在前面说的某个症状里爆发出来。我个人的建议是做功耗优化的朋友至少先把SoC的TRM里Power Domain章节完整读一遍然后在你手里那块开发板上用电流探头把idle、suspend、resume这几个典型场景的电流波形全部抓出来存档。后续每次改动只需对比波形很多争论不清的问题就能一眼看出来。这套方法我带过好几个团队真的是最直接也最可靠的一条路。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价