- 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
483 lines
16 KiB
Diff
483 lines
16 KiB
Diff
--- 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 <linux/delay.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/extcon-provider.h>
|
|
+#include <linux/debugfs.h>
|
|
+#include <linux/devm-helpers.h>
|
|
+#include <linux/iio/consumer.h>
|
|
+#include <linux/mutex.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/seq_file.h>
|
|
+#include <linux/workqueue.h>
|
|
|
|
/* Two bytes: type + subtype */
|
|
#define PM8916_PERPH_TYPE 0x04
|
|
@@ -42,6 +49,25 @@
|
|
#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_DONE_POLLS 2
|
|
+#define PM8916_LBC_SUPV_LOW_POLLS 3
|
|
+#define PM8916_LBC_SUPV_OVP_UV 4250000
|
|
+#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 +91,55 @@
|
|
unsigned int charge_voltage_safe;
|
|
unsigned int charge_current_max;
|
|
unsigned int charge_current_safe;
|
|
+
|
|
+ /* recharge supervisor */
|
|
+ struct iio_channel *vbat_chan;
|
|
+ struct delayed_work supv_work;
|
|
+ struct mutex supv_lock;
|
|
+ int state;
|
|
+ int fault_reason;
|
|
+ unsigned int recharge_uv; /* production threshold from monitored-battery */
|
|
+ unsigned int timeout_min;
|
|
+ int vbat_uv;
|
|
+ int done_polls;
|
|
+ int low_polls;
|
|
+ unsigned int n_terminated;
|
|
+ unsigned int n_recharge;
|
|
+ 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_TERMINATED,
|
|
+ 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_TERMINATED] = "TERMINATED",
|
|
+ [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 +154,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 set_ctrl)
|
|
{
|
|
int ret = 0;
|
|
unsigned int tmp;
|
|
@@ -107,10 +182,16 @@
|
|
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 recharge supervisor owns CHG_EN, so a
|
|
+ * constant_charge_current update must not re-enable charging in TERMINATED/FAULT.
|
|
+ */
|
|
+ if (set_ctrl) {
|
|
+ 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 +207,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_TERMINATED:
|
|
+ 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;
|
|
@@ -152,7 +250,7 @@
|
|
switch (prop) {
|
|
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
|
chg->charge_current_max = val->intval;
|
|
- return pm8916_lbc_charger_configure(chg);
|
|
+ return pm8916_lbc_charger_configure(chg, false);
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
@@ -170,11 +268,208 @@
|
|
}
|
|
|
|
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 recharge supervisor.
|
|
+ * 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)
|
|
+{
|
|
+ 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;
|
|
+ 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)
|
|
+{
|
|
+ /* stock-style: enabled = CHG_EN, disabled = FORCE_BATT_ON */
|
|
+ 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 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;
|
|
+ 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)) {
|
|
+ /* CHG_CTRL untouched; the next insert restarts with the verified edge */
|
|
+ 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:
|
|
+ /* probe or USB insert: always start a cycle, like stock's usbin path */
|
|
+ chg->done_polls = 0;
|
|
+ chg->low_polls = 0;
|
|
+ chg->fault_reason = LBC_FAULT_NONE;
|
|
+ if (!lbc_charge_restart(chg, true))
|
|
+ lbc_supv_log(chg, LBC_ST_CHARGING,
|
|
+ chg->state == LBC_ST_INIT ? "probe, usb present" : "usb inserted");
|
|
+ break;
|
|
+
|
|
+ case LBC_ST_CHARGING:
|
|
+ /*
|
|
+ * 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) {
|
|
+ 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) {
|
|
+ chg->n_terminated++;
|
|
+ chg->low_polls = 0;
|
|
+ lbc_supv_log(chg, LBC_ST_TERMINATED, "hardware end of charge");
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case LBC_ST_TERMINATED:
|
|
+ if (!chg->recharge_uv || chg->vbat_uv < 0)
|
|
+ break;
|
|
+ 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;
|
|
+ chg->low_polls = 0;
|
|
+ if (!lbc_charge_restart(chg, false)) {
|
|
+ chg->n_recharge++;
|
|
+ lbc_supv_log(chg, LBC_ST_CHARGING, "recharge");
|
|
+ }
|
|
+ }
|
|
+ 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 },
|
|
+ };
|
|
+ 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, "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;
|
|
+ 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 +485,9 @@
|
|
|
|
power_supply_changed(chg->charger);
|
|
|
|
+ if (chg->vbat_chan)
|
|
+ mod_delayed_work(system_freezable_wq, &chg->supv_work, 0);
|
|
+
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
@@ -230,10 +528,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;
|
|
}
|
|
@@ -309,7 +632,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 +645,22 @@
|
|
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");
|
|
+
|
|
+ {
|
|
+ struct device_node *bat = of_parse_phandle(dev->of_node, "monitored-battery", 0);
|
|
+
|
|
+ if (!bat || of_property_read_u32(bat, "re-charge-voltage-microvolt", &chg->recharge_uv))
|
|
+ dev_warn(dev, "no re-charge-voltage-microvolt: automatic recharge disabled\n");
|
|
+ of_node_put(bat);
|
|
+ }
|
|
+ mutex_init(&chg->supv_lock);
|
|
+ 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 +686,21 @@
|
|
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;
|
|
+
|
|
+ dev_info(dev, "recharge supervisor: threshold %u uV, %d x %d ms below, ovp %d uV\n",
|
|
+ chg->recharge_uv, PM8916_LBC_SUPV_LOW_POLLS, PM8916_LBC_SUPV_PERIOD_MS,
|
|
+ PM8916_LBC_SUPV_OVP_UV);
|
|
+ queue_delayed_work(system_freezable_wq, &chg->supv_work, 0);
|
|
+
|
|
return 0;
|
|
|
|
comm_error:
|