- 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
176 lines
6.8 KiB
Diff
176 lines
6.8 KiB
Diff
--- 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;
|