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);