From 00306aad27c9efc2ee7bbeea91f48abae6c2aa40 Mon Sep 17 00:00:00 2001
From: Sjoerd Simons <sjoerd.simons@collabora.co.uk>
Date: Tue, 30 Oct 2018 20:59:14 +0100
Subject: [PATCH] WIP

---
 drivers/power/supply/bq27xxx_battery.c | 181 ++++++++++++++++++-------
 1 file changed, 130 insertions(+), 51 deletions(-)

diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
index f2464ebeb2ae..94f7688fe749 100644
--- a/drivers/power/supply/bq27xxx_battery.c
+++ b/drivers/power/supply/bq27xxx_battery.c
@@ -884,6 +884,14 @@ struct bq27xxx_dm_buf {
 	bool has_data, dirty;
 };
 
+/**
+ * Struct holding ding a cache of read dm_bufs
+ */
+#define DM_BUF_CACHE_SIZE 4;
+struct bq27xxx_dm_buf_cache {
+	struct bq27xxx_dm_buf blocks[DM_BUF_CACHE_SIZE];
+}
+
 #define BQ27XXX_DM_BUF(di, i) { \
 	.class = (di)->dm_regs[i].subclass_id, \
 	.block = (di)->dm_regs[i].offset / BQ27XXX_DM_SZ, \
@@ -1121,33 +1129,35 @@ static int bq27xxx_battery_read_dm_block(struct bq27xxx_device_info *di,
 	return ret;
 }
 
-static void bq27xxx_battery_update_dm_block(struct bq27xxx_device_info *di,
-					    struct bq27xxx_dm_buf *buf,
-					    enum bq27xxx_dm_reg_id reg_id,
-					    unsigned int val)
+static int bq27xxx_battery_update_dm_block(struct bq27xxx_device_info *di,
+					   struct bq27xxx_dm_buf_cache *cache,
+					   enum bq27xxx_dm_reg_id reg_id,
+					   unsigned int val)
 {
 	struct bq27xxx_dm_reg *reg = &di->dm_regs[reg_id];
 	const char *str = bq27xxx_dm_reg_name[reg_id];
+	struct bq27xxx_dm_buf *buf = bq27xxx_battery_cache_get_dm_buf (di, cache,
+								       reg_id);
 	u16 *prev = bq27xxx_dm_reg_ptr(buf, reg);
 
 	if (prev == NULL) {
 		dev_warn(di->dev, "buffer does not match %s dm spec\n", str);
-		return;
+		return -1;
 	}
 
 	if (reg->bytes != 2) {
 		dev_warn(di->dev, "%s dm spec has unsupported byte size\n", str);
-		return;
+		return -1;
 	}
 
 	if (!buf->has_data) {
 		dev_warn(di->dev, "%s buffer has no data\n", str);
-		return;
+		return -1;
 	}
 
 	if (be16_to_cpup(prev) == val) {
 		dev_info(di->dev, "%s has %u\n", str, val);
-		return;
+		return 0;
 	}
 	dev_info(di->dev, "%s had %u\n", str, be16_to_cpup(prev));
 
@@ -1164,15 +1174,77 @@ static void bq27xxx_battery_update_dm_block(struct bq27xxx_device_info *di,
 			 "for flash/NVM data memory"
 #endif
 			 "\n", str, be16_to_cpup(prev), val);
-		return;
+		return 0;
 	}
 
 	dev_info(di->dev, "update %s to %u\n", str, val);
 
 	*prev = cpu_to_be16(val);
 	buf->dirty = true;
+	return 1;
+}
+
+static struct bq27xxx_dm_buf *bq27xxx_battery_cache_get_dm_buf (struct bq27xxx_device_info *di,
+							        struct bq27xxx_dm_buf_cache *cache,
+							        int regindex)
+{
+	u8 class = di->dm_regs[regindex].subclass_id;
+	u8 block = di->dm_regs[regindex].offset / BQ27XXX_DM_SZ,;
+	int i;
+	struct bq27xxx_dm_buf *buf;
+
+	for (i = 0; i < DM_BUF_CACHE_SIZE; i++) {
+		buf = cache->blocks + i;
+		if (buf->class == class && buf->block == block) {
+			return buf;
+		}
+
+		if (buf->class == 0) {
+			/* unallocated */
+			buf->class = class;
+			buf->block = block;
+			bq27xxx_battery_read_dm_block(di, buf);
+			return buf;
+		}
+	}
+
+	BUG_ON("ran out of cache space");
+	return NULL;
+}
+
+/*
+ * Flush cached blocked back to storage if needed; return negative on error,
+ * number of blocks written back on success
+ */
+static int bq27xxx_dm_buf *bq27xxx_battery_cache_flush (struct bq27xxx_device_info *di,
+							struct bq27xxx_dm_buf_cache *cache)
+{
+	int i;
+	int ret = 0;
+
+	for (i = 0; i < DM_BUF_CACHE_SIZE; i++) {
+		struct bq27xxx_dm_buf *buf = cache->blocks + i;
+
+		if (buf->class == 0)
+			break;
+
+		if (buf->dirty) {
+			int r;
+
+			r = bq27xxx_battery_write_dm_block (di, buf);
+
+			if (r < 0) {
+				return r;
+			}
+			ret++;
+		}
+
+	}
+
+	return ret;
 }
 
+
 static int bq27xxx_battery_cfgupdate_priv(struct bq27xxx_device_info *di, bool active)
 {
 	const int limit = 100;
@@ -1284,13 +1356,13 @@ static int bq27xxx_battery_write_dm_block(struct bq27xxx_device_info *di,
 }
 
 static int bq27xxx_battery_repair_bd_block (struct bq27xxx_device_info *di,
-                                             struct bq27xxx_dm_buf *bd)
+                                             struct bq27xxx_dm_buf_cache *cache)
 {
 	const char *model = "bq27532";
 	int i;
 	int ret;
 	int valid = true;
-	struct bq27xxx_dm_buf cc = BQ27XXX_DM_BUF(di, BQ27XXX_DM_CYCLE_COUNT);
+	struct bq27xxx_dm_buf *bd, *cc;
 	u8 defaults[] = { 0x3, 0x84, /* CC threshold */
 			 0x3, 0xe8, /* Design capacity*/
 			 0x1, /* Des Energy Scale */
@@ -1299,11 +1371,12 @@ static int bq27xxx_battery_repair_bd_block (struct bq27xxx_device_info *di,
 			 'b', 'q', '2', '7', '5', '3', '2', '\0',
 			 0x0 };
 
-	ret = bq27xxx_battery_read_dm_block(di, bd);
 
-	if (ret < 0) {
-		dev_warn(di->dev, "failed to read bd block\n");
-		return ret;
+	bd = bq27xxx_battery_cache_get_dm_buf (di, cache,
+					       BQ27XXX_DM_DESIGN_CAPACITY)
+	if (bd == NULL) {
+		dev_warn(di->dev, "failed to get bd block\n");
+		return -EINVAL;
 	}
 
 	for (i = 0; model[i] != '\0'; i++) {
@@ -1329,17 +1402,14 @@ static int bq27xxx_battery_repair_bd_block (struct bq27xxx_device_info *di,
 	}
 
 	memcpy (bd->data, defaults, ARRAY_SIZE (defaults));
-
 	bd->dirty = true;
 
-	bq27xxx_battery_write_dm_block(di,bd);
-
-	dev_info(di->dev, "Set cycle count to 0");
-	bq27xxx_battery_read_dm_block(di, &cc);
-	bq27xxx_battery_update_dm_block(di, &cc,
+	dev_info(di->dev, "Set cycle count to 0 after bd repair");
+	cc = bq27xxx_battery_cache_get_dm_buf (di, cache,
+					       BQ27XXX_DM_CYCLE_COUNT);
+	bq27xxx_battery_update_dm_block(di, cc,
 					BQ27XXX_DM_CYCLE_COUNT,
 					0);
-	bq27xxx_battery_write_dm_block(di, &cc);
 	return 1;
 }
 
@@ -1347,53 +1417,62 @@ static int bq27xxx_battery_repair_bd_block (struct bq27xxx_device_info *di,
 static void bq27xxx_battery_set_config(struct bq27xxx_device_info *di,
 				       struct power_supply_battery_info *info)
 {
-	struct bq27xxx_dm_buf bd = BQ27XXX_DM_BUF(di, BQ27XXX_DM_DESIGN_CAPACITY);
-	struct bq27xxx_dm_buf bt = BQ27XXX_DM_BUF(di, BQ27XXX_DM_TERMINATE_VOLTAGE);
+	struct bq27xxx_dm_buf_cache cache;
 	bool updated;
 	int repaired;
 
 	if (bq27xxx_battery_unseal(di) < 0)
 		return;
 
-	repaired = bq27xxx_battery_repair_bd_block(di, &bd);
+	repaired = bq27xxx_battery_repair_bd_block(di, &cache);
+
+	if (info->charge_full_design_uah != -EINVAL) {
+		struct bq27xxx_dm_buf *bd;
+		int updated;
+
+		bd = bq27xxx_battery_cache_get_dm_buf (di, &cache,
+						       BQ27XXX_DM_DESIGN_CAPACITY)
+		
+		updated = bq27xxx_battery_update_dm_block(di, bd,
+							  BQ27XXX_DM_DESIGN_CAPACITY,
+							  info->charge_full_design_uah / 1000);
+		/* Also reset Qmax 0 */
+		if (updated) {
+			struct bq27xxx_dm_buf *qmax;
+			qmax = bq27xxx_battery_cache_get_dm_buf (di, &cache,
+								 BQ27XXX_DM_DESIGN_CAPACITY)
+			bq27xxx_battery_update_dm_block(di, &qmax,
+							BQ27XXX_DM_QMAX0,
+							info->charge_full_design_uah / 1000);
+		}
 
-	if (info->charge_full_design_uah != -EINVAL &&
-	    info->energy_full_design_uwh != -EINVAL) {
-		struct bq27xxx_dm_buf qmax = BQ27XXX_DM_BUF(di, BQ27XXX_DM_QMAX0);
+		/* TODO update cycle threshold! to 90 of full design ?*/
+		/* Reset cycle count ? */
+	}
 
-		bq27xxx_battery_read_dm_block(di, &bd);
-		bq27xxx_battery_read_dm_block(di, &qmax);
+	if (info->energy_full_design_uwh != -EINVAL
+	    && di->dm_regs[BQ27XXX_DM_DESIGN_ENERGY].subclass_id != INVALID_SUBCLASS_ID) {
+		struct bq27xxx_dm_buf *be;
 
-		/* assume design energy & capacity are in same block */
+		be = bq27xxx_battery_cache_get_dm_buf (di, &cache,
+						       BQ27XXX_DM_DESIGN_ENERGY);
 		bq27xxx_battery_update_dm_block(di, &bd,
-					BQ27XXX_DM_DESIGN_CAPACITY,
-					info->charge_full_design_uah / 1000);
-		bq27xxx_battery_update_dm_block(di, &qmax,
-					BQ27XXX_DM_QMAX0,
-					info->charge_full_design_uah / 1000);
-		bq27xxx_battery_write_dm_block(di, &qmax);
-		if (di->dm_regs[BQ27XXX_DM_DESIGN_ENERGY].subclass_id != INVALID_SUBCLASS_ID)
-			bq27xxx_battery_update_dm_block(di, &bd,
-							BQ27XXX_DM_DESIGN_ENERGY,
-							info->energy_full_design_uwh / 1000);
+						BQ27XXX_DM_DESIGN_ENERGY,
+						info->energy_full_design_uwh / 1000);
 	}
 
 	if (info->voltage_min_design_uv != -EINVAL) {
-		bool same = bd.class == bt.class && bd.block == bt.block;
-		if (!same)
-			bq27xxx_battery_read_dm_block(di, &bt);
-		bq27xxx_battery_update_dm_block(di, same ? &bd : &bt,
+		struct bq27xxx_dm_buf *bt;
+		bt = bq27xxx_battery_cache_get_dm_buf (di, &cache,
+						       BQ27XXX_DM_TERMINATE_VOLTAGE);
+		bq27xxx_battery_update_dm_block(di, &bt,
 					BQ27XXX_DM_TERMINATE_VOLTAGE,
 					info->voltage_min_design_uv / 1000);
 	}
 
-	updated = repaired || bd.dirty || bt.dirty;
-
-	bq27xxx_battery_write_dm_block(di, &bd);
-	bq27xxx_battery_write_dm_block(di, &bt);
-
+        updated = bq27xxx_battery_cache_flush (di, &cache)
 
-	if (updated && !(di->opts & BQ27XXX_O_CFGUP)) {
+	if (updated > 0 && !(di->opts & BQ27XXX_O_CFGUP)) {
 		dev_info(di->dev, "Resetting due to update options");
 		bq27xxx_write(di, BQ27XXX_REG_CTRL, BQ27XXX_RESET, false);
 		BQ27XXX_MSLEEP(300); /* reset time is not documented */
-- 
GitLab