cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

wm831x_power.c (19418B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * PMU driver for Wolfson Microelectronics wm831x PMICs
      4 *
      5 * Copyright 2009 Wolfson Microelectronics PLC.
      6 */
      7
      8#include <linux/module.h>
      9#include <linux/err.h>
     10#include <linux/platform_device.h>
     11#include <linux/power_supply.h>
     12#include <linux/slab.h>
     13#include <linux/usb/phy.h>
     14
     15#include <linux/mfd/wm831x/core.h>
     16#include <linux/mfd/wm831x/auxadc.h>
     17#include <linux/mfd/wm831x/pmu.h>
     18#include <linux/mfd/wm831x/pdata.h>
     19
     20struct wm831x_power {
     21	struct wm831x *wm831x;
     22	struct power_supply *wall;
     23	struct power_supply *usb;
     24	struct power_supply *battery;
     25	struct power_supply_desc wall_desc;
     26	struct power_supply_desc usb_desc;
     27	struct power_supply_desc battery_desc;
     28	char wall_name[20];
     29	char usb_name[20];
     30	char battery_name[20];
     31	bool have_battery;
     32	struct usb_phy *usb_phy;
     33	struct notifier_block usb_notify;
     34};
     35
     36static int wm831x_power_check_online(struct wm831x *wm831x, int supply,
     37				     union power_supply_propval *val)
     38{
     39	int ret;
     40
     41	ret = wm831x_reg_read(wm831x, WM831X_SYSTEM_STATUS);
     42	if (ret < 0)
     43		return ret;
     44
     45	if (ret & supply)
     46		val->intval = 1;
     47	else
     48		val->intval = 0;
     49
     50	return 0;
     51}
     52
     53static int wm831x_power_read_voltage(struct wm831x *wm831x,
     54				     enum wm831x_auxadc src,
     55				     union power_supply_propval *val)
     56{
     57	int ret;
     58
     59	ret = wm831x_auxadc_read_uv(wm831x, src);
     60	if (ret >= 0)
     61		val->intval = ret;
     62
     63	return ret;
     64}
     65
     66/*********************************************************************
     67 *		WALL Power
     68 *********************************************************************/
     69static int wm831x_wall_get_prop(struct power_supply *psy,
     70				enum power_supply_property psp,
     71				union power_supply_propval *val)
     72{
     73	struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev.parent);
     74	struct wm831x *wm831x = wm831x_power->wm831x;
     75	int ret = 0;
     76
     77	switch (psp) {
     78	case POWER_SUPPLY_PROP_ONLINE:
     79		ret = wm831x_power_check_online(wm831x, WM831X_PWR_WALL, val);
     80		break;
     81	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
     82		ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_WALL, val);
     83		break;
     84	default:
     85		ret = -EINVAL;
     86		break;
     87	}
     88
     89	return ret;
     90}
     91
     92static enum power_supply_property wm831x_wall_props[] = {
     93	POWER_SUPPLY_PROP_ONLINE,
     94	POWER_SUPPLY_PROP_VOLTAGE_NOW,
     95};
     96
     97/*********************************************************************
     98 *		USB Power
     99 *********************************************************************/
    100static int wm831x_usb_get_prop(struct power_supply *psy,
    101			       enum power_supply_property psp,
    102			       union power_supply_propval *val)
    103{
    104	struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev.parent);
    105	struct wm831x *wm831x = wm831x_power->wm831x;
    106	int ret = 0;
    107
    108	switch (psp) {
    109	case POWER_SUPPLY_PROP_ONLINE:
    110		ret = wm831x_power_check_online(wm831x, WM831X_PWR_USB, val);
    111		break;
    112	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
    113		ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_USB, val);
    114		break;
    115	default:
    116		ret = -EINVAL;
    117		break;
    118	}
    119
    120	return ret;
    121}
    122
    123static enum power_supply_property wm831x_usb_props[] = {
    124	POWER_SUPPLY_PROP_ONLINE,
    125	POWER_SUPPLY_PROP_VOLTAGE_NOW,
    126};
    127
    128/* In milliamps */
    129static const unsigned int wm831x_usb_limits[] = {
    130	0,
    131	2,
    132	100,
    133	500,
    134	900,
    135	1500,
    136	1800,
    137	550,
    138};
    139
    140static int wm831x_usb_limit_change(struct notifier_block *nb,
    141				   unsigned long limit, void *data)
    142{
    143	struct wm831x_power *wm831x_power = container_of(nb,
    144							 struct wm831x_power,
    145							 usb_notify);
    146	unsigned int i, best;
    147
    148	/* Find the highest supported limit */
    149	best = 0;
    150	for (i = 0; i < ARRAY_SIZE(wm831x_usb_limits); i++) {
    151		if (limit >= wm831x_usb_limits[i] &&
    152		    wm831x_usb_limits[best] < wm831x_usb_limits[i])
    153			best = i;
    154	}
    155
    156	dev_dbg(wm831x_power->wm831x->dev,
    157		"Limiting USB current to %umA", wm831x_usb_limits[best]);
    158
    159	wm831x_set_bits(wm831x_power->wm831x, WM831X_POWER_STATE,
    160		        WM831X_USB_ILIM_MASK, best);
    161
    162	return 0;
    163}
    164
    165/*********************************************************************
    166 *		Battery properties
    167 *********************************************************************/
    168
    169struct chg_map {
    170	int val;
    171	int reg_val;
    172};
    173
    174static struct chg_map trickle_ilims[] = {
    175	{  50, 0 << WM831X_CHG_TRKL_ILIM_SHIFT },
    176	{ 100, 1 << WM831X_CHG_TRKL_ILIM_SHIFT },
    177	{ 150, 2 << WM831X_CHG_TRKL_ILIM_SHIFT },
    178	{ 200, 3 << WM831X_CHG_TRKL_ILIM_SHIFT },
    179};
    180
    181static struct chg_map vsels[] = {
    182	{ 4050, 0 << WM831X_CHG_VSEL_SHIFT },
    183	{ 4100, 1 << WM831X_CHG_VSEL_SHIFT },
    184	{ 4150, 2 << WM831X_CHG_VSEL_SHIFT },
    185	{ 4200, 3 << WM831X_CHG_VSEL_SHIFT },
    186};
    187
    188static struct chg_map fast_ilims[] = {
    189	{    0,  0 << WM831X_CHG_FAST_ILIM_SHIFT },
    190	{   50,  1 << WM831X_CHG_FAST_ILIM_SHIFT },
    191	{  100,  2 << WM831X_CHG_FAST_ILIM_SHIFT },
    192	{  150,  3 << WM831X_CHG_FAST_ILIM_SHIFT },
    193	{  200,  4 << WM831X_CHG_FAST_ILIM_SHIFT },
    194	{  250,  5 << WM831X_CHG_FAST_ILIM_SHIFT },
    195	{  300,  6 << WM831X_CHG_FAST_ILIM_SHIFT },
    196	{  350,  7 << WM831X_CHG_FAST_ILIM_SHIFT },
    197	{  400,  8 << WM831X_CHG_FAST_ILIM_SHIFT },
    198	{  450,  9 << WM831X_CHG_FAST_ILIM_SHIFT },
    199	{  500, 10 << WM831X_CHG_FAST_ILIM_SHIFT },
    200	{  600, 11 << WM831X_CHG_FAST_ILIM_SHIFT },
    201	{  700, 12 << WM831X_CHG_FAST_ILIM_SHIFT },
    202	{  800, 13 << WM831X_CHG_FAST_ILIM_SHIFT },
    203	{  900, 14 << WM831X_CHG_FAST_ILIM_SHIFT },
    204	{ 1000, 15 << WM831X_CHG_FAST_ILIM_SHIFT },
    205};
    206
    207static struct chg_map eoc_iterms[] = {
    208	{ 20, 0 << WM831X_CHG_ITERM_SHIFT },
    209	{ 30, 1 << WM831X_CHG_ITERM_SHIFT },
    210	{ 40, 2 << WM831X_CHG_ITERM_SHIFT },
    211	{ 50, 3 << WM831X_CHG_ITERM_SHIFT },
    212	{ 60, 4 << WM831X_CHG_ITERM_SHIFT },
    213	{ 70, 5 << WM831X_CHG_ITERM_SHIFT },
    214	{ 80, 6 << WM831X_CHG_ITERM_SHIFT },
    215	{ 90, 7 << WM831X_CHG_ITERM_SHIFT },
    216};
    217
    218static struct chg_map chg_times[] = {
    219	{  60,  0 << WM831X_CHG_TIME_SHIFT },
    220	{  90,  1 << WM831X_CHG_TIME_SHIFT },
    221	{ 120,  2 << WM831X_CHG_TIME_SHIFT },
    222	{ 150,  3 << WM831X_CHG_TIME_SHIFT },
    223	{ 180,  4 << WM831X_CHG_TIME_SHIFT },
    224	{ 210,  5 << WM831X_CHG_TIME_SHIFT },
    225	{ 240,  6 << WM831X_CHG_TIME_SHIFT },
    226	{ 270,  7 << WM831X_CHG_TIME_SHIFT },
    227	{ 300,  8 << WM831X_CHG_TIME_SHIFT },
    228	{ 330,  9 << WM831X_CHG_TIME_SHIFT },
    229	{ 360, 10 << WM831X_CHG_TIME_SHIFT },
    230	{ 390, 11 << WM831X_CHG_TIME_SHIFT },
    231	{ 420, 12 << WM831X_CHG_TIME_SHIFT },
    232	{ 450, 13 << WM831X_CHG_TIME_SHIFT },
    233	{ 480, 14 << WM831X_CHG_TIME_SHIFT },
    234	{ 510, 15 << WM831X_CHG_TIME_SHIFT },
    235};
    236
    237static void wm831x_battery_apply_config(struct wm831x *wm831x,
    238				       struct chg_map *map, int count, int val,
    239				       int *reg, const char *name,
    240				       const char *units)
    241{
    242	int i;
    243
    244	for (i = 0; i < count; i++)
    245		if (val == map[i].val)
    246			break;
    247	if (i == count) {
    248		dev_err(wm831x->dev, "Invalid %s %d%s\n",
    249			name, val, units);
    250	} else {
    251		*reg |= map[i].reg_val;
    252		dev_dbg(wm831x->dev, "Set %s of %d%s\n", name, val, units);
    253	}
    254}
    255
    256static void wm831x_config_battery(struct wm831x *wm831x)
    257{
    258	struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
    259	struct wm831x_battery_pdata *pdata;
    260	int ret, reg1, reg2;
    261
    262	if (!wm831x_pdata || !wm831x_pdata->battery) {
    263		dev_warn(wm831x->dev,
    264			 "No battery charger configuration\n");
    265		return;
    266	}
    267
    268	pdata = wm831x_pdata->battery;
    269
    270	reg1 = 0;
    271	reg2 = 0;
    272
    273	if (!pdata->enable) {
    274		dev_info(wm831x->dev, "Battery charger disabled\n");
    275		return;
    276	}
    277
    278	reg1 |= WM831X_CHG_ENA;
    279	if (pdata->off_mask)
    280		reg2 |= WM831X_CHG_OFF_MSK;
    281	if (pdata->fast_enable)
    282		reg1 |= WM831X_CHG_FAST;
    283
    284	wm831x_battery_apply_config(wm831x, trickle_ilims,
    285				   ARRAY_SIZE(trickle_ilims),
    286				   pdata->trickle_ilim, &reg2,
    287				   "trickle charge current limit", "mA");
    288
    289	wm831x_battery_apply_config(wm831x, vsels, ARRAY_SIZE(vsels),
    290				   pdata->vsel, &reg2,
    291				   "target voltage", "mV");
    292
    293	wm831x_battery_apply_config(wm831x, fast_ilims, ARRAY_SIZE(fast_ilims),
    294				   pdata->fast_ilim, &reg2,
    295				   "fast charge current limit", "mA");
    296
    297	wm831x_battery_apply_config(wm831x, eoc_iterms, ARRAY_SIZE(eoc_iterms),
    298				   pdata->eoc_iterm, &reg1,
    299				   "end of charge current threshold", "mA");
    300
    301	wm831x_battery_apply_config(wm831x, chg_times, ARRAY_SIZE(chg_times),
    302				   pdata->timeout, &reg2,
    303				   "charger timeout", "min");
    304
    305	ret = wm831x_reg_unlock(wm831x);
    306	if (ret != 0) {
    307		dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret);
    308		return;
    309	}
    310
    311	ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_1,
    312			      WM831X_CHG_ENA_MASK |
    313			      WM831X_CHG_FAST_MASK |
    314			      WM831X_CHG_ITERM_MASK,
    315			      reg1);
    316	if (ret != 0)
    317		dev_err(wm831x->dev, "Failed to set charger control 1: %d\n",
    318			ret);
    319
    320	ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_2,
    321			      WM831X_CHG_OFF_MSK |
    322			      WM831X_CHG_TIME_MASK |
    323			      WM831X_CHG_FAST_ILIM_MASK |
    324			      WM831X_CHG_TRKL_ILIM_MASK |
    325			      WM831X_CHG_VSEL_MASK,
    326			      reg2);
    327	if (ret != 0)
    328		dev_err(wm831x->dev, "Failed to set charger control 2: %d\n",
    329			ret);
    330
    331	wm831x_reg_lock(wm831x);
    332}
    333
    334static int wm831x_bat_check_status(struct wm831x *wm831x, int *status)
    335{
    336	int ret;
    337
    338	ret = wm831x_reg_read(wm831x, WM831X_SYSTEM_STATUS);
    339	if (ret < 0)
    340		return ret;
    341
    342	if (ret & WM831X_PWR_SRC_BATT) {
    343		*status = POWER_SUPPLY_STATUS_DISCHARGING;
    344		return 0;
    345	}
    346
    347	ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
    348	if (ret < 0)
    349		return ret;
    350
    351	switch (ret & WM831X_CHG_STATE_MASK) {
    352	case WM831X_CHG_STATE_OFF:
    353		*status = POWER_SUPPLY_STATUS_NOT_CHARGING;
    354		break;
    355	case WM831X_CHG_STATE_TRICKLE:
    356	case WM831X_CHG_STATE_FAST:
    357		*status = POWER_SUPPLY_STATUS_CHARGING;
    358		break;
    359
    360	default:
    361		*status = POWER_SUPPLY_STATUS_UNKNOWN;
    362		break;
    363	}
    364
    365	return 0;
    366}
    367
    368static int wm831x_bat_check_type(struct wm831x *wm831x, int *type)
    369{
    370	int ret;
    371
    372	ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
    373	if (ret < 0)
    374		return ret;
    375
    376	switch (ret & WM831X_CHG_STATE_MASK) {
    377	case WM831X_CHG_STATE_TRICKLE:
    378	case WM831X_CHG_STATE_TRICKLE_OT:
    379		*type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
    380		break;
    381	case WM831X_CHG_STATE_FAST:
    382	case WM831X_CHG_STATE_FAST_OT:
    383		*type = POWER_SUPPLY_CHARGE_TYPE_FAST;
    384		break;
    385	default:
    386		*type = POWER_SUPPLY_CHARGE_TYPE_NONE;
    387		break;
    388	}
    389
    390	return 0;
    391}
    392
    393static int wm831x_bat_check_health(struct wm831x *wm831x, int *health)
    394{
    395	int ret;
    396
    397	ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
    398	if (ret < 0)
    399		return ret;
    400
    401	if (ret & WM831X_BATT_HOT_STS) {
    402		*health = POWER_SUPPLY_HEALTH_OVERHEAT;
    403		return 0;
    404	}
    405
    406	if (ret & WM831X_BATT_COLD_STS) {
    407		*health = POWER_SUPPLY_HEALTH_COLD;
    408		return 0;
    409	}
    410
    411	if (ret & WM831X_BATT_OV_STS) {
    412		*health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
    413		return 0;
    414	}
    415
    416	switch (ret & WM831X_CHG_STATE_MASK) {
    417	case WM831X_CHG_STATE_TRICKLE_OT:
    418	case WM831X_CHG_STATE_FAST_OT:
    419		*health = POWER_SUPPLY_HEALTH_OVERHEAT;
    420		break;
    421	case WM831X_CHG_STATE_DEFECTIVE:
    422		*health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
    423		break;
    424	default:
    425		*health = POWER_SUPPLY_HEALTH_GOOD;
    426		break;
    427	}
    428
    429	return 0;
    430}
    431
    432static int wm831x_bat_get_prop(struct power_supply *psy,
    433			       enum power_supply_property psp,
    434			       union power_supply_propval *val)
    435{
    436	struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev.parent);
    437	struct wm831x *wm831x = wm831x_power->wm831x;
    438	int ret = 0;
    439
    440	switch (psp) {
    441	case POWER_SUPPLY_PROP_STATUS:
    442		ret = wm831x_bat_check_status(wm831x, &val->intval);
    443		break;
    444	case POWER_SUPPLY_PROP_ONLINE:
    445		ret = wm831x_power_check_online(wm831x, WM831X_PWR_SRC_BATT,
    446						val);
    447		break;
    448	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
    449		ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_BATT, val);
    450		break;
    451	case POWER_SUPPLY_PROP_HEALTH:
    452		ret = wm831x_bat_check_health(wm831x, &val->intval);
    453		break;
    454	case POWER_SUPPLY_PROP_CHARGE_TYPE:
    455		ret = wm831x_bat_check_type(wm831x, &val->intval);
    456		break;
    457	default:
    458		ret = -EINVAL;
    459		break;
    460	}
    461
    462	return ret;
    463}
    464
    465static enum power_supply_property wm831x_bat_props[] = {
    466	POWER_SUPPLY_PROP_STATUS,
    467	POWER_SUPPLY_PROP_ONLINE,
    468	POWER_SUPPLY_PROP_VOLTAGE_NOW,
    469	POWER_SUPPLY_PROP_HEALTH,
    470	POWER_SUPPLY_PROP_CHARGE_TYPE,
    471};
    472
    473static const char *wm831x_bat_irqs[] = {
    474	"BATT HOT",
    475	"BATT COLD",
    476	"BATT FAIL",
    477	"OV",
    478	"END",
    479	"TO",
    480	"MODE",
    481	"START",
    482};
    483
    484static irqreturn_t wm831x_bat_irq(int irq, void *data)
    485{
    486	struct wm831x_power *wm831x_power = data;
    487	struct wm831x *wm831x = wm831x_power->wm831x;
    488
    489	dev_dbg(wm831x->dev, "Battery status changed: %d\n", irq);
    490
    491	/* The battery charger is autonomous so we don't need to do
    492	 * anything except kick user space */
    493	if (wm831x_power->have_battery)
    494		power_supply_changed(wm831x_power->battery);
    495
    496	return IRQ_HANDLED;
    497}
    498
    499
    500/*********************************************************************
    501 *		Initialisation
    502 *********************************************************************/
    503
    504static irqreturn_t wm831x_syslo_irq(int irq, void *data)
    505{
    506	struct wm831x_power *wm831x_power = data;
    507	struct wm831x *wm831x = wm831x_power->wm831x;
    508
    509	/* Not much we can actually *do* but tell people for
    510	 * posterity, we're probably about to run out of power. */
    511	dev_crit(wm831x->dev, "SYSVDD under voltage\n");
    512
    513	return IRQ_HANDLED;
    514}
    515
    516static irqreturn_t wm831x_pwr_src_irq(int irq, void *data)
    517{
    518	struct wm831x_power *wm831x_power = data;
    519	struct wm831x *wm831x = wm831x_power->wm831x;
    520
    521	dev_dbg(wm831x->dev, "Power source changed\n");
    522
    523	/* Just notify for everything - little harm in overnotifying. */
    524	if (wm831x_power->have_battery)
    525		power_supply_changed(wm831x_power->battery);
    526	power_supply_changed(wm831x_power->usb);
    527	power_supply_changed(wm831x_power->wall);
    528
    529	return IRQ_HANDLED;
    530}
    531
    532static int wm831x_power_probe(struct platform_device *pdev)
    533{
    534	struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
    535	struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
    536	struct wm831x_power *power;
    537	int ret, irq, i;
    538
    539	power = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_power),
    540			     GFP_KERNEL);
    541	if (power == NULL)
    542		return -ENOMEM;
    543
    544	power->wm831x = wm831x;
    545	platform_set_drvdata(pdev, power);
    546
    547	if (wm831x_pdata && wm831x_pdata->wm831x_num) {
    548		snprintf(power->wall_name, sizeof(power->wall_name),
    549			 "wm831x-wall.%d", wm831x_pdata->wm831x_num);
    550		snprintf(power->battery_name, sizeof(power->wall_name),
    551			 "wm831x-battery.%d", wm831x_pdata->wm831x_num);
    552		snprintf(power->usb_name, sizeof(power->wall_name),
    553			 "wm831x-usb.%d", wm831x_pdata->wm831x_num);
    554	} else {
    555		snprintf(power->wall_name, sizeof(power->wall_name),
    556			 "wm831x-wall");
    557		snprintf(power->battery_name, sizeof(power->wall_name),
    558			 "wm831x-battery");
    559		snprintf(power->usb_name, sizeof(power->wall_name),
    560			 "wm831x-usb");
    561	}
    562
    563	/* We ignore configuration failures since we can still read back
    564	 * the status without enabling the charger.
    565	 */
    566	wm831x_config_battery(wm831x);
    567
    568	power->wall_desc.name = power->wall_name;
    569	power->wall_desc.type = POWER_SUPPLY_TYPE_MAINS;
    570	power->wall_desc.properties = wm831x_wall_props;
    571	power->wall_desc.num_properties = ARRAY_SIZE(wm831x_wall_props);
    572	power->wall_desc.get_property = wm831x_wall_get_prop;
    573	power->wall = power_supply_register(&pdev->dev, &power->wall_desc,
    574					    NULL);
    575	if (IS_ERR(power->wall)) {
    576		ret = PTR_ERR(power->wall);
    577		goto err;
    578	}
    579
    580	power->usb_desc.name = power->usb_name,
    581	power->usb_desc.type = POWER_SUPPLY_TYPE_USB;
    582	power->usb_desc.properties = wm831x_usb_props;
    583	power->usb_desc.num_properties = ARRAY_SIZE(wm831x_usb_props);
    584	power->usb_desc.get_property = wm831x_usb_get_prop;
    585	power->usb = power_supply_register(&pdev->dev, &power->usb_desc, NULL);
    586	if (IS_ERR(power->usb)) {
    587		ret = PTR_ERR(power->usb);
    588		goto err_wall;
    589	}
    590
    591	ret = wm831x_reg_read(wm831x, WM831X_CHARGER_CONTROL_1);
    592	if (ret < 0)
    593		goto err_wall;
    594	power->have_battery = ret & WM831X_CHG_ENA;
    595
    596	if (power->have_battery) {
    597		power->battery_desc.name = power->battery_name;
    598		power->battery_desc.properties = wm831x_bat_props;
    599		power->battery_desc.num_properties = ARRAY_SIZE(wm831x_bat_props);
    600		power->battery_desc.get_property = wm831x_bat_get_prop;
    601		power->battery_desc.use_for_apm = 1;
    602		power->battery = power_supply_register(&pdev->dev,
    603						       &power->battery_desc,
    604						       NULL);
    605		if (IS_ERR(power->battery)) {
    606			ret = PTR_ERR(power->battery);
    607			goto err_usb;
    608		}
    609	}
    610
    611	irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO"));
    612	ret = request_threaded_irq(irq, NULL, wm831x_syslo_irq,
    613				   IRQF_TRIGGER_RISING | IRQF_ONESHOT, "System power low",
    614				   power);
    615	if (ret != 0) {
    616		dev_err(&pdev->dev, "Failed to request SYSLO IRQ %d: %d\n",
    617			irq, ret);
    618		goto err_battery;
    619	}
    620
    621	irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC"));
    622	ret = request_threaded_irq(irq, NULL, wm831x_pwr_src_irq,
    623				   IRQF_TRIGGER_RISING | IRQF_ONESHOT, "Power source",
    624				   power);
    625	if (ret != 0) {
    626		dev_err(&pdev->dev, "Failed to request PWR SRC IRQ %d: %d\n",
    627			irq, ret);
    628		goto err_syslo;
    629	}
    630
    631	for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
    632		irq = wm831x_irq(wm831x,
    633				 platform_get_irq_byname(pdev,
    634							 wm831x_bat_irqs[i]));
    635		ret = request_threaded_irq(irq, NULL, wm831x_bat_irq,
    636					   IRQF_TRIGGER_RISING | IRQF_ONESHOT,
    637					   wm831x_bat_irqs[i],
    638					   power);
    639		if (ret != 0) {
    640			dev_err(&pdev->dev,
    641				"Failed to request %s IRQ %d: %d\n",
    642				wm831x_bat_irqs[i], irq, ret);
    643			goto err_bat_irq;
    644		}
    645	}
    646
    647	power->usb_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "phys", 0);
    648	ret = PTR_ERR_OR_ZERO(power->usb_phy);
    649
    650	switch (ret) {
    651	case 0:
    652		power->usb_notify.notifier_call = wm831x_usb_limit_change;
    653		ret = usb_register_notifier(power->usb_phy, &power->usb_notify);
    654		if (ret) {
    655			dev_err(&pdev->dev, "Failed to register notifier: %d\n",
    656				ret);
    657			goto err_bat_irq;
    658		}
    659		break;
    660	case -EINVAL:
    661	case -ENODEV:
    662		/* ignore missing usb-phy, it's optional */
    663		power->usb_phy = NULL;
    664		ret = 0;
    665		break;
    666	default:
    667		dev_err(&pdev->dev, "Failed to find USB phy: %d\n", ret);
    668		fallthrough;
    669	case -EPROBE_DEFER:
    670		goto err_bat_irq;
    671	}
    672
    673	return ret;
    674
    675err_bat_irq:
    676	--i;
    677	for (; i >= 0; i--) {
    678		irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
    679		free_irq(irq, power);
    680	}
    681	irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC"));
    682	free_irq(irq, power);
    683err_syslo:
    684	irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO"));
    685	free_irq(irq, power);
    686err_battery:
    687	if (power->have_battery)
    688		power_supply_unregister(power->battery);
    689err_usb:
    690	power_supply_unregister(power->usb);
    691err_wall:
    692	power_supply_unregister(power->wall);
    693err:
    694	return ret;
    695}
    696
    697static int wm831x_power_remove(struct platform_device *pdev)
    698{
    699	struct wm831x_power *wm831x_power = platform_get_drvdata(pdev);
    700	struct wm831x *wm831x = wm831x_power->wm831x;
    701	int irq, i;
    702
    703	if (wm831x_power->usb_phy) {
    704		usb_unregister_notifier(wm831x_power->usb_phy,
    705					&wm831x_power->usb_notify);
    706	}
    707
    708	for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
    709		irq = wm831x_irq(wm831x, 
    710				 platform_get_irq_byname(pdev,
    711							 wm831x_bat_irqs[i]));
    712		free_irq(irq, wm831x_power);
    713	}
    714
    715	irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC"));
    716	free_irq(irq, wm831x_power);
    717
    718	irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO"));
    719	free_irq(irq, wm831x_power);
    720
    721	if (wm831x_power->have_battery)
    722		power_supply_unregister(wm831x_power->battery);
    723	power_supply_unregister(wm831x_power->wall);
    724	power_supply_unregister(wm831x_power->usb);
    725	return 0;
    726}
    727
    728static struct platform_driver wm831x_power_driver = {
    729	.probe = wm831x_power_probe,
    730	.remove = wm831x_power_remove,
    731	.driver = {
    732		.name = "wm831x-power",
    733	},
    734};
    735
    736module_platform_driver(wm831x_power_driver);
    737
    738MODULE_DESCRIPTION("Power supply driver for WM831x PMICs");
    739MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
    740MODULE_LICENSE("GPL");
    741MODULE_ALIAS("platform:wm831x-power");