U-Boot: wire volume-up button for menu navigation #2
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
U-Boot's bootmenu currently suffers from the button-misread hijack (bootmenu_16 mirrors bootcmd as the workaround). Proper fix: wire volume-up (and ideally vol-down) as buttons in U-Boot's DT/board code so the menu can be navigated deliberately.
Notes:
gpio-keys/adc-keysbinding + BUTTON driver, then hook bootmenu key handling.2026-08-21: the "SPMI wall" is pm8150b only — vol_up's PMIC reads fine. Issue premise is already met.
Investigated with the tablet on the deck hop. Summary: the DT work this issue asks for is already in the tree, and the blocker recorded against it was a misattribution.
What's already done
dts/upstream/src/arm64/qcom/sm8150-samsung-gts6l.dtsalready carries thegpio-keysnode for volume-up (pm8150l gpio12 = index 11,GPIO_ACTIVE_LOW,KEY_VOLUMEUP), andqcom_pmic_gpio.chas the addedqcom,pm8150b-gpio/qcom,pm8150l-gpiocompatibles. The captureduboot-console.log(2026-08-17)dm treeshows:That whole log contains zero occurrences of "failure", "error" or "-6".
The correction note (2026-08-18) was wrong about the cause
It concluded vol_up can't be read because of "SPMI read failure / gpio@c000 probe error -6", and blamed U-Boot's spmi-msm v5 observer-channel handling. Measured on hardware instead:
I dumped the live PMIC arbiter tables (
/dev/mem, core APID map @0xc440000+0x900, ownership @0xc40a000+0x700) and then replayed U-Boot's exact read algorithm (same last-APID-wins scan, same0x80 * apidobserver offset, same CMD/STATUS/RDATA sequence) from userspace:gpio@c000TYPE/SUBTYPE0x1DONE0x1DONEgpio@c0000x1DONEgpio@c0000x3DONE|FAILUREOnly pm8150b fails — and our Linux kernel logs the identical failure at boot:
spmi spmi-0: pmic_arb_wait_for_done: 0x2 0x104: transaction failed (0x3) reg: 0x3408. Same status word0x3. So it is a real denial on that PMIC at the arbiter/firmware level, not a U-Boot driver gap, and it is unrelated to the buttons.gpio statusprobes every bank, so it trips pm8150b's and printsgpio@c000 probe error -6(-ENXIOfromqcom_gpio_probe's TYPE sanity check after the failed read). That is the-6in the notes — on the wrong PMIC.Also: the "a button misreads as held on ~every boot" claim is very likely just
pwrkeygenuinely being held — MAINLINE-NOTES already says so ("You press power to turn the tablet on, soprocess_button_cmds()sees it held on essentially every boot").Real (but not blocking) divergences in U-Boot's spmi-msm vs Linux
Found while checking; none of them bite this board, listed so they aren't rediscovered:
if (!regval) continue;— every unmapped APID writeschannel_map[0][0], so sid0/pid0 ends up pointing at the last empty APID.0x004 0x008 0x050 0x054 0x204 0x237 0x404 0xa04) — by luck the EE-0 APID is the highest for most, so it works out. No GPIO PPID is duplicated.0x10000 * eeterm (harmless here:qcom,ee = <0>).if (reg ^ SPMI_STATUS_DONE)collapses DONE|FAILURE / DENIED / DROPPED into one "SPMI read failure" line. Decoding these would have made this diagnosis immediate — worth fixing on the next U-Boot build.What actually remains for this issue
bootmenu_16-mirrors-bootcmdworkaround and the "avoid the menu" rules.Correcting MAINLINE-NOTES accordingly.
2026-08-21: vol_up CONFIRMED readable on hardware
Kit pressed and released the button at roughly 0.5 s intervals while
/root/volpollsampled pm8150l gpio12 at 10 Hz through U-Boot's own read algorithm (same last-APID-wins scan, same0x80 * apidobserver offset, same CMD0/STATUS/RDATA sequence):0x81→ bit0 = 1 = pin high = released;0x80→ bit0 = 0 = pressed. Matches the DT'sGPIO_ACTIVE_LOW. Transition spacing (~0.5–0.6 s) matches the reported press cadence, so this is the real button, not noise — and zero read failures across the whole run.That closes the SPMI question for this issue definitively: volume-up is fully readable from U-Boot's code path. Combined with the Aug-17
dm tree(pmic@4gpio@c000active,key-volume-upactive,button_kbdactive), the hardware and driver side of this issue is done.Remaining — and it no longer needs a flash
The U-Boot currently on
sda20already contains thegpio-keysnode (it was present in the Aug-17 build), so menu navigation can be tested with the bootloader that is already installed:BUTTON_REMAP_PHONE_KEYSmaps VOLUMEUP→UP, VOLUMEDOWN→DOWN, POWER→ENTER, so if the highlight moves, everything works and the follow-ups are:bootmenu_16mirroringbootcmd(the workaround for the supposed hijack),If the highlight does not move while the pin demonstrably reads, the remaining suspect is
button_kbd/bootmenu key handling (e.g. the keyboard uclass polling or the remap), which is a small, well-scoped U-Boot fix — and no longer an arbiter mystery.2026-08-21: SOLVED — vol_up was reading gpio11, not gpio12 (off-by-one in the DT)
Root cause
The PMIC GPIO number in DT is 1-based, and both stacks subtract one themselves:
pmic_gpio_of_xlate()→return gpio_desc->args[0] - PMIC_GPIO_PHYSICAL_OFFSET;qcom_gpio_xlate()→/* GPIOs in DT are 1-based */ desc->offset = args->args[0] - 1;Our node passed
11, on the (wrong) theory recorded in its own comment that the cell was a 0-based index. So both the bootloader and the kernel read pm8150l gpio11, not gpio12.gpio11 sits low, so under
GPIO_ACTIVE_LOWthe button read as permanently held.Measured on hardware, via the U-Boot serial console
This also explains the boot hijack (closes a separate long-standing mystery)
The 2026-08-18 note "a button misreads as held on ~every boot, so the menu REPLACES bootcmd almost always" was this bug, not
pwrkeyand not an SPMI error.button_kbdstarts withold_statezeroed (OFF), sees vol_up reading ON, and injects a spuriousKEY_UP— which cancels autoboot and leaves the menu waiting indefinitely.Both earlier theories were wrong and are now retracted: it was never an arbiter/SPMI wall (see the previous comment — that was pm8150b, which fails in Linux too), and never pwrkey-being-held.
Fixes
841eccd5ed20—<&pm8150l_gpios 12 GPIO_ACTIVE_LOW>, pushed togts6l/6.17.dts/upstream/src/arm64/qcom/sm8150-samsung-gts6l.dts, flashed (md5f39cc8ab…, readback verified).Verification
evwatch /dev/input/event3→ five cleancode=115 PRESSED/releasedpairs while Kit pressed the button. Live DT confirmsgpios = <0xe1 0x0c 0x01>.Remaining
bootmenu_16-mirrors-bootcmdworkaround and the "buttons are not trustworthy" rules in the env — they were mitigations for this bug.Also landed while here: the serial console moved to menu entry 17 (one press below the default) because the menu has no wrap-around, giving a remote U-Boot shell over
/dev/ttyACM*— that's what made this diagnosis possible.Correction to the previous comment: the U-Boot half was never actually flashed
Kit caught this. The previous comment's U-Boot verification is void. The kernel-side result stands; the bootloader-side does not.
What went wrong
The
ddof the fixed image to/dev/sda20was absorbed by the page cache and never reached the media. Themd5sum /dev/sda20"readback" then re-read that same cache and matched itself — verifying against nothing.The tell was in the output and I glossed over it:
b71cca62(env + menu swap)f39cc8ab(gpio fix)Ground truth, obtained afterwards:
versionon the U-Boot serial console →Aug 21 2026 - 00:40:26= build #1, not the 00:57 fixed build.fdt print /gpio-keys/key-volume-upon the running control FDT →gpios = <0xcf 0x0b 0x01>— still gpio 11.echo 3 > /proc/sys/vm/drop_caches; md5sum /dev/sda20→b71cca62…, the old build.Also retracted: "the hijack is gone"
That was inferred from "reaches fastboot unattended in 63 s, where before it hung in the menu indefinitely". Both halves were wrong:
uboot.envI had saved.So whether the spurious-keypress hijack is fixed is unknown, and the mechanism I described for it may also be wrong.
Current state
/dev/sda20now genuinely holdsf39cc8ab…, written withoflag=directand verified afterdrop_caches.version= 00:57,fdt print=0x0c, andbutton listshowingvol_up offat rest /onwhile held.Standing rule from this: flash with
dd ... oflag=direct conv=fsync, and verify only after dropping caches. Same family as the stale-.olesson already in MAINLINE-NOTES — verify the artifact that actually runs, never a convenient proxy for it.2026-08-21: vol_up NAVIGATES ✅ — plus a secondary bug: repeated presses swallowed
Confirmed on hardware by Kit: volume-up now moves the menu highlight up. The gpio11→gpio12 fix (
841eccd5ed20+ the same one-liner in the U-Boot DTS, flashed asf39cc8abwithoflag=direct) resolves the original issue.Secondary bug (not a dealbreaker, filed for the record)
Symptom: pressing vol_up repeatedly at ~0.5 s intervals registers only the first press; waiting longer makes it work again. vol_down is unaffected.
This is an upstream U-Boot bug, not gts6l-specific, and the asymmetry is explained by the two buttons using different drivers.
1.
button_kbdnever enables auto-repeat.button_kbd_probe()callsinput_init(), whichmemsets the config, leavingrepeat_delay_ms = repeat_rate_ms = 0andallow_repeats = false. It never callsinput_set_delays()— onlycros_ec_keybandtegra-kbcdo.2. So
_input_send_keycodes()drops any repeat unconditionally:With
repeat_rate_ms == 0the second operand short-circuits, sois_repeatis always false. There is no time window here at all — the repeat path is effectively dead code.3. Therefore a second press only gets through if the input layer saw the keycode set return to empty in between — i.e.
button_read_keys()must actually sample the button released and emit a release event (input_check_keycodes()compares againstprev_keycodesand only reports "changed" then).Why vol_down is immune —
qcom_pwrkey_get_state()has an explicit 25 ms forced-release window:Every not-pressed sample stamps the time and then forces OFF for 25 ms. That's debounce, but it also guarantees the released state is visible to the poller.
Why vol_up isn't —
button_gpio_get_state()returns the raw live GPIO level with no latching whatsoever, andbutton-gpioignores thedebounce-interval = <15>in our node (that's a Linux binding U-Boot doesn't implement — it reads onlygpiosandlinux,code). If polling doesn't happen to land during the released window, the release is never observed,old_statestaysON, and the following press produces no state change at all. Repainting 38 menu entries onto a 2560×1600 vidconsole is slow enough to plausibly swallow a 0.5 s press/release cycle.Proposed fix (not implemented — needs another flash, say the word)
button-gpio: add a release latch mirroringbutton-qcom-pmic(optionally honouringdebounce-interval). Fixes the asymmetry at its source and follows existing in-tree precedent.button_kbd: callinput_set_delays()sorepeat_rate_ms != 0. This revives the dead repeat path, so holding a button scrolls the menu — which is what a 38-entry menu wants anyway.Both are small and upstreamable. Happy to split this into its own issue if you'd rather keep #2 scoped to the original bug.
2026-08-21: button fixes landed — but they do NOT fix the swallowed-repeat symptom
Both changes are implemented, flashed (
dcb94cb5…, written withoflag=direct, readback verified afterdrop_caches) and confirmed on hardware by Kit: no change to the repeated-press behaviour. Recording the negative result so this isn't retried.What was changed (worth keeping regardless)
drivers/button/button-gpio.c: added a post-release window mirroringbutton-qcom-pmic, and taught the driver to honour thedebounce-intervalDT property it previously ignored entirely (it read onlygpiosandlinux,code). Our node's<15>is now used instead of the hardcoded 25 ms default.drivers/input/button_kbd.c: added the missinginput_set_delays(400, 200). Without itrepeat_rate_mswas 0, making theis_repeatbranch of_input_send_keycodes()unreachable — the auto-repeat path was dead code. Holding a button now repeats, which is worth having in a 38-entry menu.Both are upstreamable on their own merits (the
debounce-intervalgap and the dead repeat path are genuine upstream bugs).Why they didn't help — the actual root cause
input_tstc()only polls the buttons when the input FIFO is empty:So nothing is sampled while the 3-char ANSI sequence drains, and — more importantly — nothing is sampled at all while the menu repaints, which on a 2560×1600 vidconsole with 38 entries is slow. A press and release that both fall inside that blind window are invisible to a level-sampled driver:
old_statestaysON, and the next press produces no state change, so no event is ever generated.No amount of debounce or repeat tuning can recover an edge that happened between samples. That is why my earlier emphasis on the repeat-drop was wrong — it is real, but secondary.
What would actually fix it (not done — bigger, needs a decision)
INT_LATCHED_STS(0x18) /INT_LATCHED_CLR(0x14); configuring an edge trigger and reading+clearing the latch would capture transitions that happen between polls, regardless of blind windows. This is the robust fix, and needs work inqcom_pmic_gpioplus a way to surface "latched" state through the GPIO/button uclass.input_tstc()— a one-liner, but it touches shared core code used by every board, and it still would not cover the repaint window, so it is a partial fix at best.Verdict: symptom is cosmetic, cause is understood and documented, fix is disproportionate for now. Leaving it.