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, &reg);
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, &reg);
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, &reg);
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				  &reg);
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				  &reg);
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				  &reg);
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				  &reg);
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, &reg);
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, &reg);
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, &current_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