google_battery: add charging_state and charging_policy sysfs nodes

  - /sys/class/power_supply/battery/charging_state
  - /sys/class/power_supply/battery/charging_policy

Bug: 251427008
Change-Id: I3ca104a8dbc900762b853205a978edee232284e7
Signed-off-by: Jack Wu <[email protected]>
(cherry picked from commit 65b22ba388bb92d2c056d4651e03abdd7c3e0b38)
diff --git a/google_battery.c b/google_battery.c
index 17927c1..dfaa7f5 100644
--- a/google_battery.c
+++ b/google_battery.c
@@ -585,6 +585,10 @@
 
 	/* battery temperature filter */
 	struct batt_temp_filter temp_filter;
+
+	/* charging policy */
+	struct gvotable_election *charging_policy_votable;
+	int charging_policy;
 };
 
 static int gbatt_get_temp(struct batt_drv *batt_drv, int *temp);
@@ -4368,6 +4372,10 @@
 		batt_update_cycle_count(batt_drv);
 		batt_rl_reset(batt_drv);
 
+		/* charging_policy: vote AC false when disconnected */
+		gvotable_cast_long_vote(batt_drv->charging_policy_votable, "MSC_AC",
+					CHARGING_POLICY_VOTE_ADAPTIVE_AC, false);
+
 		/* trigger google_capacity learning. */
 		err = GPSY_SET_PROP(batt_drv->fg_psy,
 				    GBMS_PROP_BATT_CE_CTRL,
@@ -5806,20 +5814,12 @@
 	return scnprintf(buf, PAGE_SIZE, "%d\n",
 			 batt_drv->chg_health.always_on_soc);
 }
-/* setting disable (deadline = -1) or replug (deadline == 0) will disable */
-static ssize_t chg_health_charge_limit_set(struct device *dev,
-					   struct device_attribute *attr,
-					   const char *buf, size_t count)
-{
-	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 int always_on_soc = simple_strtol(buf, NULL, 10);
-	enum chg_health_state rest_state;
 
-	/* Always enable AC when SOC is over trigger */
-	if (always_on_soc < -1 || always_on_soc > 99)
-		return -EINVAL;
+/* setting disable (deadline = -1) or replug (deadline == 0) will disable */
+static void batt_set_health_charge_limit(struct batt_drv *batt_drv,
+					 const int always_on_soc)
+{
+	enum chg_health_state rest_state;
 
 	mutex_lock(&batt_drv->chg_lock);
 
@@ -5860,6 +5860,22 @@
 	batt_drv->chg_health.rest_state = rest_state;
 
 	mutex_unlock(&batt_drv->chg_lock);
+}
+
+static ssize_t chg_health_charge_limit_set(struct device *dev,
+					   struct device_attribute *attr,
+					   const char *buf, size_t count)
+{
+	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 int always_on_soc = simple_strtol(buf, NULL, 10);
+
+	/* Always enable AC when SOC is over the trigger point. */
+	if (always_on_soc < -1 || always_on_soc > 99)
+		return -EINVAL;
+
+	batt_set_health_charge_limit(batt_drv, always_on_soc);
 	power_supply_changed(batt_drv->psy);
 	return count;
 }
@@ -5942,8 +5958,14 @@
 					       deadline_s);
 	mutex_unlock(&batt_drv->chg_lock);
 
-	if (changed)
+	if (changed) {
+		/* charging_policy: vote AC */
+		gvotable_cast_long_vote(batt_drv->charging_policy_votable, "MSC_AC",
+					CHARGING_POLICY_VOTE_ADAPTIVE_AC,
+					batt_drv->chg_health.rest_deadline > 0);
+
 		power_supply_changed(batt_drv->psy);
+	}
 
 	gbms_logbuffer_prlog(batt_drv->ttf_stats.ttf_log, LOGLEVEL_INFO, 0, LOGLEVEL_DEBUG,
 			     "MSC_HEALTH: deadline_s=%lld deadline at %lld",
@@ -6844,6 +6866,141 @@
 
 static const DEVICE_ATTR_RW(first_usage_date);
 
+static int batt_get_charging_state(const struct batt_drv *batt_drv)
+{
+	int ret = BATTERY_STATUS_UNKNOWN;
+	int type, status;
+
+	/* wait for csi_type updated */
+	if (!batt_drv->csi_type_votable)
+		return ret;
+
+	type = gvotable_get_current_int_vote(batt_drv->csi_type_votable);
+	switch (type) {
+	case CSI_TYPE_Normal:
+	case CSI_TYPE_None:
+		ret = BATTERY_STATUS_NORMAL;
+		break;
+	case CSI_TYPE_JEITA:
+		/* wait for csi_status updated */
+		if (!batt_drv->csi_status_votable)
+			break;
+
+		status = gvotable_get_current_int_vote(batt_drv->csi_status_votable);
+		ret = (status == CSI_STATUS_Health_Cold) ?
+		      BATTERY_STATUS_TOO_COLD : BATTERY_STATUS_TOO_HOT;
+		break;
+	case CSI_TYPE_LongLife:
+		ret = BATTERY_STATUS_LONGLIFE;
+		break;
+	case CSI_TYPE_Adaptive:
+		ret = BATTERY_STATUS_ADAPTIVE;
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+static ssize_t charging_state_show(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 = power_supply_get_drvdata(psy);
+	int charging_state = batt_get_charging_state(batt_drv);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", charging_state);
+}
+
+static const DEVICE_ATTR_RO(charging_state);
+
+static void batt_update_charging_policy(struct batt_drv *batt_drv)
+{
+	int value;
+
+	value = gvotable_get_current_int_vote(batt_drv->charging_policy_votable);
+	if (value == batt_drv->charging_policy)
+		return;
+
+	/* update adaptive charging */
+	if (value == CHARGING_POLICY_VOTE_ADAPTIVE_AON)
+		batt_set_health_charge_limit(batt_drv, ADAPTIVE_ALWAYS_ON_SOC);
+	else if (value != CHARGING_POLICY_VOTE_ADAPTIVE_AON &&
+		   batt_drv->charging_policy == CHARGING_POLICY_VOTE_ADAPTIVE_AON)
+		batt_set_health_charge_limit(batt_drv, -1);
+
+	batt_drv->charging_policy = value;
+}
+
+static int charging_policy_translate(int value)
+{
+	int ret = CHARGING_POLICY_VOTE_DEFAULT;
+
+	switch (value) {
+	case CHARGING_POLICY_DEFAULT:
+		ret = CHARGING_POLICY_VOTE_DEFAULT;
+		break;
+	case CHARGING_POLICY_LONGLIFE:
+		ret = CHARGING_POLICY_VOTE_LONGLIFE;
+		break;
+	case CHARGING_POLICY_ADAPTIVE:
+		ret = CHARGING_POLICY_VOTE_ADAPTIVE_AON;
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+static ssize_t charging_policy_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct power_supply *psy = container_of(dev, struct power_supply, dev);
+	struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
+	int value, ret;
+
+	ret = kstrtoint(buf, 0, &value);
+	if (ret < 0)
+		return ret;
+
+	if (value > CHARGING_POLICY_ADAPTIVE || value < CHARGING_POLICY_DEFAULT)
+		return count;
+
+	if (!batt_drv->charging_policy_votable) {
+		batt_drv->charging_policy_votable =
+			gvotable_election_get_handle(VOTABLE_CHARGING_POLICY);
+		if (!batt_drv->charging_policy_votable)
+			return count;
+	}
+
+	gvotable_cast_long_vote(batt_drv->charging_policy_votable, "MSC_USER",
+				charging_policy_translate(value), true);
+	batt_update_charging_policy(batt_drv);
+
+	return count;
+}
+
+static ssize_t charging_policy_show(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 = power_supply_get_drvdata(psy);
+	int value = CHARGING_POLICY_VOTE_UNKNOWN;
+
+	if (!batt_drv->charging_policy_votable)
+		batt_drv->charging_policy_votable =
+			gvotable_election_get_handle(VOTABLE_CHARGING_POLICY);
+	if (batt_drv->charging_policy_votable)
+		value = gvotable_get_current_int_vote(batt_drv->charging_policy_votable);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", value);
+}
+
+static const DEVICE_ATTR_RW(charging_policy);
+
 /* CSI --------------------------------------------------------------------- */
 
 static ssize_t charging_speed_store(struct device *dev,
@@ -7298,6 +7455,12 @@
 	ret = device_create_file(&batt_drv->psy->dev, &dev_attr_first_usage_date);
 	if (ret)
 		dev_err(&batt_drv->psy->dev, "Failed to create first usage date\n");
+	ret = device_create_file(&batt_drv->psy->dev, &dev_attr_charging_state);
+	if (ret)
+		dev_err(&batt_drv->psy->dev, "Failed to create charging state\n");
+	ret = device_create_file(&batt_drv->psy->dev, &dev_attr_charging_policy);
+	if (ret)
+		dev_err(&batt_drv->psy->dev, "Failed to create charging policy\n");
 
 	/* csi */
 	ret = device_create_file(&batt_drv->psy->dev, &dev_attr_charging_speed);
@@ -8922,6 +9085,7 @@
 	batt_drv->fake_temp = 0;
 	batt_drv->fake_battery_present = -1;
 	batt_drv->boot_to_os_attempts = 0;
+	batt_drv->charging_policy = CHARGING_POLICY_DEFAULT;
 	batt_reset_chg_drv_state(batt_drv);
 
 	mutex_init(&batt_drv->chg_lock);
@@ -9465,6 +9629,7 @@
 	batt_drv->fan_level_votable = NULL;
 	batt_drv->csi_status_votable = NULL;
 	batt_drv->csi_type_votable = NULL;
+	batt_drv->charging_policy_votable = NULL;
 
 	return 0;
 }
diff --git a/google_bms.h b/google_bms.h
index 4bf9285..01dc3dd 100644
--- a/google_bms.h
+++ b/google_bms.h
@@ -462,6 +462,8 @@
 #define VOTABLE_CSI_STATUS	"CSI_STATUS"
 #define VOTABLE_CSI_TYPE	"CSI_TYPE"
 
+#define VOTABLE_CHARGING_POLICY	"CHARGING_POLICY"
+
 #define VOTABLE_DC_CHG_AVAIL	"DC_AVAIL"
 #define REASON_DC_DRV		"DC_DRV"
 #define REASON_MDIS		"MDIS"
@@ -648,6 +650,41 @@
 	CSI_STATUS_Charging = 200,	// All good
 };
 
+enum charging_state {
+       BATTERY_STATUS_UNKNOWN = -1,
+
+       BATTERY_STATUS_NORMAL = 1,
+       BATTERY_STATUS_TOO_COLD = 2,
+       BATTERY_STATUS_TOO_HOT = 3,
+       BATTERY_STATUS_LONGLIFE = 4,
+       BATTERY_STATUS_ADAPTIVE = 5,
+};
+
+#define LONGLIFE_CHARGE_STOP_LEVEL 80
+#define LONGLIFE_CHARGE_START_LEVEL 70
+#define ADAPTIVE_ALWAYS_ON_SOC 80
+
+enum charging_policy {
+       CHARGING_POLICY_UNKNOWN = -1,
+
+       CHARGING_POLICY_DEFAULT = 1,
+       CHARGING_POLICY_LONGLIFE = 2,
+       CHARGING_POLICY_ADAPTIVE = 3,
+};
+
+/*
+ * LONGLIFE takes precedence over AC or AON limits,
+ * and AC also must take precedence over the AON limit.
+ */
+enum charging_policy_vote {
+       CHARGING_POLICY_VOTE_UNKNOWN = -1,
+
+       CHARGING_POLICY_VOTE_DEFAULT = 1,
+       CHARGING_POLICY_VOTE_ADAPTIVE_AON = 2,
+       CHARGING_POLICY_VOTE_ADAPTIVE_AC = 3,
+       CHARGING_POLICY_VOTE_LONGLIFE = 4,
+};
+
 #define to_cooling_device(_dev)	\
 	container_of(_dev, struct thermal_cooling_device, device)
 
diff --git a/google_charger.c b/google_charger.c
index e4b0184..3d5910f 100644
--- a/google_charger.c
+++ b/google_charger.c
@@ -327,6 +327,10 @@
 	ktime_t thermal_stats_last_update;
 	int *thermal_stats_mdis_levels;
 	int thermal_levels_count;
+
+	/* charging policy */
+	struct gvotable_election *charging_policy_votable;
+	int charging_policy;
 };
 
 static void reschedule_chg_work(struct chg_drv *chg_drv)
@@ -4228,6 +4232,39 @@
 	return 0;
 }
 
+static void chg_update_charging_policy(struct chg_drv *chg_drv, const int value)
+{
+	/* set custom upper and lower bound for long_life charging policy */
+	if (value == CHARGING_POLICY_VOTE_LONGLIFE &&
+	    chg_drv->charging_policy != CHARGING_POLICY_VOTE_LONGLIFE) {
+		chg_drv->charge_stop_level = LONGLIFE_CHARGE_STOP_LEVEL;
+		chg_drv->charge_start_level = LONGLIFE_CHARGE_START_LEVEL;
+	} else if (value != CHARGING_POLICY_VOTE_LONGLIFE &&
+		   chg_drv->charging_policy == CHARGING_POLICY_VOTE_LONGLIFE) {
+		chg_drv->charge_stop_level = DEFAULT_CHARGE_STOP_LEVEL;
+		chg_drv->charge_start_level = DEFAULT_CHARGE_START_LEVEL;
+	}
+
+	chg_drv->charging_policy = value;
+}
+
+static int charging_policy_cb(struct gvotable_election *el,
+			      const char *reason, void *vote)
+{
+	struct chg_drv *chg_drv = gvotable_get_data(el);
+	int charging_policy = GVOTABLE_PTR_TO_INT(vote);
+
+	if (!chg_drv || chg_drv->charging_policy == charging_policy)
+		return 0;
+
+	chg_update_charging_policy(chg_drv, charging_policy);
+
+	if (chg_drv->bat_psy)
+		power_supply_changed(chg_drv->bat_psy);
+
+	return 0;
+}
+
 static int chg_disable_std_votables(struct chg_drv *chg_drv)
 {
 	struct gvotable_election *qc_votable;
@@ -4261,6 +4298,7 @@
 	gvotable_destroy_election(chg_drv->msc_chg_disable_votable);
 	gvotable_destroy_election(chg_drv->msc_pwr_disable_votable);
 	gvotable_destroy_election(chg_drv->msc_temp_dry_run_votable);
+	gvotable_destroy_election(chg_drv->charging_policy_votable);
 
 	chg_drv->msc_fv_votable = NULL;
 	chg_drv->msc_fcc_votable = NULL;
@@ -4270,6 +4308,7 @@
 	chg_drv->msc_temp_dry_run_votable = NULL;
 	chg_drv->csi_status_votable = NULL;
 	chg_drv->csi_type_votable = NULL;
+	chg_drv->charging_policy_votable = NULL;
 }
 
 /* TODO: qcom/battery.c mostly handles PL charging: we don't need it.
@@ -4365,6 +4404,21 @@
 	gvotable_election_set_name(chg_drv->msc_temp_dry_run_votable,
 				   VOTABLE_TEMP_DRYRUN);
 
+	chg_drv->charging_policy_votable =
+		gvotable_create_int_election(NULL, gvotable_comparator_int_max,
+					     charging_policy_cb, chg_drv);
+	if (IS_ERR_OR_NULL(chg_drv->charging_policy_votable)) {
+		ret = PTR_ERR(chg_drv->charging_policy_votable);
+		chg_drv->charging_policy_votable = NULL;
+		goto error_exit;
+	}
+
+	gvotable_set_vote2str(chg_drv->charging_policy_votable, gvotable_v2s_int);
+	gvotable_election_set_name(chg_drv->charging_policy_votable,
+				   VOTABLE_CHARGING_POLICY);
+	gvotable_cast_long_vote(chg_drv->charging_policy_votable,
+				"DEFAULT", CHARGING_POLICY_DEFAULT, true);
+
 	return 0;
 
 error_exit:
@@ -5329,6 +5383,7 @@
 	chg_drv->stop_charging = -1;
 	chg_drv->charge_stop_level = DEFAULT_CHARGE_STOP_LEVEL;
 	chg_drv->charge_start_level = DEFAULT_CHARGE_START_LEVEL;
+	chg_drv->charging_policy = CHARGING_POLICY_DEFAULT;
 	mutex_init(&chg_drv->stats_lock);
 	thermal_stats_init(chg_drv);