google_battery: Add voltage tier at 100p/fix AC deadline

This calculates time after full charge.

This also ensures that no algorithm disable can be set after
A/C has already been completed

Bug: 168980587
Bug: 161000427
Signed-off-by: Stephane Lee <[email protected]>
Change-Id: I2c126cb8419d92428d7fa8bad61fd5c14b766812
Signed-off-by: Ken Tsou <[email protected]>
(cherry picked from commit b6e2b16083675a57f5e0e69644a7462c3ce56119)
diff --git a/google_battery.c b/google_battery.c
index 27eef68..57fb89f 100644
--- a/google_battery.c
+++ b/google_battery.c
@@ -363,6 +363,7 @@
 #define SSOC_SPOOF 95
 #define SSOC_FULL 100
 #define UICURVE_BUF_SZ	(UICURVE_MAX * 15 + 1)
+#define SSOC_HIGH_SOC 90
 
 enum ssoc_uic_type {
 	SSOC_UIC_TYPE_DSG  = -1,
@@ -960,6 +961,14 @@
 	ce_data->health_stats.vtier_idx = GBMS_STATS_AC_TI_INVALID;
 	ce_data->health_stats.temp_idx = -1;
 	ce_data->health_stats.soc_in = -1;
+
+	ce_data->full_charge_stats.vtier_idx = GBMS_STATS_AC_TI_FULL_CHARGE;
+	ce_data->full_charge_stats.temp_idx = -1;
+	ce_data->full_charge_stats.soc_in = -1;
+
+	ce_data->high_soc_stats.vtier_idx = GBMS_STATS_AC_TI_HIGH_SOC;
+	ce_data->high_soc_stats.temp_idx = -1;
+	ce_data->high_soc_stats.soc_in = -1;
 }
 
 static void batt_chg_stats_start(struct batt_drv *batt_drv)
@@ -1043,45 +1052,13 @@
 	ce_data->last_soc = index;
 }
 
-/* call holding stats_lock */
-static void batt_chg_stats_update(struct batt_drv *batt_drv,
-				  int temp_idx, int tier_idx,
-				  int ibatt_ma, int temp, ktime_t elap)
+static void batt_chg_stats_update_tier(const struct batt_drv *const batt_drv,
+				       int temp_idx, int ibatt_ma, int temp,
+				       ktime_t elap, int cc,
+				       struct gbms_ce_tier_stats *tier)
 {
-	const uint16_t icl_settled = batt_drv->chg_state.f.icl;
 	const int msc_state = batt_drv->msc_state;
-	struct gbms_ce_tier_stats *tier;
-	int cc;
-
-	if (elap == 0)
-		return;
-
-	/* TODO: read at start of tier and update cc_total of previous */
-	cc = GPSY_GET_PROP(batt_drv->fg_psy, POWER_SUPPLY_PROP_CHARGE_COUNTER);
-	if (cc < 0) {
-		pr_debug("MSC_STAT cannot read cc=%d\n", cc);
-		return;
-	}
-	cc = cc / 1000;
-
-	/* works because msc_logic books the time BEFORE updating msc_state */
-	if (msc_state == MSC_HEALTH) {
-		tier = &batt_drv->ce_data.health_stats;
-
-		/* tier used for TTF during HC, check msc_logic_health() */
-	} else {
-		const qnum_t soc = ssoc_get_capacity_raw(&batt_drv->ssoc_state);
-
-		/* book to previous soc unless discharging */
-		if (msc_state != MSC_DSG) {
-
-			/* TODO: should I use ssoc instead? */
-			batt_chg_stats_soc_update(&batt_drv->ce_data, soc, elap,
-						tier_idx, cc);
-		}
-
-		tier = &batt_drv->ce_data.tier_stats[tier_idx];
-	}
+	const uint16_t icl_settled = batt_drv->chg_state.f.icl;
 
 	/* book to previous state */
 	batt_chg_stats_tier(tier, msc_state, elap);
@@ -1156,6 +1133,63 @@
 	tier->sample_count += 1;
 }
 
+/* call holding stats_lock */
+static void batt_chg_stats_update(struct batt_drv *batt_drv, int temp_idx,
+				  int tier_idx, int ibatt_ma, int temp,
+				  ktime_t elap)
+{
+	const int msc_state = batt_drv->msc_state;
+	struct gbms_ce_tier_stats *tier;
+	int cc;
+	int soc_real = ssoc_get_real(&batt_drv->ssoc_state);
+
+	if (elap == 0)
+		return;
+
+	/* TODO: read at start of tier and update cc_total of previous */
+	cc = GPSY_GET_PROP(batt_drv->fg_psy, POWER_SUPPLY_PROP_CHARGE_COUNTER);
+	if (cc < 0) {
+		pr_debug("MSC_STAT cannot read cc=%d\n", cc);
+		return;
+	}
+	cc = cc / 1000;
+
+	if (batt_drv->batt_full) {
+		/* Override regular charge tiers when fully charged */
+		tier = &batt_drv->ce_data.full_charge_stats;
+
+	} else if (msc_state == MSC_HEALTH) {
+		/*
+		 * works because msc_logic books the time BEFORE updating
+		 * msc_state
+		 */
+
+		/* tier used for TTF during HC, check msc_logic_health() */
+		tier = &batt_drv->ce_data.health_stats;
+
+	} else {
+		const qnum_t soc = ssoc_get_capacity_raw(&batt_drv->ssoc_state);
+
+		/* book to previous soc unless discharging */
+		if (msc_state != MSC_DSG) {
+			/* TODO: should I use ssoc instead? */
+			batt_chg_stats_soc_update(&batt_drv->ce_data, soc, elap,
+						  tier_idx, cc);
+		}
+
+		tier = &batt_drv->ce_data.tier_stats[tier_idx];
+	}
+
+	batt_chg_stats_update_tier(batt_drv, temp_idx, ibatt_ma, temp, elap, cc,
+				   tier);
+
+	/* Update this charge tier in parallel */
+	if (soc_real >= SSOC_HIGH_SOC) {
+		batt_chg_stats_update_tier(batt_drv, temp_idx, ibatt_ma, temp,
+					   elap, cc,
+					   &batt_drv->ce_data.high_soc_stats);
+	}
+}
 
 static int batt_chg_health_vti(const struct batt_chg_health *chg_health)
 {
@@ -1368,9 +1402,8 @@
 }
 
 /* End of charging: close stats, qualify event publish data */
-static void batt_chg_stats_pub(struct batt_drv *batt_drv,
-			       char *reason,
-			       bool force)
+static void batt_chg_stats_pub(struct batt_drv *batt_drv, char *reason,
+			       bool force, bool skip_uevent)
 {
 	bool publish;
 
@@ -1380,7 +1413,8 @@
 		ttf_stats_update(&batt_drv->ttf_stats,
 				 &batt_drv->ce_qual, false);
 
-		kobject_uevent(&batt_drv->device->kobj, KOBJ_CHANGE);
+		if (skip_uevent == false)
+			kobject_uevent(&batt_drv->device->kobj, KOBJ_CHANGE);
 	}
 
 	bat_log_chg_stats(batt_drv->ttf_stats.ttf_log, &batt_drv->ce_data);
@@ -1532,12 +1566,25 @@
 		len += batt_chg_tier_stats_cstr(&buff[len], size - len,
 						&ce_data->tier_stats[i],
 						verbose);
+
 		if (soc_next)
 			len += ttf_soc_cstr(&buff[len], size - len,
 					    &ce_data->soc_stats,
 					    soc_in, soc_next);
 	}
 
+	if (ce_data->full_charge_stats.soc_in != -1) {
+		len += batt_chg_tier_stats_cstr(&buff[len], size - len,
+						&ce_data->full_charge_stats,
+						verbose);
+	}
+
+	if (ce_data->high_soc_stats.soc_in != -1) {
+		len += batt_chg_tier_stats_cstr(&buff[len], size - len,
+						&ce_data->high_soc_stats,
+						verbose);
+	}
+
 	return len;
 }
 
@@ -1969,9 +2016,12 @@
 	} else if (deadline_s == 0) {
 		new_deadline = chg_health->rest_deadline != deadline_s;
 		/* ->rest_deadline will be reset to 0 on disconnect */
-		chg_health->rest_state = CHG_HEALTH_USER_DISABLED;
 
-	/* enabled from any previous state */
+		/* Don't disable A/C if already done */
+		if (chg_health->rest_state != CHG_HEALTH_DONE)
+			chg_health->rest_state = CHG_HEALTH_USER_DISABLED;
+
+		/* enabled from any previous state */
 	} else {
 		const ktime_t rest_deadline = get_boot_sec() + deadline_s;
 
@@ -2381,7 +2431,7 @@
 			goto msc_logic_exit;
 
 		/* here on: disconnect */
-		batt_chg_stats_pub(batt_drv, "disconnect", false);
+		batt_chg_stats_pub(batt_drv, "disconnect", false, false);
 		batt_res_state_set(&batt_drv->res_state, false);
 
 		/* change curve before changing the state */
@@ -2441,8 +2491,10 @@
 	} else if (batt_drv->batt_full) {
 		changed = batt_rl_enter(&batt_drv->ssoc_state,
 					BATT_RL_STATUS_RECHARGE);
+
+		/* We can skip the uevent because we have volt tiers >= 100 */
 		if (changed)
-			batt_chg_stats_pub(batt_drv, "100%", false);
+			batt_chg_stats_pub(batt_drv, "100%", false, true);
 	}
 
 	err = msc_logic(batt_drv);
@@ -3164,7 +3216,7 @@
 	switch (buf[0]) {
 	case 'p': /* publish data to qual */
 	case 'P': /* force publish data to qual */
-		batt_chg_stats_pub(batt_drv, "debug cmd", buf[0] == 'P');
+		batt_chg_stats_pub(batt_drv, "debug cmd", buf[0] == 'P', false);
 		break;
 	default:
 		count = -EINVAL;