aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2009-08-10 12:43:53 -0400
committerAnton Vorontsov <avorontsov@ru.mvista.com>2009-09-04 13:02:32 -0400
commit3961f7c3cf247eee5df7fabadc7a40f2deeb98f3 (patch)
tree60ed0bdd12f45b85cd7738ba7fe58024e3f88554
parentb0525b48f06714e8d5cf6a3266261b71de8d6dd4 (diff)
power_supply: Add driver for the PMU on WM831x PMICs
The WM831x PMICs provide power path management from three sources: a wall supply, USB and a battery with integrated charger. They also provide an additional backup supply with integrated for maintaining always on functionality such as the RTC and monitoring of power switches. After some initial configuration at startup the device operates autonomously, the driver simply provides reporting of the current state. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
-rw-r--r--drivers/power/Kconfig7
-rw-r--r--drivers/power/Makefile1
-rw-r--r--drivers/power/wm831x_power.c779
-rw-r--r--include/linux/mfd/wm831x/pmu.h189
4 files changed, 976 insertions, 0 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index bdbc4f73fcdc..cea6cef27e89 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -29,6 +29,13 @@ config APM_POWER
29 Say Y here to enable support APM status emulation using 29 Say Y here to enable support APM status emulation using
30 battery class devices. 30 battery class devices.
31 31
32config WM831X_POWER
33 tristate "WM831X PMU support"
34 depends on MFD_WM831X
35 help
36 Say Y here to enable support for the power management unit
37 provided by Wolfson Microelectronics WM831x PMICs.
38
32config WM8350_POWER 39config WM8350_POWER
33 tristate "WM8350 PMU support" 40 tristate "WM8350 PMU support"
34 depends on MFD_WM8350 41 depends on MFD_WM8350
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 380d17c9ae29..b96f29d91c28 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_POWER_SUPPLY) += power_supply.o
16 16
17obj-$(CONFIG_PDA_POWER) += pda_power.o 17obj-$(CONFIG_PDA_POWER) += pda_power.o
18obj-$(CONFIG_APM_POWER) += apm_power.o 18obj-$(CONFIG_APM_POWER) += apm_power.o
19obj-$(CONFIG_WM831X_POWER) += wm831x_power.o
19obj-$(CONFIG_WM8350_POWER) += wm8350_power.o 20obj-$(CONFIG_WM8350_POWER) += wm8350_power.o
20 21
21obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o 22obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
diff --git a/drivers/power/wm831x_power.c b/drivers/power/wm831x_power.c
new file mode 100644
index 000000000000..2a4c8b0b829c
--- /dev/null
+++ b/drivers/power/wm831x_power.c
@@ -0,0 +1,779 @@
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
16#include <linux/mfd/wm831x/core.h>
17#include <linux/mfd/wm831x/auxadc.h>
18#include <linux/mfd/wm831x/pmu.h>
19#include <linux/mfd/wm831x/pdata.h>
20
21struct wm831x_power {
22 struct wm831x *wm831x;
23 struct power_supply wall;
24 struct power_supply backup;
25 struct power_supply usb;
26 struct power_supply battery;
27};
28
29static int wm831x_power_check_online(struct wm831x *wm831x, int supply,
30 union power_supply_propval *val)
31{
32 int ret;
33
34 ret = wm831x_reg_read(wm831x, WM831X_SYSTEM_STATUS);
35 if (ret < 0)
36 return ret;
37
38 if (ret & supply)
39 val->intval = 1;
40 else
41 val->intval = 0;
42
43 return 0;
44}
45
46static int wm831x_power_read_voltage(struct wm831x *wm831x,
47 enum wm831x_auxadc src,
48 union power_supply_propval *val)
49{
50 int ret;
51
52 ret = wm831x_auxadc_read_uv(wm831x, src);
53 if (ret >= 0)
54 val->intval = ret;
55
56 return ret;
57}
58
59/*********************************************************************
60 * WALL Power
61 *********************************************************************/
62static int wm831x_wall_get_prop(struct power_supply *psy,
63 enum power_supply_property psp,
64 union power_supply_propval *val)
65{
66 struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev->parent);
67 struct wm831x *wm831x = wm831x_power->wm831x;
68 int ret = 0;
69
70 switch (psp) {
71 case POWER_SUPPLY_PROP_ONLINE:
72 ret = wm831x_power_check_online(wm831x, WM831X_PWR_WALL, val);
73 break;
74 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
75 ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_WALL, val);
76 break;
77 default:
78 ret = -EINVAL;
79 break;
80 }
81
82 return ret;
83}
84
85static enum power_supply_property wm831x_wall_props[] = {
86 POWER_SUPPLY_PROP_ONLINE,
87 POWER_SUPPLY_PROP_VOLTAGE_NOW,
88};
89
90/*********************************************************************
91 * USB Power
92 *********************************************************************/
93static int wm831x_usb_get_prop(struct power_supply *psy,
94 enum power_supply_property psp,
95 union power_supply_propval *val)
96{
97 struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev->parent);
98 struct wm831x *wm831x = wm831x_power->wm831x;
99 int ret = 0;
100
101 switch (psp) {
102 case POWER_SUPPLY_PROP_ONLINE:
103 ret = wm831x_power_check_online(wm831x, WM831X_PWR_USB, val);
104 break;
105 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
106 ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_USB, val);
107 break;
108 default:
109 ret = -EINVAL;
110 break;
111 }
112
113 return ret;
114}
115
116static enum power_supply_property wm831x_usb_props[] = {
117 POWER_SUPPLY_PROP_ONLINE,
118 POWER_SUPPLY_PROP_VOLTAGE_NOW,
119};
120
121/*********************************************************************
122 * Battery properties
123 *********************************************************************/
124
125struct chg_map {
126 int val;
127 int reg_val;
128};
129
130static struct chg_map trickle_ilims[] = {
131 { 50, 0 << WM831X_CHG_TRKL_ILIM_SHIFT },
132 { 100, 1 << WM831X_CHG_TRKL_ILIM_SHIFT },
133 { 150, 2 << WM831X_CHG_TRKL_ILIM_SHIFT },
134 { 200, 3 << WM831X_CHG_TRKL_ILIM_SHIFT },
135};
136
137static struct chg_map vsels[] = {
138 { 4050, 0 << WM831X_CHG_VSEL_SHIFT },
139 { 4100, 1 << WM831X_CHG_VSEL_SHIFT },
140 { 4150, 2 << WM831X_CHG_VSEL_SHIFT },
141 { 4200, 3 << WM831X_CHG_VSEL_SHIFT },
142};
143
144static struct chg_map fast_ilims[] = {
145 { 0, 0 << WM831X_CHG_FAST_ILIM_SHIFT },
146 { 50, 1 << WM831X_CHG_FAST_ILIM_SHIFT },
147 { 100, 2 << WM831X_CHG_FAST_ILIM_SHIFT },
148 { 150, 3 << WM831X_CHG_FAST_ILIM_SHIFT },
149 { 200, 4 << WM831X_CHG_FAST_ILIM_SHIFT },
150 { 250, 5 << WM831X_CHG_FAST_ILIM_SHIFT },
151 { 300, 6 << WM831X_CHG_FAST_ILIM_SHIFT },
152 { 350, 7 << WM831X_CHG_FAST_ILIM_SHIFT },
153 { 400, 8 << WM831X_CHG_FAST_ILIM_SHIFT },
154 { 450, 9 << WM831X_CHG_FAST_ILIM_SHIFT },
155 { 500, 10 << WM831X_CHG_FAST_ILIM_SHIFT },
156 { 600, 11 << WM831X_CHG_FAST_ILIM_SHIFT },
157 { 700, 12 << WM831X_CHG_FAST_ILIM_SHIFT },
158 { 800, 13 << WM831X_CHG_FAST_ILIM_SHIFT },
159 { 900, 14 << WM831X_CHG_FAST_ILIM_SHIFT },
160 { 1000, 15 << WM831X_CHG_FAST_ILIM_SHIFT },
161};
162
163static struct chg_map eoc_iterms[] = {
164 { 20, 0 << WM831X_CHG_ITERM_SHIFT },
165 { 30, 1 << WM831X_CHG_ITERM_SHIFT },
166 { 40, 2 << WM831X_CHG_ITERM_SHIFT },
167 { 50, 3 << WM831X_CHG_ITERM_SHIFT },
168 { 60, 4 << WM831X_CHG_ITERM_SHIFT },
169 { 70, 5 << WM831X_CHG_ITERM_SHIFT },
170 { 80, 6 << WM831X_CHG_ITERM_SHIFT },
171 { 90, 7 << WM831X_CHG_ITERM_SHIFT },
172};
173
174static struct chg_map chg_times[] = {
175 { 60, 0 << WM831X_CHG_TIME_SHIFT },
176 { 90, 1 << WM831X_CHG_TIME_SHIFT },
177 { 120, 2 << WM831X_CHG_TIME_SHIFT },
178 { 150, 3 << WM831X_CHG_TIME_SHIFT },
179 { 180, 4 << WM831X_CHG_TIME_SHIFT },
180 { 210, 5 << WM831X_CHG_TIME_SHIFT },
181 { 240, 6 << WM831X_CHG_TIME_SHIFT },
182 { 270, 7 << WM831X_CHG_TIME_SHIFT },
183 { 300, 8 << WM831X_CHG_TIME_SHIFT },
184 { 330, 9 << WM831X_CHG_TIME_SHIFT },
185 { 360, 10 << WM831X_CHG_TIME_SHIFT },
186 { 390, 11 << WM831X_CHG_TIME_SHIFT },
187 { 420, 12 << WM831X_CHG_TIME_SHIFT },
188 { 450, 13 << WM831X_CHG_TIME_SHIFT },
189 { 480, 14 << WM831X_CHG_TIME_SHIFT },
190 { 510, 15 << WM831X_CHG_TIME_SHIFT },
191};
192
193static void wm831x_battey_apply_config(struct wm831x *wm831x,
194 struct chg_map *map, int count, int val,
195 int *reg, const char *name,
196 const char *units)
197{
198 int i;
199
200 for (i = 0; i < count; i++)
201 if (val == map[i].val)
202 break;
203 if (i == count) {
204 dev_err(wm831x->dev, "Invalid %s %d%s\n",
205 name, val, units);
206 } else {
207 *reg |= map[i].reg_val;
208 dev_dbg(wm831x->dev, "Set %s of %d%s\n", name, val, units);
209 }
210}
211
212static void wm831x_config_battery(struct wm831x *wm831x)
213{
214 struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
215 struct wm831x_battery_pdata *pdata;
216 int ret, reg1, reg2;
217
218 if (!wm831x_pdata || !wm831x_pdata->battery) {
219 dev_warn(wm831x->dev,
220 "No battery charger configuration\n");
221 return;
222 }
223
224 pdata = wm831x_pdata->battery;
225
226 reg1 = 0;
227 reg2 = 0;
228
229 if (!pdata->enable) {
230 dev_info(wm831x->dev, "Battery charger disabled\n");
231 return;
232 }
233
234 reg1 |= WM831X_CHG_ENA;
235 if (pdata->off_mask)
236 reg2 |= WM831X_CHG_OFF_MSK;
237 if (pdata->fast_enable)
238 reg1 |= WM831X_CHG_FAST;
239
240 wm831x_battey_apply_config(wm831x, trickle_ilims,
241 ARRAY_SIZE(trickle_ilims),
242 pdata->trickle_ilim, &reg2,
243 "trickle charge current limit", "mA");
244
245 wm831x_battey_apply_config(wm831x, vsels, ARRAY_SIZE(vsels),
246 pdata->vsel, &reg2,
247 "target voltage", "mV");
248
249 wm831x_battey_apply_config(wm831x, fast_ilims, ARRAY_SIZE(fast_ilims),
250 pdata->fast_ilim, &reg2,
251 "fast charge current limit", "mA");
252
253 wm831x_battey_apply_config(wm831x, eoc_iterms, ARRAY_SIZE(eoc_iterms),
254 pdata->eoc_iterm, &reg1,
255 "end of charge current threshold", "mA");
256
257 wm831x_battey_apply_config(wm831x, chg_times, ARRAY_SIZE(chg_times),
258 pdata->timeout, &reg2,
259 "charger timeout", "min");
260
261 ret = wm831x_reg_unlock(wm831x);
262 if (ret != 0) {
263 dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret);
264 return;
265 }
266
267 ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_1,
268 WM831X_CHG_ENA_MASK |
269 WM831X_CHG_FAST_MASK |
270 WM831X_CHG_ITERM_MASK |
271 WM831X_CHG_ITERM_MASK,
272 reg1);
273 if (ret != 0)
274 dev_err(wm831x->dev, "Failed to set charger control 1: %d\n",
275 ret);
276
277 ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_2,
278 WM831X_CHG_OFF_MSK |
279 WM831X_CHG_TIME_MASK |
280 WM831X_CHG_FAST_ILIM_MASK |
281 WM831X_CHG_TRKL_ILIM_MASK |
282 WM831X_CHG_VSEL_MASK,
283 reg2);
284 if (ret != 0)
285 dev_err(wm831x->dev, "Failed to set charger control 2: %d\n",
286 ret);
287
288 wm831x_reg_lock(wm831x);
289}
290
291static int wm831x_bat_check_status(struct wm831x *wm831x, int *status)
292{
293 int ret;
294
295 ret = wm831x_reg_read(wm831x, WM831X_SYSTEM_STATUS);
296 if (ret < 0)
297 return ret;
298
299 if (ret & WM831X_PWR_SRC_BATT) {
300 *status = POWER_SUPPLY_STATUS_DISCHARGING;
301 return 0;
302 }
303
304 ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
305 if (ret < 0)
306 return ret;
307
308 switch (ret & WM831X_CHG_STATE_MASK) {
309 case WM831X_CHG_STATE_OFF:
310 *status = POWER_SUPPLY_STATUS_NOT_CHARGING;
311 break;
312 case WM831X_CHG_STATE_TRICKLE:
313 case WM831X_CHG_STATE_FAST:
314 *status = POWER_SUPPLY_STATUS_CHARGING;
315 break;
316
317 default:
318 *status = POWER_SUPPLY_STATUS_UNKNOWN;
319 break;
320 }
321
322 return 0;
323}
324
325static int wm831x_bat_check_type(struct wm831x *wm831x, int *type)
326{
327 int ret;
328
329 ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
330 if (ret < 0)
331 return ret;
332
333 switch (ret & WM831X_CHG_STATE_MASK) {
334 case WM831X_CHG_STATE_TRICKLE:
335 case WM831X_CHG_STATE_TRICKLE_OT:
336 *type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
337 break;
338 case WM831X_CHG_STATE_FAST:
339 case WM831X_CHG_STATE_FAST_OT:
340 *type = POWER_SUPPLY_CHARGE_TYPE_FAST;
341 break;
342 default:
343 *type = POWER_SUPPLY_CHARGE_TYPE_NONE;
344 break;
345 }
346
347 return 0;
348}
349
350static int wm831x_bat_check_health(struct wm831x *wm831x, int *health)
351{
352 int ret;
353
354 ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
355 if (ret < 0)
356 return ret;
357
358 if (ret & WM831X_BATT_HOT_STS) {
359 *health = POWER_SUPPLY_HEALTH_OVERHEAT;
360 return 0;
361 }
362
363 if (ret & WM831X_BATT_COLD_STS) {
364 *health = POWER_SUPPLY_HEALTH_COLD;
365 return 0;
366 }
367
368 if (ret & WM831X_BATT_OV_STS) {
369 *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
370 return 0;
371 }
372
373 switch (ret & WM831X_CHG_STATE_MASK) {
374 case WM831X_CHG_STATE_TRICKLE_OT:
375 case WM831X_CHG_STATE_FAST_OT:
376 *health = POWER_SUPPLY_HEALTH_OVERHEAT;
377 break;
378 case WM831X_CHG_STATE_DEFECTIVE:
379 *health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
380 break;
381 default:
382 *health = POWER_SUPPLY_HEALTH_GOOD;
383 break;
384 }
385
386 return 0;
387}
388
389static int wm831x_bat_get_prop(struct power_supply *psy,
390 enum power_supply_property psp,
391 union power_supply_propval *val)
392{
393 struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev->parent);
394 struct wm831x *wm831x = wm831x_power->wm831x;
395 int ret = 0;
396
397 switch (psp) {
398 case POWER_SUPPLY_PROP_STATUS:
399 ret = wm831x_bat_check_status(wm831x, &val->intval);
400 break;
401 case POWER_SUPPLY_PROP_ONLINE:
402 ret = wm831x_power_check_online(wm831x, WM831X_PWR_SRC_BATT,
403 val);
404 break;
405 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
406 ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_BATT, val);
407 break;
408 case POWER_SUPPLY_PROP_HEALTH:
409 ret = wm831x_bat_check_health(wm831x, &val->intval);
410 break;
411 case POWER_SUPPLY_PROP_CHARGE_TYPE:
412 ret = wm831x_bat_check_type(wm831x, &val->intval);
413 break;
414 default:
415 ret = -EINVAL;
416 break;
417 }
418
419 return ret;
420}
421
422static enum power_supply_property wm831x_bat_props[] = {
423 POWER_SUPPLY_PROP_STATUS,
424 POWER_SUPPLY_PROP_ONLINE,
425 POWER_SUPPLY_PROP_VOLTAGE_NOW,
426 POWER_SUPPLY_PROP_HEALTH,
427 POWER_SUPPLY_PROP_CHARGE_TYPE,
428};
429
430static const char *wm831x_bat_irqs[] = {
431 "BATT HOT",
432 "BATT COLD",
433 "BATT FAIL",
434 "OV",
435 "END",
436 "TO",
437 "MODE",
438 "START",
439};
440
441static irqreturn_t wm831x_bat_irq(int irq, void *data)
442{
443 struct wm831x_power *wm831x_power = data;
444 struct wm831x *wm831x = wm831x_power->wm831x;
445
446 dev_dbg(wm831x->dev, "Battery status changed: %d\n", irq);
447
448 /* The battery charger is autonomous so we don't need to do
449 * anything except kick user space */
450 power_supply_changed(&wm831x_power->battery);
451
452 return IRQ_HANDLED;
453}
454
455
456/*********************************************************************
457 * Backup supply properties
458 *********************************************************************/
459
460static void wm831x_config_backup(struct wm831x *wm831x)
461{
462 struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
463 struct wm831x_backup_pdata *pdata;
464 int ret, reg;
465
466 if (!wm831x_pdata || !wm831x_pdata->backup) {
467 dev_warn(wm831x->dev,
468 "No backup battery charger configuration\n");
469 return;
470 }
471
472 pdata = wm831x_pdata->backup;
473
474 reg = 0;
475
476 if (pdata->charger_enable)
477 reg |= WM831X_BKUP_CHG_ENA | WM831X_BKUP_BATT_DET_ENA;
478 if (pdata->no_constant_voltage)
479 reg |= WM831X_BKUP_CHG_MODE;
480
481 switch (pdata->vlim) {
482 case 2500:
483 break;
484 case 3100:
485 reg |= WM831X_BKUP_CHG_VLIM;
486 break;
487 default:
488 dev_err(wm831x->dev, "Invalid backup voltage limit %dmV\n",
489 pdata->vlim);
490 }
491
492 switch (pdata->ilim) {
493 case 100:
494 break;
495 case 200:
496 reg |= 1;
497 break;
498 case 300:
499 reg |= 2;
500 break;
501 case 400:
502 reg |= 3;
503 break;
504 default:
505 dev_err(wm831x->dev, "Invalid backup current limit %duA\n",
506 pdata->ilim);
507 }
508
509 ret = wm831x_reg_unlock(wm831x);
510 if (ret != 0) {
511 dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret);
512 return;
513 }
514
515 ret = wm831x_set_bits(wm831x, WM831X_BACKUP_CHARGER_CONTROL,
516 WM831X_BKUP_CHG_ENA_MASK |
517 WM831X_BKUP_CHG_MODE_MASK |
518 WM831X_BKUP_BATT_DET_ENA_MASK |
519 WM831X_BKUP_CHG_VLIM_MASK |
520 WM831X_BKUP_CHG_ILIM_MASK,
521 reg);
522 if (ret != 0)
523 dev_err(wm831x->dev,
524 "Failed to set backup charger config: %d\n", ret);
525
526 wm831x_reg_lock(wm831x);
527}
528
529static int wm831x_backup_get_prop(struct power_supply *psy,
530 enum power_supply_property psp,
531 union power_supply_propval *val)
532{
533 struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev->parent);
534 struct wm831x *wm831x = wm831x_power->wm831x;
535 int ret = 0;
536
537 ret = wm831x_reg_read(wm831x, WM831X_BACKUP_CHARGER_CONTROL);
538 if (ret < 0)
539 return ret;
540
541 switch (psp) {
542 case POWER_SUPPLY_PROP_STATUS:
543 if (ret & WM831X_BKUP_CHG_STS)
544 val->intval = POWER_SUPPLY_STATUS_CHARGING;
545 else
546 val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
547 break;
548
549 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
550 ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_BKUP_BATT,
551 val);
552 break;
553
554 case POWER_SUPPLY_PROP_PRESENT:
555 if (ret & WM831X_BKUP_CHG_STS)
556 val->intval = 1;
557 else
558 val->intval = 0;
559 break;
560
561 default:
562 ret = -EINVAL;
563 break;
564 }
565
566 return ret;
567}
568
569static enum power_supply_property wm831x_backup_props[] = {
570 POWER_SUPPLY_PROP_STATUS,
571 POWER_SUPPLY_PROP_VOLTAGE_NOW,
572 POWER_SUPPLY_PROP_PRESENT,
573};
574
575/*********************************************************************
576 * Initialisation
577 *********************************************************************/
578
579static irqreturn_t wm831x_syslo_irq(int irq, void *data)
580{
581 struct wm831x_power *wm831x_power = data;
582 struct wm831x *wm831x = wm831x_power->wm831x;
583
584 /* Not much we can actually *do* but tell people for
585 * posterity, we're probably about to run out of power. */
586 dev_crit(wm831x->dev, "SYSVDD under voltage\n");
587
588 return IRQ_HANDLED;
589}
590
591static irqreturn_t wm831x_pwr_src_irq(int irq, void *data)
592{
593 struct wm831x_power *wm831x_power = data;
594 struct wm831x *wm831x = wm831x_power->wm831x;
595
596 dev_dbg(wm831x->dev, "Power source changed\n");
597
598 /* Just notify for everything - little harm in overnotifying.
599 * The backup battery is not a power source while the system
600 * is running so skip that.
601 */
602 power_supply_changed(&wm831x_power->battery);
603 power_supply_changed(&wm831x_power->usb);
604 power_supply_changed(&wm831x_power->wall);
605
606 return IRQ_HANDLED;
607}
608
609static __devinit int wm831x_power_probe(struct platform_device *pdev)
610{
611 struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
612 struct wm831x_power *power;
613 struct power_supply *usb;
614 struct power_supply *battery;
615 struct power_supply *wall;
616 struct power_supply *backup;
617 int ret, irq, i;
618
619 power = kzalloc(sizeof(struct wm831x_power), GFP_KERNEL);
620 if (power == NULL)
621 return -ENOMEM;
622
623 power->wm831x = wm831x;
624 platform_set_drvdata(pdev, power);
625
626 usb = &power->usb;
627 battery = &power->battery;
628 wall = &power->wall;
629 backup = &power->backup;
630
631 /* We ignore configuration failures since we can still read back
632 * the status without enabling either of the chargers.
633 */
634 wm831x_config_battery(wm831x);
635 wm831x_config_backup(wm831x);
636
637 wall->name = "wm831x-wall";
638 wall->type = POWER_SUPPLY_TYPE_MAINS;
639 wall->properties = wm831x_wall_props;
640 wall->num_properties = ARRAY_SIZE(wm831x_wall_props);
641 wall->get_property = wm831x_wall_get_prop;
642 ret = power_supply_register(&pdev->dev, wall);
643 if (ret)
644 goto err_kmalloc;
645
646 battery->name = "wm831x-battery";
647 battery->properties = wm831x_bat_props;
648 battery->num_properties = ARRAY_SIZE(wm831x_bat_props);
649 battery->get_property = wm831x_bat_get_prop;
650 battery->use_for_apm = 1;
651 ret = power_supply_register(&pdev->dev, battery);
652 if (ret)
653 goto err_wall;
654
655 usb->name = "wm831x-usb",
656 usb->type = POWER_SUPPLY_TYPE_USB;
657 usb->properties = wm831x_usb_props;
658 usb->num_properties = ARRAY_SIZE(wm831x_usb_props);
659 usb->get_property = wm831x_usb_get_prop;
660 ret = power_supply_register(&pdev->dev, usb);
661 if (ret)
662 goto err_battery;
663
664 backup->name = "wm831x-backup";
665 backup->type = POWER_SUPPLY_TYPE_BATTERY;
666 backup->properties = wm831x_backup_props;
667 backup->num_properties = ARRAY_SIZE(wm831x_backup_props);
668 backup->get_property = wm831x_backup_get_prop;
669 ret = power_supply_register(&pdev->dev, backup);
670 if (ret)
671 goto err_usb;
672
673 irq = platform_get_irq_byname(pdev, "SYSLO");
674 ret = wm831x_request_irq(wm831x, irq, wm831x_syslo_irq,
675 IRQF_TRIGGER_RISING, "SYSLO",
676 power);
677 if (ret != 0) {
678 dev_err(&pdev->dev, "Failed to request SYSLO IRQ %d: %d\n",
679 irq, ret);
680 goto err_backup;
681 }
682
683 irq = platform_get_irq_byname(pdev, "PWR SRC");
684 ret = wm831x_request_irq(wm831x, irq, wm831x_pwr_src_irq,
685 IRQF_TRIGGER_RISING, "Power source",
686 power);
687 if (ret != 0) {
688 dev_err(&pdev->dev, "Failed to request PWR SRC IRQ %d: %d\n",
689 irq, ret);
690 goto err_syslo;
691 }
692
693 for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
694 irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
695 ret = wm831x_request_irq(wm831x, irq, wm831x_bat_irq,
696 IRQF_TRIGGER_RISING,
697 wm831x_bat_irqs[i],
698 power);
699 if (ret != 0) {
700 dev_err(&pdev->dev,
701 "Failed to request %s IRQ %d: %d\n",
702 wm831x_bat_irqs[i], irq, ret);
703 goto err_bat_irq;
704 }
705 }
706
707 return ret;
708
709err_bat_irq:
710 for (; i >= 0; i--) {
711 irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
712 wm831x_free_irq(wm831x, irq, power);
713 }
714 irq = platform_get_irq_byname(pdev, "PWR SRC");
715 wm831x_free_irq(wm831x, irq, power);
716err_syslo:
717 irq = platform_get_irq_byname(pdev, "SYSLO");
718 wm831x_free_irq(wm831x, irq, power);
719err_backup:
720 power_supply_unregister(backup);
721err_usb:
722 power_supply_unregister(usb);
723err_battery:
724 power_supply_unregister(battery);
725err_wall:
726 power_supply_unregister(wall);
727err_kmalloc:
728 kfree(power);
729 return ret;
730}
731
732static __devexit int wm831x_power_remove(struct platform_device *pdev)
733{
734 struct wm831x_power *wm831x_power = platform_get_drvdata(pdev);
735 struct wm831x *wm831x = wm831x_power->wm831x;
736 int irq, i;
737
738 for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
739 irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
740 wm831x_free_irq(wm831x, irq, wm831x_power);
741 }
742
743 irq = platform_get_irq_byname(pdev, "PWR SRC");
744 wm831x_free_irq(wm831x, irq, wm831x_power);
745
746 irq = platform_get_irq_byname(pdev, "SYSLO");
747 wm831x_free_irq(wm831x, irq, wm831x_power);
748
749 power_supply_unregister(&wm831x_power->backup);
750 power_supply_unregister(&wm831x_power->battery);
751 power_supply_unregister(&wm831x_power->wall);
752 power_supply_unregister(&wm831x_power->usb);
753 return 0;
754}
755
756static struct platform_driver wm831x_power_driver = {
757 .probe = wm831x_power_probe,
758 .remove = __devexit_p(wm831x_power_remove),
759 .driver = {
760 .name = "wm831x-power",
761 },
762};
763
764static int __init wm831x_power_init(void)
765{
766 return platform_driver_register(&wm831x_power_driver);
767}
768module_init(wm831x_power_init);
769
770static void __exit wm831x_power_exit(void)
771{
772 platform_driver_unregister(&wm831x_power_driver);
773}
774module_exit(wm831x_power_exit);
775
776MODULE_DESCRIPTION("Power supply driver for WM831x PMICs");
777MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
778MODULE_LICENSE("GPL");
779MODULE_ALIAS("platform:wm831x-power");
diff --git a/include/linux/mfd/wm831x/pmu.h b/include/linux/mfd/wm831x/pmu.h
new file mode 100644
index 000000000000..b18cbb027bc3
--- /dev/null
+++ b/include/linux/mfd/wm831x/pmu.h
@@ -0,0 +1,189 @@
1/*
2 * include/linux/mfd/wm831x/pmu.h -- PMU for WM831x
3 *
4 * Copyright 2009 Wolfson Microelectronics PLC.
5 *
6 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 *
13 */
14
15#ifndef __MFD_WM831X_PMU_H__
16#define __MFD_WM831X_PMU_H__
17
18/*
19 * R16387 (0x4003) - Power State
20 */
21#define WM831X_CHIP_ON 0x8000 /* CHIP_ON */
22#define WM831X_CHIP_ON_MASK 0x8000 /* CHIP_ON */
23#define WM831X_CHIP_ON_SHIFT 15 /* CHIP_ON */
24#define WM831X_CHIP_ON_WIDTH 1 /* CHIP_ON */
25#define WM831X_CHIP_SLP 0x4000 /* CHIP_SLP */
26#define WM831X_CHIP_SLP_MASK 0x4000 /* CHIP_SLP */
27#define WM831X_CHIP_SLP_SHIFT 14 /* CHIP_SLP */
28#define WM831X_CHIP_SLP_WIDTH 1 /* CHIP_SLP */
29#define WM831X_REF_LP 0x1000 /* REF_LP */
30#define WM831X_REF_LP_MASK 0x1000 /* REF_LP */
31#define WM831X_REF_LP_SHIFT 12 /* REF_LP */
32#define WM831X_REF_LP_WIDTH 1 /* REF_LP */
33#define WM831X_PWRSTATE_DLY_MASK 0x0C00 /* PWRSTATE_DLY - [11:10] */
34#define WM831X_PWRSTATE_DLY_SHIFT 10 /* PWRSTATE_DLY - [11:10] */
35#define WM831X_PWRSTATE_DLY_WIDTH 2 /* PWRSTATE_DLY - [11:10] */
36#define WM831X_SWRST_DLY 0x0200 /* SWRST_DLY */
37#define WM831X_SWRST_DLY_MASK 0x0200 /* SWRST_DLY */
38#define WM831X_SWRST_DLY_SHIFT 9 /* SWRST_DLY */
39#define WM831X_SWRST_DLY_WIDTH 1 /* SWRST_DLY */
40#define WM831X_USB100MA_STARTUP_MASK 0x0030 /* USB100MA_STARTUP - [5:4] */
41#define WM831X_USB100MA_STARTUP_SHIFT 4 /* USB100MA_STARTUP - [5:4] */
42#define WM831X_USB100MA_STARTUP_WIDTH 2 /* USB100MA_STARTUP - [5:4] */
43#define WM831X_USB_CURR_STS 0x0008 /* USB_CURR_STS */
44#define WM831X_USB_CURR_STS_MASK 0x0008 /* USB_CURR_STS */
45#define WM831X_USB_CURR_STS_SHIFT 3 /* USB_CURR_STS */
46#define WM831X_USB_CURR_STS_WIDTH 1 /* USB_CURR_STS */
47#define WM831X_USB_ILIM_MASK 0x0007 /* USB_ILIM - [2:0] */
48#define WM831X_USB_ILIM_SHIFT 0 /* USB_ILIM - [2:0] */
49#define WM831X_USB_ILIM_WIDTH 3 /* USB_ILIM - [2:0] */
50
51/*
52 * R16397 (0x400D) - System Status
53 */
54#define WM831X_THW_STS 0x8000 /* THW_STS */
55#define WM831X_THW_STS_MASK 0x8000 /* THW_STS */
56#define WM831X_THW_STS_SHIFT 15 /* THW_STS */
57#define WM831X_THW_STS_WIDTH 1 /* THW_STS */
58#define WM831X_PWR_SRC_BATT 0x0400 /* PWR_SRC_BATT */
59#define WM831X_PWR_SRC_BATT_MASK 0x0400 /* PWR_SRC_BATT */
60#define WM831X_PWR_SRC_BATT_SHIFT 10 /* PWR_SRC_BATT */
61#define WM831X_PWR_SRC_BATT_WIDTH 1 /* PWR_SRC_BATT */
62#define WM831X_PWR_WALL 0x0200 /* PWR_WALL */
63#define WM831X_PWR_WALL_MASK 0x0200 /* PWR_WALL */
64#define WM831X_PWR_WALL_SHIFT 9 /* PWR_WALL */
65#define WM831X_PWR_WALL_WIDTH 1 /* PWR_WALL */
66#define WM831X_PWR_USB 0x0100 /* PWR_USB */
67#define WM831X_PWR_USB_MASK 0x0100 /* PWR_USB */
68#define WM831X_PWR_USB_SHIFT 8 /* PWR_USB */
69#define WM831X_PWR_USB_WIDTH 1 /* PWR_USB */
70#define WM831X_MAIN_STATE_MASK 0x001F /* MAIN_STATE - [4:0] */
71#define WM831X_MAIN_STATE_SHIFT 0 /* MAIN_STATE - [4:0] */
72#define WM831X_MAIN_STATE_WIDTH 5 /* MAIN_STATE - [4:0] */
73
74/*
75 * R16456 (0x4048) - Charger Control 1
76 */
77#define WM831X_CHG_ENA 0x8000 /* CHG_ENA */
78#define WM831X_CHG_ENA_MASK 0x8000 /* CHG_ENA */
79#define WM831X_CHG_ENA_SHIFT 15 /* CHG_ENA */
80#define WM831X_CHG_ENA_WIDTH 1 /* CHG_ENA */
81#define WM831X_CHG_FRC 0x4000 /* CHG_FRC */
82#define WM831X_CHG_FRC_MASK 0x4000 /* CHG_FRC */
83#define WM831X_CHG_FRC_SHIFT 14 /* CHG_FRC */
84#define WM831X_CHG_FRC_WIDTH 1 /* CHG_FRC */
85#define WM831X_CHG_ITERM_MASK 0x1C00 /* CHG_ITERM - [12:10] */
86#define WM831X_CHG_ITERM_SHIFT 10 /* CHG_ITERM - [12:10] */
87#define WM831X_CHG_ITERM_WIDTH 3 /* CHG_ITERM - [12:10] */
88#define WM831X_CHG_FAST 0x0020 /* CHG_FAST */
89#define WM831X_CHG_FAST_MASK 0x0020 /* CHG_FAST */
90#define WM831X_CHG_FAST_SHIFT 5 /* CHG_FAST */
91#define WM831X_CHG_FAST_WIDTH 1 /* CHG_FAST */
92#define WM831X_CHG_IMON_ENA 0x0002 /* CHG_IMON_ENA */
93#define WM831X_CHG_IMON_ENA_MASK 0x0002 /* CHG_IMON_ENA */
94#define WM831X_CHG_IMON_ENA_SHIFT 1 /* CHG_IMON_ENA */
95#define WM831X_CHG_IMON_ENA_WIDTH 1 /* CHG_IMON_ENA */
96#define WM831X_CHG_CHIP_TEMP_MON 0x0001 /* CHG_CHIP_TEMP_MON */
97#define WM831X_CHG_CHIP_TEMP_MON_MASK 0x0001 /* CHG_CHIP_TEMP_MON */
98#define WM831X_CHG_CHIP_TEMP_MON_SHIFT 0 /* CHG_CHIP_TEMP_MON */
99#define WM831X_CHG_CHIP_TEMP_MON_WIDTH 1 /* CHG_CHIP_TEMP_MON */
100
101/*
102 * R16457 (0x4049) - Charger Control 2
103 */
104#define WM831X_CHG_OFF_MSK 0x4000 /* CHG_OFF_MSK */
105#define WM831X_CHG_OFF_MSK_MASK 0x4000 /* CHG_OFF_MSK */
106#define WM831X_CHG_OFF_MSK_SHIFT 14 /* CHG_OFF_MSK */
107#define WM831X_CHG_OFF_MSK_WIDTH 1 /* CHG_OFF_MSK */
108#define WM831X_CHG_TIME_MASK 0x0F00 /* CHG_TIME - [11:8] */
109#define WM831X_CHG_TIME_SHIFT 8 /* CHG_TIME - [11:8] */
110#define WM831X_CHG_TIME_WIDTH 4 /* CHG_TIME - [11:8] */
111#define WM831X_CHG_TRKL_ILIM_MASK 0x00C0 /* CHG_TRKL_ILIM - [7:6] */
112#define WM831X_CHG_TRKL_ILIM_SHIFT 6 /* CHG_TRKL_ILIM - [7:6] */
113#define WM831X_CHG_TRKL_ILIM_WIDTH 2 /* CHG_TRKL_ILIM - [7:6] */
114#define WM831X_CHG_VSEL_MASK 0x0030 /* CHG_VSEL - [5:4] */
115#define WM831X_CHG_VSEL_SHIFT 4 /* CHG_VSEL - [5:4] */
116#define WM831X_CHG_VSEL_WIDTH 2 /* CHG_VSEL - [5:4] */
117#define WM831X_CHG_FAST_ILIM_MASK 0x000F /* CHG_FAST_ILIM - [3:0] */
118#define WM831X_CHG_FAST_ILIM_SHIFT 0 /* CHG_FAST_ILIM - [3:0] */
119#define WM831X_CHG_FAST_ILIM_WIDTH 4 /* CHG_FAST_ILIM - [3:0] */
120
121/*
122 * R16458 (0x404A) - Charger Status
123 */
124#define WM831X_BATT_OV_STS 0x8000 /* BATT_OV_STS */
125#define WM831X_BATT_OV_STS_MASK 0x8000 /* BATT_OV_STS */
126#define WM831X_BATT_OV_STS_SHIFT 15 /* BATT_OV_STS */
127#define WM831X_BATT_OV_STS_WIDTH 1 /* BATT_OV_STS */
128#define WM831X_CHG_STATE_MASK 0x7000 /* CHG_STATE - [14:12] */
129#define WM831X_CHG_STATE_SHIFT 12 /* CHG_STATE - [14:12] */
130#define WM831X_CHG_STATE_WIDTH 3 /* CHG_STATE - [14:12] */
131#define WM831X_BATT_HOT_STS 0x0800 /* BATT_HOT_STS */
132#define WM831X_BATT_HOT_STS_MASK 0x0800 /* BATT_HOT_STS */
133#define WM831X_BATT_HOT_STS_SHIFT 11 /* BATT_HOT_STS */
134#define WM831X_BATT_HOT_STS_WIDTH 1 /* BATT_HOT_STS */
135#define WM831X_BATT_COLD_STS 0x0400 /* BATT_COLD_STS */
136#define WM831X_BATT_COLD_STS_MASK 0x0400 /* BATT_COLD_STS */
137#define WM831X_BATT_COLD_STS_SHIFT 10 /* BATT_COLD_STS */
138#define WM831X_BATT_COLD_STS_WIDTH 1 /* BATT_COLD_STS */
139#define WM831X_CHG_TOPOFF 0x0200 /* CHG_TOPOFF */
140#define WM831X_CHG_TOPOFF_MASK 0x0200 /* CHG_TOPOFF */
141#define WM831X_CHG_TOPOFF_SHIFT 9 /* CHG_TOPOFF */
142#define WM831X_CHG_TOPOFF_WIDTH 1 /* CHG_TOPOFF */
143#define WM831X_CHG_ACTIVE 0x0100 /* CHG_ACTIVE */
144#define WM831X_CHG_ACTIVE_MASK 0x0100 /* CHG_ACTIVE */
145#define WM831X_CHG_ACTIVE_SHIFT 8 /* CHG_ACTIVE */
146#define WM831X_CHG_ACTIVE_WIDTH 1 /* CHG_ACTIVE */
147#define WM831X_CHG_TIME_ELAPSED_MASK 0x00FF /* CHG_TIME_ELAPSED - [7:0] */
148#define WM831X_CHG_TIME_ELAPSED_SHIFT 0 /* CHG_TIME_ELAPSED - [7:0] */
149#define WM831X_CHG_TIME_ELAPSED_WIDTH 8 /* CHG_TIME_ELAPSED - [7:0] */
150
151#define WM831X_CHG_STATE_OFF (0 << WM831X_CHG_STATE_SHIFT)
152#define WM831X_CHG_STATE_TRICKLE (1 << WM831X_CHG_STATE_SHIFT)
153#define WM831X_CHG_STATE_FAST (2 << WM831X_CHG_STATE_SHIFT)
154#define WM831X_CHG_STATE_TRICKLE_OT (3 << WM831X_CHG_STATE_SHIFT)
155#define WM831X_CHG_STATE_FAST_OT (4 << WM831X_CHG_STATE_SHIFT)
156#define WM831X_CHG_STATE_DEFECTIVE (5 << WM831X_CHG_STATE_SHIFT)
157
158/*
159 * R16459 (0x404B) - Backup Charger Control
160 */
161#define WM831X_BKUP_CHG_ENA 0x8000 /* BKUP_CHG_ENA */
162#define WM831X_BKUP_CHG_ENA_MASK 0x8000 /* BKUP_CHG_ENA */
163#define WM831X_BKUP_CHG_ENA_SHIFT 15 /* BKUP_CHG_ENA */
164#define WM831X_BKUP_CHG_ENA_WIDTH 1 /* BKUP_CHG_ENA */
165#define WM831X_BKUP_CHG_STS 0x4000 /* BKUP_CHG_STS */
166#define WM831X_BKUP_CHG_STS_MASK 0x4000 /* BKUP_CHG_STS */
167#define WM831X_BKUP_CHG_STS_SHIFT 14 /* BKUP_CHG_STS */
168#define WM831X_BKUP_CHG_STS_WIDTH 1 /* BKUP_CHG_STS */
169#define WM831X_BKUP_CHG_MODE 0x1000 /* BKUP_CHG_MODE */
170#define WM831X_BKUP_CHG_MODE_MASK 0x1000 /* BKUP_CHG_MODE */
171#define WM831X_BKUP_CHG_MODE_SHIFT 12 /* BKUP_CHG_MODE */
172#define WM831X_BKUP_CHG_MODE_WIDTH 1 /* BKUP_CHG_MODE */
173#define WM831X_BKUP_BATT_DET_ENA 0x0800 /* BKUP_BATT_DET_ENA */
174#define WM831X_BKUP_BATT_DET_ENA_MASK 0x0800 /* BKUP_BATT_DET_ENA */
175#define WM831X_BKUP_BATT_DET_ENA_SHIFT 11 /* BKUP_BATT_DET_ENA */
176#define WM831X_BKUP_BATT_DET_ENA_WIDTH 1 /* BKUP_BATT_DET_ENA */
177#define WM831X_BKUP_BATT_STS 0x0400 /* BKUP_BATT_STS */
178#define WM831X_BKUP_BATT_STS_MASK 0x0400 /* BKUP_BATT_STS */
179#define WM831X_BKUP_BATT_STS_SHIFT 10 /* BKUP_BATT_STS */
180#define WM831X_BKUP_BATT_STS_WIDTH 1 /* BKUP_BATT_STS */
181#define WM831X_BKUP_CHG_VLIM 0x0010 /* BKUP_CHG_VLIM */
182#define WM831X_BKUP_CHG_VLIM_MASK 0x0010 /* BKUP_CHG_VLIM */
183#define WM831X_BKUP_CHG_VLIM_SHIFT 4 /* BKUP_CHG_VLIM */
184#define WM831X_BKUP_CHG_VLIM_WIDTH 1 /* BKUP_CHG_VLIM */
185#define WM831X_BKUP_CHG_ILIM_MASK 0x0003 /* BKUP_CHG_ILIM - [1:0] */
186#define WM831X_BKUP_CHG_ILIM_SHIFT 0 /* BKUP_CHG_ILIM - [1:0] */
187#define WM831X_BKUP_CHG_ILIM_WIDTH 2 /* BKUP_CHG_ILIM - [1:0] */
188
189#endif