18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Battery driver for wm8350 PMIC 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2007, 2008 Wolfson Microelectronics PLC. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on OLPC Battery Driver 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Copyright 2006 David Woodhouse <dwmw2@infradead.org> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/err.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 168c2ecf20Sopenharmony_ci#include <linux/mfd/wm8350/supply.h> 178c2ecf20Sopenharmony_ci#include <linux/mfd/wm8350/core.h> 188c2ecf20Sopenharmony_ci#include <linux/mfd/wm8350/comparator.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic int wm8350_read_battery_uvolts(struct wm8350 *wm8350) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci return wm8350_read_auxadc(wm8350, WM8350_AUXADC_BATT, 0, 0) 238c2ecf20Sopenharmony_ci * WM8350_AUX_COEFF; 248c2ecf20Sopenharmony_ci} 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic int wm8350_read_line_uvolts(struct wm8350 *wm8350) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci return wm8350_read_auxadc(wm8350, WM8350_AUXADC_LINE, 0, 0) 298c2ecf20Sopenharmony_ci * WM8350_AUX_COEFF; 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int wm8350_read_usb_uvolts(struct wm8350 *wm8350) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci return wm8350_read_auxadc(wm8350, WM8350_AUXADC_USB, 0, 0) 358c2ecf20Sopenharmony_ci * WM8350_AUX_COEFF; 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define WM8350_BATT_SUPPLY 1 398c2ecf20Sopenharmony_ci#define WM8350_USB_SUPPLY 2 408c2ecf20Sopenharmony_ci#define WM8350_LINE_SUPPLY 4 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic inline int wm8350_charge_time_min(struct wm8350 *wm8350, int min) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci if (!wm8350->power.rev_g_coeff) 458c2ecf20Sopenharmony_ci return (((min - 30) / 15) & 0xf) << 8; 468c2ecf20Sopenharmony_ci else 478c2ecf20Sopenharmony_ci return (((min - 30) / 30) & 0xf) << 8; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int wm8350_get_supplies(struct wm8350 *wm8350) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci u16 sm, ov, co, chrg; 538c2ecf20Sopenharmony_ci int supplies = 0; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci sm = wm8350_reg_read(wm8350, WM8350_STATE_MACHINE_STATUS); 568c2ecf20Sopenharmony_ci ov = wm8350_reg_read(wm8350, WM8350_MISC_OVERRIDES); 578c2ecf20Sopenharmony_ci co = wm8350_reg_read(wm8350, WM8350_COMPARATOR_OVERRIDES); 588c2ecf20Sopenharmony_ci chrg = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci /* USB_SM */ 618c2ecf20Sopenharmony_ci sm = (sm & WM8350_USB_SM_MASK) >> WM8350_USB_SM_SHIFT; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci /* CHG_ISEL */ 648c2ecf20Sopenharmony_ci chrg &= WM8350_CHG_ISEL_MASK; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* If the USB state machine is active then we're using that with or 678c2ecf20Sopenharmony_ci * without battery, otherwise check for wall supply */ 688c2ecf20Sopenharmony_ci if (((sm == WM8350_USB_SM_100_SLV) || 698c2ecf20Sopenharmony_ci (sm == WM8350_USB_SM_500_SLV) || 708c2ecf20Sopenharmony_ci (sm == WM8350_USB_SM_STDBY_SLV)) 718c2ecf20Sopenharmony_ci && !(ov & WM8350_USB_LIMIT_OVRDE)) 728c2ecf20Sopenharmony_ci supplies = WM8350_USB_SUPPLY; 738c2ecf20Sopenharmony_ci else if (((sm == WM8350_USB_SM_100_SLV) || 748c2ecf20Sopenharmony_ci (sm == WM8350_USB_SM_500_SLV) || 758c2ecf20Sopenharmony_ci (sm == WM8350_USB_SM_STDBY_SLV)) 768c2ecf20Sopenharmony_ci && (ov & WM8350_USB_LIMIT_OVRDE) && (chrg == 0)) 778c2ecf20Sopenharmony_ci supplies = WM8350_USB_SUPPLY | WM8350_BATT_SUPPLY; 788c2ecf20Sopenharmony_ci else if (co & WM8350_WALL_FB_OVRDE) 798c2ecf20Sopenharmony_ci supplies = WM8350_LINE_SUPPLY; 808c2ecf20Sopenharmony_ci else 818c2ecf20Sopenharmony_ci supplies = WM8350_BATT_SUPPLY; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return supplies; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic int wm8350_charger_config(struct wm8350 *wm8350, 878c2ecf20Sopenharmony_ci struct wm8350_charger_policy *policy) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci u16 reg, eoc_mA, fast_limit_mA; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (!policy) { 928c2ecf20Sopenharmony_ci dev_warn(wm8350->dev, 938c2ecf20Sopenharmony_ci "No charger policy, charger not configured.\n"); 948c2ecf20Sopenharmony_ci return -EINVAL; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci /* make sure USB fast charge current is not > 500mA */ 988c2ecf20Sopenharmony_ci if (policy->fast_limit_USB_mA > 500) { 998c2ecf20Sopenharmony_ci dev_err(wm8350->dev, "USB fast charge > 500mA\n"); 1008c2ecf20Sopenharmony_ci return -EINVAL; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci eoc_mA = WM8350_CHG_EOC_mA(policy->eoc_mA); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci wm8350_reg_unlock(wm8350); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci reg = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1) 1088c2ecf20Sopenharmony_ci & WM8350_CHG_ENA_R168; 1098c2ecf20Sopenharmony_ci wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1, 1108c2ecf20Sopenharmony_ci reg | eoc_mA | policy->trickle_start_mV | 1118c2ecf20Sopenharmony_ci WM8350_CHG_TRICKLE_TEMP_CHOKE | 1128c2ecf20Sopenharmony_ci WM8350_CHG_TRICKLE_USB_CHOKE | 1138c2ecf20Sopenharmony_ci WM8350_CHG_FAST_USB_THROTTLE); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (wm8350_get_supplies(wm8350) & WM8350_USB_SUPPLY) { 1168c2ecf20Sopenharmony_ci fast_limit_mA = 1178c2ecf20Sopenharmony_ci WM8350_CHG_FAST_LIMIT_mA(policy->fast_limit_USB_mA); 1188c2ecf20Sopenharmony_ci wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2, 1198c2ecf20Sopenharmony_ci policy->charge_mV | policy->trickle_charge_USB_mA | 1208c2ecf20Sopenharmony_ci fast_limit_mA | wm8350_charge_time_min(wm8350, 1218c2ecf20Sopenharmony_ci policy->charge_timeout)); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci } else { 1248c2ecf20Sopenharmony_ci fast_limit_mA = 1258c2ecf20Sopenharmony_ci WM8350_CHG_FAST_LIMIT_mA(policy->fast_limit_mA); 1268c2ecf20Sopenharmony_ci wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2, 1278c2ecf20Sopenharmony_ci policy->charge_mV | policy->trickle_charge_mA | 1288c2ecf20Sopenharmony_ci fast_limit_mA | wm8350_charge_time_min(wm8350, 1298c2ecf20Sopenharmony_ci policy->charge_timeout)); 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci wm8350_reg_lock(wm8350); 1338c2ecf20Sopenharmony_ci return 0; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic int wm8350_batt_status(struct wm8350 *wm8350) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci u16 state; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci state = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2); 1418c2ecf20Sopenharmony_ci state &= WM8350_CHG_STS_MASK; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci switch (state) { 1448c2ecf20Sopenharmony_ci case WM8350_CHG_STS_OFF: 1458c2ecf20Sopenharmony_ci return POWER_SUPPLY_STATUS_DISCHARGING; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci case WM8350_CHG_STS_TRICKLE: 1488c2ecf20Sopenharmony_ci case WM8350_CHG_STS_FAST: 1498c2ecf20Sopenharmony_ci return POWER_SUPPLY_STATUS_CHARGING; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci default: 1528c2ecf20Sopenharmony_ci return POWER_SUPPLY_STATUS_UNKNOWN; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic ssize_t charger_state_show(struct device *dev, 1578c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct wm8350 *wm8350 = dev_get_drvdata(dev); 1608c2ecf20Sopenharmony_ci char *charge; 1618c2ecf20Sopenharmony_ci int state; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci state = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2) & 1648c2ecf20Sopenharmony_ci WM8350_CHG_STS_MASK; 1658c2ecf20Sopenharmony_ci switch (state) { 1668c2ecf20Sopenharmony_ci case WM8350_CHG_STS_OFF: 1678c2ecf20Sopenharmony_ci charge = "Charger Off"; 1688c2ecf20Sopenharmony_ci break; 1698c2ecf20Sopenharmony_ci case WM8350_CHG_STS_TRICKLE: 1708c2ecf20Sopenharmony_ci charge = "Trickle Charging"; 1718c2ecf20Sopenharmony_ci break; 1728c2ecf20Sopenharmony_ci case WM8350_CHG_STS_FAST: 1738c2ecf20Sopenharmony_ci charge = "Fast Charging"; 1748c2ecf20Sopenharmony_ci break; 1758c2ecf20Sopenharmony_ci default: 1768c2ecf20Sopenharmony_ci return 0; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", charge); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(charger_state); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic irqreturn_t wm8350_charger_handler(int irq, void *data) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct wm8350 *wm8350 = data; 1878c2ecf20Sopenharmony_ci struct wm8350_power *power = &wm8350->power; 1888c2ecf20Sopenharmony_ci struct wm8350_charger_policy *policy = power->policy; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci switch (irq - wm8350->irq_base) { 1918c2ecf20Sopenharmony_ci case WM8350_IRQ_CHG_BAT_FAIL: 1928c2ecf20Sopenharmony_ci dev_err(wm8350->dev, "battery failed\n"); 1938c2ecf20Sopenharmony_ci break; 1948c2ecf20Sopenharmony_ci case WM8350_IRQ_CHG_TO: 1958c2ecf20Sopenharmony_ci dev_err(wm8350->dev, "charger timeout\n"); 1968c2ecf20Sopenharmony_ci power_supply_changed(power->battery); 1978c2ecf20Sopenharmony_ci break; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci case WM8350_IRQ_CHG_BAT_HOT: 2008c2ecf20Sopenharmony_ci case WM8350_IRQ_CHG_BAT_COLD: 2018c2ecf20Sopenharmony_ci case WM8350_IRQ_CHG_START: 2028c2ecf20Sopenharmony_ci case WM8350_IRQ_CHG_END: 2038c2ecf20Sopenharmony_ci power_supply_changed(power->battery); 2048c2ecf20Sopenharmony_ci break; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci case WM8350_IRQ_CHG_FAST_RDY: 2078c2ecf20Sopenharmony_ci dev_dbg(wm8350->dev, "fast charger ready\n"); 2088c2ecf20Sopenharmony_ci wm8350_charger_config(wm8350, policy); 2098c2ecf20Sopenharmony_ci wm8350_reg_unlock(wm8350); 2108c2ecf20Sopenharmony_ci wm8350_set_bits(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1, 2118c2ecf20Sopenharmony_ci WM8350_CHG_FAST); 2128c2ecf20Sopenharmony_ci wm8350_reg_lock(wm8350); 2138c2ecf20Sopenharmony_ci break; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci case WM8350_IRQ_CHG_VBATT_LT_3P9: 2168c2ecf20Sopenharmony_ci dev_warn(wm8350->dev, "battery < 3.9V\n"); 2178c2ecf20Sopenharmony_ci break; 2188c2ecf20Sopenharmony_ci case WM8350_IRQ_CHG_VBATT_LT_3P1: 2198c2ecf20Sopenharmony_ci dev_warn(wm8350->dev, "battery < 3.1V\n"); 2208c2ecf20Sopenharmony_ci break; 2218c2ecf20Sopenharmony_ci case WM8350_IRQ_CHG_VBATT_LT_2P85: 2228c2ecf20Sopenharmony_ci dev_warn(wm8350->dev, "battery < 2.85V\n"); 2238c2ecf20Sopenharmony_ci break; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* Supply change. We will overnotify but it should do 2268c2ecf20Sopenharmony_ci * no harm. */ 2278c2ecf20Sopenharmony_ci case WM8350_IRQ_EXT_USB_FB: 2288c2ecf20Sopenharmony_ci case WM8350_IRQ_EXT_WALL_FB: 2298c2ecf20Sopenharmony_ci wm8350_charger_config(wm8350, policy); 2308c2ecf20Sopenharmony_ci fallthrough; 2318c2ecf20Sopenharmony_ci case WM8350_IRQ_EXT_BAT_FB: 2328c2ecf20Sopenharmony_ci power_supply_changed(power->battery); 2338c2ecf20Sopenharmony_ci power_supply_changed(power->usb); 2348c2ecf20Sopenharmony_ci power_supply_changed(power->ac); 2358c2ecf20Sopenharmony_ci break; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci default: 2388c2ecf20Sopenharmony_ci dev_err(wm8350->dev, "Unknown interrupt %d\n", irq); 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/********************************************************************* 2458c2ecf20Sopenharmony_ci * AC Power 2468c2ecf20Sopenharmony_ci *********************************************************************/ 2478c2ecf20Sopenharmony_cistatic int wm8350_ac_get_prop(struct power_supply *psy, 2488c2ecf20Sopenharmony_ci enum power_supply_property psp, 2498c2ecf20Sopenharmony_ci union power_supply_propval *val) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct wm8350 *wm8350 = dev_get_drvdata(psy->dev.parent); 2528c2ecf20Sopenharmony_ci int ret = 0; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci switch (psp) { 2558c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 2568c2ecf20Sopenharmony_ci val->intval = !!(wm8350_get_supplies(wm8350) & 2578c2ecf20Sopenharmony_ci WM8350_LINE_SUPPLY); 2588c2ecf20Sopenharmony_ci break; 2598c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 2608c2ecf20Sopenharmony_ci val->intval = wm8350_read_line_uvolts(wm8350); 2618c2ecf20Sopenharmony_ci break; 2628c2ecf20Sopenharmony_ci default: 2638c2ecf20Sopenharmony_ci ret = -EINVAL; 2648c2ecf20Sopenharmony_ci break; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci return ret; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic enum power_supply_property wm8350_ac_props[] = { 2708c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 2718c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 2728c2ecf20Sopenharmony_ci}; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci/********************************************************************* 2758c2ecf20Sopenharmony_ci * USB Power 2768c2ecf20Sopenharmony_ci *********************************************************************/ 2778c2ecf20Sopenharmony_cistatic int wm8350_usb_get_prop(struct power_supply *psy, 2788c2ecf20Sopenharmony_ci enum power_supply_property psp, 2798c2ecf20Sopenharmony_ci union power_supply_propval *val) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci struct wm8350 *wm8350 = dev_get_drvdata(psy->dev.parent); 2828c2ecf20Sopenharmony_ci int ret = 0; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci switch (psp) { 2858c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 2868c2ecf20Sopenharmony_ci val->intval = !!(wm8350_get_supplies(wm8350) & 2878c2ecf20Sopenharmony_ci WM8350_USB_SUPPLY); 2888c2ecf20Sopenharmony_ci break; 2898c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 2908c2ecf20Sopenharmony_ci val->intval = wm8350_read_usb_uvolts(wm8350); 2918c2ecf20Sopenharmony_ci break; 2928c2ecf20Sopenharmony_ci default: 2938c2ecf20Sopenharmony_ci ret = -EINVAL; 2948c2ecf20Sopenharmony_ci break; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci return ret; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic enum power_supply_property wm8350_usb_props[] = { 3008c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 3018c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 3028c2ecf20Sopenharmony_ci}; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci/********************************************************************* 3058c2ecf20Sopenharmony_ci * Battery properties 3068c2ecf20Sopenharmony_ci *********************************************************************/ 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic int wm8350_bat_check_health(struct wm8350 *wm8350) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci u16 reg; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (wm8350_read_battery_uvolts(wm8350) < 2850000) 3138c2ecf20Sopenharmony_ci return POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci reg = wm8350_reg_read(wm8350, WM8350_CHARGER_OVERRIDES); 3168c2ecf20Sopenharmony_ci if (reg & WM8350_CHG_BATT_HOT_OVRDE) 3178c2ecf20Sopenharmony_ci return POWER_SUPPLY_HEALTH_OVERHEAT; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (reg & WM8350_CHG_BATT_COLD_OVRDE) 3208c2ecf20Sopenharmony_ci return POWER_SUPPLY_HEALTH_COLD; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci return POWER_SUPPLY_HEALTH_GOOD; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic int wm8350_bat_get_charge_type(struct wm8350 *wm8350) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci int state; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci state = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2) & 3308c2ecf20Sopenharmony_ci WM8350_CHG_STS_MASK; 3318c2ecf20Sopenharmony_ci switch (state) { 3328c2ecf20Sopenharmony_ci case WM8350_CHG_STS_OFF: 3338c2ecf20Sopenharmony_ci return POWER_SUPPLY_CHARGE_TYPE_NONE; 3348c2ecf20Sopenharmony_ci case WM8350_CHG_STS_TRICKLE: 3358c2ecf20Sopenharmony_ci return POWER_SUPPLY_CHARGE_TYPE_TRICKLE; 3368c2ecf20Sopenharmony_ci case WM8350_CHG_STS_FAST: 3378c2ecf20Sopenharmony_ci return POWER_SUPPLY_CHARGE_TYPE_FAST; 3388c2ecf20Sopenharmony_ci default: 3398c2ecf20Sopenharmony_ci return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic int wm8350_bat_get_property(struct power_supply *psy, 3448c2ecf20Sopenharmony_ci enum power_supply_property psp, 3458c2ecf20Sopenharmony_ci union power_supply_propval *val) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci struct wm8350 *wm8350 = dev_get_drvdata(psy->dev.parent); 3488c2ecf20Sopenharmony_ci int ret = 0; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci switch (psp) { 3518c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 3528c2ecf20Sopenharmony_ci val->intval = wm8350_batt_status(wm8350); 3538c2ecf20Sopenharmony_ci break; 3548c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 3558c2ecf20Sopenharmony_ci val->intval = !!(wm8350_get_supplies(wm8350) & 3568c2ecf20Sopenharmony_ci WM8350_BATT_SUPPLY); 3578c2ecf20Sopenharmony_ci break; 3588c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 3598c2ecf20Sopenharmony_ci val->intval = wm8350_read_battery_uvolts(wm8350); 3608c2ecf20Sopenharmony_ci break; 3618c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_HEALTH: 3628c2ecf20Sopenharmony_ci val->intval = wm8350_bat_check_health(wm8350); 3638c2ecf20Sopenharmony_ci break; 3648c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_TYPE: 3658c2ecf20Sopenharmony_ci val->intval = wm8350_bat_get_charge_type(wm8350); 3668c2ecf20Sopenharmony_ci break; 3678c2ecf20Sopenharmony_ci default: 3688c2ecf20Sopenharmony_ci ret = -EINVAL; 3698c2ecf20Sopenharmony_ci break; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci return ret; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic enum power_supply_property wm8350_bat_props[] = { 3768c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 3778c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 3788c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 3798c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_HEALTH, 3808c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_TYPE, 3818c2ecf20Sopenharmony_ci}; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic const struct power_supply_desc wm8350_ac_desc = { 3848c2ecf20Sopenharmony_ci .name = "wm8350-ac", 3858c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_MAINS, 3868c2ecf20Sopenharmony_ci .properties = wm8350_ac_props, 3878c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(wm8350_ac_props), 3888c2ecf20Sopenharmony_ci .get_property = wm8350_ac_get_prop, 3898c2ecf20Sopenharmony_ci}; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic const struct power_supply_desc wm8350_battery_desc = { 3928c2ecf20Sopenharmony_ci .name = "wm8350-battery", 3938c2ecf20Sopenharmony_ci .properties = wm8350_bat_props, 3948c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(wm8350_bat_props), 3958c2ecf20Sopenharmony_ci .get_property = wm8350_bat_get_property, 3968c2ecf20Sopenharmony_ci .use_for_apm = 1, 3978c2ecf20Sopenharmony_ci}; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic const struct power_supply_desc wm8350_usb_desc = { 4008c2ecf20Sopenharmony_ci .name = "wm8350-usb", 4018c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_USB, 4028c2ecf20Sopenharmony_ci .properties = wm8350_usb_props, 4038c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(wm8350_usb_props), 4048c2ecf20Sopenharmony_ci .get_property = wm8350_usb_get_prop, 4058c2ecf20Sopenharmony_ci}; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci/********************************************************************* 4088c2ecf20Sopenharmony_ci * Initialisation 4098c2ecf20Sopenharmony_ci *********************************************************************/ 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic int wm8350_init_charger(struct wm8350 *wm8350) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci int ret; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* register our interest in charger events */ 4168c2ecf20Sopenharmony_ci ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT, 4178c2ecf20Sopenharmony_ci wm8350_charger_handler, 0, "Battery hot", wm8350); 4188c2ecf20Sopenharmony_ci if (ret) 4198c2ecf20Sopenharmony_ci goto err; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD, 4228c2ecf20Sopenharmony_ci wm8350_charger_handler, 0, "Battery cold", wm8350); 4238c2ecf20Sopenharmony_ci if (ret) 4248c2ecf20Sopenharmony_ci goto free_chg_bat_hot; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL, 4278c2ecf20Sopenharmony_ci wm8350_charger_handler, 0, "Battery fail", wm8350); 4288c2ecf20Sopenharmony_ci if (ret) 4298c2ecf20Sopenharmony_ci goto free_chg_bat_cold; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_TO, 4328c2ecf20Sopenharmony_ci wm8350_charger_handler, 0, 4338c2ecf20Sopenharmony_ci "Charger timeout", wm8350); 4348c2ecf20Sopenharmony_ci if (ret) 4358c2ecf20Sopenharmony_ci goto free_chg_bat_fail; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_END, 4388c2ecf20Sopenharmony_ci wm8350_charger_handler, 0, 4398c2ecf20Sopenharmony_ci "Charge end", wm8350); 4408c2ecf20Sopenharmony_ci if (ret) 4418c2ecf20Sopenharmony_ci goto free_chg_to; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_START, 4448c2ecf20Sopenharmony_ci wm8350_charger_handler, 0, 4458c2ecf20Sopenharmony_ci "Charge start", wm8350); 4468c2ecf20Sopenharmony_ci if (ret) 4478c2ecf20Sopenharmony_ci goto free_chg_end; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY, 4508c2ecf20Sopenharmony_ci wm8350_charger_handler, 0, 4518c2ecf20Sopenharmony_ci "Fast charge ready", wm8350); 4528c2ecf20Sopenharmony_ci if (ret) 4538c2ecf20Sopenharmony_ci goto free_chg_start; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9, 4568c2ecf20Sopenharmony_ci wm8350_charger_handler, 0, 4578c2ecf20Sopenharmony_ci "Battery <3.9V", wm8350); 4588c2ecf20Sopenharmony_ci if (ret) 4598c2ecf20Sopenharmony_ci goto free_chg_fast_rdy; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1, 4628c2ecf20Sopenharmony_ci wm8350_charger_handler, 0, 4638c2ecf20Sopenharmony_ci "Battery <3.1V", wm8350); 4648c2ecf20Sopenharmony_ci if (ret) 4658c2ecf20Sopenharmony_ci goto free_chg_vbatt_lt_3p9; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85, 4688c2ecf20Sopenharmony_ci wm8350_charger_handler, 0, 4698c2ecf20Sopenharmony_ci "Battery <2.85V", wm8350); 4708c2ecf20Sopenharmony_ci if (ret) 4718c2ecf20Sopenharmony_ci goto free_chg_vbatt_lt_3p1; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* and supply change events */ 4748c2ecf20Sopenharmony_ci ret = wm8350_register_irq(wm8350, WM8350_IRQ_EXT_USB_FB, 4758c2ecf20Sopenharmony_ci wm8350_charger_handler, 0, "USB", wm8350); 4768c2ecf20Sopenharmony_ci if (ret) 4778c2ecf20Sopenharmony_ci goto free_chg_vbatt_lt_2p85; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci ret = wm8350_register_irq(wm8350, WM8350_IRQ_EXT_WALL_FB, 4808c2ecf20Sopenharmony_ci wm8350_charger_handler, 0, "Wall", wm8350); 4818c2ecf20Sopenharmony_ci if (ret) 4828c2ecf20Sopenharmony_ci goto free_ext_usb_fb; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci ret = wm8350_register_irq(wm8350, WM8350_IRQ_EXT_BAT_FB, 4858c2ecf20Sopenharmony_ci wm8350_charger_handler, 0, "Battery", wm8350); 4868c2ecf20Sopenharmony_ci if (ret) 4878c2ecf20Sopenharmony_ci goto free_ext_wall_fb; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci return 0; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cifree_ext_wall_fb: 4928c2ecf20Sopenharmony_ci wm8350_free_irq(wm8350, WM8350_IRQ_EXT_WALL_FB, wm8350); 4938c2ecf20Sopenharmony_cifree_ext_usb_fb: 4948c2ecf20Sopenharmony_ci wm8350_free_irq(wm8350, WM8350_IRQ_EXT_USB_FB, wm8350); 4958c2ecf20Sopenharmony_cifree_chg_vbatt_lt_2p85: 4968c2ecf20Sopenharmony_ci wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85, wm8350); 4978c2ecf20Sopenharmony_cifree_chg_vbatt_lt_3p1: 4988c2ecf20Sopenharmony_ci wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1, wm8350); 4998c2ecf20Sopenharmony_cifree_chg_vbatt_lt_3p9: 5008c2ecf20Sopenharmony_ci wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9, wm8350); 5018c2ecf20Sopenharmony_cifree_chg_fast_rdy: 5028c2ecf20Sopenharmony_ci wm8350_free_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY, wm8350); 5038c2ecf20Sopenharmony_cifree_chg_start: 5048c2ecf20Sopenharmony_ci wm8350_free_irq(wm8350, WM8350_IRQ_CHG_START, wm8350); 5058c2ecf20Sopenharmony_cifree_chg_end: 5068c2ecf20Sopenharmony_ci wm8350_free_irq(wm8350, WM8350_IRQ_CHG_END, wm8350); 5078c2ecf20Sopenharmony_cifree_chg_to: 5088c2ecf20Sopenharmony_ci wm8350_free_irq(wm8350, WM8350_IRQ_CHG_TO, wm8350); 5098c2ecf20Sopenharmony_cifree_chg_bat_fail: 5108c2ecf20Sopenharmony_ci wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL, wm8350); 5118c2ecf20Sopenharmony_cifree_chg_bat_cold: 5128c2ecf20Sopenharmony_ci wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD, wm8350); 5138c2ecf20Sopenharmony_cifree_chg_bat_hot: 5148c2ecf20Sopenharmony_ci wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT, wm8350); 5158c2ecf20Sopenharmony_cierr: 5168c2ecf20Sopenharmony_ci return ret; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic void free_charger_irq(struct wm8350 *wm8350) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT, wm8350); 5228c2ecf20Sopenharmony_ci wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD, wm8350); 5238c2ecf20Sopenharmony_ci wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL, wm8350); 5248c2ecf20Sopenharmony_ci wm8350_free_irq(wm8350, WM8350_IRQ_CHG_TO, wm8350); 5258c2ecf20Sopenharmony_ci wm8350_free_irq(wm8350, WM8350_IRQ_CHG_END, wm8350); 5268c2ecf20Sopenharmony_ci wm8350_free_irq(wm8350, WM8350_IRQ_CHG_START, wm8350); 5278c2ecf20Sopenharmony_ci wm8350_free_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY, wm8350); 5288c2ecf20Sopenharmony_ci wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9, wm8350); 5298c2ecf20Sopenharmony_ci wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1, wm8350); 5308c2ecf20Sopenharmony_ci wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85, wm8350); 5318c2ecf20Sopenharmony_ci wm8350_free_irq(wm8350, WM8350_IRQ_EXT_USB_FB, wm8350); 5328c2ecf20Sopenharmony_ci wm8350_free_irq(wm8350, WM8350_IRQ_EXT_WALL_FB, wm8350); 5338c2ecf20Sopenharmony_ci wm8350_free_irq(wm8350, WM8350_IRQ_EXT_BAT_FB, wm8350); 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cistatic int wm8350_power_probe(struct platform_device *pdev) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci struct wm8350 *wm8350 = platform_get_drvdata(pdev); 5398c2ecf20Sopenharmony_ci struct wm8350_power *power = &wm8350->power; 5408c2ecf20Sopenharmony_ci struct wm8350_charger_policy *policy = power->policy; 5418c2ecf20Sopenharmony_ci int ret; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci power->ac = power_supply_register(&pdev->dev, &wm8350_ac_desc, NULL); 5448c2ecf20Sopenharmony_ci if (IS_ERR(power->ac)) 5458c2ecf20Sopenharmony_ci return PTR_ERR(power->ac); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci power->battery = power_supply_register(&pdev->dev, &wm8350_battery_desc, 5488c2ecf20Sopenharmony_ci NULL); 5498c2ecf20Sopenharmony_ci if (IS_ERR(power->battery)) { 5508c2ecf20Sopenharmony_ci ret = PTR_ERR(power->battery); 5518c2ecf20Sopenharmony_ci goto battery_failed; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci power->usb = power_supply_register(&pdev->dev, &wm8350_usb_desc, NULL); 5558c2ecf20Sopenharmony_ci if (IS_ERR(power->usb)) { 5568c2ecf20Sopenharmony_ci ret = PTR_ERR(power->usb); 5578c2ecf20Sopenharmony_ci goto usb_failed; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci ret = device_create_file(&pdev->dev, &dev_attr_charger_state); 5618c2ecf20Sopenharmony_ci if (ret < 0) 5628c2ecf20Sopenharmony_ci dev_warn(wm8350->dev, "failed to add charge sysfs: %d\n", ret); 5638c2ecf20Sopenharmony_ci ret = 0; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci wm8350_init_charger(wm8350); 5668c2ecf20Sopenharmony_ci if (wm8350_charger_config(wm8350, policy) == 0) { 5678c2ecf20Sopenharmony_ci wm8350_reg_unlock(wm8350); 5688c2ecf20Sopenharmony_ci wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CHG_ENA); 5698c2ecf20Sopenharmony_ci wm8350_reg_lock(wm8350); 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci return ret; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ciusb_failed: 5758c2ecf20Sopenharmony_ci power_supply_unregister(power->battery); 5768c2ecf20Sopenharmony_cibattery_failed: 5778c2ecf20Sopenharmony_ci power_supply_unregister(power->ac); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci return ret; 5808c2ecf20Sopenharmony_ci} 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_cistatic int wm8350_power_remove(struct platform_device *pdev) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci struct wm8350 *wm8350 = platform_get_drvdata(pdev); 5858c2ecf20Sopenharmony_ci struct wm8350_power *power = &wm8350->power; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci free_charger_irq(wm8350); 5888c2ecf20Sopenharmony_ci device_remove_file(&pdev->dev, &dev_attr_charger_state); 5898c2ecf20Sopenharmony_ci power_supply_unregister(power->battery); 5908c2ecf20Sopenharmony_ci power_supply_unregister(power->ac); 5918c2ecf20Sopenharmony_ci power_supply_unregister(power->usb); 5928c2ecf20Sopenharmony_ci return 0; 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic struct platform_driver wm8350_power_driver = { 5968c2ecf20Sopenharmony_ci .probe = wm8350_power_probe, 5978c2ecf20Sopenharmony_ci .remove = wm8350_power_remove, 5988c2ecf20Sopenharmony_ci .driver = { 5998c2ecf20Sopenharmony_ci .name = "wm8350-power", 6008c2ecf20Sopenharmony_ci }, 6018c2ecf20Sopenharmony_ci}; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cimodule_platform_driver(wm8350_power_driver); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 6068c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Power supply driver for WM8350"); 6078c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:wm8350-power"); 608