blob: 282e76ab678f15c4cb3d792f571f47e04f6fd872 [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;
Mark Brownb00cd682011-06-20 12:25:58 +0100298 r |= val & mask;
Mark Brownd2bedfe2009-07-27 14:45:52 +0100299
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
309static struct resource wm831x_dcdc1_resources[] = {
310 {
311 .start = WM831X_DC1_CONTROL_1,
312 .end = WM831X_DC1_DVS_CONTROL,
313 .flags = IORESOURCE_IO,
314 },
315 {
316 .name = "UV",
317 .start = WM831X_IRQ_UV_DC1,
318 .end = WM831X_IRQ_UV_DC1,
319 .flags = IORESOURCE_IRQ,
320 },
321 {
322 .name = "HC",
323 .start = WM831X_IRQ_HC_DC1,
324 .end = WM831X_IRQ_HC_DC1,
325 .flags = IORESOURCE_IRQ,
326 },
327};
328
329
330static struct resource wm831x_dcdc2_resources[] = {
331 {
332 .start = WM831X_DC2_CONTROL_1,
333 .end = WM831X_DC2_DVS_CONTROL,
334 .flags = IORESOURCE_IO,
335 },
336 {
337 .name = "UV",
338 .start = WM831X_IRQ_UV_DC2,
339 .end = WM831X_IRQ_UV_DC2,
340 .flags = IORESOURCE_IRQ,
341 },
342 {
343 .name = "HC",
344 .start = WM831X_IRQ_HC_DC2,
345 .end = WM831X_IRQ_HC_DC2,
346 .flags = IORESOURCE_IRQ,
347 },
348};
349
350static struct resource wm831x_dcdc3_resources[] = {
351 {
352 .start = WM831X_DC3_CONTROL_1,
353 .end = WM831X_DC3_SLEEP_CONTROL,
354 .flags = IORESOURCE_IO,
355 },
356 {
357 .name = "UV",
358 .start = WM831X_IRQ_UV_DC3,
359 .end = WM831X_IRQ_UV_DC3,
360 .flags = IORESOURCE_IRQ,
361 },
362};
363
364static struct resource wm831x_dcdc4_resources[] = {
365 {
366 .start = WM831X_DC4_CONTROL,
367 .end = WM831X_DC4_SLEEP_CONTROL,
368 .flags = IORESOURCE_IO,
369 },
370 {
371 .name = "UV",
372 .start = WM831X_IRQ_UV_DC4,
373 .end = WM831X_IRQ_UV_DC4,
374 .flags = IORESOURCE_IRQ,
375 },
376};
377
Mark Brownd4e0a892009-10-01 15:41:07 +0100378static struct resource wm8320_dcdc4_buck_resources[] = {
379 {
380 .start = WM831X_DC4_CONTROL,
381 .end = WM832X_DC4_SLEEP_CONTROL,
382 .flags = IORESOURCE_IO,
383 },
384 {
385 .name = "UV",
386 .start = WM831X_IRQ_UV_DC4,
387 .end = WM831X_IRQ_UV_DC4,
388 .flags = IORESOURCE_IRQ,
389 },
390};
391
Mark Brownd2bedfe2009-07-27 14:45:52 +0100392static struct resource wm831x_gpio_resources[] = {
393 {
394 .start = WM831X_IRQ_GPIO_1,
395 .end = WM831X_IRQ_GPIO_16,
396 .flags = IORESOURCE_IRQ,
397 },
398};
399
400static struct resource wm831x_isink1_resources[] = {
401 {
402 .start = WM831X_CURRENT_SINK_1,
403 .end = WM831X_CURRENT_SINK_1,
404 .flags = IORESOURCE_IO,
405 },
406 {
407 .start = WM831X_IRQ_CS1,
408 .end = WM831X_IRQ_CS1,
409 .flags = IORESOURCE_IRQ,
410 },
411};
412
413static struct resource wm831x_isink2_resources[] = {
414 {
415 .start = WM831X_CURRENT_SINK_2,
416 .end = WM831X_CURRENT_SINK_2,
417 .flags = IORESOURCE_IO,
418 },
419 {
420 .start = WM831X_IRQ_CS2,
421 .end = WM831X_IRQ_CS2,
422 .flags = IORESOURCE_IRQ,
423 },
424};
425
426static struct resource wm831x_ldo1_resources[] = {
427 {
428 .start = WM831X_LDO1_CONTROL,
429 .end = WM831X_LDO1_SLEEP_CONTROL,
430 .flags = IORESOURCE_IO,
431 },
432 {
433 .name = "UV",
434 .start = WM831X_IRQ_UV_LDO1,
435 .end = WM831X_IRQ_UV_LDO1,
436 .flags = IORESOURCE_IRQ,
437 },
438};
439
440static struct resource wm831x_ldo2_resources[] = {
441 {
442 .start = WM831X_LDO2_CONTROL,
443 .end = WM831X_LDO2_SLEEP_CONTROL,
444 .flags = IORESOURCE_IO,
445 },
446 {
447 .name = "UV",
448 .start = WM831X_IRQ_UV_LDO2,
449 .end = WM831X_IRQ_UV_LDO2,
450 .flags = IORESOURCE_IRQ,
451 },
452};
453
454static struct resource wm831x_ldo3_resources[] = {
455 {
456 .start = WM831X_LDO3_CONTROL,
457 .end = WM831X_LDO3_SLEEP_CONTROL,
458 .flags = IORESOURCE_IO,
459 },
460 {
461 .name = "UV",
462 .start = WM831X_IRQ_UV_LDO3,
463 .end = WM831X_IRQ_UV_LDO3,
464 .flags = IORESOURCE_IRQ,
465 },
466};
467
468static struct resource wm831x_ldo4_resources[] = {
469 {
470 .start = WM831X_LDO4_CONTROL,
471 .end = WM831X_LDO4_SLEEP_CONTROL,
472 .flags = IORESOURCE_IO,
473 },
474 {
475 .name = "UV",
476 .start = WM831X_IRQ_UV_LDO4,
477 .end = WM831X_IRQ_UV_LDO4,
478 .flags = IORESOURCE_IRQ,
479 },
480};
481
482static struct resource wm831x_ldo5_resources[] = {
483 {
484 .start = WM831X_LDO5_CONTROL,
485 .end = WM831X_LDO5_SLEEP_CONTROL,
486 .flags = IORESOURCE_IO,
487 },
488 {
489 .name = "UV",
490 .start = WM831X_IRQ_UV_LDO5,
491 .end = WM831X_IRQ_UV_LDO5,
492 .flags = IORESOURCE_IRQ,
493 },
494};
495
496static struct resource wm831x_ldo6_resources[] = {
497 {
498 .start = WM831X_LDO6_CONTROL,
499 .end = WM831X_LDO6_SLEEP_CONTROL,
500 .flags = IORESOURCE_IO,
501 },
502 {
503 .name = "UV",
504 .start = WM831X_IRQ_UV_LDO6,
505 .end = WM831X_IRQ_UV_LDO6,
506 .flags = IORESOURCE_IRQ,
507 },
508};
509
510static struct resource wm831x_ldo7_resources[] = {
511 {
512 .start = WM831X_LDO7_CONTROL,
513 .end = WM831X_LDO7_SLEEP_CONTROL,
514 .flags = IORESOURCE_IO,
515 },
516 {
517 .name = "UV",
518 .start = WM831X_IRQ_UV_LDO7,
519 .end = WM831X_IRQ_UV_LDO7,
520 .flags = IORESOURCE_IRQ,
521 },
522};
523
524static struct resource wm831x_ldo8_resources[] = {
525 {
526 .start = WM831X_LDO8_CONTROL,
527 .end = WM831X_LDO8_SLEEP_CONTROL,
528 .flags = IORESOURCE_IO,
529 },
530 {
531 .name = "UV",
532 .start = WM831X_IRQ_UV_LDO8,
533 .end = WM831X_IRQ_UV_LDO8,
534 .flags = IORESOURCE_IRQ,
535 },
536};
537
538static struct resource wm831x_ldo9_resources[] = {
539 {
540 .start = WM831X_LDO9_CONTROL,
541 .end = WM831X_LDO9_SLEEP_CONTROL,
542 .flags = IORESOURCE_IO,
543 },
544 {
545 .name = "UV",
546 .start = WM831X_IRQ_UV_LDO9,
547 .end = WM831X_IRQ_UV_LDO9,
548 .flags = IORESOURCE_IRQ,
549 },
550};
551
552static struct resource wm831x_ldo10_resources[] = {
553 {
554 .start = WM831X_LDO10_CONTROL,
555 .end = WM831X_LDO10_SLEEP_CONTROL,
556 .flags = IORESOURCE_IO,
557 },
558 {
559 .name = "UV",
560 .start = WM831X_IRQ_UV_LDO10,
561 .end = WM831X_IRQ_UV_LDO10,
562 .flags = IORESOURCE_IRQ,
563 },
564};
565
566static struct resource wm831x_ldo11_resources[] = {
567 {
568 .start = WM831X_LDO11_ON_CONTROL,
569 .end = WM831X_LDO11_SLEEP_CONTROL,
570 .flags = IORESOURCE_IO,
571 },
572};
573
574static struct resource wm831x_on_resources[] = {
575 {
576 .start = WM831X_IRQ_ON,
577 .end = WM831X_IRQ_ON,
578 .flags = IORESOURCE_IRQ,
579 },
580};
581
582
583static struct resource wm831x_power_resources[] = {
584 {
585 .name = "SYSLO",
586 .start = WM831X_IRQ_PPM_SYSLO,
587 .end = WM831X_IRQ_PPM_SYSLO,
588 .flags = IORESOURCE_IRQ,
589 },
590 {
591 .name = "PWR SRC",
592 .start = WM831X_IRQ_PPM_PWR_SRC,
593 .end = WM831X_IRQ_PPM_PWR_SRC,
594 .flags = IORESOURCE_IRQ,
595 },
596 {
597 .name = "USB CURR",
598 .start = WM831X_IRQ_PPM_USB_CURR,
599 .end = WM831X_IRQ_PPM_USB_CURR,
600 .flags = IORESOURCE_IRQ,
601 },
602 {
603 .name = "BATT HOT",
604 .start = WM831X_IRQ_CHG_BATT_HOT,
605 .end = WM831X_IRQ_CHG_BATT_HOT,
606 .flags = IORESOURCE_IRQ,
607 },
608 {
609 .name = "BATT COLD",
610 .start = WM831X_IRQ_CHG_BATT_COLD,
611 .end = WM831X_IRQ_CHG_BATT_COLD,
612 .flags = IORESOURCE_IRQ,
613 },
614 {
615 .name = "BATT FAIL",
616 .start = WM831X_IRQ_CHG_BATT_FAIL,
617 .end = WM831X_IRQ_CHG_BATT_FAIL,
618 .flags = IORESOURCE_IRQ,
619 },
620 {
621 .name = "OV",
622 .start = WM831X_IRQ_CHG_OV,
623 .end = WM831X_IRQ_CHG_OV,
624 .flags = IORESOURCE_IRQ,
625 },
626 {
627 .name = "END",
628 .start = WM831X_IRQ_CHG_END,
629 .end = WM831X_IRQ_CHG_END,
630 .flags = IORESOURCE_IRQ,
631 },
632 {
633 .name = "TO",
634 .start = WM831X_IRQ_CHG_TO,
635 .end = WM831X_IRQ_CHG_TO,
636 .flags = IORESOURCE_IRQ,
637 },
638 {
639 .name = "MODE",
640 .start = WM831X_IRQ_CHG_MODE,
641 .end = WM831X_IRQ_CHG_MODE,
642 .flags = IORESOURCE_IRQ,
643 },
644 {
645 .name = "START",
646 .start = WM831X_IRQ_CHG_START,
647 .end = WM831X_IRQ_CHG_START,
648 .flags = IORESOURCE_IRQ,
649 },
650};
651
652static struct resource wm831x_rtc_resources[] = {
653 {
654 .name = "PER",
655 .start = WM831X_IRQ_RTC_PER,
656 .end = WM831X_IRQ_RTC_PER,
657 .flags = IORESOURCE_IRQ,
658 },
659 {
660 .name = "ALM",
661 .start = WM831X_IRQ_RTC_ALM,
662 .end = WM831X_IRQ_RTC_ALM,
663 .flags = IORESOURCE_IRQ,
664 },
665};
666
667static struct resource wm831x_status1_resources[] = {
668 {
669 .start = WM831X_STATUS_LED_1,
670 .end = WM831X_STATUS_LED_1,
671 .flags = IORESOURCE_IO,
672 },
673};
674
675static struct resource wm831x_status2_resources[] = {
676 {
677 .start = WM831X_STATUS_LED_2,
678 .end = WM831X_STATUS_LED_2,
679 .flags = IORESOURCE_IO,
680 },
681};
682
683static struct resource wm831x_touch_resources[] = {
684 {
685 .name = "TCHPD",
686 .start = WM831X_IRQ_TCHPD,
687 .end = WM831X_IRQ_TCHPD,
688 .flags = IORESOURCE_IRQ,
689 },
690 {
691 .name = "TCHDATA",
692 .start = WM831X_IRQ_TCHDATA,
693 .end = WM831X_IRQ_TCHDATA,
694 .flags = IORESOURCE_IRQ,
695 },
696};
697
698static struct resource wm831x_wdt_resources[] = {
699 {
700 .start = WM831X_IRQ_WDOG_TO,
701 .end = WM831X_IRQ_WDOG_TO,
702 .flags = IORESOURCE_IRQ,
703 },
704};
705
706static struct mfd_cell wm8310_devs[] = {
707 {
Mark Brownc26964e2009-10-01 15:41:06 +0100708 .name = "wm831x-backup",
709 },
710 {
Mark Brownd2bedfe2009-07-27 14:45:52 +0100711 .name = "wm831x-buckv",
712 .id = 1,
713 .num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
714 .resources = wm831x_dcdc1_resources,
715 },
716 {
717 .name = "wm831x-buckv",
718 .id = 2,
719 .num_resources = ARRAY_SIZE(wm831x_dcdc2_resources),
720 .resources = wm831x_dcdc2_resources,
721 },
722 {
723 .name = "wm831x-buckp",
724 .id = 3,
725 .num_resources = ARRAY_SIZE(wm831x_dcdc3_resources),
726 .resources = wm831x_dcdc3_resources,
727 },
728 {
729 .name = "wm831x-boostp",
730 .id = 4,
731 .num_resources = ARRAY_SIZE(wm831x_dcdc4_resources),
732 .resources = wm831x_dcdc4_resources,
733 },
734 {
Mark Browna5e06782011-06-24 12:17:07 +0100735 .name = "wm831x-clk",
736 },
737 {
Mark Brownd2bedfe2009-07-27 14:45:52 +0100738 .name = "wm831x-epe",
739 .id = 1,
740 },
741 {
742 .name = "wm831x-epe",
743 .id = 2,
744 },
745 {
746 .name = "wm831x-gpio",
747 .num_resources = ARRAY_SIZE(wm831x_gpio_resources),
748 .resources = wm831x_gpio_resources,
749 },
750 {
751 .name = "wm831x-hwmon",
752 },
753 {
754 .name = "wm831x-isink",
755 .id = 1,
756 .num_resources = ARRAY_SIZE(wm831x_isink1_resources),
757 .resources = wm831x_isink1_resources,
758 },
759 {
760 .name = "wm831x-isink",
761 .id = 2,
762 .num_resources = ARRAY_SIZE(wm831x_isink2_resources),
763 .resources = wm831x_isink2_resources,
764 },
765 {
766 .name = "wm831x-ldo",
767 .id = 1,
768 .num_resources = ARRAY_SIZE(wm831x_ldo1_resources),
769 .resources = wm831x_ldo1_resources,
770 },
771 {
772 .name = "wm831x-ldo",
773 .id = 2,
774 .num_resources = ARRAY_SIZE(wm831x_ldo2_resources),
775 .resources = wm831x_ldo2_resources,
776 },
777 {
778 .name = "wm831x-ldo",
779 .id = 3,
780 .num_resources = ARRAY_SIZE(wm831x_ldo3_resources),
781 .resources = wm831x_ldo3_resources,
782 },
783 {
784 .name = "wm831x-ldo",
785 .id = 4,
786 .num_resources = ARRAY_SIZE(wm831x_ldo4_resources),
787 .resources = wm831x_ldo4_resources,
788 },
789 {
790 .name = "wm831x-ldo",
791 .id = 5,
792 .num_resources = ARRAY_SIZE(wm831x_ldo5_resources),
793 .resources = wm831x_ldo5_resources,
794 },
795 {
796 .name = "wm831x-ldo",
797 .id = 6,
798 .num_resources = ARRAY_SIZE(wm831x_ldo6_resources),
799 .resources = wm831x_ldo6_resources,
800 },
801 {
802 .name = "wm831x-aldo",
803 .id = 7,
804 .num_resources = ARRAY_SIZE(wm831x_ldo7_resources),
805 .resources = wm831x_ldo7_resources,
806 },
807 {
808 .name = "wm831x-aldo",
809 .id = 8,
810 .num_resources = ARRAY_SIZE(wm831x_ldo8_resources),
811 .resources = wm831x_ldo8_resources,
812 },
813 {
814 .name = "wm831x-aldo",
815 .id = 9,
816 .num_resources = ARRAY_SIZE(wm831x_ldo9_resources),
817 .resources = wm831x_ldo9_resources,
818 },
819 {
820 .name = "wm831x-aldo",
821 .id = 10,
822 .num_resources = ARRAY_SIZE(wm831x_ldo10_resources),
823 .resources = wm831x_ldo10_resources,
824 },
825 {
826 .name = "wm831x-alive-ldo",
827 .id = 11,
828 .num_resources = ARRAY_SIZE(wm831x_ldo11_resources),
829 .resources = wm831x_ldo11_resources,
830 },
831 {
832 .name = "wm831x-on",
833 .num_resources = ARRAY_SIZE(wm831x_on_resources),
834 .resources = wm831x_on_resources,
835 },
836 {
837 .name = "wm831x-power",
838 .num_resources = ARRAY_SIZE(wm831x_power_resources),
839 .resources = wm831x_power_resources,
840 },
841 {
Mark Brownd2bedfe2009-07-27 14:45:52 +0100842 .name = "wm831x-status",
843 .id = 1,
844 .num_resources = ARRAY_SIZE(wm831x_status1_resources),
845 .resources = wm831x_status1_resources,
846 },
847 {
848 .name = "wm831x-status",
849 .id = 2,
850 .num_resources = ARRAY_SIZE(wm831x_status2_resources),
851 .resources = wm831x_status2_resources,
852 },
853 {
854 .name = "wm831x-watchdog",
855 .num_resources = ARRAY_SIZE(wm831x_wdt_resources),
856 .resources = wm831x_wdt_resources,
857 },
858};
859
860static struct mfd_cell wm8311_devs[] = {
861 {
Mark Brownc26964e2009-10-01 15:41:06 +0100862 .name = "wm831x-backup",
863 },
864 {
Mark Brownd2bedfe2009-07-27 14:45:52 +0100865 .name = "wm831x-buckv",
866 .id = 1,
867 .num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
868 .resources = wm831x_dcdc1_resources,
869 },
870 {
871 .name = "wm831x-buckv",
872 .id = 2,
873 .num_resources = ARRAY_SIZE(wm831x_dcdc2_resources),
874 .resources = wm831x_dcdc2_resources,
875 },
876 {
877 .name = "wm831x-buckp",
878 .id = 3,
879 .num_resources = ARRAY_SIZE(wm831x_dcdc3_resources),
880 .resources = wm831x_dcdc3_resources,
881 },
882 {
883 .name = "wm831x-boostp",
884 .id = 4,
885 .num_resources = ARRAY_SIZE(wm831x_dcdc4_resources),
886 .resources = wm831x_dcdc4_resources,
887 },
888 {
Mark Browna5e06782011-06-24 12:17:07 +0100889 .name = "wm831x-clk",
890 },
891 {
Mark Brownd2bedfe2009-07-27 14:45:52 +0100892 .name = "wm831x-epe",
893 .id = 1,
894 },
895 {
896 .name = "wm831x-epe",
897 .id = 2,
898 },
899 {
900 .name = "wm831x-gpio",
901 .num_resources = ARRAY_SIZE(wm831x_gpio_resources),
902 .resources = wm831x_gpio_resources,
903 },
904 {
905 .name = "wm831x-hwmon",
906 },
907 {
908 .name = "wm831x-isink",
909 .id = 1,
910 .num_resources = ARRAY_SIZE(wm831x_isink1_resources),
911 .resources = wm831x_isink1_resources,
912 },
913 {
914 .name = "wm831x-isink",
915 .id = 2,
916 .num_resources = ARRAY_SIZE(wm831x_isink2_resources),
917 .resources = wm831x_isink2_resources,
918 },
919 {
920 .name = "wm831x-ldo",
921 .id = 1,
922 .num_resources = ARRAY_SIZE(wm831x_ldo1_resources),
923 .resources = wm831x_ldo1_resources,
924 },
925 {
926 .name = "wm831x-ldo",
927 .id = 2,
928 .num_resources = ARRAY_SIZE(wm831x_ldo2_resources),
929 .resources = wm831x_ldo2_resources,
930 },
931 {
932 .name = "wm831x-ldo",
933 .id = 3,
934 .num_resources = ARRAY_SIZE(wm831x_ldo3_resources),
935 .resources = wm831x_ldo3_resources,
936 },
937 {
938 .name = "wm831x-ldo",
939 .id = 4,
940 .num_resources = ARRAY_SIZE(wm831x_ldo4_resources),
941 .resources = wm831x_ldo4_resources,
942 },
943 {
944 .name = "wm831x-ldo",
945 .id = 5,
946 .num_resources = ARRAY_SIZE(wm831x_ldo5_resources),
947 .resources = wm831x_ldo5_resources,
948 },
949 {
950 .name = "wm831x-aldo",
951 .id = 7,
952 .num_resources = ARRAY_SIZE(wm831x_ldo7_resources),
953 .resources = wm831x_ldo7_resources,
954 },
955 {
956 .name = "wm831x-alive-ldo",
957 .id = 11,
958 .num_resources = ARRAY_SIZE(wm831x_ldo11_resources),
959 .resources = wm831x_ldo11_resources,
960 },
961 {
962 .name = "wm831x-on",
963 .num_resources = ARRAY_SIZE(wm831x_on_resources),
964 .resources = wm831x_on_resources,
965 },
966 {
967 .name = "wm831x-power",
968 .num_resources = ARRAY_SIZE(wm831x_power_resources),
969 .resources = wm831x_power_resources,
970 },
971 {
Mark Brownd2bedfe2009-07-27 14:45:52 +0100972 .name = "wm831x-status",
973 .id = 1,
974 .num_resources = ARRAY_SIZE(wm831x_status1_resources),
975 .resources = wm831x_status1_resources,
976 },
977 {
978 .name = "wm831x-status",
979 .id = 2,
980 .num_resources = ARRAY_SIZE(wm831x_status2_resources),
981 .resources = wm831x_status2_resources,
982 },
983 {
Mark Brownd2bedfe2009-07-27 14:45:52 +0100984 .name = "wm831x-watchdog",
985 .num_resources = ARRAY_SIZE(wm831x_wdt_resources),
986 .resources = wm831x_wdt_resources,
987 },
988};
989
990static struct mfd_cell wm8312_devs[] = {
991 {
Mark Brownc26964e2009-10-01 15:41:06 +0100992 .name = "wm831x-backup",
993 },
994 {
Mark Brownd2bedfe2009-07-27 14:45:52 +0100995 .name = "wm831x-buckv",
996 .id = 1,
997 .num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
998 .resources = wm831x_dcdc1_resources,
999 },
1000 {
1001 .name = "wm831x-buckv",
1002 .id = 2,
1003 .num_resources = ARRAY_SIZE(wm831x_dcdc2_resources),
1004 .resources = wm831x_dcdc2_resources,
1005 },
1006 {
1007 .name = "wm831x-buckp",
1008 .id = 3,
1009 .num_resources = ARRAY_SIZE(wm831x_dcdc3_resources),
1010 .resources = wm831x_dcdc3_resources,
1011 },
1012 {
1013 .name = "wm831x-boostp",
1014 .id = 4,
1015 .num_resources = ARRAY_SIZE(wm831x_dcdc4_resources),
1016 .resources = wm831x_dcdc4_resources,
1017 },
1018 {
Mark Browna5e06782011-06-24 12:17:07 +01001019 .name = "wm831x-clk",
1020 },
1021 {
Mark Brownd2bedfe2009-07-27 14:45:52 +01001022 .name = "wm831x-epe",
1023 .id = 1,
1024 },
1025 {
1026 .name = "wm831x-epe",
1027 .id = 2,
1028 },
1029 {
1030 .name = "wm831x-gpio",
1031 .num_resources = ARRAY_SIZE(wm831x_gpio_resources),
1032 .resources = wm831x_gpio_resources,
1033 },
1034 {
1035 .name = "wm831x-hwmon",
1036 },
1037 {
1038 .name = "wm831x-isink",
1039 .id = 1,
1040 .num_resources = ARRAY_SIZE(wm831x_isink1_resources),
1041 .resources = wm831x_isink1_resources,
1042 },
1043 {
1044 .name = "wm831x-isink",
1045 .id = 2,
1046 .num_resources = ARRAY_SIZE(wm831x_isink2_resources),
1047 .resources = wm831x_isink2_resources,
1048 },
1049 {
1050 .name = "wm831x-ldo",
1051 .id = 1,
1052 .num_resources = ARRAY_SIZE(wm831x_ldo1_resources),
1053 .resources = wm831x_ldo1_resources,
1054 },
1055 {
1056 .name = "wm831x-ldo",
1057 .id = 2,
1058 .num_resources = ARRAY_SIZE(wm831x_ldo2_resources),
1059 .resources = wm831x_ldo2_resources,
1060 },
1061 {
1062 .name = "wm831x-ldo",
1063 .id = 3,
1064 .num_resources = ARRAY_SIZE(wm831x_ldo3_resources),
1065 .resources = wm831x_ldo3_resources,
1066 },
1067 {
1068 .name = "wm831x-ldo",
1069 .id = 4,
1070 .num_resources = ARRAY_SIZE(wm831x_ldo4_resources),
1071 .resources = wm831x_ldo4_resources,
1072 },
1073 {
1074 .name = "wm831x-ldo",
1075 .id = 5,
1076 .num_resources = ARRAY_SIZE(wm831x_ldo5_resources),
1077 .resources = wm831x_ldo5_resources,
1078 },
1079 {
1080 .name = "wm831x-ldo",
1081 .id = 6,
1082 .num_resources = ARRAY_SIZE(wm831x_ldo6_resources),
1083 .resources = wm831x_ldo6_resources,
1084 },
1085 {
1086 .name = "wm831x-aldo",
1087 .id = 7,
1088 .num_resources = ARRAY_SIZE(wm831x_ldo7_resources),
1089 .resources = wm831x_ldo7_resources,
1090 },
1091 {
1092 .name = "wm831x-aldo",
1093 .id = 8,
1094 .num_resources = ARRAY_SIZE(wm831x_ldo8_resources),
1095 .resources = wm831x_ldo8_resources,
1096 },
1097 {
1098 .name = "wm831x-aldo",
1099 .id = 9,
1100 .num_resources = ARRAY_SIZE(wm831x_ldo9_resources),
1101 .resources = wm831x_ldo9_resources,
1102 },
1103 {
1104 .name = "wm831x-aldo",
1105 .id = 10,
1106 .num_resources = ARRAY_SIZE(wm831x_ldo10_resources),
1107 .resources = wm831x_ldo10_resources,
1108 },
1109 {
1110 .name = "wm831x-alive-ldo",
1111 .id = 11,
1112 .num_resources = ARRAY_SIZE(wm831x_ldo11_resources),
1113 .resources = wm831x_ldo11_resources,
1114 },
1115 {
1116 .name = "wm831x-on",
1117 .num_resources = ARRAY_SIZE(wm831x_on_resources),
1118 .resources = wm831x_on_resources,
1119 },
1120 {
1121 .name = "wm831x-power",
1122 .num_resources = ARRAY_SIZE(wm831x_power_resources),
1123 .resources = wm831x_power_resources,
1124 },
1125 {
Mark Brownd2bedfe2009-07-27 14:45:52 +01001126 .name = "wm831x-status",
1127 .id = 1,
1128 .num_resources = ARRAY_SIZE(wm831x_status1_resources),
1129 .resources = wm831x_status1_resources,
1130 },
1131 {
1132 .name = "wm831x-status",
1133 .id = 2,
1134 .num_resources = ARRAY_SIZE(wm831x_status2_resources),
1135 .resources = wm831x_status2_resources,
1136 },
1137 {
Mark Brownd2bedfe2009-07-27 14:45:52 +01001138 .name = "wm831x-watchdog",
1139 .num_resources = ARRAY_SIZE(wm831x_wdt_resources),
1140 .resources = wm831x_wdt_resources,
1141 },
1142};
1143
Mark Brownd4e0a892009-10-01 15:41:07 +01001144static struct mfd_cell wm8320_devs[] = {
1145 {
1146 .name = "wm831x-backup",
1147 },
1148 {
1149 .name = "wm831x-buckv",
1150 .id = 1,
1151 .num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
1152 .resources = wm831x_dcdc1_resources,
1153 },
1154 {
1155 .name = "wm831x-buckv",
1156 .id = 2,
1157 .num_resources = ARRAY_SIZE(wm831x_dcdc2_resources),
1158 .resources = wm831x_dcdc2_resources,
1159 },
1160 {
1161 .name = "wm831x-buckp",
1162 .id = 3,
1163 .num_resources = ARRAY_SIZE(wm831x_dcdc3_resources),
1164 .resources = wm831x_dcdc3_resources,
1165 },
1166 {
1167 .name = "wm831x-buckp",
1168 .id = 4,
1169 .num_resources = ARRAY_SIZE(wm8320_dcdc4_buck_resources),
1170 .resources = wm8320_dcdc4_buck_resources,
1171 },
1172 {
Mark Browna5e06782011-06-24 12:17:07 +01001173 .name = "wm831x-clk",
1174 },
1175 {
Mark Brownd4e0a892009-10-01 15:41:07 +01001176 .name = "wm831x-gpio",
1177 .num_resources = ARRAY_SIZE(wm831x_gpio_resources),
1178 .resources = wm831x_gpio_resources,
1179 },
1180 {
1181 .name = "wm831x-hwmon",
1182 },
1183 {
1184 .name = "wm831x-ldo",
1185 .id = 1,
1186 .num_resources = ARRAY_SIZE(wm831x_ldo1_resources),
1187 .resources = wm831x_ldo1_resources,
1188 },
1189 {
1190 .name = "wm831x-ldo",
1191 .id = 2,
1192 .num_resources = ARRAY_SIZE(wm831x_ldo2_resources),
1193 .resources = wm831x_ldo2_resources,
1194 },
1195 {
1196 .name = "wm831x-ldo",
1197 .id = 3,
1198 .num_resources = ARRAY_SIZE(wm831x_ldo3_resources),
1199 .resources = wm831x_ldo3_resources,
1200 },
1201 {
1202 .name = "wm831x-ldo",
1203 .id = 4,
1204 .num_resources = ARRAY_SIZE(wm831x_ldo4_resources),
1205 .resources = wm831x_ldo4_resources,
1206 },
1207 {
1208 .name = "wm831x-ldo",
1209 .id = 5,
1210 .num_resources = ARRAY_SIZE(wm831x_ldo5_resources),
1211 .resources = wm831x_ldo5_resources,
1212 },
1213 {
1214 .name = "wm831x-ldo",
1215 .id = 6,
1216 .num_resources = ARRAY_SIZE(wm831x_ldo6_resources),
1217 .resources = wm831x_ldo6_resources,
1218 },
1219 {
1220 .name = "wm831x-aldo",
1221 .id = 7,
1222 .num_resources = ARRAY_SIZE(wm831x_ldo7_resources),
1223 .resources = wm831x_ldo7_resources,
1224 },
1225 {
1226 .name = "wm831x-aldo",
1227 .id = 8,
1228 .num_resources = ARRAY_SIZE(wm831x_ldo8_resources),
1229 .resources = wm831x_ldo8_resources,
1230 },
1231 {
1232 .name = "wm831x-aldo",
1233 .id = 9,
1234 .num_resources = ARRAY_SIZE(wm831x_ldo9_resources),
1235 .resources = wm831x_ldo9_resources,
1236 },
1237 {
1238 .name = "wm831x-aldo",
1239 .id = 10,
1240 .num_resources = ARRAY_SIZE(wm831x_ldo10_resources),
1241 .resources = wm831x_ldo10_resources,
1242 },
1243 {
1244 .name = "wm831x-alive-ldo",
1245 .id = 11,
1246 .num_resources = ARRAY_SIZE(wm831x_ldo11_resources),
1247 .resources = wm831x_ldo11_resources,
1248 },
1249 {
1250 .name = "wm831x-on",
1251 .num_resources = ARRAY_SIZE(wm831x_on_resources),
1252 .resources = wm831x_on_resources,
1253 },
1254 {
Mark Brownd4e0a892009-10-01 15:41:07 +01001255 .name = "wm831x-status",
1256 .id = 1,
1257 .num_resources = ARRAY_SIZE(wm831x_status1_resources),
1258 .resources = wm831x_status1_resources,
1259 },
1260 {
1261 .name = "wm831x-status",
1262 .id = 2,
1263 .num_resources = ARRAY_SIZE(wm831x_status2_resources),
1264 .resources = wm831x_status2_resources,
1265 },
1266 {
1267 .name = "wm831x-watchdog",
1268 .num_resources = ARRAY_SIZE(wm831x_wdt_resources),
1269 .resources = wm831x_wdt_resources,
1270 },
1271};
1272
Mark Brown266a5e02011-06-02 19:18:49 +01001273static struct mfd_cell touch_devs[] = {
1274 {
1275 .name = "wm831x-touch",
1276 .num_resources = ARRAY_SIZE(wm831x_touch_resources),
1277 .resources = wm831x_touch_resources,
1278 },
1279};
1280
Mark Brownb9d03d92011-06-02 19:18:50 +01001281static struct mfd_cell rtc_devs[] = {
1282 {
1283 .name = "wm831x-rtc",
1284 .num_resources = ARRAY_SIZE(wm831x_rtc_resources),
1285 .resources = wm831x_rtc_resources,
1286 },
1287};
Mark Brown266a5e02011-06-02 19:18:49 +01001288
Mark Brown63aed852009-07-27 14:45:55 +01001289static struct mfd_cell backlight_devs[] = {
1290 {
1291 .name = "wm831x-backlight",
1292 },
1293};
1294
Mark Brownd2bedfe2009-07-27 14:45:52 +01001295/*
1296 * Instantiate the generic non-control parts of the device.
1297 */
Mark Browne5b48682010-10-19 23:57:56 +02001298int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
Mark Brownd2bedfe2009-07-27 14:45:52 +01001299{
1300 struct wm831x_pdata *pdata = wm831x->dev->platform_data;
Mark Browneb503dc2011-06-02 19:18:48 +01001301 int rev, wm831x_num;
Mark Brownd2bedfe2009-07-27 14:45:52 +01001302 enum wm831x_parent parent;
Mark Brown0b14c222011-04-04 11:04:42 +09001303 int ret, i;
Mark Brownd2bedfe2009-07-27 14:45:52 +01001304
1305 mutex_init(&wm831x->io_lock);
1306 mutex_init(&wm831x->key_lock);
1307 dev_set_drvdata(wm831x->dev, wm831x);
1308
1309 ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID);
1310 if (ret < 0) {
1311 dev_err(wm831x->dev, "Failed to read parent ID: %d\n", ret);
1312 goto err;
1313 }
Mark Brownb93cef52010-12-02 16:25:43 +00001314 switch (ret) {
1315 case 0x6204:
1316 case 0x6246:
1317 break;
1318 default:
Mark Brownd2bedfe2009-07-27 14:45:52 +01001319 dev_err(wm831x->dev, "Device is not a WM831x: ID %x\n", ret);
1320 ret = -EINVAL;
1321 goto err;
1322 }
1323
1324 ret = wm831x_reg_read(wm831x, WM831X_REVISION);
1325 if (ret < 0) {
1326 dev_err(wm831x->dev, "Failed to read revision: %d\n", ret);
1327 goto err;
1328 }
1329 rev = (ret & WM831X_PARENT_REV_MASK) >> WM831X_PARENT_REV_SHIFT;
1330
1331 ret = wm831x_reg_read(wm831x, WM831X_RESET_ID);
1332 if (ret < 0) {
1333 dev_err(wm831x->dev, "Failed to read device ID: %d\n", ret);
1334 goto err;
1335 }
1336
Mark Brown894362f2009-10-01 15:41:04 +01001337 /* Some engineering samples do not have the ID set, rely on
1338 * the device being registered correctly.
1339 */
1340 if (ret == 0) {
1341 dev_info(wm831x->dev, "Device is an engineering sample\n");
1342 ret = id;
1343 }
1344
Mark Brownd2bedfe2009-07-27 14:45:52 +01001345 switch (ret) {
Mark Brown894362f2009-10-01 15:41:04 +01001346 case WM8310:
Mark Brownd2bedfe2009-07-27 14:45:52 +01001347 parent = WM8310;
Mark Brown6f2ecaa2009-10-01 15:41:05 +01001348 wm831x->num_gpio = 16;
Mark Brownb03b4d7c2010-04-08 10:02:39 +02001349 wm831x->charger_irq_wake = 1;
Mark Brownf92e8f82010-02-17 18:45:25 +00001350 if (rev > 0) {
1351 wm831x->has_gpio_ena = 1;
1352 wm831x->has_cs_sts = 1;
1353 }
1354
Mark Brown894362f2009-10-01 15:41:04 +01001355 dev_info(wm831x->dev, "WM8310 revision %c\n", 'A' + rev);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001356 break;
1357
Mark Brown894362f2009-10-01 15:41:04 +01001358 case WM8311:
Mark Brownd2bedfe2009-07-27 14:45:52 +01001359 parent = WM8311;
Mark Brown6f2ecaa2009-10-01 15:41:05 +01001360 wm831x->num_gpio = 16;
Mark Brownb03b4d7c2010-04-08 10:02:39 +02001361 wm831x->charger_irq_wake = 1;
Mark Brownf92e8f82010-02-17 18:45:25 +00001362 if (rev > 0) {
1363 wm831x->has_gpio_ena = 1;
1364 wm831x->has_cs_sts = 1;
1365 }
1366
Mark Brown894362f2009-10-01 15:41:04 +01001367 dev_info(wm831x->dev, "WM8311 revision %c\n", 'A' + rev);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001368 break;
1369
Mark Brown894362f2009-10-01 15:41:04 +01001370 case WM8312:
Mark Brownd2bedfe2009-07-27 14:45:52 +01001371 parent = WM8312;
Mark Brown6f2ecaa2009-10-01 15:41:05 +01001372 wm831x->num_gpio = 16;
Mark Brownb03b4d7c2010-04-08 10:02:39 +02001373 wm831x->charger_irq_wake = 1;
Mark Brownf92e8f82010-02-17 18:45:25 +00001374 if (rev > 0) {
1375 wm831x->has_gpio_ena = 1;
1376 wm831x->has_cs_sts = 1;
1377 }
1378
Mark Brown894362f2009-10-01 15:41:04 +01001379 dev_info(wm831x->dev, "WM8312 revision %c\n", 'A' + rev);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001380 break;
1381
Mark Brownd4e0a892009-10-01 15:41:07 +01001382 case WM8320:
1383 parent = WM8320;
1384 wm831x->num_gpio = 12;
1385 dev_info(wm831x->dev, "WM8320 revision %c\n", 'A' + rev);
1386 break;
1387
Mark Brown88913522010-07-21 14:23:37 +01001388 case WM8321:
1389 parent = WM8321;
1390 wm831x->num_gpio = 12;
1391 dev_info(wm831x->dev, "WM8321 revision %c\n", 'A' + rev);
1392 break;
1393
Mark Brown0b315882010-09-28 09:13:39 -07001394 case WM8325:
1395 parent = WM8325;
1396 wm831x->num_gpio = 12;
1397 dev_info(wm831x->dev, "WM8325 revision %c\n", 'A' + rev);
1398 break;
1399
Mark Brown412dc112010-11-24 18:01:41 +00001400 case WM8326:
1401 parent = WM8326;
1402 wm831x->num_gpio = 12;
1403 dev_info(wm831x->dev, "WM8326 revision %c\n", 'A' + rev);
1404 break;
1405
Mark Brownd2bedfe2009-07-27 14:45:52 +01001406 default:
1407 dev_err(wm831x->dev, "Unknown WM831x device %04x\n", ret);
1408 ret = -EINVAL;
1409 goto err;
1410 }
1411
1412 /* This will need revisiting in future but is OK for all
1413 * current parts.
1414 */
1415 if (parent != id)
Mark Brown894362f2009-10-01 15:41:04 +01001416 dev_warn(wm831x->dev, "Device was registered as a WM%lx\n",
Mark Brownd2bedfe2009-07-27 14:45:52 +01001417 id);
1418
1419 /* Bootstrap the user key */
1420 ret = wm831x_reg_read(wm831x, WM831X_SECURITY_KEY);
1421 if (ret < 0) {
1422 dev_err(wm831x->dev, "Failed to read security key: %d\n", ret);
1423 goto err;
1424 }
1425 if (ret != 0) {
1426 dev_warn(wm831x->dev, "Security key had non-zero value %x\n",
1427 ret);
1428 wm831x_reg_write(wm831x, WM831X_SECURITY_KEY, 0);
1429 }
1430 wm831x->locked = 1;
1431
1432 if (pdata && pdata->pre_init) {
1433 ret = pdata->pre_init(wm831x);
1434 if (ret != 0) {
1435 dev_err(wm831x->dev, "pre_init() failed: %d\n", ret);
1436 goto err;
1437 }
1438 }
1439
Mark Brown0b14c222011-04-04 11:04:42 +09001440 if (pdata) {
1441 for (i = 0; i < ARRAY_SIZE(pdata->gpio_defaults); i++) {
1442 if (!pdata->gpio_defaults[i])
1443 continue;
1444
1445 wm831x_reg_write(wm831x,
1446 WM831X_GPIO1_CONTROL + i,
1447 pdata->gpio_defaults[i] & 0xffff);
1448 }
1449 }
1450
Mark Browneb503dc2011-06-02 19:18:48 +01001451 /* Multiply by 10 as we have many subdevices of the same type */
1452 if (pdata && pdata->wm831x_num)
1453 wm831x_num = pdata->wm831x_num * 10;
1454 else
1455 wm831x_num = -1;
1456
Mark Brown7d4d0a32009-07-27 14:45:53 +01001457 ret = wm831x_irq_init(wm831x, irq);
1458 if (ret != 0)
1459 goto err;
1460
Mark Browne69b6de2011-06-02 19:18:53 +01001461 wm831x_auxadc_init(wm831x);
Mark Brown473fe732010-02-23 11:08:06 +00001462
Mark Brownd2bedfe2009-07-27 14:45:52 +01001463 /* The core device is up, instantiate the subdevices. */
1464 switch (parent) {
1465 case WM8310:
Mark Browneb503dc2011-06-02 19:18:48 +01001466 ret = mfd_add_devices(wm831x->dev, wm831x_num,
Mark Brownd2bedfe2009-07-27 14:45:52 +01001467 wm8310_devs, ARRAY_SIZE(wm8310_devs),
Mark Brown5fb4d382009-11-11 16:10:22 +00001468 NULL, wm831x->irq_base);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001469 break;
1470
1471 case WM8311:
Mark Browneb503dc2011-06-02 19:18:48 +01001472 ret = mfd_add_devices(wm831x->dev, wm831x_num,
Mark Brownd2bedfe2009-07-27 14:45:52 +01001473 wm8311_devs, ARRAY_SIZE(wm8311_devs),
Mark Brown5fb4d382009-11-11 16:10:22 +00001474 NULL, wm831x->irq_base);
Mark Brown266a5e02011-06-02 19:18:49 +01001475 if (!pdata || !pdata->disable_touch)
1476 mfd_add_devices(wm831x->dev, wm831x_num,
1477 touch_devs, ARRAY_SIZE(touch_devs),
1478 NULL, wm831x->irq_base);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001479 break;
1480
1481 case WM8312:
Mark Browneb503dc2011-06-02 19:18:48 +01001482 ret = mfd_add_devices(wm831x->dev, wm831x_num,
Mark Brownd2bedfe2009-07-27 14:45:52 +01001483 wm8312_devs, ARRAY_SIZE(wm8312_devs),
Mark Brown5fb4d382009-11-11 16:10:22 +00001484 NULL, wm831x->irq_base);
Mark Brown266a5e02011-06-02 19:18:49 +01001485 if (!pdata || !pdata->disable_touch)
1486 mfd_add_devices(wm831x->dev, wm831x_num,
1487 touch_devs, ARRAY_SIZE(touch_devs),
1488 NULL, wm831x->irq_base);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001489 break;
1490
Mark Brownd4e0a892009-10-01 15:41:07 +01001491 case WM8320:
Mark Brown88913522010-07-21 14:23:37 +01001492 case WM8321:
Mark Brown0b315882010-09-28 09:13:39 -07001493 case WM8325:
Mark Brown412dc112010-11-24 18:01:41 +00001494 case WM8326:
Mark Browneb503dc2011-06-02 19:18:48 +01001495 ret = mfd_add_devices(wm831x->dev, wm831x_num,
Mark Brown0b315882010-09-28 09:13:39 -07001496 wm8320_devs, ARRAY_SIZE(wm8320_devs),
Mark Brownbd7c72e2010-11-24 18:01:39 +00001497 NULL, wm831x->irq_base);
Mark Brown0b315882010-09-28 09:13:39 -07001498 break;
1499
Mark Brownd2bedfe2009-07-27 14:45:52 +01001500 default:
1501 /* If this happens the bus probe function is buggy */
1502 BUG();
1503 }
1504
1505 if (ret != 0) {
1506 dev_err(wm831x->dev, "Failed to add children\n");
Mark Brown7d4d0a32009-07-27 14:45:53 +01001507 goto err_irq;
Mark Brownd2bedfe2009-07-27 14:45:52 +01001508 }
1509
Mark Brownb9d03d92011-06-02 19:18:50 +01001510 /* The RTC can only be used if the 32.768kHz crystal is
1511 * enabled; this can't be controlled by software at runtime.
1512 */
1513 ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2);
1514 if (ret < 0) {
1515 dev_err(wm831x->dev, "Failed to read clock status: %d\n", ret);
1516 goto err_irq;
1517 }
1518
1519 if (ret & WM831X_XTAL_ENA) {
1520 ret = mfd_add_devices(wm831x->dev, wm831x_num,
1521 rtc_devs, ARRAY_SIZE(rtc_devs),
1522 NULL, wm831x->irq_base);
1523 if (ret != 0) {
1524 dev_err(wm831x->dev, "Failed to add RTC: %d\n", ret);
1525 goto err_irq;
1526 }
1527 } else {
1528 dev_info(wm831x->dev, "32.768kHz clock disabled, no RTC\n");
1529 }
1530
Mark Brown63aed852009-07-27 14:45:55 +01001531 if (pdata && pdata->backlight) {
1532 /* Treat errors as non-critical */
Mark Browneb503dc2011-06-02 19:18:48 +01001533 ret = mfd_add_devices(wm831x->dev, wm831x_num, backlight_devs,
Mark Brown5fb4d382009-11-11 16:10:22 +00001534 ARRAY_SIZE(backlight_devs), NULL,
1535 wm831x->irq_base);
Mark Brown63aed852009-07-27 14:45:55 +01001536 if (ret < 0)
1537 dev_err(wm831x->dev, "Failed to add backlight: %d\n",
1538 ret);
1539 }
1540
Mark Brown6704e512009-07-27 14:45:56 +01001541 wm831x_otp_init(wm831x);
1542
Mark Brownd2bedfe2009-07-27 14:45:52 +01001543 if (pdata && pdata->post_init) {
1544 ret = pdata->post_init(wm831x);
1545 if (ret != 0) {
1546 dev_err(wm831x->dev, "post_init() failed: %d\n", ret);
Mark Brown7d4d0a32009-07-27 14:45:53 +01001547 goto err_irq;
Mark Brownd2bedfe2009-07-27 14:45:52 +01001548 }
1549 }
1550
1551 return 0;
1552
Mark Brown7d4d0a32009-07-27 14:45:53 +01001553err_irq:
1554 wm831x_irq_exit(wm831x);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001555err:
1556 mfd_remove_devices(wm831x->dev);
1557 kfree(wm831x);
1558 return ret;
1559}
1560
Mark Browne5b48682010-10-19 23:57:56 +02001561void wm831x_device_exit(struct wm831x *wm831x)
Mark Brownd2bedfe2009-07-27 14:45:52 +01001562{
Mark Brown6704e512009-07-27 14:45:56 +01001563 wm831x_otp_exit(wm831x);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001564 mfd_remove_devices(wm831x->dev);
Mark Brown473fe732010-02-23 11:08:06 +00001565 if (wm831x->irq_base)
1566 free_irq(wm831x->irq_base + WM831X_IRQ_AUXADC_DATA, wm831x);
Mark Brown7d4d0a32009-07-27 14:45:53 +01001567 wm831x_irq_exit(wm831x);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001568 kfree(wm831x);
1569}
1570
Mark Browne5b48682010-10-19 23:57:56 +02001571int wm831x_device_suspend(struct wm831x *wm831x)
Mark Brownb03b4d7c2010-04-08 10:02:39 +02001572{
1573 int reg, mask;
1574
1575 /* If the charger IRQs are a wake source then make sure we ack
1576 * them even if they're not actively being used (eg, no power
1577 * driver or no IRQ line wired up) then acknowledge the
1578 * interrupts otherwise suspend won't last very long.
1579 */
1580 if (wm831x->charger_irq_wake) {
1581 reg = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_2_MASK);
1582
1583 mask = WM831X_CHG_BATT_HOT_EINT |
1584 WM831X_CHG_BATT_COLD_EINT |
1585 WM831X_CHG_BATT_FAIL_EINT |
1586 WM831X_CHG_OV_EINT | WM831X_CHG_END_EINT |
1587 WM831X_CHG_TO_EINT | WM831X_CHG_MODE_EINT |
1588 WM831X_CHG_START_EINT;
1589
1590 /* If any of the interrupts are masked read the statuses */
1591 if (reg & mask)
1592 reg = wm831x_reg_read(wm831x,
1593 WM831X_INTERRUPT_STATUS_2);
1594
1595 if (reg & mask) {
1596 dev_info(wm831x->dev,
1597 "Acknowledging masked charger IRQs: %x\n",
1598 reg & mask);
1599 wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_2,
1600 reg & mask);
1601 }
1602 }
1603
1604 return 0;
1605}
1606
Mark Browne5b48682010-10-19 23:57:56 +02001607MODULE_DESCRIPTION("Core support for the WM831X AudioPlus PMIC");
Mark Brownd2bedfe2009-07-27 14:45:52 +01001608MODULE_LICENSE("GPL");
1609MODULE_AUTHOR("Mark Brown");