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