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