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