power: pm8921-bms: increase/decrease UUC 1% at a time

We see huge jumps in Unusable Charge (UUC) as temperature, average
load or state of charge (SOC) changes, which results in huge jumps
in the reported capacity.

Increase/Decrease UUC slowly (limit 1%) each time SOC is requested.
This results in linearity improvements.

Change-Id: I82022aa499a53c5ea42e7079e96d3c94dfc0be01
Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c
index 5ddca3e..b86a026 100644
--- a/drivers/power/pm8921-bms.c
+++ b/drivers/power/pm8921-bms.c
@@ -142,6 +142,7 @@
 	int			ignore_shutdown_soc;
 	int			prev_iavg_ua;
 	int			prev_uuc_iavg_ma;
+	int			prev_pc_unusable;
 	int			adjust_soc_low_threshold;
 };
 
@@ -1159,19 +1160,60 @@
 
 static int calculate_uuc_uah_at_given_current(struct pm8921_bms_chip *chip,
 				 int batt_temp, int chargecycles,
-				int rbatt, int fcc_uah, int i_ma)
+				int rbatt, int fcc_uah, int i_ma,
+				int *ret_pc_unusable)
 {
 	int unusable_uv, pc_unusable, uuc;
 
-	/* calculate unusable charge with itest */
 	unusable_uv = (rbatt * i_ma) + (chip->v_cutoff * 1000);
 	pc_unusable = calculate_pc(chip, unusable_uv, batt_temp, chargecycles);
 	uuc = (fcc_uah * pc_unusable) / 100;
 	pr_debug("For i_ma = %d, unusable_uv = %d unusable_pc = %d uuc = %d\n",
 					i_ma, unusable_uv, pc_unusable, uuc);
+	*ret_pc_unusable = pc_unusable;
 	return uuc;
 }
 
+static int adjust_uuc(struct pm8921_bms_chip *chip, int fcc_uah,
+			int new_pc_unusable,
+			int new_uuc,
+			int batt_temp,
+			int rbatt,
+			int *iavg_ma)
+{
+	int new_unusable_mv;
+	int batt_temp_degc = batt_temp / 10;
+
+	if (chip->prev_pc_unusable == -EINVAL
+		|| abs(chip->prev_pc_unusable - new_pc_unusable) <= 1) {
+		chip->prev_pc_unusable = new_pc_unusable;
+		return new_uuc;
+	}
+
+	/* the uuc is trying to change more than 1% restrict it */
+	if (new_pc_unusable > chip->prev_pc_unusable)
+		chip->prev_pc_unusable++;
+	else
+		chip->prev_pc_unusable--;
+
+	new_uuc = (fcc_uah * chip->prev_pc_unusable) / 100;
+
+	/* also find update the iavg_ma accordingly */
+	new_unusable_mv = interpolate_ocv(chip, batt_temp_degc,
+						chip->prev_pc_unusable);
+	if (new_unusable_mv < chip->v_cutoff)
+		new_unusable_mv = chip->v_cutoff;
+
+	*iavg_ma = (new_unusable_mv - chip->v_cutoff) * 1000 / rbatt;
+	if (*iavg_ma == 0)
+		*iavg_ma = 1;
+	pr_debug("Restricting UUC to %d (%d%%) unusable_mv = %d iavg_ma = %d\n",
+					new_uuc, chip->prev_pc_unusable,
+					new_unusable_mv, *iavg_ma);
+
+	return new_uuc;
+}
+
 static void calculate_iavg_ua(struct pm8921_bms_chip *chip, int cc_uah,
 				int *iavg_ua, int *delta_time_s)
 {
@@ -1254,6 +1296,7 @@
 	static int iavg_index;
 	static int iavg_num_samples;
 	static int firsttime = 1;
+	int pc_unusable;
 
 	/*
 	 * if we are called first time fill all the
@@ -1294,8 +1337,15 @@
 
 	uuc_uah_iavg = calculate_uuc_uah_at_given_current(chip,
 					batt_temp, chargecycles,
-					rbatt, fcc_uah, iavg_ma);
+					rbatt, fcc_uah, iavg_ma,
+					&pc_unusable);
 	pr_debug("iavg = %d uuc_iavg = %d\n", iavg_ma, uuc_uah_iavg);
+
+	/* restrict the uuc such that it can increase only by one percent */
+	uuc_uah_iavg = adjust_uuc(chip, fcc_uah, pc_unusable, uuc_uah_iavg,
+					batt_temp, rbatt, &iavg_ma);
+
+	/* find out what the avg current should be for this uuc */
 	chip->prev_uuc_iavg_ma = iavg_ma;
 
 	firsttime = 0;
@@ -1693,6 +1743,7 @@
 	int soc_rbatt;
 	int iavg_ma;
 	int num_tries = 0;
+	int pc_unusable;
 
 	iavg_ma = chip->prev_uuc_iavg_ma;
 
@@ -1711,7 +1762,8 @@
 		rbatt = new_rbatt;
 		uuc_uah = calculate_uuc_uah_at_given_current(chip,
 					batt_temp, chargecycles,
-					new_rbatt, fcc_uah, iavg_ma);
+					new_rbatt, fcc_uah, iavg_ma,
+					&pc_unusable);
 
 		pr_debug("rbatt not settled uuc = %d for rbatt = %d iavg_ma = %d num_tries = %d\n",
 					uuc_uah, rbatt, iavg_ma, num_tries);
@@ -3060,6 +3112,8 @@
 	if (chip->adjust_soc_low_threshold >= 45)
 		chip->adjust_soc_low_threshold = 45;
 
+	chip->prev_pc_unusable = -EINVAL;
+
 	chip->ignore_shutdown_soc = pdata->ignore_shutdown_soc;
 	rc = set_battery_data(chip);
 	if (rc) {