1. 平台按键概述
| PWRKEY(电源键) | KPDPWR_N | 低有效,按下 = 下降沿 | 开机触发、S2 长按复位、OS 侧电源键事件 |
| RESETKEY(复位/音量下) | RESIN_N | 低有效 | 单独复位(本项目关闭)/ 音量下键 / 组合键 |
| 组合键 | KPDPWR_N + RESIN_N | 两键同按 | 强制恢复类操作(s2-kpdpwr-resin) |
按键功能全部由 PMIC PON(Power-On)外设 实现,软件链路分三层:
DT 配置 (pm.dtsi) → PmicLib 解析+编程 (PON 寄存器) → EFI 协议 (PmicDxe) → 各 DXE/库消费
2. DT 基本配置(pm.dtsi)
节点:&sw { pmic { pm { pon { … } } } },binding 为 qcom,pm-pon
文件:boot/Settings/Soc/Kuno/Core/PMIC/pm.dtsi,schema:boot/Settings/dtschema/Common/Core/PMIC/qcom,pm-pon.yaml(additionalProperties: false,节点内属性不可随意增加)
| pshold | enable / reset-type | PM_TRUE / PM_HARD_RESET | PS_HOLD 使能;掉 PS_HOLD 时的复位类型 |
| smpl | enable / smpl-delay | PM_FALSE / 800ms | 电池突然拔出(SMPL)保护关机,当前关闭 |
| s2-kpdpwr | enable / reset-type | PM_TRUE / PM_WARM_RESET | 长按电源键两段复位总开关及复位类型 |
| s1-ms | 10256 | 第一段判键时间(按住这么久判定为"长按") | |
| s2-ms | 2000 | 第一段达成后再持续这么久触发复位 | |
| s2-kpdpwr-resin | enable / reset-type | PM_TRUE / PM_HARD_RESET | 电源键+音量下(RESIN)组合键 |
| s1-ms / s2-ms | 10256 / 2000 | 同上 | |
| s2-resin | enable | PM_FALSE | 单独按 RESIN 触发复位,当前关闭 |
| s3-reset | enable / s3-src / timer-value-ms | PM_FALSE / KPDPWR / 16000 | 第三段复位(可选,Kuno 关闭) |
| uvlo-config / ovlo-config | config-value | 0 | 欠压/过压阈值(4-bit 编码) |
| long-pwrkey-dbnc-chk | dbnc-time-ms / chk-at | 200 / 2 | boot 侧"有效长按启动"软校验(load-side 校验点) |
宏定义(boot/Settings/Include/pm_defines.h):PM_WARM_RESET=0、PM_HARD_RESET=1、PM_PON_RESET_SOURCE_KPDPWR 等。
时长档位表(PON 定时器不是连续值,是硬件档位)
| S1 | 0,32,56,80,128,184,272,408,608,904,1352,2048,3072,4480,6720,10256 | pm_pon.c:286-288 |
| S2 | 0,10,50,100,250,500,1000,2000 | pm_pon.c:290-292 |
| S3 | 0,8,16,32,63,125,250,500,1000,2000,…,128000 | pm_pon.c:294-296 |
| PON 去抖 | 60,120,240,490,980,2000,3900,7800,…,250000 | pm_pon.c:302-304 |
换算用 pm_utils_get_upper_idx()(PmicLib/utils/inc/pm_utils.h:111):取 ≥ 目标值 的最近档。
即 dtsi 里 s1-ms=10256、s2-ms=2000 都是各自档位表的最大档——长按电源键 10.256s 判定进入 S1,再保持 2s 才触发复位。
3. 按键行为映射
| 开机态短按 PWRKEY | PMIC 产生 KPDPWR_ON 中断 / OS 侧 input event(开机后由 kernel 负责) |
| 开机态长按 10.256s | PON S1 阶段达成(软件可据此开始关机/重启流程) |
| 再持续 2s(累计 12.256s) | S2 触发 → reset-type = PM_WARM_RESET(暖复位,SoC 重启) |
| 电源键+音量下同时按 12.256s | S2 触发 → PM_HARD_RESET(PMIC 级硬复位) |
| 单独按 RESIN | 无复位(s2-resin=关闭);boot 中当"音量下键"轮询 |
| 关机态按 PWRKEY | 上电触发(当前默认下降沿,短按即开机,见第 6 节可改) |
⚠️ S2 是"复位"不是"关机":触发后 PMIC 重新给系统上电。产品上"长按关机并保持关机"必须走正常软关机(PS_HOLD 拉低),不能依赖 S2 通路。
Kuno target 另有 target 级有效按键校验:pm_config_target.c:237(chk_at=CORE、dbnc=400ms,与 dtsi 的 200ms/LOADER 是两处独立软校验)。
4. 按键加载全过程(函数链)
4.1 PMIC DT 解析(PmicLib)
pm_dt_get_node(PMIC_DT_NODE_PM_PON) // pm_dt_parser.c:980
└─ pm_target_information_dt_init() // DT blob 初始化
└─ pm_dt_parse_pon_config(prop_id) // pm_dt_parser.c:268
├─ pm_get_uint8_value ( &hNode, "enable", …) // 各子节点 enable
├─ pm_get_uint8_value ( &hNode, "reset-type",…) // PM_WARM/HARD_RESET
├─ pm_get_uint32_value( &hNode, "s1-ms", …)
├─ pm_get_uint32_value( &hNode, "s2-ms", …)
├─ pm_get_uint32_value( &hNode, "smpl-delay",…)
├─ pm_get_uint8_value ( &hNode, "s3-src", …) + "timer-value-ms"
├─ pm_get_uint8_value ( &hNode, "chk-at", …) + "dbnc-time-ms"
└─ → 填充 pm_dt_pon_config 结构体 // pm_dt_parser.h:167-202
4.2 PON 配置(后初始化,XBL 启动时执行一次)
pm_device_post_init() // target/kuno/system/src/pm_sbl_boot_oem.c:531
├─ pm_print_psi_version()
├─ pm_sbl_boot_pon_reset_config() // pm_sbl_boot_oem.c:456
│ ├─ pon_dt = pm_dt_get_node(PMIC_DT_NODE_PM_PON)
│ ├─ pm_app_pon_pshold_cfg(ps_hold_reset_type) // app/pon/src/pm_app_pon.c:70
│ ├─ pm_app_pon_reset_cfg(PM_APP_PON_RESET_SOURCE_KPDPWR, // pm_app_pon.c:142
│ │ WARM_RESET, 10256, 2000)
│ │ ├─ pm_app_pon_map_app_level_reset_resource() // app→drv 资源映射
│ │ ├─ pm_tgt_get_kpdpwr_reset_cfg() // 每颗 PMIC 差异化配置
│ │ ├─ pm_pon_reset_source_cfg() // drivers/pon/src/pm_pon.c
│ │ │ ├─ pm_utils_get_upper_idx(s1, pon_s1_timer_val) // ms→档位
│ │ │ ├─ pm_utils_get_upper_idx(s2, pon_s2_timer_val)
│ │ │ ├─ 写 KPDPWR_N_RESET_S1_TIMER / S2_TIMER 寄存器
│ │ │ └─ 写 RESET_S2_SW_CTL: reset_type + S2_RESET_EN
│ │ └─ pm_pon_tgt_specific_kpd_reset_cfg()
│ ├─ pm_app_pon_reset_cfg(RESIN_AND_KPDPWR, HARD_RESET, 10256, 2000) // pwr+vol down
│ │ (disable 时改调 pm_pon_reset_source_ctl(PMIC_A, 源, PM_OFF))
│ ├─ pm_app_pon_reset_cfg(RESIN, …) / pm_pon_reset_source_ctl(RESIN, PM_OFF)
│ └─ pm_app_pon_s3_reset_timer_cfg(s3_src, timer_ms) // pm_app_pon.c:213
├─ pm_pon_tgt_uvlo_ovlo_cfg() // UVLO/OVLO config-value
└─ pm_app_pwrkey_long_press_check(PM_PON_PWRKEY_DBNC_CHK_AT_LOADER) // app/pon/src/pm_app_key_press.c:53
├─ 取 dbnc_chk 配置(chk_at / dbnc_time_msec)
├─ 读 PMIC PON 实时 IRQ 状态并等待 dbnc_time_msec
├─ 读 SDAM 按键按下/释放时间(pm_config_target.c: MEM_026/030)
└─ 判定本次启动是否为"有效长按"(影响 PON reason/特殊模式判定)
4.3 运行期按键消费(EFI 协议使用者)
ButtonsInit() // QcomPkg/Library/ButtonsLib/ButtonsLib.c:301
├─ gBS->LocateProtocol(PmicPONProtocol / PmicGpioProtocol / TLMMProtocol)
├─ 配置按键 GPIO
└─ ReadRealTimeIRQStatus(EFI_PM_PON_IRQ_KPDPWR_ON, …) // ButtonsLib.c:463
PollPowerKey() → 电源键 RT 状态 // ButtonsLib.c:459
PollButtonArray()→ pwr / vol+ / vol-(EFI_PM_PON_IRQ_RESIN_ON) / home / camera
↓ 被消费
ButtonsDxe DriverEntry → PollButtonArray() // Drivers/ButtonsDxe/ButtonsDxe.c:403(按键服务/热键)
KeypadDxe(Deprecated) → PollPowerKey() // Drivers/Deprecated/KeypadDxe/KeypadDxe.c:371
QcomChargerDxe(关机充电等场景,Drivers/QcomChargerDxe/WP/QcomChargerPlatform_Fw.c)
├─ SetIrqTriggerType(0, EFI_PM_PON_IRQ_KPDPWR_ON, RISING_EDGE) // L339 等"松手"判定
├─ EnableIrq(0, KPDPWR_ON, TRUE) // L340
└─ GetPonIrqStatus(LATCHED) / ClearIrq // L343/L463
ChargerExDxe / CrashDumpDxe 等:LocateProtocol(gQcomPmicPwrOnProtocolGuid) 后读
GetPonReason / GetAllPonReasons / GetAllFaultReasons 判定上次启动原因
4.4 协议层(PmicDxe → PmicLib)
协议安装:Drivers/PmicDxe/Pmic.c:180(gQcomPmicPwrOnProtocolGuid → PmicPwronProtocolImplementation)
| GetPonRtStatus | pm_pon_get_pon_rt_status |
| EnableEdgePonTrigger(L605) | pm_pon_set_option_bit(上电触发沿,见第 6 节) |
| IrqEnable / IrqClear | pm_pon_irq_enable(pm_pon.c:1289)/ pm_pon_irq_clear |
| IrqSetTrigger(L270) | pm_pon_hlos_irq_set_trigger(pm_pon_hlos.c:229)/ PM8250 走 pm_pon_irq_set_trigger |
| GetPonIrqStatus | pon_hlos_irq_map 映射(PmicPonProtocol.c:143)后读状态 |
| GetPonReason / GetAllPonReasons / GetAllFaultReasons | pm_pon_get_pon_reason(s) / fault reason |
| WarmResetStatus | pm_pon_warm_reset_status |
| GetSpareReg / SetSpareReg | pm_pon_get_spare_reg / pm_pon_set_spare_reg |
| PON IRQ 类型 | EFI_PM_PON_IRQ_KPDPWR_ON、RESIN_ON、KPDPWR_RESIN_BARK;触发类型 PM_IRQ_TRIGGER_{RISING,FALLING,ACTIVE_LOW,ACTIVE_HIGH,DUAL}_EDGE(pm_resources_and_types.h:157) |
5. Linux 侧补充说明
进入 kernel 后 pwrkey 事件由 qcom-pon / gpio-keys 驱动产生(interrupt-type 在 kernel dts 配),不属于 bp/boot 改动范围;boot 里配的内容(S2 复位、上电触发沿、去抖)在关机态依然由 PMIC 硬件执行。
6. 修改 PWRKEY 上电触发方式(沿触发 → 电平触发,实现"按住 2s 才开机")
6.1 原理
KPDPWR_N 低有效:按下 = 下降沿。上电触发类型由 PON option bit 决定(drivers/pon/src/pm_pon.c:1762-1767):
| pm_pon_set_option_bit(PMIC_A, PM_PON_OPTION_KPDPWR_FEDGE_PON, TRUE) | set_type=edge + 低极性:按下瞬间即触发 PON(现状默认行为,短按就开机;关机后一按就亮) |
| pm_pon_set_option_bit(PMIC_A, PM_PON_OPTION_KPDPWR_FEDGE_PON, FALSE) | set_type=level + 低极性清零:电平触发,必须持续按住才可能上电;中途松手/点按均不触发,再次按下硬件去抖计数器重新计时 |
| pm_pon_debounce_ctl(PMIC_A, 2000) | 写 PMIO_PON_PBS_DEBOUNCE_CTL(pm_pon.c:1268),档位表 pon_debounce_time_val[] 精确含 2000ms 档;电平模式下按住 ≥2s 才 PON |
两个 API 配合 = 目标时序:关机态点按/按一下松手 → 保持关机;重新按下 → 重新计 2s;持续 2s → 开机。
(该函数同时把配置写入 SDAM 的 PON_TRIG_CFG 与 PMIC 中断寄存器,关机后仍生效。)
6.2 落点与示例
加在 pm_sbl_boot_pon_reset_config() 或 pm_device_post_init()(target/kuno/system/src/pm_sbl_boot_oem.c)末尾,每次开机执行一次最稳:
pm_err_flag_type
pm_device_post_init( void )
{
pm_err_flag_type err_flag = PM_ERR_FLAG_SUCCESS;
err_flag |= pm_print_psi_version();
err_flag |= pm_sbl_boot_pon_reset_config();
err_flag |= pm_pon_tgt_uvlo_ovlo_cfg();
err_flag |= pm_app_pwrkey_long_press_check(PM_PON_PWRKEY_DBNC_CHK_AT_LOADER);
/* === 新增:PWRKEY 上电改电平触发 + 2s 去抖 === */
err_flag |= pm_pon_set_option_bit(PMIC_A, PM_PON_OPTION_KPDPWR_FEDGE_PON, FALSE);
err_flag |= pm_pon_debounce_ctl(PMIC_A, 2000);
return err_flag;
}
注意:
- pm_pon_set_option_bit() 已在 Include/api/pmic/pm/pm_pon.h:1148 导出;pm_pon_debounce_ctl() 未在任何头文件导出(全树仅定义处一处),调用处需 extern 声明:extern pm_err_flag_type pm_pon_debounce_ctl(uint32 pmic_device_index, uint32 time_delay);
- EFI 协议侧等价接口:PmicPwrOnProtocol->EnableEdgePonTrigger(0, EFI_PM_PON_OPTION_KPDPWR_FEDGE_PON, FALSE)(PmicPonProtocol.c:605)
- 也可不改 XBL,在 kernel 侧写相同寄存器(PMIC PON/SDAM regmap),但推荐 XBL 一次配好,POR 之前即生效
- 只改 KPDPWR 一路,USB 插入 / DC 充电等其它 PON 源不受影响
- 与 S2 长按复位(s1/s2 定时器)是独立通路,互不影响
6.3 验证(冷关机后)
7. 文件速查表
| boot/Settings/Soc/Kuno/Core/PMIC/pm.dtsi | PON DT 配置(pshold/smpl/s2-*/s3/long-pwrkey-dbnc-chk) |
| boot/Settings/dtschema/Common/Core/PMIC/qcom,pm-pon.yaml | binding schema(属性封闭,不可随意加属性) |
| boot/Settings/Include/pm_defines.h | PM_ENABLE/PM_WARM_RESET/PM_PON_RESET_SOURCE_* 宏 |
| QcomPkg/Library/PmicLib/utils/src/pm_dt_parser.c | DT 解析 pm_dt_parse_pon_config / pm_dt_get_node |
| QcomPkg/Library/PmicLib/utils/inc/pm_dt_parser.h | pm_dt_pon_config 结构体 |
| QcomPkg/Library/PmicLib/target/kuno/system/src/pm_sbl_boot_oem.c | PON 后初始化入口 pm_device_post_init / pm_sbl_boot_pon_reset_config |
| QcomPkg/Library/PmicLib/app/pon/src/pm_app_pon.c | pm_app_pon_reset_cfg / pm_app_pon_pshold_cfg / pm_app_pon_s3_reset_timer_cfg |
| QcomPkg/Library/PmicLib/app/pon/src/pm_app_key_press.c | pm_app_pwrkey_long_press_check(有效长按校验) |
| QcomPkg/Library/PmicLib/app/pon/src/pm_shutdown.c | 关机/复位序列(pm_app_pon_pshold_cfg(PON_SHDN/PON_WARM_RST/…)) |
| QcomPkg/Library/PmicLib/drivers/pon/src/pm_pon.c | PON 寄存器驱动:定时器档位表、pm_pon_reset_source_cfg/ctl、pm_pon_set_option_bit、pm_pon_debounce_ctl |
| QcomPkg/Library/PmicLib/drivers/pon/src/pm_pon_hlos.c | HLOS PON IRQ:pm_pon_hlos_irq_set_trigger 等 |
| QcomPkg/Library/PmicLib/drivers/pon/src/pmio_pon.h / pmic5/pmio_pon_pmic5.h | PON 寄存器地址/位定义(S1/S2/S3_TIMER、DEBOUNCE_CTL、OPTION_BIT…) |
| QcomPkg/Library/PmicLib/target/kuno/settings/pm_config_target.c | Kuno target 参数(有效按键 400ms@CORE、SDAM 偏移等) |
| QcomPkg/Library/PmicLib/target/kuno/system/src/pm_pon_target.c | PON trigger 配置表 pm_pon_target_get_pon_trigger_config |
| QcomPkg/Drivers/PmicDxe/PmicPonProtocol.c | EFI PWRON 协议实现(option bit / IRQ 触发沿 / PON reason) |
| QcomPkg/Drivers/PmicDxe/Pmic.c | PmicDxe 入口、协议安装(L180) |
| QcomPkg/Library/ButtonsLib/ButtonsLib.c | boot 按键轮询(PollPowerKey / PollButtonArray) |
| QcomPkg/Drivers/ButtonsDxe/ButtonsDxe.c | 按键服务 DXE(消费 PollButtonArray) |
| QcomPkg/Drivers/QcomChargerDxe/WP/QcomChargerPlatform_Fw.c | 关机充电场景的 KPDPWR IRQ 配置(L339 RISING_EDGE) |

