介绍 Standby 模块配置和调试方法。
Standby 模块开发、维护人员。
表 1-1: 适用产品列表
产品名称 | 内核版本 | 驱动文件 |
---|---|---|
T509 | Linux-4.9 | kernel/power/* |
MR813 | Linux-4.9 | kernel/power/* |
R818 | Linux-4.9 | kernel/power/* |
A133 | Linux-4.9 | kernel/power/* |
H616 | Linux-4.9 | kernel/power/* |
T507 | Linux-4.9 | kernel/power/* |
R328 | Linux-4.9 | kernel/power/* |
T3 | Linux-4.9 | kernel/power/* |
V853 | Linux-4.9 | kernel/power/* |
• 休眠唤醒指系统进入低功耗和退出低功耗模式,一般称之为 Standby。standby 分为 super standby 和 normal standby,区别是 cpu 是否掉电。
• 假关机是类似 standby 的一种低功耗模式。进入假关机,系统会先复位,再进入低功耗模式,等待唤醒源;检测到唤醒源,系统退出假关机,直接从低功耗模式复位重启。适用于 OTT 类产品代替常规的关机,实现红外/蓝牙开机功能。
表 2-1: 术语介绍
术语 | 说明 |
---|---|
Super standby | Vdd_cpu 掉电或 Core 掉电,dram 进入 self refresh 状态 |
Normal standby | CPUX WFI,dram 进入 self refresh 状态 |
Fake Poweroff | 假关机,类似 standby,主要区别是系统退出假关机会重启,而不是唤醒 |
SCP/CPUS | 全志平台辅助进行电源管理的协处理器 |
设备树中存在的是该类芯片所有平台的模块配置,设备树文件的路径为:kernel/linux-4.9/arch/arm64(32 位平台为 arm)/boot/dts/sunxi/CHIP.dtsi(CHIP 为研发代号,如sun50iw10p1 等)。Standby 模块在 dtsi 中无用户可用配置。
board.dts 用于保存每一个板级平台的设备信息(如 demo 板,perf1 板等),里面的配置信息会覆盖上面的 Device Tree 默认配置信息。
• standby 参数配置
描述系统资源的相关信息。例如,“vdd-cpu = <0x00000006>;”,其中各个 bit 代表 PMU 的各路供电,则 vdd-cpu 使用 PMU的第二、三路供电。
“osc24m-on = <0x1>;”,表示系统休眠后 osc24m 是否关闭,0x0 表示关闭,0x1 表示不关闭。
standby_param: standby_param {
vdd-cpu = <0x00000006>;
vdd-sys = <0x00000008>;
vcc-pll = <0x00000100>;
osc24m-on = <0x1>;
...
};
注:由于 r528/v853 部分方案, 没有外挂 PMU,即硬件上不支持 vdd-sys/vdd-cpu 掉电,故R528/v853 board.dts 不需要该信息(standby_param)。
• 唤醒源配置
以 RTC 模块为例,RTC 驱动支持通过 “wakeup-source” 配置是否作为唤醒源;在 RTC 模块节点下添加 “wakeup-source” 属性,则可以设置为唤醒源。
rtc: rtc@07000000 {
compatible = "allwinner,sunxi-rtc";
device_type = "rtc";
wakeup-source;
...
};
• GPIO 为唤醒源配置
下面以 gpio-key 驱动为例, 演示一下 gpio 作为 wakeup-source 的代码编写:
keys {
compatible = "gpio-keys";
status = "okay";
wakeup {
wakeup-source; (1)
gpios = <&pio PH 9 6 2 2 1>; (2)
label = "wakeup";
linux,code = <KEY_WAKEUP>;
};
};
1.wakeup-source:device的wakeup source属性.需要设备驱动自己去解析该属性,如果有,则表示设备具有wakeup source功能.
2.gpios:配置gpio的mux,pull,drive,data等属性,如上的配置代表把PH9设置为6号功能(中断功能),下拉,驱动能力为2,data值为1.
• 假关机参数配置
描述系统关机的方式及假关机时需要用到的系统资源。如:
box_start_os0 {
compatible = "allwinner,box_start_os";
start_type = <0x1>;
irkey_used = <0x0>;
pmukey_used = <0x0>;
pmukey_num = <0x0>;
led_power = <0x0>;
led_state = <0x0>;
pinctrl-0 = <&standby_blue>;
pinctrl-1 = <&standby_red>;
pinctrl-2 = <&standby_bt>;
/*pinctrl-3 = <&standby_bt>;*/
}
...
s_cir0 {
s_cir0_used = <1>;
ir_power_key_code0 = <0x40>;
ir_addr_code0 = <0xfe01>;
ir_power_key_code1 = <0x1a>;
ir_addr_code1 = <0xfb04>;
ir_power_key_code2 = <0xf2>;
ir_addr_code2 = <0x2992>;
ir_power_key_code3 = <0x57>;
ir_addr_code3 = <0x9f00>;
ir_power_key_code4 = <0xdc>;
ir_addr_code4 = <0x4cb3>;
ir_power_key_code5 = <0x18>;
ir_addr_code5 = <0xff00>;
ir_power_key_code6 = <0xdc>;
ir_addr_code6 = <0xdd22>;
ir_power_key_code7 = <0x0d>;
ir_addr_code7 = <0xbc00>;
ir_power_key_code8 = <0x4d>;
ir_addr_code8 = <0x4040>;
wakeup-source;
}
“start_type = <0x1>;”,0x0 代表当系统启动时检测到是适配器上电启动则进入假关机(H700 TV);0x1 不做适配器上电检测判断,按普通的假关机判断流程;
“irkey_used = <0x0>;”,当前代码并未解析该节点,为无用节点,不影响假关机流程;
“pmukey_used = <0x0>;”,当前代码并未解析该节点,为无用节点,不影响假关机流程;
“pmukey_num = <0x0>;”,当前代码并未解析该节点,为无用节点,不影响假关机流程;
“led_power = <0x0>;”,当前代码并未解析该节点,为无用节点,不影响假关机流程;
“led_state = <0x0>;”,当前代码并未解析该节点,为无用节点,不影响假关机流程;
“pinctrl-0 = <&standby_blue>;”,LED 显示需要用到的 pinctrl 配置(其中一种颜色);
“pinctrl-1 = <&standby_red>;”,LED 显示需要用到的 pinctrl 配置(其中一种颜色);
“pinctrl-2 = <&standby_bt>;”,外部唤醒源(如蓝牙/wifi/rtc 模块)唤醒系统时拉对应 gpio,高脉冲有效,高脉冲持续时间需要大于 200 ms;
“pinctrl-3 = <&standby_bt>;”,外部唤醒源(如蓝牙/wifi/rtc 模块)唤醒系统时拉对应 gpio,低脉冲有效,低脉冲持续时间需要大于 200 ms;
“ir_power_key_code = <0x40>;”,ir 模块特定数据的码值,根据方案需求进行配置;
“ir_addr_code = <0xfe01>;”,ir 模块特定地址的码值,根据方案需求进行配置。
表 2-2: 平台支持唤醒源列表
平台/唤醒源 | 非CPUS域GPIO | CPUS域GPIO | POWER-KEY | RTC | USB | 红外 | 蓝牙/WiFi | MAD |
---|---|---|---|---|---|---|---|---|
T509 | 不支持 | super standby | super standby | super standby | super standby | 不支持 | super standby | 不支持 |
H616 | normal standby | 不支持 | normal standby | normal standby | normal standby | normal standby | normal standby | 不支持 |
V853 | normal standby | 不支持 | super standby | super standby | super standby | 不支持 | normal standby | 不支持 |
T113 | normal standby | 不支持 | 不支持 | normal standby | 不支持 | 不支持 | normal standby | 不支持 |
linux-4.9 内核版本:在命令行中进入 linux 目录,执行 make ARCH=arm64 menuconfig(32 位系统为 make ARCH=arm menuconfig) 进入配置主界面 (Linux-5.4 内核版本执行:./build.sh menuconfig),并按以下步骤作。
• 内核 PSCI 选项
Kernel Features --->
[*] Support for the ARM Power State Coordination Interface (PSCI)
• 内核 CPUIDLE 相关选项(可选)
CPU Power Management --->
CPU Idle --->
[*] CPU idle PM support
ARM CPU Idle Drivers --->
[*] Generic ARM/ARM64 CPU idle Driver
• 内核 POWER 相关选项
Power management options --->
[*] Suspend to RAM and standby
[ ] Opportunistic sleep
[*] User space wakeup sources interface
(100) Maximum number of user space wakeup sources (0 = no limit)
-*- Device power management core functionality
[*] Power Management Debug Support
[*] Extra PM attributes in sysfs for low-level debugging/testing
• 内核 FAKE_POWEROFF 相关选项
Device Drivers --->
[*] Real Time Clock --->
[*] support ir fake poweroff
• uboot-2018 FAKE_POWEROFF 相关配置
在平台的 defconfig 中,将 CONFIG_ATF_BOX_STANDBY 配置为 Y
CONFIG_ATF_BOX_STANDBY = Y;
Standby 的源代码位于内核 kernel/power/目录下:
kernel/power/
├── autosleep.c
├── console.c
├── hibernate.c
├── Kconfig
├── main.c
├── Makefile
├── modules.builtin
├── modules.order
├── power.h
├── poweroff.c
├── process.c
├── qos.c
├── snapshot.c
├── suspend.c
├── suspend_test.c
├── swap.c
├── user.c
├── wakelock.c
└── wakeup_reason.c
休眠唤醒指系统进入低功耗和退出低功耗模式,一般称之为 Standby。休眠过程由应用发起,经由内核的电源管理框架来进行休眠唤醒管理工作,如果存在 CPUS(一颗集成在 IC 内部的对电源进行管理的 openrisc 核,是 SoC 内置的超低功耗硬件管理模块),最终会传递到到 CPUS。因此休眠唤醒类出现问题的可能为应用层、内核层、CPUS 层,如果不存在 CPUS,则 CPU 进入WFI。休眠唤醒流程图如下,虚线部分为部分内核实现。
图 2-1: standby 驱动总体结构
图 2-2: linux standby 流程
• pm_test 节点
该节点可用于测试 linux 部分休眠唤醒功能。Eg:echo x > /sys/power/pm_test。
Freezer:表明,任务冻结后,等待 5s,即返回,执行唤醒动作。
Devices: 表明,设备冻结后,等待 5s, 即返回,执行唤醒动作。
Platform:在 a1x, a2x, a3x 上,与 devices 相同;
Processors: 冻结 non-boot cpu 后,等待 5s, 即返回,执行唤醒动作。
Core: 冻结 timer 等系统资源后,等待 5s, 即返回,执行唤醒动作。
None: 表明,整个休眠流程全部走完,等待唤醒源唤醒。
• wake_lock 节点
该节点可查看安卓系统 wake lock 状态,安卓系统在持锁时不会进入深度睡眠流程 (Suspend-to-mem)。Eg: cat /sys/power/wake_lock。
• wakeup_sources 节点
该节点可查看系统唤醒源的情况。Eg:cat /sys/kernel/debug/wakeup_sources。
问题现象
休眠后,自动被唤醒,过会自动进入休眠,屏幕黑屏,串口有输出。
问题分析
系统休眠后自动被唤醒,原因可能是,某些应用或者后台进程,通过设置闹钟的方式,定时唤醒系统。
当出现如下打印,表示 Linux 已经休眠完成,准备进入 CPUS 休眠阶段:
[ 3465.885063] PM: noirq suspend of devices complete after 16.487 msecs
[ 3465.892225] Disabling non-boot CPUs ...
......
当出现以上打印后自动唤醒,则查看如下打印:
[ 3466.063570] wake up source:0x80000 //H3 linux4.4平台
[21676.174594] [pm]platform wakeup, standby wakesource is:0x100000 //H5/H6 linux-3.10平台
后面的数字代表唤醒源,根据数字定位唤醒源,定位唤醒源后再判断为何被唤醒。
WAKEUP_SRC is as follow:
CPUS_WAKEUP_LOWBATT bit 0x1000
CPUS_WAKEUP_USB bit 0x2000
CPUS_WAKEUP_AC bit 0x4000
CPUS_WAKEUP_ASCEND bit 0x8000
CPUS_WAKEUP_DESCEND bit 0x10000
CPUS_WAKEUP_IR bit 0x80000
CPUS_WAKEUP_ALM0 bit 0x100000
CPUS_WAKEUP_HDMI_CEC bit 0x100000
使用范围适用于非 psci1.0 版本,详见 dts 文件 psci 节点配置。
常见场景:android 某些应用或者后台进程,会通过设置闹钟的方式,定时唤醒系统,当判断唤醒源为 0x100000 时,大多数为该原因导致。
问题解决
确认是某些应用或者后台进程设置闹钟定时唤醒系统,方案开发人员可以自行解决。
问题现象
休眠后,被异常唤醒。
问题分析
系统休眠后被异常唤醒,原因可能是,被其他非预期的唤醒源唤醒。
查看唤醒源。对应的代码路径在:lichee/linux4.9/include/linux/power/aw_pm.h
/* the wakeup source of assistant cpu: cpus */
#define CPUS_WAKEUP_HDMI_CEC (1<<11)
#define CPUS_WAKEUP_LOWBATT (1<<12)
#define CPUS_WAKEUP_USB (1<<13)
#define CPUS_WAKEUP_AC (1<<14)
#define CPUS_WAKEUP_ASCEND (1<<15)
#define CPUS_WAKEUP_DESCEND (1<<16)
#define CPUS_WAKEUP_SHORT_KEY (1<<17)
#define CPUS_WAKEUP_LONG_KEY (1<<18)
#define CPUS_WAKEUP_IR (1<<19)
#define CPUS_WAKEUP_ALM0 (1<<20)
#define CPUS_WAKEUP_ALM1 (1<<21)
#define CPUS_WAKEUP_TIMEOUT (1<<22)
#define CPUS_WAKEUP_GPIO (1<<23)
#define CPUS_WAKEUP_USBMOUSE (1<<24)
#define CPUS_WAKEUP_LRADC (1<<25)
#define CPUS_WAKEUP_WLAN (1<<26)
#define CPUS_WAKEUP_CODEC (1<<27)
#define CPUS_WAKEUP_BAT_TEMP (1<<28)
#define CPUS_WAKEUP_FULLBATT (1<<29)
#define CPUS_WAKEUP_HMIC (1<<30)
#define CPUS_WAKEUP_POWER_EXP (1<<31)
#define CPUS_WAKEUP_KEY (CPUS_WAKEUP_SHORT_KEY | CPUS_WAKEUP_LONG_KEY)
查看关键打印:
platform wakeup, standby wakesource is:0x800000
此时对应的是 gpio 唤醒。
问题解决
确认是其他非预期的唤醒源唤醒系统,方案开发人员可以自行解决。
问题现象
系统休眠后无法唤醒。
问题分析
系统休眠后无法唤醒,原因可能有:
• 模块休眠失败。
查看是否模块休眠失败,输入以下命令,确认是否在内核休眠唤醒模块出异常。
echo N > /sys/module/printk/parameters/console_suspend
echo 1 > /sys/power/pm_print_times
• 若上述无异常打印,则认为是 Linux 后的阶段出现异常。
不断电重启系统,将启动时候的 RTC 寄存器的信息发给休眠模块负责人,根据 RTC 寄存器信息判断。
[2341]HELLO! pmu_init stub called!
[2645]set pll start
[2648]set pll end
[2649]try to probe rtc region
[2652]rtc[0] value = 0x00000000
[2655]rtc[1] value = 0x000000e0
[2658]rtc[2] value = 0xf1f18000
[2661]rtc[3] value = 0x0000000f
[2663]rtc[4] value = 0x00000000
[2666]rtc[5] value = 0x00000000
问题解决
• 模块休眠失败。确认是模块休眠失败,方案开发人员可以自行解决。
• Linux 后的阶段出现异常。将复位重启时的 RTC 寄存器信息发给相关负责人。
问题现象
休眠后,唤醒源无法唤醒系统,串口没有输出。
问题分析
休眠后,唤醒源无法唤醒,可能是唤醒源不支持。
• cpus 休眠后异常。
当出现如下打印时表示 Linux 休眠已经完毕,此时唤醒不了,则可能是 cpus 退出休眠失败,或者唤醒源不对。
[ 3465.885063] PM: noirq suspend of devices complete after 16.487 msecs
[ 3465.892225] Disabling non-boot CPUs ...
......
通过以下手段可以判断 cpus 休眠后是否正常运行,以下命令表示休眠后 cpus 过一定时间软件自动唤醒。
如果串口能正常打印,wake up source 为 0x400000,则表示 cpus 是正常运行的,这时应该排查一下系统是否支持相应的唤醒源。
• 唤醒源不支持。
确认唤醒源不支持的情况。
问题解决
• cpus 休眠后异常。
将复位重启时的 RTC 寄存器信息发给相关负责人。
• 唤醒源不支持。
将唤醒源的情况发给相关负责人。
问题现象
红外遥控不能唤醒系统。
问题分析
红外遥控唤醒需要配置唤醒源。
问题解决
红外遥控器默认支持 NEC 红外协议遥控器唤醒,也支持 RC5 红外协议遥控器唤醒,但是需要在sys_config.fex 进行配置,配置如下:
;--------------------------------------------------------------------------------
;ir --- infra remote configuration
;ir_protocol_used : 0 = NEC / 1 = RC5
;--------------------------------------------------------------------------------
[s_cir0]
s_cir0_used = 1
ir_used = 1
ir_protocol_used = 0 //置为0 支持NEC 红外协议遥控唤醒,配置为0 支持RC5 红外协议遥控唤醒
对于同一协议的红外遥控器,能支持唤醒的个数也是有限的,具体在 sys_config.fex 配置 s_cir0 节点。
ir_power_key_code0 = 0x57 //遥控POWER 值
ir_addr_code0 = 0x9f00 //遥控设备码
ir_power_key_code1 = 0x1a
ir_addr_code1 = 0xfb04
问题现象
USB 不能唤醒系统。
问题分析
USB 需要设置为唤醒源。
问题解决
USB 设备唤醒需要系统支持 USB_STANDBY,需要在 sys_config.fex 配置 usbc* 节点。需要
注意 [usbc0] usb_wakeup_suspend = 1 //1 表示支持 USB0 唤醒,0 表示屏蔽该唤醒源。
问题现象
HDMI 不能唤醒系统。
问题分析
HDMI 需要设置为唤醒源。
问题解决
需要在 sys_config.fex 配置 hdmi 节点。
[hdmi]
hdmi_cec_support = 1
hdmi_cec_super_standby = 1
问题现象
休眠后,无法唤醒,串口没有输出。
问题分析
可能是 cpus 退出休眠失败。
如果通过写 time_to_wakeup 命令,系统没法正常唤醒,则考虑是 cpus 退出休眠失败的了,这时需要短接 reset 脚重启系统(注意不是完全断电,完全断电将无法保留 RTC 值),然后将 boot 阶段打印的 RTC 码值发送给休眠唤醒负责人,定位问题。
[2341]HELLO! pmu_init stub called!
[2645]set pll start
[2648]set pll end
[2649]try to probe rtc region
[2652]rtc[0] value = 0x00000000
[2655]rtc[1] value = 0x000000e0
[2658]rtc[2] value = 0xf1f18000
[2661]rtc[3] value = 0x0000000f
[2663]rtc[4] value = 0x00000000
[2666]rtc[5] value = 0x00000000
问题解决
• 确认是否 dram 错误。
• 确认是否上下电时序错误。
• 将复位重启时的 RTC 寄存器信息发给相关负责人。
问题现象
系统持锁,suspend 失败。
问题分析
suspend 失败,可能是系统持锁阻止休眠。
问题解决
• 安卓查看是否有持锁相关信息:
dumpsys power | grep PART
• 内核中是否有相关持锁信息:
cat /sys/kernel/debug/wakeup_sources
查看 active_since 项,若对应模块不为 0,则该模块一直阻止系统进入休眠,查看该模块是否异常;
cat /sys/power/wake_lock
查看是否有安卓申请的锁。
• Linux devices driver suspend 失败:
问题现象
定时休眠到时后,屏幕亮屏,串口可以输入,系统无法休眠。
问题分析
系统无法休眠,确认 Android 是否支持,是否禁止定时休眠。
串口输入 dumpsys power,查看如下打印。如果发现 mStayOn=true,则系统是不支持定时休眠功能,可以将该属性设置成 false;另外 screen off timeout 表示休眠时间,可通过该值判断你设置的定时时间是否正确;如果是 androidN ,screen off timeout 还取决于 Sleep timeout,Sleep timeout 的值比 Screen off timeout 小时,系统取 Sleep timeout 当做定时休眠的时间,当 Sleep timeout 为-1 时,也表示系统无法定时休眠。如果是以上情况可咨询 android 系统的同事,修改相应的属性。
Power Manager State:
......
mStayOn=true //true 保持常亮,false 可以支持定时休眠
Sleep timeout: -1 ms //只有androidN 平台无该该值,定时休眠时间
Screen off timeout: 1800000 ms //定时灭屏时间
Screen dim duration: 7000 ms //屏保时间
常见场景:eng 固件开发阶段为了测试长时间老化的测试项,会禁止系统定时进入休眠。
问题解决
确认是 Android 系统持锁阻止休眠,方案开发人员可以自行解决。
问题现象
在 Linux 某个阶段出现的休眠或者唤醒失败。
问题分析
打开内核选项
CONFIG_PM_DEBUG=y
查看具体失败在哪个阶段:
问题解决
确认是哪个阶段出现的休眠或者唤醒失败,方案开发人员可以自行解决。