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 4346c202b5SQuentin Schulz #define AXP20X_CHRG_CTRL1_TGT_VOLT GENMASK(6, 5) 4446c202b5SQuentin Schulz #define AXP20X_CHRG_CTRL1_TGT_4_1V (0 << 5) 4546c202b5SQuentin Schulz #define AXP20X_CHRG_CTRL1_TGT_4_15V (1 << 5) 4646c202b5SQuentin Schulz #define AXP20X_CHRG_CTRL1_TGT_4_2V (2 << 5) 4746c202b5SQuentin Schulz #define AXP20X_CHRG_CTRL1_TGT_4_36V (3 << 5) 4846c202b5SQuentin Schulz 4946c202b5SQuentin Schulz #define AXP22X_CHRG_CTRL1_TGT_4_22V (1 << 5) 5046c202b5SQuentin Schulz #define AXP22X_CHRG_CTRL1_TGT_4_24V (3 << 5) 5146c202b5SQuentin Schulz 5246c202b5SQuentin Schulz #define AXP20X_CHRG_CTRL1_TGT_CURR GENMASK(3, 0) 5346c202b5SQuentin Schulz 5446c202b5SQuentin Schulz #define AXP20X_V_OFF_MASK GENMASK(2, 0) 5546c202b5SQuentin Schulz 5646c202b5SQuentin Schulz struct axp20x_batt_ps { 5746c202b5SQuentin Schulz struct regmap *regmap; 5846c202b5SQuentin Schulz struct power_supply *batt; 5946c202b5SQuentin Schulz struct device *dev; 6046c202b5SQuentin Schulz struct iio_channel *batt_chrg_i; 6146c202b5SQuentin Schulz struct iio_channel *batt_dischrg_i; 6246c202b5SQuentin Schulz struct iio_channel *batt_v; 63*c8003844SQuentin Schulz /* Maximum constant charge current */ 64*c8003844SQuentin Schulz unsigned int max_ccc; 6546c202b5SQuentin Schulz u8 axp_id; 6646c202b5SQuentin Schulz }; 6746c202b5SQuentin Schulz 6846c202b5SQuentin Schulz static int axp20x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt, 6946c202b5SQuentin Schulz int *val) 7046c202b5SQuentin Schulz { 7146c202b5SQuentin Schulz int ret, reg; 7246c202b5SQuentin Schulz 7346c202b5SQuentin Schulz ret = regmap_read(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, ®); 7446c202b5SQuentin Schulz if (ret) 7546c202b5SQuentin Schulz return ret; 7646c202b5SQuentin Schulz 7746c202b5SQuentin Schulz switch (reg & AXP20X_CHRG_CTRL1_TGT_VOLT) { 7846c202b5SQuentin Schulz case AXP20X_CHRG_CTRL1_TGT_4_1V: 7946c202b5SQuentin Schulz *val = 4100000; 8046c202b5SQuentin Schulz break; 8146c202b5SQuentin Schulz case AXP20X_CHRG_CTRL1_TGT_4_15V: 8246c202b5SQuentin Schulz *val = 4150000; 8346c202b5SQuentin Schulz break; 8446c202b5SQuentin Schulz case AXP20X_CHRG_CTRL1_TGT_4_2V: 8546c202b5SQuentin Schulz *val = 4200000; 8646c202b5SQuentin Schulz break; 8746c202b5SQuentin Schulz case AXP20X_CHRG_CTRL1_TGT_4_36V: 8846c202b5SQuentin Schulz *val = 4360000; 8946c202b5SQuentin Schulz break; 9046c202b5SQuentin Schulz default: 9146c202b5SQuentin Schulz return -EINVAL; 9246c202b5SQuentin Schulz } 9346c202b5SQuentin Schulz 9446c202b5SQuentin Schulz return 0; 9546c202b5SQuentin Schulz } 9646c202b5SQuentin Schulz 9746c202b5SQuentin Schulz static int axp22x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt, 9846c202b5SQuentin Schulz int *val) 9946c202b5SQuentin Schulz { 10046c202b5SQuentin Schulz int ret, reg; 10146c202b5SQuentin Schulz 10246c202b5SQuentin Schulz ret = regmap_read(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, ®); 10346c202b5SQuentin Schulz if (ret) 10446c202b5SQuentin Schulz return ret; 10546c202b5SQuentin Schulz 10646c202b5SQuentin Schulz switch (reg & AXP20X_CHRG_CTRL1_TGT_VOLT) { 10746c202b5SQuentin Schulz case AXP20X_CHRG_CTRL1_TGT_4_1V: 10846c202b5SQuentin Schulz *val = 4100000; 10946c202b5SQuentin Schulz break; 11046c202b5SQuentin Schulz case AXP20X_CHRG_CTRL1_TGT_4_2V: 11146c202b5SQuentin Schulz *val = 4200000; 11246c202b5SQuentin Schulz break; 11346c202b5SQuentin Schulz case AXP22X_CHRG_CTRL1_TGT_4_22V: 11446c202b5SQuentin Schulz *val = 4220000; 11546c202b5SQuentin Schulz break; 11646c202b5SQuentin Schulz case AXP22X_CHRG_CTRL1_TGT_4_24V: 11746c202b5SQuentin Schulz *val = 4240000; 11846c202b5SQuentin Schulz break; 11946c202b5SQuentin Schulz default: 12046c202b5SQuentin Schulz return -EINVAL; 12146c202b5SQuentin Schulz } 12246c202b5SQuentin Schulz 12346c202b5SQuentin Schulz return 0; 12446c202b5SQuentin Schulz } 12546c202b5SQuentin Schulz 12646c202b5SQuentin Schulz static void raw_to_constant_charge_current(struct axp20x_batt_ps *axp, int *val) 12746c202b5SQuentin Schulz { 12846c202b5SQuentin Schulz if (axp->axp_id == AXP209_ID) 12946c202b5SQuentin Schulz *val = *val * 100000 + 300000; 13046c202b5SQuentin Schulz else 13146c202b5SQuentin Schulz *val = *val * 150000 + 300000; 13246c202b5SQuentin Schulz } 13346c202b5SQuentin Schulz 134*c8003844SQuentin Schulz static void constant_charge_current_to_raw(struct axp20x_batt_ps *axp, int *val) 135*c8003844SQuentin Schulz { 136*c8003844SQuentin Schulz if (axp->axp_id == AXP209_ID) 137*c8003844SQuentin Schulz *val = (*val - 300000) / 100000; 138*c8003844SQuentin Schulz else 139*c8003844SQuentin Schulz *val = (*val - 300000) / 150000; 140*c8003844SQuentin Schulz } 141*c8003844SQuentin Schulz 14246c202b5SQuentin Schulz static int axp20x_get_constant_charge_current(struct axp20x_batt_ps *axp, 14346c202b5SQuentin Schulz int *val) 14446c202b5SQuentin Schulz { 14546c202b5SQuentin Schulz int ret; 14646c202b5SQuentin Schulz 14746c202b5SQuentin Schulz ret = regmap_read(axp->regmap, AXP20X_CHRG_CTRL1, val); 14846c202b5SQuentin Schulz if (ret) 14946c202b5SQuentin Schulz return ret; 15046c202b5SQuentin Schulz 15146c202b5SQuentin Schulz *val &= AXP20X_CHRG_CTRL1_TGT_CURR; 15246c202b5SQuentin Schulz 15346c202b5SQuentin Schulz raw_to_constant_charge_current(axp, val); 15446c202b5SQuentin Schulz 15546c202b5SQuentin Schulz return 0; 15646c202b5SQuentin Schulz } 15746c202b5SQuentin Schulz 15846c202b5SQuentin Schulz static int axp20x_battery_get_prop(struct power_supply *psy, 15946c202b5SQuentin Schulz enum power_supply_property psp, 16046c202b5SQuentin Schulz union power_supply_propval *val) 16146c202b5SQuentin Schulz { 16246c202b5SQuentin Schulz struct axp20x_batt_ps *axp20x_batt = power_supply_get_drvdata(psy); 16346c202b5SQuentin Schulz struct iio_channel *chan; 16446c202b5SQuentin Schulz int ret = 0, reg, val1; 16546c202b5SQuentin Schulz 16646c202b5SQuentin Schulz switch (psp) { 16746c202b5SQuentin Schulz case POWER_SUPPLY_PROP_PRESENT: 16846c202b5SQuentin Schulz case POWER_SUPPLY_PROP_ONLINE: 16946c202b5SQuentin Schulz ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_OP_MODE, 17046c202b5SQuentin Schulz ®); 17146c202b5SQuentin Schulz if (ret) 17246c202b5SQuentin Schulz return ret; 17346c202b5SQuentin Schulz 17446c202b5SQuentin Schulz val->intval = !!(reg & AXP20X_PWR_OP_BATT_PRESENT); 17546c202b5SQuentin Schulz break; 17646c202b5SQuentin Schulz 17746c202b5SQuentin Schulz case POWER_SUPPLY_PROP_STATUS: 17846c202b5SQuentin Schulz ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_INPUT_STATUS, 17946c202b5SQuentin Schulz ®); 18046c202b5SQuentin Schulz if (ret) 18146c202b5SQuentin Schulz return ret; 18246c202b5SQuentin Schulz 18346c202b5SQuentin Schulz if (reg & AXP20X_PWR_STATUS_BAT_CHARGING) { 18446c202b5SQuentin Schulz val->intval = POWER_SUPPLY_STATUS_CHARGING; 18546c202b5SQuentin Schulz return 0; 18646c202b5SQuentin Schulz } 18746c202b5SQuentin Schulz 18846c202b5SQuentin Schulz ret = iio_read_channel_processed(axp20x_batt->batt_dischrg_i, 18946c202b5SQuentin Schulz &val1); 19046c202b5SQuentin Schulz if (ret) 19146c202b5SQuentin Schulz return ret; 19246c202b5SQuentin Schulz 19346c202b5SQuentin Schulz if (val1) { 19446c202b5SQuentin Schulz val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 19546c202b5SQuentin Schulz return 0; 19646c202b5SQuentin Schulz } 19746c202b5SQuentin Schulz 19846c202b5SQuentin Schulz ret = regmap_read(axp20x_batt->regmap, AXP20X_FG_RES, &val1); 19946c202b5SQuentin Schulz if (ret) 20046c202b5SQuentin Schulz return ret; 20146c202b5SQuentin Schulz 20246c202b5SQuentin Schulz /* 20346c202b5SQuentin Schulz * Fuel Gauge data takes 7 bits but the stored value seems to be 20446c202b5SQuentin Schulz * directly the raw percentage without any scaling to 7 bits. 20546c202b5SQuentin Schulz */ 20646c202b5SQuentin Schulz if ((val1 & AXP209_FG_PERCENT) == 100) 20746c202b5SQuentin Schulz val->intval = POWER_SUPPLY_STATUS_FULL; 20846c202b5SQuentin Schulz else 20946c202b5SQuentin Schulz val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 21046c202b5SQuentin Schulz break; 21146c202b5SQuentin Schulz 21246c202b5SQuentin Schulz case POWER_SUPPLY_PROP_HEALTH: 21346c202b5SQuentin Schulz ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_OP_MODE, 21446c202b5SQuentin Schulz &val1); 21546c202b5SQuentin Schulz if (ret) 21646c202b5SQuentin Schulz return ret; 21746c202b5SQuentin Schulz 21846c202b5SQuentin Schulz if (val1 & AXP20X_PWR_OP_BATT_ACTIVATED) { 21946c202b5SQuentin Schulz val->intval = POWER_SUPPLY_HEALTH_DEAD; 22046c202b5SQuentin Schulz return 0; 22146c202b5SQuentin Schulz } 22246c202b5SQuentin Schulz 22346c202b5SQuentin Schulz val->intval = POWER_SUPPLY_HEALTH_GOOD; 22446c202b5SQuentin Schulz break; 22546c202b5SQuentin Schulz 22646c202b5SQuentin Schulz case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: 22746c202b5SQuentin Schulz ret = axp20x_get_constant_charge_current(axp20x_batt, 22846c202b5SQuentin Schulz &val->intval); 22946c202b5SQuentin Schulz if (ret) 23046c202b5SQuentin Schulz return ret; 23146c202b5SQuentin Schulz break; 23246c202b5SQuentin Schulz 23346c202b5SQuentin Schulz case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: 234*c8003844SQuentin Schulz val->intval = axp20x_batt->max_ccc; 23546c202b5SQuentin Schulz break; 23646c202b5SQuentin Schulz 23746c202b5SQuentin Schulz case POWER_SUPPLY_PROP_CURRENT_NOW: 23846c202b5SQuentin Schulz ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_INPUT_STATUS, 23946c202b5SQuentin Schulz ®); 24046c202b5SQuentin Schulz if (ret) 24146c202b5SQuentin Schulz return ret; 24246c202b5SQuentin Schulz 24346c202b5SQuentin Schulz if (reg & AXP20X_PWR_STATUS_BAT_CHARGING) 24446c202b5SQuentin Schulz chan = axp20x_batt->batt_chrg_i; 24546c202b5SQuentin Schulz else 24646c202b5SQuentin Schulz chan = axp20x_batt->batt_dischrg_i; 24746c202b5SQuentin Schulz 24846c202b5SQuentin Schulz ret = iio_read_channel_processed(chan, &val->intval); 24946c202b5SQuentin Schulz if (ret) 25046c202b5SQuentin Schulz return ret; 25146c202b5SQuentin Schulz 25246c202b5SQuentin Schulz /* IIO framework gives mA but Power Supply framework gives uA */ 25346c202b5SQuentin Schulz val->intval *= 1000; 25446c202b5SQuentin Schulz break; 25546c202b5SQuentin Schulz 25646c202b5SQuentin Schulz case POWER_SUPPLY_PROP_CAPACITY: 25746c202b5SQuentin Schulz /* When no battery is present, return capacity is 100% */ 25846c202b5SQuentin Schulz ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_OP_MODE, 25946c202b5SQuentin Schulz ®); 26046c202b5SQuentin Schulz if (ret) 26146c202b5SQuentin Schulz return ret; 26246c202b5SQuentin Schulz 26346c202b5SQuentin Schulz if (!(reg & AXP20X_PWR_OP_BATT_PRESENT)) { 26446c202b5SQuentin Schulz val->intval = 100; 26546c202b5SQuentin Schulz return 0; 26646c202b5SQuentin Schulz } 26746c202b5SQuentin Schulz 26846c202b5SQuentin Schulz ret = regmap_read(axp20x_batt->regmap, AXP20X_FG_RES, ®); 26946c202b5SQuentin Schulz if (ret) 27046c202b5SQuentin Schulz return ret; 27146c202b5SQuentin Schulz 27246c202b5SQuentin Schulz if (axp20x_batt->axp_id == AXP221_ID && 27346c202b5SQuentin Schulz !(reg & AXP22X_FG_VALID)) 27446c202b5SQuentin Schulz return -EINVAL; 27546c202b5SQuentin Schulz 27646c202b5SQuentin Schulz /* 27746c202b5SQuentin Schulz * Fuel Gauge data takes 7 bits but the stored value seems to be 27846c202b5SQuentin Schulz * directly the raw percentage without any scaling to 7 bits. 27946c202b5SQuentin Schulz */ 28046c202b5SQuentin Schulz val->intval = reg & AXP209_FG_PERCENT; 28146c202b5SQuentin Schulz break; 28246c202b5SQuentin Schulz 28346c202b5SQuentin Schulz case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: 28446c202b5SQuentin Schulz if (axp20x_batt->axp_id == AXP209_ID) 28546c202b5SQuentin Schulz return axp20x_battery_get_max_voltage(axp20x_batt, 28646c202b5SQuentin Schulz &val->intval); 28746c202b5SQuentin Schulz return axp22x_battery_get_max_voltage(axp20x_batt, 28846c202b5SQuentin Schulz &val->intval); 28946c202b5SQuentin Schulz 29046c202b5SQuentin Schulz case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 29146c202b5SQuentin Schulz ret = regmap_read(axp20x_batt->regmap, AXP20X_V_OFF, ®); 29246c202b5SQuentin Schulz if (ret) 29346c202b5SQuentin Schulz return ret; 29446c202b5SQuentin Schulz 29546c202b5SQuentin Schulz val->intval = 2600000 + 100000 * (reg & AXP20X_V_OFF_MASK); 29646c202b5SQuentin Schulz break; 29746c202b5SQuentin Schulz 29846c202b5SQuentin Schulz case POWER_SUPPLY_PROP_VOLTAGE_NOW: 29946c202b5SQuentin Schulz ret = iio_read_channel_processed(axp20x_batt->batt_v, 30046c202b5SQuentin Schulz &val->intval); 30146c202b5SQuentin Schulz if (ret) 30246c202b5SQuentin Schulz return ret; 30346c202b5SQuentin Schulz 30446c202b5SQuentin Schulz /* IIO framework gives mV but Power Supply framework gives uV */ 30546c202b5SQuentin Schulz val->intval *= 1000; 30646c202b5SQuentin Schulz break; 30746c202b5SQuentin Schulz 30846c202b5SQuentin Schulz default: 30946c202b5SQuentin Schulz return -EINVAL; 31046c202b5SQuentin Schulz } 31146c202b5SQuentin Schulz 31246c202b5SQuentin Schulz return 0; 31346c202b5SQuentin Schulz } 31446c202b5SQuentin Schulz 31546c202b5SQuentin Schulz static int axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt, 31646c202b5SQuentin Schulz int val) 31746c202b5SQuentin Schulz { 31846c202b5SQuentin Schulz switch (val) { 31946c202b5SQuentin Schulz case 4100000: 32046c202b5SQuentin Schulz val = AXP20X_CHRG_CTRL1_TGT_4_1V; 32146c202b5SQuentin Schulz break; 32246c202b5SQuentin Schulz 32346c202b5SQuentin Schulz case 4150000: 32446c202b5SQuentin Schulz if (axp20x_batt->axp_id == AXP221_ID) 32546c202b5SQuentin Schulz return -EINVAL; 32646c202b5SQuentin Schulz 32746c202b5SQuentin Schulz val = AXP20X_CHRG_CTRL1_TGT_4_15V; 32846c202b5SQuentin Schulz break; 32946c202b5SQuentin Schulz 33046c202b5SQuentin Schulz case 4200000: 33146c202b5SQuentin Schulz val = AXP20X_CHRG_CTRL1_TGT_4_2V; 33246c202b5SQuentin Schulz break; 33346c202b5SQuentin Schulz 33446c202b5SQuentin Schulz default: 33546c202b5SQuentin Schulz /* 33646c202b5SQuentin Schulz * AXP20x max voltage can be set to 4.36V and AXP22X max voltage 33746c202b5SQuentin Schulz * can be set to 4.22V and 4.24V, but these voltages are too 33846c202b5SQuentin Schulz * high for Lithium based batteries (AXP PMICs are supposed to 33946c202b5SQuentin Schulz * be used with these kinds of battery). 34046c202b5SQuentin Schulz */ 34146c202b5SQuentin Schulz return -EINVAL; 34246c202b5SQuentin Schulz } 34346c202b5SQuentin Schulz 34446c202b5SQuentin Schulz return regmap_update_bits(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, 34546c202b5SQuentin Schulz AXP20X_CHRG_CTRL1_TGT_VOLT, val); 34646c202b5SQuentin Schulz } 34746c202b5SQuentin Schulz 34846c202b5SQuentin Schulz static int axp20x_set_constant_charge_current(struct axp20x_batt_ps *axp_batt, 34946c202b5SQuentin Schulz int charge_current) 35046c202b5SQuentin Schulz { 351*c8003844SQuentin Schulz if (charge_current > axp_batt->max_ccc) 352*c8003844SQuentin Schulz return -EINVAL; 353*c8003844SQuentin Schulz 354*c8003844SQuentin Schulz constant_charge_current_to_raw(axp_batt, &charge_current); 35546c202b5SQuentin Schulz 35646c202b5SQuentin Schulz if (charge_current > AXP20X_CHRG_CTRL1_TGT_CURR || charge_current < 0) 35746c202b5SQuentin Schulz return -EINVAL; 35846c202b5SQuentin Schulz 35946c202b5SQuentin Schulz return regmap_update_bits(axp_batt->regmap, AXP20X_CHRG_CTRL1, 36046c202b5SQuentin Schulz AXP20X_CHRG_CTRL1_TGT_CURR, charge_current); 36146c202b5SQuentin Schulz } 36246c202b5SQuentin Schulz 363*c8003844SQuentin Schulz static int axp20x_set_max_constant_charge_current(struct axp20x_batt_ps *axp, 364*c8003844SQuentin Schulz int charge_current) 365*c8003844SQuentin Schulz { 366*c8003844SQuentin Schulz bool lower_max = false; 367*c8003844SQuentin Schulz 368*c8003844SQuentin Schulz constant_charge_current_to_raw(axp, &charge_current); 369*c8003844SQuentin Schulz 370*c8003844SQuentin Schulz if (charge_current > AXP20X_CHRG_CTRL1_TGT_CURR || charge_current < 0) 371*c8003844SQuentin Schulz return -EINVAL; 372*c8003844SQuentin Schulz 373*c8003844SQuentin Schulz raw_to_constant_charge_current(axp, &charge_current); 374*c8003844SQuentin Schulz 375*c8003844SQuentin Schulz if (charge_current > axp->max_ccc) 376*c8003844SQuentin Schulz dev_warn(axp->dev, 377*c8003844SQuentin Schulz "Setting max constant charge current higher than previously defined. Note that increasing the constant charge current may damage your battery.\n"); 378*c8003844SQuentin Schulz else 379*c8003844SQuentin Schulz lower_max = true; 380*c8003844SQuentin Schulz 381*c8003844SQuentin Schulz axp->max_ccc = charge_current; 382*c8003844SQuentin Schulz 383*c8003844SQuentin Schulz if (lower_max) { 384*c8003844SQuentin Schulz int current_cc; 385*c8003844SQuentin Schulz 386*c8003844SQuentin Schulz axp20x_get_constant_charge_current(axp, ¤t_cc); 387*c8003844SQuentin Schulz if (current_cc > charge_current) 388*c8003844SQuentin Schulz axp20x_set_constant_charge_current(axp, charge_current); 389*c8003844SQuentin Schulz } 390*c8003844SQuentin Schulz 391*c8003844SQuentin Schulz return 0; 392*c8003844SQuentin Schulz } 39346c202b5SQuentin Schulz static int axp20x_set_voltage_min_design(struct axp20x_batt_ps *axp_batt, 39446c202b5SQuentin Schulz int min_voltage) 39546c202b5SQuentin Schulz { 39646c202b5SQuentin Schulz int val1 = (min_voltage - 2600000) / 100000; 39746c202b5SQuentin Schulz 39846c202b5SQuentin Schulz if (val1 < 0 || val1 > AXP20X_V_OFF_MASK) 39946c202b5SQuentin Schulz return -EINVAL; 40046c202b5SQuentin Schulz 40146c202b5SQuentin Schulz return regmap_update_bits(axp_batt->regmap, AXP20X_V_OFF, 40246c202b5SQuentin Schulz AXP20X_V_OFF_MASK, val1); 40346c202b5SQuentin Schulz } 40446c202b5SQuentin Schulz 40546c202b5SQuentin Schulz static int axp20x_battery_set_prop(struct power_supply *psy, 40646c202b5SQuentin Schulz enum power_supply_property psp, 40746c202b5SQuentin Schulz const union power_supply_propval *val) 40846c202b5SQuentin Schulz { 40946c202b5SQuentin Schulz struct axp20x_batt_ps *axp20x_batt = power_supply_get_drvdata(psy); 41046c202b5SQuentin Schulz 41146c202b5SQuentin Schulz switch (psp) { 41246c202b5SQuentin Schulz case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 41346c202b5SQuentin Schulz return axp20x_set_voltage_min_design(axp20x_batt, val->intval); 41446c202b5SQuentin Schulz 41546c202b5SQuentin Schulz case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: 41646c202b5SQuentin Schulz return axp20x_battery_set_max_voltage(axp20x_batt, val->intval); 41746c202b5SQuentin Schulz 41846c202b5SQuentin Schulz case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: 41946c202b5SQuentin Schulz return axp20x_set_constant_charge_current(axp20x_batt, 42046c202b5SQuentin Schulz val->intval); 421*c8003844SQuentin Schulz case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: 422*c8003844SQuentin Schulz return axp20x_set_max_constant_charge_current(axp20x_batt, 423*c8003844SQuentin Schulz val->intval); 42446c202b5SQuentin Schulz 42546c202b5SQuentin Schulz default: 42646c202b5SQuentin Schulz return -EINVAL; 42746c202b5SQuentin Schulz } 42846c202b5SQuentin Schulz } 42946c202b5SQuentin Schulz 43046c202b5SQuentin Schulz static enum power_supply_property axp20x_battery_props[] = { 43146c202b5SQuentin Schulz POWER_SUPPLY_PROP_PRESENT, 43246c202b5SQuentin Schulz POWER_SUPPLY_PROP_ONLINE, 43346c202b5SQuentin Schulz POWER_SUPPLY_PROP_STATUS, 43446c202b5SQuentin Schulz POWER_SUPPLY_PROP_VOLTAGE_NOW, 43546c202b5SQuentin Schulz POWER_SUPPLY_PROP_CURRENT_NOW, 43646c202b5SQuentin Schulz POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, 43746c202b5SQuentin Schulz POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, 43846c202b5SQuentin Schulz POWER_SUPPLY_PROP_HEALTH, 43946c202b5SQuentin Schulz POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 44046c202b5SQuentin Schulz POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 44146c202b5SQuentin Schulz POWER_SUPPLY_PROP_CAPACITY, 44246c202b5SQuentin Schulz }; 44346c202b5SQuentin Schulz 44446c202b5SQuentin Schulz static int axp20x_battery_prop_writeable(struct power_supply *psy, 44546c202b5SQuentin Schulz enum power_supply_property psp) 44646c202b5SQuentin Schulz { 44746c202b5SQuentin Schulz return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN || 44846c202b5SQuentin Schulz psp == POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN || 449*c8003844SQuentin Schulz psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT || 450*c8003844SQuentin Schulz psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX; 45146c202b5SQuentin Schulz } 45246c202b5SQuentin Schulz 45346c202b5SQuentin Schulz static const struct power_supply_desc axp20x_batt_ps_desc = { 45446c202b5SQuentin Schulz .name = "axp20x-battery", 45546c202b5SQuentin Schulz .type = POWER_SUPPLY_TYPE_BATTERY, 45646c202b5SQuentin Schulz .properties = axp20x_battery_props, 45746c202b5SQuentin Schulz .num_properties = ARRAY_SIZE(axp20x_battery_props), 45846c202b5SQuentin Schulz .property_is_writeable = axp20x_battery_prop_writeable, 45946c202b5SQuentin Schulz .get_property = axp20x_battery_get_prop, 46046c202b5SQuentin Schulz .set_property = axp20x_battery_set_prop, 46146c202b5SQuentin Schulz }; 46246c202b5SQuentin Schulz 46346c202b5SQuentin Schulz static const struct of_device_id axp20x_battery_ps_id[] = { 46446c202b5SQuentin Schulz { 46546c202b5SQuentin Schulz .compatible = "x-powers,axp209-battery-power-supply", 46646c202b5SQuentin Schulz .data = (void *)AXP209_ID, 46746c202b5SQuentin Schulz }, { 46846c202b5SQuentin Schulz .compatible = "x-powers,axp221-battery-power-supply", 46946c202b5SQuentin Schulz .data = (void *)AXP221_ID, 47046c202b5SQuentin Schulz }, { /* sentinel */ }, 47146c202b5SQuentin Schulz }; 47246c202b5SQuentin Schulz MODULE_DEVICE_TABLE(of, axp20x_battery_ps_id); 47346c202b5SQuentin Schulz 47446c202b5SQuentin Schulz static int axp20x_power_probe(struct platform_device *pdev) 47546c202b5SQuentin Schulz { 47646c202b5SQuentin Schulz struct axp20x_batt_ps *axp20x_batt; 47746c202b5SQuentin Schulz struct power_supply_config psy_cfg = {}; 478f8c91baeSQuentin Schulz struct power_supply_battery_info info; 47946c202b5SQuentin Schulz 48046c202b5SQuentin Schulz if (!of_device_is_available(pdev->dev.of_node)) 48146c202b5SQuentin Schulz return -ENODEV; 48246c202b5SQuentin Schulz 48346c202b5SQuentin Schulz axp20x_batt = devm_kzalloc(&pdev->dev, sizeof(*axp20x_batt), 48446c202b5SQuentin Schulz GFP_KERNEL); 48546c202b5SQuentin Schulz if (!axp20x_batt) 48646c202b5SQuentin Schulz return -ENOMEM; 48746c202b5SQuentin Schulz 48846c202b5SQuentin Schulz axp20x_batt->dev = &pdev->dev; 48946c202b5SQuentin Schulz 49046c202b5SQuentin Schulz axp20x_batt->batt_v = devm_iio_channel_get(&pdev->dev, "batt_v"); 49146c202b5SQuentin Schulz if (IS_ERR(axp20x_batt->batt_v)) { 49246c202b5SQuentin Schulz if (PTR_ERR(axp20x_batt->batt_v) == -ENODEV) 49346c202b5SQuentin Schulz return -EPROBE_DEFER; 49446c202b5SQuentin Schulz return PTR_ERR(axp20x_batt->batt_v); 49546c202b5SQuentin Schulz } 49646c202b5SQuentin Schulz 49746c202b5SQuentin Schulz axp20x_batt->batt_chrg_i = devm_iio_channel_get(&pdev->dev, 49846c202b5SQuentin Schulz "batt_chrg_i"); 49946c202b5SQuentin Schulz if (IS_ERR(axp20x_batt->batt_chrg_i)) { 50046c202b5SQuentin Schulz if (PTR_ERR(axp20x_batt->batt_chrg_i) == -ENODEV) 50146c202b5SQuentin Schulz return -EPROBE_DEFER; 50246c202b5SQuentin Schulz return PTR_ERR(axp20x_batt->batt_chrg_i); 50346c202b5SQuentin Schulz } 50446c202b5SQuentin Schulz 50546c202b5SQuentin Schulz axp20x_batt->batt_dischrg_i = devm_iio_channel_get(&pdev->dev, 50646c202b5SQuentin Schulz "batt_dischrg_i"); 50746c202b5SQuentin Schulz if (IS_ERR(axp20x_batt->batt_dischrg_i)) { 50846c202b5SQuentin Schulz if (PTR_ERR(axp20x_batt->batt_dischrg_i) == -ENODEV) 50946c202b5SQuentin Schulz return -EPROBE_DEFER; 51046c202b5SQuentin Schulz return PTR_ERR(axp20x_batt->batt_dischrg_i); 51146c202b5SQuentin Schulz } 51246c202b5SQuentin Schulz 51346c202b5SQuentin Schulz axp20x_batt->regmap = dev_get_regmap(pdev->dev.parent, NULL); 51446c202b5SQuentin Schulz platform_set_drvdata(pdev, axp20x_batt); 51546c202b5SQuentin Schulz 51646c202b5SQuentin Schulz psy_cfg.drv_data = axp20x_batt; 51746c202b5SQuentin Schulz psy_cfg.of_node = pdev->dev.of_node; 51846c202b5SQuentin Schulz 51946c202b5SQuentin Schulz axp20x_batt->axp_id = (uintptr_t)of_device_get_match_data(&pdev->dev); 52046c202b5SQuentin Schulz 52146c202b5SQuentin Schulz axp20x_batt->batt = devm_power_supply_register(&pdev->dev, 52246c202b5SQuentin Schulz &axp20x_batt_ps_desc, 52346c202b5SQuentin Schulz &psy_cfg); 52446c202b5SQuentin Schulz if (IS_ERR(axp20x_batt->batt)) { 52546c202b5SQuentin Schulz dev_err(&pdev->dev, "failed to register power supply: %ld\n", 52646c202b5SQuentin Schulz PTR_ERR(axp20x_batt->batt)); 52746c202b5SQuentin Schulz return PTR_ERR(axp20x_batt->batt); 52846c202b5SQuentin Schulz } 52946c202b5SQuentin Schulz 530f8c91baeSQuentin Schulz if (!power_supply_get_battery_info(axp20x_batt->batt, &info)) { 531f8c91baeSQuentin Schulz int vmin = info.voltage_min_design_uv; 532*c8003844SQuentin Schulz int ccc = info.constant_charge_current_max_ua; 533f8c91baeSQuentin Schulz 534f8c91baeSQuentin Schulz if (vmin > 0 && axp20x_set_voltage_min_design(axp20x_batt, 535f8c91baeSQuentin Schulz vmin)) 536f8c91baeSQuentin Schulz dev_err(&pdev->dev, 537f8c91baeSQuentin Schulz "couldn't set voltage_min_design\n"); 538*c8003844SQuentin Schulz 539*c8003844SQuentin Schulz /* Set max to unverified value to be able to set CCC */ 540*c8003844SQuentin Schulz axp20x_batt->max_ccc = ccc; 541*c8003844SQuentin Schulz 542*c8003844SQuentin Schulz if (ccc <= 0 || axp20x_set_constant_charge_current(axp20x_batt, 543*c8003844SQuentin Schulz ccc)) { 544*c8003844SQuentin Schulz dev_err(&pdev->dev, 545*c8003844SQuentin Schulz "couldn't set constant charge current from DT: fallback to minimum value\n"); 546*c8003844SQuentin Schulz ccc = 300000; 547*c8003844SQuentin Schulz axp20x_batt->max_ccc = ccc; 548*c8003844SQuentin Schulz axp20x_set_constant_charge_current(axp20x_batt, ccc); 549f8c91baeSQuentin Schulz } 550*c8003844SQuentin Schulz } 551*c8003844SQuentin Schulz 552*c8003844SQuentin Schulz /* 553*c8003844SQuentin Schulz * Update max CCC to a valid value if battery info is present or set it 554*c8003844SQuentin Schulz * to current register value by default. 555*c8003844SQuentin Schulz */ 556*c8003844SQuentin Schulz axp20x_get_constant_charge_current(axp20x_batt, 557*c8003844SQuentin Schulz &axp20x_batt->max_ccc); 558f8c91baeSQuentin Schulz 55946c202b5SQuentin Schulz return 0; 56046c202b5SQuentin Schulz } 56146c202b5SQuentin Schulz 56246c202b5SQuentin Schulz static struct platform_driver axp20x_batt_driver = { 56346c202b5SQuentin Schulz .probe = axp20x_power_probe, 56446c202b5SQuentin Schulz .driver = { 56546c202b5SQuentin Schulz .name = "axp20x-battery-power-supply", 56646c202b5SQuentin Schulz .of_match_table = axp20x_battery_ps_id, 56746c202b5SQuentin Schulz }, 56846c202b5SQuentin Schulz }; 56946c202b5SQuentin Schulz 57046c202b5SQuentin Schulz module_platform_driver(axp20x_batt_driver); 57146c202b5SQuentin Schulz 57246c202b5SQuentin Schulz MODULE_DESCRIPTION("Battery power supply driver for AXP20X and AXP22X PMICs"); 57346c202b5SQuentin Schulz MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>"); 57446c202b5SQuentin Schulz MODULE_LICENSE("GPL"); 575