162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Battery power supply driver for X-Powers AXP20X and AXP22X PMICs 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright 2016 Free Electrons NextThing Co. 562306a36Sopenharmony_ci * Quentin Schulz <quentin.schulz@free-electrons.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This driver is based on a previous upstreaming attempt by: 862306a36Sopenharmony_ci * Bruno Prémont <bonbons@linux-vserver.org> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General 1162306a36Sopenharmony_ci * Public License. See the file "COPYING" in the main directory of this 1262306a36Sopenharmony_ci * archive for more details. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * This program is distributed in the hope that it will be useful, 1562306a36Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 1662306a36Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1762306a36Sopenharmony_ci * GNU General Public License for more details. 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/err.h> 2162306a36Sopenharmony_ci#include <linux/interrupt.h> 2262306a36Sopenharmony_ci#include <linux/irq.h> 2362306a36Sopenharmony_ci#include <linux/module.h> 2462306a36Sopenharmony_ci#include <linux/of.h> 2562306a36Sopenharmony_ci#include <linux/platform_device.h> 2662306a36Sopenharmony_ci#include <linux/power_supply.h> 2762306a36Sopenharmony_ci#include <linux/regmap.h> 2862306a36Sopenharmony_ci#include <linux/slab.h> 2962306a36Sopenharmony_ci#include <linux/time.h> 3062306a36Sopenharmony_ci#include <linux/iio/iio.h> 3162306a36Sopenharmony_ci#include <linux/iio/consumer.h> 3262306a36Sopenharmony_ci#include <linux/mfd/axp20x.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define AXP20X_PWR_STATUS_BAT_CHARGING BIT(2) 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define AXP20X_PWR_OP_BATT_PRESENT BIT(5) 3762306a36Sopenharmony_ci#define AXP20X_PWR_OP_BATT_ACTIVATED BIT(3) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define AXP209_FG_PERCENT GENMASK(6, 0) 4062306a36Sopenharmony_ci#define AXP22X_FG_VALID BIT(7) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define AXP20X_CHRG_CTRL1_ENABLE BIT(7) 4362306a36Sopenharmony_ci#define AXP20X_CHRG_CTRL1_TGT_VOLT GENMASK(6, 5) 4462306a36Sopenharmony_ci#define AXP20X_CHRG_CTRL1_TGT_4_1V (0 << 5) 4562306a36Sopenharmony_ci#define AXP20X_CHRG_CTRL1_TGT_4_15V (1 << 5) 4662306a36Sopenharmony_ci#define AXP20X_CHRG_CTRL1_TGT_4_2V (2 << 5) 4762306a36Sopenharmony_ci#define AXP20X_CHRG_CTRL1_TGT_4_36V (3 << 5) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define AXP22X_CHRG_CTRL1_TGT_4_22V (1 << 5) 5062306a36Sopenharmony_ci#define AXP22X_CHRG_CTRL1_TGT_4_24V (3 << 5) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define AXP813_CHRG_CTRL1_TGT_4_35V (3 << 5) 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define AXP20X_CHRG_CTRL1_TGT_CURR GENMASK(3, 0) 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define AXP20X_V_OFF_MASK GENMASK(2, 0) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistruct axp20x_batt_ps; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistruct axp_data { 6162306a36Sopenharmony_ci int ccc_scale; 6262306a36Sopenharmony_ci int ccc_offset; 6362306a36Sopenharmony_ci bool has_fg_valid; 6462306a36Sopenharmony_ci int (*get_max_voltage)(struct axp20x_batt_ps *batt, int *val); 6562306a36Sopenharmony_ci int (*set_max_voltage)(struct axp20x_batt_ps *batt, int val); 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistruct axp20x_batt_ps { 6962306a36Sopenharmony_ci struct regmap *regmap; 7062306a36Sopenharmony_ci struct power_supply *batt; 7162306a36Sopenharmony_ci struct device *dev; 7262306a36Sopenharmony_ci struct iio_channel *batt_chrg_i; 7362306a36Sopenharmony_ci struct iio_channel *batt_dischrg_i; 7462306a36Sopenharmony_ci struct iio_channel *batt_v; 7562306a36Sopenharmony_ci /* Maximum constant charge current */ 7662306a36Sopenharmony_ci unsigned int max_ccc; 7762306a36Sopenharmony_ci const struct axp_data *data; 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic int axp20x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt, 8162306a36Sopenharmony_ci int *val) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci int ret, reg; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci ret = regmap_read(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, ®); 8662306a36Sopenharmony_ci if (ret) 8762306a36Sopenharmony_ci return ret; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci switch (reg & AXP20X_CHRG_CTRL1_TGT_VOLT) { 9062306a36Sopenharmony_ci case AXP20X_CHRG_CTRL1_TGT_4_1V: 9162306a36Sopenharmony_ci *val = 4100000; 9262306a36Sopenharmony_ci break; 9362306a36Sopenharmony_ci case AXP20X_CHRG_CTRL1_TGT_4_15V: 9462306a36Sopenharmony_ci *val = 4150000; 9562306a36Sopenharmony_ci break; 9662306a36Sopenharmony_ci case AXP20X_CHRG_CTRL1_TGT_4_2V: 9762306a36Sopenharmony_ci *val = 4200000; 9862306a36Sopenharmony_ci break; 9962306a36Sopenharmony_ci case AXP20X_CHRG_CTRL1_TGT_4_36V: 10062306a36Sopenharmony_ci *val = 4360000; 10162306a36Sopenharmony_ci break; 10262306a36Sopenharmony_ci default: 10362306a36Sopenharmony_ci return -EINVAL; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic int axp22x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt, 11062306a36Sopenharmony_ci int *val) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci int ret, reg; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci ret = regmap_read(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, ®); 11562306a36Sopenharmony_ci if (ret) 11662306a36Sopenharmony_ci return ret; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci switch (reg & AXP20X_CHRG_CTRL1_TGT_VOLT) { 11962306a36Sopenharmony_ci case AXP20X_CHRG_CTRL1_TGT_4_1V: 12062306a36Sopenharmony_ci *val = 4100000; 12162306a36Sopenharmony_ci break; 12262306a36Sopenharmony_ci case AXP20X_CHRG_CTRL1_TGT_4_2V: 12362306a36Sopenharmony_ci *val = 4200000; 12462306a36Sopenharmony_ci break; 12562306a36Sopenharmony_ci case AXP22X_CHRG_CTRL1_TGT_4_22V: 12662306a36Sopenharmony_ci *val = 4220000; 12762306a36Sopenharmony_ci break; 12862306a36Sopenharmony_ci case AXP22X_CHRG_CTRL1_TGT_4_24V: 12962306a36Sopenharmony_ci *val = 4240000; 13062306a36Sopenharmony_ci break; 13162306a36Sopenharmony_ci default: 13262306a36Sopenharmony_ci return -EINVAL; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return 0; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic int axp813_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt, 13962306a36Sopenharmony_ci int *val) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci int ret, reg; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci ret = regmap_read(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, ®); 14462306a36Sopenharmony_ci if (ret) 14562306a36Sopenharmony_ci return ret; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci switch (reg & AXP20X_CHRG_CTRL1_TGT_VOLT) { 14862306a36Sopenharmony_ci case AXP20X_CHRG_CTRL1_TGT_4_1V: 14962306a36Sopenharmony_ci *val = 4100000; 15062306a36Sopenharmony_ci break; 15162306a36Sopenharmony_ci case AXP20X_CHRG_CTRL1_TGT_4_15V: 15262306a36Sopenharmony_ci *val = 4150000; 15362306a36Sopenharmony_ci break; 15462306a36Sopenharmony_ci case AXP20X_CHRG_CTRL1_TGT_4_2V: 15562306a36Sopenharmony_ci *val = 4200000; 15662306a36Sopenharmony_ci break; 15762306a36Sopenharmony_ci case AXP813_CHRG_CTRL1_TGT_4_35V: 15862306a36Sopenharmony_ci *val = 4350000; 15962306a36Sopenharmony_ci break; 16062306a36Sopenharmony_ci default: 16162306a36Sopenharmony_ci return -EINVAL; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return 0; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic int axp20x_get_constant_charge_current(struct axp20x_batt_ps *axp, 16862306a36Sopenharmony_ci int *val) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci int ret; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci ret = regmap_read(axp->regmap, AXP20X_CHRG_CTRL1, val); 17362306a36Sopenharmony_ci if (ret) 17462306a36Sopenharmony_ci return ret; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci *val &= AXP20X_CHRG_CTRL1_TGT_CURR; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci *val = *val * axp->data->ccc_scale + axp->data->ccc_offset; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci return 0; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic int axp20x_battery_get_prop(struct power_supply *psy, 18462306a36Sopenharmony_ci enum power_supply_property psp, 18562306a36Sopenharmony_ci union power_supply_propval *val) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct axp20x_batt_ps *axp20x_batt = power_supply_get_drvdata(psy); 18862306a36Sopenharmony_ci int ret = 0, reg, val1; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci switch (psp) { 19162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 19262306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 19362306a36Sopenharmony_ci ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_OP_MODE, 19462306a36Sopenharmony_ci ®); 19562306a36Sopenharmony_ci if (ret) 19662306a36Sopenharmony_ci return ret; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci val->intval = !!(reg & AXP20X_PWR_OP_BATT_PRESENT); 19962306a36Sopenharmony_ci break; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 20262306a36Sopenharmony_ci ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_INPUT_STATUS, 20362306a36Sopenharmony_ci ®); 20462306a36Sopenharmony_ci if (ret) 20562306a36Sopenharmony_ci return ret; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (reg & AXP20X_PWR_STATUS_BAT_CHARGING) { 20862306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_CHARGING; 20962306a36Sopenharmony_ci return 0; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci ret = iio_read_channel_processed(axp20x_batt->batt_dischrg_i, 21362306a36Sopenharmony_ci &val1); 21462306a36Sopenharmony_ci if (ret) 21562306a36Sopenharmony_ci return ret; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (val1) { 21862306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 21962306a36Sopenharmony_ci return 0; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci ret = regmap_read(axp20x_batt->regmap, AXP20X_FG_RES, &val1); 22362306a36Sopenharmony_ci if (ret) 22462306a36Sopenharmony_ci return ret; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* 22762306a36Sopenharmony_ci * Fuel Gauge data takes 7 bits but the stored value seems to be 22862306a36Sopenharmony_ci * directly the raw percentage without any scaling to 7 bits. 22962306a36Sopenharmony_ci */ 23062306a36Sopenharmony_ci if ((val1 & AXP209_FG_PERCENT) == 100) 23162306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_FULL; 23262306a36Sopenharmony_ci else 23362306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci case POWER_SUPPLY_PROP_HEALTH: 23762306a36Sopenharmony_ci ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_OP_MODE, 23862306a36Sopenharmony_ci &val1); 23962306a36Sopenharmony_ci if (ret) 24062306a36Sopenharmony_ci return ret; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (val1 & AXP20X_PWR_OP_BATT_ACTIVATED) { 24362306a36Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_DEAD; 24462306a36Sopenharmony_ci return 0; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_GOOD; 24862306a36Sopenharmony_ci break; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: 25162306a36Sopenharmony_ci ret = axp20x_get_constant_charge_current(axp20x_batt, 25262306a36Sopenharmony_ci &val->intval); 25362306a36Sopenharmony_ci if (ret) 25462306a36Sopenharmony_ci return ret; 25562306a36Sopenharmony_ci break; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: 25862306a36Sopenharmony_ci val->intval = axp20x_batt->max_ccc; 25962306a36Sopenharmony_ci break; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_NOW: 26262306a36Sopenharmony_ci ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_INPUT_STATUS, 26362306a36Sopenharmony_ci ®); 26462306a36Sopenharmony_ci if (ret) 26562306a36Sopenharmony_ci return ret; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (reg & AXP20X_PWR_STATUS_BAT_CHARGING) { 26862306a36Sopenharmony_ci ret = iio_read_channel_processed(axp20x_batt->batt_chrg_i, &val->intval); 26962306a36Sopenharmony_ci } else { 27062306a36Sopenharmony_ci ret = iio_read_channel_processed(axp20x_batt->batt_dischrg_i, &val1); 27162306a36Sopenharmony_ci val->intval = -val1; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci if (ret) 27462306a36Sopenharmony_ci return ret; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* IIO framework gives mA but Power Supply framework gives uA */ 27762306a36Sopenharmony_ci val->intval *= 1000; 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CAPACITY: 28162306a36Sopenharmony_ci /* When no battery is present, return capacity is 100% */ 28262306a36Sopenharmony_ci ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_OP_MODE, 28362306a36Sopenharmony_ci ®); 28462306a36Sopenharmony_ci if (ret) 28562306a36Sopenharmony_ci return ret; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (!(reg & AXP20X_PWR_OP_BATT_PRESENT)) { 28862306a36Sopenharmony_ci val->intval = 100; 28962306a36Sopenharmony_ci return 0; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci ret = regmap_read(axp20x_batt->regmap, AXP20X_FG_RES, ®); 29362306a36Sopenharmony_ci if (ret) 29462306a36Sopenharmony_ci return ret; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (axp20x_batt->data->has_fg_valid && !(reg & AXP22X_FG_VALID)) 29762306a36Sopenharmony_ci return -EINVAL; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* 30062306a36Sopenharmony_ci * Fuel Gauge data takes 7 bits but the stored value seems to be 30162306a36Sopenharmony_ci * directly the raw percentage without any scaling to 7 bits. 30262306a36Sopenharmony_ci */ 30362306a36Sopenharmony_ci val->intval = reg & AXP209_FG_PERCENT; 30462306a36Sopenharmony_ci break; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: 30762306a36Sopenharmony_ci return axp20x_batt->data->get_max_voltage(axp20x_batt, 30862306a36Sopenharmony_ci &val->intval); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 31162306a36Sopenharmony_ci ret = regmap_read(axp20x_batt->regmap, AXP20X_V_OFF, ®); 31262306a36Sopenharmony_ci if (ret) 31362306a36Sopenharmony_ci return ret; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci val->intval = 2600000 + 100000 * (reg & AXP20X_V_OFF_MASK); 31662306a36Sopenharmony_ci break; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 31962306a36Sopenharmony_ci ret = iio_read_channel_processed(axp20x_batt->batt_v, 32062306a36Sopenharmony_ci &val->intval); 32162306a36Sopenharmony_ci if (ret) 32262306a36Sopenharmony_ci return ret; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* IIO framework gives mV but Power Supply framework gives uV */ 32562306a36Sopenharmony_ci val->intval *= 1000; 32662306a36Sopenharmony_ci break; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci default: 32962306a36Sopenharmony_ci return -EINVAL; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci return 0; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic int axp22x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt, 33662306a36Sopenharmony_ci int val) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci switch (val) { 33962306a36Sopenharmony_ci case 4100000: 34062306a36Sopenharmony_ci val = AXP20X_CHRG_CTRL1_TGT_4_1V; 34162306a36Sopenharmony_ci break; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci case 4200000: 34462306a36Sopenharmony_ci val = AXP20X_CHRG_CTRL1_TGT_4_2V; 34562306a36Sopenharmony_ci break; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci default: 34862306a36Sopenharmony_ci /* 34962306a36Sopenharmony_ci * AXP20x max voltage can be set to 4.36V and AXP22X max voltage 35062306a36Sopenharmony_ci * can be set to 4.22V and 4.24V, but these voltages are too 35162306a36Sopenharmony_ci * high for Lithium based batteries (AXP PMICs are supposed to 35262306a36Sopenharmony_ci * be used with these kinds of battery). 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_ci return -EINVAL; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci return regmap_update_bits(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, 35862306a36Sopenharmony_ci AXP20X_CHRG_CTRL1_TGT_VOLT, val); 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic int axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt, 36262306a36Sopenharmony_ci int val) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci switch (val) { 36562306a36Sopenharmony_ci case 4100000: 36662306a36Sopenharmony_ci val = AXP20X_CHRG_CTRL1_TGT_4_1V; 36762306a36Sopenharmony_ci break; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci case 4150000: 37062306a36Sopenharmony_ci val = AXP20X_CHRG_CTRL1_TGT_4_15V; 37162306a36Sopenharmony_ci break; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci case 4200000: 37462306a36Sopenharmony_ci val = AXP20X_CHRG_CTRL1_TGT_4_2V; 37562306a36Sopenharmony_ci break; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci default: 37862306a36Sopenharmony_ci /* 37962306a36Sopenharmony_ci * AXP20x max voltage can be set to 4.36V and AXP22X max voltage 38062306a36Sopenharmony_ci * can be set to 4.22V and 4.24V, but these voltages are too 38162306a36Sopenharmony_ci * high for Lithium based batteries (AXP PMICs are supposed to 38262306a36Sopenharmony_ci * be used with these kinds of battery). 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_ci return -EINVAL; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci return regmap_update_bits(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, 38862306a36Sopenharmony_ci AXP20X_CHRG_CTRL1_TGT_VOLT, val); 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic int axp20x_set_constant_charge_current(struct axp20x_batt_ps *axp_batt, 39262306a36Sopenharmony_ci int charge_current) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci if (charge_current > axp_batt->max_ccc) 39562306a36Sopenharmony_ci return -EINVAL; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci charge_current = (charge_current - axp_batt->data->ccc_offset) / 39862306a36Sopenharmony_ci axp_batt->data->ccc_scale; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (charge_current > AXP20X_CHRG_CTRL1_TGT_CURR || charge_current < 0) 40162306a36Sopenharmony_ci return -EINVAL; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return regmap_update_bits(axp_batt->regmap, AXP20X_CHRG_CTRL1, 40462306a36Sopenharmony_ci AXP20X_CHRG_CTRL1_TGT_CURR, charge_current); 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic int axp20x_set_max_constant_charge_current(struct axp20x_batt_ps *axp, 40862306a36Sopenharmony_ci int charge_current) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci bool lower_max = false; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci charge_current = (charge_current - axp->data->ccc_offset) / 41362306a36Sopenharmony_ci axp->data->ccc_scale; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (charge_current > AXP20X_CHRG_CTRL1_TGT_CURR || charge_current < 0) 41662306a36Sopenharmony_ci return -EINVAL; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci charge_current = charge_current * axp->data->ccc_scale + 41962306a36Sopenharmony_ci axp->data->ccc_offset; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (charge_current > axp->max_ccc) 42262306a36Sopenharmony_ci dev_warn(axp->dev, 42362306a36Sopenharmony_ci "Setting max constant charge current higher than previously defined. Note that increasing the constant charge current may damage your battery.\n"); 42462306a36Sopenharmony_ci else 42562306a36Sopenharmony_ci lower_max = true; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci axp->max_ccc = charge_current; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (lower_max) { 43062306a36Sopenharmony_ci int current_cc; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci axp20x_get_constant_charge_current(axp, ¤t_cc); 43362306a36Sopenharmony_ci if (current_cc > charge_current) 43462306a36Sopenharmony_ci axp20x_set_constant_charge_current(axp, charge_current); 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci return 0; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_cistatic int axp20x_set_voltage_min_design(struct axp20x_batt_ps *axp_batt, 44062306a36Sopenharmony_ci int min_voltage) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci int val1 = (min_voltage - 2600000) / 100000; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (val1 < 0 || val1 > AXP20X_V_OFF_MASK) 44562306a36Sopenharmony_ci return -EINVAL; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci return regmap_update_bits(axp_batt->regmap, AXP20X_V_OFF, 44862306a36Sopenharmony_ci AXP20X_V_OFF_MASK, val1); 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic int axp20x_battery_set_prop(struct power_supply *psy, 45262306a36Sopenharmony_ci enum power_supply_property psp, 45362306a36Sopenharmony_ci const union power_supply_propval *val) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci struct axp20x_batt_ps *axp20x_batt = power_supply_get_drvdata(psy); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci switch (psp) { 45862306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 45962306a36Sopenharmony_ci return axp20x_set_voltage_min_design(axp20x_batt, val->intval); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: 46262306a36Sopenharmony_ci return axp20x_batt->data->set_max_voltage(axp20x_batt, val->intval); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: 46562306a36Sopenharmony_ci return axp20x_set_constant_charge_current(axp20x_batt, 46662306a36Sopenharmony_ci val->intval); 46762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: 46862306a36Sopenharmony_ci return axp20x_set_max_constant_charge_current(axp20x_batt, 46962306a36Sopenharmony_ci val->intval); 47062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 47162306a36Sopenharmony_ci switch (val->intval) { 47262306a36Sopenharmony_ci case POWER_SUPPLY_STATUS_CHARGING: 47362306a36Sopenharmony_ci return regmap_update_bits(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, 47462306a36Sopenharmony_ci AXP20X_CHRG_CTRL1_ENABLE, AXP20X_CHRG_CTRL1_ENABLE); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci case POWER_SUPPLY_STATUS_DISCHARGING: 47762306a36Sopenharmony_ci case POWER_SUPPLY_STATUS_NOT_CHARGING: 47862306a36Sopenharmony_ci return regmap_update_bits(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, 47962306a36Sopenharmony_ci AXP20X_CHRG_CTRL1_ENABLE, 0); 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci fallthrough; 48262306a36Sopenharmony_ci default: 48362306a36Sopenharmony_ci return -EINVAL; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistatic enum power_supply_property axp20x_battery_props[] = { 48862306a36Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 48962306a36Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 49062306a36Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 49162306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 49262306a36Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_NOW, 49362306a36Sopenharmony_ci POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, 49462306a36Sopenharmony_ci POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, 49562306a36Sopenharmony_ci POWER_SUPPLY_PROP_HEALTH, 49662306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 49762306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 49862306a36Sopenharmony_ci POWER_SUPPLY_PROP_CAPACITY, 49962306a36Sopenharmony_ci}; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistatic int axp20x_battery_prop_writeable(struct power_supply *psy, 50262306a36Sopenharmony_ci enum power_supply_property psp) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci return psp == POWER_SUPPLY_PROP_STATUS || 50562306a36Sopenharmony_ci psp == POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN || 50662306a36Sopenharmony_ci psp == POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN || 50762306a36Sopenharmony_ci psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT || 50862306a36Sopenharmony_ci psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX; 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic const struct power_supply_desc axp20x_batt_ps_desc = { 51262306a36Sopenharmony_ci .name = "axp20x-battery", 51362306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_BATTERY, 51462306a36Sopenharmony_ci .properties = axp20x_battery_props, 51562306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(axp20x_battery_props), 51662306a36Sopenharmony_ci .property_is_writeable = axp20x_battery_prop_writeable, 51762306a36Sopenharmony_ci .get_property = axp20x_battery_get_prop, 51862306a36Sopenharmony_ci .set_property = axp20x_battery_set_prop, 51962306a36Sopenharmony_ci}; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_cistatic const struct axp_data axp209_data = { 52262306a36Sopenharmony_ci .ccc_scale = 100000, 52362306a36Sopenharmony_ci .ccc_offset = 300000, 52462306a36Sopenharmony_ci .get_max_voltage = axp20x_battery_get_max_voltage, 52562306a36Sopenharmony_ci .set_max_voltage = axp20x_battery_set_max_voltage, 52662306a36Sopenharmony_ci}; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistatic const struct axp_data axp221_data = { 52962306a36Sopenharmony_ci .ccc_scale = 150000, 53062306a36Sopenharmony_ci .ccc_offset = 300000, 53162306a36Sopenharmony_ci .has_fg_valid = true, 53262306a36Sopenharmony_ci .get_max_voltage = axp22x_battery_get_max_voltage, 53362306a36Sopenharmony_ci .set_max_voltage = axp22x_battery_set_max_voltage, 53462306a36Sopenharmony_ci}; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic const struct axp_data axp813_data = { 53762306a36Sopenharmony_ci .ccc_scale = 200000, 53862306a36Sopenharmony_ci .ccc_offset = 200000, 53962306a36Sopenharmony_ci .has_fg_valid = true, 54062306a36Sopenharmony_ci .get_max_voltage = axp813_battery_get_max_voltage, 54162306a36Sopenharmony_ci .set_max_voltage = axp20x_battery_set_max_voltage, 54262306a36Sopenharmony_ci}; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic const struct of_device_id axp20x_battery_ps_id[] = { 54562306a36Sopenharmony_ci { 54662306a36Sopenharmony_ci .compatible = "x-powers,axp209-battery-power-supply", 54762306a36Sopenharmony_ci .data = (void *)&axp209_data, 54862306a36Sopenharmony_ci }, { 54962306a36Sopenharmony_ci .compatible = "x-powers,axp221-battery-power-supply", 55062306a36Sopenharmony_ci .data = (void *)&axp221_data, 55162306a36Sopenharmony_ci }, { 55262306a36Sopenharmony_ci .compatible = "x-powers,axp813-battery-power-supply", 55362306a36Sopenharmony_ci .data = (void *)&axp813_data, 55462306a36Sopenharmony_ci }, { /* sentinel */ }, 55562306a36Sopenharmony_ci}; 55662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, axp20x_battery_ps_id); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic int axp20x_power_probe(struct platform_device *pdev) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci struct axp20x_batt_ps *axp20x_batt; 56162306a36Sopenharmony_ci struct power_supply_config psy_cfg = {}; 56262306a36Sopenharmony_ci struct power_supply_battery_info *info; 56362306a36Sopenharmony_ci struct device *dev = &pdev->dev; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci if (!of_device_is_available(pdev->dev.of_node)) 56662306a36Sopenharmony_ci return -ENODEV; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci axp20x_batt = devm_kzalloc(&pdev->dev, sizeof(*axp20x_batt), 56962306a36Sopenharmony_ci GFP_KERNEL); 57062306a36Sopenharmony_ci if (!axp20x_batt) 57162306a36Sopenharmony_ci return -ENOMEM; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci axp20x_batt->dev = &pdev->dev; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci axp20x_batt->batt_v = devm_iio_channel_get(&pdev->dev, "batt_v"); 57662306a36Sopenharmony_ci if (IS_ERR(axp20x_batt->batt_v)) { 57762306a36Sopenharmony_ci if (PTR_ERR(axp20x_batt->batt_v) == -ENODEV) 57862306a36Sopenharmony_ci return -EPROBE_DEFER; 57962306a36Sopenharmony_ci return PTR_ERR(axp20x_batt->batt_v); 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci axp20x_batt->batt_chrg_i = devm_iio_channel_get(&pdev->dev, 58362306a36Sopenharmony_ci "batt_chrg_i"); 58462306a36Sopenharmony_ci if (IS_ERR(axp20x_batt->batt_chrg_i)) { 58562306a36Sopenharmony_ci if (PTR_ERR(axp20x_batt->batt_chrg_i) == -ENODEV) 58662306a36Sopenharmony_ci return -EPROBE_DEFER; 58762306a36Sopenharmony_ci return PTR_ERR(axp20x_batt->batt_chrg_i); 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci axp20x_batt->batt_dischrg_i = devm_iio_channel_get(&pdev->dev, 59162306a36Sopenharmony_ci "batt_dischrg_i"); 59262306a36Sopenharmony_ci if (IS_ERR(axp20x_batt->batt_dischrg_i)) { 59362306a36Sopenharmony_ci if (PTR_ERR(axp20x_batt->batt_dischrg_i) == -ENODEV) 59462306a36Sopenharmony_ci return -EPROBE_DEFER; 59562306a36Sopenharmony_ci return PTR_ERR(axp20x_batt->batt_dischrg_i); 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci axp20x_batt->regmap = dev_get_regmap(pdev->dev.parent, NULL); 59962306a36Sopenharmony_ci platform_set_drvdata(pdev, axp20x_batt); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci psy_cfg.drv_data = axp20x_batt; 60262306a36Sopenharmony_ci psy_cfg.of_node = pdev->dev.of_node; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci axp20x_batt->data = (struct axp_data *)of_device_get_match_data(dev); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci axp20x_batt->batt = devm_power_supply_register(&pdev->dev, 60762306a36Sopenharmony_ci &axp20x_batt_ps_desc, 60862306a36Sopenharmony_ci &psy_cfg); 60962306a36Sopenharmony_ci if (IS_ERR(axp20x_batt->batt)) { 61062306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register power supply: %ld\n", 61162306a36Sopenharmony_ci PTR_ERR(axp20x_batt->batt)); 61262306a36Sopenharmony_ci return PTR_ERR(axp20x_batt->batt); 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (!power_supply_get_battery_info(axp20x_batt->batt, &info)) { 61662306a36Sopenharmony_ci int vmin = info->voltage_min_design_uv; 61762306a36Sopenharmony_ci int ccc = info->constant_charge_current_max_ua; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (vmin > 0 && axp20x_set_voltage_min_design(axp20x_batt, 62062306a36Sopenharmony_ci vmin)) 62162306a36Sopenharmony_ci dev_err(&pdev->dev, 62262306a36Sopenharmony_ci "couldn't set voltage_min_design\n"); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci /* Set max to unverified value to be able to set CCC */ 62562306a36Sopenharmony_ci axp20x_batt->max_ccc = ccc; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (ccc <= 0 || axp20x_set_constant_charge_current(axp20x_batt, 62862306a36Sopenharmony_ci ccc)) { 62962306a36Sopenharmony_ci dev_err(&pdev->dev, 63062306a36Sopenharmony_ci "couldn't set constant charge current from DT: fallback to minimum value\n"); 63162306a36Sopenharmony_ci ccc = 300000; 63262306a36Sopenharmony_ci axp20x_batt->max_ccc = ccc; 63362306a36Sopenharmony_ci axp20x_set_constant_charge_current(axp20x_batt, ccc); 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci /* 63862306a36Sopenharmony_ci * Update max CCC to a valid value if battery info is present or set it 63962306a36Sopenharmony_ci * to current register value by default. 64062306a36Sopenharmony_ci */ 64162306a36Sopenharmony_ci axp20x_get_constant_charge_current(axp20x_batt, 64262306a36Sopenharmony_ci &axp20x_batt->max_ccc); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci return 0; 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic struct platform_driver axp20x_batt_driver = { 64862306a36Sopenharmony_ci .probe = axp20x_power_probe, 64962306a36Sopenharmony_ci .driver = { 65062306a36Sopenharmony_ci .name = "axp20x-battery-power-supply", 65162306a36Sopenharmony_ci .of_match_table = axp20x_battery_ps_id, 65262306a36Sopenharmony_ci }, 65362306a36Sopenharmony_ci}; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_cimodule_platform_driver(axp20x_batt_driver); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ciMODULE_DESCRIPTION("Battery power supply driver for AXP20X and AXP22X PMICs"); 65862306a36Sopenharmony_ciMODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>"); 65962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 660