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

139 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.