google_battery: report AdaptiveCharging state to userspace

Bug: 150385595
Test: enabled/disabled AC, cecked state
Signed-off-by: AleX Pelosi <[email protected]>
Change-Id: Ie7079dad248f4a76d0d366ff34d09657ac4ce8e1
Signed-off-by: Ken Tsou <[email protected]>
(cherry picked from commit 6f26002c535db5c7886b2d6780b3578d21faa84f)
diff --git a/google_battery.c b/google_battery.c
index f89380b..c9daae8 100644
--- a/google_battery.c
+++ b/google_battery.c
@@ -173,8 +173,10 @@
 /* health/rest alternate charging policy */
 enum chg_health_state {
 	CHG_HEALTH_DISABLED = -1,
-	CHG_HEALTH_INACTIVE,
+	CHG_HEALTH_INACTIVE = 0,
+	CHG_HEALTH_ENABLED,
 	CHG_HEALTH_ACTIVE,
+	CHG_HEALTH_DONE,
 };
 
 struct batt_chg_health {
@@ -1696,53 +1698,86 @@
 
 		vbatt = GPSY_GET_PROP(batt_drv->fg_psy,
 				      POWER_SUPPLY_PROP_VOLTAGE_NOW);
-		/* negative on error */
+		/* vbatt is negative on error */
 		vrest = (vbatt >= batt_drv->chg_health.rest_voltage);
 	}
 
-	return (srest || vrest) ? CHG_HEALTH_ACTIVE : CHG_HEALTH_INACTIVE;
+	return (srest || vrest) ? CHG_HEALTH_ACTIVE : CHG_HEALTH_ENABLED;
 }
 
-/* health based charging trade charging speed for battery cycle life.
- */
-static int msc_logic_health(struct batt_chg_health *rest,
-			    const struct batt_drv *batt_drv)
+/* provide absolute deadline */
+static bool batt_health_set_chg_deadline(struct batt_chg_health *chg_health,
+					 ktime_t deadline)
 {
-	enum chg_health_state rest_state = rest->rest_state;
+	const bool new_deadline = chg_health->rest_deadline != deadline;
+	enum chg_health_state rest_state = chg_health->rest_state;
+
+	if (chg_health->rest_deadline == deadline)
+		return false;
+
+	if (deadline < 0) {
+		chg_health->rest_state = CHG_HEALTH_DISABLED;
+		chg_health->rest_deadline = -1;
+	} else if (deadline == 0) {
+		chg_health->rest_state = CHG_HEALTH_INACTIVE;
+		chg_health->rest_deadline = 0;
+	} else {
+		chg_health->rest_state = CHG_HEALTH_ENABLED;
+		chg_health->rest_deadline = deadline;
+	}
+
+	return new_deadline || rest_state != chg_health->rest_state;
+}
+
+/* health based charging trade charging speed for battery cycle life. */
+static bool msc_logic_health(struct batt_chg_health *rest,
+			     const struct batt_drv *batt_drv)
+{
 	const ktime_t deadline = rest->rest_deadline;
 	const ktime_t now = get_boot_sec();
+	enum chg_health_state rest_state = rest->rest_state;
 	int ttf_ret = 0, fv_uv = -1, cc_max = -1;
+	bool changed = false;
 	ktime_t ttf = -1;
 
 	/* Disabled is a one way trip until state is reset */
 	if (rest_state == CHG_HEALTH_DISABLED || !deadline)
 		goto done_exit;
 
-	rest_state = msc_health_active(rest, batt_drv);
-	if (rest_state == CHG_HEALTH_INACTIVE)
-		goto done_exit;
-
-	/* this happens when the device is discharging. It should not happen
-	 * if rest_state==CHG_HEALTH_ACTIVE since we have a very low charge
-	 * rate BUT it is possible if we have large sysloads with an
-	 * underpowered adapter. Current strategy leave everything "as is"
-	 * assuming that the large load is transient.
+	/*
+	 * ttf_ret < 0 when the device is discharging. This can happen with
+	 * a large sysload on a underpowered adapter. Current strategy leave
+	 * everything as is (hoping) that the load is temporary.
+	 * TODO: send out power supply events?
 	 */
 	ttf_ret = batt_ttf_estimate(&ttf, batt_drv);
 	if (ttf_ret < 0)
-		return ttf_ret;
+		return false;
 
-	if ((now + ttf) > deadline) {
-		/* Disable health based charging for this session if the
-		 * deadline cannot be met with the current rate. Health based
-		 * charging is re-evaluated on next charge cycle or when the
-		 * deadline changes.
-		 * TODO: consider adding a margin
+	if (ttf == 0)  {
+		rest_state = CHG_HEALTH_DONE;
+		rest->rest_deadline = 0;
+	} else if ((now + ttf) > deadline) {
+		/*
+		 * Disable health based charging for this session if the
+		 * deadline cannot be met with the current rate. Set a new
+		 * deadline to re-enable for this session.
+		 * TODO: consider adding a margin or debounce it.
 		 */
 		rest_state = CHG_HEALTH_DISABLED;
+		rest->rest_deadline = -1;
 	} else {
 		const struct gbms_chg_profile *profile = &batt_drv->chg_profile;
 
+		/*
+		 * only needs to be done if rest_state != CHG_HEALTH_ACTIVE
+		 * NOTE: could also DISABLE when transitioning from
+		 * ACTIVE to ENABLED
+		 */
+		rest_state = msc_health_active(rest, batt_drv);
+		if (rest_state != CHG_HEALTH_ACTIVE)
+			goto done_exit;
+
 		/* battery_capacity in mAh, rest_rate in deciC, cc_max in */
 		if (rest->rest_rate == 0) {
 			cc_max = 0;
@@ -1751,28 +1786,30 @@
 			cc_max *= 10;
 		}
 
-		/* default FV_UV to the last charge tier, this might need
-		 * to be adjusted for the actual charge tiers that have nonzero
-		 * current
+		/*
+		 * default FV_UV to the last charge tier.
+		 * NOTE this might need to be adjusted for the actual charge
+		 * tiers that have nonzero charging current
 		 */
 		fv_uv = profile->volt_limits[profile->volt_nb_limits - 1];
-		rest_state = CHG_HEALTH_ACTIVE;
 
 		/* TODO: make sure that we wakeup when we are close to ttf */
 	}
 
 done_exit:
-	pr_info("MSC_HEALTH: now=%lld deadline=%lld ttf=%lld state=%d->%d fv_uv=%d, cc_max=%d\n",
-		now, rest->rest_deadline, (ttf_ret < 0) ? ttf_ret : ttf,
-		rest->rest_state, rest_state, fv_uv, cc_max);
+	/* send a power supply event when rest_state changes */
+	changed = rest->rest_state != rest_state;
+	if (changed)
+		pr_info("MSC_HEALTH: now=%lld deadline=%lld ttf=%lld state=%d->%d fv_uv=%d, cc_max=%d\n",
+			now, rest->rest_deadline, (ttf_ret < 0) ? ttf_ret : ttf,
+			rest->rest_state, rest_state, fv_uv, cc_max);
 
-	/* msc_logic_* will vote on cc_max and fv_uv. The actual charge current
-	 * will be the minimum between the vote from MSC_LOGIC and health.
-	 */
+	/* msc_logic_* will vote on cc_max and fv_uv. */
 	rest->rest_state = rest_state;
 	rest->rest_cc_max = cc_max;
 	rest->rest_fv_uv = fv_uv;
-	return 0;
+
+	return changed;
 }
 
 static int msc_pm_hold(int msc_state)
@@ -2126,7 +2163,7 @@
 	 * TODO: this might need to behave in a different way when health
 	 * based charging is active
 	 */
-	(void)msc_logic_health(&batt_drv->chg_health, batt_drv);
+	changed |= msc_logic_health(&batt_drv->chg_health, batt_drv);
 
 msc_logic_done:
 	/* set ->cc_max = 0 on RL and SW_JEITA, no vote on interval in RL_DSG */
@@ -2343,6 +2380,8 @@
 
 /* ------------------------------------------------------------------------- */
 
+#ifdef CONFIG_DEBUG_FS
+
 #define BATTERY_DEBUG_ATTRIBUTE(name, fn_read, fn_write) \
 static const struct file_operations name = {	\
 	.open	= simple_open,			\
@@ -2530,117 +2569,10 @@
 DEFINE_SIMPLE_ATTRIBUTE(debug_ssoc_rls_fops,
 				debug_get_ssoc_rls, debug_set_ssoc_rls, "%llu\n");
 
-static int debug_force_psy_update(void *data, u64 val)
-{
-	struct batt_drv *batt_drv = (struct batt_drv *)data;
 
-	if (!batt_drv->psy)
-		return -EINVAL;
-
-	power_supply_changed(batt_drv->psy);
-	return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(debug_force_psy_update_fops,
-				NULL, debug_force_psy_update, "%llu\n");
-
-
-static int debug_chg_health_rest_rate_read(void *data, u64 *val)
-{
-	struct batt_drv *batt_drv = (struct batt_drv *)data;
-
-	if (!batt_drv->psy)
-		return -EINVAL;
-
-	*val = batt_drv->chg_health.rest_rate;
-	return 0;
-}
-
-static int debug_chg_health_rest_rate_write(void *data, u64 val)
-{
-	struct batt_drv *batt_drv = (struct batt_drv *)data;
-
-	if (!batt_drv->psy)
-		return -EINVAL;
-
-	batt_drv->chg_health.rest_rate = val;
-	return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(debug_chg_health_rest_rate_fops,
-			debug_chg_health_rest_rate_read,
-			debug_chg_health_rest_rate_write, "%llu\n");
-
-static int debug_chg_health_thr_soc_read(void *data, u64 *val)
-{
-	struct batt_drv *batt_drv = (struct batt_drv *)data;
-
-	if (!batt_drv->psy)
-		return -EINVAL;
-
-	*val = batt_drv->chg_health.rest_soc;
-	return 0;
-}
-
-static int debug_chg_health_thr_soc_write(void *data, u64 val)
-{
-	struct batt_drv *batt_drv = (struct batt_drv *)data;
-
-	if (!batt_drv->psy)
-		return -EINVAL;
-
-	batt_drv->chg_health.rest_soc = val;
-	return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(debug_chg_health_thr_soc_fops,
-			debug_chg_health_thr_soc_read,
-			debug_chg_health_thr_soc_write, "%llu\n");
-
-static int debug_chg_health_thr_volt_read(void *data, u64 *val)
-{
-	struct batt_drv *batt_drv = (struct batt_drv *)data;
-
-	if (!batt_drv->psy)
-		return -EINVAL;
-
-	*val = batt_drv->chg_health.rest_voltage;
-	return 0;
-}
-
-static int debug_chg_health_thr_volt_write(void *data, u64 val)
-{
-	struct batt_drv *batt_drv = (struct batt_drv *)data;
-
-	if (!batt_drv->psy)
-		return -EINVAL;
-
-	batt_drv->chg_health.rest_voltage = val;
-	return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(debug_chg_health_thr_volt_fops,
-			debug_chg_health_thr_volt_read,
-			debug_chg_health_thr_volt_write, "%llu\n");
-
-
-static int debug_chg_health_resting(void *data, u64 *val)
-{
-	struct batt_drv *batt_drv = (struct batt_drv *)data;
-
-	if (!batt_drv->psy)
-		return -EINVAL;
-
-	*val = batt_drv->chg_health.rest_state;
-	return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(debug_chg_health_resting_fops,
-			debug_chg_health_resting,
-			NULL, "%llu\n");
-
-static ssize_t debug_get_ssoc_uicurve(struct file *filp, char __user *buf,
-				      size_t count, loff_t *ppos)
+static ssize_t debug_get_ssoc_uicurve(struct file *filp,
+					   char __user *buf,
+					   size_t count, loff_t *ppos)
 {
 	struct batt_drv *batt_drv = (struct batt_drv *)filp->private_data;
 	char tmp[UICURVE_BUF_SZ] = { 0 };
@@ -2653,8 +2585,8 @@
 }
 
 static ssize_t debug_set_ssoc_uicurve(struct file *filp,
-				      const char __user *user_buf,
-				      size_t count, loff_t *ppos)
+					 const char __user *user_buf,
+					 size_t count, loff_t *ppos)
 {
 	struct batt_drv *batt_drv = (struct batt_drv *)filp->private_data;
 	int ret, curve_type;
@@ -2684,6 +2616,135 @@
 					debug_get_ssoc_uicurve,
 					debug_set_ssoc_uicurve);
 
+static int debug_force_psy_update(void *data, u64 val)
+{
+	struct batt_drv *batt_drv = (struct batt_drv *)data;
+
+	if (!batt_drv->psy)
+		return -EINVAL;
+
+	power_supply_changed(batt_drv->psy);
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debug_force_psy_update_fops,
+				NULL, debug_force_psy_update, "%llu\n");
+
+/* Adaptive Charging */
+static int debug_chg_health_rest_rate_read(void *data, u64 *val)
+{
+	struct batt_drv *batt_drv = (struct batt_drv *)data;
+
+	if (!batt_drv->psy)
+		return -EINVAL;
+
+	*val = batt_drv->chg_health.rest_rate;
+	return 0;
+}
+
+/* Adaptive Charging */
+static int debug_chg_health_rest_rate_write(void *data, u64 val)
+{
+	struct batt_drv *batt_drv = (struct batt_drv *)data;
+
+	if (!batt_drv->psy)
+		return -EINVAL;
+
+	batt_drv->chg_health.rest_rate = val;
+	return 0;
+}
+
+/* Adaptive Charging */
+DEFINE_SIMPLE_ATTRIBUTE(debug_chg_health_rest_rate_fops,
+			debug_chg_health_rest_rate_read,
+			debug_chg_health_rest_rate_write, "%llu\n");
+
+/* Adaptive Charging */
+static int debug_chg_health_thr_soc_read(void *data, u64 *val)
+{
+	struct batt_drv *batt_drv = (struct batt_drv *)data;
+
+	if (!batt_drv->psy)
+		return -EINVAL;
+
+	*val = batt_drv->chg_health.rest_soc;
+	return 0;
+}
+
+/* Adaptive Charging */
+static int debug_chg_health_thr_soc_write(void *data, u64 val)
+{
+	struct batt_drv *batt_drv = (struct batt_drv *)data;
+
+	if (!batt_drv->psy)
+		return -EINVAL;
+
+	batt_drv->chg_health.rest_soc = val;
+	return 0;
+}
+
+/* Adaptive Charging */
+DEFINE_SIMPLE_ATTRIBUTE(debug_chg_health_thr_soc_fops,
+			debug_chg_health_thr_soc_read,
+			debug_chg_health_thr_soc_write, "%llu\n");
+
+/* Adaptive Charging */
+static int debug_chg_health_thr_volt_read(void *data, u64 *val)
+{
+	struct batt_drv *batt_drv = (struct batt_drv *)data;
+
+	if (!batt_drv->psy)
+		return -EINVAL;
+
+	*val = batt_drv->chg_health.rest_voltage;
+	return 0;
+}
+
+/* Adaptive Charging */
+static int debug_chg_health_thr_volt_write(void *data, u64 val)
+{
+	struct batt_drv *batt_drv = (struct batt_drv *)data;
+
+	if (!batt_drv->psy)
+		return -EINVAL;
+
+	batt_drv->chg_health.rest_voltage = val;
+	return 0;
+}
+
+/* Adaptive Charging */
+DEFINE_SIMPLE_ATTRIBUTE(debug_chg_health_thr_volt_fops,
+			debug_chg_health_thr_volt_read,
+			debug_chg_health_thr_volt_write, "%llu\n");
+
+/* Adaptive Charging */
+static int debug_chg_health_set_stage(void *data, u64 val)
+{
+	struct batt_drv *batt_drv = (struct batt_drv *)data;
+
+	if (!batt_drv->psy)
+		return -EINVAL;
+
+	switch (val) {
+	case CHG_HEALTH_DISABLED:
+	case CHG_HEALTH_INACTIVE:
+	case CHG_HEALTH_ENABLED:
+	case CHG_HEALTH_ACTIVE:
+	case CHG_HEALTH_DONE:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	batt_drv->chg_health.rest_state = val;
+	return 0;
+}
+
+/* Adaptive Charging */
+DEFINE_SIMPLE_ATTRIBUTE(debug_chg_health_stage_fops, NULL,
+			debug_chg_health_set_stage, "%llu\n");
+#endif
+
 /* ------------------------------------------------------------------------- */
 
 static ssize_t debug_get_fake_temp(struct file *filp,
@@ -2988,8 +3049,7 @@
 	return len;
 }
 
-static const DEVICE_ATTR(ttf_details, 0444, batt_show_ttf_details,
-					    NULL);
+static const DEVICE_ATTR(ttf_details, 0444, batt_show_ttf_details, NULL);
 
 /* house stats */
 static ssize_t batt_show_ttf_stats(struct device *dev,
@@ -3058,42 +3118,60 @@
 
 /* ------------------------------------------------------------------------- */
 
+static ssize_t chg_health_show_stage(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct power_supply *psy = container_of(dev, struct power_supply, dev);
+	struct batt_drv *batt_drv = (struct batt_drv *)
+					power_supply_get_drvdata(psy);
+	const char *s = "Inactive";
+
+	mutex_lock(&batt_drv->chg_lock);
+	switch (batt_drv->chg_health.rest_state) {
+	case CHG_HEALTH_DISABLED:
+		s = "Disabled";
+		break;
+	case CHG_HEALTH_ENABLED:
+		s = "Enabled";
+		break;
+	case CHG_HEALTH_ACTIVE:
+		s = "Active";
+		break;
+	case CHG_HEALTH_DONE:
+		s = "Active";
+		break;
+	default:
+		break;
+	}
+	mutex_unlock(&batt_drv->chg_lock);
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n", s);
+}
+
+static const DEVICE_ATTR(charge_stage, 0444, chg_health_show_stage, NULL);
+
+
 static ssize_t batt_show_chg_deadline(struct device *dev,
 				      struct device_attribute *attr, char *buf)
 {
 	struct power_supply *psy = container_of(dev, struct power_supply, dev);
 	struct batt_drv *batt_drv =(struct batt_drv *)
 					power_supply_get_drvdata(psy);
-	long long deadline = 0;
 	const ktime_t now = get_boot_sec();
+	long long deadline = 0;
 
 	/* API works in seconds */
 	mutex_lock(&batt_drv->chg_lock);
-	if (batt_drv->chg_health.rest_deadline == 0)
-		deadline = 0;
-	else
-		deadline = batt_drv->chg_health.rest_deadline - now;
+
+	deadline = batt_drv->chg_health.rest_deadline;
+	if (batt_drv->chg_health.rest_deadline > 0)
+		deadline -= now;
+
 	mutex_unlock(&batt_drv->chg_lock);
 
 	return scnprintf(buf, PAGE_SIZE, "%ld\n", (unsigned long)deadline);
 }
 
-/* absolute deadline */
-static void batt_health_set_chg_deadline(struct batt_chg_health *chg_health,
-					 int deadline)
-{
-	if (chg_health->rest_deadline == deadline)
-		return;
-
-	if (deadline <= 0)
-		chg_health->rest_deadline = 0;
-	else
-		chg_health->rest_deadline = deadline;
-
-	/* changes to deadline reset the health logic */
-	chg_health->rest_state = CHG_HEALTH_INACTIVE;
-}
-
 /* userspace restore the TTF data with this */
 static ssize_t batt_set_chg_deadline(struct device *dev,
 				     struct device_attribute *attr,
@@ -3104,15 +3182,24 @@
 					power_supply_get_drvdata(psy);
 	const ktime_t now = get_boot_sec();
 	int deadline_min;
+	bool changed;
 
 	deadline_min = simple_strtoull(buf, NULL, 10);
 
 	/* API works in seconds */
 	mutex_lock(&batt_drv->chg_lock);
-	batt_health_set_chg_deadline(&batt_drv->chg_health,
-				     now + deadline_min);
+	if (!batt_drv->ssoc_state.buck_enabled) {
+		mutex_unlock(&batt_drv->chg_lock);
+		return -EINVAL;
+	}
+
+	changed = batt_health_set_chg_deadline(&batt_drv->chg_health,
+					       now + deadline_min);
 	mutex_unlock(&batt_drv->chg_lock);
 
+	if (changed)
+		power_supply_changed(batt_drv->psy);
+
 	pr_info("MSC_HEALTH deadline at %lld, now=%lld\n",
 		batt_drv->chg_health.rest_deadline,
 		now);
@@ -3155,6 +3242,11 @@
 		dev_err(&batt_drv->psy->dev,
 				"Failed to create chg_deadline\n");
 
+	ret = device_create_file(&batt_drv->psy->dev, &dev_attr_charge_stage);
+	if (ret)
+		dev_err(&batt_drv->psy->dev,
+				"Failed to create charge_stage\n");
+
 	/* time to full */
 	ret = device_create_file(&batt_drv->psy->dev, &dev_attr_ttf_stats);
 	if (ret)
@@ -3223,8 +3315,8 @@
 			    &debug_chg_health_thr_volt_fops);
 	debugfs_create_file("chg_health_rest_rate", 0600, de, batt_drv,
 			    &debug_chg_health_rest_rate_fops);
-	debugfs_create_file("chg_health_resting", 0600, de, batt_drv,
-			    &debug_chg_health_resting_fops);
+	debugfs_create_file("chg_health_stage", 0600, de, batt_drv,
+			    &debug_chg_health_stage_fops);
 
 	return 0;
 }