max1720x_battery: switch RC version for MixCap issue
MixCap increased abnormally during FG empty detection, switch to RC1
and disable learning if soc < 20% or temperature under 5 degC
Bug: 213425610
Signed-off-by: Jenny Ho <[email protected]>
Change-Id: I6dbeb332d2046b04978c6168a869f1c553c21028
diff --git a/max1720x_battery.c b/max1720x_battery.c
index 6cb2b5a..94e6d12 100644
--- a/max1720x_battery.c
+++ b/max1720x_battery.c
@@ -100,6 +100,17 @@
int start_vfsoc;
};
+struct max1720x_rc_switch {
+ struct delayed_work switch_work;
+ bool available;
+ bool enable;
+ int soc;
+ int temp;
+ u16 rc1_tempco;
+ u16 rc2_tempco;
+ u16 rc2_learncfg;
+};
+
#define DEFAULT_BATTERY_ID 0
#define DEFAULT_BATTERY_ID_RETRIES 5
@@ -247,6 +258,7 @@
int bhi_fcn_count;
int bhi_acim;
+ struct max1720x_rc_switch rc_switch;
};
#define MAX1720_EMPTY_VOLTAGE(profile, temp, cycle) \
@@ -1065,6 +1077,39 @@
static const DEVICE_ATTR_RO(resistance);
+static ssize_t rc_switch_enable_store(struct device *dev, struct device_attribute *attr,
+ const char *buff, size_t count)
+{
+ struct power_supply *psy = container_of(dev, struct power_supply, dev);
+ struct max1720x_chip *chip = power_supply_get_drvdata(psy);
+ bool curr_enable = chip->rc_switch.enable;
+ int ret;
+
+ if (kstrtobool(buff, &chip->rc_switch.enable))
+ return -EINVAL;
+
+ /* Set back to original INI setting when disable */
+ if (curr_enable == true && chip->rc_switch.enable == false) {
+ ret = REGMAP_WRITE(&chip->regmap, MAX_M5_LEARNCFG, chip->rc_switch.rc2_learncfg);
+ dev_info(chip->dev, "Disable RC switch, recover to learncfg %#x. ret=%d",
+ chip->rc_switch.rc2_learncfg, ret);
+ }
+
+ mod_delayed_work(system_wq, &chip->rc_switch.switch_work, 0);
+
+ return count;
+}
+
+static ssize_t rc_switch_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buff)
+{
+ struct power_supply *psy = container_of(dev, struct power_supply, dev);
+ struct max1720x_chip *chip = power_supply_get_drvdata(psy);
+
+ return scnprintf(buff, PAGE_SIZE, "%d\n", chip->rc_switch.enable);
+}
+
+static const DEVICE_ATTR_RW(rc_switch_enable);
/* lsb 1/256, race with max1720x_model_work() */
static int max1720x_get_capacity_raw(struct max1720x_chip *chip, u16 *data)
@@ -2482,6 +2527,7 @@
{
u16 data, repsoc, vfsoc, avcap, repcap, fullcap, fullcaprep;
u16 fullcapnom, qh0, qh, dqacc, dpacc, qresidual, fstat;
+ u16 learncfg, tempco;
int ret = 0, charge_counter = -1;
ret = REGMAP_READ(&chip->regmap, MAX1720X_REPSOC, &data);
@@ -2540,6 +2586,14 @@
if (ret < 0)
return ret;
+ ret = REGMAP_READ(&chip->regmap, MAX1720X_LEARNCFG, &learncfg);
+ if (ret < 0)
+ return ret;
+
+ ret = REGMAP_READ(&chip->regmap, MAX1720X_TEMPCO, &tempco);
+ if (ret < 0)
+ return ret;
+
ret = max1720x_update_battery_qh_based_capacity(chip);
if (ret == 0)
charge_counter = reg_to_capacity_uah(chip->current_capacity, chip);
@@ -2547,13 +2601,14 @@
gbms_logbuffer_prlog(chip->monitor_log, LOGLEVEL_INFO, 0, LOGLEVEL_INFO,
"%s %02X:%04X %02X:%04X %02X:%04X %02X:%04X %02X:%04X"
" %02X:%04X %02X:%04X %02X:%04X %02X:%04X %02X:%04X"
- " %02X:%04X %02X:%04X %02X:%04X CC:%d",
+ " %02X:%04X %02X:%04X %02X:%04X %02X:%04X %02X:%04X CC:%d",
chip->max1720x_psy_desc.name, MAX1720X_REPSOC, data, MAX1720X_VFSOC,
vfsoc, MAX1720X_AVCAP, avcap, MAX1720X_REPCAP, repcap,
MAX1720X_FULLCAP, fullcap, MAX1720X_FULLCAPREP, fullcaprep,
MAX1720X_FULLCAPNOM, fullcapnom, MAX1720X_QH0, qh0,
MAX1720X_QH, qh, MAX1720X_DQACC, dqacc, MAX1720X_DPACC, dpacc,
MAX1720X_QRESIDUAL, qresidual, MAX1720X_FSTAT, fstat,
+ MAX1720X_LEARNCFG, learncfg, MAX1720X_TEMPCO, tempco,
charge_counter);
chip->pre_repsoc = repsoc;
@@ -4291,6 +4346,146 @@
mutex_unlock(&chip->model_lock);
}
+
+static int max17201_init_rc_switch(struct max1720x_chip *chip)
+{
+ int ret = 0;
+
+ if (chip->gauge_type != MAX_M5_GAUGE_TYPE)
+ return -EINVAL;
+
+ chip->rc_switch.enable = of_property_read_bool(chip->dev->of_node, "maxim,rc-enable");
+
+ ret = of_property_read_u32(chip->dev->of_node, "maxim,rc-soc", &chip->rc_switch.soc);
+ if (ret < 0)
+ return ret;
+
+ ret = of_property_read_u32(chip->dev->of_node, "maxim,rc-temp", &chip->rc_switch.temp);
+ if (ret < 0)
+ return ret;
+
+ ret = of_property_read_u16(chip->batt_node, "maxim,rc1-tempco", &chip->rc_switch.rc1_tempco);
+ if (ret < 0)
+ return ret;
+
+ /* Same as INI value */
+ ret = of_property_read_u16(chip->batt_node, "maxim,rc2-tempco", &chip->rc_switch.rc2_tempco);
+ if (ret < 0)
+ return ret;
+
+ /* Same as INI value */
+ ret = of_property_read_u16(chip->batt_node, "maxim,rc2-learncfg", &chip->rc_switch.rc2_learncfg);
+ if (ret < 0)
+ return ret;
+
+ chip->rc_switch.available = true;
+
+ dev_warn(chip->dev, "rc_switch: enable:%d soc/temp:%d/%d tempco_rc1/rc2:%#x/%#x\n",
+ chip->rc_switch.enable, chip->rc_switch.soc, chip->rc_switch.temp,
+ chip->rc_switch.rc1_tempco, chip->rc_switch.rc2_tempco);
+
+ if (chip->rc_switch.enable)
+ schedule_delayed_work(&chip->rc_switch.switch_work, msecs_to_jiffies(60 * 1000));
+
+ return 0;
+}
+
+#define RC_WORK_TIME_MS 60 * 1000
+#define RC_WORK_TIME_QUICK_MS 5 * 1000
+static void max1720x_rc_work(struct work_struct *work)
+{
+ struct max1720x_chip *chip = container_of(work, struct max1720x_chip,
+ rc_switch.switch_work.work);
+ int interval = RC_WORK_TIME_MS;
+ u16 data, learncfg;
+ bool to_rc1, to_rc2;
+ int ret, soc, temp;
+
+ if (!chip->rc_switch.available || !chip->rc_switch.enable)
+ return;
+
+ /* Read SOC */
+ ret = REGMAP_READ(&chip->regmap, MAX_M5_REPSOC, &data);
+ if (ret < 0)
+ goto reschedule;
+
+ soc = (data >> 8) & 0x00FF;
+
+ /* Read Temperature */
+ ret = max17x0x_reg_read(&chip->regmap, MAX17X0X_TAG_temp, &data);
+ if (ret < 0)
+ goto reschedule;
+
+ temp = reg_to_deci_deg_cel(data);
+
+ /* Read LearnCfg */
+ ret = REGMAP_READ(&chip->regmap, MAX_M5_LEARNCFG, &learncfg);
+ if (ret < 0)
+ goto reschedule;
+
+ /* Disable LearnCfg.LearnTCO */
+ if (learncfg & MAX_M5_LEARNCFG_LEARNTCO_CLEAR) {
+ learncfg = MAX_M5_LEARNCFG_LEARNTCO_CLR(learncfg);
+ ret = REGMAP_WRITE(&chip->regmap, MAX_M5_LEARNCFG, learncfg);
+ if (ret < 0)
+ dev_warn(chip->dev, "Unable to clear LearnTCO\n");
+ }
+
+ to_rc1 = soc < chip->rc_switch.soc || temp < chip->rc_switch.temp;
+ to_rc2 = soc >= chip->rc_switch.soc && temp >= chip->rc_switch.temp;
+
+ if (to_rc1 && ((learncfg & MAX_M5_LEARNCFG_RC_VER) == MAX_M5_LEARNCFG_RC2)) {
+ /*
+ * 1: set LearnCfg.LearnRComp = 0
+ * 2: load TempCo value from RC1 INI file
+ * 3: set LearnCfg.RCx = 0
+ */
+ learncfg = MAX_M5_LEARNCFG_LEARNRCOMP_CLR(learncfg);
+ ret = REGMAP_WRITE(&chip->regmap, MAX_M5_LEARNCFG, learncfg);
+
+ if (ret == 0)
+ ret = REGMAP_WRITE(&chip->regmap, MAX_M5_TEMPCO, chip->rc_switch.rc1_tempco);
+
+ learncfg = MAX_M5_LEARNCFG_RC_VER_CLR(learncfg);
+ if (ret == 0)
+ ret = REGMAP_WRITE(&chip->regmap, MAX_M5_LEARNCFG, learncfg);
+
+ gbms_logbuffer_prlog(chip->monitor_log, LOGLEVEL_INFO, 0, LOGLEVEL_INFO,
+ "%s to RC1. ret=%d soc=%d temp=%d tempco=0x%x, learncfg=0x%x",
+ __func__, ret, soc, temp, chip->rc_switch.rc1_tempco, learncfg);
+
+ } else if (to_rc2 && ((learncfg & MAX_M5_LEARNCFG_RC_VER) == MAX_M5_LEARNCFG_RC1)) {
+ /*
+ * 1: load LearnCfg.LearnRComp from RC2 INI value
+ * 2: load TempCo value from RC2 INI value
+ * 3: set LearnCfg.RCx = 1
+ */
+
+ learncfg |= (chip->rc_switch.rc2_learncfg & MAX_M5_LEARNCFG_LEARNRCOMP);
+ ret = REGMAP_WRITE(&chip->regmap, MAX_M5_LEARNCFG, learncfg);
+
+ if (ret == 0)
+ ret = REGMAP_WRITE(&chip->regmap, MAX_M5_TEMPCO, chip->rc_switch.rc2_tempco);
+
+ learncfg = MAX_M5_LEARNCFG_RC_VER_SET(learncfg);
+ if (ret == 0)
+ ret = REGMAP_WRITE(&chip->regmap, MAX_M5_LEARNCFG, learncfg);
+
+ gbms_logbuffer_prlog(chip->monitor_log, LOGLEVEL_INFO, 0, LOGLEVEL_INFO,
+ "%s to RC2. ret=%d soc=%d temp=%d tempco=0x%x, learncfg=0x%x",
+ __func__, ret, soc, temp, chip->rc_switch.rc2_tempco, learncfg);
+ }
+
+reschedule:
+ if (ret != 0) {
+ interval = RC_WORK_TIME_QUICK_MS;
+ gbms_logbuffer_prlog(chip->monitor_log, LOGLEVEL_WARNING, 0, LOGLEVEL_INFO,
+ "%s didn't finish. ret=%d", __func__, ret);
+ }
+
+ mod_delayed_work(system_wq, &chip->rc_switch.switch_work, msecs_to_jiffies(interval));
+}
+
static int read_chip_property_u32(const struct max1720x_chip *chip,
char *property, u32 *data32)
{
@@ -4671,6 +4866,13 @@
ddata->ini_tempco);
}
+ /*
+ * The RC change is WA for MaxCap increase abnormally b/213425610
+ */
+ ret = max17201_init_rc_switch(chip);
+ if (ret < 0)
+ chip->rc_switch.available = false;
+
/* not needed for FG with NVRAM */
ret = max17x0x_handle_dt_shadow_config(chip);
if (ret == -EPROBE_DEFER)
@@ -5723,6 +5925,11 @@
if (ret)
dev_err(dev, "Failed to create gmsr attribute\n");
+ /* RC switch enable/disable */
+ ret = device_create_file(&chip->psy->dev, &dev_attr_rc_switch_enable);
+ if (ret)
+ dev_err(dev, "Failed to create rc_switch_enable attribute\n");
+
/*
* TODO:
* POWER_SUPPLY_PROP_CHARGE_FULL_ESTIMATE -> GBMS_TAG_GCFE
@@ -5768,6 +5975,7 @@
batt_ce_capacityfiltered_work);
INIT_DELAYED_WORK(&chip->init_work, max1720x_init_work);
INIT_DELAYED_WORK(&chip->model_work, max1720x_model_work);
+ INIT_DELAYED_WORK(&chip->rc_switch.switch_work, max1720x_rc_work);
schedule_delayed_work(&chip->init_work, 0);
@@ -5796,6 +6004,7 @@
max_m5_free_data(chip->model_data);
cancel_delayed_work(&chip->init_work);
cancel_delayed_work(&chip->model_work);
+ cancel_delayed_work(&chip->rc_switch.switch_work);
if (chip->primary->irq)
free_irq(chip->primary->irq, chip);