power: pm8921-bms: Fine tune SOC reporting during suspend/resume

Report SOC based on the time for which the system was suspended.

Remember -
1. If we recalculated the SOC in resume
2. The last_soc before going into suspend

Two cases arise - if we had a short suspend or if the suspend
was significant to case a drop in SOC. For the former do not
report a change in SOC and for the latter, report the
newly calculated_soc. In either of the cases, soc should not
increase because we cannot suspend while charging.

There is a possibility that the report_soc may be called
before BMS resume (update_power_supply), causing the driver
to report the old SOC. Handle this case by setting state
variables before recalculating the soc in resume.

CRs-Fixed: 482611
Change-Id: Ic21a24a1126800011f5e7e4531c1953436812811
Signed-off-by: Anirudh Ghayal <aghayal@codeaurora.org>
diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c
index b1958aa..0032ea8 100644
--- a/drivers/power/pm8921-bms.c
+++ b/drivers/power/pm8921-bms.c
@@ -178,7 +178,8 @@
 	int			low_voltage_detect;
 	int			vbatt_cutoff_retries;
 	bool			first_report_after_suspend;
-	int			calc_soc_at_suspend;
+	bool			soc_updated_on_resume;
+	int			last_soc_at_suspend;
 };
 
 /*
@@ -2389,10 +2390,11 @@
 			rbatt, fcc_uah, unusable_charge_uah, cc_uah);
 
 	pr_debug("calculated SOC = %d\n", new_calculated_soc);
-	if (new_calculated_soc != calculated_soc)
+	if (new_calculated_soc != calculated_soc) {
+		calculated_soc = new_calculated_soc;
 		update_power_supply(chip);
+	}
 
-	calculated_soc = new_calculated_soc;
 	firsttime = 0;
 	get_current_time(&chip->last_recalc_time);
 
@@ -2499,30 +2501,28 @@
 	if (last_soc != -EINVAL && last_soc < soc && soc != 100)
 		soc = scale_soc_while_chg(chip, delta_time_us, soc, last_soc);
 
-	/* restrict soc to 1% change unless reporting 1st time after suspend */
-	if (chip->first_report_after_suspend == true) {
-		chip->first_report_after_suspend = false;
-		if (last_soc != -EINVAL) {
-			if (last_soc < soc)
+	if (last_soc != -EINVAL) {
+		if (chip->first_report_after_suspend) {
+			chip->first_report_after_suspend = false;
+			if (chip->soc_updated_on_resume) {
+				/*  coming here after a long suspend */
+				chip->soc_updated_on_resume = false;
+				if (last_soc < soc)
+					/* if soc has falsely increased during
+					 * suspend, set the soc_at_suspend
+					 */
+					soc = chip->last_soc_at_suspend;
+			} else {
 				/*
-				 * can't suspend while charging
-				 * don't report the increased the soc
+				 * suspended for a short time
+				 * report the last_soc before suspend
 				 */
-				soc = last_soc;
-			else if (last_soc > soc) {
-				if (chip->calc_soc_at_suspend > soc)
-					soc = last_soc -
-					(chip->calc_soc_at_suspend - soc);
-				else
-					soc = last_soc;
+				soc = chip->last_soc_at_suspend;
 			}
-		}
-	} else {
-		if (last_soc != -EINVAL) {
-			if (soc < last_soc && soc != 0)
-				soc = last_soc - 1;
-			if (soc > last_soc && soc != 100)
-				soc = last_soc + 1;
+		} else if (soc < last_soc && soc != 0) {
+			soc = last_soc - 1;
+		} else if (soc > last_soc && soc != 100) {
+			soc = last_soc + 1;
 		}
 	}
 
@@ -3584,8 +3584,7 @@
 
 	cancel_delayed_work_sync(&chip->calculate_soc_delayed_work);
 
-	chip->first_report_after_suspend = true;
-	chip->calc_soc_at_suspend = calculated_soc;
+	chip->last_soc_at_suspend = last_soc;
 
 	return 0;
 }
@@ -3602,6 +3601,7 @@
 		pr_err("Could not read current time: %d\n", rc);
 		return 0;
 	}
+
 	if (tm_now_sec > chip->last_recalc_time) {
 		time_since_last_recalc = tm_now_sec -
 				chip->last_recalc_time;
@@ -3611,8 +3611,11 @@
 					chip->soc_calc_period) {
 			chip->last_recalc_time = tm_now_sec;
 			recalculate_soc(chip);
+			chip->soc_updated_on_resume = true;
 		}
 	}
+	chip->first_report_after_suspend = true;
+	update_power_supply(chip);
 	schedule_delayed_work(&chip->calculate_soc_delayed_work,
 				msecs_to_jiffies(chip->soc_calc_period));