162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * AXP20x PMIC USB power supply status driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com>
662306a36Sopenharmony_ci * Copyright (C) 2014 Bruno Prémont <bonbons@linux-vserver.org>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/bitops.h>
1062306a36Sopenharmony_ci#include <linux/device.h>
1162306a36Sopenharmony_ci#include <linux/devm-helpers.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/interrupt.h>
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/mfd/axp20x.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/of.h>
1862306a36Sopenharmony_ci#include <linux/platform_device.h>
1962306a36Sopenharmony_ci#include <linux/pm.h>
2062306a36Sopenharmony_ci#include <linux/power_supply.h>
2162306a36Sopenharmony_ci#include <linux/regmap.h>
2262306a36Sopenharmony_ci#include <linux/slab.h>
2362306a36Sopenharmony_ci#include <linux/iio/consumer.h>
2462306a36Sopenharmony_ci#include <linux/workqueue.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define DRVNAME "axp20x-usb-power-supply"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define AXP192_USB_OTG_STATUS		0x04
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define AXP20X_PWR_STATUS_VBUS_PRESENT	BIT(5)
3162306a36Sopenharmony_ci#define AXP20X_PWR_STATUS_VBUS_USED	BIT(4)
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define AXP20X_USB_STATUS_VBUS_VALID	BIT(2)
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define AXP20X_VBUS_VHOLD_uV(b)		(4000000 + (((b) >> 3) & 7) * 100000)
3662306a36Sopenharmony_ci#define AXP20X_VBUS_VHOLD_MASK		GENMASK(5, 3)
3762306a36Sopenharmony_ci#define AXP20X_VBUS_VHOLD_OFFSET	3
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define AXP20X_ADC_EN1_VBUS_CURR	BIT(2)
4062306a36Sopenharmony_ci#define AXP20X_ADC_EN1_VBUS_VOLT	BIT(3)
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/*
4362306a36Sopenharmony_ci * Note do not raise the debounce time, we must report Vusb high within
4462306a36Sopenharmony_ci * 100ms otherwise we get Vbus errors in musb.
4562306a36Sopenharmony_ci */
4662306a36Sopenharmony_ci#define DEBOUNCE_TIME			msecs_to_jiffies(50)
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistruct axp_data {
4962306a36Sopenharmony_ci	const struct power_supply_desc	*power_desc;
5062306a36Sopenharmony_ci	const char * const		*irq_names;
5162306a36Sopenharmony_ci	unsigned int			num_irq_names;
5262306a36Sopenharmony_ci	const int			*curr_lim_table;
5362306a36Sopenharmony_ci	struct reg_field		curr_lim_fld;
5462306a36Sopenharmony_ci	struct reg_field		vbus_valid_bit;
5562306a36Sopenharmony_ci	struct reg_field		vbus_mon_bit;
5662306a36Sopenharmony_ci	struct reg_field		usb_bc_en_bit;
5762306a36Sopenharmony_ci	struct reg_field		vbus_disable_bit;
5862306a36Sopenharmony_ci	bool				vbus_needs_polling: 1;
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistruct axp20x_usb_power {
6262306a36Sopenharmony_ci	struct regmap *regmap;
6362306a36Sopenharmony_ci	struct regmap_field *curr_lim_fld;
6462306a36Sopenharmony_ci	struct regmap_field *vbus_valid_bit;
6562306a36Sopenharmony_ci	struct regmap_field *vbus_mon_bit;
6662306a36Sopenharmony_ci	struct regmap_field *usb_bc_en_bit;
6762306a36Sopenharmony_ci	struct regmap_field *vbus_disable_bit;
6862306a36Sopenharmony_ci	struct power_supply *supply;
6962306a36Sopenharmony_ci	const struct axp_data *axp_data;
7062306a36Sopenharmony_ci	struct iio_channel *vbus_v;
7162306a36Sopenharmony_ci	struct iio_channel *vbus_i;
7262306a36Sopenharmony_ci	struct delayed_work vbus_detect;
7362306a36Sopenharmony_ci	unsigned int old_status;
7462306a36Sopenharmony_ci	unsigned int online;
7562306a36Sopenharmony_ci	unsigned int num_irqs;
7662306a36Sopenharmony_ci	unsigned int irqs[];
7762306a36Sopenharmony_ci};
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic bool axp20x_usb_vbus_needs_polling(struct axp20x_usb_power *power)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	/*
8262306a36Sopenharmony_ci	 * Polling is only necessary while VBUS is offline. While online, a
8362306a36Sopenharmony_ci	 * present->absent transition implies an online->offline transition
8462306a36Sopenharmony_ci	 * and will trigger the VBUS_REMOVAL IRQ.
8562306a36Sopenharmony_ci	 */
8662306a36Sopenharmony_ci	if (power->axp_data->vbus_needs_polling && !power->online)
8762306a36Sopenharmony_ci		return true;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	return false;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic irqreturn_t axp20x_usb_power_irq(int irq, void *devid)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	struct axp20x_usb_power *power = devid;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	power_supply_changed(power->supply);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	mod_delayed_work(system_power_efficient_wq, &power->vbus_detect, DEBOUNCE_TIME);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	return IRQ_HANDLED;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic void axp20x_usb_power_poll_vbus(struct work_struct *work)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	struct axp20x_usb_power *power =
10662306a36Sopenharmony_ci		container_of(work, struct axp20x_usb_power, vbus_detect.work);
10762306a36Sopenharmony_ci	unsigned int val;
10862306a36Sopenharmony_ci	int ret;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &val);
11162306a36Sopenharmony_ci	if (ret)
11262306a36Sopenharmony_ci		goto out;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	val &= (AXP20X_PWR_STATUS_VBUS_PRESENT | AXP20X_PWR_STATUS_VBUS_USED);
11562306a36Sopenharmony_ci	if (val != power->old_status)
11662306a36Sopenharmony_ci		power_supply_changed(power->supply);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	power->old_status = val;
11962306a36Sopenharmony_ci	power->online = val & AXP20X_PWR_STATUS_VBUS_USED;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ciout:
12262306a36Sopenharmony_ci	if (axp20x_usb_vbus_needs_polling(power))
12362306a36Sopenharmony_ci		mod_delayed_work(system_power_efficient_wq, &power->vbus_detect, DEBOUNCE_TIME);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic int axp20x_usb_power_get_property(struct power_supply *psy,
12762306a36Sopenharmony_ci	enum power_supply_property psp, union power_supply_propval *val)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct axp20x_usb_power *power = power_supply_get_drvdata(psy);
13062306a36Sopenharmony_ci	unsigned int input, v;
13162306a36Sopenharmony_ci	int ret;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	switch (psp) {
13462306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
13562306a36Sopenharmony_ci		ret = regmap_read(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, &v);
13662306a36Sopenharmony_ci		if (ret)
13762306a36Sopenharmony_ci			return ret;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci		val->intval = AXP20X_VBUS_VHOLD_uV(v);
14062306a36Sopenharmony_ci		return 0;
14162306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
14262306a36Sopenharmony_ci		if (IS_ENABLED(CONFIG_AXP20X_ADC)) {
14362306a36Sopenharmony_ci			ret = iio_read_channel_processed(power->vbus_v,
14462306a36Sopenharmony_ci							 &val->intval);
14562306a36Sopenharmony_ci			if (ret)
14662306a36Sopenharmony_ci				return ret;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci			/*
14962306a36Sopenharmony_ci			 * IIO framework gives mV but Power Supply framework
15062306a36Sopenharmony_ci			 * gives uV.
15162306a36Sopenharmony_ci			 */
15262306a36Sopenharmony_ci			val->intval *= 1000;
15362306a36Sopenharmony_ci			return 0;
15462306a36Sopenharmony_ci		}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci		ret = axp20x_read_variable_width(power->regmap,
15762306a36Sopenharmony_ci						 AXP20X_VBUS_V_ADC_H, 12);
15862306a36Sopenharmony_ci		if (ret < 0)
15962306a36Sopenharmony_ci			return ret;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci		val->intval = ret * 1700; /* 1 step = 1.7 mV */
16262306a36Sopenharmony_ci		return 0;
16362306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_MAX:
16462306a36Sopenharmony_ci		ret = regmap_field_read(power->curr_lim_fld, &v);
16562306a36Sopenharmony_ci		if (ret)
16662306a36Sopenharmony_ci			return ret;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci		val->intval = power->axp_data->curr_lim_table[v];
16962306a36Sopenharmony_ci		return 0;
17062306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_NOW:
17162306a36Sopenharmony_ci		if (IS_ENABLED(CONFIG_AXP20X_ADC)) {
17262306a36Sopenharmony_ci			ret = iio_read_channel_processed(power->vbus_i,
17362306a36Sopenharmony_ci							 &val->intval);
17462306a36Sopenharmony_ci			if (ret)
17562306a36Sopenharmony_ci				return ret;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci			/*
17862306a36Sopenharmony_ci			 * IIO framework gives mA but Power Supply framework
17962306a36Sopenharmony_ci			 * gives uA.
18062306a36Sopenharmony_ci			 */
18162306a36Sopenharmony_ci			val->intval *= 1000;
18262306a36Sopenharmony_ci			return 0;
18362306a36Sopenharmony_ci		}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci		ret = axp20x_read_variable_width(power->regmap,
18662306a36Sopenharmony_ci						 AXP20X_VBUS_I_ADC_H, 12);
18762306a36Sopenharmony_ci		if (ret < 0)
18862306a36Sopenharmony_ci			return ret;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci		val->intval = ret * 375; /* 1 step = 0.375 mA */
19162306a36Sopenharmony_ci		return 0;
19262306a36Sopenharmony_ci	default:
19362306a36Sopenharmony_ci		break;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	/* All the properties below need the input-status reg value */
19762306a36Sopenharmony_ci	ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &input);
19862306a36Sopenharmony_ci	if (ret)
19962306a36Sopenharmony_ci		return ret;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	switch (psp) {
20262306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_HEALTH:
20362306a36Sopenharmony_ci		if (!(input & AXP20X_PWR_STATUS_VBUS_PRESENT)) {
20462306a36Sopenharmony_ci			val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
20562306a36Sopenharmony_ci			break;
20662306a36Sopenharmony_ci		}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci		val->intval = POWER_SUPPLY_HEALTH_GOOD;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci		if (power->vbus_valid_bit) {
21162306a36Sopenharmony_ci			ret = regmap_field_read(power->vbus_valid_bit, &v);
21262306a36Sopenharmony_ci			if (ret)
21362306a36Sopenharmony_ci				return ret;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci			if (v == 0)
21662306a36Sopenharmony_ci				val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
21762306a36Sopenharmony_ci		}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		break;
22062306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_PRESENT:
22162306a36Sopenharmony_ci		val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_PRESENT);
22262306a36Sopenharmony_ci		break;
22362306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
22462306a36Sopenharmony_ci		val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_USED);
22562306a36Sopenharmony_ci		break;
22662306a36Sopenharmony_ci	default:
22762306a36Sopenharmony_ci		return -EINVAL;
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	return 0;
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic int axp20x_usb_power_set_voltage_min(struct axp20x_usb_power *power,
23462306a36Sopenharmony_ci					    int intval)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	int val;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	switch (intval) {
23962306a36Sopenharmony_ci	case 4000000:
24062306a36Sopenharmony_ci	case 4100000:
24162306a36Sopenharmony_ci	case 4200000:
24262306a36Sopenharmony_ci	case 4300000:
24362306a36Sopenharmony_ci	case 4400000:
24462306a36Sopenharmony_ci	case 4500000:
24562306a36Sopenharmony_ci	case 4600000:
24662306a36Sopenharmony_ci	case 4700000:
24762306a36Sopenharmony_ci		val = (intval - 4000000) / 100000;
24862306a36Sopenharmony_ci		return regmap_update_bits(power->regmap,
24962306a36Sopenharmony_ci					  AXP20X_VBUS_IPSOUT_MGMT,
25062306a36Sopenharmony_ci					  AXP20X_VBUS_VHOLD_MASK,
25162306a36Sopenharmony_ci					  val << AXP20X_VBUS_VHOLD_OFFSET);
25262306a36Sopenharmony_ci	default:
25362306a36Sopenharmony_ci		return -EINVAL;
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	return -EINVAL;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic int axp20x_usb_power_set_current_max(struct axp20x_usb_power *power, int intval)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	const unsigned int max = GENMASK(power->axp_data->curr_lim_fld.msb,
26262306a36Sopenharmony_ci					 power->axp_data->curr_lim_fld.lsb);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	if (intval == -1)
26562306a36Sopenharmony_ci		return -EINVAL;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	for (unsigned int i = 0; i <= max; ++i)
26862306a36Sopenharmony_ci		if (power->axp_data->curr_lim_table[i] == intval)
26962306a36Sopenharmony_ci			return regmap_field_write(power->curr_lim_fld, i);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	return -EINVAL;
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic int axp20x_usb_power_set_property(struct power_supply *psy,
27562306a36Sopenharmony_ci					 enum power_supply_property psp,
27662306a36Sopenharmony_ci					 const union power_supply_propval *val)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	struct axp20x_usb_power *power = power_supply_get_drvdata(psy);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	switch (psp) {
28162306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
28262306a36Sopenharmony_ci		if (!power->vbus_disable_bit)
28362306a36Sopenharmony_ci			return -EINVAL;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci		return regmap_field_write(power->vbus_disable_bit, !val->intval);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
28862306a36Sopenharmony_ci		return axp20x_usb_power_set_voltage_min(power, val->intval);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_MAX:
29162306a36Sopenharmony_ci		return axp20x_usb_power_set_current_max(power, val->intval);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	default:
29462306a36Sopenharmony_ci		return -EINVAL;
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	return -EINVAL;
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic int axp20x_usb_power_prop_writeable(struct power_supply *psy,
30162306a36Sopenharmony_ci					   enum power_supply_property psp)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	struct axp20x_usb_power *power = power_supply_get_drvdata(psy);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	/*
30662306a36Sopenharmony_ci	 * The VBUS path select flag works differently on AXP288 and newer:
30762306a36Sopenharmony_ci	 *  - On AXP20x and AXP22x, the flag enables VBUS (ignoring N_VBUSEN).
30862306a36Sopenharmony_ci	 *  - On AXP288 and AXP8xx, the flag disables VBUS (ignoring N_VBUSEN).
30962306a36Sopenharmony_ci	 * We only expose the control on variants where it can be used to force
31062306a36Sopenharmony_ci	 * the VBUS input offline.
31162306a36Sopenharmony_ci	 */
31262306a36Sopenharmony_ci	if (psp == POWER_SUPPLY_PROP_ONLINE)
31362306a36Sopenharmony_ci		return power->vbus_disable_bit != NULL;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN ||
31662306a36Sopenharmony_ci	       psp == POWER_SUPPLY_PROP_CURRENT_MAX;
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic enum power_supply_property axp20x_usb_power_properties[] = {
32062306a36Sopenharmony_ci	POWER_SUPPLY_PROP_HEALTH,
32162306a36Sopenharmony_ci	POWER_SUPPLY_PROP_PRESENT,
32262306a36Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
32362306a36Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_MIN,
32462306a36Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_NOW,
32562306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CURRENT_MAX,
32662306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CURRENT_NOW,
32762306a36Sopenharmony_ci};
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic enum power_supply_property axp22x_usb_power_properties[] = {
33062306a36Sopenharmony_ci	POWER_SUPPLY_PROP_HEALTH,
33162306a36Sopenharmony_ci	POWER_SUPPLY_PROP_PRESENT,
33262306a36Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
33362306a36Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_MIN,
33462306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CURRENT_MAX,
33562306a36Sopenharmony_ci};
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic const struct power_supply_desc axp20x_usb_power_desc = {
33862306a36Sopenharmony_ci	.name = "axp20x-usb",
33962306a36Sopenharmony_ci	.type = POWER_SUPPLY_TYPE_USB,
34062306a36Sopenharmony_ci	.properties = axp20x_usb_power_properties,
34162306a36Sopenharmony_ci	.num_properties = ARRAY_SIZE(axp20x_usb_power_properties),
34262306a36Sopenharmony_ci	.property_is_writeable = axp20x_usb_power_prop_writeable,
34362306a36Sopenharmony_ci	.get_property = axp20x_usb_power_get_property,
34462306a36Sopenharmony_ci	.set_property = axp20x_usb_power_set_property,
34562306a36Sopenharmony_ci};
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic const struct power_supply_desc axp22x_usb_power_desc = {
34862306a36Sopenharmony_ci	.name = "axp20x-usb",
34962306a36Sopenharmony_ci	.type = POWER_SUPPLY_TYPE_USB,
35062306a36Sopenharmony_ci	.properties = axp22x_usb_power_properties,
35162306a36Sopenharmony_ci	.num_properties = ARRAY_SIZE(axp22x_usb_power_properties),
35262306a36Sopenharmony_ci	.property_is_writeable = axp20x_usb_power_prop_writeable,
35362306a36Sopenharmony_ci	.get_property = axp20x_usb_power_get_property,
35462306a36Sopenharmony_ci	.set_property = axp20x_usb_power_set_property,
35562306a36Sopenharmony_ci};
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic const char * const axp20x_irq_names[] = {
35862306a36Sopenharmony_ci	"VBUS_PLUGIN",
35962306a36Sopenharmony_ci	"VBUS_REMOVAL",
36062306a36Sopenharmony_ci	"VBUS_VALID",
36162306a36Sopenharmony_ci	"VBUS_NOT_VALID",
36262306a36Sopenharmony_ci};
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistatic const char * const axp22x_irq_names[] = {
36562306a36Sopenharmony_ci	"VBUS_PLUGIN",
36662306a36Sopenharmony_ci	"VBUS_REMOVAL",
36762306a36Sopenharmony_ci};
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic int axp192_usb_curr_lim_table[] = {
37062306a36Sopenharmony_ci	-1,
37162306a36Sopenharmony_ci	-1,
37262306a36Sopenharmony_ci	500000,
37362306a36Sopenharmony_ci	100000,
37462306a36Sopenharmony_ci};
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cistatic int axp20x_usb_curr_lim_table[] = {
37762306a36Sopenharmony_ci	900000,
37862306a36Sopenharmony_ci	500000,
37962306a36Sopenharmony_ci	100000,
38062306a36Sopenharmony_ci	-1,
38162306a36Sopenharmony_ci};
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic int axp221_usb_curr_lim_table[] = {
38462306a36Sopenharmony_ci	900000,
38562306a36Sopenharmony_ci	500000,
38662306a36Sopenharmony_ci	-1,
38762306a36Sopenharmony_ci	-1,
38862306a36Sopenharmony_ci};
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_cistatic int axp813_usb_curr_lim_table[] = {
39162306a36Sopenharmony_ci	900000,
39262306a36Sopenharmony_ci	1500000,
39362306a36Sopenharmony_ci	2000000,
39462306a36Sopenharmony_ci	2500000,
39562306a36Sopenharmony_ci};
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic const struct axp_data axp192_data = {
39862306a36Sopenharmony_ci	.power_desc	= &axp20x_usb_power_desc,
39962306a36Sopenharmony_ci	.irq_names	= axp20x_irq_names,
40062306a36Sopenharmony_ci	.num_irq_names	= ARRAY_SIZE(axp20x_irq_names),
40162306a36Sopenharmony_ci	.curr_lim_table = axp192_usb_curr_lim_table,
40262306a36Sopenharmony_ci	.curr_lim_fld   = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
40362306a36Sopenharmony_ci	.vbus_valid_bit = REG_FIELD(AXP192_USB_OTG_STATUS, 2, 2),
40462306a36Sopenharmony_ci	.vbus_mon_bit   = REG_FIELD(AXP20X_VBUS_MON, 3, 3),
40562306a36Sopenharmony_ci};
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cistatic const struct axp_data axp202_data = {
40862306a36Sopenharmony_ci	.power_desc	= &axp20x_usb_power_desc,
40962306a36Sopenharmony_ci	.irq_names	= axp20x_irq_names,
41062306a36Sopenharmony_ci	.num_irq_names	= ARRAY_SIZE(axp20x_irq_names),
41162306a36Sopenharmony_ci	.curr_lim_table = axp20x_usb_curr_lim_table,
41262306a36Sopenharmony_ci	.curr_lim_fld   = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
41362306a36Sopenharmony_ci	.vbus_valid_bit = REG_FIELD(AXP20X_USB_OTG_STATUS, 2, 2),
41462306a36Sopenharmony_ci	.vbus_mon_bit   = REG_FIELD(AXP20X_VBUS_MON, 3, 3),
41562306a36Sopenharmony_ci};
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic const struct axp_data axp221_data = {
41862306a36Sopenharmony_ci	.power_desc	= &axp22x_usb_power_desc,
41962306a36Sopenharmony_ci	.irq_names	= axp22x_irq_names,
42062306a36Sopenharmony_ci	.num_irq_names	= ARRAY_SIZE(axp22x_irq_names),
42162306a36Sopenharmony_ci	.curr_lim_table = axp221_usb_curr_lim_table,
42262306a36Sopenharmony_ci	.curr_lim_fld   = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
42362306a36Sopenharmony_ci	.vbus_needs_polling = true,
42462306a36Sopenharmony_ci};
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic const struct axp_data axp223_data = {
42762306a36Sopenharmony_ci	.power_desc	= &axp22x_usb_power_desc,
42862306a36Sopenharmony_ci	.irq_names	= axp22x_irq_names,
42962306a36Sopenharmony_ci	.num_irq_names	= ARRAY_SIZE(axp22x_irq_names),
43062306a36Sopenharmony_ci	.curr_lim_table = axp20x_usb_curr_lim_table,
43162306a36Sopenharmony_ci	.curr_lim_fld   = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
43262306a36Sopenharmony_ci	.vbus_needs_polling = true,
43362306a36Sopenharmony_ci};
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic const struct axp_data axp813_data = {
43662306a36Sopenharmony_ci	.power_desc	= &axp22x_usb_power_desc,
43762306a36Sopenharmony_ci	.irq_names	= axp22x_irq_names,
43862306a36Sopenharmony_ci	.num_irq_names	= ARRAY_SIZE(axp22x_irq_names),
43962306a36Sopenharmony_ci	.curr_lim_table = axp813_usb_curr_lim_table,
44062306a36Sopenharmony_ci	.curr_lim_fld   = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
44162306a36Sopenharmony_ci	.usb_bc_en_bit	= REG_FIELD(AXP288_BC_GLOBAL, 0, 0),
44262306a36Sopenharmony_ci	.vbus_disable_bit = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 7, 7),
44362306a36Sopenharmony_ci	.vbus_needs_polling = true,
44462306a36Sopenharmony_ci};
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
44762306a36Sopenharmony_cistatic int axp20x_usb_power_suspend(struct device *dev)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	struct axp20x_usb_power *power = dev_get_drvdata(dev);
45062306a36Sopenharmony_ci	int i = 0;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	/*
45362306a36Sopenharmony_ci	 * Allow wake via VBUS_PLUGIN only.
45462306a36Sopenharmony_ci	 *
45562306a36Sopenharmony_ci	 * As nested threaded IRQs are not automatically disabled during
45662306a36Sopenharmony_ci	 * suspend, we must explicitly disable the remainder of the IRQs.
45762306a36Sopenharmony_ci	 */
45862306a36Sopenharmony_ci	if (device_may_wakeup(&power->supply->dev))
45962306a36Sopenharmony_ci		enable_irq_wake(power->irqs[i++]);
46062306a36Sopenharmony_ci	while (i < power->num_irqs)
46162306a36Sopenharmony_ci		disable_irq(power->irqs[i++]);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	return 0;
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cistatic int axp20x_usb_power_resume(struct device *dev)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci	struct axp20x_usb_power *power = dev_get_drvdata(dev);
46962306a36Sopenharmony_ci	int i = 0;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	if (device_may_wakeup(&power->supply->dev))
47262306a36Sopenharmony_ci		disable_irq_wake(power->irqs[i++]);
47362306a36Sopenharmony_ci	while (i < power->num_irqs)
47462306a36Sopenharmony_ci		enable_irq(power->irqs[i++]);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	mod_delayed_work(system_power_efficient_wq, &power->vbus_detect, DEBOUNCE_TIME);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	return 0;
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci#endif
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(axp20x_usb_power_pm_ops, axp20x_usb_power_suspend,
48362306a36Sopenharmony_ci						  axp20x_usb_power_resume);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic int configure_iio_channels(struct platform_device *pdev,
48662306a36Sopenharmony_ci				  struct axp20x_usb_power *power)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	power->vbus_v = devm_iio_channel_get(&pdev->dev, "vbus_v");
48962306a36Sopenharmony_ci	if (IS_ERR(power->vbus_v)) {
49062306a36Sopenharmony_ci		if (PTR_ERR(power->vbus_v) == -ENODEV)
49162306a36Sopenharmony_ci			return -EPROBE_DEFER;
49262306a36Sopenharmony_ci		return PTR_ERR(power->vbus_v);
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	power->vbus_i = devm_iio_channel_get(&pdev->dev, "vbus_i");
49662306a36Sopenharmony_ci	if (IS_ERR(power->vbus_i)) {
49762306a36Sopenharmony_ci		if (PTR_ERR(power->vbus_i) == -ENODEV)
49862306a36Sopenharmony_ci			return -EPROBE_DEFER;
49962306a36Sopenharmony_ci		return PTR_ERR(power->vbus_i);
50062306a36Sopenharmony_ci	}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	return 0;
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_cistatic int configure_adc_registers(struct axp20x_usb_power *power)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	/* Enable vbus voltage and current measurement */
50862306a36Sopenharmony_ci	return regmap_update_bits(power->regmap, AXP20X_ADC_EN1,
50962306a36Sopenharmony_ci				  AXP20X_ADC_EN1_VBUS_CURR |
51062306a36Sopenharmony_ci				  AXP20X_ADC_EN1_VBUS_VOLT,
51162306a36Sopenharmony_ci				  AXP20X_ADC_EN1_VBUS_CURR |
51262306a36Sopenharmony_ci				  AXP20X_ADC_EN1_VBUS_VOLT);
51362306a36Sopenharmony_ci}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_cistatic int axp20x_regmap_field_alloc_optional(struct device *dev,
51662306a36Sopenharmony_ci					      struct regmap *regmap,
51762306a36Sopenharmony_ci					      struct reg_field fdesc,
51862306a36Sopenharmony_ci					      struct regmap_field **fieldp)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	struct regmap_field *field;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	if (fdesc.reg == 0) {
52362306a36Sopenharmony_ci		*fieldp = NULL;
52462306a36Sopenharmony_ci		return 0;
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	field = devm_regmap_field_alloc(dev, regmap, fdesc);
52862306a36Sopenharmony_ci	if (IS_ERR(field))
52962306a36Sopenharmony_ci		return PTR_ERR(field);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	*fieldp = field;
53262306a36Sopenharmony_ci	return 0;
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic int axp20x_usb_power_probe(struct platform_device *pdev)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
53862306a36Sopenharmony_ci	struct power_supply_config psy_cfg = {};
53962306a36Sopenharmony_ci	struct axp20x_usb_power *power;
54062306a36Sopenharmony_ci	const struct axp_data *axp_data;
54162306a36Sopenharmony_ci	int i, irq, ret;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (!of_device_is_available(pdev->dev.of_node))
54462306a36Sopenharmony_ci		return -ENODEV;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	if (!axp20x) {
54762306a36Sopenharmony_ci		dev_err(&pdev->dev, "Parent drvdata not set\n");
54862306a36Sopenharmony_ci		return -EINVAL;
54962306a36Sopenharmony_ci	}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	axp_data = of_device_get_match_data(&pdev->dev);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	power = devm_kzalloc(&pdev->dev,
55462306a36Sopenharmony_ci			     struct_size(power, irqs, axp_data->num_irq_names),
55562306a36Sopenharmony_ci			     GFP_KERNEL);
55662306a36Sopenharmony_ci	if (!power)
55762306a36Sopenharmony_ci		return -ENOMEM;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	platform_set_drvdata(pdev, power);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	power->axp_data = axp_data;
56262306a36Sopenharmony_ci	power->regmap = axp20x->regmap;
56362306a36Sopenharmony_ci	power->num_irqs = axp_data->num_irq_names;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	power->curr_lim_fld = devm_regmap_field_alloc(&pdev->dev, power->regmap,
56662306a36Sopenharmony_ci						      axp_data->curr_lim_fld);
56762306a36Sopenharmony_ci	if (IS_ERR(power->curr_lim_fld))
56862306a36Sopenharmony_ci		return PTR_ERR(power->curr_lim_fld);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap,
57162306a36Sopenharmony_ci						 axp_data->vbus_valid_bit,
57262306a36Sopenharmony_ci						 &power->vbus_valid_bit);
57362306a36Sopenharmony_ci	if (ret)
57462306a36Sopenharmony_ci		return ret;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap,
57762306a36Sopenharmony_ci						 axp_data->vbus_mon_bit,
57862306a36Sopenharmony_ci						 &power->vbus_mon_bit);
57962306a36Sopenharmony_ci	if (ret)
58062306a36Sopenharmony_ci		return ret;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap,
58362306a36Sopenharmony_ci						 axp_data->usb_bc_en_bit,
58462306a36Sopenharmony_ci						 &power->usb_bc_en_bit);
58562306a36Sopenharmony_ci	if (ret)
58662306a36Sopenharmony_ci		return ret;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap,
58962306a36Sopenharmony_ci						 axp_data->vbus_disable_bit,
59062306a36Sopenharmony_ci						 &power->vbus_disable_bit);
59162306a36Sopenharmony_ci	if (ret)
59262306a36Sopenharmony_ci		return ret;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	ret = devm_delayed_work_autocancel(&pdev->dev, &power->vbus_detect,
59562306a36Sopenharmony_ci					   axp20x_usb_power_poll_vbus);
59662306a36Sopenharmony_ci	if (ret)
59762306a36Sopenharmony_ci		return ret;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	if (power->vbus_mon_bit) {
60062306a36Sopenharmony_ci		/* Enable vbus valid checking */
60162306a36Sopenharmony_ci		ret = regmap_field_write(power->vbus_mon_bit, 1);
60262306a36Sopenharmony_ci		if (ret)
60362306a36Sopenharmony_ci			return ret;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci		if (IS_ENABLED(CONFIG_AXP20X_ADC))
60662306a36Sopenharmony_ci			ret = configure_iio_channels(pdev, power);
60762306a36Sopenharmony_ci		else
60862306a36Sopenharmony_ci			ret = configure_adc_registers(power);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci		if (ret)
61162306a36Sopenharmony_ci			return ret;
61262306a36Sopenharmony_ci	}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	if (power->usb_bc_en_bit) {
61562306a36Sopenharmony_ci		/* Enable USB Battery Charging specification detection */
61662306a36Sopenharmony_ci		ret = regmap_field_write(power->usb_bc_en_bit, 1);
61762306a36Sopenharmony_ci		if (ret)
61862306a36Sopenharmony_ci			return ret;
61962306a36Sopenharmony_ci	}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	psy_cfg.of_node = pdev->dev.of_node;
62262306a36Sopenharmony_ci	psy_cfg.drv_data = power;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	power->supply = devm_power_supply_register(&pdev->dev,
62562306a36Sopenharmony_ci						   axp_data->power_desc,
62662306a36Sopenharmony_ci						   &psy_cfg);
62762306a36Sopenharmony_ci	if (IS_ERR(power->supply))
62862306a36Sopenharmony_ci		return PTR_ERR(power->supply);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	/* Request irqs after registering, as irqs may trigger immediately */
63162306a36Sopenharmony_ci	for (i = 0; i < axp_data->num_irq_names; i++) {
63262306a36Sopenharmony_ci		irq = platform_get_irq_byname(pdev, axp_data->irq_names[i]);
63362306a36Sopenharmony_ci		if (irq < 0)
63462306a36Sopenharmony_ci			return irq;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci		power->irqs[i] = regmap_irq_get_virq(axp20x->regmap_irqc, irq);
63762306a36Sopenharmony_ci		ret = devm_request_any_context_irq(&pdev->dev, power->irqs[i],
63862306a36Sopenharmony_ci						   axp20x_usb_power_irq, 0,
63962306a36Sopenharmony_ci						   DRVNAME, power);
64062306a36Sopenharmony_ci		if (ret < 0) {
64162306a36Sopenharmony_ci			dev_err(&pdev->dev, "Error requesting %s IRQ: %d\n",
64262306a36Sopenharmony_ci				axp_data->irq_names[i], ret);
64362306a36Sopenharmony_ci			return ret;
64462306a36Sopenharmony_ci		}
64562306a36Sopenharmony_ci	}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	if (axp20x_usb_vbus_needs_polling(power))
64862306a36Sopenharmony_ci		queue_delayed_work(system_power_efficient_wq, &power->vbus_detect, 0);
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	return 0;
65162306a36Sopenharmony_ci}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_cistatic const struct of_device_id axp20x_usb_power_match[] = {
65462306a36Sopenharmony_ci	{
65562306a36Sopenharmony_ci		.compatible = "x-powers,axp192-usb-power-supply",
65662306a36Sopenharmony_ci		.data = &axp192_data,
65762306a36Sopenharmony_ci	}, {
65862306a36Sopenharmony_ci		.compatible = "x-powers,axp202-usb-power-supply",
65962306a36Sopenharmony_ci		.data = &axp202_data,
66062306a36Sopenharmony_ci	}, {
66162306a36Sopenharmony_ci		.compatible = "x-powers,axp221-usb-power-supply",
66262306a36Sopenharmony_ci		.data = &axp221_data,
66362306a36Sopenharmony_ci	}, {
66462306a36Sopenharmony_ci		.compatible = "x-powers,axp223-usb-power-supply",
66562306a36Sopenharmony_ci		.data = &axp223_data,
66662306a36Sopenharmony_ci	}, {
66762306a36Sopenharmony_ci		.compatible = "x-powers,axp813-usb-power-supply",
66862306a36Sopenharmony_ci		.data = &axp813_data,
66962306a36Sopenharmony_ci	}, { /* sentinel */ }
67062306a36Sopenharmony_ci};
67162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, axp20x_usb_power_match);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_cistatic struct platform_driver axp20x_usb_power_driver = {
67462306a36Sopenharmony_ci	.probe = axp20x_usb_power_probe,
67562306a36Sopenharmony_ci	.driver = {
67662306a36Sopenharmony_ci		.name		= DRVNAME,
67762306a36Sopenharmony_ci		.of_match_table	= axp20x_usb_power_match,
67862306a36Sopenharmony_ci		.pm		= &axp20x_usb_power_pm_ops,
67962306a36Sopenharmony_ci	},
68062306a36Sopenharmony_ci};
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cimodule_platform_driver(axp20x_usb_power_driver);
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ciMODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
68562306a36Sopenharmony_ciMODULE_DESCRIPTION("AXP20x PMIC USB power supply status driver");
68662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
687