162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PMU driver for Wolfson Microelectronics wm831x PMICs 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2009 Wolfson Microelectronics PLC. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/err.h> 1062306a36Sopenharmony_ci#include <linux/platform_device.h> 1162306a36Sopenharmony_ci#include <linux/power_supply.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/usb/phy.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/mfd/wm831x/core.h> 1662306a36Sopenharmony_ci#include <linux/mfd/wm831x/auxadc.h> 1762306a36Sopenharmony_ci#include <linux/mfd/wm831x/pmu.h> 1862306a36Sopenharmony_ci#include <linux/mfd/wm831x/pdata.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct wm831x_power { 2162306a36Sopenharmony_ci struct wm831x *wm831x; 2262306a36Sopenharmony_ci struct power_supply *wall; 2362306a36Sopenharmony_ci struct power_supply *usb; 2462306a36Sopenharmony_ci struct power_supply *battery; 2562306a36Sopenharmony_ci struct power_supply_desc wall_desc; 2662306a36Sopenharmony_ci struct power_supply_desc usb_desc; 2762306a36Sopenharmony_ci struct power_supply_desc battery_desc; 2862306a36Sopenharmony_ci char wall_name[20]; 2962306a36Sopenharmony_ci char usb_name[20]; 3062306a36Sopenharmony_ci char battery_name[20]; 3162306a36Sopenharmony_ci bool have_battery; 3262306a36Sopenharmony_ci struct usb_phy *usb_phy; 3362306a36Sopenharmony_ci struct notifier_block usb_notify; 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic int wm831x_power_check_online(struct wm831x *wm831x, int supply, 3762306a36Sopenharmony_ci union power_supply_propval *val) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci int ret; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci ret = wm831x_reg_read(wm831x, WM831X_SYSTEM_STATUS); 4262306a36Sopenharmony_ci if (ret < 0) 4362306a36Sopenharmony_ci return ret; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (ret & supply) 4662306a36Sopenharmony_ci val->intval = 1; 4762306a36Sopenharmony_ci else 4862306a36Sopenharmony_ci val->intval = 0; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci return 0; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic int wm831x_power_read_voltage(struct wm831x *wm831x, 5462306a36Sopenharmony_ci enum wm831x_auxadc src, 5562306a36Sopenharmony_ci union power_supply_propval *val) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci int ret; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci ret = wm831x_auxadc_read_uv(wm831x, src); 6062306a36Sopenharmony_ci if (ret >= 0) 6162306a36Sopenharmony_ci val->intval = ret; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci return ret; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/********************************************************************* 6762306a36Sopenharmony_ci * WALL Power 6862306a36Sopenharmony_ci *********************************************************************/ 6962306a36Sopenharmony_cistatic int wm831x_wall_get_prop(struct power_supply *psy, 7062306a36Sopenharmony_ci enum power_supply_property psp, 7162306a36Sopenharmony_ci union power_supply_propval *val) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev.parent); 7462306a36Sopenharmony_ci struct wm831x *wm831x = wm831x_power->wm831x; 7562306a36Sopenharmony_ci int ret = 0; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci switch (psp) { 7862306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 7962306a36Sopenharmony_ci ret = wm831x_power_check_online(wm831x, WM831X_PWR_WALL, val); 8062306a36Sopenharmony_ci break; 8162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 8262306a36Sopenharmony_ci ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_WALL, val); 8362306a36Sopenharmony_ci break; 8462306a36Sopenharmony_ci default: 8562306a36Sopenharmony_ci ret = -EINVAL; 8662306a36Sopenharmony_ci break; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci return ret; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic enum power_supply_property wm831x_wall_props[] = { 9362306a36Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 9462306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 9562306a36Sopenharmony_ci}; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/********************************************************************* 9862306a36Sopenharmony_ci * USB Power 9962306a36Sopenharmony_ci *********************************************************************/ 10062306a36Sopenharmony_cistatic int wm831x_usb_get_prop(struct power_supply *psy, 10162306a36Sopenharmony_ci enum power_supply_property psp, 10262306a36Sopenharmony_ci union power_supply_propval *val) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev.parent); 10562306a36Sopenharmony_ci struct wm831x *wm831x = wm831x_power->wm831x; 10662306a36Sopenharmony_ci int ret = 0; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci switch (psp) { 10962306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 11062306a36Sopenharmony_ci ret = wm831x_power_check_online(wm831x, WM831X_PWR_USB, val); 11162306a36Sopenharmony_ci break; 11262306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 11362306a36Sopenharmony_ci ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_USB, val); 11462306a36Sopenharmony_ci break; 11562306a36Sopenharmony_ci default: 11662306a36Sopenharmony_ci ret = -EINVAL; 11762306a36Sopenharmony_ci break; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return ret; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic enum power_supply_property wm831x_usb_props[] = { 12462306a36Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 12562306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 12662306a36Sopenharmony_ci}; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/* In milliamps */ 12962306a36Sopenharmony_cistatic const unsigned int wm831x_usb_limits[] = { 13062306a36Sopenharmony_ci 0, 13162306a36Sopenharmony_ci 2, 13262306a36Sopenharmony_ci 100, 13362306a36Sopenharmony_ci 500, 13462306a36Sopenharmony_ci 900, 13562306a36Sopenharmony_ci 1500, 13662306a36Sopenharmony_ci 1800, 13762306a36Sopenharmony_ci 550, 13862306a36Sopenharmony_ci}; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic int wm831x_usb_limit_change(struct notifier_block *nb, 14162306a36Sopenharmony_ci unsigned long limit, void *data) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct wm831x_power *wm831x_power = container_of(nb, 14462306a36Sopenharmony_ci struct wm831x_power, 14562306a36Sopenharmony_ci usb_notify); 14662306a36Sopenharmony_ci unsigned int i, best; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* Find the highest supported limit */ 14962306a36Sopenharmony_ci best = 0; 15062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(wm831x_usb_limits); i++) { 15162306a36Sopenharmony_ci if (limit >= wm831x_usb_limits[i] && 15262306a36Sopenharmony_ci wm831x_usb_limits[best] < wm831x_usb_limits[i]) 15362306a36Sopenharmony_ci best = i; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci dev_dbg(wm831x_power->wm831x->dev, 15762306a36Sopenharmony_ci "Limiting USB current to %umA", wm831x_usb_limits[best]); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci wm831x_set_bits(wm831x_power->wm831x, WM831X_POWER_STATE, 16062306a36Sopenharmony_ci WM831X_USB_ILIM_MASK, best); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci return 0; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/********************************************************************* 16662306a36Sopenharmony_ci * Battery properties 16762306a36Sopenharmony_ci *********************************************************************/ 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistruct chg_map { 17062306a36Sopenharmony_ci int val; 17162306a36Sopenharmony_ci int reg_val; 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic struct chg_map trickle_ilims[] = { 17562306a36Sopenharmony_ci { 50, 0 << WM831X_CHG_TRKL_ILIM_SHIFT }, 17662306a36Sopenharmony_ci { 100, 1 << WM831X_CHG_TRKL_ILIM_SHIFT }, 17762306a36Sopenharmony_ci { 150, 2 << WM831X_CHG_TRKL_ILIM_SHIFT }, 17862306a36Sopenharmony_ci { 200, 3 << WM831X_CHG_TRKL_ILIM_SHIFT }, 17962306a36Sopenharmony_ci}; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic struct chg_map vsels[] = { 18262306a36Sopenharmony_ci { 4050, 0 << WM831X_CHG_VSEL_SHIFT }, 18362306a36Sopenharmony_ci { 4100, 1 << WM831X_CHG_VSEL_SHIFT }, 18462306a36Sopenharmony_ci { 4150, 2 << WM831X_CHG_VSEL_SHIFT }, 18562306a36Sopenharmony_ci { 4200, 3 << WM831X_CHG_VSEL_SHIFT }, 18662306a36Sopenharmony_ci}; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic struct chg_map fast_ilims[] = { 18962306a36Sopenharmony_ci { 0, 0 << WM831X_CHG_FAST_ILIM_SHIFT }, 19062306a36Sopenharmony_ci { 50, 1 << WM831X_CHG_FAST_ILIM_SHIFT }, 19162306a36Sopenharmony_ci { 100, 2 << WM831X_CHG_FAST_ILIM_SHIFT }, 19262306a36Sopenharmony_ci { 150, 3 << WM831X_CHG_FAST_ILIM_SHIFT }, 19362306a36Sopenharmony_ci { 200, 4 << WM831X_CHG_FAST_ILIM_SHIFT }, 19462306a36Sopenharmony_ci { 250, 5 << WM831X_CHG_FAST_ILIM_SHIFT }, 19562306a36Sopenharmony_ci { 300, 6 << WM831X_CHG_FAST_ILIM_SHIFT }, 19662306a36Sopenharmony_ci { 350, 7 << WM831X_CHG_FAST_ILIM_SHIFT }, 19762306a36Sopenharmony_ci { 400, 8 << WM831X_CHG_FAST_ILIM_SHIFT }, 19862306a36Sopenharmony_ci { 450, 9 << WM831X_CHG_FAST_ILIM_SHIFT }, 19962306a36Sopenharmony_ci { 500, 10 << WM831X_CHG_FAST_ILIM_SHIFT }, 20062306a36Sopenharmony_ci { 600, 11 << WM831X_CHG_FAST_ILIM_SHIFT }, 20162306a36Sopenharmony_ci { 700, 12 << WM831X_CHG_FAST_ILIM_SHIFT }, 20262306a36Sopenharmony_ci { 800, 13 << WM831X_CHG_FAST_ILIM_SHIFT }, 20362306a36Sopenharmony_ci { 900, 14 << WM831X_CHG_FAST_ILIM_SHIFT }, 20462306a36Sopenharmony_ci { 1000, 15 << WM831X_CHG_FAST_ILIM_SHIFT }, 20562306a36Sopenharmony_ci}; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic struct chg_map eoc_iterms[] = { 20862306a36Sopenharmony_ci { 20, 0 << WM831X_CHG_ITERM_SHIFT }, 20962306a36Sopenharmony_ci { 30, 1 << WM831X_CHG_ITERM_SHIFT }, 21062306a36Sopenharmony_ci { 40, 2 << WM831X_CHG_ITERM_SHIFT }, 21162306a36Sopenharmony_ci { 50, 3 << WM831X_CHG_ITERM_SHIFT }, 21262306a36Sopenharmony_ci { 60, 4 << WM831X_CHG_ITERM_SHIFT }, 21362306a36Sopenharmony_ci { 70, 5 << WM831X_CHG_ITERM_SHIFT }, 21462306a36Sopenharmony_ci { 80, 6 << WM831X_CHG_ITERM_SHIFT }, 21562306a36Sopenharmony_ci { 90, 7 << WM831X_CHG_ITERM_SHIFT }, 21662306a36Sopenharmony_ci}; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic struct chg_map chg_times[] = { 21962306a36Sopenharmony_ci { 60, 0 << WM831X_CHG_TIME_SHIFT }, 22062306a36Sopenharmony_ci { 90, 1 << WM831X_CHG_TIME_SHIFT }, 22162306a36Sopenharmony_ci { 120, 2 << WM831X_CHG_TIME_SHIFT }, 22262306a36Sopenharmony_ci { 150, 3 << WM831X_CHG_TIME_SHIFT }, 22362306a36Sopenharmony_ci { 180, 4 << WM831X_CHG_TIME_SHIFT }, 22462306a36Sopenharmony_ci { 210, 5 << WM831X_CHG_TIME_SHIFT }, 22562306a36Sopenharmony_ci { 240, 6 << WM831X_CHG_TIME_SHIFT }, 22662306a36Sopenharmony_ci { 270, 7 << WM831X_CHG_TIME_SHIFT }, 22762306a36Sopenharmony_ci { 300, 8 << WM831X_CHG_TIME_SHIFT }, 22862306a36Sopenharmony_ci { 330, 9 << WM831X_CHG_TIME_SHIFT }, 22962306a36Sopenharmony_ci { 360, 10 << WM831X_CHG_TIME_SHIFT }, 23062306a36Sopenharmony_ci { 390, 11 << WM831X_CHG_TIME_SHIFT }, 23162306a36Sopenharmony_ci { 420, 12 << WM831X_CHG_TIME_SHIFT }, 23262306a36Sopenharmony_ci { 450, 13 << WM831X_CHG_TIME_SHIFT }, 23362306a36Sopenharmony_ci { 480, 14 << WM831X_CHG_TIME_SHIFT }, 23462306a36Sopenharmony_ci { 510, 15 << WM831X_CHG_TIME_SHIFT }, 23562306a36Sopenharmony_ci}; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic void wm831x_battery_apply_config(struct wm831x *wm831x, 23862306a36Sopenharmony_ci struct chg_map *map, int count, int val, 23962306a36Sopenharmony_ci int *reg, const char *name, 24062306a36Sopenharmony_ci const char *units) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci int i; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci for (i = 0; i < count; i++) 24562306a36Sopenharmony_ci if (val == map[i].val) 24662306a36Sopenharmony_ci break; 24762306a36Sopenharmony_ci if (i == count) { 24862306a36Sopenharmony_ci dev_err(wm831x->dev, "Invalid %s %d%s\n", 24962306a36Sopenharmony_ci name, val, units); 25062306a36Sopenharmony_ci } else { 25162306a36Sopenharmony_ci *reg |= map[i].reg_val; 25262306a36Sopenharmony_ci dev_dbg(wm831x->dev, "Set %s of %d%s\n", name, val, units); 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic void wm831x_config_battery(struct wm831x *wm831x) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data; 25962306a36Sopenharmony_ci struct wm831x_battery_pdata *pdata; 26062306a36Sopenharmony_ci int ret, reg1, reg2; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (!wm831x_pdata || !wm831x_pdata->battery) { 26362306a36Sopenharmony_ci dev_warn(wm831x->dev, 26462306a36Sopenharmony_ci "No battery charger configuration\n"); 26562306a36Sopenharmony_ci return; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci pdata = wm831x_pdata->battery; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci reg1 = 0; 27162306a36Sopenharmony_ci reg2 = 0; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (!pdata->enable) { 27462306a36Sopenharmony_ci dev_info(wm831x->dev, "Battery charger disabled\n"); 27562306a36Sopenharmony_ci return; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci reg1 |= WM831X_CHG_ENA; 27962306a36Sopenharmony_ci if (pdata->off_mask) 28062306a36Sopenharmony_ci reg2 |= WM831X_CHG_OFF_MSK; 28162306a36Sopenharmony_ci if (pdata->fast_enable) 28262306a36Sopenharmony_ci reg1 |= WM831X_CHG_FAST; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci wm831x_battery_apply_config(wm831x, trickle_ilims, 28562306a36Sopenharmony_ci ARRAY_SIZE(trickle_ilims), 28662306a36Sopenharmony_ci pdata->trickle_ilim, ®2, 28762306a36Sopenharmony_ci "trickle charge current limit", "mA"); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci wm831x_battery_apply_config(wm831x, vsels, ARRAY_SIZE(vsels), 29062306a36Sopenharmony_ci pdata->vsel, ®2, 29162306a36Sopenharmony_ci "target voltage", "mV"); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci wm831x_battery_apply_config(wm831x, fast_ilims, ARRAY_SIZE(fast_ilims), 29462306a36Sopenharmony_ci pdata->fast_ilim, ®2, 29562306a36Sopenharmony_ci "fast charge current limit", "mA"); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci wm831x_battery_apply_config(wm831x, eoc_iterms, ARRAY_SIZE(eoc_iterms), 29862306a36Sopenharmony_ci pdata->eoc_iterm, ®1, 29962306a36Sopenharmony_ci "end of charge current threshold", "mA"); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci wm831x_battery_apply_config(wm831x, chg_times, ARRAY_SIZE(chg_times), 30262306a36Sopenharmony_ci pdata->timeout, ®2, 30362306a36Sopenharmony_ci "charger timeout", "min"); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci ret = wm831x_reg_unlock(wm831x); 30662306a36Sopenharmony_ci if (ret != 0) { 30762306a36Sopenharmony_ci dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret); 30862306a36Sopenharmony_ci return; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_1, 31262306a36Sopenharmony_ci WM831X_CHG_ENA_MASK | 31362306a36Sopenharmony_ci WM831X_CHG_FAST_MASK | 31462306a36Sopenharmony_ci WM831X_CHG_ITERM_MASK, 31562306a36Sopenharmony_ci reg1); 31662306a36Sopenharmony_ci if (ret != 0) 31762306a36Sopenharmony_ci dev_err(wm831x->dev, "Failed to set charger control 1: %d\n", 31862306a36Sopenharmony_ci ret); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_2, 32162306a36Sopenharmony_ci WM831X_CHG_OFF_MSK | 32262306a36Sopenharmony_ci WM831X_CHG_TIME_MASK | 32362306a36Sopenharmony_ci WM831X_CHG_FAST_ILIM_MASK | 32462306a36Sopenharmony_ci WM831X_CHG_TRKL_ILIM_MASK | 32562306a36Sopenharmony_ci WM831X_CHG_VSEL_MASK, 32662306a36Sopenharmony_ci reg2); 32762306a36Sopenharmony_ci if (ret != 0) 32862306a36Sopenharmony_ci dev_err(wm831x->dev, "Failed to set charger control 2: %d\n", 32962306a36Sopenharmony_ci ret); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci wm831x_reg_lock(wm831x); 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic int wm831x_bat_check_status(struct wm831x *wm831x, int *status) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci int ret; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci ret = wm831x_reg_read(wm831x, WM831X_SYSTEM_STATUS); 33962306a36Sopenharmony_ci if (ret < 0) 34062306a36Sopenharmony_ci return ret; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (ret & WM831X_PWR_SRC_BATT) { 34362306a36Sopenharmony_ci *status = POWER_SUPPLY_STATUS_DISCHARGING; 34462306a36Sopenharmony_ci return 0; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS); 34862306a36Sopenharmony_ci if (ret < 0) 34962306a36Sopenharmony_ci return ret; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci switch (ret & WM831X_CHG_STATE_MASK) { 35262306a36Sopenharmony_ci case WM831X_CHG_STATE_OFF: 35362306a36Sopenharmony_ci *status = POWER_SUPPLY_STATUS_NOT_CHARGING; 35462306a36Sopenharmony_ci break; 35562306a36Sopenharmony_ci case WM831X_CHG_STATE_TRICKLE: 35662306a36Sopenharmony_ci case WM831X_CHG_STATE_FAST: 35762306a36Sopenharmony_ci *status = POWER_SUPPLY_STATUS_CHARGING; 35862306a36Sopenharmony_ci break; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci default: 36162306a36Sopenharmony_ci *status = POWER_SUPPLY_STATUS_UNKNOWN; 36262306a36Sopenharmony_ci break; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci return 0; 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic int wm831x_bat_check_type(struct wm831x *wm831x, int *type) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci int ret; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS); 37362306a36Sopenharmony_ci if (ret < 0) 37462306a36Sopenharmony_ci return ret; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci switch (ret & WM831X_CHG_STATE_MASK) { 37762306a36Sopenharmony_ci case WM831X_CHG_STATE_TRICKLE: 37862306a36Sopenharmony_ci case WM831X_CHG_STATE_TRICKLE_OT: 37962306a36Sopenharmony_ci *type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; 38062306a36Sopenharmony_ci break; 38162306a36Sopenharmony_ci case WM831X_CHG_STATE_FAST: 38262306a36Sopenharmony_ci case WM831X_CHG_STATE_FAST_OT: 38362306a36Sopenharmony_ci *type = POWER_SUPPLY_CHARGE_TYPE_FAST; 38462306a36Sopenharmony_ci break; 38562306a36Sopenharmony_ci default: 38662306a36Sopenharmony_ci *type = POWER_SUPPLY_CHARGE_TYPE_NONE; 38762306a36Sopenharmony_ci break; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci return 0; 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic int wm831x_bat_check_health(struct wm831x *wm831x, int *health) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci int ret; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS); 39862306a36Sopenharmony_ci if (ret < 0) 39962306a36Sopenharmony_ci return ret; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (ret & WM831X_BATT_HOT_STS) { 40262306a36Sopenharmony_ci *health = POWER_SUPPLY_HEALTH_OVERHEAT; 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (ret & WM831X_BATT_COLD_STS) { 40762306a36Sopenharmony_ci *health = POWER_SUPPLY_HEALTH_COLD; 40862306a36Sopenharmony_ci return 0; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (ret & WM831X_BATT_OV_STS) { 41262306a36Sopenharmony_ci *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 41362306a36Sopenharmony_ci return 0; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci switch (ret & WM831X_CHG_STATE_MASK) { 41762306a36Sopenharmony_ci case WM831X_CHG_STATE_TRICKLE_OT: 41862306a36Sopenharmony_ci case WM831X_CHG_STATE_FAST_OT: 41962306a36Sopenharmony_ci *health = POWER_SUPPLY_HEALTH_OVERHEAT; 42062306a36Sopenharmony_ci break; 42162306a36Sopenharmony_ci case WM831X_CHG_STATE_DEFECTIVE: 42262306a36Sopenharmony_ci *health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; 42362306a36Sopenharmony_ci break; 42462306a36Sopenharmony_ci default: 42562306a36Sopenharmony_ci *health = POWER_SUPPLY_HEALTH_GOOD; 42662306a36Sopenharmony_ci break; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci return 0; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic int wm831x_bat_get_prop(struct power_supply *psy, 43362306a36Sopenharmony_ci enum power_supply_property psp, 43462306a36Sopenharmony_ci union power_supply_propval *val) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev.parent); 43762306a36Sopenharmony_ci struct wm831x *wm831x = wm831x_power->wm831x; 43862306a36Sopenharmony_ci int ret = 0; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci switch (psp) { 44162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 44262306a36Sopenharmony_ci ret = wm831x_bat_check_status(wm831x, &val->intval); 44362306a36Sopenharmony_ci break; 44462306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 44562306a36Sopenharmony_ci ret = wm831x_power_check_online(wm831x, WM831X_PWR_SRC_BATT, 44662306a36Sopenharmony_ci val); 44762306a36Sopenharmony_ci break; 44862306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 44962306a36Sopenharmony_ci ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_BATT, val); 45062306a36Sopenharmony_ci break; 45162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_HEALTH: 45262306a36Sopenharmony_ci ret = wm831x_bat_check_health(wm831x, &val->intval); 45362306a36Sopenharmony_ci break; 45462306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_TYPE: 45562306a36Sopenharmony_ci ret = wm831x_bat_check_type(wm831x, &val->intval); 45662306a36Sopenharmony_ci break; 45762306a36Sopenharmony_ci default: 45862306a36Sopenharmony_ci ret = -EINVAL; 45962306a36Sopenharmony_ci break; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci return ret; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic enum power_supply_property wm831x_bat_props[] = { 46662306a36Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 46762306a36Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 46862306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 46962306a36Sopenharmony_ci POWER_SUPPLY_PROP_HEALTH, 47062306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_TYPE, 47162306a36Sopenharmony_ci}; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic const char *wm831x_bat_irqs[] = { 47462306a36Sopenharmony_ci "BATT HOT", 47562306a36Sopenharmony_ci "BATT COLD", 47662306a36Sopenharmony_ci "BATT FAIL", 47762306a36Sopenharmony_ci "OV", 47862306a36Sopenharmony_ci "END", 47962306a36Sopenharmony_ci "TO", 48062306a36Sopenharmony_ci "MODE", 48162306a36Sopenharmony_ci "START", 48262306a36Sopenharmony_ci}; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic irqreturn_t wm831x_bat_irq(int irq, void *data) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct wm831x_power *wm831x_power = data; 48762306a36Sopenharmony_ci struct wm831x *wm831x = wm831x_power->wm831x; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci dev_dbg(wm831x->dev, "Battery status changed: %d\n", irq); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci /* The battery charger is autonomous so we don't need to do 49262306a36Sopenharmony_ci * anything except kick user space */ 49362306a36Sopenharmony_ci if (wm831x_power->have_battery) 49462306a36Sopenharmony_ci power_supply_changed(wm831x_power->battery); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci return IRQ_HANDLED; 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci/********************************************************************* 50162306a36Sopenharmony_ci * Initialisation 50262306a36Sopenharmony_ci *********************************************************************/ 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cistatic irqreturn_t wm831x_syslo_irq(int irq, void *data) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct wm831x_power *wm831x_power = data; 50762306a36Sopenharmony_ci struct wm831x *wm831x = wm831x_power->wm831x; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* Not much we can actually *do* but tell people for 51062306a36Sopenharmony_ci * posterity, we're probably about to run out of power. */ 51162306a36Sopenharmony_ci dev_crit(wm831x->dev, "SYSVDD under voltage\n"); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci return IRQ_HANDLED; 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic irqreturn_t wm831x_pwr_src_irq(int irq, void *data) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci struct wm831x_power *wm831x_power = data; 51962306a36Sopenharmony_ci struct wm831x *wm831x = wm831x_power->wm831x; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci dev_dbg(wm831x->dev, "Power source changed\n"); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* Just notify for everything - little harm in overnotifying. */ 52462306a36Sopenharmony_ci if (wm831x_power->have_battery) 52562306a36Sopenharmony_ci power_supply_changed(wm831x_power->battery); 52662306a36Sopenharmony_ci power_supply_changed(wm831x_power->usb); 52762306a36Sopenharmony_ci power_supply_changed(wm831x_power->wall); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci return IRQ_HANDLED; 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic int wm831x_power_probe(struct platform_device *pdev) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); 53562306a36Sopenharmony_ci struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data; 53662306a36Sopenharmony_ci struct wm831x_power *power; 53762306a36Sopenharmony_ci int ret, irq, i; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci power = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_power), 54062306a36Sopenharmony_ci GFP_KERNEL); 54162306a36Sopenharmony_ci if (power == NULL) 54262306a36Sopenharmony_ci return -ENOMEM; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci power->wm831x = wm831x; 54562306a36Sopenharmony_ci platform_set_drvdata(pdev, power); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (wm831x_pdata && wm831x_pdata->wm831x_num) { 54862306a36Sopenharmony_ci snprintf(power->wall_name, sizeof(power->wall_name), 54962306a36Sopenharmony_ci "wm831x-wall.%d", wm831x_pdata->wm831x_num); 55062306a36Sopenharmony_ci snprintf(power->battery_name, sizeof(power->wall_name), 55162306a36Sopenharmony_ci "wm831x-battery.%d", wm831x_pdata->wm831x_num); 55262306a36Sopenharmony_ci snprintf(power->usb_name, sizeof(power->wall_name), 55362306a36Sopenharmony_ci "wm831x-usb.%d", wm831x_pdata->wm831x_num); 55462306a36Sopenharmony_ci } else { 55562306a36Sopenharmony_ci snprintf(power->wall_name, sizeof(power->wall_name), 55662306a36Sopenharmony_ci "wm831x-wall"); 55762306a36Sopenharmony_ci snprintf(power->battery_name, sizeof(power->wall_name), 55862306a36Sopenharmony_ci "wm831x-battery"); 55962306a36Sopenharmony_ci snprintf(power->usb_name, sizeof(power->wall_name), 56062306a36Sopenharmony_ci "wm831x-usb"); 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci /* We ignore configuration failures since we can still read back 56462306a36Sopenharmony_ci * the status without enabling the charger. 56562306a36Sopenharmony_ci */ 56662306a36Sopenharmony_ci wm831x_config_battery(wm831x); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci power->wall_desc.name = power->wall_name; 56962306a36Sopenharmony_ci power->wall_desc.type = POWER_SUPPLY_TYPE_MAINS; 57062306a36Sopenharmony_ci power->wall_desc.properties = wm831x_wall_props; 57162306a36Sopenharmony_ci power->wall_desc.num_properties = ARRAY_SIZE(wm831x_wall_props); 57262306a36Sopenharmony_ci power->wall_desc.get_property = wm831x_wall_get_prop; 57362306a36Sopenharmony_ci power->wall = power_supply_register(&pdev->dev, &power->wall_desc, 57462306a36Sopenharmony_ci NULL); 57562306a36Sopenharmony_ci if (IS_ERR(power->wall)) { 57662306a36Sopenharmony_ci ret = PTR_ERR(power->wall); 57762306a36Sopenharmony_ci goto err; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci power->usb_desc.name = power->usb_name, 58162306a36Sopenharmony_ci power->usb_desc.type = POWER_SUPPLY_TYPE_USB; 58262306a36Sopenharmony_ci power->usb_desc.properties = wm831x_usb_props; 58362306a36Sopenharmony_ci power->usb_desc.num_properties = ARRAY_SIZE(wm831x_usb_props); 58462306a36Sopenharmony_ci power->usb_desc.get_property = wm831x_usb_get_prop; 58562306a36Sopenharmony_ci power->usb = power_supply_register(&pdev->dev, &power->usb_desc, NULL); 58662306a36Sopenharmony_ci if (IS_ERR(power->usb)) { 58762306a36Sopenharmony_ci ret = PTR_ERR(power->usb); 58862306a36Sopenharmony_ci goto err_wall; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci ret = wm831x_reg_read(wm831x, WM831X_CHARGER_CONTROL_1); 59262306a36Sopenharmony_ci if (ret < 0) 59362306a36Sopenharmony_ci goto err_wall; 59462306a36Sopenharmony_ci power->have_battery = ret & WM831X_CHG_ENA; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (power->have_battery) { 59762306a36Sopenharmony_ci power->battery_desc.name = power->battery_name; 59862306a36Sopenharmony_ci power->battery_desc.properties = wm831x_bat_props; 59962306a36Sopenharmony_ci power->battery_desc.num_properties = ARRAY_SIZE(wm831x_bat_props); 60062306a36Sopenharmony_ci power->battery_desc.get_property = wm831x_bat_get_prop; 60162306a36Sopenharmony_ci power->battery_desc.use_for_apm = 1; 60262306a36Sopenharmony_ci power->battery = power_supply_register(&pdev->dev, 60362306a36Sopenharmony_ci &power->battery_desc, 60462306a36Sopenharmony_ci NULL); 60562306a36Sopenharmony_ci if (IS_ERR(power->battery)) { 60662306a36Sopenharmony_ci ret = PTR_ERR(power->battery); 60762306a36Sopenharmony_ci goto err_usb; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO")); 61262306a36Sopenharmony_ci ret = request_threaded_irq(irq, NULL, wm831x_syslo_irq, 61362306a36Sopenharmony_ci IRQF_TRIGGER_RISING | IRQF_ONESHOT, "System power low", 61462306a36Sopenharmony_ci power); 61562306a36Sopenharmony_ci if (ret != 0) { 61662306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to request SYSLO IRQ %d: %d\n", 61762306a36Sopenharmony_ci irq, ret); 61862306a36Sopenharmony_ci goto err_battery; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC")); 62262306a36Sopenharmony_ci ret = request_threaded_irq(irq, NULL, wm831x_pwr_src_irq, 62362306a36Sopenharmony_ci IRQF_TRIGGER_RISING | IRQF_ONESHOT, "Power source", 62462306a36Sopenharmony_ci power); 62562306a36Sopenharmony_ci if (ret != 0) { 62662306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to request PWR SRC IRQ %d: %d\n", 62762306a36Sopenharmony_ci irq, ret); 62862306a36Sopenharmony_ci goto err_syslo; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) { 63262306a36Sopenharmony_ci irq = wm831x_irq(wm831x, 63362306a36Sopenharmony_ci platform_get_irq_byname(pdev, 63462306a36Sopenharmony_ci wm831x_bat_irqs[i])); 63562306a36Sopenharmony_ci ret = request_threaded_irq(irq, NULL, wm831x_bat_irq, 63662306a36Sopenharmony_ci IRQF_TRIGGER_RISING | IRQF_ONESHOT, 63762306a36Sopenharmony_ci wm831x_bat_irqs[i], 63862306a36Sopenharmony_ci power); 63962306a36Sopenharmony_ci if (ret != 0) { 64062306a36Sopenharmony_ci dev_err(&pdev->dev, 64162306a36Sopenharmony_ci "Failed to request %s IRQ %d: %d\n", 64262306a36Sopenharmony_ci wm831x_bat_irqs[i], irq, ret); 64362306a36Sopenharmony_ci goto err_bat_irq; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci power->usb_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "phys", 0); 64862306a36Sopenharmony_ci ret = PTR_ERR_OR_ZERO(power->usb_phy); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci switch (ret) { 65162306a36Sopenharmony_ci case 0: 65262306a36Sopenharmony_ci power->usb_notify.notifier_call = wm831x_usb_limit_change; 65362306a36Sopenharmony_ci ret = usb_register_notifier(power->usb_phy, &power->usb_notify); 65462306a36Sopenharmony_ci if (ret) { 65562306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to register notifier: %d\n", 65662306a36Sopenharmony_ci ret); 65762306a36Sopenharmony_ci goto err_bat_irq; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci break; 66062306a36Sopenharmony_ci case -EINVAL: 66162306a36Sopenharmony_ci case -ENODEV: 66262306a36Sopenharmony_ci /* ignore missing usb-phy, it's optional */ 66362306a36Sopenharmony_ci power->usb_phy = NULL; 66462306a36Sopenharmony_ci ret = 0; 66562306a36Sopenharmony_ci break; 66662306a36Sopenharmony_ci default: 66762306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to find USB phy: %d\n", ret); 66862306a36Sopenharmony_ci fallthrough; 66962306a36Sopenharmony_ci case -EPROBE_DEFER: 67062306a36Sopenharmony_ci goto err_bat_irq; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci return ret; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cierr_bat_irq: 67662306a36Sopenharmony_ci --i; 67762306a36Sopenharmony_ci for (; i >= 0; i--) { 67862306a36Sopenharmony_ci irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]); 67962306a36Sopenharmony_ci free_irq(irq, power); 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC")); 68262306a36Sopenharmony_ci free_irq(irq, power); 68362306a36Sopenharmony_cierr_syslo: 68462306a36Sopenharmony_ci irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO")); 68562306a36Sopenharmony_ci free_irq(irq, power); 68662306a36Sopenharmony_cierr_battery: 68762306a36Sopenharmony_ci if (power->have_battery) 68862306a36Sopenharmony_ci power_supply_unregister(power->battery); 68962306a36Sopenharmony_cierr_usb: 69062306a36Sopenharmony_ci power_supply_unregister(power->usb); 69162306a36Sopenharmony_cierr_wall: 69262306a36Sopenharmony_ci power_supply_unregister(power->wall); 69362306a36Sopenharmony_cierr: 69462306a36Sopenharmony_ci return ret; 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistatic int wm831x_power_remove(struct platform_device *pdev) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci struct wm831x_power *wm831x_power = platform_get_drvdata(pdev); 70062306a36Sopenharmony_ci struct wm831x *wm831x = wm831x_power->wm831x; 70162306a36Sopenharmony_ci int irq, i; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci if (wm831x_power->usb_phy) { 70462306a36Sopenharmony_ci usb_unregister_notifier(wm831x_power->usb_phy, 70562306a36Sopenharmony_ci &wm831x_power->usb_notify); 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) { 70962306a36Sopenharmony_ci irq = wm831x_irq(wm831x, 71062306a36Sopenharmony_ci platform_get_irq_byname(pdev, 71162306a36Sopenharmony_ci wm831x_bat_irqs[i])); 71262306a36Sopenharmony_ci free_irq(irq, wm831x_power); 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC")); 71662306a36Sopenharmony_ci free_irq(irq, wm831x_power); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO")); 71962306a36Sopenharmony_ci free_irq(irq, wm831x_power); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci if (wm831x_power->have_battery) 72262306a36Sopenharmony_ci power_supply_unregister(wm831x_power->battery); 72362306a36Sopenharmony_ci power_supply_unregister(wm831x_power->wall); 72462306a36Sopenharmony_ci power_supply_unregister(wm831x_power->usb); 72562306a36Sopenharmony_ci return 0; 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_cistatic struct platform_driver wm831x_power_driver = { 72962306a36Sopenharmony_ci .probe = wm831x_power_probe, 73062306a36Sopenharmony_ci .remove = wm831x_power_remove, 73162306a36Sopenharmony_ci .driver = { 73262306a36Sopenharmony_ci .name = "wm831x-power", 73362306a36Sopenharmony_ci }, 73462306a36Sopenharmony_ci}; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cimodule_platform_driver(wm831x_power_driver); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ciMODULE_DESCRIPTION("Power supply driver for WM831x PMICs"); 73962306a36Sopenharmony_ciMODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 74062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 74162306a36Sopenharmony_ciMODULE_ALIAS("platform:wm831x-power"); 742