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