diff --git a/drivers/mfd/rk8xx-core.c b/drivers/mfd/rk8xx-core.c index d90c45cd5..c2707fd00 100644 --- a/drivers/mfd/rk8xx-core.c +++ b/drivers/mfd/rk8xx-core.c @@ -515,6 +515,14 @@ static const struct regmap_irq_chip rk81 .init_ack_masked = true, }; +static int rk808_pm_power_off_prepare(struct sys_off_data *data) +{ + struct rk808 *rk808 = data->cb_data; + regmap_update_bits(rk808->regmap, RK808_LDO_EN_REG, 0x08, 0x00); + + return NOTIFY_DONE; +} + static int rk808_power_off(struct sys_off_data *data) { struct rk808 *rk808 = data->cb_data; @@ -708,6 +708,16 @@ static int rk8xx_probe(struct device *dev } } + ret = devm_register_sys_off_handler(dev, + SYS_OFF_MODE_POWER_OFF_PREPARE, + SYS_OFF_PRIO_DEFAULT, + rk808_pm_power_off_prepare, + rk808); + if (ret) { + dev_warn(dev, "failed to register sys-off handler: %d\n", ret); + return ret; + } + return 0; } EXPORT_SYMBOL_GPL(rk8xx_probe); diff --git a/drivers/power/supply/axp20x_ac_power.c b/drivers/power/supply/axp20x_ac_power.c index ac360016b..c11024f3c 100644 --- a/drivers/power/supply/axp20x_ac_power.c +++ b/drivers/power/supply/axp20x_ac_power.c @@ -53,6 +53,9 @@ static irqreturn_t axp20x_ac_power_irq(int irq, void *devid) { struct axp20x_ac_power *power = devid; + regmap_update_bits(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, 0x03, 0x00); + regmap_update_bits(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, 0x03, 0x03); + power_supply_changed(power->supply); return IRQ_HANDLED; diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c index e84b6e4da..b62c361e2 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,9 @@ struct axp20x_batt_ps { struct iio_channel *batt_v; /* Maximum constant charge current */ unsigned int max_ccc; + int energy_full_design; + int current_now; + int voltage_now; const struct axp_data *data; }; @@ -276,6 +283,7 @@ static int axp20x_battery_get_prop(struc /* IIO framework gives mA but Power Supply framework gives uA */ val->intval *= 1000; + axp20x_batt->current_now = val->intval; break; case POWER_SUPPLY_PROP_CAPACITY: @@ -324,8 +332,60 @@ static int axp20x_battery_get_prop(struc /* IIO framework gives mV but Power Supply framework gives uV */ val->intval *= 1000; + axp20x_batt->voltage_now = val->intval; break; + 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, + ®); + if (ret) + return ret; + + if (!(reg & AXP20X_PWR_OP_BATT_PRESENT)) { + val->intval = 0; + return 0; + } + + if(psp == POWER_SUPPLY_PROP_ENERGY_FULL) { + // TODO + val->intval = axp20x_batt->energy_full_design; + return 0; + } + + if(psp == POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN) { + val->intval = axp20x_batt->energy_full_design; + return 0; + } + + ret = regmap_read(axp20x_batt->regmap, AXP20X_FG_RES, ®); + if (ret) + return ret; + + if (axp20x_batt->data->has_fg_valid && !(reg & AXP22X_FG_VALID)) + return -EINVAL; + + val1 = reg & AXP209_FG_PERCENT; + val1 = max(min(val1, 100), 0); + val->intval = (val1 * ((long long int)axp20x_batt->energy_full_design)) / 100; + break; + + 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; + + case POWER_SUPPLY_PROP_POWER_NOW: + val->intval = (axp20x_batt->voltage_now / 10000) * axp20x_batt->current_now; + val->intval = val->intval / 100; // uW + break; + default: return -EINVAL; } @@ -454,6 +514,7 @@ static int axp20x_battery_set_prop(struc 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: @@ -468,6 +529,16 @@ static int axp20x_battery_set_prop(struc 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: @@ -497,6 +568,11 @@ static enum power_supply_property axp20x POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_ENERGY_FULL, + POWER_SUPPLY_PROP_ENERGY_NOW, + POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, + POWER_SUPPLY_PROP_CALIBRATE, + POWER_SUPPLY_PROP_POWER_NOW, }; static int axp20x_battery_prop_writeable(struct power_supply *psy, @@ -506,7 +582,8 @@ static int axp20x_battery_prop_writeable 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 = { @@ -616,6 +693,7 @@ static int axp20x_power_probe(struct pla 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)) @@ -633,7 +711,21 @@ static int axp20x_power_probe(struct pla axp20x_batt->max_ccc = ccc; axp20x_set_constant_charge_current(axp20x_batt, ccc); } - } + + axp20x_batt->energy_full_design = info->energy_full_design_uwh; + // tell pmic about our battery + if (cfd) { + // [14:8], [7:0], cfd = Value * 1.456mAh + cfd = cfd / 1456; + regmap_update_bits(axp20x_batt->regmap, AXP288_FG_DES_CAP0_REG, 0xff, cfd & 0xff); + regmap_update_bits(axp20x_batt->regmap, AXP288_FG_DES_CAP1_REG, 0xff, BIT(7) | ((cfd >> 8) & 0xff)); + } else { + dev_warn(axp20x_batt->dev, "charge full design is not set"); + } + } else { + axp20x_batt->energy_full_design = 8000000; + dev_warn(axp20x_batt->dev, "energy full design is not set, default to %d\n", axp20x_batt->energy_full_design); + } /* * Update max CCC to a valid value if battery info is present or set it @@ -642,6 +734,12 @@ static int axp20x_power_probe(struct pla axp20x_get_constant_charge_current(axp20x_batt, &axp20x_batt->max_ccc); + regmap_update_bits(axp20x_batt->regmap, AXP20X_VBUS_IPSOUT_MGMT, 0x03, 0x03); + regmap_update_bits(axp20x_batt->regmap, AXP20X_OFF_CTRL, 0x08, 0x08); + regmap_update_bits(axp20x_batt->regmap, AXP20X_CHRG_CTRL2, 0x30, 0x20); + regmap_update_bits(axp20x_batt->regmap, AXP20X_PEK_KEY, 0x0f, 0x0b); + // regmap_update_bits(axp20x_batt->regmap, AXP20X_GPIO0_CTRL, 0x07, 0x00); + return 0; }