max_m5: add criteria to rerun loading custom parameters

Test for a race condition between AP SW and FG firmware during the model loading process. Add register value checks and rerun on failure due to the simultaneous update of the value by firmware.

Bug: 240115405
Change-Id: I39da136336be92cc6afa1d3f650383121b0a52ac
Signed-off-by: Jenny Ho <[email protected]>
diff --git a/max_m5.c b/max_m5.c
index 873f8aa..372b7ef 100644
--- a/max_m5.c
+++ b/max_m5.c
@@ -457,41 +457,12 @@
 	return cap_lsb;
 }
 
-/* 0 is ok */
-int max_m5_load_gauge_model(struct max_m5_data *m5_data)
+static int max_m5_update_gauge_custom_parameters(struct max_m5_data *m5_data)
 {
 	struct max17x0x_regmap *regmap = m5_data->regmap;
 	int ret, retries;
 	u16 data;
 
-	if (!regmap)
-		return -EIO;
-
-	if (!m5_data || !m5_data->custom_model || !m5_data->custom_model_size)
-		return -ENODATA;
-
-	/* check FStat.DNR to wait it clear for data ready */
-	for (retries = 20; retries > 0; retries--) {
-		ret = REGMAP_READ(regmap, MAX_M5_FSTAT, &data);
-		if (ret == 0 && !(data & MAX_M5_FSTAT_DNR))
-			break;
-		msleep(50);
-	}
-	dev_info(m5_data->dev, "retries:%d, FSTAT:%#x\n", retries, data);
-
-	/* loading in progress, this is not good (tm) */
-	ret = REGMAP_READ(regmap, MAX_M5_CONFIG2, &data);
-	if (ret == 0 && (data & MAX_M5_CONFIG2_LDMDL)) {
-		dev_err(m5_data->dev, "load model in progress (%x)\n", data);
-		return -EINVAL;
-	}
-
-	ret = max_m5_update_custom_model(m5_data);
-	if (ret < 0) {
-		dev_err(m5_data->dev, "cannot update custom model (%d)\n", ret);
-		return ret;
-	}
-
 	/* write parameters (which include state) */
 	ret = max_m5_update_custom_parameters(m5_data);
 	if (ret < 0) {
@@ -568,6 +539,92 @@
 	return -ETIMEDOUT;
 }
 
+/* protected from mutex_lock(&chip->model_lock) */
+static int max_m5_check_model_parameters(struct max_m5_data *m5_data)
+{
+	struct max_m5_custom_parameters *cp = &m5_data->parameters;
+	struct max17x0x_regmap *regmap = m5_data->regmap;
+	int ret, cap_delta_threshold, cap_delta_real;
+	u16 fullcaprep, fullcapnom;
+
+	/* b/240115405#comment44 */
+	cap_delta_threshold = abs(cp->fullcapnom - cp->fullcaprep) + cp->designcap / 100;
+
+	ret = REGMAP_READ(regmap, MAX_M5_FULLCAPREP, &fullcaprep);
+	if (ret < 0)
+		return ret;
+
+	ret = REGMAP_READ(regmap, MAX_M5_FULLCAPNOM, &fullcapnom);
+	if (ret < 0)
+		return ret;
+
+	cap_delta_real = abs(fullcapnom - fullcaprep);
+
+	dev_info(m5_data->dev, "write: nom:%#x, rep:%#x, design:%#x (threshold=%d),"
+		 " read: nom:%#x, rep:%#x (delta=%d), retry:%d\n",
+		 cp->fullcapnom, cp->fullcaprep, cp->designcap, cap_delta_threshold,
+		 fullcapnom, fullcaprep, cap_delta_real, m5_data->load_retry);
+
+	if (cap_delta_real > cap_delta_threshold && m5_data->load_retry < MAX_M5_RETRY_TIMES)
+		return -ERANGE;
+
+	return 0;
+}
+
+/* 0 is ok , protected from mutex_lock(&chip->model_lock) in max7102x_battery.c */
+int max_m5_load_gauge_model(struct max_m5_data *m5_data)
+{
+	struct max17x0x_regmap *regmap = m5_data->regmap;
+	int ret, retries;
+	u16 data;
+
+	if (!regmap)
+		return -EIO;
+
+	if (!m5_data || !m5_data->custom_model || !m5_data->custom_model_size)
+		return -ENODATA;
+
+	/* check FStat.DNR to wait it clear for data ready */
+	for (retries = 20; retries > 0; retries--) {
+		ret = REGMAP_READ(regmap, MAX_M5_FSTAT, &data);
+		if (ret == 0 && !(data & MAX_M5_FSTAT_DNR))
+			break;
+		msleep(50);
+	}
+	dev_info(m5_data->dev, "retries:%d, FSTAT:%#x\n", retries, data);
+
+	/* loading in progress, this is not good (tm) */
+	ret = REGMAP_READ(regmap, MAX_M5_CONFIG2, &data);
+	if (ret == 0 && (data & MAX_M5_CONFIG2_LDMDL)) {
+		dev_err(m5_data->dev, "load model in progress (%x)\n", data);
+		return -EINVAL;
+	}
+
+	ret = max_m5_update_custom_model(m5_data);
+	if (ret < 0) {
+		dev_err(m5_data->dev, "cannot update custom model (%d)\n", ret);
+		return ret;
+	}
+
+	do {
+		msleep(500);
+
+		ret = max_m5_update_gauge_custom_parameters(m5_data);
+		if (ret < 0)
+			return ret;
+
+		ret = max_m5_check_model_parameters(m5_data);
+		if (ret < 0) {
+			m5_data->load_retry++;
+		} else {
+			m5_data->load_retry = 0;
+			break;
+		}
+	} while (m5_data->load_retry < MAX_M5_RETRY_TIMES);
+
+	return ret;
+}
+
 /* algo version is ignored here, check code in max1720x_outliers */
 int max_m5_fixup_outliers(struct max1720x_drift_data *ddata,
 			  struct max_m5_data *m5_data)
diff --git a/max_m5.h b/max_m5.h
index 29bcaa8..62fc4b5 100644
--- a/max_m5.h
+++ b/max_m5.h
@@ -50,6 +50,7 @@
 
 #define MAX_M5_RECAL_MAX_ROUNDS	3
 
+#define MAX_M5_RETRY_TIMES 	3
 
 /** ------------------------------------------------------------------------ */
 
@@ -130,6 +131,7 @@
 	u16 *custom_model;
 	u32 model_version;
 	bool force_reset_model_data;
+	int load_retry;
 
 	/* to/from GMSR */
 	struct model_state_save model_save;