--- a/drivers/iio/adc/qcom-spmi-vadc.c +++ b/drivers/iio/adc/qcom-spmi-vadc.c @@ -563,7 +563,7 @@ VADC_CHAN_NO_SCALE(SPARE1_03, 1) VADC_CHAN_NO_SCALE(USB_ID_MV, 1) VADC_CHAN_VOLT(VCOIN, 1, SCALE_DEFAULT) - VADC_CHAN_NO_SCALE(VBAT_SNS, 1) + VADC_CHAN_VOLT(VBAT_SNS, 1, SCALE_DEFAULT) VADC_CHAN_VOLT(VSYS, 1, SCALE_DEFAULT) VADC_CHAN_TEMP(DIE_TEMP, 0, SCALE_PMIC_THERM) VADC_CHAN_VOLT(REF_625MV, 0, SCALE_DEFAULT) --- a/drivers/power/supply/pm8916_lbc.c +++ b/drivers/power/supply/pm8916_lbc.c @@ -13,6 +13,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include /* Two bytes: type + subtype */ #define PM8916_PERPH_TYPE 0x04 @@ -42,6 +49,30 @@ #define PM8916_LBC_CHGR_CHG_CTRL 0x49 #define PM8916_LBC_CHGR_CHG_EN BIT(7) #define PM8916_LBC_CHGR_PSTG_EN BIT(5) +#define PM8916_LBC_CHGR_FORCE_BATT_ON BIT(0) +#define PM8916_LBC_CHGR_EN_MASK (PM8916_LBC_CHGR_CHG_EN | PM8916_LBC_CHGR_FORCE_BATT_ON) + +#define PM8916_LBC_CHGR_CHG_FAILED 0x4a +#define PM8916_LBC_CHGR_CHG_FAILED_BIT BIT(7) + +/* USB_CHGPTH 0x08: 0x02 while the charge path is on, 0x01 after end of charge (observed, not documented) */ +#define PM8916_LBC_USB_PATH_STS 0x08 +#define PM8916_LBC_USB_PATH_ON BIT(1) + +/* Aurora B10B-3 recharge supervisor */ +#define PM8916_LBC_SUPV_PERIOD_MS 30000 +#define PM8916_LBC_SUPV_LATCH_POLLS 2 +#define PM8916_LBC_SUPV_NOCV_POLLS 2 +#define PM8916_LBC_SUPV_OVP_UV 4250000 +#define PM8916_LBC_CV_HOLD_DEFAULT_MIN 60 +#define PM8916_LBC_CHGR_CV_LOOP BIT(1) + +#define PM8916_LBC_MISC_BOOT_DONE 0x42 +#define PM8916_LBC_MISC_BOOT_DONE_BIT BIT(7) +#define PM8916_LBC_TIMEOUT_DEFAULT_MIN 512 +#define PM8916_LBC_TIMEOUT_STEP_MIN 4 +#define PM8916_LBC_TIMEOUT_MAX_MIN 512 +#define PM8916_LBC_SUPV_LOG 16 #define PM8916_LBC_CHGR_MIN_CURRENT 90000 #define PM8916_LBC_CHGR_MAX_CURRENT 1440000 @@ -65,6 +96,61 @@ unsigned int charge_voltage_safe; unsigned int charge_current_max; unsigned int charge_current_safe; + + /* charger supervisor (Aurora B10B-3 v3) */ + struct iio_channel *vbat_chan; + struct delayed_work supv_work; + struct mutex supv_lock; + int state; + int fault_reason; + unsigned int hold_uv; /* aurora,hold-voltage-microvolt, 0 = HOLD disabled */ + unsigned int full_min_uv; /* aurora,full-min-voltage-microvolt */ + unsigned int cv_hold_min; /* aurora,cv-hold-minutes */ + unsigned int ibat_code; /* IBAT_MAX code programmed at probe */ + unsigned int timeout_min; + int vbat_uv; + int latch_polls; + int nocv_polls; + time64_t cv_since; /* start of the current CV run, 0 = none */ + bool supv_ready; /* set at the end of probe; IRQ kicks only after that */ + unsigned int n_insert; + unsigned int n_hold; + unsigned int n_latch; + struct { + time64_t t; + int from, to, vbat; + const char *why; + } log[PM8916_LBC_SUPV_LOG]; + unsigned int log_n; + struct dentry *debugfs; +}; + +enum { + LBC_ST_INIT = 0, + LBC_ST_NO_USB, + LBC_ST_CHARGING, + LBC_ST_HOLD, + LBC_ST_FAULT, +}; + +static const char * const lbc_state_name[] = { + [LBC_ST_INIT] = "INIT", + [LBC_ST_NO_USB] = "NO_USB", + [LBC_ST_CHARGING] = "CHARGING", + [LBC_ST_HOLD] = "HOLD", + [LBC_ST_FAULT] = "FAULT", +}; + +enum { + LBC_FAULT_NONE = 0, + LBC_FAULT_TIMER, + LBC_FAULT_OVP, +}; + +static const char * const lbc_fault_name[] = { + [LBC_FAULT_NONE] = "none", + [LBC_FAULT_TIMER] = "safety-timer (CHG_FAILED)", + [LBC_FAULT_OVP] = "vbat-overvoltage", }; static const unsigned int pm8916_lbc_charger_cable[] = { @@ -79,7 +165,7 @@ LBC_MISC, }; -static int pm8916_lbc_charger_configure(struct pm8916_lbc_charger *chg) +static int pm8916_lbc_charger_configure(struct pm8916_lbc_charger *chg, bool probe) { int ret = 0; unsigned int tmp; @@ -91,9 +177,12 @@ tmp /= PM8916_LBC_CHGR_VOLTAGE_STEP; chg->charge_voltage_max = PM8916_LBC_CHGR_MIN_VOLTAGE + tmp * PM8916_LBC_CHGR_VOLTAGE_STEP; - ret = regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_VDD_MAX, tmp); - if (ret) - goto error; + /* after probe the supervisor owns VDD_MAX (charge level / hold level) */ + if (probe) { + ret = regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_VDD_MAX, tmp); + if (ret) + goto error; + } chg->charge_current_max = min(chg->charge_current_max, chg->charge_current_safe); @@ -102,15 +191,22 @@ tmp = chg->charge_current_max / PM8916_LBC_CHGR_MIN_CURRENT - 1; chg->charge_current_max = (tmp + 1) * PM8916_LBC_CHGR_MIN_CURRENT; + chg->ibat_code = tmp; ret = regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_IBAT_MAX, tmp); if (ret) goto error; - ret = regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_CHG_CTRL, - PM8916_LBC_CHGR_CHG_EN | PM8916_LBC_CHGR_PSTG_EN); - if (ret) - goto error; + /* + * CHG_CTRL is written only at probe. Afterwards the supervisor owns CHG_EN, so a + * constant_charge_current update must not restart charging in HOLD/FAULT. + */ + if (probe) { + ret = regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_CHG_CTRL, + PM8916_LBC_CHGR_CHG_EN | PM8916_LBC_CHGR_PSTG_EN); + if (ret) + goto error; + } return ret; @@ -126,6 +222,23 @@ struct pm8916_lbc_charger *chg = power_supply_get_drvdata(psy); switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + switch (chg->state) { + case LBC_ST_CHARGING: + val->intval = POWER_SUPPLY_STATUS_CHARGING; + break; + case LBC_ST_HOLD: + val->intval = POWER_SUPPLY_STATUS_FULL; + break; + case LBC_ST_NO_USB: + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + break; + default: + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + } + return 0; + case POWER_SUPPLY_PROP_ONLINE: val->intval = chg->online; return 0; @@ -151,8 +264,15 @@ switch (prop) { case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + { + int ret; + + mutex_lock(&chg->supv_lock); chg->charge_current_max = val->intval; - return pm8916_lbc_charger_configure(chg); + ret = pm8916_lbc_charger_configure(chg, false); + mutex_unlock(&chg->supv_lock); + return ret; + } default: return -EINVAL; } @@ -170,11 +290,265 @@ } static enum power_supply_property pm8916_lbc_charger_properties[] = { + POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, }; + +/* + * Aurora B10B-3 v3 charger supervisor: CHARGE -> HOLD -> (battery) -> CHARGE. + * 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. + * NO_USB: nothing is written. + * 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. + */ +static void lbc_supv_log(struct pm8916_lbc_charger *chg, int to, const char *why) +{ + unsigned int i = chg->log_n++ % PM8916_LBC_SUPV_LOG; + + chg->log[i].t = ktime_get_seconds(); + chg->log[i].from = chg->state; + chg->log[i].to = to; + 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", + 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], + lbc_state_name[to], why, chg->vbat_uv); + chg->state = to; +} + +static int lbc_charge_enable(struct pm8916_lbc_charger *chg, bool en) +{ + return regmap_update_bits(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_CHG_CTRL, + PM8916_LBC_CHGR_EN_MASK, + en ? PM8916_LBC_CHGR_CHG_EN : PM8916_LBC_CHGR_FORCE_BATT_ON); +} + +/* 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; + + if (clear_failed) { + ret = regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_CHG_FAILED, + PM8916_LBC_CHGR_CHG_FAILED_BIT); + if (ret) + return ret; + } + ret = lbc_charge_enable(chg, false); + if (ret) + return ret; + msleep(20); + return lbc_charge_enable(chg, true); +} + +static int lbc_set_vdd_max(struct pm8916_lbc_charger *chg, unsigned int uv) +{ + uv = clamp_t(u32, uv, PM8916_LBC_CHGR_MIN_VOLTAGE, chg->charge_voltage_safe); + return regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_VDD_MAX, + (uv - PM8916_LBC_CHGR_MIN_VOLTAGE) / PM8916_LBC_CHGR_VOLTAGE_STEP); +} + +/* USB insert / probe with USB: start a full cycle at the charge level */ +static int lbc_enter_charging(struct pm8916_lbc_charger *chg) +{ + unsigned int v; + int ret; + + ret = regmap_update_bits(chg->regmap, chg->reg[LBC_MISC] + PM8916_LBC_MISC_BOOT_DONE, + PM8916_LBC_MISC_BOOT_DONE_BIT, PM8916_LBC_MISC_BOOT_DONE_BIT); + ret = ret ?: lbc_set_vdd_max(chg, chg->charge_voltage_max); + ret = ret ?: regmap_read(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_IBAT_MAX, &v); + if (!ret && v != chg->ibat_code) + ret = regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_IBAT_MAX, chg->ibat_code); + ret = ret ?: lbc_charge_restart(chg, true); + chg->latch_polls = 0; + chg->nocv_polls = 0; + chg->cv_since = 0; + return ret; +} + +/* 00/01 latch with USB valid: count polls; restart with the verified edge (VDD_MAX untouched) */ +static bool lbc_handle_latch(struct pm8916_lbc_charger *chg, unsigned int sts, unsigned int path) +{ + if (sts == 0 && !(path & PM8916_LBC_USB_PATH_ON)) + chg->latch_polls++; + else + chg->latch_polls = 0; + if (chg->latch_polls < PM8916_LBC_SUPV_LATCH_POLLS) + return false; + chg->latch_polls = 0; + chg->n_latch++; + if (!lbc_charge_restart(chg, false)) + dev_warn(chg->dev, "supervisor: 00/01 latch in %s (vbat %d uV) -> restart edge #%u\n", + lbc_state_name[chg->state], chg->vbat_uv, chg->n_latch); + 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; + } + return false; +} + +static void lbc_supv_work(struct work_struct *work) +{ + struct pm8916_lbc_charger *chg = container_of(work, struct pm8916_lbc_charger, supv_work.work); + unsigned int usb_rt, sts, path, failed; + time64_t now = ktime_get_seconds(); + int ret, uv; + + mutex_lock(&chg->supv_lock); + + ret = regmap_read(chg->regmap, chg->reg[LBC_USB] + PM8916_INT_RT_STS, &usb_rt); + ret = ret ?: regmap_read(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_CHG_STATUS, &sts); + ret = ret ?: regmap_read(chg->regmap, chg->reg[LBC_USB] + PM8916_LBC_USB_PATH_STS, &path); + ret = ret ?: regmap_read(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_CHG_FAILED, &failed); + if (ret) { + dev_err(chg->dev, "supervisor: register read failed: %d\n", ret); + goto out; + } + if (!iio_read_channel_processed(chg->vbat_chan, &uv)) + chg->vbat_uv = uv; + else + chg->vbat_uv = -1; + + if (!(usb_rt & PM8916_LBC_USB_USBIN_VALID)) { + if (chg->state != LBC_ST_NO_USB) { + chg->fault_reason = LBC_FAULT_NONE; + lbc_supv_log(chg, LBC_ST_NO_USB, "usb removed"); + } + goto out; + } + + switch (chg->state) { + case LBC_ST_INIT: + case LBC_ST_NO_USB: + chg->fault_reason = LBC_FAULT_NONE; + chg->n_insert++; + if (!lbc_enter_charging(chg)) + lbc_supv_log(chg, LBC_ST_CHARGING, + chg->state == LBC_ST_INIT ? "probe, usb present" : "usb inserted"); + break; + + case LBC_ST_CHARGING: + if (lbc_check_fault(chg, failed)) + break; + if (lbc_handle_latch(chg, sts, path)) { + chg->cv_since = 0; + chg->nocv_polls = 0; + break; + } + if (!chg->hold_uv || chg->vbat_uv < 0) + break; + /* CV run: CV loop bit with VBAT >= full-min; tolerate one poll without the bit (05/07 interleave) */ + if ((unsigned int)chg->vbat_uv < chg->full_min_uv) { + chg->cv_since = 0; + chg->nocv_polls = 0; + break; + } + if (sts & PM8916_LBC_CHGR_CV_LOOP) { + chg->nocv_polls = 0; + if (!chg->cv_since) + chg->cv_since = now; + } else if (++chg->nocv_polls >= PM8916_LBC_SUPV_NOCV_POLLS) { + chg->cv_since = 0; + } + if (chg->cv_since && now - chg->cv_since >= (time64_t)chg->cv_hold_min * 60) { + if (!lbc_set_vdd_max(chg, chg->hold_uv)) { + chg->n_hold++; + chg->latch_polls = 0; + lbc_supv_log(chg, LBC_ST_HOLD, "CV time reached, VDD_MAX -> hold level"); + } + } + break; + + case LBC_ST_HOLD: + if (lbc_check_fault(chg, failed)) + break; + /* keep the board on USB: a 00/01 latch gets the restart edge, VDD_MAX stays at the hold level */ + lbc_handle_latch(chg, sts, path); + break; + + case LBC_ST_FAULT: + /* latched: left only by USB removal/insert or reboot */ + break; + } + +out: + mutex_unlock(&chg->supv_lock); + power_supply_changed(chg->charger); + queue_delayed_work(system_freezable_wq, &chg->supv_work, + msecs_to_jiffies(PM8916_LBC_SUPV_PERIOD_MS)); +} + +static int lbc_state_show(struct seq_file *s, void *unused) +{ + struct pm8916_lbc_charger *chg = s->private; + static const struct { const char *n; int blk; unsigned int off; } regs[] = { + { "CHG_STATUS", LBC_CHGR, PM8916_LBC_CHGR_CHG_STATUS }, + { "CHGR_RT", LBC_CHGR, PM8916_INT_RT_STS }, + { "CHG_CTRL", LBC_CHGR, PM8916_LBC_CHGR_CHG_CTRL }, + { "CHG_FAILED", LBC_CHGR, PM8916_LBC_CHGR_CHG_FAILED }, + { "VDD_MAX", LBC_CHGR, PM8916_LBC_CHGR_VDD_MAX }, + { "IBAT_MAX", LBC_CHGR, PM8916_LBC_CHGR_IBAT_MAX }, + { "TCHG_MAX_EN", LBC_CHGR, PM8916_LBC_CHGR_TCHG_MAX_EN }, + { "TCHG_MAX", LBC_CHGR, PM8916_LBC_CHGR_TCHG_MAX }, + { "USB_PATH", LBC_USB, PM8916_LBC_USB_PATH_STS }, + { "USB_RT", LBC_USB, PM8916_INT_RT_STS }, + { "BAT_RT", LBC_BAT_IF, PM8916_INT_RT_STS }, + { "BOOT_DONE", LBC_MISC, PM8916_LBC_MISC_BOOT_DONE }, + }; + time64_t now = ktime_get_seconds(); + unsigned int i, v; + + 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, "charge_uv %u hold_uv %u full_min_uv %u cv_hold_min %u cv_s %lld nocv %d timeout_min %u\n", + chg->charge_voltage_max, chg->hold_uv, chg->full_min_uv, chg->cv_hold_min, + chg->cv_since ? now - chg->cv_since : -1LL, chg->nocv_polls, chg->timeout_min); + seq_printf(s, "n_insert %u n_hold %u n_latch %u\n", chg->n_insert, chg->n_hold, chg->n_latch); + for (i = 0; i < ARRAY_SIZE(regs); i++) { + if (regmap_read(chg->regmap, chg->reg[regs[i].blk] + regs[i].off, &v)) + v = 0xffff; + seq_printf(s, "%s %02x\n", regs[i].n, v); + } + for (i = chg->log_n > PM8916_LBC_SUPV_LOG ? chg->log_n - PM8916_LBC_SUPV_LOG : 0; i < chg->log_n; i++) { + unsigned int j = i % PM8916_LBC_SUPV_LOG; + + seq_printf(s, "log %u t=%lld %s->%s vbat=%d %s\n", i, chg->log[j].t, + lbc_state_name[chg->log[j].from], lbc_state_name[chg->log[j].to], + chg->log[j].vbat, chg->log[j].why); + } + mutex_unlock(&chg->supv_lock); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(lbc_state); + +static void lbc_debugfs_remove(void *data) +{ + debugfs_remove_recursive(data); +} + static irqreturn_t pm8916_lbc_charger_state_changed_irq(int irq, void *data) { struct pm8916_lbc_charger *chg = data; @@ -190,6 +564,9 @@ power_supply_changed(chg->charger); + if (READ_ONCE(chg->supv_ready)) + mod_delayed_work(system_freezable_wq, &chg->supv_work, 0); + return IRQ_HANDLED; } @@ -230,10 +607,35 @@ if (ret) return ret; - /* Disable charger timeout. */ + /* + * Keep the charger safety timer running (upstream disabled it). Same sequence as the + * downstream qpnp-linear-charger: timer off, TCHG_MAX = minutes / 4 - 1 (4..512 min), timer on. + */ + chg->timeout_min = PM8916_LBC_TIMEOUT_DEFAULT_MIN; + device_property_read_u32(dev, "qcom,charge-timeout-minutes", &chg->timeout_min); + chg->timeout_min = clamp_t(u32, chg->timeout_min, PM8916_LBC_TIMEOUT_STEP_MIN, + PM8916_LBC_TIMEOUT_MAX_MIN); + chg->timeout_min -= chg->timeout_min % PM8916_LBC_TIMEOUT_STEP_MIN; + ret = regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_TCHG_MAX_EN, 0x00); if (ret) return ret; + tmp = chg->timeout_min / PM8916_LBC_TIMEOUT_STEP_MIN - 1; + ret = regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_TCHG_MAX, tmp); + if (ret) + return ret; + ret = regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_TCHG_MAX_EN, + PM8916_LBC_CHGR_TCHG_MAX_ENABLED); + if (ret) + return ret; + + ret = regmap_read(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_TCHG_MAX, &tmp); + if (ret) + return ret; + if (tmp != chg->timeout_min / PM8916_LBC_TIMEOUT_STEP_MIN - 1) + return dev_err_probe(dev, -EIO, "TCHG_MAX readback 0x%x != 0x%x\n", tmp, + chg->timeout_min / PM8916_LBC_TIMEOUT_STEP_MIN - 1); + dev_info(dev, "safety timer %u min (TCHG_MAX 0x%02x), enabled\n", chg->timeout_min, tmp); return ret; } @@ -261,6 +663,7 @@ return -ENOMEM; chg->dev = dev; + mutex_init(&chg->supv_lock); chg->regmap = dev_get_regmap(pdev->dev.parent, NULL); if (!chg->regmap) @@ -309,7 +712,7 @@ ret = pm8916_lbc_charger_probe_dt(chg); if (ret) - dev_err_probe(dev, ret, "Error while parsing device tree\n"); + return dev_err_probe(dev, ret, "Error while parsing device tree\n"); psy_cfg.drv_data = chg; psy_cfg.fwnode = dev_fwnode(dev); @@ -322,6 +725,21 @@ if (ret) return dev_err_probe(dev, ret, "Unable to get battery info\n"); + chg->vbat_chan = devm_iio_channel_get(dev, "vbat"); + if (IS_ERR(chg->vbat_chan)) + return dev_err_probe(dev, PTR_ERR(chg->vbat_chan), "no VADC vbat channel\n"); + + chg->cv_hold_min = PM8916_LBC_CV_HOLD_DEFAULT_MIN; + device_property_read_u32(dev, "aurora,cv-hold-minutes", &chg->cv_hold_min); + if (device_property_read_u32(dev, "aurora,hold-voltage-microvolt", &chg->hold_uv) || + device_property_read_u32(dev, "aurora,full-min-voltage-microvolt", &chg->full_min_uv)) { + chg->hold_uv = 0; + dev_warn(dev, "no aurora,hold-voltage/full-min-voltage: HOLD disabled\n"); + } + ret = devm_delayed_work_autocancel(dev, &chg->supv_work, lbc_supv_work); + if (ret) + return ret; + irq = platform_get_irq_byname(pdev, "usb_vbus"); if (irq < 0) return irq; @@ -347,10 +765,29 @@ extcon_set_state_sync(chg->edev, EXTCON_USB, chg->online); chg->charge_voltage_max = chg->info->voltage_max_design_uv; - ret = pm8916_lbc_charger_configure(chg); + ret = pm8916_lbc_charger_configure(chg, true); + if (ret) + return ret; + + chg->debugfs = debugfs_create_dir("pm8916_lbc", NULL); + debugfs_create_file("state", 0444, chg->debugfs, chg, &lbc_state_fops); + ret = devm_add_action_or_reset(dev, lbc_debugfs_remove, chg->debugfs); if (ret) return ret; + if (chg->hold_uv && (chg->hold_uv < PM8916_LBC_CHGR_MIN_VOLTAGE || + chg->hold_uv >= chg->charge_voltage_max || + chg->full_min_uv > chg->charge_voltage_max)) { + dev_warn(dev, "invalid hold %u / full-min %u for charge level %u: HOLD disabled\n", + chg->hold_uv, chg->full_min_uv, chg->charge_voltage_max); + chg->hold_uv = 0; + } + dev_info(dev, "supervisor v3: charge %u uV, hold %u uV, full-min %u uV, cv %u min, IBAT_MAX code %u, ovp %d uV\n", + chg->charge_voltage_max, chg->hold_uv, chg->full_min_uv, chg->cv_hold_min, + chg->ibat_code, PM8916_LBC_SUPV_OVP_UV); + WRITE_ONCE(chg->supv_ready, true); + queue_delayed_work(system_freezable_wq, &chg->supv_work, 0); + return 0; comm_error: