From 64524afd6b6598addcf77c92832abdcd508fc8b4 Mon Sep 17 00:00:00 2001 From: Potato 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, ®); @@ -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, ®); // [7:0] + if (ret) + return ret; + + val1 = reg; + + ret = regmap_read(axp20x_batt->regmap, AXP288_FG_DES_CAP1_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, ®); + 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