18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * PMU driver for Wolfson Microelectronics wm831x PMICs
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2009 Wolfson Microelectronics PLC.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/err.h>
108c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
118c2ecf20Sopenharmony_ci#include <linux/power_supply.h>
128c2ecf20Sopenharmony_ci#include <linux/slab.h>
138c2ecf20Sopenharmony_ci#include <linux/usb/phy.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/mfd/wm831x/core.h>
168c2ecf20Sopenharmony_ci#include <linux/mfd/wm831x/auxadc.h>
178c2ecf20Sopenharmony_ci#include <linux/mfd/wm831x/pmu.h>
188c2ecf20Sopenharmony_ci#include <linux/mfd/wm831x/pdata.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistruct wm831x_power {
218c2ecf20Sopenharmony_ci	struct wm831x *wm831x;
228c2ecf20Sopenharmony_ci	struct power_supply *wall;
238c2ecf20Sopenharmony_ci	struct power_supply *usb;
248c2ecf20Sopenharmony_ci	struct power_supply *battery;
258c2ecf20Sopenharmony_ci	struct power_supply_desc wall_desc;
268c2ecf20Sopenharmony_ci	struct power_supply_desc usb_desc;
278c2ecf20Sopenharmony_ci	struct power_supply_desc battery_desc;
288c2ecf20Sopenharmony_ci	char wall_name[20];
298c2ecf20Sopenharmony_ci	char usb_name[20];
308c2ecf20Sopenharmony_ci	char battery_name[20];
318c2ecf20Sopenharmony_ci	bool have_battery;
328c2ecf20Sopenharmony_ci	struct usb_phy *usb_phy;
338c2ecf20Sopenharmony_ci	struct notifier_block usb_notify;
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic int wm831x_power_check_online(struct wm831x *wm831x, int supply,
378c2ecf20Sopenharmony_ci				     union power_supply_propval *val)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	int ret;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	ret = wm831x_reg_read(wm831x, WM831X_SYSTEM_STATUS);
428c2ecf20Sopenharmony_ci	if (ret < 0)
438c2ecf20Sopenharmony_ci		return ret;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	if (ret & supply)
468c2ecf20Sopenharmony_ci		val->intval = 1;
478c2ecf20Sopenharmony_ci	else
488c2ecf20Sopenharmony_ci		val->intval = 0;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	return 0;
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic int wm831x_power_read_voltage(struct wm831x *wm831x,
548c2ecf20Sopenharmony_ci				     enum wm831x_auxadc src,
558c2ecf20Sopenharmony_ci				     union power_supply_propval *val)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	int ret;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	ret = wm831x_auxadc_read_uv(wm831x, src);
608c2ecf20Sopenharmony_ci	if (ret >= 0)
618c2ecf20Sopenharmony_ci		val->intval = ret;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	return ret;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/*********************************************************************
678c2ecf20Sopenharmony_ci *		WALL Power
688c2ecf20Sopenharmony_ci *********************************************************************/
698c2ecf20Sopenharmony_cistatic int wm831x_wall_get_prop(struct power_supply *psy,
708c2ecf20Sopenharmony_ci				enum power_supply_property psp,
718c2ecf20Sopenharmony_ci				union power_supply_propval *val)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev.parent);
748c2ecf20Sopenharmony_ci	struct wm831x *wm831x = wm831x_power->wm831x;
758c2ecf20Sopenharmony_ci	int ret = 0;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	switch (psp) {
788c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
798c2ecf20Sopenharmony_ci		ret = wm831x_power_check_online(wm831x, WM831X_PWR_WALL, val);
808c2ecf20Sopenharmony_ci		break;
818c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
828c2ecf20Sopenharmony_ci		ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_WALL, val);
838c2ecf20Sopenharmony_ci		break;
848c2ecf20Sopenharmony_ci	default:
858c2ecf20Sopenharmony_ci		ret = -EINVAL;
868c2ecf20Sopenharmony_ci		break;
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	return ret;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic enum power_supply_property wm831x_wall_props[] = {
938c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
948c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_NOW,
958c2ecf20Sopenharmony_ci};
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci/*********************************************************************
988c2ecf20Sopenharmony_ci *		USB Power
998c2ecf20Sopenharmony_ci *********************************************************************/
1008c2ecf20Sopenharmony_cistatic int wm831x_usb_get_prop(struct power_supply *psy,
1018c2ecf20Sopenharmony_ci			       enum power_supply_property psp,
1028c2ecf20Sopenharmony_ci			       union power_supply_propval *val)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev.parent);
1058c2ecf20Sopenharmony_ci	struct wm831x *wm831x = wm831x_power->wm831x;
1068c2ecf20Sopenharmony_ci	int ret = 0;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	switch (psp) {
1098c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
1108c2ecf20Sopenharmony_ci		ret = wm831x_power_check_online(wm831x, WM831X_PWR_USB, val);
1118c2ecf20Sopenharmony_ci		break;
1128c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
1138c2ecf20Sopenharmony_ci		ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_USB, val);
1148c2ecf20Sopenharmony_ci		break;
1158c2ecf20Sopenharmony_ci	default:
1168c2ecf20Sopenharmony_ci		ret = -EINVAL;
1178c2ecf20Sopenharmony_ci		break;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	return ret;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic enum power_supply_property wm831x_usb_props[] = {
1248c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
1258c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_NOW,
1268c2ecf20Sopenharmony_ci};
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci/* In milliamps */
1298c2ecf20Sopenharmony_cistatic const unsigned int wm831x_usb_limits[] = {
1308c2ecf20Sopenharmony_ci	0,
1318c2ecf20Sopenharmony_ci	2,
1328c2ecf20Sopenharmony_ci	100,
1338c2ecf20Sopenharmony_ci	500,
1348c2ecf20Sopenharmony_ci	900,
1358c2ecf20Sopenharmony_ci	1500,
1368c2ecf20Sopenharmony_ci	1800,
1378c2ecf20Sopenharmony_ci	550,
1388c2ecf20Sopenharmony_ci};
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic int wm831x_usb_limit_change(struct notifier_block *nb,
1418c2ecf20Sopenharmony_ci				   unsigned long limit, void *data)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	struct wm831x_power *wm831x_power = container_of(nb,
1448c2ecf20Sopenharmony_ci							 struct wm831x_power,
1458c2ecf20Sopenharmony_ci							 usb_notify);
1468c2ecf20Sopenharmony_ci	unsigned int i, best;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	/* Find the highest supported limit */
1498c2ecf20Sopenharmony_ci	best = 0;
1508c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(wm831x_usb_limits); i++) {
1518c2ecf20Sopenharmony_ci		if (limit >= wm831x_usb_limits[i] &&
1528c2ecf20Sopenharmony_ci		    wm831x_usb_limits[best] < wm831x_usb_limits[i])
1538c2ecf20Sopenharmony_ci			best = i;
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	dev_dbg(wm831x_power->wm831x->dev,
1578c2ecf20Sopenharmony_ci		"Limiting USB current to %umA", wm831x_usb_limits[best]);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	wm831x_set_bits(wm831x_power->wm831x, WM831X_POWER_STATE,
1608c2ecf20Sopenharmony_ci		        WM831X_USB_ILIM_MASK, best);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	return 0;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci/*********************************************************************
1668c2ecf20Sopenharmony_ci *		Battery properties
1678c2ecf20Sopenharmony_ci *********************************************************************/
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistruct chg_map {
1708c2ecf20Sopenharmony_ci	int val;
1718c2ecf20Sopenharmony_ci	int reg_val;
1728c2ecf20Sopenharmony_ci};
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic struct chg_map trickle_ilims[] = {
1758c2ecf20Sopenharmony_ci	{  50, 0 << WM831X_CHG_TRKL_ILIM_SHIFT },
1768c2ecf20Sopenharmony_ci	{ 100, 1 << WM831X_CHG_TRKL_ILIM_SHIFT },
1778c2ecf20Sopenharmony_ci	{ 150, 2 << WM831X_CHG_TRKL_ILIM_SHIFT },
1788c2ecf20Sopenharmony_ci	{ 200, 3 << WM831X_CHG_TRKL_ILIM_SHIFT },
1798c2ecf20Sopenharmony_ci};
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic struct chg_map vsels[] = {
1828c2ecf20Sopenharmony_ci	{ 4050, 0 << WM831X_CHG_VSEL_SHIFT },
1838c2ecf20Sopenharmony_ci	{ 4100, 1 << WM831X_CHG_VSEL_SHIFT },
1848c2ecf20Sopenharmony_ci	{ 4150, 2 << WM831X_CHG_VSEL_SHIFT },
1858c2ecf20Sopenharmony_ci	{ 4200, 3 << WM831X_CHG_VSEL_SHIFT },
1868c2ecf20Sopenharmony_ci};
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic struct chg_map fast_ilims[] = {
1898c2ecf20Sopenharmony_ci	{    0,  0 << WM831X_CHG_FAST_ILIM_SHIFT },
1908c2ecf20Sopenharmony_ci	{   50,  1 << WM831X_CHG_FAST_ILIM_SHIFT },
1918c2ecf20Sopenharmony_ci	{  100,  2 << WM831X_CHG_FAST_ILIM_SHIFT },
1928c2ecf20Sopenharmony_ci	{  150,  3 << WM831X_CHG_FAST_ILIM_SHIFT },
1938c2ecf20Sopenharmony_ci	{  200,  4 << WM831X_CHG_FAST_ILIM_SHIFT },
1948c2ecf20Sopenharmony_ci	{  250,  5 << WM831X_CHG_FAST_ILIM_SHIFT },
1958c2ecf20Sopenharmony_ci	{  300,  6 << WM831X_CHG_FAST_ILIM_SHIFT },
1968c2ecf20Sopenharmony_ci	{  350,  7 << WM831X_CHG_FAST_ILIM_SHIFT },
1978c2ecf20Sopenharmony_ci	{  400,  8 << WM831X_CHG_FAST_ILIM_SHIFT },
1988c2ecf20Sopenharmony_ci	{  450,  9 << WM831X_CHG_FAST_ILIM_SHIFT },
1998c2ecf20Sopenharmony_ci	{  500, 10 << WM831X_CHG_FAST_ILIM_SHIFT },
2008c2ecf20Sopenharmony_ci	{  600, 11 << WM831X_CHG_FAST_ILIM_SHIFT },
2018c2ecf20Sopenharmony_ci	{  700, 12 << WM831X_CHG_FAST_ILIM_SHIFT },
2028c2ecf20Sopenharmony_ci	{  800, 13 << WM831X_CHG_FAST_ILIM_SHIFT },
2038c2ecf20Sopenharmony_ci	{  900, 14 << WM831X_CHG_FAST_ILIM_SHIFT },
2048c2ecf20Sopenharmony_ci	{ 1000, 15 << WM831X_CHG_FAST_ILIM_SHIFT },
2058c2ecf20Sopenharmony_ci};
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic struct chg_map eoc_iterms[] = {
2088c2ecf20Sopenharmony_ci	{ 20, 0 << WM831X_CHG_ITERM_SHIFT },
2098c2ecf20Sopenharmony_ci	{ 30, 1 << WM831X_CHG_ITERM_SHIFT },
2108c2ecf20Sopenharmony_ci	{ 40, 2 << WM831X_CHG_ITERM_SHIFT },
2118c2ecf20Sopenharmony_ci	{ 50, 3 << WM831X_CHG_ITERM_SHIFT },
2128c2ecf20Sopenharmony_ci	{ 60, 4 << WM831X_CHG_ITERM_SHIFT },
2138c2ecf20Sopenharmony_ci	{ 70, 5 << WM831X_CHG_ITERM_SHIFT },
2148c2ecf20Sopenharmony_ci	{ 80, 6 << WM831X_CHG_ITERM_SHIFT },
2158c2ecf20Sopenharmony_ci	{ 90, 7 << WM831X_CHG_ITERM_SHIFT },
2168c2ecf20Sopenharmony_ci};
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic struct chg_map chg_times[] = {
2198c2ecf20Sopenharmony_ci	{  60,  0 << WM831X_CHG_TIME_SHIFT },
2208c2ecf20Sopenharmony_ci	{  90,  1 << WM831X_CHG_TIME_SHIFT },
2218c2ecf20Sopenharmony_ci	{ 120,  2 << WM831X_CHG_TIME_SHIFT },
2228c2ecf20Sopenharmony_ci	{ 150,  3 << WM831X_CHG_TIME_SHIFT },
2238c2ecf20Sopenharmony_ci	{ 180,  4 << WM831X_CHG_TIME_SHIFT },
2248c2ecf20Sopenharmony_ci	{ 210,  5 << WM831X_CHG_TIME_SHIFT },
2258c2ecf20Sopenharmony_ci	{ 240,  6 << WM831X_CHG_TIME_SHIFT },
2268c2ecf20Sopenharmony_ci	{ 270,  7 << WM831X_CHG_TIME_SHIFT },
2278c2ecf20Sopenharmony_ci	{ 300,  8 << WM831X_CHG_TIME_SHIFT },
2288c2ecf20Sopenharmony_ci	{ 330,  9 << WM831X_CHG_TIME_SHIFT },
2298c2ecf20Sopenharmony_ci	{ 360, 10 << WM831X_CHG_TIME_SHIFT },
2308c2ecf20Sopenharmony_ci	{ 390, 11 << WM831X_CHG_TIME_SHIFT },
2318c2ecf20Sopenharmony_ci	{ 420, 12 << WM831X_CHG_TIME_SHIFT },
2328c2ecf20Sopenharmony_ci	{ 450, 13 << WM831X_CHG_TIME_SHIFT },
2338c2ecf20Sopenharmony_ci	{ 480, 14 << WM831X_CHG_TIME_SHIFT },
2348c2ecf20Sopenharmony_ci	{ 510, 15 << WM831X_CHG_TIME_SHIFT },
2358c2ecf20Sopenharmony_ci};
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic void wm831x_battey_apply_config(struct wm831x *wm831x,
2388c2ecf20Sopenharmony_ci				       struct chg_map *map, int count, int val,
2398c2ecf20Sopenharmony_ci				       int *reg, const char *name,
2408c2ecf20Sopenharmony_ci				       const char *units)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	int i;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++)
2458c2ecf20Sopenharmony_ci		if (val == map[i].val)
2468c2ecf20Sopenharmony_ci			break;
2478c2ecf20Sopenharmony_ci	if (i == count) {
2488c2ecf20Sopenharmony_ci		dev_err(wm831x->dev, "Invalid %s %d%s\n",
2498c2ecf20Sopenharmony_ci			name, val, units);
2508c2ecf20Sopenharmony_ci	} else {
2518c2ecf20Sopenharmony_ci		*reg |= map[i].reg_val;
2528c2ecf20Sopenharmony_ci		dev_dbg(wm831x->dev, "Set %s of %d%s\n", name, val, units);
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic void wm831x_config_battery(struct wm831x *wm831x)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
2598c2ecf20Sopenharmony_ci	struct wm831x_battery_pdata *pdata;
2608c2ecf20Sopenharmony_ci	int ret, reg1, reg2;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	if (!wm831x_pdata || !wm831x_pdata->battery) {
2638c2ecf20Sopenharmony_ci		dev_warn(wm831x->dev,
2648c2ecf20Sopenharmony_ci			 "No battery charger configuration\n");
2658c2ecf20Sopenharmony_ci		return;
2668c2ecf20Sopenharmony_ci	}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	pdata = wm831x_pdata->battery;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	reg1 = 0;
2718c2ecf20Sopenharmony_ci	reg2 = 0;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	if (!pdata->enable) {
2748c2ecf20Sopenharmony_ci		dev_info(wm831x->dev, "Battery charger disabled\n");
2758c2ecf20Sopenharmony_ci		return;
2768c2ecf20Sopenharmony_ci	}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	reg1 |= WM831X_CHG_ENA;
2798c2ecf20Sopenharmony_ci	if (pdata->off_mask)
2808c2ecf20Sopenharmony_ci		reg2 |= WM831X_CHG_OFF_MSK;
2818c2ecf20Sopenharmony_ci	if (pdata->fast_enable)
2828c2ecf20Sopenharmony_ci		reg1 |= WM831X_CHG_FAST;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	wm831x_battey_apply_config(wm831x, trickle_ilims,
2858c2ecf20Sopenharmony_ci				   ARRAY_SIZE(trickle_ilims),
2868c2ecf20Sopenharmony_ci				   pdata->trickle_ilim, &reg2,
2878c2ecf20Sopenharmony_ci				   "trickle charge current limit", "mA");
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	wm831x_battey_apply_config(wm831x, vsels, ARRAY_SIZE(vsels),
2908c2ecf20Sopenharmony_ci				   pdata->vsel, &reg2,
2918c2ecf20Sopenharmony_ci				   "target voltage", "mV");
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	wm831x_battey_apply_config(wm831x, fast_ilims, ARRAY_SIZE(fast_ilims),
2948c2ecf20Sopenharmony_ci				   pdata->fast_ilim, &reg2,
2958c2ecf20Sopenharmony_ci				   "fast charge current limit", "mA");
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	wm831x_battey_apply_config(wm831x, eoc_iterms, ARRAY_SIZE(eoc_iterms),
2988c2ecf20Sopenharmony_ci				   pdata->eoc_iterm, &reg1,
2998c2ecf20Sopenharmony_ci				   "end of charge current threshold", "mA");
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	wm831x_battey_apply_config(wm831x, chg_times, ARRAY_SIZE(chg_times),
3028c2ecf20Sopenharmony_ci				   pdata->timeout, &reg2,
3038c2ecf20Sopenharmony_ci				   "charger timeout", "min");
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	ret = wm831x_reg_unlock(wm831x);
3068c2ecf20Sopenharmony_ci	if (ret != 0) {
3078c2ecf20Sopenharmony_ci		dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret);
3088c2ecf20Sopenharmony_ci		return;
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_1,
3128c2ecf20Sopenharmony_ci			      WM831X_CHG_ENA_MASK |
3138c2ecf20Sopenharmony_ci			      WM831X_CHG_FAST_MASK |
3148c2ecf20Sopenharmony_ci			      WM831X_CHG_ITERM_MASK,
3158c2ecf20Sopenharmony_ci			      reg1);
3168c2ecf20Sopenharmony_ci	if (ret != 0)
3178c2ecf20Sopenharmony_ci		dev_err(wm831x->dev, "Failed to set charger control 1: %d\n",
3188c2ecf20Sopenharmony_ci			ret);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_2,
3218c2ecf20Sopenharmony_ci			      WM831X_CHG_OFF_MSK |
3228c2ecf20Sopenharmony_ci			      WM831X_CHG_TIME_MASK |
3238c2ecf20Sopenharmony_ci			      WM831X_CHG_FAST_ILIM_MASK |
3248c2ecf20Sopenharmony_ci			      WM831X_CHG_TRKL_ILIM_MASK |
3258c2ecf20Sopenharmony_ci			      WM831X_CHG_VSEL_MASK,
3268c2ecf20Sopenharmony_ci			      reg2);
3278c2ecf20Sopenharmony_ci	if (ret != 0)
3288c2ecf20Sopenharmony_ci		dev_err(wm831x->dev, "Failed to set charger control 2: %d\n",
3298c2ecf20Sopenharmony_ci			ret);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	wm831x_reg_lock(wm831x);
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_cistatic int wm831x_bat_check_status(struct wm831x *wm831x, int *status)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	int ret;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	ret = wm831x_reg_read(wm831x, WM831X_SYSTEM_STATUS);
3398c2ecf20Sopenharmony_ci	if (ret < 0)
3408c2ecf20Sopenharmony_ci		return ret;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	if (ret & WM831X_PWR_SRC_BATT) {
3438c2ecf20Sopenharmony_ci		*status = POWER_SUPPLY_STATUS_DISCHARGING;
3448c2ecf20Sopenharmony_ci		return 0;
3458c2ecf20Sopenharmony_ci	}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
3488c2ecf20Sopenharmony_ci	if (ret < 0)
3498c2ecf20Sopenharmony_ci		return ret;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	switch (ret & WM831X_CHG_STATE_MASK) {
3528c2ecf20Sopenharmony_ci	case WM831X_CHG_STATE_OFF:
3538c2ecf20Sopenharmony_ci		*status = POWER_SUPPLY_STATUS_NOT_CHARGING;
3548c2ecf20Sopenharmony_ci		break;
3558c2ecf20Sopenharmony_ci	case WM831X_CHG_STATE_TRICKLE:
3568c2ecf20Sopenharmony_ci	case WM831X_CHG_STATE_FAST:
3578c2ecf20Sopenharmony_ci		*status = POWER_SUPPLY_STATUS_CHARGING;
3588c2ecf20Sopenharmony_ci		break;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	default:
3618c2ecf20Sopenharmony_ci		*status = POWER_SUPPLY_STATUS_UNKNOWN;
3628c2ecf20Sopenharmony_ci		break;
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	return 0;
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic int wm831x_bat_check_type(struct wm831x *wm831x, int *type)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	int ret;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
3738c2ecf20Sopenharmony_ci	if (ret < 0)
3748c2ecf20Sopenharmony_ci		return ret;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	switch (ret & WM831X_CHG_STATE_MASK) {
3778c2ecf20Sopenharmony_ci	case WM831X_CHG_STATE_TRICKLE:
3788c2ecf20Sopenharmony_ci	case WM831X_CHG_STATE_TRICKLE_OT:
3798c2ecf20Sopenharmony_ci		*type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
3808c2ecf20Sopenharmony_ci		break;
3818c2ecf20Sopenharmony_ci	case WM831X_CHG_STATE_FAST:
3828c2ecf20Sopenharmony_ci	case WM831X_CHG_STATE_FAST_OT:
3838c2ecf20Sopenharmony_ci		*type = POWER_SUPPLY_CHARGE_TYPE_FAST;
3848c2ecf20Sopenharmony_ci		break;
3858c2ecf20Sopenharmony_ci	default:
3868c2ecf20Sopenharmony_ci		*type = POWER_SUPPLY_CHARGE_TYPE_NONE;
3878c2ecf20Sopenharmony_ci		break;
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	return 0;
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic int wm831x_bat_check_health(struct wm831x *wm831x, int *health)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	int ret;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
3988c2ecf20Sopenharmony_ci	if (ret < 0)
3998c2ecf20Sopenharmony_ci		return ret;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	if (ret & WM831X_BATT_HOT_STS) {
4028c2ecf20Sopenharmony_ci		*health = POWER_SUPPLY_HEALTH_OVERHEAT;
4038c2ecf20Sopenharmony_ci		return 0;
4048c2ecf20Sopenharmony_ci	}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	if (ret & WM831X_BATT_COLD_STS) {
4078c2ecf20Sopenharmony_ci		*health = POWER_SUPPLY_HEALTH_COLD;
4088c2ecf20Sopenharmony_ci		return 0;
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	if (ret & WM831X_BATT_OV_STS) {
4128c2ecf20Sopenharmony_ci		*health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
4138c2ecf20Sopenharmony_ci		return 0;
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	switch (ret & WM831X_CHG_STATE_MASK) {
4178c2ecf20Sopenharmony_ci	case WM831X_CHG_STATE_TRICKLE_OT:
4188c2ecf20Sopenharmony_ci	case WM831X_CHG_STATE_FAST_OT:
4198c2ecf20Sopenharmony_ci		*health = POWER_SUPPLY_HEALTH_OVERHEAT;
4208c2ecf20Sopenharmony_ci		break;
4218c2ecf20Sopenharmony_ci	case WM831X_CHG_STATE_DEFECTIVE:
4228c2ecf20Sopenharmony_ci		*health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
4238c2ecf20Sopenharmony_ci		break;
4248c2ecf20Sopenharmony_ci	default:
4258c2ecf20Sopenharmony_ci		*health = POWER_SUPPLY_HEALTH_GOOD;
4268c2ecf20Sopenharmony_ci		break;
4278c2ecf20Sopenharmony_ci	}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	return 0;
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_cistatic int wm831x_bat_get_prop(struct power_supply *psy,
4338c2ecf20Sopenharmony_ci			       enum power_supply_property psp,
4348c2ecf20Sopenharmony_ci			       union power_supply_propval *val)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev.parent);
4378c2ecf20Sopenharmony_ci	struct wm831x *wm831x = wm831x_power->wm831x;
4388c2ecf20Sopenharmony_ci	int ret = 0;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	switch (psp) {
4418c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_STATUS:
4428c2ecf20Sopenharmony_ci		ret = wm831x_bat_check_status(wm831x, &val->intval);
4438c2ecf20Sopenharmony_ci		break;
4448c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
4458c2ecf20Sopenharmony_ci		ret = wm831x_power_check_online(wm831x, WM831X_PWR_SRC_BATT,
4468c2ecf20Sopenharmony_ci						val);
4478c2ecf20Sopenharmony_ci		break;
4488c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
4498c2ecf20Sopenharmony_ci		ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_BATT, val);
4508c2ecf20Sopenharmony_ci		break;
4518c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_HEALTH:
4528c2ecf20Sopenharmony_ci		ret = wm831x_bat_check_health(wm831x, &val->intval);
4538c2ecf20Sopenharmony_ci		break;
4548c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CHARGE_TYPE:
4558c2ecf20Sopenharmony_ci		ret = wm831x_bat_check_type(wm831x, &val->intval);
4568c2ecf20Sopenharmony_ci		break;
4578c2ecf20Sopenharmony_ci	default:
4588c2ecf20Sopenharmony_ci		ret = -EINVAL;
4598c2ecf20Sopenharmony_ci		break;
4608c2ecf20Sopenharmony_ci	}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	return ret;
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic enum power_supply_property wm831x_bat_props[] = {
4668c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_STATUS,
4678c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
4688c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_NOW,
4698c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_HEALTH,
4708c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CHARGE_TYPE,
4718c2ecf20Sopenharmony_ci};
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_cistatic const char *wm831x_bat_irqs[] = {
4748c2ecf20Sopenharmony_ci	"BATT HOT",
4758c2ecf20Sopenharmony_ci	"BATT COLD",
4768c2ecf20Sopenharmony_ci	"BATT FAIL",
4778c2ecf20Sopenharmony_ci	"OV",
4788c2ecf20Sopenharmony_ci	"END",
4798c2ecf20Sopenharmony_ci	"TO",
4808c2ecf20Sopenharmony_ci	"MODE",
4818c2ecf20Sopenharmony_ci	"START",
4828c2ecf20Sopenharmony_ci};
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cistatic irqreturn_t wm831x_bat_irq(int irq, void *data)
4858c2ecf20Sopenharmony_ci{
4868c2ecf20Sopenharmony_ci	struct wm831x_power *wm831x_power = data;
4878c2ecf20Sopenharmony_ci	struct wm831x *wm831x = wm831x_power->wm831x;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	dev_dbg(wm831x->dev, "Battery status changed: %d\n", irq);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	/* The battery charger is autonomous so we don't need to do
4928c2ecf20Sopenharmony_ci	 * anything except kick user space */
4938c2ecf20Sopenharmony_ci	if (wm831x_power->have_battery)
4948c2ecf20Sopenharmony_ci		power_supply_changed(wm831x_power->battery);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
4978c2ecf20Sopenharmony_ci}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci/*********************************************************************
5018c2ecf20Sopenharmony_ci *		Initialisation
5028c2ecf20Sopenharmony_ci *********************************************************************/
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_cistatic irqreturn_t wm831x_syslo_irq(int irq, void *data)
5058c2ecf20Sopenharmony_ci{
5068c2ecf20Sopenharmony_ci	struct wm831x_power *wm831x_power = data;
5078c2ecf20Sopenharmony_ci	struct wm831x *wm831x = wm831x_power->wm831x;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	/* Not much we can actually *do* but tell people for
5108c2ecf20Sopenharmony_ci	 * posterity, we're probably about to run out of power. */
5118c2ecf20Sopenharmony_ci	dev_crit(wm831x->dev, "SYSVDD under voltage\n");
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
5148c2ecf20Sopenharmony_ci}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_cistatic irqreturn_t wm831x_pwr_src_irq(int irq, void *data)
5178c2ecf20Sopenharmony_ci{
5188c2ecf20Sopenharmony_ci	struct wm831x_power *wm831x_power = data;
5198c2ecf20Sopenharmony_ci	struct wm831x *wm831x = wm831x_power->wm831x;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	dev_dbg(wm831x->dev, "Power source changed\n");
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	/* Just notify for everything - little harm in overnotifying. */
5248c2ecf20Sopenharmony_ci	if (wm831x_power->have_battery)
5258c2ecf20Sopenharmony_ci		power_supply_changed(wm831x_power->battery);
5268c2ecf20Sopenharmony_ci	power_supply_changed(wm831x_power->usb);
5278c2ecf20Sopenharmony_ci	power_supply_changed(wm831x_power->wall);
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_cistatic int wm831x_power_probe(struct platform_device *pdev)
5338c2ecf20Sopenharmony_ci{
5348c2ecf20Sopenharmony_ci	struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
5358c2ecf20Sopenharmony_ci	struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
5368c2ecf20Sopenharmony_ci	struct wm831x_power *power;
5378c2ecf20Sopenharmony_ci	int ret, irq, i;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	power = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_power),
5408c2ecf20Sopenharmony_ci			     GFP_KERNEL);
5418c2ecf20Sopenharmony_ci	if (power == NULL)
5428c2ecf20Sopenharmony_ci		return -ENOMEM;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	power->wm831x = wm831x;
5458c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, power);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	if (wm831x_pdata && wm831x_pdata->wm831x_num) {
5488c2ecf20Sopenharmony_ci		snprintf(power->wall_name, sizeof(power->wall_name),
5498c2ecf20Sopenharmony_ci			 "wm831x-wall.%d", wm831x_pdata->wm831x_num);
5508c2ecf20Sopenharmony_ci		snprintf(power->battery_name, sizeof(power->wall_name),
5518c2ecf20Sopenharmony_ci			 "wm831x-battery.%d", wm831x_pdata->wm831x_num);
5528c2ecf20Sopenharmony_ci		snprintf(power->usb_name, sizeof(power->wall_name),
5538c2ecf20Sopenharmony_ci			 "wm831x-usb.%d", wm831x_pdata->wm831x_num);
5548c2ecf20Sopenharmony_ci	} else {
5558c2ecf20Sopenharmony_ci		snprintf(power->wall_name, sizeof(power->wall_name),
5568c2ecf20Sopenharmony_ci			 "wm831x-wall");
5578c2ecf20Sopenharmony_ci		snprintf(power->battery_name, sizeof(power->wall_name),
5588c2ecf20Sopenharmony_ci			 "wm831x-battery");
5598c2ecf20Sopenharmony_ci		snprintf(power->usb_name, sizeof(power->wall_name),
5608c2ecf20Sopenharmony_ci			 "wm831x-usb");
5618c2ecf20Sopenharmony_ci	}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	/* We ignore configuration failures since we can still read back
5648c2ecf20Sopenharmony_ci	 * the status without enabling the charger.
5658c2ecf20Sopenharmony_ci	 */
5668c2ecf20Sopenharmony_ci	wm831x_config_battery(wm831x);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	power->wall_desc.name = power->wall_name;
5698c2ecf20Sopenharmony_ci	power->wall_desc.type = POWER_SUPPLY_TYPE_MAINS;
5708c2ecf20Sopenharmony_ci	power->wall_desc.properties = wm831x_wall_props;
5718c2ecf20Sopenharmony_ci	power->wall_desc.num_properties = ARRAY_SIZE(wm831x_wall_props);
5728c2ecf20Sopenharmony_ci	power->wall_desc.get_property = wm831x_wall_get_prop;
5738c2ecf20Sopenharmony_ci	power->wall = power_supply_register(&pdev->dev, &power->wall_desc,
5748c2ecf20Sopenharmony_ci					    NULL);
5758c2ecf20Sopenharmony_ci	if (IS_ERR(power->wall)) {
5768c2ecf20Sopenharmony_ci		ret = PTR_ERR(power->wall);
5778c2ecf20Sopenharmony_ci		goto err;
5788c2ecf20Sopenharmony_ci	}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	power->usb_desc.name = power->usb_name,
5818c2ecf20Sopenharmony_ci	power->usb_desc.type = POWER_SUPPLY_TYPE_USB;
5828c2ecf20Sopenharmony_ci	power->usb_desc.properties = wm831x_usb_props;
5838c2ecf20Sopenharmony_ci	power->usb_desc.num_properties = ARRAY_SIZE(wm831x_usb_props);
5848c2ecf20Sopenharmony_ci	power->usb_desc.get_property = wm831x_usb_get_prop;
5858c2ecf20Sopenharmony_ci	power->usb = power_supply_register(&pdev->dev, &power->usb_desc, NULL);
5868c2ecf20Sopenharmony_ci	if (IS_ERR(power->usb)) {
5878c2ecf20Sopenharmony_ci		ret = PTR_ERR(power->usb);
5888c2ecf20Sopenharmony_ci		goto err_wall;
5898c2ecf20Sopenharmony_ci	}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	ret = wm831x_reg_read(wm831x, WM831X_CHARGER_CONTROL_1);
5928c2ecf20Sopenharmony_ci	if (ret < 0)
5938c2ecf20Sopenharmony_ci		goto err_wall;
5948c2ecf20Sopenharmony_ci	power->have_battery = ret & WM831X_CHG_ENA;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	if (power->have_battery) {
5978c2ecf20Sopenharmony_ci		power->battery_desc.name = power->battery_name;
5988c2ecf20Sopenharmony_ci		power->battery_desc.properties = wm831x_bat_props;
5998c2ecf20Sopenharmony_ci		power->battery_desc.num_properties = ARRAY_SIZE(wm831x_bat_props);
6008c2ecf20Sopenharmony_ci		power->battery_desc.get_property = wm831x_bat_get_prop;
6018c2ecf20Sopenharmony_ci		power->battery_desc.use_for_apm = 1;
6028c2ecf20Sopenharmony_ci		power->battery = power_supply_register(&pdev->dev,
6038c2ecf20Sopenharmony_ci						       &power->battery_desc,
6048c2ecf20Sopenharmony_ci						       NULL);
6058c2ecf20Sopenharmony_ci		if (IS_ERR(power->battery)) {
6068c2ecf20Sopenharmony_ci			ret = PTR_ERR(power->battery);
6078c2ecf20Sopenharmony_ci			goto err_usb;
6088c2ecf20Sopenharmony_ci		}
6098c2ecf20Sopenharmony_ci	}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO"));
6128c2ecf20Sopenharmony_ci	ret = request_threaded_irq(irq, NULL, wm831x_syslo_irq,
6138c2ecf20Sopenharmony_ci				   IRQF_TRIGGER_RISING | IRQF_ONESHOT, "System power low",
6148c2ecf20Sopenharmony_ci				   power);
6158c2ecf20Sopenharmony_ci	if (ret != 0) {
6168c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to request SYSLO IRQ %d: %d\n",
6178c2ecf20Sopenharmony_ci			irq, ret);
6188c2ecf20Sopenharmony_ci		goto err_battery;
6198c2ecf20Sopenharmony_ci	}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC"));
6228c2ecf20Sopenharmony_ci	ret = request_threaded_irq(irq, NULL, wm831x_pwr_src_irq,
6238c2ecf20Sopenharmony_ci				   IRQF_TRIGGER_RISING | IRQF_ONESHOT, "Power source",
6248c2ecf20Sopenharmony_ci				   power);
6258c2ecf20Sopenharmony_ci	if (ret != 0) {
6268c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to request PWR SRC IRQ %d: %d\n",
6278c2ecf20Sopenharmony_ci			irq, ret);
6288c2ecf20Sopenharmony_ci		goto err_syslo;
6298c2ecf20Sopenharmony_ci	}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
6328c2ecf20Sopenharmony_ci		irq = wm831x_irq(wm831x,
6338c2ecf20Sopenharmony_ci				 platform_get_irq_byname(pdev,
6348c2ecf20Sopenharmony_ci							 wm831x_bat_irqs[i]));
6358c2ecf20Sopenharmony_ci		ret = request_threaded_irq(irq, NULL, wm831x_bat_irq,
6368c2ecf20Sopenharmony_ci					   IRQF_TRIGGER_RISING | IRQF_ONESHOT,
6378c2ecf20Sopenharmony_ci					   wm831x_bat_irqs[i],
6388c2ecf20Sopenharmony_ci					   power);
6398c2ecf20Sopenharmony_ci		if (ret != 0) {
6408c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
6418c2ecf20Sopenharmony_ci				"Failed to request %s IRQ %d: %d\n",
6428c2ecf20Sopenharmony_ci				wm831x_bat_irqs[i], irq, ret);
6438c2ecf20Sopenharmony_ci			goto err_bat_irq;
6448c2ecf20Sopenharmony_ci		}
6458c2ecf20Sopenharmony_ci	}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	power->usb_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "phys", 0);
6488c2ecf20Sopenharmony_ci	ret = PTR_ERR_OR_ZERO(power->usb_phy);
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	switch (ret) {
6518c2ecf20Sopenharmony_ci	case 0:
6528c2ecf20Sopenharmony_ci		power->usb_notify.notifier_call = wm831x_usb_limit_change;
6538c2ecf20Sopenharmony_ci		ret = usb_register_notifier(power->usb_phy, &power->usb_notify);
6548c2ecf20Sopenharmony_ci		if (ret) {
6558c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "Failed to register notifier: %d\n",
6568c2ecf20Sopenharmony_ci				ret);
6578c2ecf20Sopenharmony_ci			goto err_bat_irq;
6588c2ecf20Sopenharmony_ci		}
6598c2ecf20Sopenharmony_ci		break;
6608c2ecf20Sopenharmony_ci	case -EINVAL:
6618c2ecf20Sopenharmony_ci	case -ENODEV:
6628c2ecf20Sopenharmony_ci		/* ignore missing usb-phy, it's optional */
6638c2ecf20Sopenharmony_ci		power->usb_phy = NULL;
6648c2ecf20Sopenharmony_ci		ret = 0;
6658c2ecf20Sopenharmony_ci		break;
6668c2ecf20Sopenharmony_ci	default:
6678c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to find USB phy: %d\n", ret);
6688c2ecf20Sopenharmony_ci		fallthrough;
6698c2ecf20Sopenharmony_ci	case -EPROBE_DEFER:
6708c2ecf20Sopenharmony_ci		goto err_bat_irq;
6718c2ecf20Sopenharmony_ci		break;
6728c2ecf20Sopenharmony_ci	}
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	return ret;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_cierr_bat_irq:
6778c2ecf20Sopenharmony_ci	--i;
6788c2ecf20Sopenharmony_ci	for (; i >= 0; i--) {
6798c2ecf20Sopenharmony_ci		irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
6808c2ecf20Sopenharmony_ci		free_irq(irq, power);
6818c2ecf20Sopenharmony_ci	}
6828c2ecf20Sopenharmony_ci	irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC"));
6838c2ecf20Sopenharmony_ci	free_irq(irq, power);
6848c2ecf20Sopenharmony_cierr_syslo:
6858c2ecf20Sopenharmony_ci	irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO"));
6868c2ecf20Sopenharmony_ci	free_irq(irq, power);
6878c2ecf20Sopenharmony_cierr_battery:
6888c2ecf20Sopenharmony_ci	if (power->have_battery)
6898c2ecf20Sopenharmony_ci		power_supply_unregister(power->battery);
6908c2ecf20Sopenharmony_cierr_usb:
6918c2ecf20Sopenharmony_ci	power_supply_unregister(power->usb);
6928c2ecf20Sopenharmony_cierr_wall:
6938c2ecf20Sopenharmony_ci	power_supply_unregister(power->wall);
6948c2ecf20Sopenharmony_cierr:
6958c2ecf20Sopenharmony_ci	return ret;
6968c2ecf20Sopenharmony_ci}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_cistatic int wm831x_power_remove(struct platform_device *pdev)
6998c2ecf20Sopenharmony_ci{
7008c2ecf20Sopenharmony_ci	struct wm831x_power *wm831x_power = platform_get_drvdata(pdev);
7018c2ecf20Sopenharmony_ci	struct wm831x *wm831x = wm831x_power->wm831x;
7028c2ecf20Sopenharmony_ci	int irq, i;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	if (wm831x_power->usb_phy) {
7058c2ecf20Sopenharmony_ci		usb_unregister_notifier(wm831x_power->usb_phy,
7068c2ecf20Sopenharmony_ci					&wm831x_power->usb_notify);
7078c2ecf20Sopenharmony_ci	}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
7108c2ecf20Sopenharmony_ci		irq = wm831x_irq(wm831x,
7118c2ecf20Sopenharmony_ci				 platform_get_irq_byname(pdev,
7128c2ecf20Sopenharmony_ci							 wm831x_bat_irqs[i]));
7138c2ecf20Sopenharmony_ci		free_irq(irq, wm831x_power);
7148c2ecf20Sopenharmony_ci	}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC"));
7178c2ecf20Sopenharmony_ci	free_irq(irq, wm831x_power);
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO"));
7208c2ecf20Sopenharmony_ci	free_irq(irq, wm831x_power);
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	if (wm831x_power->have_battery)
7238c2ecf20Sopenharmony_ci		power_supply_unregister(wm831x_power->battery);
7248c2ecf20Sopenharmony_ci	power_supply_unregister(wm831x_power->wall);
7258c2ecf20Sopenharmony_ci	power_supply_unregister(wm831x_power->usb);
7268c2ecf20Sopenharmony_ci	return 0;
7278c2ecf20Sopenharmony_ci}
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_cistatic struct platform_driver wm831x_power_driver = {
7308c2ecf20Sopenharmony_ci	.probe = wm831x_power_probe,
7318c2ecf20Sopenharmony_ci	.remove = wm831x_power_remove,
7328c2ecf20Sopenharmony_ci	.driver = {
7338c2ecf20Sopenharmony_ci		.name = "wm831x-power",
7348c2ecf20Sopenharmony_ci	},
7358c2ecf20Sopenharmony_ci};
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_cimodule_platform_driver(wm831x_power_driver);
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Power supply driver for WM831x PMICs");
7408c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
7418c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
7428c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:wm831x-power");
743