From 64524afd6b6598addcf77c92832abdcd508fc8b4 Mon Sep 17 00:00:00 2001
From: Potato <nikko@faint.day>
Date: Sat, 4 Nov 2023 19:02:17 +0800
Subject: [PATCH 6/6] power: axp20x_battery: implement calibration

- add sysfs interface to initiate calibration
- capacity/energy is read from PMU rather than using a predefined value
- energy_full_designed is loaded from either
  energy-full-design-microwatt-hours or charge-full-design-microamp-hours
  - default is 8Wh
---
 drivers/power/supply/axp20x_battery.c | 78 +++++++++++++++++++++++----
 1 file changed, 68 insertions(+), 10 deletions(-)

diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c
index 53b53137a480..609d240305a2 100644
--- a/drivers/power/supply/axp20x_battery.c
+++ b/drivers/power/supply/axp20x_battery.c
@@ -56,6 +56,10 @@
 
 #define AXP20X_V_OFF_MASK		GENMASK(2, 0)
 
+#define AXP228_FULL_CAPACITY_CALIBRATE_EN BIT(5)
+#define AXP228_CAPACITY_CALIBRATE BIT(4)
+#define AXP228_CALIBRATE_MASK (BIT(4) | BIT(5))
+
 struct axp20x_batt_ps;
 
 struct axp_data {
@@ -75,6 +79,7 @@ struct axp20x_batt_ps {
 	struct iio_channel *batt_v;
 	/* Maximum constant charge current */
 	unsigned int max_ccc;
+	int energy_full_design;
 	const struct axp_data	*data;
 };
 
@@ -328,6 +333,7 @@ static int axp20x_battery_get_prop(struct power_supply *psy,
 
 	case POWER_SUPPLY_PROP_ENERGY_FULL:
 	case POWER_SUPPLY_PROP_ENERGY_NOW:
+	case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
 		/* When no battery is present, return 0 */
 		ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_OP_MODE,
 				  &reg);
@@ -339,8 +345,29 @@ static int axp20x_battery_get_prop(struct power_supply *psy,
 			return 0;
 		}
 
+		if(psp == POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN) {
+			val->intval = axp20x_batt->energy_full_design;
+			return 0;
+		}
+
+		/* read capacity from PMU */
+		val->intval = axp20x_batt->energy_full_design;
+
+		ret = regmap_read(axp20x_batt->regmap, AXP288_FG_DES_CAP0_REG, &reg); // [7:0]
+		if (ret)
+			return ret;
+
+		val1 = reg;
+
+		ret = regmap_read(axp20x_batt->regmap, AXP288_FG_DES_CAP1_REG, &reg); // [14:8]
+		if (ret)
+			return ret;
+
+		val1 |= (reg & 0x3F) << 8; // capacity report from pmu, unit 1.456mAh
+		val1 = val1 * 1456 * 36 / 10; // uWh
+
 		if(psp == POWER_SUPPLY_PROP_ENERGY_FULL) {
-			val->intval = 8000000;
+			val->intval = val1;
 			return 0;
 		}
 
@@ -351,15 +378,18 @@ static int axp20x_battery_get_prop(struct power_supply *psy,
 		if (axp20x_batt->data->has_fg_valid && !(reg & AXP22X_FG_VALID))
 			return -EINVAL;
 
-		val1 = reg & AXP209_FG_PERCENT;
-		if (val1 > 90)
-			val1= 80;
-		else if (val1 < 10)
-			val1 = 0;
-		else
-			val1 -= 10;
+		reg = reg & AXP209_FG_PERCENT;
+		reg = max(min(reg, 100), 0);
+		val->intval = (reg * ((long long int)(val1))) / 100;
+		break;
 
-		val->intval = val1 * 100000;
+	case POWER_SUPPLY_PROP_CALIBRATE:
+		/* report both calibrate enable flag and calibration status */
+		ret = regmap_read(axp20x_batt->regmap, AXP20X_CC_CTRL, &reg);
+		if (ret)
+			return ret;
+		val1 = reg & AXP228_CALIBRATE_MASK;
+		val->intval = val1;
 		break;
 
 	default:
@@ -490,6 +520,7 @@ static int axp20x_battery_set_prop(struct power_supply *psy,
 				   const union power_supply_propval *val)
 {
 	struct axp20x_batt_ps *axp20x_batt = power_supply_get_drvdata(psy);
+	int val1;
 
 	switch (psp) {
 	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
@@ -504,6 +535,15 @@ static int axp20x_battery_set_prop(struct power_supply *psy,
 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
 		return axp20x_set_max_constant_charge_current(axp20x_batt,
 							      val->intval);
+	case POWER_SUPPLY_PROP_CALIBRATE:
+		if (val->intval) {
+			// enable calibrate
+			val1 = AXP228_FULL_CAPACITY_CALIBRATE_EN | AXP228_CAPACITY_CALIBRATE;
+		} else {
+			// disable calibrate
+			val1 = 0;
+		}
+		return regmap_update_bits(axp20x_batt->regmap, AXP20X_CC_CTRL, AXP228_CALIBRATE_MASK, val1);
 	case POWER_SUPPLY_PROP_STATUS:
 		switch (val->intval) {
 		case POWER_SUPPLY_STATUS_CHARGING:
@@ -535,6 +575,8 @@ static enum power_supply_property axp20x_battery_props[] = {
 	POWER_SUPPLY_PROP_CAPACITY,
 	POWER_SUPPLY_PROP_ENERGY_FULL,
 	POWER_SUPPLY_PROP_ENERGY_NOW,
+	POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
+	POWER_SUPPLY_PROP_CALIBRATE,
 };
 
 static int axp20x_battery_prop_writeable(struct power_supply *psy,
@@ -544,7 +586,8 @@ static int axp20x_battery_prop_writeable(struct power_supply *psy,
 	       psp == POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN ||
 	       psp == POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN ||
 	       psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT ||
-	       psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX;
+	       psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX ||
+	       psp == POWER_SUPPLY_PROP_CALIBRATE;
 }
 
 static const struct power_supply_desc axp20x_batt_ps_desc = {
@@ -654,6 +697,7 @@ static int axp20x_power_probe(struct platform_device *pdev)
 	if (!power_supply_get_battery_info(axp20x_batt->batt, &info)) {
 		int vmin = info->voltage_min_design_uv;
 		int ccc = info->constant_charge_current_max_ua;
+		int cfd = info->charge_full_design_uah;
 
 		if (vmin > 0 && axp20x_set_voltage_min_design(axp20x_batt,
 							      vmin))
@@ -671,6 +715,20 @@ static int axp20x_power_probe(struct platform_device *pdev)
 			axp20x_batt->max_ccc = ccc;
 			axp20x_set_constant_charge_current(axp20x_batt, ccc);
 		}
+
+		if (info->energy_full_design_uwh != info->charge_full_design_uah) {
+			if (info->energy_full_design_uwh == -EINVAL)
+				dev_warn(axp20x_batt->dev, "missing battery:energy-full-design-microwatt-hours\n");
+			else if (info->charge_full_design_uah == -EINVAL)
+				dev_warn(axp20x_batt->dev, "missing battery:charge-full-design-microamp-hours\n");
+		}
+
+		if (info->energy_full_design_uwh != -EINVAL)
+			axp20x_batt->energy_full_design = info->energy_full_design_uwh;
+		else if (info->charge_full_design_uah != -EINVAL)
+			axp20x_batt->energy_full_design = cfd / 10 * 36; // assume standard voltage 3.6V
+		else
+			axp20x_batt->energy_full_design = 8000000; // default capacity, 8Wh
 	}
 
 	/*
-- 
2.43.0

