uzbek-plus/logs/b10/b10b3/B10B3-TIMER-SEMANTICS.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

106 lines
8.5 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.

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