162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * MP2629 battery charger driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2020 Monolithic Power Systems, Inc
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Saravanan Sekar <sravanhome@gmail.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/bits.h>
1162306a36Sopenharmony_ci#include <linux/iio/consumer.h>
1262306a36Sopenharmony_ci#include <linux/iio/types.h>
1362306a36Sopenharmony_ci#include <linux/interrupt.h>
1462306a36Sopenharmony_ci#include <linux/mfd/mp2629.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
1762306a36Sopenharmony_ci#include <linux/platform_device.h>
1862306a36Sopenharmony_ci#include <linux/power_supply.h>
1962306a36Sopenharmony_ci#include <linux/regmap.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define MP2629_REG_INPUT_ILIM		0x00
2262306a36Sopenharmony_ci#define MP2629_REG_INPUT_VLIM		0x01
2362306a36Sopenharmony_ci#define MP2629_REG_CHARGE_CTRL		0x04
2462306a36Sopenharmony_ci#define MP2629_REG_CHARGE_ILIM		0x05
2562306a36Sopenharmony_ci#define MP2629_REG_PRECHARGE		0x06
2662306a36Sopenharmony_ci#define MP2629_REG_TERM_CURRENT		0x06
2762306a36Sopenharmony_ci#define MP2629_REG_CHARGE_VLIM		0x07
2862306a36Sopenharmony_ci#define MP2629_REG_TIMER_CTRL		0x08
2962306a36Sopenharmony_ci#define MP2629_REG_IMPEDANCE_COMP	0x09
3062306a36Sopenharmony_ci#define MP2629_REG_INTERRUPT		0x0b
3162306a36Sopenharmony_ci#define MP2629_REG_STATUS		0x0c
3262306a36Sopenharmony_ci#define MP2629_REG_FAULT		0x0d
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define MP2629_MASK_INPUT_TYPE		GENMASK(7, 5)
3562306a36Sopenharmony_ci#define MP2629_MASK_CHARGE_TYPE		GENMASK(4, 3)
3662306a36Sopenharmony_ci#define MP2629_MASK_CHARGE_CTRL		GENMASK(5, 4)
3762306a36Sopenharmony_ci#define MP2629_MASK_WDOG_CTRL		GENMASK(5, 4)
3862306a36Sopenharmony_ci#define MP2629_MASK_IMPEDANCE		GENMASK(7, 4)
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define MP2629_INPUTSOURCE_CHANGE	GENMASK(7, 5)
4162306a36Sopenharmony_ci#define MP2629_CHARGING_CHANGE		GENMASK(4, 3)
4262306a36Sopenharmony_ci#define MP2629_FAULT_BATTERY		BIT(3)
4362306a36Sopenharmony_ci#define MP2629_FAULT_THERMAL		BIT(4)
4462306a36Sopenharmony_ci#define MP2629_FAULT_INPUT		BIT(5)
4562306a36Sopenharmony_ci#define MP2629_FAULT_OTG		BIT(6)
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define MP2629_MAX_BATT_CAPACITY	100
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define MP2629_PROPS(_idx, _min, _max, _step)		\
5062306a36Sopenharmony_ci	[_idx] = {					\
5162306a36Sopenharmony_ci		.min	= _min,				\
5262306a36Sopenharmony_ci		.max	= _max,				\
5362306a36Sopenharmony_ci		.step	= _step,			\
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cienum mp2629_source_type {
5762306a36Sopenharmony_ci	MP2629_SOURCE_TYPE_NO_INPUT,
5862306a36Sopenharmony_ci	MP2629_SOURCE_TYPE_NON_STD,
5962306a36Sopenharmony_ci	MP2629_SOURCE_TYPE_SDP,
6062306a36Sopenharmony_ci	MP2629_SOURCE_TYPE_CDP,
6162306a36Sopenharmony_ci	MP2629_SOURCE_TYPE_DCP,
6262306a36Sopenharmony_ci	MP2629_SOURCE_TYPE_OTG = 7,
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cienum mp2629_field {
6662306a36Sopenharmony_ci	INPUT_ILIM,
6762306a36Sopenharmony_ci	INPUT_VLIM,
6862306a36Sopenharmony_ci	CHARGE_ILIM,
6962306a36Sopenharmony_ci	CHARGE_VLIM,
7062306a36Sopenharmony_ci	PRECHARGE,
7162306a36Sopenharmony_ci	TERM_CURRENT,
7262306a36Sopenharmony_ci	MP2629_MAX_FIELD
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistruct mp2629_charger {
7662306a36Sopenharmony_ci	struct device *dev;
7762306a36Sopenharmony_ci	int status;
7862306a36Sopenharmony_ci	int fault;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	struct regmap *regmap;
8162306a36Sopenharmony_ci	struct regmap_field *regmap_fields[MP2629_MAX_FIELD];
8262306a36Sopenharmony_ci	struct mutex lock;
8362306a36Sopenharmony_ci	struct power_supply *usb;
8462306a36Sopenharmony_ci	struct power_supply *battery;
8562306a36Sopenharmony_ci	struct iio_channel *iiochan[MP2629_ADC_CHAN_END];
8662306a36Sopenharmony_ci};
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistruct mp2629_prop {
8962306a36Sopenharmony_ci	int reg;
9062306a36Sopenharmony_ci	int mask;
9162306a36Sopenharmony_ci	int min;
9262306a36Sopenharmony_ci	int max;
9362306a36Sopenharmony_ci	int step;
9462306a36Sopenharmony_ci	int shift;
9562306a36Sopenharmony_ci};
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic enum power_supply_usb_type mp2629_usb_types[] = {
9862306a36Sopenharmony_ci	POWER_SUPPLY_USB_TYPE_SDP,
9962306a36Sopenharmony_ci	POWER_SUPPLY_USB_TYPE_DCP,
10062306a36Sopenharmony_ci	POWER_SUPPLY_USB_TYPE_CDP,
10162306a36Sopenharmony_ci	POWER_SUPPLY_USB_TYPE_PD_DRP,
10262306a36Sopenharmony_ci	POWER_SUPPLY_USB_TYPE_UNKNOWN
10362306a36Sopenharmony_ci};
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic enum power_supply_property mp2629_charger_usb_props[] = {
10662306a36Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
10762306a36Sopenharmony_ci	POWER_SUPPLY_PROP_USB_TYPE,
10862306a36Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_NOW,
10962306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CURRENT_NOW,
11062306a36Sopenharmony_ci	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
11162306a36Sopenharmony_ci	POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT,
11262306a36Sopenharmony_ci};
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic enum power_supply_property mp2629_charger_bat_props[] = {
11562306a36Sopenharmony_ci	POWER_SUPPLY_PROP_STATUS,
11662306a36Sopenharmony_ci	POWER_SUPPLY_PROP_HEALTH,
11762306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CHARGE_TYPE,
11862306a36Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_NOW,
11962306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CURRENT_NOW,
12062306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CAPACITY,
12162306a36Sopenharmony_ci	POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
12262306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
12362306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
12462306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
12562306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
12662306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic struct mp2629_prop props[] = {
13062306a36Sopenharmony_ci	MP2629_PROPS(INPUT_ILIM, 100000, 3250000, 50000),
13162306a36Sopenharmony_ci	MP2629_PROPS(INPUT_VLIM, 3800000, 5300000, 100000),
13262306a36Sopenharmony_ci	MP2629_PROPS(CHARGE_ILIM, 320000, 4520000, 40000),
13362306a36Sopenharmony_ci	MP2629_PROPS(CHARGE_VLIM, 3400000, 4670000, 10000),
13462306a36Sopenharmony_ci	MP2629_PROPS(PRECHARGE, 120000, 720000, 40000),
13562306a36Sopenharmony_ci	MP2629_PROPS(TERM_CURRENT, 80000, 680000, 40000),
13662306a36Sopenharmony_ci};
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic const struct reg_field mp2629_reg_fields[] = {
13962306a36Sopenharmony_ci	[INPUT_ILIM]	= REG_FIELD(MP2629_REG_INPUT_ILIM, 0, 5),
14062306a36Sopenharmony_ci	[INPUT_VLIM]	= REG_FIELD(MP2629_REG_INPUT_VLIM, 0, 3),
14162306a36Sopenharmony_ci	[CHARGE_ILIM]	= REG_FIELD(MP2629_REG_CHARGE_ILIM, 0, 6),
14262306a36Sopenharmony_ci	[CHARGE_VLIM]	= REG_FIELD(MP2629_REG_CHARGE_VLIM, 1, 7),
14362306a36Sopenharmony_ci	[PRECHARGE]	= REG_FIELD(MP2629_REG_PRECHARGE, 4, 7),
14462306a36Sopenharmony_ci	[TERM_CURRENT]	= REG_FIELD(MP2629_REG_TERM_CURRENT, 0, 3),
14562306a36Sopenharmony_ci};
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic char *adc_chan_name[] = {
14862306a36Sopenharmony_ci	"mp2629-batt-volt",
14962306a36Sopenharmony_ci	"mp2629-system-volt",
15062306a36Sopenharmony_ci	"mp2629-input-volt",
15162306a36Sopenharmony_ci	"mp2629-batt-current",
15262306a36Sopenharmony_ci	"mp2629-input-current",
15362306a36Sopenharmony_ci};
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic int mp2629_read_adc(struct mp2629_charger *charger,
15662306a36Sopenharmony_ci			   enum mp2629_adc_chan ch,
15762306a36Sopenharmony_ci			   union power_supply_propval *val)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	int ret;
16062306a36Sopenharmony_ci	int chval;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	ret = iio_read_channel_processed(charger->iiochan[ch], &chval);
16362306a36Sopenharmony_ci	if (ret)
16462306a36Sopenharmony_ci		return ret;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	val->intval = chval * 1000;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	return 0;
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic int mp2629_get_prop(struct mp2629_charger *charger,
17262306a36Sopenharmony_ci			   enum mp2629_field fld,
17362306a36Sopenharmony_ci			   union power_supply_propval *val)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	int ret;
17662306a36Sopenharmony_ci	unsigned int rval;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	ret = regmap_field_read(charger->regmap_fields[fld], &rval);
17962306a36Sopenharmony_ci	if (ret)
18062306a36Sopenharmony_ci		return ret;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	val->intval = rval * props[fld].step + props[fld].min;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	return 0;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic int mp2629_set_prop(struct mp2629_charger *charger,
18862306a36Sopenharmony_ci			   enum mp2629_field fld,
18962306a36Sopenharmony_ci			   const union power_supply_propval *val)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	unsigned int rval;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if (val->intval < props[fld].min || val->intval > props[fld].max)
19462306a36Sopenharmony_ci		return -EINVAL;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	rval = (val->intval - props[fld].min) / props[fld].step;
19762306a36Sopenharmony_ci	return regmap_field_write(charger->regmap_fields[fld], rval);
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic int mp2629_get_battery_capacity(struct mp2629_charger *charger,
20162306a36Sopenharmony_ci				       union power_supply_propval *val)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	union power_supply_propval vnow, vlim;
20462306a36Sopenharmony_ci	int ret;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	ret = mp2629_read_adc(charger, MP2629_BATT_VOLT, &vnow);
20762306a36Sopenharmony_ci	if (ret)
20862306a36Sopenharmony_ci		return ret;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	ret = mp2629_get_prop(charger, CHARGE_VLIM, &vlim);
21162306a36Sopenharmony_ci	if (ret)
21262306a36Sopenharmony_ci		return ret;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	val->intval = (vnow.intval * 100) / vlim.intval;
21562306a36Sopenharmony_ci	val->intval = min(val->intval, MP2629_MAX_BATT_CAPACITY);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	return 0;
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic int mp2629_charger_battery_get_prop(struct power_supply *psy,
22162306a36Sopenharmony_ci					enum power_supply_property psp,
22262306a36Sopenharmony_ci					union power_supply_propval *val)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	struct mp2629_charger *charger = dev_get_drvdata(psy->dev.parent);
22562306a36Sopenharmony_ci	unsigned int rval;
22662306a36Sopenharmony_ci	int ret = 0;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	switch (psp) {
22962306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
23062306a36Sopenharmony_ci		ret = mp2629_read_adc(charger, MP2629_BATT_VOLT, val);
23162306a36Sopenharmony_ci		break;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_NOW:
23462306a36Sopenharmony_ci		ret = mp2629_read_adc(charger, MP2629_BATT_CURRENT, val);
23562306a36Sopenharmony_ci		break;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
23862306a36Sopenharmony_ci		val->intval = 4520000;
23962306a36Sopenharmony_ci		break;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
24262306a36Sopenharmony_ci		val->intval = 4670000;
24362306a36Sopenharmony_ci		break;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CAPACITY:
24662306a36Sopenharmony_ci		ret = mp2629_get_battery_capacity(charger, val);
24762306a36Sopenharmony_ci		break;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
25062306a36Sopenharmony_ci		ret = mp2629_get_prop(charger, TERM_CURRENT, val);
25162306a36Sopenharmony_ci		break;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
25462306a36Sopenharmony_ci		ret = mp2629_get_prop(charger, PRECHARGE, val);
25562306a36Sopenharmony_ci		break;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
25862306a36Sopenharmony_ci		ret = mp2629_get_prop(charger, CHARGE_VLIM, val);
25962306a36Sopenharmony_ci		break;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
26262306a36Sopenharmony_ci		ret = mp2629_get_prop(charger, CHARGE_ILIM, val);
26362306a36Sopenharmony_ci		break;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_HEALTH:
26662306a36Sopenharmony_ci		if (!charger->fault)
26762306a36Sopenharmony_ci			val->intval = POWER_SUPPLY_HEALTH_GOOD;
26862306a36Sopenharmony_ci		if (MP2629_FAULT_BATTERY & charger->fault)
26962306a36Sopenharmony_ci			val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
27062306a36Sopenharmony_ci		else if (MP2629_FAULT_THERMAL & charger->fault)
27162306a36Sopenharmony_ci			val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
27262306a36Sopenharmony_ci		else if (MP2629_FAULT_INPUT & charger->fault)
27362306a36Sopenharmony_ci			val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
27462306a36Sopenharmony_ci		break;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_STATUS:
27762306a36Sopenharmony_ci		ret = regmap_read(charger->regmap, MP2629_REG_STATUS, &rval);
27862306a36Sopenharmony_ci		if (ret)
27962306a36Sopenharmony_ci			break;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci		rval = (rval & MP2629_MASK_CHARGE_TYPE) >> 3;
28262306a36Sopenharmony_ci		switch (rval) {
28362306a36Sopenharmony_ci		case 0x00:
28462306a36Sopenharmony_ci			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
28562306a36Sopenharmony_ci			break;
28662306a36Sopenharmony_ci		case 0x01:
28762306a36Sopenharmony_ci		case 0x10:
28862306a36Sopenharmony_ci			val->intval = POWER_SUPPLY_STATUS_CHARGING;
28962306a36Sopenharmony_ci			break;
29062306a36Sopenharmony_ci		case 0x11:
29162306a36Sopenharmony_ci			val->intval = POWER_SUPPLY_STATUS_FULL;
29262306a36Sopenharmony_ci		}
29362306a36Sopenharmony_ci		break;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CHARGE_TYPE:
29662306a36Sopenharmony_ci		ret = regmap_read(charger->regmap, MP2629_REG_STATUS, &rval);
29762306a36Sopenharmony_ci		if (ret)
29862306a36Sopenharmony_ci			break;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci		rval = (rval & MP2629_MASK_CHARGE_TYPE) >> 3;
30162306a36Sopenharmony_ci		switch (rval) {
30262306a36Sopenharmony_ci		case 0x00:
30362306a36Sopenharmony_ci			val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
30462306a36Sopenharmony_ci			break;
30562306a36Sopenharmony_ci		case 0x01:
30662306a36Sopenharmony_ci			val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
30762306a36Sopenharmony_ci			break;
30862306a36Sopenharmony_ci		case 0x10:
30962306a36Sopenharmony_ci			val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
31062306a36Sopenharmony_ci			break;
31162306a36Sopenharmony_ci		default:
31262306a36Sopenharmony_ci			val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
31362306a36Sopenharmony_ci		}
31462306a36Sopenharmony_ci		break;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	default:
31762306a36Sopenharmony_ci		return -EINVAL;
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	return ret;
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistatic int mp2629_charger_battery_set_prop(struct power_supply *psy,
32462306a36Sopenharmony_ci					enum power_supply_property psp,
32562306a36Sopenharmony_ci					const union power_supply_propval *val)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	struct mp2629_charger *charger = dev_get_drvdata(psy->dev.parent);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	switch (psp) {
33062306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
33162306a36Sopenharmony_ci		return mp2629_set_prop(charger, TERM_CURRENT, val);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
33462306a36Sopenharmony_ci		return mp2629_set_prop(charger, PRECHARGE, val);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
33762306a36Sopenharmony_ci		return mp2629_set_prop(charger, CHARGE_VLIM, val);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
34062306a36Sopenharmony_ci		return mp2629_set_prop(charger, CHARGE_ILIM, val);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	default:
34362306a36Sopenharmony_ci		return -EINVAL;
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic int mp2629_charger_usb_get_prop(struct power_supply *psy,
34862306a36Sopenharmony_ci				enum power_supply_property psp,
34962306a36Sopenharmony_ci				union power_supply_propval *val)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	struct mp2629_charger *charger = dev_get_drvdata(psy->dev.parent);
35262306a36Sopenharmony_ci	unsigned int rval;
35362306a36Sopenharmony_ci	int ret;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	switch (psp) {
35662306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
35762306a36Sopenharmony_ci		ret = regmap_read(charger->regmap, MP2629_REG_STATUS, &rval);
35862306a36Sopenharmony_ci		if (ret)
35962306a36Sopenharmony_ci			break;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci		val->intval = !!(rval & MP2629_MASK_INPUT_TYPE);
36262306a36Sopenharmony_ci		break;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_USB_TYPE:
36562306a36Sopenharmony_ci		ret = regmap_read(charger->regmap, MP2629_REG_STATUS, &rval);
36662306a36Sopenharmony_ci		if (ret)
36762306a36Sopenharmony_ci			break;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci		rval = (rval & MP2629_MASK_INPUT_TYPE) >> 5;
37062306a36Sopenharmony_ci		switch (rval) {
37162306a36Sopenharmony_ci		case MP2629_SOURCE_TYPE_SDP:
37262306a36Sopenharmony_ci			val->intval = POWER_SUPPLY_USB_TYPE_SDP;
37362306a36Sopenharmony_ci			break;
37462306a36Sopenharmony_ci		case MP2629_SOURCE_TYPE_CDP:
37562306a36Sopenharmony_ci			val->intval = POWER_SUPPLY_USB_TYPE_CDP;
37662306a36Sopenharmony_ci			break;
37762306a36Sopenharmony_ci		case MP2629_SOURCE_TYPE_DCP:
37862306a36Sopenharmony_ci			val->intval = POWER_SUPPLY_USB_TYPE_DCP;
37962306a36Sopenharmony_ci			break;
38062306a36Sopenharmony_ci		case MP2629_SOURCE_TYPE_OTG:
38162306a36Sopenharmony_ci			val->intval = POWER_SUPPLY_USB_TYPE_PD_DRP;
38262306a36Sopenharmony_ci			break;
38362306a36Sopenharmony_ci		default:
38462306a36Sopenharmony_ci			val->intval = POWER_SUPPLY_USB_TYPE_UNKNOWN;
38562306a36Sopenharmony_ci			break;
38662306a36Sopenharmony_ci		}
38762306a36Sopenharmony_ci		break;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
39062306a36Sopenharmony_ci		ret = mp2629_read_adc(charger, MP2629_INPUT_VOLT, val);
39162306a36Sopenharmony_ci		break;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_NOW:
39462306a36Sopenharmony_ci		ret = mp2629_read_adc(charger, MP2629_INPUT_CURRENT, val);
39562306a36Sopenharmony_ci		break;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
39862306a36Sopenharmony_ci		ret = mp2629_get_prop(charger, INPUT_VLIM, val);
39962306a36Sopenharmony_ci		break;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
40262306a36Sopenharmony_ci		ret = mp2629_get_prop(charger, INPUT_ILIM, val);
40362306a36Sopenharmony_ci		break;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	default:
40662306a36Sopenharmony_ci		return -EINVAL;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	return ret;
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_cistatic int mp2629_charger_usb_set_prop(struct power_supply *psy,
41362306a36Sopenharmony_ci				enum power_supply_property psp,
41462306a36Sopenharmony_ci				const union power_supply_propval *val)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	struct mp2629_charger *charger = dev_get_drvdata(psy->dev.parent);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	switch (psp) {
41962306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
42062306a36Sopenharmony_ci		return mp2629_set_prop(charger, INPUT_VLIM, val);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
42362306a36Sopenharmony_ci		return mp2629_set_prop(charger, INPUT_ILIM, val);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	default:
42662306a36Sopenharmony_ci		return -EINVAL;
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_cistatic int mp2629_charger_battery_prop_writeable(struct power_supply *psy,
43162306a36Sopenharmony_ci				     enum power_supply_property psp)
43262306a36Sopenharmony_ci{
43362306a36Sopenharmony_ci	return (psp == POWER_SUPPLY_PROP_PRECHARGE_CURRENT) ||
43462306a36Sopenharmony_ci	       (psp == POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT) ||
43562306a36Sopenharmony_ci	       (psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT) ||
43662306a36Sopenharmony_ci	       (psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE);
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic int mp2629_charger_usb_prop_writeable(struct power_supply *psy,
44062306a36Sopenharmony_ci				     enum power_supply_property psp)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	return (psp == POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT) ||
44362306a36Sopenharmony_ci	       (psp == POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT);
44462306a36Sopenharmony_ci}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cistatic irqreturn_t mp2629_irq_handler(int irq, void *dev_id)
44762306a36Sopenharmony_ci{
44862306a36Sopenharmony_ci	struct mp2629_charger *charger = dev_id;
44962306a36Sopenharmony_ci	unsigned int rval;
45062306a36Sopenharmony_ci	int ret;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	mutex_lock(&charger->lock);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	ret = regmap_read(charger->regmap, MP2629_REG_FAULT, &rval);
45562306a36Sopenharmony_ci	if (ret)
45662306a36Sopenharmony_ci		goto unlock;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	if (rval) {
45962306a36Sopenharmony_ci		charger->fault = rval;
46062306a36Sopenharmony_ci		if (MP2629_FAULT_BATTERY & rval)
46162306a36Sopenharmony_ci			dev_err(charger->dev, "Battery fault OVP\n");
46262306a36Sopenharmony_ci		else if (MP2629_FAULT_THERMAL & rval)
46362306a36Sopenharmony_ci			dev_err(charger->dev, "Thermal shutdown fault\n");
46462306a36Sopenharmony_ci		else if (MP2629_FAULT_INPUT & rval)
46562306a36Sopenharmony_ci			dev_err(charger->dev, "no input or input OVP\n");
46662306a36Sopenharmony_ci		else if (MP2629_FAULT_OTG & rval)
46762306a36Sopenharmony_ci			dev_err(charger->dev, "VIN overloaded\n");
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci		goto unlock;
47062306a36Sopenharmony_ci	}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	ret = regmap_read(charger->regmap, MP2629_REG_STATUS, &rval);
47362306a36Sopenharmony_ci	if (ret)
47462306a36Sopenharmony_ci		goto unlock;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	if (rval & MP2629_INPUTSOURCE_CHANGE)
47762306a36Sopenharmony_ci		power_supply_changed(charger->usb);
47862306a36Sopenharmony_ci	else if (rval & MP2629_CHARGING_CHANGE)
47962306a36Sopenharmony_ci		power_supply_changed(charger->battery);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ciunlock:
48262306a36Sopenharmony_ci	mutex_unlock(&charger->lock);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	return IRQ_HANDLED;
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistatic const struct power_supply_desc mp2629_usb_desc = {
48862306a36Sopenharmony_ci	.name		= "mp2629_usb",
48962306a36Sopenharmony_ci	.type		= POWER_SUPPLY_TYPE_USB,
49062306a36Sopenharmony_ci	.usb_types      = mp2629_usb_types,
49162306a36Sopenharmony_ci	.num_usb_types  = ARRAY_SIZE(mp2629_usb_types),
49262306a36Sopenharmony_ci	.properties	= mp2629_charger_usb_props,
49362306a36Sopenharmony_ci	.num_properties	= ARRAY_SIZE(mp2629_charger_usb_props),
49462306a36Sopenharmony_ci	.get_property	= mp2629_charger_usb_get_prop,
49562306a36Sopenharmony_ci	.set_property	= mp2629_charger_usb_set_prop,
49662306a36Sopenharmony_ci	.property_is_writeable = mp2629_charger_usb_prop_writeable,
49762306a36Sopenharmony_ci};
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistatic const struct power_supply_desc mp2629_battery_desc = {
50062306a36Sopenharmony_ci	.name		= "mp2629_battery",
50162306a36Sopenharmony_ci	.type		= POWER_SUPPLY_TYPE_BATTERY,
50262306a36Sopenharmony_ci	.properties	= mp2629_charger_bat_props,
50362306a36Sopenharmony_ci	.num_properties	= ARRAY_SIZE(mp2629_charger_bat_props),
50462306a36Sopenharmony_ci	.get_property	= mp2629_charger_battery_get_prop,
50562306a36Sopenharmony_ci	.set_property	= mp2629_charger_battery_set_prop,
50662306a36Sopenharmony_ci	.property_is_writeable = mp2629_charger_battery_prop_writeable,
50762306a36Sopenharmony_ci};
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cistatic ssize_t batt_impedance_compensation_show(struct device *dev,
51062306a36Sopenharmony_ci					   struct device_attribute *attr,
51162306a36Sopenharmony_ci					   char *buf)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	struct mp2629_charger *charger = dev_get_drvdata(dev->parent);
51462306a36Sopenharmony_ci	unsigned int rval;
51562306a36Sopenharmony_ci	int ret;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	ret = regmap_read(charger->regmap, MP2629_REG_IMPEDANCE_COMP, &rval);
51862306a36Sopenharmony_ci	if (ret)
51962306a36Sopenharmony_ci		return ret;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	rval = (rval >> 4) * 10;
52262306a36Sopenharmony_ci	return sysfs_emit(buf, "%d mohm\n", rval);
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_cistatic ssize_t batt_impedance_compensation_store(struct device *dev,
52662306a36Sopenharmony_ci					    struct device_attribute *attr,
52762306a36Sopenharmony_ci					    const char *buf,
52862306a36Sopenharmony_ci					    size_t count)
52962306a36Sopenharmony_ci{
53062306a36Sopenharmony_ci	struct mp2629_charger *charger = dev_get_drvdata(dev->parent);
53162306a36Sopenharmony_ci	unsigned int val;
53262306a36Sopenharmony_ci	int ret;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	ret = kstrtouint(buf, 10, &val);
53562306a36Sopenharmony_ci	if (ret)
53662306a36Sopenharmony_ci		return ret;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	if (val > 140)
53962306a36Sopenharmony_ci		return -ERANGE;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	/* multiples of 10 mohm so round off */
54262306a36Sopenharmony_ci	val = val / 10;
54362306a36Sopenharmony_ci	ret = regmap_update_bits(charger->regmap, MP2629_REG_IMPEDANCE_COMP,
54462306a36Sopenharmony_ci					MP2629_MASK_IMPEDANCE, val << 4);
54562306a36Sopenharmony_ci	if (ret)
54662306a36Sopenharmony_ci		return ret;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	return count;
54962306a36Sopenharmony_ci}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_cistatic DEVICE_ATTR_RW(batt_impedance_compensation);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_cistatic struct attribute *mp2629_charger_sysfs_attrs[] = {
55462306a36Sopenharmony_ci	&dev_attr_batt_impedance_compensation.attr,
55562306a36Sopenharmony_ci	NULL
55662306a36Sopenharmony_ci};
55762306a36Sopenharmony_ciATTRIBUTE_GROUPS(mp2629_charger_sysfs);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic void mp2629_charger_disable(void *data)
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	struct mp2629_charger *charger = data;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	regmap_update_bits(charger->regmap, MP2629_REG_CHARGE_CTRL,
56462306a36Sopenharmony_ci					MP2629_MASK_CHARGE_CTRL, 0);
56562306a36Sopenharmony_ci}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_cistatic int mp2629_charger_probe(struct platform_device *pdev)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
57062306a36Sopenharmony_ci	struct mp2629_data *ddata = dev_get_drvdata(dev->parent);
57162306a36Sopenharmony_ci	struct mp2629_charger *charger;
57262306a36Sopenharmony_ci	struct power_supply_config psy_cfg = {};
57362306a36Sopenharmony_ci	int ret, i, irq;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	charger = devm_kzalloc(dev, sizeof(*charger), GFP_KERNEL);
57662306a36Sopenharmony_ci	if (!charger)
57762306a36Sopenharmony_ci		return -ENOMEM;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	charger->regmap = ddata->regmap;
58062306a36Sopenharmony_ci	charger->dev = dev;
58162306a36Sopenharmony_ci	platform_set_drvdata(pdev, charger);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	irq = platform_get_irq(to_platform_device(dev->parent), 0);
58462306a36Sopenharmony_ci	if (irq < 0)
58562306a36Sopenharmony_ci		return irq;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	for (i = 0; i < MP2629_MAX_FIELD; i++) {
58862306a36Sopenharmony_ci		charger->regmap_fields[i] = devm_regmap_field_alloc(dev,
58962306a36Sopenharmony_ci					charger->regmap, mp2629_reg_fields[i]);
59062306a36Sopenharmony_ci		if (IS_ERR(charger->regmap_fields[i])) {
59162306a36Sopenharmony_ci			dev_err(dev, "regmap field alloc fail %d\n", i);
59262306a36Sopenharmony_ci			return PTR_ERR(charger->regmap_fields[i]);
59362306a36Sopenharmony_ci		}
59462306a36Sopenharmony_ci	}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	for (i = 0; i < MP2629_ADC_CHAN_END; i++) {
59762306a36Sopenharmony_ci		charger->iiochan[i] = devm_iio_channel_get(dev,
59862306a36Sopenharmony_ci							adc_chan_name[i]);
59962306a36Sopenharmony_ci		if (IS_ERR(charger->iiochan[i])) {
60062306a36Sopenharmony_ci			dev_err(dev, "iio chan get %s err\n", adc_chan_name[i]);
60162306a36Sopenharmony_ci			return PTR_ERR(charger->iiochan[i]);
60262306a36Sopenharmony_ci		}
60362306a36Sopenharmony_ci	}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	ret = devm_add_action_or_reset(dev, mp2629_charger_disable, charger);
60662306a36Sopenharmony_ci	if (ret)
60762306a36Sopenharmony_ci		return ret;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	charger->usb = devm_power_supply_register(dev, &mp2629_usb_desc, NULL);
61062306a36Sopenharmony_ci	if (IS_ERR(charger->usb)) {
61162306a36Sopenharmony_ci		dev_err(dev, "power supply register usb failed\n");
61262306a36Sopenharmony_ci		return PTR_ERR(charger->usb);
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	psy_cfg.drv_data = charger;
61662306a36Sopenharmony_ci	psy_cfg.attr_grp = mp2629_charger_sysfs_groups;
61762306a36Sopenharmony_ci	charger->battery = devm_power_supply_register(dev,
61862306a36Sopenharmony_ci					 &mp2629_battery_desc, &psy_cfg);
61962306a36Sopenharmony_ci	if (IS_ERR(charger->battery)) {
62062306a36Sopenharmony_ci		dev_err(dev, "power supply register battery failed\n");
62162306a36Sopenharmony_ci		return PTR_ERR(charger->battery);
62262306a36Sopenharmony_ci	}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	ret = regmap_update_bits(charger->regmap, MP2629_REG_CHARGE_CTRL,
62562306a36Sopenharmony_ci					MP2629_MASK_CHARGE_CTRL, BIT(4));
62662306a36Sopenharmony_ci	if (ret) {
62762306a36Sopenharmony_ci		dev_err(dev, "enable charge fail: %d\n", ret);
62862306a36Sopenharmony_ci		return ret;
62962306a36Sopenharmony_ci	}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	regmap_update_bits(charger->regmap, MP2629_REG_TIMER_CTRL,
63262306a36Sopenharmony_ci					MP2629_MASK_WDOG_CTRL, 0);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	mutex_init(&charger->lock);
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	ret = devm_request_threaded_irq(dev, irq, NULL,	mp2629_irq_handler,
63762306a36Sopenharmony_ci					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
63862306a36Sopenharmony_ci					"mp2629-charger", charger);
63962306a36Sopenharmony_ci	if (ret) {
64062306a36Sopenharmony_ci		dev_err(dev, "failed to request gpio IRQ\n");
64162306a36Sopenharmony_ci		return ret;
64262306a36Sopenharmony_ci	}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	regmap_update_bits(charger->regmap, MP2629_REG_INTERRUPT,
64562306a36Sopenharmony_ci				GENMASK(6, 5), BIT(6) | BIT(5));
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	return 0;
64862306a36Sopenharmony_ci}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_cistatic const struct of_device_id mp2629_charger_of_match[] = {
65162306a36Sopenharmony_ci	{ .compatible = "mps,mp2629_charger"},
65262306a36Sopenharmony_ci	{}
65362306a36Sopenharmony_ci};
65462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mp2629_charger_of_match);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_cistatic struct platform_driver mp2629_charger_driver = {
65762306a36Sopenharmony_ci	.driver = {
65862306a36Sopenharmony_ci		.name = "mp2629_charger",
65962306a36Sopenharmony_ci		.of_match_table = mp2629_charger_of_match,
66062306a36Sopenharmony_ci	},
66162306a36Sopenharmony_ci	.probe		= mp2629_charger_probe,
66262306a36Sopenharmony_ci};
66362306a36Sopenharmony_cimodule_platform_driver(mp2629_charger_driver);
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ciMODULE_AUTHOR("Saravanan Sekar <sravanhome@gmail.com>");
66662306a36Sopenharmony_ciMODULE_DESCRIPTION("MP2629 Charger driver");
66762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
668