--- src-b10b3/drivers/power/supply/pm8916_lbc.c 2026-10-03 21:11:03.584000000 +0600 +++ src-b10b5/drivers/power/supply/pm8916_lbc.c 2026-10-04 20:07:45.456000000 +0600 @@ -298,7 +298,7 @@ /* - * Aurora B10B-3 v3 charger supervisor: CHARGE -> HOLD -> (battery) -> CHARGE. + * Aurora B10B-3 v4 charger supervisor: CHARGE -> HOLD -> (battery) -> CHARGE (v4 = v3 + FAULT safe clamp, B10B-5). * Facts it relies on (B10A/B10B-2/B10B-3 tests): CHG_STATUS 00 + USB path 01 is a latch, not an end-of-charge; * no hardware EOC was seen with BOOT_DONE set; CHG_EN = 0 does not stop the charger; verified levers are VDD_MAX, * IBAT_MAX and the CHG_CTRL 0x21 -> 0xa0 edge (releases the latch). All decisions use the calibrated VADC VBAT. @@ -306,7 +306,10 @@ * CHARGING: entered on USB insert/probe: BOOT_DONE, VDD_MAX = charge level, IBAT_MAX, restart edge. * -> HOLD after the CV loop with VBAT >= full-min lasted cv-hold-minutes; 00/01 latch -> restart, stay. * HOLD: only VDD_MAX = hold level; 00/01 latch -> restart edge, VDD_MAX stays at the hold level. - * FAULT: logical only (VBAT > 4.25 V or CHG_FAILED): no restarts. There is no verified software stop. + * FAULT: entered on VBAT > 4.25 V or CHG_FAILED; on entry only VDD_MAX = 0x00 (FAULT safe clamp to 4.00 V, B10B-4) + + * readback; no further charger writes, no restarts; left only by USB removal (-> NO_USB) or reboot. + * The clamp is NOT a guaranteed charge disconnect: with VBAT > 4.00 V charging stops and the board runs + * from the battery; with VBAT < 4.00 V the charger may still charge up to 4.00 V. */ static void lbc_supv_log(struct pm8916_lbc_charger *chg, int to, const char *why) { @@ -318,7 +321,7 @@ chg->log[i].vbat = chg->vbat_uv; chg->log[i].why = why; if (to == LBC_ST_FAULT) - dev_crit(chg->dev, "supervisor %s -> FAULT (%s), vbat %d uV: restarts blocked, NO software stop available\n", + dev_crit(chg->dev, "supervisor %s -> FAULT (%s), vbat %d uV: restarts blocked, FAULT safe clamp to 4.00 V\n", lbc_state_name[chg->state], why, chg->vbat_uv); else dev_info(chg->dev, "supervisor %s -> %s (%s), vbat %d uV\n", lbc_state_name[chg->state], @@ -394,18 +397,36 @@ return true; } +/* + * FAULT safe clamp to 4.00 V (B10B-4): the only charger write on FAULT entry is VDD_MAX = 0x00 (lowest LBC step). + * Not a guaranteed charge disconnect - below 4.00 V the charger may still charge up to 4.00 V. + */ +static void lbc_fault_clamp(struct pm8916_lbc_charger *chg, const char *why) +{ + unsigned int old = 0xff, rb = 0xff; + int ret; + + ret = regmap_read(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_VDD_MAX, &old); + ret = ret ?: regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_VDD_MAX, 0x00); + ret = ret ?: regmap_read(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_VDD_MAX, &rb); + dev_crit(chg->dev, "FAULT safe clamp to 4.00 V: cause %s, vbat %d uV, VDD_MAX old 0x%02x -> readback 0x%02x (ret %d)\n", + why, chg->vbat_uv, old, rb, ret); +} + +static bool lbc_enter_fault(struct pm8916_lbc_charger *chg, int reason, const char *why) +{ + chg->fault_reason = reason; + lbc_fault_clamp(chg, why); + lbc_supv_log(chg, LBC_ST_FAULT, why); + return true; +} + static bool lbc_check_fault(struct pm8916_lbc_charger *chg, unsigned int failed) { - if (failed & PM8916_LBC_CHGR_CHG_FAILED_BIT) { - chg->fault_reason = LBC_FAULT_TIMER; - lbc_supv_log(chg, LBC_ST_FAULT, "CHG_FAILED (safety timer)"); - return true; - } - if (chg->vbat_uv > PM8916_LBC_SUPV_OVP_UV) { - chg->fault_reason = LBC_FAULT_OVP; - lbc_supv_log(chg, LBC_ST_FAULT, "vbat > 4.25 V"); - return true; - } + if (failed & PM8916_LBC_CHGR_CHG_FAILED_BIT) + return lbc_enter_fault(chg, LBC_FAULT_TIMER, "CHG_FAILED (safety timer)"); + if (chg->vbat_uv > PM8916_LBC_SUPV_OVP_UV) + return lbc_enter_fault(chg, LBC_FAULT_OVP, "vbat > 4.25 V"); return false; }