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