- 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
106 lines
8.5 KiB
Markdown
106 lines
8.5 KiB
Markdown
# B10B3-TIMER-SEMANTICS — PM8916 LBC safety timer: what the Qualcomm sources say (desk study, no board access)
|
||
|
||
Date: 2026-10-04. No code changed, no test run. Sources (copies + sha256 in `downstream-7f1748ce/SOURCES.txt`):
|
||
- `LBC` = android.googlesource kernel/msm @7f1748ce `drivers/power/qpnp-linear-charger.c` (PM8916 linear battery charger, our PMIC)
|
||
- `LBC-DT` = `Documentation/devicetree/bindings/power/qpnp-linear-charger.txt`
|
||
- `SMBB` = same tree `drivers/power/qpnp-charger.c` (PM8941/PM8226 switch-mode charger, same QPNP generation and the same CHGR register map)
|
||
- ours = `/home/q/aurora-kbuild/src-b10b3/drivers/power/supply/pm8916_lbc.c` (v3)
|
||
|
||
## 1. Register meaning (only what the code states)
|
||
| reg | name in source | what the source does with it |
|
||
|---|---|---|
|
||
| 0x1060 | `CHG_TCHG_MAX_EN_REG` 0x60, `CHG_TCHG_MAX_EN_BIT` BIT(7) (LBC:64–65) | bit7 = timer enable; written 0 before and 1 after programming TCHG_MAX |
|
||
| 0x1061 | `CHG_TCHG_MAX_REG` 0x61, `CHG_TCHG_MAX_MASK` LBC_MASK(6,0) (LBC:66–67) | 7-bit limit, minutes = (val + 1) × 4 |
|
||
| 0x104A | `CHG_FAILED_REG` 0x4A, `CHG_FAILED_BIT` BIT(7) (LBC:59–60) | **only written** (bit7 = 1) in the chg-failed IRQ handler = "chg_fail clear bit". **Never read** in LBC or SMBB |
|
||
| 0x1010 bit6 | chg-failed IRQ (LBC-DT:173–180 `<0x0 0x10 0x6>`; SMBB:192 `CHG_FAILED_IRQ BIT(6)`) | the actual failure **status/interrupt** (RISING edge) |
|
||
| 0x1010 bit5 | `FAST_CHG_ON_IRQ` BIT(5) (LBC:34), DT `fast-chg-on` `<0x0 0x10 0x5>` | LBC: the **only** indicator of "charging": `get_prop_batt_status` → CHARGING iff bit5, `get_prop_charge_type` → FAST iff bit5 (LBC:1060–1097) |
|
||
| 0x1010 bit7 / bit0 | `chg-done` / `vbat-det-lo` (LBC-DT:173–181) | |
|
||
| 0x105E/0x105F | not in LBC; SMBB:87–88 `CHGR_TTRKL_MAX_EN` / `CHGR_TTRKL_MAX` | SMBB has a **separate trickle-charge timer** next to TCHG; SMBB RT bit4 = `TRKL_CHG_ON_IRQ` (SMBB:194) |
|
||
| 0x1009 bit1 | `CHG_VDD_LOOP_BIT` (LBC:47) | CV loop; bits 0/2 not named |
|
||
|
||
Excerpts:
|
||
```c
|
||
/* LBC:932-963 qpnp_lbc_tchg_max_set() */
|
||
#define QPNP_LBC_TCHG_MIN 4
|
||
#define QPNP_LBC_TCHG_MAX 512
|
||
#define QPNP_LBC_TCHG_STEP 4
|
||
minutes = clamp(minutes, QPNP_LBC_TCHG_MIN, QPNP_LBC_TCHG_MAX);
|
||
/* Disable timer */ ... CHG_TCHG_MAX_EN_BIT, 0
|
||
reg_val = (minutes / QPNP_LBC_TCHG_STEP) - 1;
|
||
... CHG_TCHG_MAX_REG, CHG_TCHG_MAX_MASK, reg_val
|
||
/* Enable timer */ ... CHG_TCHG_MAX_EN_BIT, CHG_TCHG_MAX_EN_BIT
|
||
/* LBC:1653 (init): only if the DT has the property */
|
||
if (of_property_read_bool(chip->spmi->dev.of_node, "qcom,tchg-mins")) {
|
||
rc = qpnp_lbc_tchg_max_set(chip, chip->cfg_tchg_mins);
|
||
/* LBC:2066-2083 qpnp_lbc_chg_failed_irq_handler() */
|
||
u8 reg_val = CHG_FAILED_BIT;
|
||
pr_debug("chg_failed triggered count=%u\n", ++chip->chg_failed_count);
|
||
rc = qpnp_lbc_write(chip, chip->chgr_base + CHG_FAILED_REG, ®_val, 1);
|
||
if (rc) pr_err("Failed to write chg_fail clear bit rc=%d\n", rc);
|
||
... power_supply_changed(&chip->batt_psy);
|
||
/* LBC:2273 */ SPMI_REQUEST_IRQ(chip, CHG_FAILED, rc, chg_failed, 0, IRQF_TRIGGER_RISING, 1);
|
||
/* LBC-DT:47 */ - qcom,tchg-mins: Maximum total software initialized charge time.
|
||
/* SMBB:3298-3330 qpnp_chg_tchg_max_set(): identical (TCHG_MIN 4, MAX 512, STEP 4, EN 0x80, MASK 0x7F, minutes/4-1) */
|
||
```
|
||
|
||
## 2. Formula `minutes / 4 − 1`
|
||
Confirmed: identical in LBC (`qpnp_lbc_tchg_max_set`) and SMBB (`qpnp_chg_tchg_max_set`), 7-bit field. Our v3 uses the same formula and the
|
||
same disable → write → enable sequence. **This is Qualcomm driver code, not a register specification**; it is the best available evidence, not proof.
|
||
|
||
## 3. Encoding table (from the formula)
|
||
4 min = 0x00 · **8 min = 0x01** · 16 min = 0x03 · 120 min = 0x1D (SBL default) · 152 min = 0x25 (binding example) · 232 min = 0x39 (stock DT)
|
||
· 512 min = 0x7F (maximum).
|
||
|
||
## 4. When does the hardware timer count?
|
||
**No source states it.** Neither LBC nor SMBB documents counting, pausing in CV, reset on state transitions, or reset by CHG_CTRL/VDD_MAX writes.
|
||
What the code implies, in decreasing strength:
|
||
1. Programming is done **once at probe** with EN 0 → value → EN 1; nothing else in either driver touches TCHG (no re-arm on USB insert, on CHG_EN
|
||
toggles or on recharge). EN 0→1 is therefore the only "arming" action Qualcomm uses (implied, not stated).
|
||
2. The binding calls it "maximum total **software initialized** charge time".
|
||
3. SMBB has a separate trickle timer (TTRKL 0x5E/0x5F) next to TCHG and distinguishes TRKL_CHG_ON / FAST_CHG_ON states → TCHG is the
|
||
charge-phase (fast-charge) timer of the hardware charge state machine, not a "time since CHG_EN" counter (inference).
|
||
4. Both drivers equate "charging" with the FAST_CHG_ON RT bit (SMBB also TRKL_CHG_ON).
|
||
→ The best-supported model: **TCHG counts while the charger FSM is in its fast-charge state (FAST_CHG_ON)**. "Counts whenever CHG_EN" — our
|
||
Test B/D model — has no support in the sources. Stops in CV / resets by VDD_MAX writes: unknown.
|
||
|
||
## 5. How the stock driver uses it
|
||
Programs it (if `qcom,tchg-mins`) at init; stock DT `tchg-mins = <232>` (0x39). Expiry handling = chg-failed IRQ (RT bit6, rising):
|
||
**write CHG_FAILED bit7 to clear, count, power_supply_changed** — no FAULT state, no CHG_EN change, no restart, no reprogramming. Fault is never
|
||
read back; battery status/health never use it. Recovery = the clear itself (whether charging resumes after the clear is not stated).
|
||
|
||
## 6. Our data against this
|
||
FAST_CHG_ON (CHGR RT 0x1010 bit5) per run, from the monitor logs:
|
||
| run | driver | CHGR RT | USB path 0x1308 | CHG_CTRL | samples | note |
|
||
|---|---|---|---|---|---|---|
|
||
| B10B-3 ram1 | v1 | **20** | **02** | a0 | 2 | after probe (VBAT 3.90 V) |
|
||
| B10B-3 ram1 | v1 | 00 | 01 | 21 | 11 | after `force_terminate` (CHG_CTRL 0x21 held): FAST_CHG_ON off, path 02→01, **meter stayed 0.358 A** |
|
||
| B10B-3T t1 | v2 | **21** | **02** | a0 | 227 (≈114 min) | VBAT 3.85–3.99 V, VDD_MAX 4.00 V, timer 512 min |
|
||
| Test C c1 | v3 | 01 | 01 | a0 | 134 | 0.358 A CC |
|
||
| Test B b1 | v3 | 01 | 01 | a0 | 174 | |
|
||
| Test D d1 | v3 | 01 | 01 | a0 | 161 | 0.42 A CC |
|
||
| B9C/SBL (class A) | — | 01 | 02 | 90 | dumps | BOOT_DONE 0, SBL charge |
|
||
- **FAST_CHG_ON appears exactly when path = 02 under the Linux driver (v1/v2); never in any v3 run (path 01), although current flows.**
|
||
- t1 vs v3 configuration: of the 69 charger registers logged in `t1-final-state.txt`, only 0x1010 (RT), 0x1308 (path) and 0x1061 (timer value)
|
||
differ from the v3 end dumps → a **state** difference of the charger FSM, not a configuration difference.
|
||
- v1/v2 and v3 use the same restart edge at CHARGING entry (`lbc_charge_restart`: CHG_FAILED clear, CHG_CTRL 0x21 → 20 ms → 0xa0); v3 adds
|
||
BOOT_DONE (already 1 from lk1st fastboot) and a VDD_MAX/IBAT_MAX write just before the edge. What puts the FSM into "path 01, no FAST_CHG_ON,
|
||
still charging" in v3 is **not established** (candidates: entry ordering, the state before probe — B9C SBL path 02 vs latched path 01 before t1 —,
|
||
VBAT relative to the VBAT_DET comparator without the stock COMP_OVR override).
|
||
- chg-failed RT (bit6) never set in any run; 0x104A always 00 (weak evidence: write-to-clear in the sources).
|
||
- 0x105E/0x105F (TTRKL in the SMBB map) = 80/2b in every dump (SBL value); if 1-min steps, 44 min — coincides with the BD=0 00/01 stop
|
||
43.7–44.7 min after class A (EOC audit case A). Hypothesis only.
|
||
- Stock init differences: COMP_OVR1 VBAT_DET override = 0x2 (LBC:1660–1669, also on USB insert), ENUM_T_STOP = 0 (LBC:1741–1745);
|
||
ours 0x10EE 00, 0x134E 00.
|
||
|
||
## 7. Why Test D gave no timeout
|
||
At t = 480 s: TCHG 80/01, CHG_STATUS 05, CHG_CTRL a0, path 01, 0.42 A — but CHGR RT = 01: **FAST_CHG_ON was not set** (as in every v3 run).
|
||
Under the source-supported model the TCHG timer belongs to the FSM's fast-charge state; in v3 the LBC charges at full current in a state that
|
||
does not report fast charge (path 01), so the timer most likely **never counted** — in Test B and in Test D alike. The CV argument (VBAT at
|
||
4.10 V from ≈ t 300 s) is secondary. No hidden expiry: chg-failed RT bit6 stayed 0.
|
||
Not proven: (a) that TCHG counts only with FAST_CHG_ON, (b) which state "path 01 + current" is, (c) why v3 lands there and v1/v2 did not.
|
||
|
||
## 8. Consequences (no code change yet)
|
||
- **The safety timer is not a protection in the v3 configuration** — it very likely does not run. Do not count on it.
|
||
- The "path 01 + charging" state also matters beyond the timer (it is the state that the 00/01 latch is part of).
|
||
- Supervisor fault detection should also use CHGR INT_RT bit6 (chg-failed) — separate decision.
|
||
- A D2 with lower IBAT on v3 would very likely repeat B/D (no FAST_CHG_ON) and adds little. The informative test needs the FAST_CHG_ON state.
|