max1720x_battery: support fix cycle count and history
- Add sysfs node to fix cycle count and history functionality.
- Remove recursive call in max1720x_restore_battery_cycle().
Bug: 322426625
Test: manually write wrong history and cycle count then recover to
normal cycle count successfully.
Change-Id: I60808ebf190e37eedd87a0f1bbdf34ccc894657d
Merged-In: I2b4a7e9c76c52e5ae963cbbb20ac2504474701d0
Signed-off-by: Spade Lee <[email protected]>
(cherry picked from commit 46d54b5736a53207528f317f8bc94cfd8871f377)
diff --git a/google_battery.c b/google_battery.c
index 619ec7a..8943870 100644
--- a/google_battery.c
+++ b/google_battery.c
@@ -8911,6 +8911,9 @@
if (cycle_cnt <= 0)
return -EIO;
+ if (cycle_cnt != batt_drv->cycle_count)
+ batt_drv->cycle_count = cycle_cnt;
+
if (batt_drv->blf_collect_now) {
pr_info("MSC_HIST cycle_cnt:%d->%d saved_cnt=%d\n",
cycle_cnt, batt_drv->blf_collect_now,
diff --git a/max1720x_battery.c b/max1720x_battery.c
index aae381e..4eb0d93 100644
--- a/max1720x_battery.c
+++ b/max1720x_battery.c
@@ -293,6 +293,7 @@
static int max1720x_monitor_log_data(struct max1720x_chip *chip, bool force_log);
static int max17201_init_rc_switch(struct max1720x_chip *chip);
static int max1720x_update_cycle_count(struct max1720x_chip *chip);
+static int max1720x_check_history(struct max1720x_chip *chip, bool fix);
static bool max17x0x_reglog_init(struct max1720x_chip *chip)
{
@@ -1142,6 +1143,24 @@
static DEVICE_ATTR_RW(fg_learning_events);
+static ssize_t fix_cycle_count_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 max1720x_chip *chip = power_supply_get_drvdata(psy);
+ int ret;
+
+ if (buf[0] == '1') {
+ ret = max1720x_check_history(chip, true);
+ dev_info(chip->dev, "%s: fix cycle count (ret=%d)\n", __func__, ret);
+ }
+
+ return count;
+}
+
+static const DEVICE_ATTR_WO(fix_cycle_count);
+
/* lsb 1/256, race with max1720x_model_work() */
static int max1720x_get_capacity_raw(struct max1720x_chip *chip, u16 *data)
{
@@ -1516,8 +1535,7 @@
/* recover (last - OVERFLOW_START_ENTRY + 1) entries */
for (index = offset; index < BATT_MAX_HIST_CNT; index++) {
- ret = gbms_storage_read_data(GBMS_TAG_HIST, &temp,
- sizeof(temp), index);
+ ret = gbms_storage_read_data(GBMS_TAG_HIST, &temp, sizeof(temp), index);
if (ret < 0)
return -EIO;
@@ -1525,39 +1543,128 @@
break;
}
- return index == BATT_MAX_HIST_CNT ? -1 : index - offset;
+ return index - offset;
}
-static int max1720x_check_history(struct max1720x_chip *chip)
+static int max1720x_recover_history(struct max1720x_chip *chip, int first_empty, int count,
+ int real_cycle)
+{
+ struct max17x0x_eeprom_history *src, dst;
+ const int entry_size = sizeof(dst);
+ int ret, idx, retry, dst_entry;
+ const u16 eeprom_cycle = (real_cycle * CYCLE_LSB_UNIT) >> 1;
+
+ /* real cycle count should less than current cycle count */
+ if (real_cycle >= chip->cycle_count)
+ return -EINVAL;
+
+ /* 1. Copy history */
+ src = kmalloc(entry_size * count, GFP_KERNEL);
+ if (!src)
+ return -ENOMEM;
+
+ /* read and cache all misplaced entries */
+ for (idx = 0; idx < count; idx++) {
+ ret = gbms_storage_read_data(GBMS_TAG_HIST, &src[idx], entry_size,
+ OVERFLOW_START_ENTRY + idx);
+ if (ret < 0)
+ goto error_out;
+ }
+
+ /* copy one by one starting from the last entry */
+ for (idx = count - 1, dst_entry = first_empty + idx; idx >= 0; idx--, dst_entry--) {
+ /* mark timerh MSB = 1 as it's moving from overflow entry */
+ src[idx].timerh |= 0x80;
+ ret = gbms_storage_write_data(GBMS_TAG_HIST, &src[idx], entry_size, dst_entry);
+ if (ret < 0)
+ goto error_out;
+
+ gbms_logbuffer_devlog(chip->ce_log, chip->dev, LOGLEVEL_INFO, 0, LOGLEVEL_INFO,
+ "copy history entry %d to %d successfully",
+ OVERFLOW_START_ENTRY + idx, dst_entry);
+ }
+
+ /* 2. Erase misplaced history */
+ memset(&dst, 0xff, entry_size);
+ for (idx = OVERFLOW_START_ENTRY + count - 1; idx > first_empty + count; idx--)
+ gbms_storage_write_data(GBMS_TAG_HIST, &dst, entry_size, idx);
+
+ /* 3. Update eeprom_cycle, cycle_count, cycle_count_offset */
+ chip->eeprom_cycle = eeprom_cycle;
+ chip->cycle_count = real_cycle;
+ if (real_cycle < MAXIM_CYCLE_COUNT_RESET)
+ chip->cycle_count_offset = 0;
+
+ /* 4. Update Cycle register, EEPROM CNHS with retry */
+ for (retry = 10; retry > 0; retry--) {
+ ret = REGMAP_WRITE_VERIFY(&chip->regmap, MAX1720X_CYCLES,
+ (real_cycle * CYCLE_LSB_UNIT) & 0xFFFF);
+ if (ret == 0)
+ break;
+
+ dev_err(chip->dev, "Fail to update cycles reg (%d)", ret);
+ }
+
+ for (retry = 10; retry > 0; retry--) {
+ ret = gbms_storage_write(GBMS_TAG_CNHS, &eeprom_cycle, sizeof(eeprom_cycle));
+ if (ret == sizeof(eeprom_cycle))
+ break;
+
+ dev_err(chip->dev, "Fail to write eeprom cycle (%d)", ret);
+ }
+
+error_out:
+ kfree(src);
+ return ret < 0 ? ret : 0;
+}
+
+/* needs mutex_lock(&chip->model_lock) && mutex_lock(&chip->history_lock) */
+static int max1720x_check_history(struct max1720x_chip *chip, bool fix)
{
struct max17x0x_eeprom_history temp = { 0 };
int ret, misplaced_count, first_empty, est_cycle;
+ const int last_cycle_count = chip->cycle_count;
- /* when the entry before the overflow is non empty we are good */
+ if (chip->cycle_count_offset < MAXIM_CYCLE_COUNT_RESET)
+ return 0;
+
+ /* when the last entry before the overflow is non empty we are good */
ret = gbms_storage_read_data(GBMS_TAG_HIST, &temp, sizeof(temp),
OVERFLOW_START_ENTRY - 1);
- if (ret < 0 || !max1720x_history_empty(&temp))
+ if (ret < 0)
return ret;
- /* # entries that need to be moved from OVERFLOW_START_ENTRY */
- misplaced_count = max1720x_find_empty(OVERFLOW_START_ENTRY);
+ if (!max1720x_history_empty(&temp))
+ return 0;
+ /* # of entries that need to be moved from OVERFLOW_START_ENTRY */
+ misplaced_count = max1720x_find_empty(OVERFLOW_START_ENTRY);
if (misplaced_count <= 0)
- return misplaced_count;
+ return 0;
/* where entries will be moved to */
first_empty = max1720x_find_empty(0);
+ if (first_empty >= OVERFLOW_START_ENTRY)
+ return 0;
- if (first_empty < 0)
- return -EINVAL;
+ est_cycle = (first_empty + DIV_ROUND_UP(last_cycle_count, EEPROM_DELTA_CYCLE) -
+ OVERFLOW_START_ENTRY) * EEPROM_DELTA_CYCLE;
- est_cycle = (first_empty + misplaced_count) * EEPROM_DELTA_CYCLE;
+ if (fix) {
+ ret = max1720x_recover_history(chip, first_empty, misplaced_count, est_cycle);
+ /* log first empty after recovery and result */
+ first_empty = max1720x_find_empty(0);
+ gbms_logbuffer_devlog(chip->monitor_log, chip->dev,
+ LOGLEVEL_INFO, 0, LOGLEVEL_INFO,
+ "0x%04X 00:%04X 01:%04X 02:%04X 03:%04X", MONITOR_TAG_HV,
+ first_empty, ret, last_cycle_count, chip->cycle_count);
+ return ret;
+ }
gbms_logbuffer_devlog(chip->monitor_log, chip->dev,
LOGLEVEL_INFO, 0, LOGLEVEL_INFO,
- "%#04X 00:%04X 01:%04X 02:%04X 03:%04X", MONITOR_TAG_HV,
- first_empty, misplaced_count, chip->cycle_count, est_cycle);
-
+ "0x%04X 00:%04X 01:%04X 02:%04X 03:%04X", MONITOR_TAG_HV,
+ first_empty, misplaced_count, last_cycle_count, est_cycle);
return 0;
}
@@ -1607,11 +1714,15 @@
dev_err(chip->dev, "fail to update cycles (%d)", ret);
return ret;
}
+ reg_cycle = eeprom_cycle;
}
+ chip->cycle_count = reg_to_cycles((u32)reg_cycle, chip->gauge_type) +
+ chip->cycle_count_offset;
chip->cycle_reg_ok = true;
- max1720x_update_cycle_count(chip);
- max1720x_check_history(chip);
+ mutex_lock(&chip->history_lock);
+ max1720x_check_history(chip, false);
+ mutex_unlock(&chip->history_lock);
return 0;
}
@@ -1704,7 +1815,13 @@
static int max1720x_get_cycle_count(struct max1720x_chip *chip)
{
- return chip->cycle_count;
+ int cc;
+
+ mutex_lock(&chip->history_lock);
+ cc = chip->cycle_count;
+ mutex_unlock(&chip->history_lock);
+
+ return cc;
}
static int max1720x_update_cycle_count(struct max1720x_chip *chip)
@@ -1722,7 +1839,9 @@
/* if cycle reg hasn't been restored from storage, restore it before update cycle count */
if (!chip->cycle_reg_ok && chip->gauge_type == MAX_M5_GAUGE_TYPE &&
max_m5_recal_state(chip->model_data) == RE_CAL_STATE_IDLE) {
+ mutex_lock(&chip->model_lock);
err = max1720x_restore_battery_cycle(chip);
+ mutex_unlock(&chip->model_lock);
if (err < 0)
dev_err(chip->dev, "%s cannot restore cycle count (%d)\n", __func__, err);
@@ -4393,6 +4512,11 @@
if (ret)
dev_err(&chip->psy->dev, "Failed to create act_impedance\n");
+ /* fix cycle count mismatch */
+ ret = device_create_file(&chip->psy->dev, &dev_attr_fix_cycle_count);
+ if (ret)
+ dev_err(&chip->psy->dev, "Failed to create fix_cycle_count\n");
+
de = debugfs_create_dir(chip->max1720x_psy_desc.name, 0);
if (IS_ERR_OR_NULL(de))
return -ENOENT;
@@ -5356,12 +5480,12 @@
MAX1720X_STATUS_BI, 0x0);
}
- ret = max1720x_restore_battery_cycle(chip);
- if (ret < 0)
- dev_err(chip->dev, "%s cannot restore cycle count (%d)\n", __func__, ret);
-
/* max_m5 triggers loading of the model in the irq handler on POR */
if (!chip->por && chip->gauge_type == MAX_M5_GAUGE_TYPE) {
+ ret = max1720x_restore_battery_cycle(chip);
+ if (ret < 0)
+ dev_err(chip->dev, "%s cannot restore cycle count (%d)\n", __func__, ret);
+
ret = max1720x_init_max_m5(chip);
if (ret < 0)
return ret;
@@ -5581,8 +5705,6 @@
{
struct device *hcdev;
- mutex_init(&chip->history_lock);
-
chip->hcmajor = -1;
/* cat /proc/devices */
@@ -6407,6 +6529,7 @@
/* fuel gauge model needs to know the batt_id */
mutex_init(&chip->model_lock);
+ mutex_init(&chip->history_lock);
chip->get_prop_ws = wakeup_source_register(NULL, "GetProp");
if (!chip->get_prop_ws)