Root/drivers/power/wm831x_power.c

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

Archive Download this file



interactive