146c202b5SQuentin Schulz /* 246c202b5SQuentin Schulz * Battery power supply driver for X-Powers AXP20X and AXP22X PMICs 346c202b5SQuentin Schulz * 446c202b5SQuentin Schulz * Copyright 2016 Free Electrons NextThing Co. 546c202b5SQuentin Schulz * Quentin Schulz <quentin.schulz@free-electrons.com> 646c202b5SQuentin Schulz * 746c202b5SQuentin Schulz * This driver is based on a previous upstreaming attempt by: 846c202b5SQuentin Schulz * Bruno Prémont <bonbons@linux-vserver.org> 946c202b5SQuentin Schulz * 1046c202b5SQuentin Schulz * This file is subject to the terms and conditions of the GNU General 1146c202b5SQuentin Schulz * Public License. See the file "COPYING" in the main directory of this 1246c202b5SQuentin Schulz * archive for more details. 1346c202b5SQuentin Schulz * 1446c202b5SQuentin Schulz * This program is distributed in the hope that it will be useful, 1546c202b5SQuentin Schulz * but WITHOUT ANY WARRANTY; without even the implied warranty of 1646c202b5SQuentin Schulz * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1746c202b5SQuentin Schulz * GNU General Public License for more details. 1846c202b5SQuentin Schulz */ 1946c202b5SQuentin Schulz 2046c202b5SQuentin Schulz #include <linux/err.h> 2146c202b5SQuentin Schulz #include <linux/interrupt.h> 2246c202b5SQuentin Schulz #include <linux/irq.h> 2346c202b5SQuentin Schulz #include <linux/module.h> 2446c202b5SQuentin Schulz #include <linux/of.h> 2546c202b5SQuentin Schulz #include <linux/of_device.h> 2646c202b5SQuentin Schulz #include <linux/platform_device.h> 2746c202b5SQuentin Schulz #include <linux/power_supply.h> 2846c202b5SQuentin Schulz #include <linux/regmap.h> 2946c202b5SQuentin Schulz #include <linux/slab.h> 3046c202b5SQuentin Schulz #include <linux/time.h> 3146c202b5SQuentin Schulz #include <linux/iio/iio.h> 3246c202b5SQuentin Schulz #include <linux/iio/consumer.h> 3346c202b5SQuentin Schulz #include <linux/mfd/axp20x.h> 3446c202b5SQuentin Schulz 3546c202b5SQuentin Schulz #define AXP20X_PWR_STATUS_BAT_CHARGING BIT(2) 3646c202b5SQuentin Schulz 3746c202b5SQuentin Schulz #define AXP20X_PWR_OP_BATT_PRESENT BIT(5) 3846c202b5SQuentin Schulz #define AXP20X_PWR_OP_BATT_ACTIVATED BIT(3) 3946c202b5SQuentin Schulz 4046c202b5SQuentin Schulz #define AXP209_FG_PERCENT GENMASK(6, 0) 4146c202b5SQuentin Schulz #define AXP22X_FG_VALID BIT(7) 4246c202b5SQuentin Schulz 436a0fcc87SHermann Lauer #define AXP20X_CHRG_CTRL1_ENABLE BIT(7) 4446c202b5SQuentin Schulz #define AXP20X_CHRG_CTRL1_TGT_VOLT GENMASK(6, 5) 4546c202b5SQuentin Schulz #define AXP20X_CHRG_CTRL1_TGT_4_1V (0 << 5) 4646c202b5SQuentin Schulz #define AXP20X_CHRG_CTRL1_TGT_4_15V (1 << 5) 4746c202b5SQuentin Schulz #define AXP20X_CHRG_CTRL1_TGT_4_2V (2 << 5) 4846c202b5SQuentin Schulz #define AXP20X_CHRG_CTRL1_TGT_4_36V (3 << 5) 4946c202b5SQuentin Schulz 5046c202b5SQuentin Schulz #define AXP22X_CHRG_CTRL1_TGT_4_22V (1 << 5) 5146c202b5SQuentin Schulz #define AXP22X_CHRG_CTRL1_TGT_4_24V (3 << 5) 5246c202b5SQuentin Schulz 536ff653e3SQuentin Schulz #define AXP813_CHRG_CTRL1_TGT_4_35V (3 << 5) 546ff653e3SQuentin Schulz 5546c202b5SQuentin Schulz #define AXP20X_CHRG_CTRL1_TGT_CURR GENMASK(3, 0) 5646c202b5SQuentin Schulz 5746c202b5SQuentin Schulz #define AXP20X_V_OFF_MASK GENMASK(2, 0) 5846c202b5SQuentin Schulz 59648badd7SQuentin Schulz struct axp20x_batt_ps; 60648badd7SQuentin Schulz 61648badd7SQuentin Schulz struct axp_data { 62648badd7SQuentin Schulz int ccc_scale; 63648badd7SQuentin Schulz int ccc_offset; 64648badd7SQuentin Schulz bool has_fg_valid; 65648badd7SQuentin Schulz int (*get_max_voltage)(struct axp20x_batt_ps *batt, int *val); 66648badd7SQuentin Schulz int (*set_max_voltage)(struct axp20x_batt_ps *batt, int val); 67648badd7SQuentin Schulz }; 68648badd7SQuentin Schulz 6946c202b5SQuentin Schulz struct axp20x_batt_ps { 7046c202b5SQuentin Schulz struct regmap *regmap; 7146c202b5SQuentin Schulz struct power_supply *batt; 7246c202b5SQuentin Schulz struct device *dev; 7346c202b5SQuentin Schulz struct iio_channel *batt_chrg_i; 7446c202b5SQuentin Schulz struct iio_channel *batt_dischrg_i; 7546c202b5SQuentin Schulz struct iio_channel *batt_v; 76c8003844SQuentin Schulz /* Maximum constant charge current */ 77c8003844SQuentin Schulz unsigned int max_ccc; 78648badd7SQuentin Schulz const struct axp_data *data; 7946c202b5SQuentin Schulz }; 8046c202b5SQuentin Schulz 8146c202b5SQuentin Schulz static int axp20x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt, 8246c202b5SQuentin Schulz int *val) 8346c202b5SQuentin Schulz { 8446c202b5SQuentin Schulz int ret, reg; 8546c202b5SQuentin Schulz 8646c202b5SQuentin Schulz ret = regmap_read(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, ®); 8746c202b5SQuentin Schulz if (ret) 8846c202b5SQuentin Schulz return ret; 8946c202b5SQuentin Schulz 9046c202b5SQuentin Schulz switch (reg & AXP20X_CHRG_CTRL1_TGT_VOLT) { 9146c202b5SQuentin Schulz case AXP20X_CHRG_CTRL1_TGT_4_1V: 9246c202b5SQuentin Schulz *val = 4100000; 9346c202b5SQuentin Schulz break; 9446c202b5SQuentin Schulz case AXP20X_CHRG_CTRL1_TGT_4_15V: 9546c202b5SQuentin Schulz *val = 4150000; 9646c202b5SQuentin Schulz break; 9746c202b5SQuentin Schulz case AXP20X_CHRG_CTRL1_TGT_4_2V: 9846c202b5SQuentin Schulz *val = 4200000; 9946c202b5SQuentin Schulz break; 10046c202b5SQuentin Schulz case AXP20X_CHRG_CTRL1_TGT_4_36V: 10146c202b5SQuentin Schulz *val = 4360000; 10246c202b5SQuentin Schulz break; 10346c202b5SQuentin Schulz default: 10446c202b5SQuentin Schulz return -EINVAL; 10546c202b5SQuentin Schulz } 10646c202b5SQuentin Schulz 10746c202b5SQuentin Schulz return 0; 10846c202b5SQuentin Schulz } 10946c202b5SQuentin Schulz 11046c202b5SQuentin Schulz static int axp22x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt, 11146c202b5SQuentin Schulz int *val) 11246c202b5SQuentin Schulz { 11346c202b5SQuentin Schulz int ret, reg; 11446c202b5SQuentin Schulz 11546c202b5SQuentin Schulz ret = regmap_read(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, ®); 11646c202b5SQuentin Schulz if (ret) 11746c202b5SQuentin Schulz return ret; 11846c202b5SQuentin Schulz 11946c202b5SQuentin Schulz switch (reg & AXP20X_CHRG_CTRL1_TGT_VOLT) { 12046c202b5SQuentin Schulz case AXP20X_CHRG_CTRL1_TGT_4_1V: 12146c202b5SQuentin Schulz *val = 4100000; 12246c202b5SQuentin Schulz break; 12346c202b5SQuentin Schulz case AXP20X_CHRG_CTRL1_TGT_4_2V: 12446c202b5SQuentin Schulz *val = 4200000; 12546c202b5SQuentin Schulz break; 12646c202b5SQuentin Schulz case AXP22X_CHRG_CTRL1_TGT_4_22V: 12746c202b5SQuentin Schulz *val = 4220000; 12846c202b5SQuentin Schulz break; 12946c202b5SQuentin Schulz case AXP22X_CHRG_CTRL1_TGT_4_24V: 13046c202b5SQuentin Schulz *val = 4240000; 13146c202b5SQuentin Schulz break; 13246c202b5SQuentin Schulz default: 13346c202b5SQuentin Schulz return -EINVAL; 13446c202b5SQuentin Schulz } 13546c202b5SQuentin Schulz 13646c202b5SQuentin Schulz return 0; 13746c202b5SQuentin Schulz } 13846c202b5SQuentin Schulz 1396ff653e3SQuentin Schulz static int axp813_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt, 1406ff653e3SQuentin Schulz int *val) 1416ff653e3SQuentin Schulz { 1426ff653e3SQuentin Schulz int ret, reg; 1436ff653e3SQuentin Schulz 1446ff653e3SQuentin Schulz ret = regmap_read(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, ®); 1456ff653e3SQuentin Schulz if (ret) 1466ff653e3SQuentin Schulz return ret; 1476ff653e3SQuentin Schulz 1486ff653e3SQuentin Schulz switch (reg & AXP20X_CHRG_CTRL1_TGT_VOLT) { 1496ff653e3SQuentin Schulz case AXP20X_CHRG_CTRL1_TGT_4_1V: 1506ff653e3SQuentin Schulz *val = 4100000; 1516ff653e3SQuentin Schulz break; 1526ff653e3SQuentin Schulz case AXP20X_CHRG_CTRL1_TGT_4_15V: 1536ff653e3SQuentin Schulz *val = 4150000; 1546ff653e3SQuentin Schulz break; 1556ff653e3SQuentin Schulz case AXP20X_CHRG_CTRL1_TGT_4_2V: 1566ff653e3SQuentin Schulz *val = 4200000; 1576ff653e3SQuentin Schulz break; 1586ff653e3SQuentin Schulz case AXP813_CHRG_CTRL1_TGT_4_35V: 1596ff653e3SQuentin Schulz *val = 4350000; 1606ff653e3SQuentin Schulz break; 1616ff653e3SQuentin Schulz default: 1626ff653e3SQuentin Schulz return -EINVAL; 1636ff653e3SQuentin Schulz } 1646ff653e3SQuentin Schulz 1656ff653e3SQuentin Schulz return 0; 1666ff653e3SQuentin Schulz } 1676ff653e3SQuentin Schulz 16846c202b5SQuentin Schulz static int axp20x_get_constant_charge_current(struct axp20x_batt_ps *axp, 16946c202b5SQuentin Schulz int *val) 17046c202b5SQuentin Schulz { 17146c202b5SQuentin Schulz int ret; 17246c202b5SQuentin Schulz 17346c202b5SQuentin Schulz ret = regmap_read(axp->regmap, AXP20X_CHRG_CTRL1, val); 17446c202b5SQuentin Schulz if (ret) 17546c202b5SQuentin Schulz return ret; 17646c202b5SQuentin Schulz 17746c202b5SQuentin Schulz *val &= AXP20X_CHRG_CTRL1_TGT_CURR; 17846c202b5SQuentin Schulz 179648badd7SQuentin Schulz *val = *val * axp->data->ccc_scale + axp->data->ccc_offset; 18046c202b5SQuentin Schulz 18146c202b5SQuentin Schulz return 0; 18246c202b5SQuentin Schulz } 18346c202b5SQuentin Schulz 18446c202b5SQuentin Schulz static int axp20x_battery_get_prop(struct power_supply *psy, 18546c202b5SQuentin Schulz enum power_supply_property psp, 18646c202b5SQuentin Schulz union power_supply_propval *val) 18746c202b5SQuentin Schulz { 18846c202b5SQuentin Schulz struct axp20x_batt_ps *axp20x_batt = power_supply_get_drvdata(psy); 18946c202b5SQuentin Schulz int ret = 0, reg, val1; 19046c202b5SQuentin Schulz 19146c202b5SQuentin Schulz switch (psp) { 19246c202b5SQuentin Schulz case POWER_SUPPLY_PROP_PRESENT: 19346c202b5SQuentin Schulz case POWER_SUPPLY_PROP_ONLINE: 19446c202b5SQuentin Schulz ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_OP_MODE, 19546c202b5SQuentin Schulz ®); 19646c202b5SQuentin Schulz if (ret) 19746c202b5SQuentin Schulz return ret; 19846c202b5SQuentin Schulz 19946c202b5SQuentin Schulz val->intval = !!(reg & AXP20X_PWR_OP_BATT_PRESENT); 20046c202b5SQuentin Schulz break; 20146c202b5SQuentin Schulz 20246c202b5SQuentin Schulz case POWER_SUPPLY_PROP_STATUS: 20346c202b5SQuentin Schulz ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_INPUT_STATUS, 20446c202b5SQuentin Schulz ®); 20546c202b5SQuentin Schulz if (ret) 20646c202b5SQuentin Schulz return ret; 20746c202b5SQuentin Schulz 20846c202b5SQuentin Schulz if (reg & AXP20X_PWR_STATUS_BAT_CHARGING) { 20946c202b5SQuentin Schulz val->intval = POWER_SUPPLY_STATUS_CHARGING; 21046c202b5SQuentin Schulz return 0; 21146c202b5SQuentin Schulz } 21246c202b5SQuentin Schulz 21346c202b5SQuentin Schulz ret = iio_read_channel_processed(axp20x_batt->batt_dischrg_i, 21446c202b5SQuentin Schulz &val1); 21546c202b5SQuentin Schulz if (ret) 21646c202b5SQuentin Schulz return ret; 21746c202b5SQuentin Schulz 21846c202b5SQuentin Schulz if (val1) { 21946c202b5SQuentin Schulz val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 22046c202b5SQuentin Schulz return 0; 22146c202b5SQuentin Schulz } 22246c202b5SQuentin Schulz 22346c202b5SQuentin Schulz ret = regmap_read(axp20x_batt->regmap, AXP20X_FG_RES, &val1); 22446c202b5SQuentin Schulz if (ret) 22546c202b5SQuentin Schulz return ret; 22646c202b5SQuentin Schulz 22746c202b5SQuentin Schulz /* 22846c202b5SQuentin Schulz * Fuel Gauge data takes 7 bits but the stored value seems to be 22946c202b5SQuentin Schulz * directly the raw percentage without any scaling to 7 bits. 23046c202b5SQuentin Schulz */ 23146c202b5SQuentin Schulz if ((val1 & AXP209_FG_PERCENT) == 100) 23246c202b5SQuentin Schulz val->intval = POWER_SUPPLY_STATUS_FULL; 23346c202b5SQuentin Schulz else 23446c202b5SQuentin Schulz val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 23546c202b5SQuentin Schulz break; 23646c202b5SQuentin Schulz 23746c202b5SQuentin Schulz case POWER_SUPPLY_PROP_HEALTH: 23846c202b5SQuentin Schulz ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_OP_MODE, 23946c202b5SQuentin Schulz &val1); 24046c202b5SQuentin Schulz if (ret) 24146c202b5SQuentin Schulz return ret; 24246c202b5SQuentin Schulz 24346c202b5SQuentin Schulz if (val1 & AXP20X_PWR_OP_BATT_ACTIVATED) { 24446c202b5SQuentin Schulz val->intval = POWER_SUPPLY_HEALTH_DEAD; 24546c202b5SQuentin Schulz return 0; 24646c202b5SQuentin Schulz } 24746c202b5SQuentin Schulz 24846c202b5SQuentin Schulz val->intval = POWER_SUPPLY_HEALTH_GOOD; 24946c202b5SQuentin Schulz break; 25046c202b5SQuentin Schulz 25146c202b5SQuentin Schulz case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: 25246c202b5SQuentin Schulz ret = axp20x_get_constant_charge_current(axp20x_batt, 25346c202b5SQuentin Schulz &val->intval); 25446c202b5SQuentin Schulz if (ret) 25546c202b5SQuentin Schulz return ret; 25646c202b5SQuentin Schulz break; 25746c202b5SQuentin Schulz 25846c202b5SQuentin Schulz case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: 259c8003844SQuentin Schulz val->intval = axp20x_batt->max_ccc; 26046c202b5SQuentin Schulz break; 26146c202b5SQuentin Schulz 26246c202b5SQuentin Schulz case POWER_SUPPLY_PROP_CURRENT_NOW: 26346c202b5SQuentin Schulz ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_INPUT_STATUS, 26446c202b5SQuentin Schulz ®); 26546c202b5SQuentin Schulz if (ret) 26646c202b5SQuentin Schulz return ret; 26746c202b5SQuentin Schulz 268*d4f408cdSEvgeny Boger if (reg & AXP20X_PWR_STATUS_BAT_CHARGING) { 269*d4f408cdSEvgeny Boger ret = iio_read_channel_processed(axp20x_batt->batt_chrg_i, &val->intval); 270*d4f408cdSEvgeny Boger } else { 271*d4f408cdSEvgeny Boger ret = iio_read_channel_processed(axp20x_batt->batt_dischrg_i, &val1); 272*d4f408cdSEvgeny Boger val->intval = -val1; 273*d4f408cdSEvgeny Boger } 27446c202b5SQuentin Schulz if (ret) 27546c202b5SQuentin Schulz return ret; 27646c202b5SQuentin Schulz 27746c202b5SQuentin Schulz /* IIO framework gives mA but Power Supply framework gives uA */ 27846c202b5SQuentin Schulz val->intval *= 1000; 27946c202b5SQuentin Schulz break; 28046c202b5SQuentin Schulz 28146c202b5SQuentin Schulz case POWER_SUPPLY_PROP_CAPACITY: 28246c202b5SQuentin Schulz /* When no battery is present, return capacity is 100% */ 28346c202b5SQuentin Schulz ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_OP_MODE, 28446c202b5SQuentin Schulz ®); 28546c202b5SQuentin Schulz if (ret) 28646c202b5SQuentin Schulz return ret; 28746c202b5SQuentin Schulz 28846c202b5SQuentin Schulz if (!(reg & AXP20X_PWR_OP_BATT_PRESENT)) { 28946c202b5SQuentin Schulz val->intval = 100; 29046c202b5SQuentin Schulz return 0; 29146c202b5SQuentin Schulz } 29246c202b5SQuentin Schulz 29346c202b5SQuentin Schulz ret = regmap_read(axp20x_batt->regmap, AXP20X_FG_RES, ®); 29446c202b5SQuentin Schulz if (ret) 29546c202b5SQuentin Schulz return ret; 29646c202b5SQuentin Schulz 297648badd7SQuentin Schulz if (axp20x_batt->data->has_fg_valid && !(reg & AXP22X_FG_VALID)) 29846c202b5SQuentin Schulz return -EINVAL; 29946c202b5SQuentin Schulz 30046c202b5SQuentin Schulz /* 30146c202b5SQuentin Schulz * Fuel Gauge data takes 7 bits but the stored value seems to be 30246c202b5SQuentin Schulz * directly the raw percentage without any scaling to 7 bits. 30346c202b5SQuentin Schulz */ 30446c202b5SQuentin Schulz val->intval = reg & AXP209_FG_PERCENT; 30546c202b5SQuentin Schulz break; 30646c202b5SQuentin Schulz 30746c202b5SQuentin Schulz case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: 308648badd7SQuentin Schulz return axp20x_batt->data->get_max_voltage(axp20x_batt, 30946c202b5SQuentin Schulz &val->intval); 31046c202b5SQuentin Schulz 31146c202b5SQuentin Schulz case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 31246c202b5SQuentin Schulz ret = regmap_read(axp20x_batt->regmap, AXP20X_V_OFF, ®); 31346c202b5SQuentin Schulz if (ret) 31446c202b5SQuentin Schulz return ret; 31546c202b5SQuentin Schulz 31646c202b5SQuentin Schulz val->intval = 2600000 + 100000 * (reg & AXP20X_V_OFF_MASK); 31746c202b5SQuentin Schulz break; 31846c202b5SQuentin Schulz 31946c202b5SQuentin Schulz case POWER_SUPPLY_PROP_VOLTAGE_NOW: 32046c202b5SQuentin Schulz ret = iio_read_channel_processed(axp20x_batt->batt_v, 32146c202b5SQuentin Schulz &val->intval); 32246c202b5SQuentin Schulz if (ret) 32346c202b5SQuentin Schulz return ret; 32446c202b5SQuentin Schulz 32546c202b5SQuentin Schulz /* IIO framework gives mV but Power Supply framework gives uV */ 32646c202b5SQuentin Schulz val->intval *= 1000; 32746c202b5SQuentin Schulz break; 32846c202b5SQuentin Schulz 32946c202b5SQuentin Schulz default: 33046c202b5SQuentin Schulz return -EINVAL; 33146c202b5SQuentin Schulz } 33246c202b5SQuentin Schulz 33346c202b5SQuentin Schulz return 0; 33446c202b5SQuentin Schulz } 33546c202b5SQuentin Schulz 336648badd7SQuentin Schulz static int axp22x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt, 337648badd7SQuentin Schulz int val) 338648badd7SQuentin Schulz { 339648badd7SQuentin Schulz switch (val) { 340648badd7SQuentin Schulz case 4100000: 341648badd7SQuentin Schulz val = AXP20X_CHRG_CTRL1_TGT_4_1V; 342648badd7SQuentin Schulz break; 343648badd7SQuentin Schulz 344648badd7SQuentin Schulz case 4200000: 345648badd7SQuentin Schulz val = AXP20X_CHRG_CTRL1_TGT_4_2V; 346648badd7SQuentin Schulz break; 347648badd7SQuentin Schulz 348648badd7SQuentin Schulz default: 349648badd7SQuentin Schulz /* 350648badd7SQuentin Schulz * AXP20x max voltage can be set to 4.36V and AXP22X max voltage 351648badd7SQuentin Schulz * can be set to 4.22V and 4.24V, but these voltages are too 352648badd7SQuentin Schulz * high for Lithium based batteries (AXP PMICs are supposed to 353648badd7SQuentin Schulz * be used with these kinds of battery). 354648badd7SQuentin Schulz */ 355648badd7SQuentin Schulz return -EINVAL; 356648badd7SQuentin Schulz } 357648badd7SQuentin Schulz 358648badd7SQuentin Schulz return regmap_update_bits(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, 359648badd7SQuentin Schulz AXP20X_CHRG_CTRL1_TGT_VOLT, val); 360648badd7SQuentin Schulz } 361648badd7SQuentin Schulz 36246c202b5SQuentin Schulz static int axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt, 36346c202b5SQuentin Schulz int val) 36446c202b5SQuentin Schulz { 36546c202b5SQuentin Schulz switch (val) { 36646c202b5SQuentin Schulz case 4100000: 36746c202b5SQuentin Schulz val = AXP20X_CHRG_CTRL1_TGT_4_1V; 36846c202b5SQuentin Schulz break; 36946c202b5SQuentin Schulz 37046c202b5SQuentin Schulz case 4150000: 37146c202b5SQuentin Schulz val = AXP20X_CHRG_CTRL1_TGT_4_15V; 37246c202b5SQuentin Schulz break; 37346c202b5SQuentin Schulz 37446c202b5SQuentin Schulz case 4200000: 37546c202b5SQuentin Schulz val = AXP20X_CHRG_CTRL1_TGT_4_2V; 37646c202b5SQuentin Schulz break; 37746c202b5SQuentin Schulz 37846c202b5SQuentin Schulz default: 37946c202b5SQuentin Schulz /* 38046c202b5SQuentin Schulz * AXP20x max voltage can be set to 4.36V and AXP22X max voltage 38146c202b5SQuentin Schulz * can be set to 4.22V and 4.24V, but these voltages are too 38246c202b5SQuentin Schulz * high for Lithium based batteries (AXP PMICs are supposed to 38346c202b5SQuentin Schulz * be used with these kinds of battery). 38446c202b5SQuentin Schulz */ 38546c202b5SQuentin Schulz return -EINVAL; 38646c202b5SQuentin Schulz } 38746c202b5SQuentin Schulz 38846c202b5SQuentin Schulz return regmap_update_bits(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, 38946c202b5SQuentin Schulz AXP20X_CHRG_CTRL1_TGT_VOLT, val); 39046c202b5SQuentin Schulz } 39146c202b5SQuentin Schulz 39246c202b5SQuentin Schulz static int axp20x_set_constant_charge_current(struct axp20x_batt_ps *axp_batt, 39346c202b5SQuentin Schulz int charge_current) 39446c202b5SQuentin Schulz { 395c8003844SQuentin Schulz if (charge_current > axp_batt->max_ccc) 396c8003844SQuentin Schulz return -EINVAL; 397c8003844SQuentin Schulz 398648badd7SQuentin Schulz charge_current = (charge_current - axp_batt->data->ccc_offset) / 399648badd7SQuentin Schulz axp_batt->data->ccc_scale; 40046c202b5SQuentin Schulz 40146c202b5SQuentin Schulz if (charge_current > AXP20X_CHRG_CTRL1_TGT_CURR || charge_current < 0) 40246c202b5SQuentin Schulz return -EINVAL; 40346c202b5SQuentin Schulz 40446c202b5SQuentin Schulz return regmap_update_bits(axp_batt->regmap, AXP20X_CHRG_CTRL1, 40546c202b5SQuentin Schulz AXP20X_CHRG_CTRL1_TGT_CURR, charge_current); 40646c202b5SQuentin Schulz } 40746c202b5SQuentin Schulz 408c8003844SQuentin Schulz static int axp20x_set_max_constant_charge_current(struct axp20x_batt_ps *axp, 409c8003844SQuentin Schulz int charge_current) 410c8003844SQuentin Schulz { 411c8003844SQuentin Schulz bool lower_max = false; 412c8003844SQuentin Schulz 413648badd7SQuentin Schulz charge_current = (charge_current - axp->data->ccc_offset) / 414648badd7SQuentin Schulz axp->data->ccc_scale; 415c8003844SQuentin Schulz 416c8003844SQuentin Schulz if (charge_current > AXP20X_CHRG_CTRL1_TGT_CURR || charge_current < 0) 417c8003844SQuentin Schulz return -EINVAL; 418c8003844SQuentin Schulz 419648badd7SQuentin Schulz charge_current = charge_current * axp->data->ccc_scale + 420648badd7SQuentin Schulz axp->data->ccc_offset; 421c8003844SQuentin Schulz 422c8003844SQuentin Schulz if (charge_current > axp->max_ccc) 423c8003844SQuentin Schulz dev_warn(axp->dev, 424c8003844SQuentin Schulz "Setting max constant charge current higher than previously defined. Note that increasing the constant charge current may damage your battery.\n"); 425c8003844SQuentin Schulz else 426c8003844SQuentin Schulz lower_max = true; 427c8003844SQuentin Schulz 428c8003844SQuentin Schulz axp->max_ccc = charge_current; 429c8003844SQuentin Schulz 430c8003844SQuentin Schulz if (lower_max) { 431c8003844SQuentin Schulz int current_cc; 432c8003844SQuentin Schulz 433c8003844SQuentin Schulz axp20x_get_constant_charge_current(axp, ¤t_cc); 434c8003844SQuentin Schulz if (current_cc > charge_current) 435c8003844SQuentin Schulz axp20x_set_constant_charge_current(axp, charge_current); 436c8003844SQuentin Schulz } 437c8003844SQuentin Schulz 438c8003844SQuentin Schulz return 0; 439c8003844SQuentin Schulz } 44046c202b5SQuentin Schulz static int axp20x_set_voltage_min_design(struct axp20x_batt_ps *axp_batt, 44146c202b5SQuentin Schulz int min_voltage) 44246c202b5SQuentin Schulz { 44346c202b5SQuentin Schulz int val1 = (min_voltage - 2600000) / 100000; 44446c202b5SQuentin Schulz 44546c202b5SQuentin Schulz if (val1 < 0 || val1 > AXP20X_V_OFF_MASK) 44646c202b5SQuentin Schulz return -EINVAL; 44746c202b5SQuentin Schulz 44846c202b5SQuentin Schulz return regmap_update_bits(axp_batt->regmap, AXP20X_V_OFF, 44946c202b5SQuentin Schulz AXP20X_V_OFF_MASK, val1); 45046c202b5SQuentin Schulz } 45146c202b5SQuentin Schulz 45246c202b5SQuentin Schulz static int axp20x_battery_set_prop(struct power_supply *psy, 45346c202b5SQuentin Schulz enum power_supply_property psp, 45446c202b5SQuentin Schulz const union power_supply_propval *val) 45546c202b5SQuentin Schulz { 45646c202b5SQuentin Schulz struct axp20x_batt_ps *axp20x_batt = power_supply_get_drvdata(psy); 45746c202b5SQuentin Schulz 45846c202b5SQuentin Schulz switch (psp) { 45946c202b5SQuentin Schulz case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 46046c202b5SQuentin Schulz return axp20x_set_voltage_min_design(axp20x_batt, val->intval); 46146c202b5SQuentin Schulz 46246c202b5SQuentin Schulz case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: 463648badd7SQuentin Schulz return axp20x_batt->data->set_max_voltage(axp20x_batt, val->intval); 46446c202b5SQuentin Schulz 46546c202b5SQuentin Schulz case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: 46646c202b5SQuentin Schulz return axp20x_set_constant_charge_current(axp20x_batt, 46746c202b5SQuentin Schulz val->intval); 468c8003844SQuentin Schulz case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: 469c8003844SQuentin Schulz return axp20x_set_max_constant_charge_current(axp20x_batt, 470c8003844SQuentin Schulz val->intval); 4716a0fcc87SHermann Lauer case POWER_SUPPLY_PROP_STATUS: 4726a0fcc87SHermann Lauer switch (val->intval) { 4736a0fcc87SHermann Lauer case POWER_SUPPLY_STATUS_CHARGING: 4746a0fcc87SHermann Lauer return regmap_update_bits(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, 4756a0fcc87SHermann Lauer AXP20X_CHRG_CTRL1_ENABLE, AXP20X_CHRG_CTRL1_ENABLE); 47646c202b5SQuentin Schulz 4776a0fcc87SHermann Lauer case POWER_SUPPLY_STATUS_DISCHARGING: 4786a0fcc87SHermann Lauer case POWER_SUPPLY_STATUS_NOT_CHARGING: 4796a0fcc87SHermann Lauer return regmap_update_bits(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, 4806a0fcc87SHermann Lauer AXP20X_CHRG_CTRL1_ENABLE, 0); 4816a0fcc87SHermann Lauer } 4826a0fcc87SHermann Lauer fallthrough; 48346c202b5SQuentin Schulz default: 48446c202b5SQuentin Schulz return -EINVAL; 48546c202b5SQuentin Schulz } 48646c202b5SQuentin Schulz } 48746c202b5SQuentin Schulz 48846c202b5SQuentin Schulz static enum power_supply_property axp20x_battery_props[] = { 48946c202b5SQuentin Schulz POWER_SUPPLY_PROP_PRESENT, 49046c202b5SQuentin Schulz POWER_SUPPLY_PROP_ONLINE, 49146c202b5SQuentin Schulz POWER_SUPPLY_PROP_STATUS, 49246c202b5SQuentin Schulz POWER_SUPPLY_PROP_VOLTAGE_NOW, 49346c202b5SQuentin Schulz POWER_SUPPLY_PROP_CURRENT_NOW, 49446c202b5SQuentin Schulz POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, 49546c202b5SQuentin Schulz POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, 49646c202b5SQuentin Schulz POWER_SUPPLY_PROP_HEALTH, 49746c202b5SQuentin Schulz POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 49846c202b5SQuentin Schulz POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 49946c202b5SQuentin Schulz POWER_SUPPLY_PROP_CAPACITY, 50046c202b5SQuentin Schulz }; 50146c202b5SQuentin Schulz 50246c202b5SQuentin Schulz static int axp20x_battery_prop_writeable(struct power_supply *psy, 50346c202b5SQuentin Schulz enum power_supply_property psp) 50446c202b5SQuentin Schulz { 5056a0fcc87SHermann Lauer return psp == POWER_SUPPLY_PROP_STATUS || 5066a0fcc87SHermann Lauer psp == POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN || 50746c202b5SQuentin Schulz psp == POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN || 508c8003844SQuentin Schulz psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT || 509c8003844SQuentin Schulz psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX; 51046c202b5SQuentin Schulz } 51146c202b5SQuentin Schulz 51246c202b5SQuentin Schulz static const struct power_supply_desc axp20x_batt_ps_desc = { 51346c202b5SQuentin Schulz .name = "axp20x-battery", 51446c202b5SQuentin Schulz .type = POWER_SUPPLY_TYPE_BATTERY, 51546c202b5SQuentin Schulz .properties = axp20x_battery_props, 51646c202b5SQuentin Schulz .num_properties = ARRAY_SIZE(axp20x_battery_props), 51746c202b5SQuentin Schulz .property_is_writeable = axp20x_battery_prop_writeable, 51846c202b5SQuentin Schulz .get_property = axp20x_battery_get_prop, 51946c202b5SQuentin Schulz .set_property = axp20x_battery_set_prop, 52046c202b5SQuentin Schulz }; 52146c202b5SQuentin Schulz 522648badd7SQuentin Schulz static const struct axp_data axp209_data = { 523648badd7SQuentin Schulz .ccc_scale = 100000, 524648badd7SQuentin Schulz .ccc_offset = 300000, 525648badd7SQuentin Schulz .get_max_voltage = axp20x_battery_get_max_voltage, 526648badd7SQuentin Schulz .set_max_voltage = axp20x_battery_set_max_voltage, 527648badd7SQuentin Schulz }; 528648badd7SQuentin Schulz 529648badd7SQuentin Schulz static const struct axp_data axp221_data = { 530648badd7SQuentin Schulz .ccc_scale = 150000, 531648badd7SQuentin Schulz .ccc_offset = 300000, 532648badd7SQuentin Schulz .has_fg_valid = true, 533648badd7SQuentin Schulz .get_max_voltage = axp22x_battery_get_max_voltage, 534648badd7SQuentin Schulz .set_max_voltage = axp22x_battery_set_max_voltage, 535648badd7SQuentin Schulz }; 536648badd7SQuentin Schulz 5376ff653e3SQuentin Schulz static const struct axp_data axp813_data = { 5386ff653e3SQuentin Schulz .ccc_scale = 200000, 5396ff653e3SQuentin Schulz .ccc_offset = 200000, 5406ff653e3SQuentin Schulz .has_fg_valid = true, 5416ff653e3SQuentin Schulz .get_max_voltage = axp813_battery_get_max_voltage, 5426ff653e3SQuentin Schulz .set_max_voltage = axp20x_battery_set_max_voltage, 5436ff653e3SQuentin Schulz }; 5446ff653e3SQuentin Schulz 54546c202b5SQuentin Schulz static const struct of_device_id axp20x_battery_ps_id[] = { 54646c202b5SQuentin Schulz { 54746c202b5SQuentin Schulz .compatible = "x-powers,axp209-battery-power-supply", 548648badd7SQuentin Schulz .data = (void *)&axp209_data, 54946c202b5SQuentin Schulz }, { 55046c202b5SQuentin Schulz .compatible = "x-powers,axp221-battery-power-supply", 551648badd7SQuentin Schulz .data = (void *)&axp221_data, 5526ff653e3SQuentin Schulz }, { 5536ff653e3SQuentin Schulz .compatible = "x-powers,axp813-battery-power-supply", 5546ff653e3SQuentin Schulz .data = (void *)&axp813_data, 55546c202b5SQuentin Schulz }, { /* sentinel */ }, 55646c202b5SQuentin Schulz }; 55746c202b5SQuentin Schulz MODULE_DEVICE_TABLE(of, axp20x_battery_ps_id); 55846c202b5SQuentin Schulz 55946c202b5SQuentin Schulz static int axp20x_power_probe(struct platform_device *pdev) 56046c202b5SQuentin Schulz { 56146c202b5SQuentin Schulz struct axp20x_batt_ps *axp20x_batt; 56246c202b5SQuentin Schulz struct power_supply_config psy_cfg = {}; 56325fd3303SLinus Walleij struct power_supply_battery_info *info; 564648badd7SQuentin Schulz struct device *dev = &pdev->dev; 56546c202b5SQuentin Schulz 56646c202b5SQuentin Schulz if (!of_device_is_available(pdev->dev.of_node)) 56746c202b5SQuentin Schulz return -ENODEV; 56846c202b5SQuentin Schulz 56946c202b5SQuentin Schulz axp20x_batt = devm_kzalloc(&pdev->dev, sizeof(*axp20x_batt), 57046c202b5SQuentin Schulz GFP_KERNEL); 57146c202b5SQuentin Schulz if (!axp20x_batt) 57246c202b5SQuentin Schulz return -ENOMEM; 57346c202b5SQuentin Schulz 57446c202b5SQuentin Schulz axp20x_batt->dev = &pdev->dev; 57546c202b5SQuentin Schulz 57646c202b5SQuentin Schulz axp20x_batt->batt_v = devm_iio_channel_get(&pdev->dev, "batt_v"); 57746c202b5SQuentin Schulz if (IS_ERR(axp20x_batt->batt_v)) { 57846c202b5SQuentin Schulz if (PTR_ERR(axp20x_batt->batt_v) == -ENODEV) 57946c202b5SQuentin Schulz return -EPROBE_DEFER; 58046c202b5SQuentin Schulz return PTR_ERR(axp20x_batt->batt_v); 58146c202b5SQuentin Schulz } 58246c202b5SQuentin Schulz 58346c202b5SQuentin Schulz axp20x_batt->batt_chrg_i = devm_iio_channel_get(&pdev->dev, 58446c202b5SQuentin Schulz "batt_chrg_i"); 58546c202b5SQuentin Schulz if (IS_ERR(axp20x_batt->batt_chrg_i)) { 58646c202b5SQuentin Schulz if (PTR_ERR(axp20x_batt->batt_chrg_i) == -ENODEV) 58746c202b5SQuentin Schulz return -EPROBE_DEFER; 58846c202b5SQuentin Schulz return PTR_ERR(axp20x_batt->batt_chrg_i); 58946c202b5SQuentin Schulz } 59046c202b5SQuentin Schulz 59146c202b5SQuentin Schulz axp20x_batt->batt_dischrg_i = devm_iio_channel_get(&pdev->dev, 59246c202b5SQuentin Schulz "batt_dischrg_i"); 59346c202b5SQuentin Schulz if (IS_ERR(axp20x_batt->batt_dischrg_i)) { 59446c202b5SQuentin Schulz if (PTR_ERR(axp20x_batt->batt_dischrg_i) == -ENODEV) 59546c202b5SQuentin Schulz return -EPROBE_DEFER; 59646c202b5SQuentin Schulz return PTR_ERR(axp20x_batt->batt_dischrg_i); 59746c202b5SQuentin Schulz } 59846c202b5SQuentin Schulz 59946c202b5SQuentin Schulz axp20x_batt->regmap = dev_get_regmap(pdev->dev.parent, NULL); 60046c202b5SQuentin Schulz platform_set_drvdata(pdev, axp20x_batt); 60146c202b5SQuentin Schulz 60246c202b5SQuentin Schulz psy_cfg.drv_data = axp20x_batt; 60346c202b5SQuentin Schulz psy_cfg.of_node = pdev->dev.of_node; 60446c202b5SQuentin Schulz 605648badd7SQuentin Schulz axp20x_batt->data = (struct axp_data *)of_device_get_match_data(dev); 60646c202b5SQuentin Schulz 60746c202b5SQuentin Schulz axp20x_batt->batt = devm_power_supply_register(&pdev->dev, 60846c202b5SQuentin Schulz &axp20x_batt_ps_desc, 60946c202b5SQuentin Schulz &psy_cfg); 61046c202b5SQuentin Schulz if (IS_ERR(axp20x_batt->batt)) { 61146c202b5SQuentin Schulz dev_err(&pdev->dev, "failed to register power supply: %ld\n", 61246c202b5SQuentin Schulz PTR_ERR(axp20x_batt->batt)); 61346c202b5SQuentin Schulz return PTR_ERR(axp20x_batt->batt); 61446c202b5SQuentin Schulz } 61546c202b5SQuentin Schulz 616f8c91baeSQuentin Schulz if (!power_supply_get_battery_info(axp20x_batt->batt, &info)) { 61725fd3303SLinus Walleij int vmin = info->voltage_min_design_uv; 61825fd3303SLinus Walleij int ccc = info->constant_charge_current_max_ua; 619f8c91baeSQuentin Schulz 620f8c91baeSQuentin Schulz if (vmin > 0 && axp20x_set_voltage_min_design(axp20x_batt, 621f8c91baeSQuentin Schulz vmin)) 622f8c91baeSQuentin Schulz dev_err(&pdev->dev, 623f8c91baeSQuentin Schulz "couldn't set voltage_min_design\n"); 624c8003844SQuentin Schulz 625c8003844SQuentin Schulz /* Set max to unverified value to be able to set CCC */ 626c8003844SQuentin Schulz axp20x_batt->max_ccc = ccc; 627c8003844SQuentin Schulz 628c8003844SQuentin Schulz if (ccc <= 0 || axp20x_set_constant_charge_current(axp20x_batt, 629c8003844SQuentin Schulz ccc)) { 630c8003844SQuentin Schulz dev_err(&pdev->dev, 631c8003844SQuentin Schulz "couldn't set constant charge current from DT: fallback to minimum value\n"); 632c8003844SQuentin Schulz ccc = 300000; 633c8003844SQuentin Schulz axp20x_batt->max_ccc = ccc; 634c8003844SQuentin Schulz axp20x_set_constant_charge_current(axp20x_batt, ccc); 635f8c91baeSQuentin Schulz } 636c8003844SQuentin Schulz } 637c8003844SQuentin Schulz 638c8003844SQuentin Schulz /* 639c8003844SQuentin Schulz * Update max CCC to a valid value if battery info is present or set it 640c8003844SQuentin Schulz * to current register value by default. 641c8003844SQuentin Schulz */ 642c8003844SQuentin Schulz axp20x_get_constant_charge_current(axp20x_batt, 643c8003844SQuentin Schulz &axp20x_batt->max_ccc); 644f8c91baeSQuentin Schulz 64546c202b5SQuentin Schulz return 0; 64646c202b5SQuentin Schulz } 64746c202b5SQuentin Schulz 64846c202b5SQuentin Schulz static struct platform_driver axp20x_batt_driver = { 64946c202b5SQuentin Schulz .probe = axp20x_power_probe, 65046c202b5SQuentin Schulz .driver = { 65146c202b5SQuentin Schulz .name = "axp20x-battery-power-supply", 65246c202b5SQuentin Schulz .of_match_table = axp20x_battery_ps_id, 65346c202b5SQuentin Schulz }, 65446c202b5SQuentin Schulz }; 65546c202b5SQuentin Schulz 65646c202b5SQuentin Schulz module_platform_driver(axp20x_batt_driver); 65746c202b5SQuentin Schulz 65846c202b5SQuentin Schulz MODULE_DESCRIPTION("Battery power supply driver for AXP20X and AXP22X PMICs"); 65946c202b5SQuentin Schulz MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>"); 66046c202b5SQuentin Schulz MODULE_LICENSE("GPL"); 661