summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPixelBot AutoMerger <android-nexus-securitybot@system.gserviceaccount.com>2024-02-25 18:59:27 -0800
committerSecurityBot <android-nexus-securitybot@system.gserviceaccount.com>2024-02-25 18:59:28 -0800
commit0dc90bee72facdd7959eae79864663cf8f683b85 (patch)
treeb3110e88fa9cbd2d0b2df163b1bf2a6f2bff17f5
parent1735a681469d120473b7fac4642619db74628056 (diff)
parente87ffcff3d1816a2129eacbf59aa48040ce9f5dd (diff)
downloadbms-android-gs-raviole-5.10-android15-dp.tar.gz
SBMerger: 605678113 Change-Id: I445440a3e3c82acffb1bde7b129e4dc83071529d Signed-off-by: SecurityBot <android-nexus-securitybot@system.gserviceaccount.com>
-rw-r--r--Documentation/ABI/testing/sysfs-class-power9
-rw-r--r--Makefile1
-rw-r--r--google_bms.c24
-rw-r--r--google_bms.h4
-rw-r--r--max1720x_battery.c152
-rw-r--r--max1720x_battery.h46
-rw-r--r--max_m5.c11
-rw-r--r--maxfg_logging.c277
-rw-r--r--maxfg_logging.h57
-rw-r--r--p9221_charger.c1
10 files changed, 548 insertions, 34 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-power b/Documentation/ABI/testing/sysfs-class-power
new file mode 100644
index 0000000..04d0edd
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-power
@@ -0,0 +1,9 @@
+What: /sys/class/power_supply/maxfg/fg_learning_events
+Date: Feb 2024
+Contact: apelosi@google.com
+Description:
+ Report a snapshot of fuel gauge registers captured during
+ each learning event. Write 0 to clear the events.
+
+ Access: Read/Write
+ Valid values: Represented as string \ No newline at end of file
diff --git a/Makefile b/Makefile
index efd0f3b..ac52f71 100644
--- a/Makefile
+++ b/Makefile
@@ -101,6 +101,7 @@ max77759-objs += max77759_maxq.o
obj-$(CONFIG_MAX1720X_BATTERY) += max1720x-battery.o
max1720x-battery-objs += max1720x_battery.o
max1720x-battery-objs += max1720x_outliers.o
+max1720x-battery-objs += maxfg_logging.o
max1720x-battery-objs += max_m5.o
# OVP
diff --git a/google_bms.c b/google_bms.c
index 3e78080..fc2f171 100644
--- a/google_bms.c
+++ b/google_bms.c
@@ -717,6 +717,30 @@ void gbms_logbuffer_prlog(struct logbuffer *log, int level, int debug_no_logbuff
}
EXPORT_SYMBOL_GPL(gbms_logbuffer_prlog);
+void gbms_logbuffer_devlog(struct logbuffer *log, struct device *dev, int level,
+ int debug_no_logbuffer, int debug_printk_prlog,
+ const char *f, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ va_start(args, f);
+
+ vaf.fmt = f;
+ vaf.va = &args;
+
+ if (!debug_no_logbuffer)
+ logbuffer_vlog(log, f, args);
+
+ if (level <= debug_printk_prlog)
+ dev_printk_emit(level, dev, "%s %s: %pV",
+ dev_driver_string(dev),
+ dev_name(dev), &vaf);
+
+ va_end(args);
+}
+EXPORT_SYMBOL_GPL(gbms_logbuffer_devlog);
+
bool chg_state_is_disconnected(const union gbms_charger_state *chg_state)
{
return ((chg_state->f.flags & GBMS_CS_FLAG_BUCK_EN) == 0) &&
diff --git a/google_bms.h b/google_bms.h
index 83fc9ad..fec3346 100644
--- a/google_bms.h
+++ b/google_bms.h
@@ -456,6 +456,10 @@ __printf(5,6)
void gbms_logbuffer_prlog(struct logbuffer *log, int level, int debug_no_logbuffer,
int debug_printk_prlog, const char *f, ...);
+void gbms_logbuffer_devlog(struct logbuffer *log, struct device *dev, int level,
+ int debug_no_logbuffer, int debug_printk_prlog,
+ const char *f, ...);
+
/* debug/print */
const char *gbms_chg_type_s(int chg_type);
const char *gbms_chg_status_s(int chg_status);
diff --git a/max1720x_battery.c b/max1720x_battery.c
index d27896e..a5601e1 100644
--- a/max1720x_battery.c
+++ b/max1720x_battery.c
@@ -35,6 +35,7 @@
#include "gbms_power_supply.h"
#include "google_bms.h"
#include "max1720x_battery.h"
+#include "maxfg_logging.h"
#include <linux/debugfs.h>
@@ -278,6 +279,9 @@ struct max1720x_chip {
/* Current Offset */
bool current_offset_done;
+
+ /* buffer for recording learning history */
+ struct maxfg_capture_buf cb_lh;
};
#define MAX1720_EMPTY_VOLTAGE(profile, temp, cycle) \
@@ -308,41 +312,7 @@ static bool max17x0x_reglog_init(struct max1720x_chip *chip)
/* TODO: split between NV and Volatile? */
-static const struct max17x0x_reg * max17x0x_find_by_index(struct max17x0x_regtags *tags,
- int index)
-{
- if (index < 0 || !tags || index >= tags->max)
- return NULL;
-
- return &tags->map[index];
-}
-
-static const struct max17x0x_reg * max17x0x_find_by_tag(struct max17x0x_regmap *map,
- enum max17x0x_reg_tags tag)
-{
- return max17x0x_find_by_index(&map->regtags, tag);
-}
-
-static inline int max17x0x_reg_read(struct max17x0x_regmap *map,
- enum max17x0x_reg_tags tag,
- u16 *val)
-{
- const struct max17x0x_reg *reg;
- unsigned int tmp;
- int rtn;
-
- reg = max17x0x_find_by_tag(map, tag);
- if (!reg)
- return -EINVAL;
-
- rtn = regmap_read(map->regmap, reg->reg, &tmp);
- if (rtn)
- pr_err("Failed to read %x\n", reg->reg);
- else
- *val = tmp;
- return rtn;
-}
/* ------------------------------------------------------------------------- */
@@ -1142,6 +1112,36 @@ static ssize_t rc_switch_enable_show(struct device *dev,
static const DEVICE_ATTR_RW(rc_switch_enable);
+
+static ssize_t fg_learning_events_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct power_supply *psy = container_of(dev, struct power_supply, dev);
+ struct max1720x_chip *chip = power_supply_get_drvdata(psy);
+
+ return maxfg_show_captured_buffer(&chip->cb_lh, buf, PAGE_SIZE);
+}
+
+static ssize_t fg_learning_events_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 value, ret;
+
+ ret = kstrtoint(buf, 0, &value);
+ if (ret < 0)
+ return ret;
+
+ if (value == 0)
+ maxfg_clear_capture_buf(&chip->cb_lh);
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(fg_learning_events);
+
/* lsb 1/256, race with max1720x_model_work() */
static int max1720x_get_capacity_raw(struct max1720x_chip *chip, u16 *data)
{
@@ -2272,6 +2272,49 @@ static int max1720x_current_offset_fix(struct max1720x_chip *chip)
return ret;
}
+static int max1720x_monitor_log_learning(struct max1720x_chip *chip, bool force)
+{
+ const bool seed = !chip->cb_lh.latest_entry;
+ bool log_it;
+ char *buf;
+ int ret;
+
+ /* do nothing if no changes on dpacc/dqacc or relaxation */
+ log_it = force || seed ||
+ maxfg_ce_relaxed(&chip->regmap, MAX_M5_FSTAT_RELDT | MAX_M5_FSTAT_RELDT2,
+ (u16 *)chip->cb_lh.latest_entry);
+ if (!log_it)
+ return 0;
+
+ ret = maxfg_capture_registers(&chip->cb_lh);
+ if (ret < 0) {
+ dev_dbg(chip->dev, "cannot read learning parameters (%d)\n", ret);
+ return ret;
+ }
+
+ /* no need to log at boot */
+ if (seed)
+ return 0;
+
+ buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = maxfg_capture_to_cstr(&chip->cb_lh.config,
+ (u16 *)chip->cb_lh.latest_entry,
+ buf, PAGE_SIZE);
+ if (ret > 0)
+ gbms_logbuffer_devlog(chip->monitor_log, chip->dev,
+ LOGLEVEL_INFO, 0, LOGLEVEL_INFO,
+ "learn %s", buf);
+
+ kfree(buf);
+
+ kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE);
+
+ return 0;
+}
+
static int max1720x_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
@@ -2309,6 +2352,9 @@ static int max1720x_get_property(struct power_supply *psy,
if (err == POWER_SUPPLY_STATUS_FULL)
batt_ce_start(&chip->cap_estimate,
chip->cap_estimate.cap_tsettle);
+ /* check for relaxation event and log it */
+ max1720x_monitor_log_learning(chip, false);
+
/* return data ok */
err = 0;
break;
@@ -2940,6 +2986,7 @@ static bool max1720x_fg_irq_storm_check(struct max1720x_chip *chip)
return storm;
}
+
static irqreturn_t max1720x_fg_irq_thread_fn(int irq, void *obj)
{
struct max1720x_chip *chip = (struct max1720x_chip *)obj;
@@ -3104,6 +3151,7 @@ static irqreturn_t max1720x_fg_irq_thread_fn(int irq, void *obj)
if (storm) {
pr_debug("Force power_supply_change in storm\n");
} else {
+ max1720x_monitor_log_learning(chip, false);
max1720x_monitor_log_data(chip, false);
if (chip->gauge_type == MAX_M5_GAUGE_TYPE)
max_m5_check_recal_state(chip->model_data,
@@ -3830,6 +3878,17 @@ static int debug_fake_battery_set(void *data, u64 val)
DEFINE_SIMPLE_ATTRIBUTE(debug_fake_battery_fops, NULL,
debug_fake_battery_set, "%llu\n");
+
+static int max1720x_log_learn_set(void *data, u64 val)
+{
+ struct max1720x_chip *chip = data;
+
+ max1720x_monitor_log_learning(chip, true);
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(debug_log_learn_fops, NULL, max1720x_log_learn_set, "%llu\n");
+
+
static void max17x0x_reglog_dump(struct max17x0x_reglog *regs,
size_t size,
char *buff)
@@ -4231,6 +4290,7 @@ static ssize_t act_impedance_show(struct device *dev,
static const DEVICE_ATTR_RW(act_impedance);
+
static int max17x0x_init_sysfs(struct max1720x_chip *chip)
{
struct dentry *de;
@@ -4252,6 +4312,7 @@ static int max17x0x_init_sysfs(struct max1720x_chip *chip)
debugfs_create_file("fake_battery", 0400, de, chip, &debug_fake_battery_fops);
debugfs_create_file("batt_id", 0600, de, chip, &debug_batt_id_fops);
debugfs_create_file("force_psy_update", 0600, de, chip, &debug_force_psy_update_fops);
+ debugfs_create_file("log_learn", 0400, de, chip, &debug_log_learn_fops);
if (chip->regmap.reglog)
debugfs_create_file("regmap_writes", 0440, de,
@@ -6164,6 +6225,26 @@ void *max1720x_get_model_data(struct i2c_client *client)
return chip ? chip->model_data : NULL;
}
+
+static int max1720x_init_fg_capture(struct max1720x_chip *chip)
+{
+ struct device *dev = &chip->psy->dev;
+ int ret;
+
+ /* Logging FG Learning */
+ maxfg_init_fg_learn_capture_config(&chip->cb_lh.config,
+ &chip->regmap, &chip->regmap);
+ ret = maxfg_alloc_capture_buf(&chip->cb_lh, MAX_FG_LEARN_PARAM_MAX_HIST);
+ if (ret < 0)
+ return -ENOMEM;
+
+ ret = device_create_file(dev, &dev_attr_fg_learning_events);
+ if (ret)
+ dev_err(dev, "Failed to create fg_learning_params attribute\n");
+
+ return ret;
+}
+
static int max1720x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -6338,6 +6419,7 @@ static int max1720x_probe(struct i2c_client *client,
chip->reg_prop_capacity_raw = (reg) ? reg->reg : MAX1720X_REPSOC;
max17x0x_init_sysfs(chip);
+ max1720x_init_fg_capture(chip);
INIT_DELAYED_WORK(&chip->cap_estimate.settle_timer,
batt_ce_capacityfiltered_work);
@@ -6381,6 +6463,8 @@ static int max1720x_remove(struct i2c_client *client)
if (chip->secondary)
i2c_unregister_device(chip->secondary);
+
+ maxfg_free_capture_buf(&chip->cb_lh);
wakeup_source_unregister(chip->get_prop_ws);
return 0;
diff --git a/max1720x_battery.h b/max1720x_battery.h
index 93e8f3f..403b992 100644
--- a/max1720x_battery.h
+++ b/max1720x_battery.h
@@ -67,6 +67,17 @@ enum max17x0x_reg_tags {
MAX17X0X_TAG_BCEA,
MAX17X0X_TAG_rset,
MAX17X0X_TAG_BRES,
+
+ MAXFG_TAG_fcnom,
+ MAXFG_TAG_dpacc,
+ MAXFG_TAG_dqacc,
+ MAXFG_TAG_fcrep,
+ MAXFG_TAG_repsoc,
+ MAXFG_TAG_msoc,
+ MAXFG_TAG_learn,
+ MAXFG_TAG_rcomp0,
+ MAXFG_TAG_tempco,
+ MAXFG_TAG_fstat,
};
enum max17x0x_reg_types {
@@ -139,6 +150,41 @@ struct max17x0x_regmap {
struct max17x0x_reglog *reglog;
};
+static inline const struct max17x0x_reg *max17x0x_find_by_index(struct max17x0x_regtags *tags,
+ int index)
+{
+ if (index < 0 || !tags || index >= tags->max)
+ return NULL;
+
+ return &tags->map[index];
+}
+
+static inline const struct max17x0x_reg *max17x0x_find_by_tag(struct max17x0x_regmap *map,
+ enum max17x0x_reg_tags tag)
+{
+ return max17x0x_find_by_index(&map->regtags, tag);
+}
+
+static inline int max17x0x_reg_read(struct max17x0x_regmap *map,
+ enum max17x0x_reg_tags tag,
+ u16 *val)
+{
+ const struct max17x0x_reg *reg;
+ unsigned int tmp;
+ int rtn;
+
+ reg = max17x0x_find_by_tag(map, tag);
+ if (!reg)
+ return -EINVAL;
+
+ rtn = regmap_read(map->regmap, reg->reg, &tmp);
+ if (rtn == 0)
+ *val = tmp;
+
+ return rtn;
+}
+
+
int max1720x_get_capacity(struct i2c_client *client, int *iic_raw);
int max1720x_get_voltage_now(struct i2c_client *client, int *iic_raw);
int max17x0x_sw_reset(struct i2c_client *client);
diff --git a/max_m5.c b/max_m5.c
index 35db63f..7cdc61a 100644
--- a/max_m5.c
+++ b/max_m5.c
@@ -1594,6 +1594,17 @@ const struct max17x0x_reg max_m5[] = {
[MAX17X0X_TAG_curr] = { ATOM_INIT_REG16(MAX_M5_CURRENT)},
[MAX17X0X_TAG_mcap] = { ATOM_INIT_REG16(MAX_M5_MIXCAP)},
[MAX17X0X_TAG_vfsoc] = { ATOM_INIT_REG16(MAX_M5_VFSOC)},
+
+ [MAXFG_TAG_tempco] = { ATOM_INIT_REG16(MAX_M5_TEMPCO)},
+ [MAXFG_TAG_rcomp0] = { ATOM_INIT_REG16(MAX_M5_RCOMP0)},
+ [MAXFG_TAG_fcnom] = { ATOM_INIT_REG16(MAX_M5_FULLCAPNOM)},
+ [MAXFG_TAG_fcrep] = { ATOM_INIT_REG16(MAX_M5_FULLCAPREP)},
+ [MAXFG_TAG_repsoc] = { ATOM_INIT_REG16(MAX_M5_REPSOC)},
+ [MAXFG_TAG_msoc] = { ATOM_INIT_REG16(MAX_M5_MIXSOC)},
+ [MAXFG_TAG_learn] = { ATOM_INIT_REG16(MAX_M5_LEARNCFG)},
+ [MAXFG_TAG_fstat] = { ATOM_INIT_REG16(MAX_M5_FSTAT)},
+ [MAXFG_TAG_dqacc] = { ATOM_INIT_REG16(MAX_M5_DQACC)},
+ [MAXFG_TAG_dpacc] = { ATOM_INIT_REG16(MAX_M5_DPACC)},
};
int max_m5_regmap_init(struct max17x0x_regmap *regmap, struct i2c_client *clnt)
diff --git a/maxfg_logging.c b/maxfg_logging.c
new file mode 100644
index 0000000..d4acc0a
--- /dev/null
+++ b/maxfg_logging.c
@@ -0,0 +1,277 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2024 Google LLC
+ */
+
+#include <linux/err.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include "google_bms.h"
+#include "max1720x_battery.h"
+#include "maxfg_logging.h"
+
+/* learning parameters */
+#define MAX_FG_LEARNING_CONFIG_NORMAL_REGS 8
+#define MAX_FG_LEARNING_CONFIG_DEBUG_REGS 2
+
+/* see maxfg_ce_relaxed() */
+static const enum max17x0x_reg_tags fg_learning_param[] = {
+ /* from normal regmap */
+ MAXFG_TAG_fcnom,
+ MAXFG_TAG_dpacc,
+ MAXFG_TAG_dqacc,
+ MAXFG_TAG_fcrep,
+ MAXFG_TAG_repsoc,
+ MAXFG_TAG_msoc,
+ MAX17X0X_TAG_vfsoc,
+ MAXFG_TAG_fstat,
+
+ /* from debug_regmap */
+ MAXFG_TAG_rcomp0,
+ MAXFG_TAG_tempco,
+};
+
+/* this could be static */
+void maxfg_init_fg_learn_capture_config(struct maxfg_capture_config *config,
+ struct max17x0x_regmap *regmap,
+ struct max17x0x_regmap *debug_regmap)
+{
+ strscpy(config->name, "FG Learning Events", sizeof(config->name));
+ config->normal.tag = &fg_learning_param[0];
+ config->normal.reg_cnt = MAX_FG_LEARNING_CONFIG_NORMAL_REGS;
+ config->normal.regmap = regmap;
+
+ config->debug.tag = &fg_learning_param[MAX_FG_LEARNING_CONFIG_NORMAL_REGS];
+ config->debug.reg_cnt = MAX_FG_LEARNING_CONFIG_DEBUG_REGS;
+ config->debug.regmap = debug_regmap;
+
+ config->data_size = ARRAY_SIZE(fg_learning_param) * sizeof(u16);
+}
+
+
+int maxfg_alloc_capture_buf(struct maxfg_capture_buf *buf, int slots)
+{
+ if ((slots & (slots - 1)) || !buf || !buf->config.data_size || !slots)
+ return -EINVAL;
+
+ buf->slots = 0;
+ buf->latest_entry = NULL;
+
+ buf->cb.buf = kzalloc(buf->config.data_size * slots, GFP_KERNEL);
+ if (!buf->cb.buf)
+ return -ENOMEM;
+
+ buf->cb.head = 0;
+ buf->cb.tail = 0;
+ buf->slots = slots;
+
+ mutex_init(&buf->cb_wr_lock);
+ mutex_init(&buf->cb_rd_lock);
+
+ return 0;
+}
+
+void maxfg_clear_capture_buf(struct maxfg_capture_buf *buf)
+{
+ if (!buf)
+ return;
+
+ mutex_lock(&buf->cb_wr_lock);
+ mutex_lock(&buf->cb_rd_lock);
+
+ buf->latest_entry = NULL;
+ buf->cb.head = 0;
+ buf->cb.tail = 0;
+
+ mutex_unlock(&buf->cb_rd_lock);
+ mutex_unlock(&buf->cb_wr_lock);
+}
+
+void maxfg_free_capture_buf(struct maxfg_capture_buf *buf)
+{
+ if (!buf)
+ return;
+
+ if (buf->cb.buf && buf->slots > 0)
+ kfree(buf->cb.buf);
+
+ mutex_destroy(&buf->cb_wr_lock);
+ mutex_destroy(&buf->cb_rd_lock);
+
+ buf->cb.buf = NULL;
+ buf->slots = 0;
+}
+
+static inline int maxfg_read_registers(struct maxfg_capture_regs *regs, u16 *buffer)
+{
+ int ret, idx;
+
+ for (idx = 0; idx < regs->reg_cnt; idx++) {
+ ret = max17x0x_reg_read(regs->regmap, regs->tag[idx], &buffer[idx]);
+ if (ret < 0) {
+ pr_err("failed to reg_tag(%u) %d\n", regs->tag[idx], ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int maxfg_capture_registers(struct maxfg_capture_buf *buf)
+{
+ struct maxfg_capture_config *config = &buf->config;
+ const int data_size = config->data_size;
+ void *latest_entry;
+ int head, tail, ret;
+ u16 *reg_val;
+
+ mutex_lock(&buf->cb_wr_lock);
+
+ head = buf->cb.head;
+ tail = READ_ONCE(buf->cb.tail);
+
+ /* if buffer is full, drop the last entry */
+ if (CIRC_SPACE(head, tail, buf->slots) == 0) {
+ mutex_lock(&buf->cb_rd_lock);
+ WRITE_ONCE(buf->cb.tail, (tail + 1) & (buf->slots - 1));
+ mutex_unlock(&buf->cb_rd_lock);
+ }
+
+ reg_val = (u16 *)&buf->cb.buf[head * data_size];
+ latest_entry = reg_val;
+
+ ret = maxfg_read_registers(&config->normal, reg_val);
+ if (ret < 0)
+ goto exit_done;
+
+ reg_val += config->normal.reg_cnt;
+
+ ret = maxfg_read_registers(&config->debug, reg_val);
+ if (ret < 0)
+ goto exit_done;
+
+ smp_wmb();
+ WRITE_ONCE(buf->cb.head, (head + 1) & (buf->slots - 1));
+
+ buf->latest_entry = latest_entry;
+
+exit_done:
+ mutex_unlock(&buf->cb_wr_lock);
+ return ret;
+}
+
+int maxfg_capture_to_cstr(struct maxfg_capture_config *config, u16 *reg_val,
+ char *str_buf, int buf_len)
+{
+ const struct max17x0x_reg *fg_reg;
+ int reg_idx;
+ int len = 0;
+
+ for (reg_idx = 0; reg_idx < config->normal.reg_cnt && len < buf_len; reg_idx++) {
+ fg_reg = max17x0x_find_by_tag(config->normal.regmap,
+ config->normal.tag[reg_idx]);
+ if (!fg_reg)
+ return len;
+
+ len += scnprintf(&str_buf[len], buf_len - len, "%02X:%04X ",
+ fg_reg->reg, reg_val[reg_idx]);
+ }
+
+ reg_val += config->normal.reg_cnt;
+
+ for (reg_idx = 0; reg_idx < config->debug.reg_cnt && len < buf_len; reg_idx++) {
+ fg_reg = max17x0x_find_by_tag(config->debug.regmap,
+ config->debug.tag[reg_idx]);
+ if (!fg_reg)
+ return len;
+
+ len += scnprintf(&str_buf[len], buf_len - len, "%02X:%04X ",
+ fg_reg->reg, reg_val[reg_idx]);
+ }
+
+ return len;
+}
+
+int maxfg_show_captured_buffer(struct maxfg_capture_buf *buf,
+ char *str_buf, int buf_len)
+{
+ struct maxfg_capture_config *config = &buf->config;
+ const int data_size = config->data_size;
+ int head, tail, count, to_end, idx, rt;
+ u16 *reg_val;
+
+ if (!buf)
+ return -EINVAL;
+
+ mutex_lock(&buf->cb_rd_lock);
+
+ head = READ_ONCE(buf->cb.head);
+ tail = buf->cb.tail;
+
+ count = CIRC_CNT(head, tail, buf->slots);
+ rt = scnprintf(&str_buf[0], buf_len, "%s (%d):\n", config->name, count);
+
+ if (count == 0)
+ goto maxfg_show_captured_buffer_exit;
+
+ to_end = CIRC_CNT_TO_END(head, tail, buf->slots);
+
+ for (idx = 0; idx < to_end && rt < buf_len; idx++) {
+ reg_val = (u16 *)&buf->cb.buf[(tail + idx) * data_size];
+ rt += maxfg_capture_to_cstr(config, reg_val, &str_buf[rt],
+ buf_len - rt);
+ rt += scnprintf(&str_buf[rt], buf_len - rt, "\n");
+ }
+
+ count -= idx;
+
+ for (idx = 0; idx < count && rt < buf_len; idx++) {
+ reg_val = (u16 *)&buf->cb.buf[idx * data_size];
+ rt += maxfg_capture_to_cstr(config, reg_val, &str_buf[rt],
+ buf_len - rt);
+ rt += scnprintf(&str_buf[rt], buf_len - rt, "\n");
+ }
+
+maxfg_show_captured_buffer_exit:
+ mutex_unlock(&buf->cb_rd_lock);
+ return rt;
+}
+
+/*
+ * data in prev_val follows the order of fg_learning_param[]
+ * prev_val[0]: fcnom
+ * prev_val[1]: dpacc
+ * prev_val[2]: dqacc
+ * prev_val[7]: fstat
+ */
+bool maxfg_ce_relaxed(struct max17x0x_regmap *regmap, const u16 relax_mask,
+ const u16 *prev_val)
+{
+ u16 fstat, fcnom, dpacc, dqacc;
+ int ret;
+
+ ret = max17x0x_reg_read(regmap, MAXFG_TAG_fstat, &fstat);
+ if (ret < 0)
+ return false;
+
+ ret = max17x0x_reg_read(regmap, MAXFG_TAG_fcnom, &fcnom);
+ if (ret < 0)
+ return false;
+
+ ret = max17x0x_reg_read(regmap, MAXFG_TAG_dpacc, &dpacc);
+ if (ret < 0)
+ return false;
+
+ ret = max17x0x_reg_read(regmap, MAXFG_TAG_dqacc, &dqacc);
+ if (ret < 0)
+ return false;
+
+ /*
+ * log when relaxed state changes, when fcnom, dpacc, dqacc change
+ * TODO: log only when dpacc, dqacc or fcnom change and simply
+ * count the relaxation event otherwise.
+ */
+ return (fstat & relax_mask) != (prev_val[7] & relax_mask) ||
+ dpacc != prev_val[1] || dqacc != prev_val[2] ||
+ fcnom != prev_val[0];
+}
diff --git a/maxfg_logging.h b/maxfg_logging.h
new file mode 100644
index 0000000..ac1e63b
--- /dev/null
+++ b/maxfg_logging.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2024 Google LLC
+ */
+
+#ifndef MAXFG_LOGGING_H_
+#define MAXFG_LOGGING_H_
+
+#include <linux/circ_buf.h>
+
+#define MAX_FG_LEARN_PARAM_MAX_HIST 32
+#define MAX_FG_CAPTURE_CONFIG_NAME_MAX 32
+
+struct maxfg_capture_regs {
+ struct max17x0x_regmap *regmap;
+ const enum max17x0x_reg_tags *tag;
+ int reg_cnt;
+};
+
+/* a configuration can simply be a list of tags, a regmap and a name */
+struct maxfg_capture_config {
+ char name[MAX_FG_CAPTURE_CONFIG_NAME_MAX];
+ struct maxfg_capture_regs normal;
+ struct maxfg_capture_regs debug;
+ int data_size;
+};
+
+/* only one configuration now */
+struct maxfg_capture_buf {
+ struct maxfg_capture_config config;
+
+ int slots;
+ struct circ_buf cb;
+ void *latest_entry;
+ struct mutex cb_wr_lock;
+ struct mutex cb_rd_lock;
+};
+
+void maxfg_init_fg_learn_capture_config(struct maxfg_capture_config *config,
+ struct max17x0x_regmap *regmap,
+ struct max17x0x_regmap *debug_regmap);
+
+int maxfg_alloc_capture_buf(struct maxfg_capture_buf *buf, int slots);
+void maxfg_clear_capture_buf(struct maxfg_capture_buf *buf);
+void maxfg_free_capture_buf(struct maxfg_capture_buf *buf);
+
+int maxfg_capture_registers(struct maxfg_capture_buf *buf);
+
+int maxfg_show_captured_buffer(struct maxfg_capture_buf *buf,
+ char *str_buf, int buf_len);
+int maxfg_capture_to_cstr(struct maxfg_capture_config *config, u16 *reg_val,
+ char *str_buf, int buf_len);
+
+bool maxfg_ce_relaxed(struct max17x0x_regmap *regmap, const u16 relax_mask,
+ const u16 *prev_val);
+
+#endif \ No newline at end of file
diff --git a/p9221_charger.c b/p9221_charger.c
index 72cee14..1a208f6 100644
--- a/p9221_charger.c
+++ b/p9221_charger.c
@@ -6897,6 +6897,7 @@ static int p9221_charger_probe(struct i2c_client *client,
}
i2c_set_clientdata(client, charger);
charger->dev = &client->dev;
+ charger->dev->init_name = "i2c-p9221";
charger->client = client;
charger->pdata = pdata;
charger->resume_complete = true;