18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * AXP20X and AXP22X PMICs' ACIN power supply driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Free Electrons
68c2ecf20Sopenharmony_ci *	Quentin Schulz <quentin.schulz@free-electrons.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/device.h>
108c2ecf20Sopenharmony_ci#include <linux/init.h>
118c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
128c2ecf20Sopenharmony_ci#include <linux/kernel.h>
138c2ecf20Sopenharmony_ci#include <linux/mfd/axp20x.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/of.h>
168c2ecf20Sopenharmony_ci#include <linux/of_device.h>
178c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
188c2ecf20Sopenharmony_ci#include <linux/pm.h>
198c2ecf20Sopenharmony_ci#include <linux/power_supply.h>
208c2ecf20Sopenharmony_ci#include <linux/regmap.h>
218c2ecf20Sopenharmony_ci#include <linux/slab.h>
228c2ecf20Sopenharmony_ci#include <linux/iio/consumer.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define AXP20X_PWR_STATUS_ACIN_PRESENT	BIT(7)
258c2ecf20Sopenharmony_ci#define AXP20X_PWR_STATUS_ACIN_AVAIL	BIT(6)
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define AXP813_ACIN_PATH_SEL		BIT(7)
288c2ecf20Sopenharmony_ci#define AXP813_ACIN_PATH_SEL_TO_BIT(x)	(!!(x) << 7)
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define AXP813_VHOLD_MASK		GENMASK(5, 3)
318c2ecf20Sopenharmony_ci#define AXP813_VHOLD_UV_TO_BIT(x)	((((x) / 100000) - 40) << 3)
328c2ecf20Sopenharmony_ci#define AXP813_VHOLD_REG_TO_UV(x)	\
338c2ecf20Sopenharmony_ci	(((((x) & AXP813_VHOLD_MASK) >> 3) + 40) * 100000)
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define AXP813_CURR_LIMIT_MASK		GENMASK(2, 0)
368c2ecf20Sopenharmony_ci#define AXP813_CURR_LIMIT_UA_TO_BIT(x)	(((x) / 500000) - 3)
378c2ecf20Sopenharmony_ci#define AXP813_CURR_LIMIT_REG_TO_UA(x)	\
388c2ecf20Sopenharmony_ci	((((x) & AXP813_CURR_LIMIT_MASK) + 3) * 500000)
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define DRVNAME "axp20x-ac-power-supply"
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistruct axp20x_ac_power {
438c2ecf20Sopenharmony_ci	struct regmap *regmap;
448c2ecf20Sopenharmony_ci	struct power_supply *supply;
458c2ecf20Sopenharmony_ci	struct iio_channel *acin_v;
468c2ecf20Sopenharmony_ci	struct iio_channel *acin_i;
478c2ecf20Sopenharmony_ci	bool has_acin_path_sel;
488c2ecf20Sopenharmony_ci	unsigned int num_irqs;
498c2ecf20Sopenharmony_ci	unsigned int irqs[];
508c2ecf20Sopenharmony_ci};
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic irqreturn_t axp20x_ac_power_irq(int irq, void *devid)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	struct axp20x_ac_power *power = devid;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	power_supply_changed(power->supply);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic int axp20x_ac_power_get_property(struct power_supply *psy,
628c2ecf20Sopenharmony_ci					enum power_supply_property psp,
638c2ecf20Sopenharmony_ci					union power_supply_propval *val)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct axp20x_ac_power *power = power_supply_get_drvdata(psy);
668c2ecf20Sopenharmony_ci	int ret, reg;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	switch (psp) {
698c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_HEALTH:
708c2ecf20Sopenharmony_ci		ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &reg);
718c2ecf20Sopenharmony_ci		if (ret)
728c2ecf20Sopenharmony_ci			return ret;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci		if (reg & AXP20X_PWR_STATUS_ACIN_PRESENT) {
758c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_HEALTH_GOOD;
768c2ecf20Sopenharmony_ci			return 0;
778c2ecf20Sopenharmony_ci		}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci		val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
808c2ecf20Sopenharmony_ci		return 0;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_PRESENT:
838c2ecf20Sopenharmony_ci		ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &reg);
848c2ecf20Sopenharmony_ci		if (ret)
858c2ecf20Sopenharmony_ci			return ret;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci		val->intval = !!(reg & AXP20X_PWR_STATUS_ACIN_PRESENT);
888c2ecf20Sopenharmony_ci		return 0;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
918c2ecf20Sopenharmony_ci		ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &reg);
928c2ecf20Sopenharmony_ci		if (ret)
938c2ecf20Sopenharmony_ci			return ret;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci		val->intval = !!(reg & AXP20X_PWR_STATUS_ACIN_AVAIL);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci		/* ACIN_PATH_SEL disables ACIN even if ACIN_AVAIL is set. */
988c2ecf20Sopenharmony_ci		if (val->intval && power->has_acin_path_sel) {
998c2ecf20Sopenharmony_ci			ret = regmap_read(power->regmap, AXP813_ACIN_PATH_CTRL,
1008c2ecf20Sopenharmony_ci					  &reg);
1018c2ecf20Sopenharmony_ci			if (ret)
1028c2ecf20Sopenharmony_ci				return ret;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci			val->intval = !!(reg & AXP813_ACIN_PATH_SEL);
1058c2ecf20Sopenharmony_ci		}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci		return 0;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
1108c2ecf20Sopenharmony_ci		ret = iio_read_channel_processed(power->acin_v, &val->intval);
1118c2ecf20Sopenharmony_ci		if (ret)
1128c2ecf20Sopenharmony_ci			return ret;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci		/* IIO framework gives mV but Power Supply framework gives uV */
1158c2ecf20Sopenharmony_ci		val->intval *= 1000;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci		return 0;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_NOW:
1208c2ecf20Sopenharmony_ci		ret = iio_read_channel_processed(power->acin_i, &val->intval);
1218c2ecf20Sopenharmony_ci		if (ret)
1228c2ecf20Sopenharmony_ci			return ret;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci		/* IIO framework gives mA but Power Supply framework gives uA */
1258c2ecf20Sopenharmony_ci		val->intval *= 1000;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci		return 0;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
1308c2ecf20Sopenharmony_ci		ret = regmap_read(power->regmap, AXP813_ACIN_PATH_CTRL, &reg);
1318c2ecf20Sopenharmony_ci		if (ret)
1328c2ecf20Sopenharmony_ci			return ret;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci		val->intval = AXP813_VHOLD_REG_TO_UV(reg);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci		return 0;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
1398c2ecf20Sopenharmony_ci		ret = regmap_read(power->regmap, AXP813_ACIN_PATH_CTRL, &reg);
1408c2ecf20Sopenharmony_ci		if (ret)
1418c2ecf20Sopenharmony_ci			return ret;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci		val->intval = AXP813_CURR_LIMIT_REG_TO_UA(reg);
1448c2ecf20Sopenharmony_ci		/* AXP813 datasheet defines values 11x as 4000mA */
1458c2ecf20Sopenharmony_ci		if (val->intval > 4000000)
1468c2ecf20Sopenharmony_ci			val->intval = 4000000;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci		return 0;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	default:
1518c2ecf20Sopenharmony_ci		return -EINVAL;
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	return -EINVAL;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic int axp813_ac_power_set_property(struct power_supply *psy,
1588c2ecf20Sopenharmony_ci					enum power_supply_property psp,
1598c2ecf20Sopenharmony_ci					const union power_supply_propval *val)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	struct axp20x_ac_power *power = power_supply_get_drvdata(psy);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	switch (psp) {
1648c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
1658c2ecf20Sopenharmony_ci		return regmap_update_bits(power->regmap, AXP813_ACIN_PATH_CTRL,
1668c2ecf20Sopenharmony_ci					  AXP813_ACIN_PATH_SEL,
1678c2ecf20Sopenharmony_ci					  AXP813_ACIN_PATH_SEL_TO_BIT(val->intval));
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
1708c2ecf20Sopenharmony_ci		if (val->intval < 4000000 || val->intval > 4700000)
1718c2ecf20Sopenharmony_ci			return -EINVAL;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci		return regmap_update_bits(power->regmap, AXP813_ACIN_PATH_CTRL,
1748c2ecf20Sopenharmony_ci					  AXP813_VHOLD_MASK,
1758c2ecf20Sopenharmony_ci					  AXP813_VHOLD_UV_TO_BIT(val->intval));
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
1788c2ecf20Sopenharmony_ci		if (val->intval < 1500000 || val->intval > 4000000)
1798c2ecf20Sopenharmony_ci			return -EINVAL;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci		return regmap_update_bits(power->regmap, AXP813_ACIN_PATH_CTRL,
1828c2ecf20Sopenharmony_ci					  AXP813_CURR_LIMIT_MASK,
1838c2ecf20Sopenharmony_ci					  AXP813_CURR_LIMIT_UA_TO_BIT(val->intval));
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	default:
1868c2ecf20Sopenharmony_ci		return -EINVAL;
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	return -EINVAL;
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic int axp813_ac_power_prop_writeable(struct power_supply *psy,
1938c2ecf20Sopenharmony_ci					  enum power_supply_property psp)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	return psp == POWER_SUPPLY_PROP_ONLINE ||
1968c2ecf20Sopenharmony_ci	       psp == POWER_SUPPLY_PROP_VOLTAGE_MIN ||
1978c2ecf20Sopenharmony_ci	       psp == POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT;
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic enum power_supply_property axp20x_ac_power_properties[] = {
2018c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_HEALTH,
2028c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_PRESENT,
2038c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
2048c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_NOW,
2058c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CURRENT_NOW,
2068c2ecf20Sopenharmony_ci};
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic enum power_supply_property axp22x_ac_power_properties[] = {
2098c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_HEALTH,
2108c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_PRESENT,
2118c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
2128c2ecf20Sopenharmony_ci};
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic enum power_supply_property axp813_ac_power_properties[] = {
2158c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_HEALTH,
2168c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_PRESENT,
2178c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
2188c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_MIN,
2198c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
2208c2ecf20Sopenharmony_ci};
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic const struct power_supply_desc axp20x_ac_power_desc = {
2238c2ecf20Sopenharmony_ci	.name = "axp20x-ac",
2248c2ecf20Sopenharmony_ci	.type = POWER_SUPPLY_TYPE_MAINS,
2258c2ecf20Sopenharmony_ci	.properties = axp20x_ac_power_properties,
2268c2ecf20Sopenharmony_ci	.num_properties = ARRAY_SIZE(axp20x_ac_power_properties),
2278c2ecf20Sopenharmony_ci	.get_property = axp20x_ac_power_get_property,
2288c2ecf20Sopenharmony_ci};
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic const struct power_supply_desc axp22x_ac_power_desc = {
2318c2ecf20Sopenharmony_ci	.name = "axp22x-ac",
2328c2ecf20Sopenharmony_ci	.type = POWER_SUPPLY_TYPE_MAINS,
2338c2ecf20Sopenharmony_ci	.properties = axp22x_ac_power_properties,
2348c2ecf20Sopenharmony_ci	.num_properties = ARRAY_SIZE(axp22x_ac_power_properties),
2358c2ecf20Sopenharmony_ci	.get_property = axp20x_ac_power_get_property,
2368c2ecf20Sopenharmony_ci};
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistatic const struct power_supply_desc axp813_ac_power_desc = {
2398c2ecf20Sopenharmony_ci	.name = "axp813-ac",
2408c2ecf20Sopenharmony_ci	.type = POWER_SUPPLY_TYPE_MAINS,
2418c2ecf20Sopenharmony_ci	.properties = axp813_ac_power_properties,
2428c2ecf20Sopenharmony_ci	.num_properties = ARRAY_SIZE(axp813_ac_power_properties),
2438c2ecf20Sopenharmony_ci	.property_is_writeable = axp813_ac_power_prop_writeable,
2448c2ecf20Sopenharmony_ci	.get_property = axp20x_ac_power_get_property,
2458c2ecf20Sopenharmony_ci	.set_property = axp813_ac_power_set_property,
2468c2ecf20Sopenharmony_ci};
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic const char * const axp20x_irq_names[] = {
2498c2ecf20Sopenharmony_ci	"ACIN_PLUGIN",
2508c2ecf20Sopenharmony_ci	"ACIN_REMOVAL",
2518c2ecf20Sopenharmony_ci};
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistruct axp_data {
2548c2ecf20Sopenharmony_ci	const struct power_supply_desc	*power_desc;
2558c2ecf20Sopenharmony_ci	const char * const		*irq_names;
2568c2ecf20Sopenharmony_ci	unsigned int			num_irq_names;
2578c2ecf20Sopenharmony_ci	bool				acin_adc;
2588c2ecf20Sopenharmony_ci	bool				acin_path_sel;
2598c2ecf20Sopenharmony_ci};
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic const struct axp_data axp20x_data = {
2628c2ecf20Sopenharmony_ci	.power_desc	= &axp20x_ac_power_desc,
2638c2ecf20Sopenharmony_ci	.irq_names	= axp20x_irq_names,
2648c2ecf20Sopenharmony_ci	.num_irq_names	= ARRAY_SIZE(axp20x_irq_names),
2658c2ecf20Sopenharmony_ci	.acin_adc	= true,
2668c2ecf20Sopenharmony_ci	.acin_path_sel	= false,
2678c2ecf20Sopenharmony_ci};
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic const struct axp_data axp22x_data = {
2708c2ecf20Sopenharmony_ci	.power_desc	= &axp22x_ac_power_desc,
2718c2ecf20Sopenharmony_ci	.irq_names	= axp20x_irq_names,
2728c2ecf20Sopenharmony_ci	.num_irq_names	= ARRAY_SIZE(axp20x_irq_names),
2738c2ecf20Sopenharmony_ci	.acin_adc	= false,
2748c2ecf20Sopenharmony_ci	.acin_path_sel	= false,
2758c2ecf20Sopenharmony_ci};
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic const struct axp_data axp813_data = {
2788c2ecf20Sopenharmony_ci	.power_desc	= &axp813_ac_power_desc,
2798c2ecf20Sopenharmony_ci	.irq_names	= axp20x_irq_names,
2808c2ecf20Sopenharmony_ci	.num_irq_names	= ARRAY_SIZE(axp20x_irq_names),
2818c2ecf20Sopenharmony_ci	.acin_adc	= false,
2828c2ecf20Sopenharmony_ci	.acin_path_sel	= true,
2838c2ecf20Sopenharmony_ci};
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
2868c2ecf20Sopenharmony_cistatic int axp20x_ac_power_suspend(struct device *dev)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	struct axp20x_ac_power *power = dev_get_drvdata(dev);
2898c2ecf20Sopenharmony_ci	int i = 0;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	/*
2928c2ecf20Sopenharmony_ci	 * Allow wake via ACIN_PLUGIN only.
2938c2ecf20Sopenharmony_ci	 *
2948c2ecf20Sopenharmony_ci	 * As nested threaded IRQs are not automatically disabled during
2958c2ecf20Sopenharmony_ci	 * suspend, we must explicitly disable the remainder of the IRQs.
2968c2ecf20Sopenharmony_ci	 */
2978c2ecf20Sopenharmony_ci	if (device_may_wakeup(&power->supply->dev))
2988c2ecf20Sopenharmony_ci		enable_irq_wake(power->irqs[i++]);
2998c2ecf20Sopenharmony_ci	while (i < power->num_irqs)
3008c2ecf20Sopenharmony_ci		disable_irq(power->irqs[i++]);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	return 0;
3038c2ecf20Sopenharmony_ci}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cistatic int axp20x_ac_power_resume(struct device *dev)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	struct axp20x_ac_power *power = dev_get_drvdata(dev);
3088c2ecf20Sopenharmony_ci	int i = 0;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	if (device_may_wakeup(&power->supply->dev))
3118c2ecf20Sopenharmony_ci		disable_irq_wake(power->irqs[i++]);
3128c2ecf20Sopenharmony_ci	while (i < power->num_irqs)
3138c2ecf20Sopenharmony_ci		enable_irq(power->irqs[i++]);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	return 0;
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci#endif
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(axp20x_ac_power_pm_ops, axp20x_ac_power_suspend,
3208c2ecf20Sopenharmony_ci						 axp20x_ac_power_resume);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic int axp20x_ac_power_probe(struct platform_device *pdev)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
3258c2ecf20Sopenharmony_ci	struct power_supply_config psy_cfg = {};
3268c2ecf20Sopenharmony_ci	struct axp20x_ac_power *power;
3278c2ecf20Sopenharmony_ci	const struct axp_data *axp_data;
3288c2ecf20Sopenharmony_ci	int i, irq, ret;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	if (!of_device_is_available(pdev->dev.of_node))
3318c2ecf20Sopenharmony_ci		return -ENODEV;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	if (!axp20x) {
3348c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Parent drvdata not set\n");
3358c2ecf20Sopenharmony_ci		return -EINVAL;
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	axp_data = of_device_get_match_data(&pdev->dev);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	power = devm_kzalloc(&pdev->dev,
3418c2ecf20Sopenharmony_ci			     struct_size(power, irqs, axp_data->num_irq_names),
3428c2ecf20Sopenharmony_ci			     GFP_KERNEL);
3438c2ecf20Sopenharmony_ci	if (!power)
3448c2ecf20Sopenharmony_ci		return -ENOMEM;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	if (axp_data->acin_adc) {
3478c2ecf20Sopenharmony_ci		power->acin_v = devm_iio_channel_get(&pdev->dev, "acin_v");
3488c2ecf20Sopenharmony_ci		if (IS_ERR(power->acin_v)) {
3498c2ecf20Sopenharmony_ci			if (PTR_ERR(power->acin_v) == -ENODEV)
3508c2ecf20Sopenharmony_ci				return -EPROBE_DEFER;
3518c2ecf20Sopenharmony_ci			return PTR_ERR(power->acin_v);
3528c2ecf20Sopenharmony_ci		}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci		power->acin_i = devm_iio_channel_get(&pdev->dev, "acin_i");
3558c2ecf20Sopenharmony_ci		if (IS_ERR(power->acin_i)) {
3568c2ecf20Sopenharmony_ci			if (PTR_ERR(power->acin_i) == -ENODEV)
3578c2ecf20Sopenharmony_ci				return -EPROBE_DEFER;
3588c2ecf20Sopenharmony_ci			return PTR_ERR(power->acin_i);
3598c2ecf20Sopenharmony_ci		}
3608c2ecf20Sopenharmony_ci	}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	power->regmap = dev_get_regmap(pdev->dev.parent, NULL);
3638c2ecf20Sopenharmony_ci	power->has_acin_path_sel = axp_data->acin_path_sel;
3648c2ecf20Sopenharmony_ci	power->num_irqs = axp_data->num_irq_names;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, power);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	psy_cfg.of_node = pdev->dev.of_node;
3698c2ecf20Sopenharmony_ci	psy_cfg.drv_data = power;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	power->supply = devm_power_supply_register(&pdev->dev,
3728c2ecf20Sopenharmony_ci						   axp_data->power_desc,
3738c2ecf20Sopenharmony_ci						   &psy_cfg);
3748c2ecf20Sopenharmony_ci	if (IS_ERR(power->supply))
3758c2ecf20Sopenharmony_ci		return PTR_ERR(power->supply);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	/* Request irqs after registering, as irqs may trigger immediately */
3788c2ecf20Sopenharmony_ci	for (i = 0; i < axp_data->num_irq_names; i++) {
3798c2ecf20Sopenharmony_ci		irq = platform_get_irq_byname(pdev, axp_data->irq_names[i]);
3808c2ecf20Sopenharmony_ci		if (irq < 0) {
3818c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "No IRQ for %s: %d\n",
3828c2ecf20Sopenharmony_ci				axp_data->irq_names[i], irq);
3838c2ecf20Sopenharmony_ci			return irq;
3848c2ecf20Sopenharmony_ci		}
3858c2ecf20Sopenharmony_ci		power->irqs[i] = regmap_irq_get_virq(axp20x->regmap_irqc, irq);
3868c2ecf20Sopenharmony_ci		ret = devm_request_any_context_irq(&pdev->dev, power->irqs[i],
3878c2ecf20Sopenharmony_ci						   axp20x_ac_power_irq, 0,
3888c2ecf20Sopenharmony_ci						   DRVNAME, power);
3898c2ecf20Sopenharmony_ci		if (ret < 0) {
3908c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "Error requesting %s IRQ: %d\n",
3918c2ecf20Sopenharmony_ci				axp_data->irq_names[i], ret);
3928c2ecf20Sopenharmony_ci			return ret;
3938c2ecf20Sopenharmony_ci		}
3948c2ecf20Sopenharmony_ci	}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	return 0;
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_cistatic const struct of_device_id axp20x_ac_power_match[] = {
4008c2ecf20Sopenharmony_ci	{
4018c2ecf20Sopenharmony_ci		.compatible = "x-powers,axp202-ac-power-supply",
4028c2ecf20Sopenharmony_ci		.data = &axp20x_data,
4038c2ecf20Sopenharmony_ci	}, {
4048c2ecf20Sopenharmony_ci		.compatible = "x-powers,axp221-ac-power-supply",
4058c2ecf20Sopenharmony_ci		.data = &axp22x_data,
4068c2ecf20Sopenharmony_ci	}, {
4078c2ecf20Sopenharmony_ci		.compatible = "x-powers,axp813-ac-power-supply",
4088c2ecf20Sopenharmony_ci		.data = &axp813_data,
4098c2ecf20Sopenharmony_ci	}, { /* sentinel */ }
4108c2ecf20Sopenharmony_ci};
4118c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, axp20x_ac_power_match);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic struct platform_driver axp20x_ac_power_driver = {
4148c2ecf20Sopenharmony_ci	.probe = axp20x_ac_power_probe,
4158c2ecf20Sopenharmony_ci	.driver = {
4168c2ecf20Sopenharmony_ci		.name		= DRVNAME,
4178c2ecf20Sopenharmony_ci		.of_match_table	= axp20x_ac_power_match,
4188c2ecf20Sopenharmony_ci		.pm		= &axp20x_ac_power_pm_ops,
4198c2ecf20Sopenharmony_ci	},
4208c2ecf20Sopenharmony_ci};
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cimodule_platform_driver(axp20x_ac_power_driver);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ciMODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>");
4258c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AXP20X and AXP22X PMICs' AC power supply driver");
4268c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
427