--- v1 +++ v2 @@ -99,14 +99,12 @@ int state; int fault_reason; unsigned int recharge_uv; /* production threshold from monitored-battery */ - u32 test_recharge_uv; /* debugfs test-only override, 0 = off */ unsigned int timeout_min; int vbat_uv; int done_polls; int low_polls; unsigned int n_terminated; unsigned int n_recharge; - unsigned int n_forced; struct { time64_t t; int from, to, vbat; @@ -279,9 +277,12 @@ /* * Aurora B10B-3 recharge supervisor. - * The LBC ends a charge cycle in hardware (iterm comparator) and then stays off until software gives CHG_EN a - * 0 -> 1 edge (downstream qpnp-linear-charger did this from BMS SOC / VBAT_DET IRQ). This supervisor polls the - * VADC VBAT every 30 s and restarts charging when VBAT stays below the monitored-battery + * The LBC ends a charge cycle in hardware (end-of-charge latch: CHG_STATUS 00, USB path 01, USB input ~0) and then + * stays off until software gives CHG_EN a 0 -> 1 edge (downstream qpnp-linear-charger did this from BMS SOC / + * VBAT_DET IRQ). Verified on Aurora (B10B-3 ram1): CHG_CTRL 0x21 -> 0xa0 releases the latch. + * Also verified: writing CHG_EN = 0 does NOT stop the charger (USB input stays ~0.36 A), so the supervisor never + * uses CHG_CTRL to "turn charging off"; TERMINATED is only the hardware latch, observed after CHARGING. + * The supervisor polls the VADC VBAT every 30 s and restarts charging when VBAT stays below the monitored-battery * re-charge-voltage-microvolt for 3 consecutive polls. */ static void lbc_supv_log(struct pm8916_lbc_charger *chg, int to, const char *why) @@ -306,7 +307,7 @@ en ? PM8916_LBC_CHGR_CHG_EN : PM8916_LBC_CHGR_FORCE_BATT_ON); } -/* explicit CHG_EN 0 -> 1 edge; on (re)insert also clear a latched CHG_FAILED */ +/* the verified restart: CHG_CTRL 0x21 (CHG_EN 0) -> 20 ms -> 0xa0 (CHG_EN 1); on (re)insert also clear CHG_FAILED */ static int lbc_charge_restart(struct pm8916_lbc_charger *chg, bool clear_failed) { int ret; @@ -324,11 +325,6 @@ return lbc_charge_enable(chg, true); } -static unsigned int lbc_recharge_uv(struct pm8916_lbc_charger *chg) -{ - return chg->test_recharge_uv ? chg->test_recharge_uv : chg->recharge_uv; -} - static void lbc_supv_work(struct work_struct *work) { struct pm8916_lbc_charger *chg = container_of(work, struct pm8916_lbc_charger, supv_work.work); @@ -351,8 +347,8 @@ chg->vbat_uv = -1; if (!(usb_rt & PM8916_LBC_USB_USBIN_VALID)) { + /* CHG_CTRL untouched; the next insert restarts with the verified edge */ if (chg->state != LBC_ST_NO_USB) { - lbc_charge_enable(chg, false); chg->fault_reason = LBC_FAULT_NONE; lbc_supv_log(chg, LBC_ST_NO_USB, "usb removed"); } @@ -372,24 +368,27 @@ break; case LBC_ST_CHARGING: - if (chg->vbat_uv > PM8916_LBC_SUPV_OVP_UV) { - lbc_charge_enable(chg, false); - chg->fault_reason = LBC_FAULT_OVP; - lbc_supv_log(chg, LBC_ST_FAULT, "vbat > 4.25 V"); - break; - } + /* + * Safety timer expiry: the hardware stops and sets CHG_FAILED. Latch FAULT so the supervisor never + * restarts; no register write (CHG_EN = 0 is not a verified way to stop the charger). + */ if (failed & PM8916_LBC_CHGR_CHG_FAILED_BIT) { - lbc_charge_enable(chg, false); chg->fault_reason = LBC_FAULT_TIMER; lbc_supv_log(chg, LBC_ST_FAULT, "CHG_FAILED (safety timer)"); break; } + if (chg->vbat_uv > PM8916_LBC_SUPV_OVP_UV) { + /* defence only: VDD_MAX regulates in hardware; latch FAULT = no further restarts */ + chg->fault_reason = LBC_FAULT_OVP; + lbc_supv_log(chg, LBC_ST_FAULT, "vbat > 4.25 V"); + break; + } + /* TERMINATED = hardware end-of-charge latch only; CHG_CTRL is not changed */ if (sts == 0 && !(path & PM8916_LBC_USB_PATH_ON)) chg->done_polls++; else chg->done_polls = 0; if (chg->done_polls >= PM8916_LBC_SUPV_DONE_POLLS) { - lbc_charge_enable(chg, false); chg->n_terminated++; chg->low_polls = 0; lbc_supv_log(chg, LBC_ST_TERMINATED, "hardware end of charge"); @@ -397,18 +396,18 @@ break; case LBC_ST_TERMINATED: - if (!lbc_recharge_uv(chg) || chg->vbat_uv < 0) + if (!chg->recharge_uv || chg->vbat_uv < 0) break; - if ((unsigned int)chg->vbat_uv < lbc_recharge_uv(chg)) + if ((unsigned int)chg->vbat_uv < chg->recharge_uv) chg->low_polls++; else chg->low_polls = 0; if (chg->low_polls >= PM8916_LBC_SUPV_LOW_POLLS) { chg->done_polls = 0; - if (!lbc_charge_enable(chg, true)) { + chg->low_polls = 0; + if (!lbc_charge_restart(chg, false)) { chg->n_recharge++; - lbc_supv_log(chg, LBC_ST_CHARGING, - chg->test_recharge_uv ? "recharge (TEST threshold)" : "recharge"); + lbc_supv_log(chg, LBC_ST_CHARGING, "recharge"); } } break; @@ -446,11 +445,9 @@ mutex_lock(&chg->supv_lock); seq_printf(s, "state %s fault %s vbat_uv %d\n", lbc_state_name[chg->state], lbc_fault_name[chg->fault_reason], chg->vbat_uv); - seq_printf(s, "recharge_uv %u (production %u, test %u) done_polls %d low_polls %d timeout_min %u\n", - lbc_recharge_uv(chg), chg->recharge_uv, chg->test_recharge_uv, chg->done_polls, - chg->low_polls, chg->timeout_min); - seq_printf(s, "n_terminated %u n_recharge %u n_forced %u\n", chg->n_terminated, - chg->n_recharge, chg->n_forced); + seq_printf(s, "recharge_uv %u done_polls %d low_polls %d timeout_min %u\n", + chg->recharge_uv, chg->done_polls, chg->low_polls, chg->timeout_min); + seq_printf(s, "n_terminated %u n_recharge %u\n", chg->n_terminated, chg->n_recharge); for (i = 0; i < ARRAY_SIZE(regs); i++) { if (regmap_read(chg->regmap, chg->reg[regs[i].blk] + regs[i].off, &v)) v = 0xffff; @@ -468,27 +465,6 @@ } DEFINE_SHOW_ATTRIBUTE(lbc_state); -/* TEST ONLY (RAM test images): the supervisor performs its normal TERMINATED action now. */ -static ssize_t lbc_force_terminate_write(struct file *file, const char __user *buf, size_t len, - loff_t *ppos) -{ - struct pm8916_lbc_charger *chg = file->private_data; - - mutex_lock(&chg->supv_lock); - if (chg->state == LBC_ST_CHARGING && !lbc_charge_enable(chg, false)) { - chg->n_forced++; - chg->low_polls = 0; - lbc_supv_log(chg, LBC_ST_TERMINATED, "FORCED by test hook"); - } - mutex_unlock(&chg->supv_lock); - return len; -} - -static const struct file_operations lbc_force_terminate_fops = { - .open = simple_open, - .write = lbc_force_terminate_write, -}; - static void lbc_debugfs_remove(void *data) { debugfs_remove_recursive(data); @@ -716,8 +692,6 @@ chg->debugfs = debugfs_create_dir("pm8916_lbc", NULL); debugfs_create_file("state", 0444, chg->debugfs, chg, &lbc_state_fops); - debugfs_create_file("force_terminate", 0200, chg->debugfs, chg, &lbc_force_terminate_fops); - debugfs_create_u32("test_recharge_uv", 0600, chg->debugfs, &chg->test_recharge_uv); ret = devm_add_action_or_reset(dev, lbc_debugfs_remove, chg->debugfs); if (ret) return ret;