175853406SSamuel Holland // SPDX-License-Identifier: GPL-2.0
275853406SSamuel Holland //
375853406SSamuel Holland // Copyright (C) 2021 Samuel Holland <samuel@sholland.org>
475853406SSamuel Holland 
575853406SSamuel Holland #include <linux/i2c.h>
675853406SSamuel Holland #include <linux/module.h>
775853406SSamuel Holland #include <linux/power_supply.h>
875853406SSamuel Holland #include <linux/regmap.h>
975853406SSamuel Holland 
1075853406SSamuel Holland #define IP5XXX_SYS_CTL0			0x01
1175853406SSamuel Holland #define IP5XXX_SYS_CTL0_WLED_DET_EN		BIT(4)
1275853406SSamuel Holland #define IP5XXX_SYS_CTL0_WLED_EN			BIT(3)
1375853406SSamuel Holland #define IP5XXX_SYS_CTL0_BOOST_EN		BIT(2)
1475853406SSamuel Holland #define IP5XXX_SYS_CTL0_CHARGER_EN		BIT(1)
1575853406SSamuel Holland #define IP5XXX_SYS_CTL1			0x02
1675853406SSamuel Holland #define IP5XXX_SYS_CTL1_LIGHT_SHDN_EN		BIT(1)
1775853406SSamuel Holland #define IP5XXX_SYS_CTL1_LOAD_PWRUP_EN		BIT(0)
1875853406SSamuel Holland #define IP5XXX_SYS_CTL2			0x0c
1975853406SSamuel Holland #define IP5XXX_SYS_CTL2_LIGHT_SHDN_TH		GENMASK(7, 3)
2075853406SSamuel Holland #define IP5XXX_SYS_CTL3			0x03
2175853406SSamuel Holland #define IP5XXX_SYS_CTL3_LONG_PRESS_TIME_SEL	GENMASK(7, 6)
2275853406SSamuel Holland #define IP5XXX_SYS_CTL3_BTN_SHDN_EN		BIT(5)
2375853406SSamuel Holland #define IP5XXX_SYS_CTL4			0x04
2475853406SSamuel Holland #define IP5XXX_SYS_CTL4_SHDN_TIME_SEL		GENMASK(7, 6)
2575853406SSamuel Holland #define IP5XXX_SYS_CTL4_VIN_PULLOUT_BOOST_EN	BIT(5)
2675853406SSamuel Holland #define IP5XXX_SYS_CTL5			0x07
2775853406SSamuel Holland #define IP5XXX_SYS_CTL5_NTC_DIS			BIT(6)
2875853406SSamuel Holland #define IP5XXX_SYS_CTL5_WLED_MODE_SEL		BIT(1)
2975853406SSamuel Holland #define IP5XXX_SYS_CTL5_BTN_SHDN_SEL		BIT(0)
3075853406SSamuel Holland #define IP5XXX_CHG_CTL1			0x22
3175853406SSamuel Holland #define IP5XXX_CHG_CTL1_BOOST_UVP_SEL		GENMASK(3, 2)
3275853406SSamuel Holland #define IP5XXX_CHG_CTL2			0x24
3375853406SSamuel Holland #define IP5XXX_CHG_CTL2_BAT_TYPE_SEL		GENMASK(6, 5)
3475853406SSamuel Holland #define IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_2V	(0x0 << 5)
3575853406SSamuel Holland #define IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_3V	(0x1 << 5)
3675853406SSamuel Holland #define IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_35V	(0x2 << 5)
3775853406SSamuel Holland #define IP5XXX_CHG_CTL2_CONST_VOLT_SEL		GENMASK(2, 1)
3875853406SSamuel Holland #define IP5XXX_CHG_CTL4			0x26
3975853406SSamuel Holland #define IP5XXX_CHG_CTL4_BAT_TYPE_SEL_EN		BIT(6)
4075853406SSamuel Holland #define IP5XXX_CHG_CTL4A		0x25
4175853406SSamuel Holland #define IP5XXX_CHG_CTL4A_CONST_CUR_SEL		GENMASK(4, 0)
4275853406SSamuel Holland #define IP5XXX_MFP_CTL0			0x51
4375853406SSamuel Holland #define IP5XXX_MFP_CTL1			0x52
4475853406SSamuel Holland #define IP5XXX_GPIO_CTL2		0x53
4575853406SSamuel Holland #define IP5XXX_GPIO_CTL2A		0x54
4675853406SSamuel Holland #define IP5XXX_GPIO_CTL3		0x55
4775853406SSamuel Holland #define IP5XXX_READ0			0x71
4875853406SSamuel Holland #define IP5XXX_READ0_CHG_STAT			GENMASK(7, 5)
4975853406SSamuel Holland #define IP5XXX_READ0_CHG_STAT_IDLE		(0x0 << 5)
5075853406SSamuel Holland #define IP5XXX_READ0_CHG_STAT_TRICKLE		(0x1 << 5)
5175853406SSamuel Holland #define IP5XXX_READ0_CHG_STAT_CONST_VOLT	(0x2 << 5)
5275853406SSamuel Holland #define IP5XXX_READ0_CHG_STAT_CONST_CUR		(0x3 << 5)
5375853406SSamuel Holland #define IP5XXX_READ0_CHG_STAT_CONST_VOLT_STOP	(0x4 << 5)
5475853406SSamuel Holland #define IP5XXX_READ0_CHG_STAT_FULL		(0x5 << 5)
5575853406SSamuel Holland #define IP5XXX_READ0_CHG_STAT_TIMEOUT		(0x6 << 5)
5675853406SSamuel Holland #define IP5XXX_READ0_CHG_OP			BIT(4)
5775853406SSamuel Holland #define IP5XXX_READ0_CHG_END			BIT(3)
5875853406SSamuel Holland #define IP5XXX_READ0_CONST_VOLT_TIMEOUT		BIT(2)
5975853406SSamuel Holland #define IP5XXX_READ0_CHG_TIMEOUT		BIT(1)
6075853406SSamuel Holland #define IP5XXX_READ0_TRICKLE_TIMEOUT		BIT(0)
6175853406SSamuel Holland #define IP5XXX_READ0_TIMEOUT			GENMASK(2, 0)
6275853406SSamuel Holland #define IP5XXX_READ1			0x72
6375853406SSamuel Holland #define IP5XXX_READ1_WLED_PRESENT		BIT(7)
6475853406SSamuel Holland #define IP5XXX_READ1_LIGHT_LOAD			BIT(6)
6575853406SSamuel Holland #define IP5XXX_READ1_VIN_OVERVOLT		BIT(5)
6675853406SSamuel Holland #define IP5XXX_READ2			0x77
6775853406SSamuel Holland #define IP5XXX_READ2_BTN_PRESS			BIT(3)
6875853406SSamuel Holland #define IP5XXX_READ2_BTN_LONG_PRESS		BIT(1)
6975853406SSamuel Holland #define IP5XXX_READ2_BTN_SHORT_PRESS		BIT(0)
7075853406SSamuel Holland #define IP5XXX_BATVADC_DAT0		0xa2
7175853406SSamuel Holland #define IP5XXX_BATVADC_DAT1		0xa3
7275853406SSamuel Holland #define IP5XXX_BATIADC_DAT0		0xa4
7375853406SSamuel Holland #define IP5XXX_BATIADC_DAT1		0xa5
7475853406SSamuel Holland #define IP5XXX_BATOCV_DAT0		0xa8
7575853406SSamuel Holland #define IP5XXX_BATOCV_DAT1		0xa9
7675853406SSamuel Holland 
7775853406SSamuel Holland struct ip5xxx {
7875853406SSamuel Holland 	struct regmap *regmap;
7975853406SSamuel Holland 	bool initialized;
8075853406SSamuel Holland };
8175853406SSamuel Holland 
8275853406SSamuel Holland /*
8375853406SSamuel Holland  * The IP5xxx charger only responds on I2C when it is "awake". The charger is
8475853406SSamuel Holland  * generally only awake when VIN is powered or when its boost converter is
8575853406SSamuel Holland  * enabled. Going into shutdown resets all register values. To handle this:
8675853406SSamuel Holland  *  1) When any bus error occurs, assume the charger has gone into shutdown.
8775853406SSamuel Holland  *  2) Attempt the initialization sequence on each subsequent register access
8875853406SSamuel Holland  *     until it succeeds.
8975853406SSamuel Holland  */
ip5xxx_read(struct ip5xxx * ip5xxx,unsigned int reg,unsigned int * val)9075853406SSamuel Holland static int ip5xxx_read(struct ip5xxx *ip5xxx, unsigned int reg,
9175853406SSamuel Holland 		       unsigned int *val)
9275853406SSamuel Holland {
9375853406SSamuel Holland 	int ret;
9475853406SSamuel Holland 
9575853406SSamuel Holland 	ret = regmap_read(ip5xxx->regmap, reg, val);
9675853406SSamuel Holland 	if (ret)
9775853406SSamuel Holland 		ip5xxx->initialized = false;
9875853406SSamuel Holland 
9975853406SSamuel Holland 	return ret;
10075853406SSamuel Holland }
10175853406SSamuel Holland 
ip5xxx_update_bits(struct ip5xxx * ip5xxx,unsigned int reg,unsigned int mask,unsigned int val)10275853406SSamuel Holland static int ip5xxx_update_bits(struct ip5xxx *ip5xxx, unsigned int reg,
10375853406SSamuel Holland 			      unsigned int mask, unsigned int val)
10475853406SSamuel Holland {
10575853406SSamuel Holland 	int ret;
10675853406SSamuel Holland 
10775853406SSamuel Holland 	ret = regmap_update_bits(ip5xxx->regmap, reg, mask, val);
10875853406SSamuel Holland 	if (ret)
10975853406SSamuel Holland 		ip5xxx->initialized = false;
11075853406SSamuel Holland 
11175853406SSamuel Holland 	return ret;
11275853406SSamuel Holland }
11375853406SSamuel Holland 
ip5xxx_initialize(struct power_supply * psy)11475853406SSamuel Holland static int ip5xxx_initialize(struct power_supply *psy)
11575853406SSamuel Holland {
11675853406SSamuel Holland 	struct ip5xxx *ip5xxx = power_supply_get_drvdata(psy);
11775853406SSamuel Holland 	int ret;
11875853406SSamuel Holland 
11975853406SSamuel Holland 	if (ip5xxx->initialized)
12075853406SSamuel Holland 		return 0;
12175853406SSamuel Holland 
12275853406SSamuel Holland 	/*
12375853406SSamuel Holland 	 * Disable shutdown under light load.
12475853406SSamuel Holland 	 * Enable power on when under load.
12575853406SSamuel Holland 	 */
12675853406SSamuel Holland 	ret = ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL1,
12775853406SSamuel Holland 				 IP5XXX_SYS_CTL1_LIGHT_SHDN_EN |
12875853406SSamuel Holland 				 IP5XXX_SYS_CTL1_LOAD_PWRUP_EN,
12975853406SSamuel Holland 				 IP5XXX_SYS_CTL1_LOAD_PWRUP_EN);
13075853406SSamuel Holland 	if (ret)
13175853406SSamuel Holland 		return ret;
13275853406SSamuel Holland 
13375853406SSamuel Holland 	/*
13475853406SSamuel Holland 	 * Enable shutdown after a long button press (as configured below).
13575853406SSamuel Holland 	 */
13675853406SSamuel Holland 	ret = ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL3,
13775853406SSamuel Holland 				 IP5XXX_SYS_CTL3_BTN_SHDN_EN,
13875853406SSamuel Holland 				 IP5XXX_SYS_CTL3_BTN_SHDN_EN);
13975853406SSamuel Holland 	if (ret)
14075853406SSamuel Holland 		return ret;
14175853406SSamuel Holland 
14275853406SSamuel Holland 	/*
14375853406SSamuel Holland 	 * Power on automatically when VIN is removed.
14475853406SSamuel Holland 	 */
14575853406SSamuel Holland 	ret = ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL4,
14675853406SSamuel Holland 				 IP5XXX_SYS_CTL4_VIN_PULLOUT_BOOST_EN,
14775853406SSamuel Holland 				 IP5XXX_SYS_CTL4_VIN_PULLOUT_BOOST_EN);
14875853406SSamuel Holland 	if (ret)
14975853406SSamuel Holland 		return ret;
15075853406SSamuel Holland 
15175853406SSamuel Holland 	/*
15275853406SSamuel Holland 	 * Enable the NTC.
15375853406SSamuel Holland 	 * Configure the button for two presses => LED, long press => shutdown.
15475853406SSamuel Holland 	 */
15575853406SSamuel Holland 	ret = ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL5,
15675853406SSamuel Holland 				 IP5XXX_SYS_CTL5_NTC_DIS |
15775853406SSamuel Holland 				 IP5XXX_SYS_CTL5_WLED_MODE_SEL |
15875853406SSamuel Holland 				 IP5XXX_SYS_CTL5_BTN_SHDN_SEL,
15975853406SSamuel Holland 				 IP5XXX_SYS_CTL5_WLED_MODE_SEL |
16075853406SSamuel Holland 				 IP5XXX_SYS_CTL5_BTN_SHDN_SEL);
16175853406SSamuel Holland 	if (ret)
16275853406SSamuel Holland 		return ret;
16375853406SSamuel Holland 
16475853406SSamuel Holland 	ip5xxx->initialized = true;
16575853406SSamuel Holland 	dev_dbg(psy->dev.parent, "Initialized after power on\n");
16675853406SSamuel Holland 
16775853406SSamuel Holland 	return 0;
16875853406SSamuel Holland }
16975853406SSamuel Holland 
17075853406SSamuel Holland static const enum power_supply_property ip5xxx_battery_properties[] = {
17175853406SSamuel Holland 	POWER_SUPPLY_PROP_STATUS,
17275853406SSamuel Holland 	POWER_SUPPLY_PROP_CHARGE_TYPE,
17375853406SSamuel Holland 	POWER_SUPPLY_PROP_HEALTH,
17475853406SSamuel Holland 	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
17575853406SSamuel Holland 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
17675853406SSamuel Holland 	POWER_SUPPLY_PROP_VOLTAGE_OCV,
17775853406SSamuel Holland 	POWER_SUPPLY_PROP_CURRENT_NOW,
17875853406SSamuel Holland 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
17975853406SSamuel Holland 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
18075853406SSamuel Holland 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
18175853406SSamuel Holland 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
18275853406SSamuel Holland };
18375853406SSamuel Holland 
ip5xxx_battery_get_status(struct ip5xxx * ip5xxx,int * val)18475853406SSamuel Holland static int ip5xxx_battery_get_status(struct ip5xxx *ip5xxx, int *val)
18575853406SSamuel Holland {
18675853406SSamuel Holland 	unsigned int rval;
18775853406SSamuel Holland 	int ret;
18875853406SSamuel Holland 
18975853406SSamuel Holland 	ret = ip5xxx_read(ip5xxx, IP5XXX_READ0, &rval);
19075853406SSamuel Holland 	if (ret)
19175853406SSamuel Holland 		return ret;
19275853406SSamuel Holland 
19375853406SSamuel Holland 	switch (rval & IP5XXX_READ0_CHG_STAT) {
19475853406SSamuel Holland 	case IP5XXX_READ0_CHG_STAT_IDLE:
19575853406SSamuel Holland 		*val = POWER_SUPPLY_STATUS_DISCHARGING;
19675853406SSamuel Holland 		break;
19775853406SSamuel Holland 	case IP5XXX_READ0_CHG_STAT_TRICKLE:
19875853406SSamuel Holland 	case IP5XXX_READ0_CHG_STAT_CONST_CUR:
19975853406SSamuel Holland 	case IP5XXX_READ0_CHG_STAT_CONST_VOLT:
20075853406SSamuel Holland 		*val = POWER_SUPPLY_STATUS_CHARGING;
20175853406SSamuel Holland 		break;
20275853406SSamuel Holland 	case IP5XXX_READ0_CHG_STAT_CONST_VOLT_STOP:
20375853406SSamuel Holland 	case IP5XXX_READ0_CHG_STAT_FULL:
20475853406SSamuel Holland 		*val = POWER_SUPPLY_STATUS_FULL;
20575853406SSamuel Holland 		break;
20675853406SSamuel Holland 	case IP5XXX_READ0_CHG_STAT_TIMEOUT:
20775853406SSamuel Holland 		*val = POWER_SUPPLY_STATUS_NOT_CHARGING;
20875853406SSamuel Holland 		break;
20975853406SSamuel Holland 	default:
21075853406SSamuel Holland 		return -EINVAL;
21175853406SSamuel Holland 	}
21275853406SSamuel Holland 
21375853406SSamuel Holland 	return 0;
21475853406SSamuel Holland }
21575853406SSamuel Holland 
ip5xxx_battery_get_charge_type(struct ip5xxx * ip5xxx,int * val)21675853406SSamuel Holland static int ip5xxx_battery_get_charge_type(struct ip5xxx *ip5xxx, int *val)
21775853406SSamuel Holland {
21875853406SSamuel Holland 	unsigned int rval;
21975853406SSamuel Holland 	int ret;
22075853406SSamuel Holland 
22175853406SSamuel Holland 	ret = ip5xxx_read(ip5xxx, IP5XXX_READ0, &rval);
22275853406SSamuel Holland 	if (ret)
22375853406SSamuel Holland 		return ret;
22475853406SSamuel Holland 
22575853406SSamuel Holland 	switch (rval & IP5XXX_READ0_CHG_STAT) {
22675853406SSamuel Holland 	case IP5XXX_READ0_CHG_STAT_IDLE:
22775853406SSamuel Holland 	case IP5XXX_READ0_CHG_STAT_CONST_VOLT_STOP:
22875853406SSamuel Holland 	case IP5XXX_READ0_CHG_STAT_FULL:
22975853406SSamuel Holland 	case IP5XXX_READ0_CHG_STAT_TIMEOUT:
23075853406SSamuel Holland 		*val = POWER_SUPPLY_CHARGE_TYPE_NONE;
23175853406SSamuel Holland 		break;
23275853406SSamuel Holland 	case IP5XXX_READ0_CHG_STAT_TRICKLE:
23375853406SSamuel Holland 		*val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
23475853406SSamuel Holland 		break;
23575853406SSamuel Holland 	case IP5XXX_READ0_CHG_STAT_CONST_CUR:
23675853406SSamuel Holland 	case IP5XXX_READ0_CHG_STAT_CONST_VOLT:
23775853406SSamuel Holland 		*val = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
23875853406SSamuel Holland 		break;
23975853406SSamuel Holland 	default:
24075853406SSamuel Holland 		return -EINVAL;
24175853406SSamuel Holland 	}
24275853406SSamuel Holland 
24375853406SSamuel Holland 	return 0;
24475853406SSamuel Holland }
24575853406SSamuel Holland 
ip5xxx_battery_get_health(struct ip5xxx * ip5xxx,int * val)24675853406SSamuel Holland static int ip5xxx_battery_get_health(struct ip5xxx *ip5xxx, int *val)
24775853406SSamuel Holland {
24875853406SSamuel Holland 	unsigned int rval;
24975853406SSamuel Holland 	int ret;
25075853406SSamuel Holland 
25175853406SSamuel Holland 	ret = ip5xxx_read(ip5xxx, IP5XXX_READ0, &rval);
25275853406SSamuel Holland 	if (ret)
25375853406SSamuel Holland 		return ret;
25475853406SSamuel Holland 
25575853406SSamuel Holland 	if (rval & IP5XXX_READ0_TIMEOUT)
25675853406SSamuel Holland 		*val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
25775853406SSamuel Holland 	else
25875853406SSamuel Holland 		*val = POWER_SUPPLY_HEALTH_GOOD;
25975853406SSamuel Holland 
26075853406SSamuel Holland 	return 0;
26175853406SSamuel Holland }
26275853406SSamuel Holland 
ip5xxx_battery_get_voltage_max(struct ip5xxx * ip5xxx,int * val)26375853406SSamuel Holland static int ip5xxx_battery_get_voltage_max(struct ip5xxx *ip5xxx, int *val)
26475853406SSamuel Holland {
26575853406SSamuel Holland 	unsigned int rval;
26675853406SSamuel Holland 	int ret;
26775853406SSamuel Holland 
26875853406SSamuel Holland 	ret = ip5xxx_read(ip5xxx, IP5XXX_CHG_CTL2, &rval);
26975853406SSamuel Holland 	if (ret)
27075853406SSamuel Holland 		return ret;
27175853406SSamuel Holland 
27275853406SSamuel Holland 	/*
27375853406SSamuel Holland 	 * It is not clear what this will return if
27475853406SSamuel Holland 	 * IP5XXX_CHG_CTL4_BAT_TYPE_SEL_EN is not set...
27575853406SSamuel Holland 	 */
27675853406SSamuel Holland 	switch (rval & IP5XXX_CHG_CTL2_BAT_TYPE_SEL) {
27775853406SSamuel Holland 	case IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_2V:
27875853406SSamuel Holland 		*val = 4200000;
27975853406SSamuel Holland 		break;
28075853406SSamuel Holland 	case IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_3V:
28175853406SSamuel Holland 		*val = 4300000;
28275853406SSamuel Holland 		break;
28375853406SSamuel Holland 	case IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_35V:
28475853406SSamuel Holland 		*val = 4350000;
28575853406SSamuel Holland 		break;
28675853406SSamuel Holland 	default:
28775853406SSamuel Holland 		return -EINVAL;
28875853406SSamuel Holland 	}
28975853406SSamuel Holland 
29075853406SSamuel Holland 	return 0;
29175853406SSamuel Holland }
29275853406SSamuel Holland 
ip5xxx_battery_read_adc(struct ip5xxx * ip5xxx,u8 lo_reg,u8 hi_reg,int * val)29375853406SSamuel Holland static int ip5xxx_battery_read_adc(struct ip5xxx *ip5xxx,
29475853406SSamuel Holland 				   u8 lo_reg, u8 hi_reg, int *val)
29575853406SSamuel Holland {
29675853406SSamuel Holland 	unsigned int hi, lo;
29775853406SSamuel Holland 	int ret;
29875853406SSamuel Holland 
29975853406SSamuel Holland 	ret = ip5xxx_read(ip5xxx, lo_reg, &lo);
30075853406SSamuel Holland 	if (ret)
30175853406SSamuel Holland 		return ret;
30275853406SSamuel Holland 
30375853406SSamuel Holland 	ret = ip5xxx_read(ip5xxx, hi_reg, &hi);
30475853406SSamuel Holland 	if (ret)
30575853406SSamuel Holland 		return ret;
30675853406SSamuel Holland 
30775853406SSamuel Holland 	*val = sign_extend32(hi << 8 | lo, 13);
30875853406SSamuel Holland 
30975853406SSamuel Holland 	return 0;
31075853406SSamuel Holland }
31175853406SSamuel Holland 
ip5xxx_battery_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)31275853406SSamuel Holland static int ip5xxx_battery_get_property(struct power_supply *psy,
31375853406SSamuel Holland 				       enum power_supply_property psp,
31475853406SSamuel Holland 				       union power_supply_propval *val)
31575853406SSamuel Holland {
31675853406SSamuel Holland 	struct ip5xxx *ip5xxx = power_supply_get_drvdata(psy);
31775853406SSamuel Holland 	int raw, ret, vmax;
31875853406SSamuel Holland 	unsigned int rval;
31975853406SSamuel Holland 
32075853406SSamuel Holland 	ret = ip5xxx_initialize(psy);
32175853406SSamuel Holland 	if (ret)
32275853406SSamuel Holland 		return ret;
32375853406SSamuel Holland 
32475853406SSamuel Holland 	switch (psp) {
32575853406SSamuel Holland 	case POWER_SUPPLY_PROP_STATUS:
32675853406SSamuel Holland 		return ip5xxx_battery_get_status(ip5xxx, &val->intval);
32775853406SSamuel Holland 
32875853406SSamuel Holland 	case POWER_SUPPLY_PROP_CHARGE_TYPE:
32975853406SSamuel Holland 		return ip5xxx_battery_get_charge_type(ip5xxx, &val->intval);
33075853406SSamuel Holland 
33175853406SSamuel Holland 	case POWER_SUPPLY_PROP_HEALTH:
33275853406SSamuel Holland 		return ip5xxx_battery_get_health(ip5xxx, &val->intval);
33375853406SSamuel Holland 
33475853406SSamuel Holland 	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
33575853406SSamuel Holland 		return ip5xxx_battery_get_voltage_max(ip5xxx, &val->intval);
33675853406SSamuel Holland 
33775853406SSamuel Holland 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
33875853406SSamuel Holland 		ret = ip5xxx_battery_read_adc(ip5xxx, IP5XXX_BATVADC_DAT0,
33975853406SSamuel Holland 					      IP5XXX_BATVADC_DAT1, &raw);
34075853406SSamuel Holland 
34175853406SSamuel Holland 		val->intval = 2600000 + DIV_ROUND_CLOSEST(raw * 26855, 100);
34275853406SSamuel Holland 		return 0;
34375853406SSamuel Holland 
34475853406SSamuel Holland 	case POWER_SUPPLY_PROP_VOLTAGE_OCV:
34575853406SSamuel Holland 		ret = ip5xxx_battery_read_adc(ip5xxx, IP5XXX_BATOCV_DAT0,
34675853406SSamuel Holland 					      IP5XXX_BATOCV_DAT1, &raw);
34775853406SSamuel Holland 
34875853406SSamuel Holland 		val->intval = 2600000 + DIV_ROUND_CLOSEST(raw * 26855, 100);
34975853406SSamuel Holland 		return 0;
35075853406SSamuel Holland 
35175853406SSamuel Holland 	case POWER_SUPPLY_PROP_CURRENT_NOW:
35275853406SSamuel Holland 		ret = ip5xxx_battery_read_adc(ip5xxx, IP5XXX_BATIADC_DAT0,
35375853406SSamuel Holland 					      IP5XXX_BATIADC_DAT1, &raw);
35475853406SSamuel Holland 
355f9be5cb6SOndrej Jirman 		val->intval = DIV_ROUND_CLOSEST(raw * 149197, 200);
35675853406SSamuel Holland 		return 0;
35775853406SSamuel Holland 
35875853406SSamuel Holland 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
35975853406SSamuel Holland 		ret = ip5xxx_read(ip5xxx, IP5XXX_CHG_CTL4A, &rval);
36075853406SSamuel Holland 		if (ret)
36175853406SSamuel Holland 			return ret;
36275853406SSamuel Holland 
36375853406SSamuel Holland 		rval &= IP5XXX_CHG_CTL4A_CONST_CUR_SEL;
36475853406SSamuel Holland 		val->intval = 100000 * rval;
36575853406SSamuel Holland 		return 0;
36675853406SSamuel Holland 
36775853406SSamuel Holland 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
36875853406SSamuel Holland 		val->intval = 100000 * 0x1f;
36975853406SSamuel Holland 		return 0;
37075853406SSamuel Holland 
37175853406SSamuel Holland 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
37275853406SSamuel Holland 		ret = ip5xxx_battery_get_voltage_max(ip5xxx, &vmax);
37375853406SSamuel Holland 		if (ret)
37475853406SSamuel Holland 			return ret;
37575853406SSamuel Holland 
37675853406SSamuel Holland 		ret = ip5xxx_read(ip5xxx, IP5XXX_CHG_CTL2, &rval);
37775853406SSamuel Holland 		if (ret)
37875853406SSamuel Holland 			return ret;
37975853406SSamuel Holland 
38075853406SSamuel Holland 		rval &= IP5XXX_CHG_CTL2_CONST_VOLT_SEL;
38175853406SSamuel Holland 		val->intval = vmax + 14000 * (rval >> 1);
38275853406SSamuel Holland 		return 0;
38375853406SSamuel Holland 
38475853406SSamuel Holland 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
38575853406SSamuel Holland 		ret = ip5xxx_battery_get_voltage_max(ip5xxx, &vmax);
38675853406SSamuel Holland 		if (ret)
38775853406SSamuel Holland 			return ret;
38875853406SSamuel Holland 
38975853406SSamuel Holland 		val->intval = vmax + 14000 * 3;
39075853406SSamuel Holland 		return 0;
39175853406SSamuel Holland 
39275853406SSamuel Holland 	default:
39375853406SSamuel Holland 		return -EINVAL;
39475853406SSamuel Holland 	}
39575853406SSamuel Holland }
39675853406SSamuel Holland 
ip5xxx_battery_set_voltage_max(struct ip5xxx * ip5xxx,int val)39775853406SSamuel Holland static int ip5xxx_battery_set_voltage_max(struct ip5xxx *ip5xxx, int val)
39875853406SSamuel Holland {
39975853406SSamuel Holland 	unsigned int rval;
40075853406SSamuel Holland 	int ret;
40175853406SSamuel Holland 
40275853406SSamuel Holland 	switch (val) {
40375853406SSamuel Holland 	case 4200000:
40475853406SSamuel Holland 		rval = IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_2V;
40575853406SSamuel Holland 		break;
40675853406SSamuel Holland 	case 4300000:
40775853406SSamuel Holland 		rval = IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_3V;
40875853406SSamuel Holland 		break;
40975853406SSamuel Holland 	case 4350000:
41075853406SSamuel Holland 		rval = IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_35V;
41175853406SSamuel Holland 		break;
41275853406SSamuel Holland 	default:
41375853406SSamuel Holland 		return -EINVAL;
41475853406SSamuel Holland 	}
41575853406SSamuel Holland 
41675853406SSamuel Holland 	ret = ip5xxx_update_bits(ip5xxx, IP5XXX_CHG_CTL2,
41775853406SSamuel Holland 				 IP5XXX_CHG_CTL2_BAT_TYPE_SEL, rval);
41875853406SSamuel Holland 	if (ret)
41975853406SSamuel Holland 		return ret;
42075853406SSamuel Holland 
42175853406SSamuel Holland 	ret = ip5xxx_update_bits(ip5xxx, IP5XXX_CHG_CTL4,
42275853406SSamuel Holland 				 IP5XXX_CHG_CTL4_BAT_TYPE_SEL_EN,
42375853406SSamuel Holland 				 IP5XXX_CHG_CTL4_BAT_TYPE_SEL_EN);
42475853406SSamuel Holland 	if (ret)
42575853406SSamuel Holland 		return ret;
42675853406SSamuel Holland 
42775853406SSamuel Holland 	return 0;
42875853406SSamuel Holland }
42975853406SSamuel Holland 
ip5xxx_battery_set_property(struct power_supply * psy,enum power_supply_property psp,const union power_supply_propval * val)43075853406SSamuel Holland static int ip5xxx_battery_set_property(struct power_supply *psy,
43175853406SSamuel Holland 				       enum power_supply_property psp,
43275853406SSamuel Holland 				       const union power_supply_propval *val)
43375853406SSamuel Holland {
43475853406SSamuel Holland 	struct ip5xxx *ip5xxx = power_supply_get_drvdata(psy);
43575853406SSamuel Holland 	unsigned int rval;
43675853406SSamuel Holland 	int ret, vmax;
43775853406SSamuel Holland 
43875853406SSamuel Holland 	ret = ip5xxx_initialize(psy);
43975853406SSamuel Holland 	if (ret)
44075853406SSamuel Holland 		return ret;
44175853406SSamuel Holland 
44275853406SSamuel Holland 	switch (psp) {
44375853406SSamuel Holland 	case POWER_SUPPLY_PROP_STATUS:
44475853406SSamuel Holland 		switch (val->intval) {
44575853406SSamuel Holland 		case POWER_SUPPLY_STATUS_CHARGING:
44675853406SSamuel Holland 			rval = IP5XXX_SYS_CTL0_CHARGER_EN;
44775853406SSamuel Holland 			break;
44875853406SSamuel Holland 		case POWER_SUPPLY_STATUS_DISCHARGING:
44975853406SSamuel Holland 		case POWER_SUPPLY_STATUS_NOT_CHARGING:
45075853406SSamuel Holland 			rval = 0;
45175853406SSamuel Holland 			break;
45275853406SSamuel Holland 		default:
45375853406SSamuel Holland 			return -EINVAL;
45475853406SSamuel Holland 		}
45575853406SSamuel Holland 		return ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL0,
45675853406SSamuel Holland 					  IP5XXX_SYS_CTL0_CHARGER_EN, rval);
45775853406SSamuel Holland 
45875853406SSamuel Holland 	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
45975853406SSamuel Holland 		return ip5xxx_battery_set_voltage_max(ip5xxx, val->intval);
46075853406SSamuel Holland 
46175853406SSamuel Holland 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
46275853406SSamuel Holland 		rval = val->intval / 100000;
46375853406SSamuel Holland 		return ip5xxx_update_bits(ip5xxx, IP5XXX_CHG_CTL4A,
46475853406SSamuel Holland 					  IP5XXX_CHG_CTL4A_CONST_CUR_SEL, rval);
46575853406SSamuel Holland 
46675853406SSamuel Holland 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
46775853406SSamuel Holland 		ret = ip5xxx_battery_get_voltage_max(ip5xxx, &vmax);
46875853406SSamuel Holland 		if (ret)
46975853406SSamuel Holland 			return ret;
47075853406SSamuel Holland 
47175853406SSamuel Holland 		rval = ((val->intval - vmax) / 14000) << 1;
47275853406SSamuel Holland 		return ip5xxx_update_bits(ip5xxx, IP5XXX_CHG_CTL2,
47375853406SSamuel Holland 					  IP5XXX_CHG_CTL2_CONST_VOLT_SEL, rval);
47475853406SSamuel Holland 
47575853406SSamuel Holland 	default:
47675853406SSamuel Holland 		return -EINVAL;
47775853406SSamuel Holland 	}
47875853406SSamuel Holland }
47975853406SSamuel Holland 
ip5xxx_battery_property_is_writeable(struct power_supply * psy,enum power_supply_property psp)48075853406SSamuel Holland static int ip5xxx_battery_property_is_writeable(struct power_supply *psy,
48175853406SSamuel Holland 						enum power_supply_property psp)
48275853406SSamuel Holland {
48375853406SSamuel Holland 	return psp == POWER_SUPPLY_PROP_STATUS ||
48475853406SSamuel Holland 	       psp == POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN ||
48575853406SSamuel Holland 	       psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT ||
48675853406SSamuel Holland 	       psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE;
48775853406SSamuel Holland }
48875853406SSamuel Holland 
48975853406SSamuel Holland static const struct power_supply_desc ip5xxx_battery_desc = {
49075853406SSamuel Holland 	.name			= "ip5xxx-battery",
49175853406SSamuel Holland 	.type			= POWER_SUPPLY_TYPE_BATTERY,
49275853406SSamuel Holland 	.properties		= ip5xxx_battery_properties,
49375853406SSamuel Holland 	.num_properties		= ARRAY_SIZE(ip5xxx_battery_properties),
49475853406SSamuel Holland 	.get_property		= ip5xxx_battery_get_property,
49575853406SSamuel Holland 	.set_property		= ip5xxx_battery_set_property,
49675853406SSamuel Holland 	.property_is_writeable	= ip5xxx_battery_property_is_writeable,
49775853406SSamuel Holland };
49875853406SSamuel Holland 
49975853406SSamuel Holland static const enum power_supply_property ip5xxx_boost_properties[] = {
50075853406SSamuel Holland 	POWER_SUPPLY_PROP_ONLINE,
50175853406SSamuel Holland 	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
50275853406SSamuel Holland };
50375853406SSamuel Holland 
ip5xxx_boost_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)50475853406SSamuel Holland static int ip5xxx_boost_get_property(struct power_supply *psy,
50575853406SSamuel Holland 				     enum power_supply_property psp,
50675853406SSamuel Holland 				     union power_supply_propval *val)
50775853406SSamuel Holland {
50875853406SSamuel Holland 	struct ip5xxx *ip5xxx = power_supply_get_drvdata(psy);
50975853406SSamuel Holland 	unsigned int rval;
51075853406SSamuel Holland 	int ret;
51175853406SSamuel Holland 
51275853406SSamuel Holland 	ret = ip5xxx_initialize(psy);
51375853406SSamuel Holland 	if (ret)
51475853406SSamuel Holland 		return ret;
51575853406SSamuel Holland 
51675853406SSamuel Holland 	switch (psp) {
51775853406SSamuel Holland 	case POWER_SUPPLY_PROP_ONLINE:
51875853406SSamuel Holland 		ret = ip5xxx_read(ip5xxx, IP5XXX_SYS_CTL0, &rval);
51975853406SSamuel Holland 		if (ret)
52075853406SSamuel Holland 			return ret;
52175853406SSamuel Holland 
52275853406SSamuel Holland 		val->intval = !!(rval & IP5XXX_SYS_CTL0_BOOST_EN);
52375853406SSamuel Holland 		return 0;
52475853406SSamuel Holland 
52575853406SSamuel Holland 	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
52675853406SSamuel Holland 		ret = ip5xxx_read(ip5xxx, IP5XXX_CHG_CTL1, &rval);
52775853406SSamuel Holland 		if (ret)
52875853406SSamuel Holland 			return ret;
52975853406SSamuel Holland 
53075853406SSamuel Holland 		rval &= IP5XXX_CHG_CTL1_BOOST_UVP_SEL;
53175853406SSamuel Holland 		val->intval = 4530000 + 100000 * (rval >> 2);
53275853406SSamuel Holland 		return 0;
53375853406SSamuel Holland 
53475853406SSamuel Holland 	default:
53575853406SSamuel Holland 		return -EINVAL;
53675853406SSamuel Holland 	}
53775853406SSamuel Holland }
53875853406SSamuel Holland 
ip5xxx_boost_set_property(struct power_supply * psy,enum power_supply_property psp,const union power_supply_propval * val)53975853406SSamuel Holland static int ip5xxx_boost_set_property(struct power_supply *psy,
54075853406SSamuel Holland 				     enum power_supply_property psp,
54175853406SSamuel Holland 				     const union power_supply_propval *val)
54275853406SSamuel Holland {
54375853406SSamuel Holland 	struct ip5xxx *ip5xxx = power_supply_get_drvdata(psy);
54475853406SSamuel Holland 	unsigned int rval;
54575853406SSamuel Holland 	int ret;
54675853406SSamuel Holland 
54775853406SSamuel Holland 	ret = ip5xxx_initialize(psy);
54875853406SSamuel Holland 	if (ret)
54975853406SSamuel Holland 		return ret;
55075853406SSamuel Holland 
55175853406SSamuel Holland 	switch (psp) {
55275853406SSamuel Holland 	case POWER_SUPPLY_PROP_ONLINE:
55375853406SSamuel Holland 		rval = val->intval ? IP5XXX_SYS_CTL0_BOOST_EN : 0;
55475853406SSamuel Holland 		return ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL0,
55575853406SSamuel Holland 					  IP5XXX_SYS_CTL0_BOOST_EN, rval);
55675853406SSamuel Holland 
55775853406SSamuel Holland 	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
55875853406SSamuel Holland 		rval = ((val->intval - 4530000) / 100000) << 2;
55975853406SSamuel Holland 		return ip5xxx_update_bits(ip5xxx, IP5XXX_CHG_CTL1,
56075853406SSamuel Holland 					  IP5XXX_CHG_CTL1_BOOST_UVP_SEL, rval);
56175853406SSamuel Holland 
56275853406SSamuel Holland 	default:
56375853406SSamuel Holland 		return -EINVAL;
56475853406SSamuel Holland 	}
56575853406SSamuel Holland }
56675853406SSamuel Holland 
ip5xxx_boost_property_is_writeable(struct power_supply * psy,enum power_supply_property psp)56775853406SSamuel Holland static int ip5xxx_boost_property_is_writeable(struct power_supply *psy,
56875853406SSamuel Holland 					      enum power_supply_property psp)
56975853406SSamuel Holland {
57075853406SSamuel Holland 	return true;
57175853406SSamuel Holland }
57275853406SSamuel Holland 
57375853406SSamuel Holland static const struct power_supply_desc ip5xxx_boost_desc = {
57475853406SSamuel Holland 	.name			= "ip5xxx-boost",
57575853406SSamuel Holland 	.type			= POWER_SUPPLY_TYPE_USB,
57675853406SSamuel Holland 	.properties		= ip5xxx_boost_properties,
57775853406SSamuel Holland 	.num_properties		= ARRAY_SIZE(ip5xxx_boost_properties),
57875853406SSamuel Holland 	.get_property		= ip5xxx_boost_get_property,
57975853406SSamuel Holland 	.set_property		= ip5xxx_boost_set_property,
58075853406SSamuel Holland 	.property_is_writeable	= ip5xxx_boost_property_is_writeable,
58175853406SSamuel Holland };
58275853406SSamuel Holland 
58375853406SSamuel Holland static const struct regmap_config ip5xxx_regmap_config = {
58475853406SSamuel Holland 	.reg_bits		= 8,
58575853406SSamuel Holland 	.val_bits		= 8,
58675853406SSamuel Holland 	.max_register		= IP5XXX_BATOCV_DAT1,
58775853406SSamuel Holland };
58875853406SSamuel Holland 
ip5xxx_power_probe(struct i2c_client * client)58975853406SSamuel Holland static int ip5xxx_power_probe(struct i2c_client *client)
59075853406SSamuel Holland {
59175853406SSamuel Holland 	struct power_supply_config psy_cfg = {};
59275853406SSamuel Holland 	struct device *dev = &client->dev;
59375853406SSamuel Holland 	struct power_supply *psy;
59475853406SSamuel Holland 	struct ip5xxx *ip5xxx;
59575853406SSamuel Holland 
59675853406SSamuel Holland 	ip5xxx = devm_kzalloc(dev, sizeof(*ip5xxx), GFP_KERNEL);
59775853406SSamuel Holland 	if (!ip5xxx)
59875853406SSamuel Holland 		return -ENOMEM;
59975853406SSamuel Holland 
60075853406SSamuel Holland 	ip5xxx->regmap = devm_regmap_init_i2c(client, &ip5xxx_regmap_config);
60175853406SSamuel Holland 	if (IS_ERR(ip5xxx->regmap))
60275853406SSamuel Holland 		return PTR_ERR(ip5xxx->regmap);
60375853406SSamuel Holland 
60475853406SSamuel Holland 	psy_cfg.of_node = dev->of_node;
60575853406SSamuel Holland 	psy_cfg.drv_data = ip5xxx;
60675853406SSamuel Holland 
60775853406SSamuel Holland 	psy = devm_power_supply_register(dev, &ip5xxx_battery_desc, &psy_cfg);
60875853406SSamuel Holland 	if (IS_ERR(psy))
60975853406SSamuel Holland 		return PTR_ERR(psy);
61075853406SSamuel Holland 
61175853406SSamuel Holland 	psy = devm_power_supply_register(dev, &ip5xxx_boost_desc, &psy_cfg);
61275853406SSamuel Holland 	if (IS_ERR(psy))
61375853406SSamuel Holland 		return PTR_ERR(psy);
61475853406SSamuel Holland 
61575853406SSamuel Holland 	return 0;
61675853406SSamuel Holland }
61775853406SSamuel Holland 
61875853406SSamuel Holland static const struct of_device_id ip5xxx_power_of_match[] = {
61975853406SSamuel Holland 	{ .compatible = "injoinic,ip5108" },
62075853406SSamuel Holland 	{ .compatible = "injoinic,ip5109" },
62175853406SSamuel Holland 	{ .compatible = "injoinic,ip5207" },
62275853406SSamuel Holland 	{ .compatible = "injoinic,ip5209" },
62375853406SSamuel Holland 	{ }
62475853406SSamuel Holland };
62575853406SSamuel Holland MODULE_DEVICE_TABLE(of, ip5xxx_power_of_match);
62675853406SSamuel Holland 
62775853406SSamuel Holland static struct i2c_driver ip5xxx_power_driver = {
628*fe20b1dcSUwe Kleine-König 	.probe		= ip5xxx_power_probe,
62975853406SSamuel Holland 	.driver		= {
63075853406SSamuel Holland 		.name		= "ip5xxx-power",
63175853406SSamuel Holland 		.of_match_table	= ip5xxx_power_of_match,
63275853406SSamuel Holland 	}
63375853406SSamuel Holland };
63475853406SSamuel Holland module_i2c_driver(ip5xxx_power_driver);
63575853406SSamuel Holland 
63675853406SSamuel Holland MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>");
63775853406SSamuel Holland MODULE_DESCRIPTION("Injoinic IP5xxx power bank IC driver");
63875853406SSamuel Holland MODULE_LICENSE("GPL");
639