/* * Battery power supply driver for X-Powers AXP20X and AXP22X PMICs * * Copyright 2016 Free Electrons NextThing Co. * Quentin Schulz <quentin.schulz@free-electrons.com> * * This driver is based on a previous upstreaming attempt by: * Bruno Prémont <bonbons@linux-vserver.org> * * This file is subject to the terms and conditions of the GNU General * Public License. See the file "COPYING" in the main directory of this * archive for more details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details.
*/
staticint axp20x_battery_get_prop(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val)
{ struct axp20x_batt_ps *axp20x_batt = power_supply_get_drvdata(psy); int ret = 0, reg, val1;
switch (psp) { case POWER_SUPPLY_PROP_PRESENT: case POWER_SUPPLY_PROP_ONLINE:
ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_OP_MODE,
®); if (ret) return ret;
ret = iio_read_channel_processed(axp20x_batt->batt_dischrg_i,
&val1); if (ret) return ret;
if (val1) {
val->intval = POWER_SUPPLY_STATUS_DISCHARGING; return 0;
}
ret = regmap_read(axp20x_batt->regmap, AXP20X_FG_RES, &val1); if (ret) return ret;
/* * Fuel Gauge data takes 7 bits but the stored value seems to be * directly the raw percentage without any scaling to 7 bits.
*/ if ((val1 & AXP209_FG_PERCENT) == 100)
val->intval = POWER_SUPPLY_STATUS_FULL; else
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; break;
case POWER_SUPPLY_PROP_HEALTH:
ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_OP_MODE,
&val1); if (ret) return ret;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
ret = axp20x_get_constant_charge_current(axp20x_batt,
&val->intval); if (ret) return ret; break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
val->intval = axp20x_batt->max_ccc; break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_INPUT_STATUS,
®); if (ret) return ret;
/* IIO framework gives mA but Power Supply framework gives uA */ if (reg & AXP20X_PWR_STATUS_BAT_CHARGING) {
ret = iio_read_channel_processed_scale(axp20x_batt->batt_chrg_i,
&val->intval, 1000);
} else {
ret = iio_read_channel_processed_scale(axp20x_batt->batt_dischrg_i,
&val1, 1000);
val->intval = -val1;
} if (ret) return ret;
break;
case POWER_SUPPLY_PROP_CAPACITY: /* When no battery is present, return capacity is 100% */
ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_OP_MODE,
®); if (ret) return ret;
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;
/* * Fuel Gauge data takes 7 bits but the stored value seems to be * directly the raw percentage without any scaling to 7 bits.
*/
val->intval = reg & AXP209_FG_PERCENT; break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX: return axp20x_batt->data->get_max_voltage(axp20x_batt,
&val->intval);
case POWER_SUPPLY_PROP_VOLTAGE_MIN:
ret = regmap_read(axp20x_batt->regmap, AXP20X_V_OFF, ®); if (ret) return ret;
case POWER_SUPPLY_PROP_VOLTAGE_NOW: /* IIO framework gives mV but Power Supply framework gives uV */
ret = iio_read_channel_processed_scale(axp20x_batt->batt_v,
&val->intval, 1000); if (ret) return ret;
break;
default: return -EINVAL;
}
return 0;
}
staticint axp717_battery_get_prop(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val)
{ struct axp20x_batt_ps *axp20x_batt = power_supply_get_drvdata(psy); int ret = 0, reg;
switch (psp) { case POWER_SUPPLY_PROP_PRESENT: case POWER_SUPPLY_PROP_ONLINE:
ret = regmap_read(axp20x_batt->regmap, AXP717_ON_INDICATE,
®); if (ret) return ret;
/* * If a fault is detected it must also be cleared; if the * condition persists it should reappear. A restart was not * sufficient to clear the bit in testing despite the register * listed as POR.
*/ case POWER_SUPPLY_PROP_HEALTH:
ret = regmap_read(axp20x_batt->regmap, AXP717_PMU_FAULT,
®); if (ret) return ret;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
ret = axp717_get_constant_charge_current(axp20x_batt,
&val->intval); if (ret) return ret; return 0;
case POWER_SUPPLY_PROP_CURRENT_NOW: /* * The offset of this value is currently unknown and is * not documented in the datasheet. Based on * observation it's assumed to be somewhere around * 450ma. I will leave the value raw for now. Note that * IIO framework gives mA but Power Supply framework * gives uA.
*/
ret = iio_read_channel_processed_scale(axp20x_batt->batt_chrg_i,
&val->intval, 1000); if (ret) return ret;
return 0;
case POWER_SUPPLY_PROP_CAPACITY:
ret = regmap_read(axp20x_batt->regmap, AXP717_ON_INDICATE,
®); if (ret) return ret;
if (!FIELD_GET(AXP717_PWR_OP_BATT_PRESENT, reg)) return -ENODEV;
ret = regmap_read(axp20x_batt->regmap,
AXP717_BATT_PERCENT_DATA, ®); if (ret) return ret;
/* * Fuel Gauge data takes 7 bits but the stored value seems to be * directly the raw percentage without any scaling to 7 bits.
*/
val->intval = reg & AXP209_FG_PERCENT; return 0;
case POWER_SUPPLY_PROP_VOLTAGE_MAX: return axp20x_batt->data->get_max_voltage(axp20x_batt,
&val->intval);
case POWER_SUPPLY_PROP_VOLTAGE_MIN:
ret = regmap_read(axp20x_batt->regmap,
AXP717_VSYS_V_POWEROFF, ®); if (ret) return ret;
case POWER_SUPPLY_PROP_VOLTAGE_NOW: /* IIO framework gives mV but Power Supply framework gives uV */
ret = iio_read_channel_processed_scale(axp20x_batt->batt_v,
&val->intval, 1000); if (ret) return ret;
return 0;
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
ret = regmap_read(axp20x_batt->regmap,
AXP717_ITERM_CHG_SET, ®); if (ret) return ret;
staticint axp22x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt, int val)
{ switch (val) { case 4100000:
val = AXP20X_CHRG_CTRL1_TGT_4_1V; break;
case 4200000:
val = AXP20X_CHRG_CTRL1_TGT_4_2V; break;
default: /* * AXP20x max voltage can be set to 4.36V and AXP22X max voltage * can be set to 4.22V and 4.24V, but these voltages are too * high for Lithium based batteries (AXP PMICs are supposed to * be used with these kinds of battery).
*/ return -EINVAL;
}
staticint axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt, int val)
{ switch (val) { case 4100000:
val = AXP20X_CHRG_CTRL1_TGT_4_1V; break;
case 4150000:
val = AXP20X_CHRG_CTRL1_TGT_4_15V; break;
case 4200000:
val = AXP20X_CHRG_CTRL1_TGT_4_2V; break;
default: /* * AXP20x max voltage can be set to 4.36V and AXP22X max voltage * can be set to 4.22V and 4.24V, but these voltages are too * high for Lithium based batteries (AXP PMICs are supposed to * be used with these kinds of battery).
*/ return -EINVAL;
}
if (charge_current > axp->max_ccc)
dev_warn(axp->dev, "Setting max constant charge current higher than previously defined. Note that increasing the constant charge current may damage your battery.\n"); else
lower_max = true;
axp->max_ccc = charge_current;
if (lower_max) { int current_cc;
axp20x_get_constant_charge_current(axp, ¤t_cc); if (current_cc > charge_current)
axp20x_set_constant_charge_current(axp, charge_current);
}
return 0;
} staticint axp20x_set_voltage_min_design(struct axp20x_batt_ps *axp_batt, int min_voltage)
{ int val1 = (min_voltage - 2600000) / 100000;
if (val1 < 0 || val1 > AXP20X_V_OFF_MASK) return -EINVAL;
/* * Under rare conditions an incorrectly programmed efuse for * the temp sensor on the PMIC may trigger a fault condition. * Allow users to hard-code if the ts pin is not used to work * around this problem. Note that this requires the battery * be correctly defined in the device tree with a monitored * battery node.
*/ if (axp_batt->ts_disable) {
regmap_update_bits(axp_batt->regmap,
AXP717_TS_PIN_CFG,
AXP717_TS_PIN_DISABLE,
AXP717_TS_PIN_DISABLE);
}
if (vmin > 0 && axp717_set_voltage_min_design(axp_batt, vmin))
dev_err(&pdev->dev, "couldn't set voltage_min_design\n");
if (vmax > 0 && axp717_battery_set_max_voltage(axp_batt, vmax))
dev_err(&pdev->dev, "couldn't set voltage_max_design\n");
axp717_get_constant_charge_current(axp_batt, &val);
axp_batt->max_ccc = ccc; if (ccc <= 0 || axp717_set_constant_charge_current(axp_batt, ccc)) {
dev_err(&pdev->dev, "couldn't set ccc from DT: current ccc is %d\n",
val);
}
}
ret = axp20x_batt->data->cfg_iio_chan(pdev, axp20x_batt); if (ret) return ret;
axp20x_batt->batt = devm_power_supply_register(&pdev->dev,
axp20x_batt->data->bat_ps_desc,
&psy_cfg); if (IS_ERR(axp20x_batt->batt)) {
dev_err(&pdev->dev, "failed to register power supply: %ld\n",
PTR_ERR(axp20x_batt->batt)); return PTR_ERR(axp20x_batt->batt);
}
if (!power_supply_get_battery_info(axp20x_batt->batt, &info)) {
axp20x_batt->data->set_bat_info(pdev, axp20x_batt, info);
power_supply_put_battery_info(axp20x_batt->batt, info);
}
/* * Update max CCC to a valid value if battery info is present or set it * to current register value by default.
*/
axp20x_get_constant_charge_current(axp20x_batt, &axp20x_batt->max_ccc);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.