uzbek-plus/logs/b10/b10a/B10A-DRIVER-AUDIT.md
q 58dcb3d121 B10: battery charging — PM8916 LBC supervisor v4 (CHARGE/HOLD, FAULT safe clamp to 4.00 V) and persistent B10B-5 cache
- LBC supervisor v1..v4 (linux/patches/b10b3-lbc-supervisor-v3.patch + b10/b10b3/lbc-v3-to-v4-fault-clamp.diff):
  CHARGING 4.20 V/450 mA -> HOLD 4.05 V after 60 min CV -> battery -> new cycle on USB insert; FAULT = VDD_MAX 4.00 V clamp
- RAM tests C/B/D/D2/D3/D4 (safety timer only ends fast charge), B10B-4 actuator audit (VDD_MAX works, USB_SUSP does not),
  B10B-5 FAULT clamp regression, production run, warm reboot via PON reboot reason
- cache = B10B-5 c3732515 (HW EDL write, readback + full partition verify), class-A persistent validation
- test-only hooks/images are not part of the production kernel; images with the AP PSK are not published
2026-10-04 19:00:27 +03:00

12 KiB
Raw Blame History

B10A-DRIVER-AUDIT — charge termination and recharge: stock vs upstream (code-only, read-only)

Date: 2026-10-03. Board untouched (no PMIC writes, no build). Sources:

  • downstream: android.googlesource kernel/msm @7f1748ce drivers/power/qpnp-linear-charger.c, qpnp-vm-bms.c (copies: b10/b10a/lbc-7f1748.c, vmbms-7f1748.c). The stock Aurora kernel (3.10.28, partitions/boot.bin) is a slightly different revision: its strings match this LBC source ("Failed to override VBAT_DET", "vbatdet-lo triggered", "Failed to disable EOC comp", "resume-soc") and its BMS additionally has "soc dropped below resume_soc soc=%d resume_soc=%d, restart charging". Same design.
  • upstream: Linux 7.2.7 drivers/power/supply/pm8916_lbc.c, pm8916_bms_vm.c, arch/arm64/boot/dts/qcom/pm8916.dtsi, reference board msm8916-longcheer-l8150.dts.
  • live registers: b10/b10a/b10a-live1.txt, b10a-live2.txt (B10A).

Register names (downstream defines, offsets from CHGR 0x1000 / BAT_IF 0x1200 / USB 0x1300 / MISC 0x1600)

Reg Name Live (SBL1 state) Meaning
0x1009 CHG_STATUS, bit1 = VDD_LOOP (CV) 05 / 03 / 07 / 00 03 = CV loop, 05 = other loop (CC/input), 00 = not charging (bits 0/2 not named in source)
0x1010 CHGR INT_RT_STS (bit5 FAST_CHG_ON) 01 fast_chg RT was never seen set
0x1040/41 VDD_MAX / VDD_SAFE 08 / 08 4.200 V
0x1044/45 IBAT_MAX / IBAT_SAFE (90 mA steps) 00 / 0a 90 mA / 990 mA
0x1047 VIN_MIN (4200 + 27 mV·n) 04 4308 mV = stock vinmin-mv (B10A table had this wrong: 0x1043 is not VIN_MIN)
0x1049 CHG_CTRL: bit7 CHG_ENABLE, bit0 CHG_FORCE_BATT_ON 90 enabled; bit4 unknown
0x104A CHG_FAILED (bit7) 00
0x1052 VBAT_WEAK 0b
0x105B CHG_IBATTERM_EN, bit3 = HW end-of-charge (iterm) comparator 09 → bit3 = 1 HW termination ENABLED
0x1060/61 TCHG_MAX_EN / TCHG_MAX (4 min steps) 80 / 1d timer on, (0x1d+1)·4 = 120 min
0x1065 CHG_WDOG_EN 00 off
0x10DA PERPH_RESET_CTRL3 ("follow PMIC reset") 0b
0x10EE CHG_COMP_OVR1, bits1:0 = VBAT_DET override 00 no override → VBAT_DET comparator active
0x1309 USB_PTH_STS (bits7:6 = USB_IN_VALID) 90 valid
0x1308 (not in the downstream source) 02 charging / 01 after EOC empirical
0x1642 MISC BOOT_DONE (bit7) 00 never set (downstream sets it at probe)
0x16F3/F4 MISC TRIM3/TRIM4 (VDD trim) 58 / 49

A. Stock (downstream) recharge logic

Termination is a software decision, recharge is software-triggered:

  1. Probe (qpnp_lbc_probe): misc_init (reads VDD trim, writes BOOT_DONE), chg_init (VBAT_WEAK, VIN_MIN, VDD_SAFE, VDD_MAX + VDD trim, IBAT_SAFE, TCHG_MAX if tchg-mins, VBAT_DET override = 0 (CHG_COMP_OVR1) "charge irrespective of VBAT above VBAT_DET", HW iterm comparator OFF (0x105B bit3 = 0) unless qcom,charger-detect-eoc or float-charge, charger WDOG off), bat_if_init (BPD source, force VREF_BAT_THM), usb_path_init (USB enum timer stop = 0, CHG_CTRL CHG_ENABLE), battery psy, initial status, IRQs, VDD-trim alarm. Stock DT has neither charger-detect-eoc nor float-charge nor disable-vbatdet-based-recharge, so all three SW paths below are active.
  2. IRQs: chg_failed, fast_chg (on: clear chg_done, start 50 s VDD-trim alarm), chg_done (only sets a flag), vbatdet_lo (falling edge), batt_pres, batt_temp, usbin_valid, usb_overtemp.
  3. EOC: VM-BMS (qcom,report-charger-eoc) decides "full" (SOC 100 / OCV at 100) and calls battery set_property(STATUS, FULL). The LBC driver then disables charging (CHG_CTRL: CHG_EN=0, FORCE_BATT_ON=1), sets chg_done, removes the VBAT_DET override and enables the vbatdet_lo IRQ.
  4. Recharge, three independent paths, all in software:
    • vbatdet_lo IRQ (VBAT fell below VBAT_DET): disable the IRQ, override VBAT_DET to 0 again, CHG_ENABLE.
    • resume-soc (DT 99 %): get_prop_capacity (polled by healthd/BMS) re-enables charging when SOC ≤ resume_soc and USB is in.
    • usbin_valid insert: override VBAT_DET to 0, CHG_ENABLE (and on removal: disable + 90 mA).
    • Also external_power_changed: IBAT_MAX = min(USB current_max (500 mA), thermal, JEITA) and enable/disable on suspend (≤ 2 mA).
  5. Other runtime: VDD_MAX trim alarm every 50 s while fast charging (compensates VDD_MAX against measured VBAT), JEITA via ADC_TM batt_therm, thermal mitigation levels 1440/720/630/0 mA.

B. Upstream pm8916_lbc

Only programs the LBC and leaves the state machine to the hardware. No termination/recharge logic at all.

  • Probe writes: VDD_SAFE (qcom,fast-charge-safe-voltage), IBAT_SAFE (qcom,fast-charge-safe-current), TCHG_MAX_EN = 0x00 (safety timer OFF), VDD_MAX (from monitored-battery voltage-max-design, clamped to safe), IBAT_MAX (= safe current, settable later via sysfs constant_charge_current), CHG_CTRL = 0xA0 (CHG_EN | PSTG_EN) (overwrites the live 0x90 → clears bit4, sets bit5).
  • Does NOT touch: IBATTERM_EN (HW termination stays as SBL1 left it = ON), CHG_COMP_OVR1/VBAT_DET, BOOT_DONE, WDOG, VIN_MIN, VBAT_WEAK, BAT_IF/BPD/BTC, VDD trim, USB enum timer.
  • IRQs: only usb_vbus (0x13 bit1) → online + its own extcon (EXTCON_USB) + power_supply_changed. chg_done/vbat_det/fast_chg/ chg_fail/bat/temp interrupts are listed in the DT binding but never requested.
  • Properties: ONLINE, CONSTANT_CHARGE_VOLTAGE_MAX, CONSTANT_CHARGE_CURRENT (writable → re-runs configure → rewrites VDD_MAX, IBAT_MAX and CHG_CTRL with the same value; no 0→1 edge on CHG_EN).
  • Requires monitored-battery (probe fails without battery info). Minor bug: the clamp result in configure() is discarded (line 100–103).
  • Upstream BMS-VM: probe writes S1/S2 sample interval (10/10), FIFO length (2/2), EN_CTL; suspend/resume force S3/normal via SEC_ACCESS. Reports VOLTAGE_NOW = FIFO·300 µV, OCV for 180 s after boot, STATUS = charging if supplied; no SOC, no EOC, no recharge. Downstream converts FIFO raw with VADC calibration: raw·97.656 µV·3, then gain-corrected with the 625 mV/1.25 V references (uncalibrated: 14682 → 4.301 V; our CV level means the gain correction is ≈ −2.4 %). The ×300 µV constant is ≈ +5 % high here.

C. Difference

Stock Upstream Our board now (no driver)
Who ends the charge SW (BMS SOC=100 → disable) HW iterm comparator (whatever SBL1 left) HW iterm comparator (0x105B bit3 = 1)
HW iterm disabled at probe untouched enabled
VBAT_DET override 0 while charging; removed at EOC, re-armed on recharge untouched none (0x10EE = 0)
Recharge vbatdet_lo IRQ / resume-soc / USB insert → CHG_EN none none
BOOT_DONE set not set not set
Safety timer tchg-mins 232 disabled 120 min, on
IBAT_MAX min(USB 500 mA, thermal, JEITA) fixed (safe current) 90 mA field
VDD_MAX trim 50 s alarm none SBL1 trim
USB extcon msm_otg LBC's own extcon (conflicts with our pm8916_usbin on the same IRQ) pm8916_usbin

D. Why the hardware does not start a new cycle, and what our board needs

Explanation (consistent with all B10A observations; the exact FSM rule is undocumented → hypothesis, high confidence):

  • SBL1 leaves the LBC in pure hardware mode: HW iterm termination ON, no VBAT_DET override, BOOT_DONE not set.
  • After the CV taper the iterm comparator ends the cycle: CHG_STATUS 03 → 00, USB path 02 → 01. VPH = VBAT (no power path), so the whole load moves to the battery.
  • The FSM stays latched in that state. Nothing in this design restarts it automatically: on stock, restart is always a software CHG_EN edge (vbatdet_lo IRQ / resume-soc / USB insert). A warm reset does not reset the LBC peripheral (class B kept the state, PERPH_RESET_CTRL3 = 0x0b); only a full PMIC POR (class A) does.
  • Not proven: whether the LBC would resume by itself at a much lower VBAT (VBAT_DET comparator with no override). In 30 min it fell ≈ 70–80 mV below the CV level without resuming. The VBAT_DET threshold register is not named in the source.

Is enabling upstream pm8916_lbc enough? No. It programs the limits once and adds the usb_vbus extcon, but it has no chg_done/vbat_det handling and no recharge. After the first termination the board would be exactly in today's state. At best, a USB re-plug would not help either (upstream does not touch CHG_EN on insert).

What our board needs (beyond upstream):

  1. A recharge mechanism that produces a CHG_EN 0→1 edge when VBAT drops (and on USB insert), e.g.:
    • kernel patch to pm8916_lbc: request chg_done + vbat_det IRQs, emulate the stock flow (EOC → disable + arm VBAT_DET → re-enable), or simpler: a periodic check (VBAT/BMS below threshold ∧ USB valid ∧ CHG_STATUS == 00 → CHG_EN toggle);
    • or a userspace supervisor (shell + regmap/debugfs or a small sysfs knob) doing the same. Debugfs writes are a debug path only.
  2. A decision on termination: either keep HW iterm (then recharge logic is mandatory) or disable it (0x105B bit3 = 0, like stock) and accept float charging at a reduced VDD_MAX — float at 4.2 V forever is bad for the cell; not recommended without a lower float voltage.
  3. A battery description (simple-battery, voltage-max 4.20 V for now, voltage-min ≈3.4 V, capacity from the label) — mandatory for both drivers.
  4. USB extcon: move usb/usb_hs_phy from pm8916_usbin to pm8916_charger (like l8150) and disable pm8916_usbin, otherwise both drivers request the same usb_vbus IRQ.
  5. IIO + QCOM_SPMI_VADC (+ add channel 6 VBAT_SNS with pre-scaling 1/3, maybe 0x30 batt_therm / 0x31 batt_id) for a calibrated VBAT, and to calibrate the BMS FIFO scale.
  6. Keep the safety timer in mind: upstream turns it off. With HW iterm + recharge it is acceptable; otherwise keep it.
  7. USB gadget MaxPower 2 mA → 500 mA (separate, unrelated to the PMIC).

E. Minimal B10B plan (RAM-only, each step its own GO)

Common rules: RAM boot via RESET-held power-on + fastboot boot (cache/aboot untouched); class-A power-on before each step so the LBC starts from SBL1 defaults; USB meter in line; results streamed to 480s; full PMIC dump (b10a-probe.sh) before boot actions and after each probe → diff.

  • B10B-0 (no new PMIC writes by drivers): kernel b9c + IIO, QCOM_SPMI_VADC, QCOM_SPMI_TEMP_ALARM; DT adds VADC channel 6 (VBAT_SNS 1/3). Note: VADC conversions themselves write VADC control registers (0x31xx) — that is the only write class. Check: VBAT via IIO vs BMS FIFO (calibrate the scale), USBIN, VPH, die temp, PMIC thermal zone. Baseline/diff: CHGR/BAT_IF/USB/MISC/BMS unchanged.
  • B10B-1 BMS: + BATTERY_PM8916_BMS_VM, simple-battery node (4.20 V max, 3.4 V min). Expected writes: BMS 0x4055/56/47/46 only. Check: power_supply pm8916-bms-vm VOLTAGE_NOW/OCV vs IIO. Charger untouched.
  • B10B-2 charger (upstream as-is, observation): + CHARGER_PM8916_LBC, qcom,fast-charge-safe-voltage = 4200000, qcom,fast-charge-safe-current = 500000, battery voltage-max 4.20 V; extcon moved to pm8916_charger, pm8916_usbin disabled. Expected writes: 0x1041, 0x1045, 0x1060 (timer off!), 0x1040, 0x1044 (=4 → 450 mA: 500000/90000−1 = 4, i.e. 450 mA), 0x1049 = 0xA0. Check: USB enumeration still works (NCM), meter current, CHG_STATUS/0x1308 trend through CC → CV → termination, and confirm that no recharge happens (expected). Decide whether to keep the safety timer (would need a patch).
  • B10B-3 recharge logic (needs design GO): minimal patch or userspace supervisor: on (USB valid ∧ CHG_STATUS == 00 ∧ VBAT < 4.10 V [threshold TBD]) → CHG_CTRL 0x80 → 0x81/0x00 → 0x80 (or stock-style VBAT_DET override + vbat_det IRQ). Test: full cycle, termination, discharge to the threshold, automatic recharge, repeat ≥2 cycles, USB unplug/replug, class-B reboot during done-state.
  • Only after B10B-3 PASS: persistent integration (cache), separate GO.