============================================================ Physical keys: power, volume, and the boot-mode dispatcher ============================================================ This device has exactly two physical buttons: **Power** (KPDPWR) and **Volume-Down** (RESIN). There is no Volume-Up button wired at the hardware level. Everything that is a physical key you actually press — including how ABL reads them at power-on to choose a boot mode — lives on this page. Everything else that merely uses a GPIO pin without being something you press (the hall-effect cover switch, the keyboard-cover connector pins) is *not* a key and stays in :doc:`/sensors/misc`. Two independent bindings ========================= The same two physical buttons are wired through **two separate, unrelated** DT paths, read by two different pieces of software at two different times: .. list-table:: :header-rows: 1 :widths: 26 20 54 * - Path - When it's read - Binding * - PMIC PON registers (``qcom,qpnp-power-on@800``) - Pre-boot, by XBL ``ButtonsDxe`` / ABL - ``KPDPWR`` → ``KEY_POWER`` (``0x74``), ``RESIN`` → ``KEY_VOLUMEDOWN`` (``0x72``); no ``gpio-keys`` node in this path, no VOL+ line at all * - Android ``gpio_keys`` node - At runtime, by the booted kernel - ``vol_up`` alone in ``fragment@28``; both ``vol_up``/``vol_down`` re-declared together in an override fragment, ``fragment@79`` — two DT fragments for the same logical device, not a discrepancy The boot-mode state machine below is entirely about the **first** path (PMIC PON, read by XBL/ABL before Android ever starts) — the ``gpio_keys`` node has nothing to do with boot-mode selection; it's how the running Android kernel later reports volume presses to apps. ABL selects its boot mode from the buttons read at power-on. It reads keystrokes as EFI scancodes from an ``EFI_SIMPLE_TEXT_INPUT_EX`` protocol installed by XBL ``ButtonsDxe``, dispatches on the first keystroke, and runs timed debounce loops that decode recovery, fastboot and download entry. RVAs are LinuxLoader-relative (= ABL DXE file offset − ``0xb8``); runtime = load base + RVA. Timing basis ============ Every debounce cycle calls ``gBS->Stall(0x186A0 = 100000 µs)`` via gBS at RVA ``0x51e38`` (``[gBS+0xF8]=Stall``, ``[+0x118]=OpenProtocol`` confirm it is Boot Services), giving approximately 100 ms per cycle. Cycle counts are code-exact; wall-clock assumes ``gBS->Stall`` microseconds (~100 ms/cycle). Key input and code-to-button map ================================= ABL reads keys via ``ReadKeyStroke`` on ``EFI_SIMPLE_TEXT_INPUT_EX`` (GUID ``DD9E7534-7762-4698-8C14-F58517A625AA`` at RVA ``0x4dcb4``; helper ``0x1d9a8``). The returned values are EFI ``EFI_INPUT_KEY.ScanCode``, not PMIC PON register bits. The physical-key-to-scancode mapper is XBL ``ButtonsDxe``, not ABL: it supports PWR/VOL+/VOL- via PMIC PON (KPDPWR/RESIN) plus PMIC GPIO (VOL+), and installs the ``SimpleTextInputEx`` that ABL consumes. ABL contains no scancode assignment (only a printf uses ``0x102``). .. list-table:: :header-rows: 1 :widths: 10 14 42 34 * - Code - EFI key - Physical button (this device) - ABL action * - ``0x01`` - SCAN_UP - VOL+ — generic; likely not wired, DTB has none - set recovery flag * - ``0x02`` - SCAN_DOWN - Volume-Down (RESIN) - BootIntoMode * - ``0x08`` - SCAN_DELETE - none on this 2-button device - BootIntoMode (dead path) * - ``0x17`` - SCAN_ESC - none - direct EDL (``RESET_PARAM="EDL"``) * - ``0x102`` - vendor - Power (KPDPWR) - ``GetBootIntoModeRecovery`` (5-tap) - Power = ``0x102`` is confirmed (consumed as power everywhere; "Pressed down Power key[0x102]"). - VolDown = SCAN_DOWN (``0x02``) is inferred (UEFI standard plus RESIN is the only non-power button); ``ButtonsDxe``'s keymap is data-driven, so the exact value was not byte-extractable. PMIC PON reason register ========================= Distinct from the keystroke codes. ``GetPonReason`` (``0x1968``) reads the PON reason register; per ABL's own log its bits are: bit3=USB, bit4=CBLPWR, bit5=PON1, bit6=DC, bit7=KPDPWR. This register is used only as the gate inside BootIntoMode (``arg & 0x10`` = CBLPWR), not as the 2/8 keystroke codes. XBL vocabulary matches (``KPDPWR/RESIN/PON1/SMPL/RTC/USB_CHG/DC_CHG/CBLPWR/PS_HOLD``). Top-level dispatch ================== Switch on first ``ReadKeyStroke`` (``[sp+0x10]``):: ==2 -> BootIntoMode(PON_word) (0x15d0 -> 0x1bfc) ==8 -> BootIntoMode(PON_word) ==0x102 -> GetBootIntoModeRecovery() (0x1638 -> 0x1e0c) [5-tap] ==0x17 -> dload flag[5]=1 ; 0x10708 -> reboot into EDL ==1 -> recovery flag[4]=1 default -> "Boot reason 0x%x not handled" -> normal boot ``g_mode_flags`` at RVA ``0x51e18``: ``[0]=fastboot [4]=recovery [5]=dload [8]=ChgPresent``. ``0x10708`` = UEFI var ``RESET_PARAM="EDL"`` + ``gRT->ResetSystem(EfiResetPlatformSpecific,"EDL")``. BootIntoMode (``0x1bfc``) ========================= Handles dload / fastboot / hold-recovery. Gate (``0x1c70``): dload/recovery-hold branch if ``(PON & 0x10 CBLPWR) | ChgPresent``; else power-key (fastboot) branch. Then a debounce poll of the held key: ===================== ========================================== ============= Path requirement (≈100 ms/cycle) outcome ===================== ========================================== ============= EDL/Download (hold) combo held ≥50 cyc (~5 s) then release dload flag Recovery (hold) combo held full 100 cyc (~10 s) recovery flag Fastboot Power held full 100 cyc (~10 s) fastboot flag ===================== ========================================== ============= ``BootIntoDload`` deep-dive ---------------------------- Commit block at ``0x1cd0`` sets ``g_mode_flags[5]`` and logs "BootIntoDload detected". Two ways in: (a) software cookie: reboot-reason ``0x17`` sets flag[5] directly (``0x1690``) → ``0x10708``; (b) hardware: ``BootIntoMode`` dload branch (gate above) held 50–99 cycles then released. The poll loop re-reads the key (``0x218c``→ReadKeyStroke) each ~100 ms; 50–99 cyc → dload, ≥100 cyc → recovery, <50 → abort. Function A ``GetPonReason`` (``0x1968``) supplies the PON word (gate); Function B is ``BootIntoMode`` (``0x1bfc``). GetBootIntoModeRecovery (``0x1e0c``) — the 5-tap recovery ============================================================= Entered on power-key boot (reason ``0x102``). Debug strings: ``"GetBootIntoModeRecovery detect begin. Power key Down/Up"``, ``"BootIntoMode detect seg[%d] Power key Down/Up"``, ``"BootIntoRecovery detected"``. Stage 1 — hold-then-release (``0x1e78``):: 0x1e88 cmp w22,#0x64(100); b.eq abort ; held ≥10 s -> abort 0x1e98 read key ; 0x1ea0 cbz -> exit on RELEASE 0x1ea8 Stall(100000)=~100ms ; w22++ 0x1eb4 cmp w22,#0x1e(30); b.lo abort ; must have been held ≥30 cyc (~3 s) Stage 2 — count 5 power taps (``0x1ee8``):: 0x1ef0 per-tap timeout = 0x14 (20 cyc ~2 s) 0x1ef8 cmp w21,#5 ; b.eq success ; TAP COUNTER w21 (wait press==0x102, then release==0, each ≤~2 s; increment @0x1fb8) 0x1fc0 cmp w21,#5 ; b.ne abort 0x1fd4 strb w8,[g_mode_flags+4] ; RECOVERY The documented recovery sequence, mapped ========================================== Documented user sequence: (1) power off; (2) hold Power until blue LED; (3) release, hold ~8 s; (4) release, tap Power ×5. .. list-table:: :header-rows: 1 :widths: 25 35 40 * - Step - Code - Why * - off → hold until blue LED - power-key boot ⇒ reason ``0x102`` ⇒ ``GetBootIntoModeRecovery`` - blue LED is driven by XBL (``"not power_key_press, blue led on!"``, ``QcomChargerApp``) → PBL done, XBL running, ABL about to load. Confirms power-on. Exact XBL branch is a dead end (GOT-indirected, see :doc:`/xbl/charger-detection`). * - release, hold ~8 s - stage 1: hold ≥30 cyc (~3 s), release, <100 cyc (~10 s) - must release; loop advances only on release (``0x1ea0``) * - release, tap ×5 - stage 2: 5 press/release edges, ``cmp #5`` at ``0x1ef8``/``0x1fc0`` → recovery flag at ``0x1fd4`` - count-to-5 Why ~8 s: the lower bound is ABL's ~3 s counted hold plus the XBL→ABL boot latency after the LED (ABL only starts counting once it reaches ``0x1e78``); the upper bound is the PMIC KPDPWR S2 reset ~8 s (see :doc:`/soc/pmic`) — release before that or the SoC power-cycles. "~8 s" is the PMIC ceiling, floored by ABL's 3 s plus latency. Why release/re-press (not just UX): stage 1 advances only on the release edge; stage 2 counts 5 tap edges; and a single continuous hold would hit the PMIC S2 reset (~8 s), so a continuous hold cannot reach recovery. Code-required, twice over. Reachability ============ Which of these code paths a human finger can actually reach is bounded by the PMIC's own hold-to-reset ceiling, not by this state machine — see :doc:`/soc/pmic` for the mode-by-mode reachability table. Caveats ======= - ~100 ms/cycle assumes standard EFI Stall µs; cycle counts (30/50/100/20, taps=5) are exact. Provenance ========== :Source: ``../artifacts/abl_a/abl_dxe_fv.bin``, ``../artifacts/boot_a/board.dts``, ``../_READONLY/lun1/xbl_a.bin``. :Method: RVAs LinuxLoader-relative (= ABL DXE file offset − ``0xb8``); runtime = load base + RVA. Cycle counts are code-exact from disassembly; wall-clock assumes ``gBS->Stall`` microseconds (~100 ms/cycle). :Cross-refs: :doc:`/abl/container`, :doc:`/soc/pmic`, :doc:`/edl/entry`, :doc:`/xbl/charger-detection`, :doc:`/sensors/misc` (non-key GPIO accessories).