blob: ff57a1674758812d8e58417400211b69f8f198d6 [file] [log] [blame]
Mark Brownd2bedfe2009-07-27 14:45:52 +01001/*
2 * wm831x-core.c -- Device access for Wolfson WM831x PMICs
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#include <linux/kernel.h>
16#include <linux/module.h>
Mark Brown7e9f9fd2009-07-27 14:45:54 +010017#include <linux/bcd.h>
18#include <linux/delay.h>
Mark Brownd2bedfe2009-07-27 14:45:52 +010019#include <linux/mfd/core.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090020#include <linux/slab.h>
Mark Brownd2bedfe2009-07-27 14:45:52 +010021
22#include <linux/mfd/wm831x/core.h>
23#include <linux/mfd/wm831x/pdata.h>
Mark Brown7d4d0a32009-07-27 14:45:53 +010024#include <linux/mfd/wm831x/irq.h>
Mark Brown7e9f9fd2009-07-27 14:45:54 +010025#include <linux/mfd/wm831x/auxadc.h>
Mark Brown6704e512009-07-27 14:45:56 +010026#include <linux/mfd/wm831x/otp.h>
Mark Brown698659d2009-07-27 14:45:57 +010027#include <linux/mfd/wm831x/regulator.h>
28
29/* Current settings - values are 2*2^(reg_val/4) microamps. These are
30 * exported since they are used by multiple drivers.
31 */
Mark Brown77169772009-11-30 13:24:18 +000032int wm831x_isinkv_values[WM831X_ISINK_MAX_ISEL + 1] = {
Mark Brown698659d2009-07-27 14:45:57 +010033 2,
34 2,
35 3,
36 3,
37 4,
38 5,
39 6,
40 7,
41 8,
42 10,
43 11,
44 13,
45 16,
46 19,
47 23,
48 27,
49 32,
50 38,
51 45,
52 54,
53 64,
54 76,
55 91,
56 108,
57 128,
58 152,
59 181,
60 215,
61 256,
62 304,
63 362,
64 431,
65 512,
66 609,
67 724,
68 861,
69 1024,
70 1218,
71 1448,
72 1722,
73 2048,
74 2435,
75 2896,
76 3444,
77 4096,
78 4871,
79 5793,
80 6889,
81 8192,
82 9742,
83 11585,
84 13777,
85 16384,
86 19484,
87 23170,
88 27554,
89};
90EXPORT_SYMBOL_GPL(wm831x_isinkv_values);
Mark Brownd2bedfe2009-07-27 14:45:52 +010091
Mark Brownd2bedfe2009-07-27 14:45:52 +010092static int wm831x_reg_locked(struct wm831x *wm831x, unsigned short reg)
93{
94 if (!wm831x->locked)
95 return 0;
96
97 switch (reg) {
98 case WM831X_WATCHDOG:
99 case WM831X_DC4_CONTROL:
100 case WM831X_ON_PIN_CONTROL:
101 case WM831X_BACKUP_CHARGER_CONTROL:
102 case WM831X_CHARGER_CONTROL_1:
103 case WM831X_CHARGER_CONTROL_2:
104 return 1;
105
106 default:
107 return 0;
108 }
109}
110
111/**
112 * wm831x_reg_unlock: Unlock user keyed registers
113 *
114 * The WM831x has a user key preventing writes to particularly
115 * critical registers. This function locks those registers,
116 * allowing writes to them.
117 */
118void wm831x_reg_lock(struct wm831x *wm831x)
119{
120 int ret;
121
122 ret = wm831x_reg_write(wm831x, WM831X_SECURITY_KEY, 0);
123 if (ret == 0) {
124 dev_vdbg(wm831x->dev, "Registers locked\n");
125
126 mutex_lock(&wm831x->io_lock);
127 WARN_ON(wm831x->locked);
128 wm831x->locked = 1;
129 mutex_unlock(&wm831x->io_lock);
130 } else {
131 dev_err(wm831x->dev, "Failed to lock registers: %d\n", ret);
132 }
133
134}
135EXPORT_SYMBOL_GPL(wm831x_reg_lock);
136
137/**
138 * wm831x_reg_unlock: Unlock user keyed registers
139 *
140 * The WM831x has a user key preventing writes to particularly
141 * critical registers. This function locks those registers,
142 * preventing spurious writes.
143 */
144int wm831x_reg_unlock(struct wm831x *wm831x)
145{
146 int ret;
147
148 /* 0x9716 is the value required to unlock the registers */
149 ret = wm831x_reg_write(wm831x, WM831X_SECURITY_KEY, 0x9716);
150 if (ret == 0) {
151 dev_vdbg(wm831x->dev, "Registers unlocked\n");
152
153 mutex_lock(&wm831x->io_lock);
154 WARN_ON(!wm831x->locked);
155 wm831x->locked = 0;
156 mutex_unlock(&wm831x->io_lock);
157 }
158
159 return ret;
160}
161EXPORT_SYMBOL_GPL(wm831x_reg_unlock);
162
163static int wm831x_read(struct wm831x *wm831x, unsigned short reg,
164 int bytes, void *dest)
165{
166 int ret, i;
167 u16 *buf = dest;
168
169 BUG_ON(bytes % 2);
170 BUG_ON(bytes <= 0);
171
172 ret = wm831x->read_dev(wm831x, reg, bytes, dest);
173 if (ret < 0)
174 return ret;
175
176 for (i = 0; i < bytes / 2; i++) {
177 buf[i] = be16_to_cpu(buf[i]);
178
179 dev_vdbg(wm831x->dev, "Read %04x from R%d(0x%x)\n",
180 buf[i], reg + i, reg + i);
181 }
182
183 return 0;
184}
185
186/**
187 * wm831x_reg_read: Read a single WM831x register.
188 *
189 * @wm831x: Device to read from.
190 * @reg: Register to read.
191 */
192int wm831x_reg_read(struct wm831x *wm831x, unsigned short reg)
193{
194 unsigned short val;
195 int ret;
196
197 mutex_lock(&wm831x->io_lock);
198
199 ret = wm831x_read(wm831x, reg, 2, &val);
200
201 mutex_unlock(&wm831x->io_lock);
202
203 if (ret < 0)
204 return ret;
205 else
206 return val;
207}
208EXPORT_SYMBOL_GPL(wm831x_reg_read);
209
210/**
211 * wm831x_bulk_read: Read multiple WM831x registers
212 *
213 * @wm831x: Device to read from
214 * @reg: First register
215 * @count: Number of registers
216 * @buf: Buffer to fill.
217 */
218int wm831x_bulk_read(struct wm831x *wm831x, unsigned short reg,
219 int count, u16 *buf)
220{
221 int ret;
222
223 mutex_lock(&wm831x->io_lock);
224
225 ret = wm831x_read(wm831x, reg, count * 2, buf);
226
227 mutex_unlock(&wm831x->io_lock);
228
229 return ret;
230}
231EXPORT_SYMBOL_GPL(wm831x_bulk_read);
232
233static int wm831x_write(struct wm831x *wm831x, unsigned short reg,
234 int bytes, void *src)
235{
236 u16 *buf = src;
237 int i;
238
239 BUG_ON(bytes % 2);
240 BUG_ON(bytes <= 0);
241
242 for (i = 0; i < bytes / 2; i++) {
243 if (wm831x_reg_locked(wm831x, reg))
244 return -EPERM;
245
246 dev_vdbg(wm831x->dev, "Write %04x to R%d(0x%x)\n",
247 buf[i], reg + i, reg + i);
248
249 buf[i] = cpu_to_be16(buf[i]);
250 }
251
252 return wm831x->write_dev(wm831x, reg, bytes, src);
253}
254
255/**
256 * wm831x_reg_write: Write a single WM831x register.
257 *
258 * @wm831x: Device to write to.
259 * @reg: Register to write to.
260 * @val: Value to write.
261 */
262int wm831x_reg_write(struct wm831x *wm831x, unsigned short reg,
263 unsigned short val)
264{
265 int ret;
266
267 mutex_lock(&wm831x->io_lock);
268
269 ret = wm831x_write(wm831x, reg, 2, &val);
270
271 mutex_unlock(&wm831x->io_lock);
272
273 return ret;
274}
275EXPORT_SYMBOL_GPL(wm831x_reg_write);
276
277/**
278 * wm831x_set_bits: Set the value of a bitfield in a WM831x register
279 *
280 * @wm831x: Device to write to.
281 * @reg: Register to write to.
282 * @mask: Mask of bits to set.
283 * @val: Value to set (unshifted)
284 */
285int wm831x_set_bits(struct wm831x *wm831x, unsigned short reg,
286 unsigned short mask, unsigned short val)
287{
288 int ret;
289 u16 r;
290
291 mutex_lock(&wm831x->io_lock);
292
293 ret = wm831x_read(wm831x, reg, 2, &r);
294 if (ret < 0)
295 goto out;
296
297 r &= ~mask;
298 r |= val;
299
300 ret = wm831x_write(wm831x, reg, 2, &r);
301
302out:
303 mutex_unlock(&wm831x->io_lock);
304
305 return ret;
306}
307EXPORT_SYMBOL_GPL(wm831x_set_bits);
308
Mark Brown7e9f9fd2009-07-27 14:45:54 +0100309/**
310 * wm831x_auxadc_read: Read a value from the WM831x AUXADC
311 *
312 * @wm831x: Device to read from.
313 * @input: AUXADC input to read.
314 */
315int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
316{
Mark Brown7cc13922010-04-02 16:31:03 +0100317 int ret, src, irq_masked, timeout;
318
319 /* Are we using the interrupt? */
320 irq_masked = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_1_MASK);
321 irq_masked &= WM831X_AUXADC_DATA_EINT;
Mark Brown7e9f9fd2009-07-27 14:45:54 +0100322
323 mutex_lock(&wm831x->auxadc_lock);
324
325 ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
326 WM831X_AUX_ENA, WM831X_AUX_ENA);
327 if (ret < 0) {
328 dev_err(wm831x->dev, "Failed to enable AUXADC: %d\n", ret);
329 goto out;
330 }
331
332 /* We force a single source at present */
333 src = input;
334 ret = wm831x_reg_write(wm831x, WM831X_AUXADC_SOURCE,
335 1 << src);
336 if (ret < 0) {
337 dev_err(wm831x->dev, "Failed to set AUXADC source: %d\n", ret);
338 goto out;
339 }
340
Mark Brown7cc13922010-04-02 16:31:03 +0100341 /* Clear any notification from a very late arriving interrupt */
342 try_wait_for_completion(&wm831x->auxadc_done);
343
Mark Brown7e9f9fd2009-07-27 14:45:54 +0100344 ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
345 WM831X_AUX_CVT_ENA, WM831X_AUX_CVT_ENA);
346 if (ret < 0) {
347 dev_err(wm831x->dev, "Failed to start AUXADC: %d\n", ret);
348 goto disable;
349 }
350
Mark Brown7cc13922010-04-02 16:31:03 +0100351 if (irq_masked) {
352 /* If we're not using interrupts then poll the
353 * interrupt status register */
354 timeout = 5;
355 while (timeout) {
356 msleep(1);
Mark Brown5051d412010-04-02 13:08:39 +0100357
Mark Brown7cc13922010-04-02 16:31:03 +0100358 ret = wm831x_reg_read(wm831x,
359 WM831X_INTERRUPT_STATUS_1);
360 if (ret < 0) {
361 dev_err(wm831x->dev,
362 "ISR 1 read failed: %d\n", ret);
363 goto disable;
364 }
Mark Brown7e9f9fd2009-07-27 14:45:54 +0100365
Mark Brown7cc13922010-04-02 16:31:03 +0100366 /* Did it complete? */
367 if (ret & WM831X_AUXADC_DATA_EINT) {
368 wm831x_reg_write(wm831x,
369 WM831X_INTERRUPT_STATUS_1,
370 WM831X_AUXADC_DATA_EINT);
371 break;
372 } else {
373 dev_err(wm831x->dev,
374 "AUXADC conversion timeout\n");
375 ret = -EBUSY;
376 goto disable;
377 }
378 }
379 } else {
380 /* If we are using interrupts then wait for the
381 * interrupt to complete. Use an extremely long
382 * timeout to handle situations with heavy load where
383 * the notification of the interrupt may be delayed by
384 * threaded IRQ handling. */
385 if (!wait_for_completion_timeout(&wm831x->auxadc_done,
386 msecs_to_jiffies(500))) {
387 dev_err(wm831x->dev, "Timed out waiting for AUXADC\n");
388 ret = -EBUSY;
389 goto disable;
390 }
Mark Brown7e9f9fd2009-07-27 14:45:54 +0100391 }
392
393 ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA);
394 if (ret < 0) {
395 dev_err(wm831x->dev, "Failed to read AUXADC data: %d\n", ret);
396 } else {
397 src = ((ret & WM831X_AUX_DATA_SRC_MASK)
398 >> WM831X_AUX_DATA_SRC_SHIFT) - 1;
399
400 if (src == 14)
401 src = WM831X_AUX_CAL;
402
403 if (src != input) {
404 dev_err(wm831x->dev, "Data from source %d not %d\n",
405 src, input);
406 ret = -EINVAL;
407 } else {
408 ret &= WM831X_AUX_DATA_MASK;
409 }
410 }
411
412disable:
413 wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, WM831X_AUX_ENA, 0);
414out:
415 mutex_unlock(&wm831x->auxadc_lock);
416 return ret;
417}
418EXPORT_SYMBOL_GPL(wm831x_auxadc_read);
419
Mark Brown473fe732010-02-23 11:08:06 +0000420static irqreturn_t wm831x_auxadc_irq(int irq, void *irq_data)
421{
422 struct wm831x *wm831x = irq_data;
423
424 complete(&wm831x->auxadc_done);
425
426 return IRQ_HANDLED;
427}
428
Mark Brown7e9f9fd2009-07-27 14:45:54 +0100429/**
430 * wm831x_auxadc_read_uv: Read a voltage from the WM831x AUXADC
431 *
432 * @wm831x: Device to read from.
433 * @input: AUXADC input to read.
434 */
435int wm831x_auxadc_read_uv(struct wm831x *wm831x, enum wm831x_auxadc input)
436{
437 int ret;
438
439 ret = wm831x_auxadc_read(wm831x, input);
440 if (ret < 0)
441 return ret;
442
443 ret *= 1465;
444
445 return ret;
446}
447EXPORT_SYMBOL_GPL(wm831x_auxadc_read_uv);
448
Mark Brownd2bedfe2009-07-27 14:45:52 +0100449static struct resource wm831x_dcdc1_resources[] = {
450 {
451 .start = WM831X_DC1_CONTROL_1,
452 .end = WM831X_DC1_DVS_CONTROL,
453 .flags = IORESOURCE_IO,
454 },
455 {
456 .name = "UV",
457 .start = WM831X_IRQ_UV_DC1,
458 .end = WM831X_IRQ_UV_DC1,
459 .flags = IORESOURCE_IRQ,
460 },
461 {
462 .name = "HC",
463 .start = WM831X_IRQ_HC_DC1,
464 .end = WM831X_IRQ_HC_DC1,
465 .flags = IORESOURCE_IRQ,
466 },
467};
468
469
470static struct resource wm831x_dcdc2_resources[] = {
471 {
472 .start = WM831X_DC2_CONTROL_1,
473 .end = WM831X_DC2_DVS_CONTROL,
474 .flags = IORESOURCE_IO,
475 },
476 {
477 .name = "UV",
478 .start = WM831X_IRQ_UV_DC2,
479 .end = WM831X_IRQ_UV_DC2,
480 .flags = IORESOURCE_IRQ,
481 },
482 {
483 .name = "HC",
484 .start = WM831X_IRQ_HC_DC2,
485 .end = WM831X_IRQ_HC_DC2,
486 .flags = IORESOURCE_IRQ,
487 },
488};
489
490static struct resource wm831x_dcdc3_resources[] = {
491 {
492 .start = WM831X_DC3_CONTROL_1,
493 .end = WM831X_DC3_SLEEP_CONTROL,
494 .flags = IORESOURCE_IO,
495 },
496 {
497 .name = "UV",
498 .start = WM831X_IRQ_UV_DC3,
499 .end = WM831X_IRQ_UV_DC3,
500 .flags = IORESOURCE_IRQ,
501 },
502};
503
504static struct resource wm831x_dcdc4_resources[] = {
505 {
506 .start = WM831X_DC4_CONTROL,
507 .end = WM831X_DC4_SLEEP_CONTROL,
508 .flags = IORESOURCE_IO,
509 },
510 {
511 .name = "UV",
512 .start = WM831X_IRQ_UV_DC4,
513 .end = WM831X_IRQ_UV_DC4,
514 .flags = IORESOURCE_IRQ,
515 },
516};
517
Mark Brownd4e0a892009-10-01 15:41:07 +0100518static struct resource wm8320_dcdc4_buck_resources[] = {
519 {
520 .start = WM831X_DC4_CONTROL,
521 .end = WM832X_DC4_SLEEP_CONTROL,
522 .flags = IORESOURCE_IO,
523 },
524 {
525 .name = "UV",
526 .start = WM831X_IRQ_UV_DC4,
527 .end = WM831X_IRQ_UV_DC4,
528 .flags = IORESOURCE_IRQ,
529 },
530};
531
Mark Brownd2bedfe2009-07-27 14:45:52 +0100532static struct resource wm831x_gpio_resources[] = {
533 {
534 .start = WM831X_IRQ_GPIO_1,
535 .end = WM831X_IRQ_GPIO_16,
536 .flags = IORESOURCE_IRQ,
537 },
538};
539
540static struct resource wm831x_isink1_resources[] = {
541 {
542 .start = WM831X_CURRENT_SINK_1,
543 .end = WM831X_CURRENT_SINK_1,
544 .flags = IORESOURCE_IO,
545 },
546 {
547 .start = WM831X_IRQ_CS1,
548 .end = WM831X_IRQ_CS1,
549 .flags = IORESOURCE_IRQ,
550 },
551};
552
553static struct resource wm831x_isink2_resources[] = {
554 {
555 .start = WM831X_CURRENT_SINK_2,
556 .end = WM831X_CURRENT_SINK_2,
557 .flags = IORESOURCE_IO,
558 },
559 {
560 .start = WM831X_IRQ_CS2,
561 .end = WM831X_IRQ_CS2,
562 .flags = IORESOURCE_IRQ,
563 },
564};
565
566static struct resource wm831x_ldo1_resources[] = {
567 {
568 .start = WM831X_LDO1_CONTROL,
569 .end = WM831X_LDO1_SLEEP_CONTROL,
570 .flags = IORESOURCE_IO,
571 },
572 {
573 .name = "UV",
574 .start = WM831X_IRQ_UV_LDO1,
575 .end = WM831X_IRQ_UV_LDO1,
576 .flags = IORESOURCE_IRQ,
577 },
578};
579
580static struct resource wm831x_ldo2_resources[] = {
581 {
582 .start = WM831X_LDO2_CONTROL,
583 .end = WM831X_LDO2_SLEEP_CONTROL,
584 .flags = IORESOURCE_IO,
585 },
586 {
587 .name = "UV",
588 .start = WM831X_IRQ_UV_LDO2,
589 .end = WM831X_IRQ_UV_LDO2,
590 .flags = IORESOURCE_IRQ,
591 },
592};
593
594static struct resource wm831x_ldo3_resources[] = {
595 {
596 .start = WM831X_LDO3_CONTROL,
597 .end = WM831X_LDO3_SLEEP_CONTROL,
598 .flags = IORESOURCE_IO,
599 },
600 {
601 .name = "UV",
602 .start = WM831X_IRQ_UV_LDO3,
603 .end = WM831X_IRQ_UV_LDO3,
604 .flags = IORESOURCE_IRQ,
605 },
606};
607
608static struct resource wm831x_ldo4_resources[] = {
609 {
610 .start = WM831X_LDO4_CONTROL,
611 .end = WM831X_LDO4_SLEEP_CONTROL,
612 .flags = IORESOURCE_IO,
613 },
614 {
615 .name = "UV",
616 .start = WM831X_IRQ_UV_LDO4,
617 .end = WM831X_IRQ_UV_LDO4,
618 .flags = IORESOURCE_IRQ,
619 },
620};
621
622static struct resource wm831x_ldo5_resources[] = {
623 {
624 .start = WM831X_LDO5_CONTROL,
625 .end = WM831X_LDO5_SLEEP_CONTROL,
626 .flags = IORESOURCE_IO,
627 },
628 {
629 .name = "UV",
630 .start = WM831X_IRQ_UV_LDO5,
631 .end = WM831X_IRQ_UV_LDO5,
632 .flags = IORESOURCE_IRQ,
633 },
634};
635
636static struct resource wm831x_ldo6_resources[] = {
637 {
638 .start = WM831X_LDO6_CONTROL,
639 .end = WM831X_LDO6_SLEEP_CONTROL,
640 .flags = IORESOURCE_IO,
641 },
642 {
643 .name = "UV",
644 .start = WM831X_IRQ_UV_LDO6,
645 .end = WM831X_IRQ_UV_LDO6,
646 .flags = IORESOURCE_IRQ,
647 },
648};
649
650static struct resource wm831x_ldo7_resources[] = {
651 {
652 .start = WM831X_LDO7_CONTROL,
653 .end = WM831X_LDO7_SLEEP_CONTROL,
654 .flags = IORESOURCE_IO,
655 },
656 {
657 .name = "UV",
658 .start = WM831X_IRQ_UV_LDO7,
659 .end = WM831X_IRQ_UV_LDO7,
660 .flags = IORESOURCE_IRQ,
661 },
662};
663
664static struct resource wm831x_ldo8_resources[] = {
665 {
666 .start = WM831X_LDO8_CONTROL,
667 .end = WM831X_LDO8_SLEEP_CONTROL,
668 .flags = IORESOURCE_IO,
669 },
670 {
671 .name = "UV",
672 .start = WM831X_IRQ_UV_LDO8,
673 .end = WM831X_IRQ_UV_LDO8,
674 .flags = IORESOURCE_IRQ,
675 },
676};
677
678static struct resource wm831x_ldo9_resources[] = {
679 {
680 .start = WM831X_LDO9_CONTROL,
681 .end = WM831X_LDO9_SLEEP_CONTROL,
682 .flags = IORESOURCE_IO,
683 },
684 {
685 .name = "UV",
686 .start = WM831X_IRQ_UV_LDO9,
687 .end = WM831X_IRQ_UV_LDO9,
688 .flags = IORESOURCE_IRQ,
689 },
690};
691
692static struct resource wm831x_ldo10_resources[] = {
693 {
694 .start = WM831X_LDO10_CONTROL,
695 .end = WM831X_LDO10_SLEEP_CONTROL,
696 .flags = IORESOURCE_IO,
697 },
698 {
699 .name = "UV",
700 .start = WM831X_IRQ_UV_LDO10,
701 .end = WM831X_IRQ_UV_LDO10,
702 .flags = IORESOURCE_IRQ,
703 },
704};
705
706static struct resource wm831x_ldo11_resources[] = {
707 {
708 .start = WM831X_LDO11_ON_CONTROL,
709 .end = WM831X_LDO11_SLEEP_CONTROL,
710 .flags = IORESOURCE_IO,
711 },
712};
713
714static struct resource wm831x_on_resources[] = {
715 {
716 .start = WM831X_IRQ_ON,
717 .end = WM831X_IRQ_ON,
718 .flags = IORESOURCE_IRQ,
719 },
720};
721
722
723static struct resource wm831x_power_resources[] = {
724 {
725 .name = "SYSLO",
726 .start = WM831X_IRQ_PPM_SYSLO,
727 .end = WM831X_IRQ_PPM_SYSLO,
728 .flags = IORESOURCE_IRQ,
729 },
730 {
731 .name = "PWR SRC",
732 .start = WM831X_IRQ_PPM_PWR_SRC,
733 .end = WM831X_IRQ_PPM_PWR_SRC,
734 .flags = IORESOURCE_IRQ,
735 },
736 {
737 .name = "USB CURR",
738 .start = WM831X_IRQ_PPM_USB_CURR,
739 .end = WM831X_IRQ_PPM_USB_CURR,
740 .flags = IORESOURCE_IRQ,
741 },
742 {
743 .name = "BATT HOT",
744 .start = WM831X_IRQ_CHG_BATT_HOT,
745 .end = WM831X_IRQ_CHG_BATT_HOT,
746 .flags = IORESOURCE_IRQ,
747 },
748 {
749 .name = "BATT COLD",
750 .start = WM831X_IRQ_CHG_BATT_COLD,
751 .end = WM831X_IRQ_CHG_BATT_COLD,
752 .flags = IORESOURCE_IRQ,
753 },
754 {
755 .name = "BATT FAIL",
756 .start = WM831X_IRQ_CHG_BATT_FAIL,
757 .end = WM831X_IRQ_CHG_BATT_FAIL,
758 .flags = IORESOURCE_IRQ,
759 },
760 {
761 .name = "OV",
762 .start = WM831X_IRQ_CHG_OV,
763 .end = WM831X_IRQ_CHG_OV,
764 .flags = IORESOURCE_IRQ,
765 },
766 {
767 .name = "END",
768 .start = WM831X_IRQ_CHG_END,
769 .end = WM831X_IRQ_CHG_END,
770 .flags = IORESOURCE_IRQ,
771 },
772 {
773 .name = "TO",
774 .start = WM831X_IRQ_CHG_TO,
775 .end = WM831X_IRQ_CHG_TO,
776 .flags = IORESOURCE_IRQ,
777 },
778 {
779 .name = "MODE",
780 .start = WM831X_IRQ_CHG_MODE,
781 .end = WM831X_IRQ_CHG_MODE,
782 .flags = IORESOURCE_IRQ,
783 },
784 {
785 .name = "START",
786 .start = WM831X_IRQ_CHG_START,
787 .end = WM831X_IRQ_CHG_START,
788 .flags = IORESOURCE_IRQ,
789 },
790};
791
792static struct resource wm831x_rtc_resources[] = {
793 {
794 .name = "PER",
795 .start = WM831X_IRQ_RTC_PER,
796 .end = WM831X_IRQ_RTC_PER,
797 .flags = IORESOURCE_IRQ,
798 },
799 {
800 .name = "ALM",
801 .start = WM831X_IRQ_RTC_ALM,
802 .end = WM831X_IRQ_RTC_ALM,
803 .flags = IORESOURCE_IRQ,
804 },
805};
806
807static struct resource wm831x_status1_resources[] = {
808 {
809 .start = WM831X_STATUS_LED_1,
810 .end = WM831X_STATUS_LED_1,
811 .flags = IORESOURCE_IO,
812 },
813};
814
815static struct resource wm831x_status2_resources[] = {
816 {
817 .start = WM831X_STATUS_LED_2,
818 .end = WM831X_STATUS_LED_2,
819 .flags = IORESOURCE_IO,
820 },
821};
822
823static struct resource wm831x_touch_resources[] = {
824 {
825 .name = "TCHPD",
826 .start = WM831X_IRQ_TCHPD,
827 .end = WM831X_IRQ_TCHPD,
828 .flags = IORESOURCE_IRQ,
829 },
830 {
831 .name = "TCHDATA",
832 .start = WM831X_IRQ_TCHDATA,
833 .end = WM831X_IRQ_TCHDATA,
834 .flags = IORESOURCE_IRQ,
835 },
836};
837
838static struct resource wm831x_wdt_resources[] = {
839 {
840 .start = WM831X_IRQ_WDOG_TO,
841 .end = WM831X_IRQ_WDOG_TO,
842 .flags = IORESOURCE_IRQ,
843 },
844};
845
846static struct mfd_cell wm8310_devs[] = {
847 {
Mark Brownc26964e2009-10-01 15:41:06 +0100848 .name = "wm831x-backup",
849 },
850 {
Mark Brownd2bedfe2009-07-27 14:45:52 +0100851 .name = "wm831x-buckv",
852 .id = 1,
853 .num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
854 .resources = wm831x_dcdc1_resources,
855 },
856 {
857 .name = "wm831x-buckv",
858 .id = 2,
859 .num_resources = ARRAY_SIZE(wm831x_dcdc2_resources),
860 .resources = wm831x_dcdc2_resources,
861 },
862 {
863 .name = "wm831x-buckp",
864 .id = 3,
865 .num_resources = ARRAY_SIZE(wm831x_dcdc3_resources),
866 .resources = wm831x_dcdc3_resources,
867 },
868 {
869 .name = "wm831x-boostp",
870 .id = 4,
871 .num_resources = ARRAY_SIZE(wm831x_dcdc4_resources),
872 .resources = wm831x_dcdc4_resources,
873 },
874 {
875 .name = "wm831x-epe",
876 .id = 1,
877 },
878 {
879 .name = "wm831x-epe",
880 .id = 2,
881 },
882 {
883 .name = "wm831x-gpio",
884 .num_resources = ARRAY_SIZE(wm831x_gpio_resources),
885 .resources = wm831x_gpio_resources,
886 },
887 {
888 .name = "wm831x-hwmon",
889 },
890 {
891 .name = "wm831x-isink",
892 .id = 1,
893 .num_resources = ARRAY_SIZE(wm831x_isink1_resources),
894 .resources = wm831x_isink1_resources,
895 },
896 {
897 .name = "wm831x-isink",
898 .id = 2,
899 .num_resources = ARRAY_SIZE(wm831x_isink2_resources),
900 .resources = wm831x_isink2_resources,
901 },
902 {
903 .name = "wm831x-ldo",
904 .id = 1,
905 .num_resources = ARRAY_SIZE(wm831x_ldo1_resources),
906 .resources = wm831x_ldo1_resources,
907 },
908 {
909 .name = "wm831x-ldo",
910 .id = 2,
911 .num_resources = ARRAY_SIZE(wm831x_ldo2_resources),
912 .resources = wm831x_ldo2_resources,
913 },
914 {
915 .name = "wm831x-ldo",
916 .id = 3,
917 .num_resources = ARRAY_SIZE(wm831x_ldo3_resources),
918 .resources = wm831x_ldo3_resources,
919 },
920 {
921 .name = "wm831x-ldo",
922 .id = 4,
923 .num_resources = ARRAY_SIZE(wm831x_ldo4_resources),
924 .resources = wm831x_ldo4_resources,
925 },
926 {
927 .name = "wm831x-ldo",
928 .id = 5,
929 .num_resources = ARRAY_SIZE(wm831x_ldo5_resources),
930 .resources = wm831x_ldo5_resources,
931 },
932 {
933 .name = "wm831x-ldo",
934 .id = 6,
935 .num_resources = ARRAY_SIZE(wm831x_ldo6_resources),
936 .resources = wm831x_ldo6_resources,
937 },
938 {
939 .name = "wm831x-aldo",
940 .id = 7,
941 .num_resources = ARRAY_SIZE(wm831x_ldo7_resources),
942 .resources = wm831x_ldo7_resources,
943 },
944 {
945 .name = "wm831x-aldo",
946 .id = 8,
947 .num_resources = ARRAY_SIZE(wm831x_ldo8_resources),
948 .resources = wm831x_ldo8_resources,
949 },
950 {
951 .name = "wm831x-aldo",
952 .id = 9,
953 .num_resources = ARRAY_SIZE(wm831x_ldo9_resources),
954 .resources = wm831x_ldo9_resources,
955 },
956 {
957 .name = "wm831x-aldo",
958 .id = 10,
959 .num_resources = ARRAY_SIZE(wm831x_ldo10_resources),
960 .resources = wm831x_ldo10_resources,
961 },
962 {
963 .name = "wm831x-alive-ldo",
964 .id = 11,
965 .num_resources = ARRAY_SIZE(wm831x_ldo11_resources),
966 .resources = wm831x_ldo11_resources,
967 },
968 {
969 .name = "wm831x-on",
970 .num_resources = ARRAY_SIZE(wm831x_on_resources),
971 .resources = wm831x_on_resources,
972 },
973 {
974 .name = "wm831x-power",
975 .num_resources = ARRAY_SIZE(wm831x_power_resources),
976 .resources = wm831x_power_resources,
977 },
978 {
979 .name = "wm831x-rtc",
980 .num_resources = ARRAY_SIZE(wm831x_rtc_resources),
981 .resources = wm831x_rtc_resources,
982 },
983 {
984 .name = "wm831x-status",
985 .id = 1,
986 .num_resources = ARRAY_SIZE(wm831x_status1_resources),
987 .resources = wm831x_status1_resources,
988 },
989 {
990 .name = "wm831x-status",
991 .id = 2,
992 .num_resources = ARRAY_SIZE(wm831x_status2_resources),
993 .resources = wm831x_status2_resources,
994 },
995 {
996 .name = "wm831x-watchdog",
997 .num_resources = ARRAY_SIZE(wm831x_wdt_resources),
998 .resources = wm831x_wdt_resources,
999 },
1000};
1001
1002static struct mfd_cell wm8311_devs[] = {
1003 {
Mark Brownc26964e2009-10-01 15:41:06 +01001004 .name = "wm831x-backup",
1005 },
1006 {
Mark Brownd2bedfe2009-07-27 14:45:52 +01001007 .name = "wm831x-buckv",
1008 .id = 1,
1009 .num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
1010 .resources = wm831x_dcdc1_resources,
1011 },
1012 {
1013 .name = "wm831x-buckv",
1014 .id = 2,
1015 .num_resources = ARRAY_SIZE(wm831x_dcdc2_resources),
1016 .resources = wm831x_dcdc2_resources,
1017 },
1018 {
1019 .name = "wm831x-buckp",
1020 .id = 3,
1021 .num_resources = ARRAY_SIZE(wm831x_dcdc3_resources),
1022 .resources = wm831x_dcdc3_resources,
1023 },
1024 {
1025 .name = "wm831x-boostp",
1026 .id = 4,
1027 .num_resources = ARRAY_SIZE(wm831x_dcdc4_resources),
1028 .resources = wm831x_dcdc4_resources,
1029 },
1030 {
1031 .name = "wm831x-epe",
1032 .id = 1,
1033 },
1034 {
1035 .name = "wm831x-epe",
1036 .id = 2,
1037 },
1038 {
1039 .name = "wm831x-gpio",
1040 .num_resources = ARRAY_SIZE(wm831x_gpio_resources),
1041 .resources = wm831x_gpio_resources,
1042 },
1043 {
1044 .name = "wm831x-hwmon",
1045 },
1046 {
1047 .name = "wm831x-isink",
1048 .id = 1,
1049 .num_resources = ARRAY_SIZE(wm831x_isink1_resources),
1050 .resources = wm831x_isink1_resources,
1051 },
1052 {
1053 .name = "wm831x-isink",
1054 .id = 2,
1055 .num_resources = ARRAY_SIZE(wm831x_isink2_resources),
1056 .resources = wm831x_isink2_resources,
1057 },
1058 {
1059 .name = "wm831x-ldo",
1060 .id = 1,
1061 .num_resources = ARRAY_SIZE(wm831x_ldo1_resources),
1062 .resources = wm831x_ldo1_resources,
1063 },
1064 {
1065 .name = "wm831x-ldo",
1066 .id = 2,
1067 .num_resources = ARRAY_SIZE(wm831x_ldo2_resources),
1068 .resources = wm831x_ldo2_resources,
1069 },
1070 {
1071 .name = "wm831x-ldo",
1072 .id = 3,
1073 .num_resources = ARRAY_SIZE(wm831x_ldo3_resources),
1074 .resources = wm831x_ldo3_resources,
1075 },
1076 {
1077 .name = "wm831x-ldo",
1078 .id = 4,
1079 .num_resources = ARRAY_SIZE(wm831x_ldo4_resources),
1080 .resources = wm831x_ldo4_resources,
1081 },
1082 {
1083 .name = "wm831x-ldo",
1084 .id = 5,
1085 .num_resources = ARRAY_SIZE(wm831x_ldo5_resources),
1086 .resources = wm831x_ldo5_resources,
1087 },
1088 {
1089 .name = "wm831x-aldo",
1090 .id = 7,
1091 .num_resources = ARRAY_SIZE(wm831x_ldo7_resources),
1092 .resources = wm831x_ldo7_resources,
1093 },
1094 {
1095 .name = "wm831x-alive-ldo",
1096 .id = 11,
1097 .num_resources = ARRAY_SIZE(wm831x_ldo11_resources),
1098 .resources = wm831x_ldo11_resources,
1099 },
1100 {
1101 .name = "wm831x-on",
1102 .num_resources = ARRAY_SIZE(wm831x_on_resources),
1103 .resources = wm831x_on_resources,
1104 },
1105 {
1106 .name = "wm831x-power",
1107 .num_resources = ARRAY_SIZE(wm831x_power_resources),
1108 .resources = wm831x_power_resources,
1109 },
1110 {
1111 .name = "wm831x-rtc",
1112 .num_resources = ARRAY_SIZE(wm831x_rtc_resources),
1113 .resources = wm831x_rtc_resources,
1114 },
1115 {
1116 .name = "wm831x-status",
1117 .id = 1,
1118 .num_resources = ARRAY_SIZE(wm831x_status1_resources),
1119 .resources = wm831x_status1_resources,
1120 },
1121 {
1122 .name = "wm831x-status",
1123 .id = 2,
1124 .num_resources = ARRAY_SIZE(wm831x_status2_resources),
1125 .resources = wm831x_status2_resources,
1126 },
1127 {
Mark Brownd2bedfe2009-07-27 14:45:52 +01001128 .name = "wm831x-watchdog",
1129 .num_resources = ARRAY_SIZE(wm831x_wdt_resources),
1130 .resources = wm831x_wdt_resources,
1131 },
1132};
1133
1134static struct mfd_cell wm8312_devs[] = {
1135 {
Mark Brownc26964e2009-10-01 15:41:06 +01001136 .name = "wm831x-backup",
1137 },
1138 {
Mark Brownd2bedfe2009-07-27 14:45:52 +01001139 .name = "wm831x-buckv",
1140 .id = 1,
1141 .num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
1142 .resources = wm831x_dcdc1_resources,
1143 },
1144 {
1145 .name = "wm831x-buckv",
1146 .id = 2,
1147 .num_resources = ARRAY_SIZE(wm831x_dcdc2_resources),
1148 .resources = wm831x_dcdc2_resources,
1149 },
1150 {
1151 .name = "wm831x-buckp",
1152 .id = 3,
1153 .num_resources = ARRAY_SIZE(wm831x_dcdc3_resources),
1154 .resources = wm831x_dcdc3_resources,
1155 },
1156 {
1157 .name = "wm831x-boostp",
1158 .id = 4,
1159 .num_resources = ARRAY_SIZE(wm831x_dcdc4_resources),
1160 .resources = wm831x_dcdc4_resources,
1161 },
1162 {
1163 .name = "wm831x-epe",
1164 .id = 1,
1165 },
1166 {
1167 .name = "wm831x-epe",
1168 .id = 2,
1169 },
1170 {
1171 .name = "wm831x-gpio",
1172 .num_resources = ARRAY_SIZE(wm831x_gpio_resources),
1173 .resources = wm831x_gpio_resources,
1174 },
1175 {
1176 .name = "wm831x-hwmon",
1177 },
1178 {
1179 .name = "wm831x-isink",
1180 .id = 1,
1181 .num_resources = ARRAY_SIZE(wm831x_isink1_resources),
1182 .resources = wm831x_isink1_resources,
1183 },
1184 {
1185 .name = "wm831x-isink",
1186 .id = 2,
1187 .num_resources = ARRAY_SIZE(wm831x_isink2_resources),
1188 .resources = wm831x_isink2_resources,
1189 },
1190 {
1191 .name = "wm831x-ldo",
1192 .id = 1,
1193 .num_resources = ARRAY_SIZE(wm831x_ldo1_resources),
1194 .resources = wm831x_ldo1_resources,
1195 },
1196 {
1197 .name = "wm831x-ldo",
1198 .id = 2,
1199 .num_resources = ARRAY_SIZE(wm831x_ldo2_resources),
1200 .resources = wm831x_ldo2_resources,
1201 },
1202 {
1203 .name = "wm831x-ldo",
1204 .id = 3,
1205 .num_resources = ARRAY_SIZE(wm831x_ldo3_resources),
1206 .resources = wm831x_ldo3_resources,
1207 },
1208 {
1209 .name = "wm831x-ldo",
1210 .id = 4,
1211 .num_resources = ARRAY_SIZE(wm831x_ldo4_resources),
1212 .resources = wm831x_ldo4_resources,
1213 },
1214 {
1215 .name = "wm831x-ldo",
1216 .id = 5,
1217 .num_resources = ARRAY_SIZE(wm831x_ldo5_resources),
1218 .resources = wm831x_ldo5_resources,
1219 },
1220 {
1221 .name = "wm831x-ldo",
1222 .id = 6,
1223 .num_resources = ARRAY_SIZE(wm831x_ldo6_resources),
1224 .resources = wm831x_ldo6_resources,
1225 },
1226 {
1227 .name = "wm831x-aldo",
1228 .id = 7,
1229 .num_resources = ARRAY_SIZE(wm831x_ldo7_resources),
1230 .resources = wm831x_ldo7_resources,
1231 },
1232 {
1233 .name = "wm831x-aldo",
1234 .id = 8,
1235 .num_resources = ARRAY_SIZE(wm831x_ldo8_resources),
1236 .resources = wm831x_ldo8_resources,
1237 },
1238 {
1239 .name = "wm831x-aldo",
1240 .id = 9,
1241 .num_resources = ARRAY_SIZE(wm831x_ldo9_resources),
1242 .resources = wm831x_ldo9_resources,
1243 },
1244 {
1245 .name = "wm831x-aldo",
1246 .id = 10,
1247 .num_resources = ARRAY_SIZE(wm831x_ldo10_resources),
1248 .resources = wm831x_ldo10_resources,
1249 },
1250 {
1251 .name = "wm831x-alive-ldo",
1252 .id = 11,
1253 .num_resources = ARRAY_SIZE(wm831x_ldo11_resources),
1254 .resources = wm831x_ldo11_resources,
1255 },
1256 {
1257 .name = "wm831x-on",
1258 .num_resources = ARRAY_SIZE(wm831x_on_resources),
1259 .resources = wm831x_on_resources,
1260 },
1261 {
1262 .name = "wm831x-power",
1263 .num_resources = ARRAY_SIZE(wm831x_power_resources),
1264 .resources = wm831x_power_resources,
1265 },
1266 {
1267 .name = "wm831x-rtc",
1268 .num_resources = ARRAY_SIZE(wm831x_rtc_resources),
1269 .resources = wm831x_rtc_resources,
1270 },
1271 {
1272 .name = "wm831x-status",
1273 .id = 1,
1274 .num_resources = ARRAY_SIZE(wm831x_status1_resources),
1275 .resources = wm831x_status1_resources,
1276 },
1277 {
1278 .name = "wm831x-status",
1279 .id = 2,
1280 .num_resources = ARRAY_SIZE(wm831x_status2_resources),
1281 .resources = wm831x_status2_resources,
1282 },
1283 {
Mark Brownd2bedfe2009-07-27 14:45:52 +01001284 .name = "wm831x-watchdog",
1285 .num_resources = ARRAY_SIZE(wm831x_wdt_resources),
1286 .resources = wm831x_wdt_resources,
1287 },
1288};
1289
Mark Brownd4e0a892009-10-01 15:41:07 +01001290static struct mfd_cell wm8320_devs[] = {
1291 {
1292 .name = "wm831x-backup",
1293 },
1294 {
1295 .name = "wm831x-buckv",
1296 .id = 1,
1297 .num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
1298 .resources = wm831x_dcdc1_resources,
1299 },
1300 {
1301 .name = "wm831x-buckv",
1302 .id = 2,
1303 .num_resources = ARRAY_SIZE(wm831x_dcdc2_resources),
1304 .resources = wm831x_dcdc2_resources,
1305 },
1306 {
1307 .name = "wm831x-buckp",
1308 .id = 3,
1309 .num_resources = ARRAY_SIZE(wm831x_dcdc3_resources),
1310 .resources = wm831x_dcdc3_resources,
1311 },
1312 {
1313 .name = "wm831x-buckp",
1314 .id = 4,
1315 .num_resources = ARRAY_SIZE(wm8320_dcdc4_buck_resources),
1316 .resources = wm8320_dcdc4_buck_resources,
1317 },
1318 {
1319 .name = "wm831x-gpio",
1320 .num_resources = ARRAY_SIZE(wm831x_gpio_resources),
1321 .resources = wm831x_gpio_resources,
1322 },
1323 {
1324 .name = "wm831x-hwmon",
1325 },
1326 {
1327 .name = "wm831x-ldo",
1328 .id = 1,
1329 .num_resources = ARRAY_SIZE(wm831x_ldo1_resources),
1330 .resources = wm831x_ldo1_resources,
1331 },
1332 {
1333 .name = "wm831x-ldo",
1334 .id = 2,
1335 .num_resources = ARRAY_SIZE(wm831x_ldo2_resources),
1336 .resources = wm831x_ldo2_resources,
1337 },
1338 {
1339 .name = "wm831x-ldo",
1340 .id = 3,
1341 .num_resources = ARRAY_SIZE(wm831x_ldo3_resources),
1342 .resources = wm831x_ldo3_resources,
1343 },
1344 {
1345 .name = "wm831x-ldo",
1346 .id = 4,
1347 .num_resources = ARRAY_SIZE(wm831x_ldo4_resources),
1348 .resources = wm831x_ldo4_resources,
1349 },
1350 {
1351 .name = "wm831x-ldo",
1352 .id = 5,
1353 .num_resources = ARRAY_SIZE(wm831x_ldo5_resources),
1354 .resources = wm831x_ldo5_resources,
1355 },
1356 {
1357 .name = "wm831x-ldo",
1358 .id = 6,
1359 .num_resources = ARRAY_SIZE(wm831x_ldo6_resources),
1360 .resources = wm831x_ldo6_resources,
1361 },
1362 {
1363 .name = "wm831x-aldo",
1364 .id = 7,
1365 .num_resources = ARRAY_SIZE(wm831x_ldo7_resources),
1366 .resources = wm831x_ldo7_resources,
1367 },
1368 {
1369 .name = "wm831x-aldo",
1370 .id = 8,
1371 .num_resources = ARRAY_SIZE(wm831x_ldo8_resources),
1372 .resources = wm831x_ldo8_resources,
1373 },
1374 {
1375 .name = "wm831x-aldo",
1376 .id = 9,
1377 .num_resources = ARRAY_SIZE(wm831x_ldo9_resources),
1378 .resources = wm831x_ldo9_resources,
1379 },
1380 {
1381 .name = "wm831x-aldo",
1382 .id = 10,
1383 .num_resources = ARRAY_SIZE(wm831x_ldo10_resources),
1384 .resources = wm831x_ldo10_resources,
1385 },
1386 {
1387 .name = "wm831x-alive-ldo",
1388 .id = 11,
1389 .num_resources = ARRAY_SIZE(wm831x_ldo11_resources),
1390 .resources = wm831x_ldo11_resources,
1391 },
1392 {
1393 .name = "wm831x-on",
1394 .num_resources = ARRAY_SIZE(wm831x_on_resources),
1395 .resources = wm831x_on_resources,
1396 },
1397 {
1398 .name = "wm831x-rtc",
1399 .num_resources = ARRAY_SIZE(wm831x_rtc_resources),
1400 .resources = wm831x_rtc_resources,
1401 },
1402 {
1403 .name = "wm831x-status",
1404 .id = 1,
1405 .num_resources = ARRAY_SIZE(wm831x_status1_resources),
1406 .resources = wm831x_status1_resources,
1407 },
1408 {
1409 .name = "wm831x-status",
1410 .id = 2,
1411 .num_resources = ARRAY_SIZE(wm831x_status2_resources),
1412 .resources = wm831x_status2_resources,
1413 },
1414 {
1415 .name = "wm831x-watchdog",
1416 .num_resources = ARRAY_SIZE(wm831x_wdt_resources),
1417 .resources = wm831x_wdt_resources,
1418 },
1419};
1420
Mark Brown266a5e02011-06-02 19:18:49 +01001421static struct mfd_cell touch_devs[] = {
1422 {
1423 .name = "wm831x-touch",
1424 .num_resources = ARRAY_SIZE(wm831x_touch_resources),
1425 .resources = wm831x_touch_resources,
1426 },
1427};
1428
1429
Mark Brown63aed852009-07-27 14:45:55 +01001430static struct mfd_cell backlight_devs[] = {
1431 {
1432 .name = "wm831x-backlight",
1433 },
1434};
1435
Mark Brownd2bedfe2009-07-27 14:45:52 +01001436/*
1437 * Instantiate the generic non-control parts of the device.
1438 */
Mark Browne5b48682010-10-19 23:57:56 +02001439int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
Mark Brownd2bedfe2009-07-27 14:45:52 +01001440{
1441 struct wm831x_pdata *pdata = wm831x->dev->platform_data;
Mark Browneb503dc2011-06-02 19:18:48 +01001442 int rev, wm831x_num;
Mark Brownd2bedfe2009-07-27 14:45:52 +01001443 enum wm831x_parent parent;
Mark Brown0b14c222011-04-04 11:04:42 +09001444 int ret, i;
Mark Brownd2bedfe2009-07-27 14:45:52 +01001445
1446 mutex_init(&wm831x->io_lock);
1447 mutex_init(&wm831x->key_lock);
Mark Brown7e9f9fd2009-07-27 14:45:54 +01001448 mutex_init(&wm831x->auxadc_lock);
Mark Brown473fe732010-02-23 11:08:06 +00001449 init_completion(&wm831x->auxadc_done);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001450 dev_set_drvdata(wm831x->dev, wm831x);
1451
1452 ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID);
1453 if (ret < 0) {
1454 dev_err(wm831x->dev, "Failed to read parent ID: %d\n", ret);
1455 goto err;
1456 }
Mark Brownb93cef52010-12-02 16:25:43 +00001457 switch (ret) {
1458 case 0x6204:
1459 case 0x6246:
1460 break;
1461 default:
Mark Brownd2bedfe2009-07-27 14:45:52 +01001462 dev_err(wm831x->dev, "Device is not a WM831x: ID %x\n", ret);
1463 ret = -EINVAL;
1464 goto err;
1465 }
1466
1467 ret = wm831x_reg_read(wm831x, WM831X_REVISION);
1468 if (ret < 0) {
1469 dev_err(wm831x->dev, "Failed to read revision: %d\n", ret);
1470 goto err;
1471 }
1472 rev = (ret & WM831X_PARENT_REV_MASK) >> WM831X_PARENT_REV_SHIFT;
1473
1474 ret = wm831x_reg_read(wm831x, WM831X_RESET_ID);
1475 if (ret < 0) {
1476 dev_err(wm831x->dev, "Failed to read device ID: %d\n", ret);
1477 goto err;
1478 }
1479
Mark Brown894362f2009-10-01 15:41:04 +01001480 /* Some engineering samples do not have the ID set, rely on
1481 * the device being registered correctly.
1482 */
1483 if (ret == 0) {
1484 dev_info(wm831x->dev, "Device is an engineering sample\n");
1485 ret = id;
1486 }
1487
Mark Brownd2bedfe2009-07-27 14:45:52 +01001488 switch (ret) {
Mark Brown894362f2009-10-01 15:41:04 +01001489 case WM8310:
Mark Brownd2bedfe2009-07-27 14:45:52 +01001490 parent = WM8310;
Mark Brown6f2ecaa2009-10-01 15:41:05 +01001491 wm831x->num_gpio = 16;
Mark Brownb03b4d7c2010-04-08 10:02:39 +02001492 wm831x->charger_irq_wake = 1;
Mark Brownf92e8f82010-02-17 18:45:25 +00001493 if (rev > 0) {
1494 wm831x->has_gpio_ena = 1;
1495 wm831x->has_cs_sts = 1;
1496 }
1497
Mark Brown894362f2009-10-01 15:41:04 +01001498 dev_info(wm831x->dev, "WM8310 revision %c\n", 'A' + rev);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001499 break;
1500
Mark Brown894362f2009-10-01 15:41:04 +01001501 case WM8311:
Mark Brownd2bedfe2009-07-27 14:45:52 +01001502 parent = WM8311;
Mark Brown6f2ecaa2009-10-01 15:41:05 +01001503 wm831x->num_gpio = 16;
Mark Brownb03b4d7c2010-04-08 10:02:39 +02001504 wm831x->charger_irq_wake = 1;
Mark Brownf92e8f82010-02-17 18:45:25 +00001505 if (rev > 0) {
1506 wm831x->has_gpio_ena = 1;
1507 wm831x->has_cs_sts = 1;
1508 }
1509
Mark Brown894362f2009-10-01 15:41:04 +01001510 dev_info(wm831x->dev, "WM8311 revision %c\n", 'A' + rev);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001511 break;
1512
Mark Brown894362f2009-10-01 15:41:04 +01001513 case WM8312:
Mark Brownd2bedfe2009-07-27 14:45:52 +01001514 parent = WM8312;
Mark Brown6f2ecaa2009-10-01 15:41:05 +01001515 wm831x->num_gpio = 16;
Mark Brownb03b4d7c2010-04-08 10:02:39 +02001516 wm831x->charger_irq_wake = 1;
Mark Brownf92e8f82010-02-17 18:45:25 +00001517 if (rev > 0) {
1518 wm831x->has_gpio_ena = 1;
1519 wm831x->has_cs_sts = 1;
1520 }
1521
Mark Brown894362f2009-10-01 15:41:04 +01001522 dev_info(wm831x->dev, "WM8312 revision %c\n", 'A' + rev);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001523 break;
1524
Mark Brownd4e0a892009-10-01 15:41:07 +01001525 case WM8320:
1526 parent = WM8320;
1527 wm831x->num_gpio = 12;
1528 dev_info(wm831x->dev, "WM8320 revision %c\n", 'A' + rev);
1529 break;
1530
Mark Brown88913522010-07-21 14:23:37 +01001531 case WM8321:
1532 parent = WM8321;
1533 wm831x->num_gpio = 12;
1534 dev_info(wm831x->dev, "WM8321 revision %c\n", 'A' + rev);
1535 break;
1536
Mark Brown0b315882010-09-28 09:13:39 -07001537 case WM8325:
1538 parent = WM8325;
1539 wm831x->num_gpio = 12;
1540 dev_info(wm831x->dev, "WM8325 revision %c\n", 'A' + rev);
1541 break;
1542
Mark Brown412dc112010-11-24 18:01:41 +00001543 case WM8326:
1544 parent = WM8326;
1545 wm831x->num_gpio = 12;
1546 dev_info(wm831x->dev, "WM8326 revision %c\n", 'A' + rev);
1547 break;
1548
Mark Brownd2bedfe2009-07-27 14:45:52 +01001549 default:
1550 dev_err(wm831x->dev, "Unknown WM831x device %04x\n", ret);
1551 ret = -EINVAL;
1552 goto err;
1553 }
1554
1555 /* This will need revisiting in future but is OK for all
1556 * current parts.
1557 */
1558 if (parent != id)
Mark Brown894362f2009-10-01 15:41:04 +01001559 dev_warn(wm831x->dev, "Device was registered as a WM%lx\n",
Mark Brownd2bedfe2009-07-27 14:45:52 +01001560 id);
1561
1562 /* Bootstrap the user key */
1563 ret = wm831x_reg_read(wm831x, WM831X_SECURITY_KEY);
1564 if (ret < 0) {
1565 dev_err(wm831x->dev, "Failed to read security key: %d\n", ret);
1566 goto err;
1567 }
1568 if (ret != 0) {
1569 dev_warn(wm831x->dev, "Security key had non-zero value %x\n",
1570 ret);
1571 wm831x_reg_write(wm831x, WM831X_SECURITY_KEY, 0);
1572 }
1573 wm831x->locked = 1;
1574
1575 if (pdata && pdata->pre_init) {
1576 ret = pdata->pre_init(wm831x);
1577 if (ret != 0) {
1578 dev_err(wm831x->dev, "pre_init() failed: %d\n", ret);
1579 goto err;
1580 }
1581 }
1582
Mark Brown0b14c222011-04-04 11:04:42 +09001583 if (pdata) {
1584 for (i = 0; i < ARRAY_SIZE(pdata->gpio_defaults); i++) {
1585 if (!pdata->gpio_defaults[i])
1586 continue;
1587
1588 wm831x_reg_write(wm831x,
1589 WM831X_GPIO1_CONTROL + i,
1590 pdata->gpio_defaults[i] & 0xffff);
1591 }
1592 }
1593
Mark Browneb503dc2011-06-02 19:18:48 +01001594 /* Multiply by 10 as we have many subdevices of the same type */
1595 if (pdata && pdata->wm831x_num)
1596 wm831x_num = pdata->wm831x_num * 10;
1597 else
1598 wm831x_num = -1;
1599
Mark Brown7d4d0a32009-07-27 14:45:53 +01001600 ret = wm831x_irq_init(wm831x, irq);
1601 if (ret != 0)
1602 goto err;
1603
Mark Brown473fe732010-02-23 11:08:06 +00001604 if (wm831x->irq_base) {
1605 ret = request_threaded_irq(wm831x->irq_base +
1606 WM831X_IRQ_AUXADC_DATA,
1607 NULL, wm831x_auxadc_irq, 0,
1608 "auxadc", wm831x);
1609 if (ret < 0)
1610 dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n",
1611 ret);
1612 }
1613
Mark Brownd2bedfe2009-07-27 14:45:52 +01001614 /* The core device is up, instantiate the subdevices. */
1615 switch (parent) {
1616 case WM8310:
Mark Browneb503dc2011-06-02 19:18:48 +01001617 ret = mfd_add_devices(wm831x->dev, wm831x_num,
Mark Brownd2bedfe2009-07-27 14:45:52 +01001618 wm8310_devs, ARRAY_SIZE(wm8310_devs),
Mark Brown5fb4d382009-11-11 16:10:22 +00001619 NULL, wm831x->irq_base);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001620 break;
1621
1622 case WM8311:
Mark Browneb503dc2011-06-02 19:18:48 +01001623 ret = mfd_add_devices(wm831x->dev, wm831x_num,
Mark Brownd2bedfe2009-07-27 14:45:52 +01001624 wm8311_devs, ARRAY_SIZE(wm8311_devs),
Mark Brown5fb4d382009-11-11 16:10:22 +00001625 NULL, wm831x->irq_base);
Mark Brown266a5e02011-06-02 19:18:49 +01001626 if (!pdata || !pdata->disable_touch)
1627 mfd_add_devices(wm831x->dev, wm831x_num,
1628 touch_devs, ARRAY_SIZE(touch_devs),
1629 NULL, wm831x->irq_base);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001630 break;
1631
1632 case WM8312:
Mark Browneb503dc2011-06-02 19:18:48 +01001633 ret = mfd_add_devices(wm831x->dev, wm831x_num,
Mark Brownd2bedfe2009-07-27 14:45:52 +01001634 wm8312_devs, ARRAY_SIZE(wm8312_devs),
Mark Brown5fb4d382009-11-11 16:10:22 +00001635 NULL, wm831x->irq_base);
Mark Brown266a5e02011-06-02 19:18:49 +01001636 if (!pdata || !pdata->disable_touch)
1637 mfd_add_devices(wm831x->dev, wm831x_num,
1638 touch_devs, ARRAY_SIZE(touch_devs),
1639 NULL, wm831x->irq_base);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001640 break;
1641
Mark Brownd4e0a892009-10-01 15:41:07 +01001642 case WM8320:
Mark Brown88913522010-07-21 14:23:37 +01001643 case WM8321:
Mark Brown0b315882010-09-28 09:13:39 -07001644 case WM8325:
Mark Brown412dc112010-11-24 18:01:41 +00001645 case WM8326:
Mark Browneb503dc2011-06-02 19:18:48 +01001646 ret = mfd_add_devices(wm831x->dev, wm831x_num,
Mark Brown0b315882010-09-28 09:13:39 -07001647 wm8320_devs, ARRAY_SIZE(wm8320_devs),
Mark Brownbd7c72e2010-11-24 18:01:39 +00001648 NULL, wm831x->irq_base);
Mark Brown0b315882010-09-28 09:13:39 -07001649 break;
1650
Mark Brownd2bedfe2009-07-27 14:45:52 +01001651 default:
1652 /* If this happens the bus probe function is buggy */
1653 BUG();
1654 }
1655
1656 if (ret != 0) {
1657 dev_err(wm831x->dev, "Failed to add children\n");
Mark Brown7d4d0a32009-07-27 14:45:53 +01001658 goto err_irq;
Mark Brownd2bedfe2009-07-27 14:45:52 +01001659 }
1660
Mark Brown63aed852009-07-27 14:45:55 +01001661 if (pdata && pdata->backlight) {
1662 /* Treat errors as non-critical */
Mark Browneb503dc2011-06-02 19:18:48 +01001663 ret = mfd_add_devices(wm831x->dev, wm831x_num, backlight_devs,
Mark Brown5fb4d382009-11-11 16:10:22 +00001664 ARRAY_SIZE(backlight_devs), NULL,
1665 wm831x->irq_base);
Mark Brown63aed852009-07-27 14:45:55 +01001666 if (ret < 0)
1667 dev_err(wm831x->dev, "Failed to add backlight: %d\n",
1668 ret);
1669 }
1670
Mark Brown6704e512009-07-27 14:45:56 +01001671 wm831x_otp_init(wm831x);
1672
Mark Brownd2bedfe2009-07-27 14:45:52 +01001673 if (pdata && pdata->post_init) {
1674 ret = pdata->post_init(wm831x);
1675 if (ret != 0) {
1676 dev_err(wm831x->dev, "post_init() failed: %d\n", ret);
Mark Brown7d4d0a32009-07-27 14:45:53 +01001677 goto err_irq;
Mark Brownd2bedfe2009-07-27 14:45:52 +01001678 }
1679 }
1680
1681 return 0;
1682
Mark Brown7d4d0a32009-07-27 14:45:53 +01001683err_irq:
1684 wm831x_irq_exit(wm831x);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001685err:
1686 mfd_remove_devices(wm831x->dev);
1687 kfree(wm831x);
1688 return ret;
1689}
1690
Mark Browne5b48682010-10-19 23:57:56 +02001691void wm831x_device_exit(struct wm831x *wm831x)
Mark Brownd2bedfe2009-07-27 14:45:52 +01001692{
Mark Brown6704e512009-07-27 14:45:56 +01001693 wm831x_otp_exit(wm831x);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001694 mfd_remove_devices(wm831x->dev);
Mark Brown473fe732010-02-23 11:08:06 +00001695 if (wm831x->irq_base)
1696 free_irq(wm831x->irq_base + WM831X_IRQ_AUXADC_DATA, wm831x);
Mark Brown7d4d0a32009-07-27 14:45:53 +01001697 wm831x_irq_exit(wm831x);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001698 kfree(wm831x);
1699}
1700
Mark Browne5b48682010-10-19 23:57:56 +02001701int wm831x_device_suspend(struct wm831x *wm831x)
Mark Brownb03b4d7c2010-04-08 10:02:39 +02001702{
1703 int reg, mask;
1704
1705 /* If the charger IRQs are a wake source then make sure we ack
1706 * them even if they're not actively being used (eg, no power
1707 * driver or no IRQ line wired up) then acknowledge the
1708 * interrupts otherwise suspend won't last very long.
1709 */
1710 if (wm831x->charger_irq_wake) {
1711 reg = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_2_MASK);
1712
1713 mask = WM831X_CHG_BATT_HOT_EINT |
1714 WM831X_CHG_BATT_COLD_EINT |
1715 WM831X_CHG_BATT_FAIL_EINT |
1716 WM831X_CHG_OV_EINT | WM831X_CHG_END_EINT |
1717 WM831X_CHG_TO_EINT | WM831X_CHG_MODE_EINT |
1718 WM831X_CHG_START_EINT;
1719
1720 /* If any of the interrupts are masked read the statuses */
1721 if (reg & mask)
1722 reg = wm831x_reg_read(wm831x,
1723 WM831X_INTERRUPT_STATUS_2);
1724
1725 if (reg & mask) {
1726 dev_info(wm831x->dev,
1727 "Acknowledging masked charger IRQs: %x\n",
1728 reg & mask);
1729 wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_2,
1730 reg & mask);
1731 }
1732 }
1733
1734 return 0;
1735}
1736
Mark Browne5b48682010-10-19 23:57:56 +02001737MODULE_DESCRIPTION("Core support for the WM831X AudioPlus PMIC");
Mark Brownd2bedfe2009-07-27 14:45:52 +01001738MODULE_LICENSE("GPL");
1739MODULE_AUTHOR("Mark Brown");