591 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Battery driver for wm8350 PMIC4 *5 * Copyright 2007, 2008 Wolfson Microelectronics PLC.6 *7 * Based on OLPC Battery Driver8 *9 * Copyright 2006 David Woodhouse <dwmw2@infradead.org>10 */11 12#include <linux/module.h>13#include <linux/err.h>14#include <linux/platform_device.h>15#include <linux/power_supply.h>16#include <linux/mfd/wm8350/supply.h>17#include <linux/mfd/wm8350/core.h>18#include <linux/mfd/wm8350/comparator.h>19 20static int wm8350_read_battery_uvolts(struct wm8350 *wm8350)21{22 return wm8350_read_auxadc(wm8350, WM8350_AUXADC_BATT, 0, 0)23 * WM8350_AUX_COEFF;24}25 26static int wm8350_read_line_uvolts(struct wm8350 *wm8350)27{28 return wm8350_read_auxadc(wm8350, WM8350_AUXADC_LINE, 0, 0)29 * WM8350_AUX_COEFF;30}31 32static int wm8350_read_usb_uvolts(struct wm8350 *wm8350)33{34 return wm8350_read_auxadc(wm8350, WM8350_AUXADC_USB, 0, 0)35 * WM8350_AUX_COEFF;36}37 38#define WM8350_BATT_SUPPLY 139#define WM8350_USB_SUPPLY 240#define WM8350_LINE_SUPPLY 441 42static inline int wm8350_charge_time_min(struct wm8350 *wm8350, int min)43{44 if (!wm8350->power.rev_g_coeff)45 return (((min - 30) / 15) & 0xf) << 8;46 else47 return (((min - 30) / 30) & 0xf) << 8;48}49 50static int wm8350_get_supplies(struct wm8350 *wm8350)51{52 u16 sm, ov, co, chrg;53 int supplies = 0;54 55 sm = wm8350_reg_read(wm8350, WM8350_STATE_MACHINE_STATUS);56 ov = wm8350_reg_read(wm8350, WM8350_MISC_OVERRIDES);57 co = wm8350_reg_read(wm8350, WM8350_COMPARATOR_OVERRIDES);58 chrg = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2);59 60 /* USB_SM */61 sm = (sm & WM8350_USB_SM_MASK) >> WM8350_USB_SM_SHIFT;62 63 /* CHG_ISEL */64 chrg &= WM8350_CHG_ISEL_MASK;65 66 /* If the USB state machine is active then we're using that with or67 * without battery, otherwise check for wall supply */68 if (((sm == WM8350_USB_SM_100_SLV) ||69 (sm == WM8350_USB_SM_500_SLV) ||70 (sm == WM8350_USB_SM_STDBY_SLV))71 && !(ov & WM8350_USB_LIMIT_OVRDE))72 supplies = WM8350_USB_SUPPLY;73 else if (((sm == WM8350_USB_SM_100_SLV) ||74 (sm == WM8350_USB_SM_500_SLV) ||75 (sm == WM8350_USB_SM_STDBY_SLV))76 && (ov & WM8350_USB_LIMIT_OVRDE) && (chrg == 0))77 supplies = WM8350_USB_SUPPLY | WM8350_BATT_SUPPLY;78 else if (co & WM8350_WALL_FB_OVRDE)79 supplies = WM8350_LINE_SUPPLY;80 else81 supplies = WM8350_BATT_SUPPLY;82 83 return supplies;84}85 86static int wm8350_charger_config(struct wm8350 *wm8350,87 struct wm8350_charger_policy *policy)88{89 u16 reg, eoc_mA, fast_limit_mA;90 91 if (!policy) {92 dev_warn(wm8350->dev,93 "No charger policy, charger not configured.\n");94 return -EINVAL;95 }96 97 /* make sure USB fast charge current is not > 500mA */98 if (policy->fast_limit_USB_mA > 500) {99 dev_err(wm8350->dev, "USB fast charge > 500mA\n");100 return -EINVAL;101 }102 103 eoc_mA = WM8350_CHG_EOC_mA(policy->eoc_mA);104 105 wm8350_reg_unlock(wm8350);106 107 reg = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1)108 & WM8350_CHG_ENA_R168;109 wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1,110 reg | eoc_mA | policy->trickle_start_mV |111 WM8350_CHG_TRICKLE_TEMP_CHOKE |112 WM8350_CHG_TRICKLE_USB_CHOKE |113 WM8350_CHG_FAST_USB_THROTTLE);114 115 if (wm8350_get_supplies(wm8350) & WM8350_USB_SUPPLY) {116 fast_limit_mA =117 WM8350_CHG_FAST_LIMIT_mA(policy->fast_limit_USB_mA);118 wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2,119 policy->charge_mV | policy->trickle_charge_USB_mA |120 fast_limit_mA | wm8350_charge_time_min(wm8350,121 policy->charge_timeout));122 123 } else {124 fast_limit_mA =125 WM8350_CHG_FAST_LIMIT_mA(policy->fast_limit_mA);126 wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2,127 policy->charge_mV | policy->trickle_charge_mA |128 fast_limit_mA | wm8350_charge_time_min(wm8350,129 policy->charge_timeout));130 }131 132 wm8350_reg_lock(wm8350);133 return 0;134}135 136static int wm8350_batt_status(struct wm8350 *wm8350)137{138 u16 state;139 140 state = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2);141 state &= WM8350_CHG_STS_MASK;142 143 switch (state) {144 case WM8350_CHG_STS_OFF:145 return POWER_SUPPLY_STATUS_DISCHARGING;146 147 case WM8350_CHG_STS_TRICKLE:148 case WM8350_CHG_STS_FAST:149 return POWER_SUPPLY_STATUS_CHARGING;150 151 default:152 return POWER_SUPPLY_STATUS_UNKNOWN;153 }154}155 156static ssize_t charger_state_show(struct device *dev,157 struct device_attribute *attr, char *buf)158{159 struct wm8350 *wm8350 = dev_get_drvdata(dev);160 char *charge;161 int state;162 163 state = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2) &164 WM8350_CHG_STS_MASK;165 switch (state) {166 case WM8350_CHG_STS_OFF:167 charge = "Charger Off";168 break;169 case WM8350_CHG_STS_TRICKLE:170 charge = "Trickle Charging";171 break;172 case WM8350_CHG_STS_FAST:173 charge = "Fast Charging";174 break;175 default:176 return 0;177 }178 179 return sysfs_emit(buf, "%s\n", charge);180}181 182static DEVICE_ATTR_RO(charger_state);183 184static irqreturn_t wm8350_charger_handler(int irq, void *data)185{186 struct wm8350 *wm8350 = data;187 struct wm8350_power *power = &wm8350->power;188 struct wm8350_charger_policy *policy = power->policy;189 190 switch (irq - wm8350->irq_base) {191 case WM8350_IRQ_CHG_BAT_FAIL:192 dev_err(wm8350->dev, "battery failed\n");193 break;194 case WM8350_IRQ_CHG_TO:195 dev_err(wm8350->dev, "charger timeout\n");196 power_supply_changed(power->battery);197 break;198 199 case WM8350_IRQ_CHG_BAT_HOT:200 case WM8350_IRQ_CHG_BAT_COLD:201 case WM8350_IRQ_CHG_START:202 case WM8350_IRQ_CHG_END:203 power_supply_changed(power->battery);204 break;205 206 case WM8350_IRQ_CHG_FAST_RDY:207 dev_dbg(wm8350->dev, "fast charger ready\n");208 wm8350_charger_config(wm8350, policy);209 wm8350_reg_unlock(wm8350);210 wm8350_set_bits(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1,211 WM8350_CHG_FAST);212 wm8350_reg_lock(wm8350);213 break;214 215 case WM8350_IRQ_CHG_VBATT_LT_3P9:216 dev_warn(wm8350->dev, "battery < 3.9V\n");217 break;218 case WM8350_IRQ_CHG_VBATT_LT_3P1:219 dev_warn(wm8350->dev, "battery < 3.1V\n");220 break;221 case WM8350_IRQ_CHG_VBATT_LT_2P85:222 dev_warn(wm8350->dev, "battery < 2.85V\n");223 break;224 225 /* Supply change. We will overnotify but it should do226 * no harm. */227 case WM8350_IRQ_EXT_USB_FB:228 case WM8350_IRQ_EXT_WALL_FB:229 wm8350_charger_config(wm8350, policy);230 fallthrough;231 case WM8350_IRQ_EXT_BAT_FB:232 power_supply_changed(power->battery);233 power_supply_changed(power->usb);234 power_supply_changed(power->ac);235 break;236 237 default:238 dev_err(wm8350->dev, "Unknown interrupt %d\n", irq);239 }240 241 return IRQ_HANDLED;242}243 244/*********************************************************************245 * AC Power246 *********************************************************************/247static int wm8350_ac_get_prop(struct power_supply *psy,248 enum power_supply_property psp,249 union power_supply_propval *val)250{251 struct wm8350 *wm8350 = dev_get_drvdata(psy->dev.parent);252 int ret = 0;253 254 switch (psp) {255 case POWER_SUPPLY_PROP_ONLINE:256 val->intval = !!(wm8350_get_supplies(wm8350) &257 WM8350_LINE_SUPPLY);258 break;259 case POWER_SUPPLY_PROP_VOLTAGE_NOW:260 val->intval = wm8350_read_line_uvolts(wm8350);261 break;262 default:263 ret = -EINVAL;264 break;265 }266 return ret;267}268 269static enum power_supply_property wm8350_ac_props[] = {270 POWER_SUPPLY_PROP_ONLINE,271 POWER_SUPPLY_PROP_VOLTAGE_NOW,272};273 274/*********************************************************************275 * USB Power276 *********************************************************************/277static int wm8350_usb_get_prop(struct power_supply *psy,278 enum power_supply_property psp,279 union power_supply_propval *val)280{281 struct wm8350 *wm8350 = dev_get_drvdata(psy->dev.parent);282 int ret = 0;283 284 switch (psp) {285 case POWER_SUPPLY_PROP_ONLINE:286 val->intval = !!(wm8350_get_supplies(wm8350) &287 WM8350_USB_SUPPLY);288 break;289 case POWER_SUPPLY_PROP_VOLTAGE_NOW:290 val->intval = wm8350_read_usb_uvolts(wm8350);291 break;292 default:293 ret = -EINVAL;294 break;295 }296 return ret;297}298 299static enum power_supply_property wm8350_usb_props[] = {300 POWER_SUPPLY_PROP_ONLINE,301 POWER_SUPPLY_PROP_VOLTAGE_NOW,302};303 304/*********************************************************************305 * Battery properties306 *********************************************************************/307 308static int wm8350_bat_check_health(struct wm8350 *wm8350)309{310 u16 reg;311 312 if (wm8350_read_battery_uvolts(wm8350) < 2850000)313 return POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;314 315 reg = wm8350_reg_read(wm8350, WM8350_CHARGER_OVERRIDES);316 if (reg & WM8350_CHG_BATT_HOT_OVRDE)317 return POWER_SUPPLY_HEALTH_OVERHEAT;318 319 if (reg & WM8350_CHG_BATT_COLD_OVRDE)320 return POWER_SUPPLY_HEALTH_COLD;321 322 return POWER_SUPPLY_HEALTH_GOOD;323}324 325static int wm8350_bat_get_charge_type(struct wm8350 *wm8350)326{327 int state;328 329 state = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2) &330 WM8350_CHG_STS_MASK;331 switch (state) {332 case WM8350_CHG_STS_OFF:333 return POWER_SUPPLY_CHARGE_TYPE_NONE;334 case WM8350_CHG_STS_TRICKLE:335 return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;336 case WM8350_CHG_STS_FAST:337 return POWER_SUPPLY_CHARGE_TYPE_FAST;338 default:339 return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;340 }341}342 343static int wm8350_bat_get_property(struct power_supply *psy,344 enum power_supply_property psp,345 union power_supply_propval *val)346{347 struct wm8350 *wm8350 = dev_get_drvdata(psy->dev.parent);348 int ret = 0;349 350 switch (psp) {351 case POWER_SUPPLY_PROP_STATUS:352 val->intval = wm8350_batt_status(wm8350);353 break;354 case POWER_SUPPLY_PROP_ONLINE:355 val->intval = !!(wm8350_get_supplies(wm8350) &356 WM8350_BATT_SUPPLY);357 break;358 case POWER_SUPPLY_PROP_VOLTAGE_NOW:359 val->intval = wm8350_read_battery_uvolts(wm8350);360 break;361 case POWER_SUPPLY_PROP_HEALTH:362 val->intval = wm8350_bat_check_health(wm8350);363 break;364 case POWER_SUPPLY_PROP_CHARGE_TYPE:365 val->intval = wm8350_bat_get_charge_type(wm8350);366 break;367 default:368 ret = -EINVAL;369 break;370 }371 372 return ret;373}374 375static enum power_supply_property wm8350_bat_props[] = {376 POWER_SUPPLY_PROP_STATUS,377 POWER_SUPPLY_PROP_ONLINE,378 POWER_SUPPLY_PROP_VOLTAGE_NOW,379 POWER_SUPPLY_PROP_HEALTH,380 POWER_SUPPLY_PROP_CHARGE_TYPE,381};382 383static const struct power_supply_desc wm8350_ac_desc = {384 .name = "wm8350-ac",385 .type = POWER_SUPPLY_TYPE_MAINS,386 .properties = wm8350_ac_props,387 .num_properties = ARRAY_SIZE(wm8350_ac_props),388 .get_property = wm8350_ac_get_prop,389};390 391static const struct power_supply_desc wm8350_battery_desc = {392 .name = "wm8350-battery",393 .properties = wm8350_bat_props,394 .num_properties = ARRAY_SIZE(wm8350_bat_props),395 .get_property = wm8350_bat_get_property,396 .use_for_apm = 1,397};398 399static const struct power_supply_desc wm8350_usb_desc = {400 .name = "wm8350-usb",401 .type = POWER_SUPPLY_TYPE_USB,402 .properties = wm8350_usb_props,403 .num_properties = ARRAY_SIZE(wm8350_usb_props),404 .get_property = wm8350_usb_get_prop,405};406 407/*********************************************************************408 * Initialisation409 *********************************************************************/410 411static int wm8350_init_charger(struct wm8350 *wm8350)412{413 int ret;414 415 /* register our interest in charger events */416 ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT,417 wm8350_charger_handler, 0, "Battery hot", wm8350);418 if (ret)419 goto err;420 421 ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD,422 wm8350_charger_handler, 0, "Battery cold", wm8350);423 if (ret)424 goto free_chg_bat_hot;425 426 ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL,427 wm8350_charger_handler, 0, "Battery fail", wm8350);428 if (ret)429 goto free_chg_bat_cold;430 431 ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_TO,432 wm8350_charger_handler, 0,433 "Charger timeout", wm8350);434 if (ret)435 goto free_chg_bat_fail;436 437 ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_END,438 wm8350_charger_handler, 0,439 "Charge end", wm8350);440 if (ret)441 goto free_chg_to;442 443 ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_START,444 wm8350_charger_handler, 0,445 "Charge start", wm8350);446 if (ret)447 goto free_chg_end;448 449 ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY,450 wm8350_charger_handler, 0,451 "Fast charge ready", wm8350);452 if (ret)453 goto free_chg_start;454 455 ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9,456 wm8350_charger_handler, 0,457 "Battery <3.9V", wm8350);458 if (ret)459 goto free_chg_fast_rdy;460 461 ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1,462 wm8350_charger_handler, 0,463 "Battery <3.1V", wm8350);464 if (ret)465 goto free_chg_vbatt_lt_3p9;466 467 ret = wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85,468 wm8350_charger_handler, 0,469 "Battery <2.85V", wm8350);470 if (ret)471 goto free_chg_vbatt_lt_3p1;472 473 /* and supply change events */474 ret = wm8350_register_irq(wm8350, WM8350_IRQ_EXT_USB_FB,475 wm8350_charger_handler, 0, "USB", wm8350);476 if (ret)477 goto free_chg_vbatt_lt_2p85;478 479 ret = wm8350_register_irq(wm8350, WM8350_IRQ_EXT_WALL_FB,480 wm8350_charger_handler, 0, "Wall", wm8350);481 if (ret)482 goto free_ext_usb_fb;483 484 ret = wm8350_register_irq(wm8350, WM8350_IRQ_EXT_BAT_FB,485 wm8350_charger_handler, 0, "Battery", wm8350);486 if (ret)487 goto free_ext_wall_fb;488 489 return 0;490 491free_ext_wall_fb:492 wm8350_free_irq(wm8350, WM8350_IRQ_EXT_WALL_FB, wm8350);493free_ext_usb_fb:494 wm8350_free_irq(wm8350, WM8350_IRQ_EXT_USB_FB, wm8350);495free_chg_vbatt_lt_2p85:496 wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85, wm8350);497free_chg_vbatt_lt_3p1:498 wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1, wm8350);499free_chg_vbatt_lt_3p9:500 wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9, wm8350);501free_chg_fast_rdy:502 wm8350_free_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY, wm8350);503free_chg_start:504 wm8350_free_irq(wm8350, WM8350_IRQ_CHG_START, wm8350);505free_chg_end:506 wm8350_free_irq(wm8350, WM8350_IRQ_CHG_END, wm8350);507free_chg_to:508 wm8350_free_irq(wm8350, WM8350_IRQ_CHG_TO, wm8350);509free_chg_bat_fail:510 wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL, wm8350);511free_chg_bat_cold:512 wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD, wm8350);513free_chg_bat_hot:514 wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT, wm8350);515err:516 return ret;517}518 519static void free_charger_irq(struct wm8350 *wm8350)520{521 wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT, wm8350);522 wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD, wm8350);523 wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL, wm8350);524 wm8350_free_irq(wm8350, WM8350_IRQ_CHG_TO, wm8350);525 wm8350_free_irq(wm8350, WM8350_IRQ_CHG_END, wm8350);526 wm8350_free_irq(wm8350, WM8350_IRQ_CHG_START, wm8350);527 wm8350_free_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY, wm8350);528 wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9, wm8350);529 wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1, wm8350);530 wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85, wm8350);531 wm8350_free_irq(wm8350, WM8350_IRQ_EXT_USB_FB, wm8350);532 wm8350_free_irq(wm8350, WM8350_IRQ_EXT_WALL_FB, wm8350);533 wm8350_free_irq(wm8350, WM8350_IRQ_EXT_BAT_FB, wm8350);534}535 536static int wm8350_power_probe(struct platform_device *pdev)537{538 struct wm8350 *wm8350 = platform_get_drvdata(pdev);539 struct wm8350_power *power = &wm8350->power;540 struct wm8350_charger_policy *policy = power->policy;541 int ret;542 543 power->ac = devm_power_supply_register(&pdev->dev, &wm8350_ac_desc, NULL);544 if (IS_ERR(power->ac))545 return PTR_ERR(power->ac);546 547 power->battery = devm_power_supply_register(&pdev->dev, &wm8350_battery_desc, NULL);548 if (IS_ERR(power->battery))549 return PTR_ERR(power->battery);550 551 power->usb = devm_power_supply_register(&pdev->dev, &wm8350_usb_desc, NULL);552 if (IS_ERR(power->usb))553 return PTR_ERR(power->usb);554 555 ret = device_create_file(&pdev->dev, &dev_attr_charger_state);556 if (ret < 0)557 dev_warn(wm8350->dev, "failed to add charge sysfs: %d\n", ret);558 ret = 0;559 560 wm8350_init_charger(wm8350);561 if (wm8350_charger_config(wm8350, policy) == 0) {562 wm8350_reg_unlock(wm8350);563 wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CHG_ENA);564 wm8350_reg_lock(wm8350);565 }566 567 return ret;568}569 570static void wm8350_power_remove(struct platform_device *pdev)571{572 struct wm8350 *wm8350 = platform_get_drvdata(pdev);573 574 free_charger_irq(wm8350);575 device_remove_file(&pdev->dev, &dev_attr_charger_state);576}577 578static struct platform_driver wm8350_power_driver = {579 .probe = wm8350_power_probe,580 .remove_new = wm8350_power_remove,581 .driver = {582 .name = "wm8350-power",583 },584};585 586module_platform_driver(wm8350_power_driver);587 588MODULE_LICENSE("GPL");589MODULE_DESCRIPTION("Power supply driver for WM8350");590MODULE_ALIAS("platform:wm8350-power");591