max1720x_battery: port of capacity drift

Port of the code for capacity drift from P18+P19 and fixes for MWA1.

. b/171764830 MW A1 RCOMP0 is now 16 bits
. b/171741751 workaround RC1 regression

missing debug nodes to force the values, devicetree parameters and
auto-detection of the drift algorithm.

Bug: 171764830
Bug: 171770409
Bug: 171741751
Test: recompile, boot, check charging
Signed-off-by: AleX Pelosi <[email protected]>
Change-Id: I6936e8b3f25257e5bc2e5fc099d3b44b50fd5417
diff --git a/max1720x_battery.c b/max1720x_battery.c
index 708c28f..16f83ae 100644
--- a/max1720x_battery.c
+++ b/max1720x_battery.c
@@ -51,6 +51,12 @@
 #define TEMP_BUCKET_SIZE 5		/* unit is 0.1 degree C */
 #define NB_CYCLE_BUCKETS 4
 
+/* capacity drift */
+#define BATTERY_DEFAULT_CYCLE_STABLE	0
+#define BATTERY_DEFAULT_CYCLE_FADE	0
+#define BATTERY_DEFAULT_CYCLE_BAND	10
+#define BATTERY_MAX_CYCLE_BAND		20
+
 #define HISTORY_DEVICENAME "maxfg_history"
 
 #include "max1720x.h"
@@ -112,7 +118,6 @@
 	u16 *history;
 };
 
-
 struct max1720x_chip {
 	struct device *dev;
 	bool irq_shared;
@@ -186,6 +191,10 @@
 	unsigned long icnt;
 	int zero_irq;
 
+	/* fix capacity drift */
+	struct max1720x_drift_data drift_data;
+	int comp_update_count;
+	int dxacc_update_count;
 
 	/* Capacity Estimation */
 	struct gbatt_capacity_estimation cap_estimate;
@@ -1186,6 +1195,11 @@
 {
 	int offset = 0;
 
+	/*
+	 * uses history on devices that have it (max1720x), use EEPROM
+	 * in others. it might be written in terms of storage.
+	 */
+
 	return offset;
 }
 
@@ -1746,6 +1760,48 @@
 	return err;
 }
 
+static void max1720x_fixup_capacity(struct max1720x_chip *chip, int plugged)
+{
+	struct max1720x_drift_data *ddata = &chip->drift_data;
+	int ret, cycle_count;
+	u16 data16;
+
+	/* do not execute when POR is set */
+	ret = REGMAP_READ(&chip->regmap, MAX1720X_STATUS, &data16);
+	if (ret < 0 || data16 & MAX1720X_STATUS_POR)
+		return;
+
+	/* capacity outliers: fix rcomp0, tempco */
+	ret = max1720x_fixup_comp(ddata, &chip->regmap, plugged);
+	if (ret > 0) {
+		chip->comp_update_count += 1;
+
+		data16 = chip->comp_update_count;
+		ret = gbms_storage_write(GBMS_TAG_CMPC, &data16, sizeof(data16));
+		if (ret < 0)
+			dev_err(chip->dev, "update comp stats (%d)\n", ret);
+	}
+
+	cycle_count = max1720x_get_cycle_count(chip);
+	if (cycle_count < 0) {
+		dev_err(chip->dev, "cannot read cycle_count (%d)\n",
+			cycle_count);
+		return;
+	}
+
+	/* capacity outliers: fix capacity */
+	ret = max1720x_fixup_dxacc(ddata, &chip->regmap, cycle_count, plugged);
+	if (ret > 0) {
+		chip->dxacc_update_count += 1;
+
+		data16 = chip->dxacc_update_count;
+		ret = gbms_storage_write(GBMS_TAG_DXAC, &data16, sizeof(data16));
+		if (ret < 0)
+			dev_err(chip->dev, "update cap stats (%d)\n", ret);
+	}
+
+}
+
 static int max1720x_set_property(struct power_supply *psy,
 				 enum power_supply_property psp,
 				 const union power_supply_propval *val)
@@ -1753,6 +1809,7 @@
 	struct max1720x_chip *chip = (struct max1720x_chip *)
 					power_supply_get_drvdata(psy);
 	struct gbatt_capacity_estimation *ce = &chip->cap_estimate;
+	int delay_ms = 0;
 	int rc = 0;
 
 	pm_runtime_get_sync(chip->dev);
@@ -1764,6 +1821,7 @@
 
 	switch (psp) {
 	case GBMS_PROP_BATT_CE_CTRL:
+
 		mutex_lock(&ce->batt_ce_lock);
 		if (val->intval) {
 
@@ -1773,9 +1831,6 @@
 			}
 
 		} else if (ce->cable_in) {
-			/* check cycle count and save state if needed */
-			mod_delayed_work(system_wq, &chip->model_work, 0);
-
 			if (ce->estimate_state == ESTIMATE_PENDING)
 				cancel_delayed_work_sync(&ce->settle_timer);
 
@@ -1785,6 +1840,12 @@
 			ce->cable_in = false;
 		}
 		mutex_unlock(&ce->batt_ce_lock);
+
+
+		/* check cycle count, save state, check drift if needed */
+		delay_ms = max1720x_check_drift_delay(&chip->drift_data);
+		mod_delayed_work(system_wq, &chip->model_work, delay_ms);
+
 		break;
 	default:
 		return -EINVAL;
@@ -2059,8 +2120,15 @@
 	/* NOTE: should always clear everything even if we lose state */
 	REGMAP_WRITE(&chip->regmap, MAX1720X_STATUS, fg_status_clr);
 
-	if (storm && (fg_status & MAX1720X_STATUS_DSOCI)) {
-		pr_debug("Force power_supply_change in storm\n");
+	/* SOC interrupts need to go through all the time */
+	if (fg_status & MAX1720X_STATUS_DSOCI) {
+		const bool plugged = chip->cap_estimate.cable_in;
+
+		if (max1720x_check_drift_on_soc(&chip->drift_data))
+			max1720x_fixup_capacity(chip, plugged);
+
+		if (storm)
+			pr_debug("Force power_supply_change in storm\n");
 		storm = false;
 	}
 
@@ -3106,6 +3174,11 @@
 		}
 	}
 
+	/* b/171741751, fix capacity drift (if POR is cleared) */
+	if (max1720x_check_drift_enabled(&chip->drift_data))
+		max1720x_fixup_capacity(chip, chip->cap_estimate.cable_in);
+
+	/* save model data */
 	if (cycle_count > chip->model_next_update) {
 
 		pr_debug("%s: cycle_count=%d next_update=%ld\n", __func__,
@@ -3132,6 +3205,135 @@
 	mutex_unlock(&chip->model_lock);
 }
 
+static int read_chip_property_u32(const struct max1720x_chip *chip,
+				  char *property, u32 *data32)
+{
+	int ret;
+
+	if (chip->batt_node) {
+		ret = of_property_read_u32(chip->batt_node, property, data32);
+		if (ret == 0)
+			return ret;
+	}
+
+	return of_property_read_u32(chip->dev->of_node, property, data32);
+}
+
+/* capacity outliers, capacity drift */
+static int max17201_init_fix_capacity(struct max1720x_chip *chip)
+{
+	struct max1720x_drift_data *ddata = &chip->drift_data;
+	u32 data32 = 0;
+	u16 data16;
+	int ret;
+
+	ret = gbms_storage_read(GBMS_TAG_CMPC, &data16, sizeof(data16));
+	if (ret == -EPROBE_DEFER)
+		return -EPROBE_DEFER;
+	if (ret == 0)
+		chip->comp_update_count = data16;
+	else
+		chip->comp_update_count = 0;
+
+	ret = gbms_storage_read(GBMS_TAG_DXAC, &data16, sizeof(data16));
+	if (ret == -EPROBE_DEFER)
+		return -EPROBE_DEFER;
+	if (ret == 0)
+		chip->dxacc_update_count = data16;
+	else
+		chip->dxacc_update_count = 0;
+
+	/* device dependent values */
+	ddata->rsense = chip->RSense;
+
+	ret = of_property_read_u32(chip->dev->of_node, "maxim,capacity-design",
+				   &data32);
+	if (ret < 0) {
+		ddata->design_capacity = -1;
+	} else if (data32 != 0) {
+		ddata->design_capacity = data32;
+	} else if (chip->regmap_nvram.regmap) {
+		/*
+		 * TODO: read design capacity from NVRAM if available
+		 * ret = REGMAP_READ(&chip->regmap_nvram, MAX1720X_NDESIGNCAP,
+		 *		  &ddata->design_capacity);
+		 */
+		ret = REGMAP_READ(&chip->regmap, MAX1720X_DESIGNCAP,
+				  &ddata->design_capacity);
+		if (ret < 0)
+			return -EPROBE_DEFER;
+
+		/* add retries? */
+	}
+
+	/*
+	 * chemistry dependent codes:
+	 * NOTE: ->batt_node is initialized in *_handle_dt_shadow_config
+	 */
+	ret = read_chip_property_u32(chip, "maxim,capacity-rcomp0", &data32);
+	if (ret < 0)
+		ddata->ini_rcomp0 = -1;
+	else
+		ddata->ini_rcomp0 = data32;
+
+	ret = read_chip_property_u32(chip, "maxim,capacity-tempco", &data32);
+	if (ret < 0)
+		ddata->ini_tempco = -1;
+	else
+		ddata->ini_tempco = data32;
+
+	ret = of_property_read_u32(chip->dev->of_node, "maxim,capacity-stable",
+				   &data32);
+	if (ret < 0)
+		ddata->cycle_stable = BATTERY_DEFAULT_CYCLE_STABLE;
+	else
+		ddata->cycle_stable = data32;
+
+	ret = of_property_read_u32(chip->dev->of_node, "maxim,capacity-fade",
+				   &data32);
+	if (ret < 0)
+		ddata->cycle_fade = BATTERY_DEFAULT_CYCLE_FADE;
+	else
+		ddata->cycle_fade = data32;
+
+	ret = of_property_read_u32(chip->dev->of_node, "maxim,capacity-band",
+				   &data32);
+	if (ret < 0) {
+		ddata->cycle_band = BATTERY_DEFAULT_CYCLE_BAND;
+	} else {
+		ddata->cycle_band = data32;
+		if (ddata->cycle_band > BATTERY_MAX_CYCLE_BAND)
+			ddata->cycle_band = BATTERY_MAX_CYCLE_BAND;
+	}
+
+	ret = of_property_read_u32(chip->dev->of_node, "maxim,algo-version",
+				   &data32);
+	if (ret < 0)
+		ddata->algo_ver = MAX1720X_DA_VER_ORIG;
+	else if (data32 < MAX1720X_DA_VER_NONE || data32 > MAX1720X_DA_VER_MWA2)
+		ddata->algo_ver = MAX1720X_DA_VER_NONE;
+	else
+		ddata->algo_ver = MAX1720X_DA_VER_NONE;
+
+	dev_info(chip->dev, "ver=%d rsns=%d cnts=%d,%d dc=%d cap_sta=%d cap_fad=%d rcomp0=0x%x tempco=0x%x\n",
+		ddata->algo_ver, ddata->rsense,
+		chip->comp_update_count, chip->dxacc_update_count,
+		ddata->design_capacity, ddata->cycle_stable, ddata->cycle_fade,
+		ddata->ini_rcomp0, ddata->ini_tempco);
+
+	ret = read_chip_property_u32(chip, "maxim,capacity-filtercfg", &data32);
+	if (ret < 0)
+		ddata->ini_filtercfg = -1;
+	else
+		ddata->ini_filtercfg = data32;
+
+	if (ddata->ini_filtercfg)
+		dev_info(chip->dev, "ini_filtercfg=0x%x\n", ddata->ini_filtercfg);
+
+	return 0;
+}
+
+
 static int max1720x_init_chip(struct max1720x_chip *chip)
 {
 	int ret;
@@ -3184,7 +3386,7 @@
 						chip->gauge_type);
 		if (ret == 0)
 			ret = max17x0x_cache_load(&chip->nRAM_por,
-							&chip->regmap_nvram);
+						  &chip->regmap_nvram);
 		if (ret < 0) {
 			dev_err(chip->dev, "POR: Failed to backup config\n");
 			return -EPROBE_DEFER;
@@ -3281,6 +3483,12 @@
 		/* POR for M5 is handled in the irq_thread() */
 	}
 
+	/* Fix capacity drift b/134500876 */
+	ret = max17201_init_fix_capacity(chip);
+	if (ret < 0)
+		dev_err(chip->dev, "Failed to initialize capacity fix (%d)\n",
+			ret);
+
 	max1720x_restore_battery_qh_capacity(chip);
 
 	return 0;
@@ -3639,7 +3847,8 @@
 {
 	struct max1720x_chip *chip = (struct max1720x_chip *)ptr;
 	static gbms_tag_t keys[] = {GBMS_TAG_SNUM, GBMS_TAG_BCNT,
-				    GBMS_TAG_RAVG, GBMS_TAG_RFCN};
+				    GBMS_TAG_RAVG, GBMS_TAG_RFCN,
+				    GBMS_TAG_CMPC, GBMS_TAG_DXAC};
 	const int count = ARRAY_SIZE(keys);
 
 
@@ -3699,6 +3908,13 @@
 			*(u16 *)buff = -1;
 		return 0;
 
+	case GBMS_TAG_DXAC:
+/*	MAX17201_DXACC_UPDATE_CNT = MAX1720X_NTALRTTH, */
+	case GBMS_TAG_CMPC:
+/*	MAX17201_COMP_UPDATE_CNT = MAX1720X_NVALRTTH, */
+		reg = NULL;
+		break;
+
 	default:
 		reg = NULL;
 		break;
@@ -3743,6 +3959,13 @@
 		return batt_res_registers(chip, false, SEL_RES_FILTER_COUNT,
 					  (u16 *)buff);
 
+	case GBMS_TAG_DXAC:
+/*	MAX17201_DXACC_UPDATE_CNT = MAX1720X_NTALRTTH, */
+	case GBMS_TAG_CMPC:
+/*	MAX17201_COMP_UPDATE_CNT = MAX1720X_NVALRTTH, */
+		reg = NULL;
+		break;
+
 	default:
 		reg = NULL;
 		break;
@@ -3868,6 +4091,7 @@
 
 	if (chip->gauge_type != -1) {
 
+		/* TODO: move to max1720x1 */
 		if (chip->regmap_nvram.regmap) {
 			ret = gbms_storage_register(&max17x0x_storage_dsc,
 						    "maxfg_sr", chip);
@@ -3875,6 +4099,7 @@
 				ret = 0;
 		}
 
+		/* these don't require nvm storage */
 		ret = gbms_storage_register(&max17x0x_prop_dsc, "maxfg", chip);
 		if (ret == -EBUSY)
 			ret = 0;