blob: 578e0c2ad82bca0f5469eff3b7b7ba63b3f22002 [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 Brown1df59812011-06-10 19:28:10 +010021#include <linux/err.h>
Mark Brownd2bedfe2009-07-27 14:45:52 +010022
23#include <linux/mfd/wm831x/core.h>
24#include <linux/mfd/wm831x/pdata.h>
Mark Brown7d4d0a32009-07-27 14:45:53 +010025#include <linux/mfd/wm831x/irq.h>
Mark Brown7e9f9fd2009-07-27 14:45:54 +010026#include <linux/mfd/wm831x/auxadc.h>
Mark Brown6704e512009-07-27 14:45:56 +010027#include <linux/mfd/wm831x/otp.h>
Mark Brown698659d2009-07-27 14:45:57 +010028#include <linux/mfd/wm831x/regulator.h>
29
30/* Current settings - values are 2*2^(reg_val/4) microamps. These are
31 * exported since they are used by multiple drivers.
32 */
Mark Brown77169772009-11-30 13:24:18 +000033int wm831x_isinkv_values[WM831X_ISINK_MAX_ISEL + 1] = {
Mark Brown698659d2009-07-27 14:45:57 +010034 2,
35 2,
36 3,
37 3,
38 4,
39 5,
40 6,
41 7,
42 8,
43 10,
44 11,
45 13,
46 16,
47 19,
48 23,
49 27,
50 32,
51 38,
52 45,
53 54,
54 64,
55 76,
56 91,
57 108,
58 128,
59 152,
60 181,
61 215,
62 256,
63 304,
64 362,
65 431,
66 512,
67 609,
68 724,
69 861,
70 1024,
71 1218,
72 1448,
73 1722,
74 2048,
75 2435,
76 2896,
77 3444,
78 4096,
79 4871,
80 5793,
81 6889,
82 8192,
83 9742,
84 11585,
85 13777,
86 16384,
87 19484,
88 23170,
89 27554,
90};
91EXPORT_SYMBOL_GPL(wm831x_isinkv_values);
Mark Brownd2bedfe2009-07-27 14:45:52 +010092
Mark Brownd2bedfe2009-07-27 14:45:52 +010093static int wm831x_reg_locked(struct wm831x *wm831x, unsigned short reg)
94{
95 if (!wm831x->locked)
96 return 0;
97
98 switch (reg) {
99 case WM831X_WATCHDOG:
100 case WM831X_DC4_CONTROL:
101 case WM831X_ON_PIN_CONTROL:
102 case WM831X_BACKUP_CHARGER_CONTROL:
103 case WM831X_CHARGER_CONTROL_1:
104 case WM831X_CHARGER_CONTROL_2:
105 return 1;
106
107 default:
108 return 0;
109 }
110}
111
112/**
113 * wm831x_reg_unlock: Unlock user keyed registers
114 *
115 * The WM831x has a user key preventing writes to particularly
116 * critical registers. This function locks those registers,
117 * allowing writes to them.
118 */
119void wm831x_reg_lock(struct wm831x *wm831x)
120{
121 int ret;
122
123 ret = wm831x_reg_write(wm831x, WM831X_SECURITY_KEY, 0);
124 if (ret == 0) {
125 dev_vdbg(wm831x->dev, "Registers locked\n");
126
127 mutex_lock(&wm831x->io_lock);
128 WARN_ON(wm831x->locked);
129 wm831x->locked = 1;
130 mutex_unlock(&wm831x->io_lock);
131 } else {
132 dev_err(wm831x->dev, "Failed to lock registers: %d\n", ret);
133 }
134
135}
136EXPORT_SYMBOL_GPL(wm831x_reg_lock);
137
138/**
139 * wm831x_reg_unlock: Unlock user keyed registers
140 *
141 * The WM831x has a user key preventing writes to particularly
142 * critical registers. This function locks those registers,
143 * preventing spurious writes.
144 */
145int wm831x_reg_unlock(struct wm831x *wm831x)
146{
147 int ret;
148
149 /* 0x9716 is the value required to unlock the registers */
150 ret = wm831x_reg_write(wm831x, WM831X_SECURITY_KEY, 0x9716);
151 if (ret == 0) {
152 dev_vdbg(wm831x->dev, "Registers unlocked\n");
153
154 mutex_lock(&wm831x->io_lock);
155 WARN_ON(!wm831x->locked);
156 wm831x->locked = 0;
157 mutex_unlock(&wm831x->io_lock);
158 }
159
160 return ret;
161}
162EXPORT_SYMBOL_GPL(wm831x_reg_unlock);
163
Mark Brownd2bedfe2009-07-27 14:45:52 +0100164/**
165 * wm831x_reg_read: Read a single WM831x register.
166 *
167 * @wm831x: Device to read from.
168 * @reg: Register to read.
169 */
170int wm831x_reg_read(struct wm831x *wm831x, unsigned short reg)
171{
Mark Brown1df59812011-06-10 19:28:10 +0100172 unsigned int val;
Mark Brownd2bedfe2009-07-27 14:45:52 +0100173 int ret;
174
Mark Brown1df59812011-06-10 19:28:10 +0100175 ret = regmap_read(wm831x->regmap, reg, &val);
Mark Brownd2bedfe2009-07-27 14:45:52 +0100176
177 if (ret < 0)
178 return ret;
179 else
180 return val;
181}
182EXPORT_SYMBOL_GPL(wm831x_reg_read);
183
184/**
185 * wm831x_bulk_read: Read multiple WM831x registers
186 *
187 * @wm831x: Device to read from
188 * @reg: First register
189 * @count: Number of registers
190 * @buf: Buffer to fill.
191 */
192int wm831x_bulk_read(struct wm831x *wm831x, unsigned short reg,
193 int count, u16 *buf)
194{
Mark Brown1df59812011-06-10 19:28:10 +0100195 return regmap_bulk_read(wm831x->regmap, reg, buf, count);
Mark Brownd2bedfe2009-07-27 14:45:52 +0100196}
197EXPORT_SYMBOL_GPL(wm831x_bulk_read);
198
199static int wm831x_write(struct wm831x *wm831x, unsigned short reg,
200 int bytes, void *src)
201{
202 u16 *buf = src;
Mark Brown1df59812011-06-10 19:28:10 +0100203 int i, ret;
Mark Brownd2bedfe2009-07-27 14:45:52 +0100204
205 BUG_ON(bytes % 2);
206 BUG_ON(bytes <= 0);
207
208 for (i = 0; i < bytes / 2; i++) {
209 if (wm831x_reg_locked(wm831x, reg))
210 return -EPERM;
211
212 dev_vdbg(wm831x->dev, "Write %04x to R%d(0x%x)\n",
213 buf[i], reg + i, reg + i);
Mark Brown1df59812011-06-10 19:28:10 +0100214 ret = regmap_write(wm831x->regmap, reg + i, buf[i]);
Mark Brownd2bedfe2009-07-27 14:45:52 +0100215 }
216
Mark Brown1df59812011-06-10 19:28:10 +0100217 return 0;
Mark Brownd2bedfe2009-07-27 14:45:52 +0100218}
219
220/**
221 * wm831x_reg_write: Write a single WM831x register.
222 *
223 * @wm831x: Device to write to.
224 * @reg: Register to write to.
225 * @val: Value to write.
226 */
227int wm831x_reg_write(struct wm831x *wm831x, unsigned short reg,
228 unsigned short val)
229{
230 int ret;
231
232 mutex_lock(&wm831x->io_lock);
233
234 ret = wm831x_write(wm831x, reg, 2, &val);
235
236 mutex_unlock(&wm831x->io_lock);
237
238 return ret;
239}
240EXPORT_SYMBOL_GPL(wm831x_reg_write);
241
242/**
243 * wm831x_set_bits: Set the value of a bitfield in a WM831x register
244 *
245 * @wm831x: Device to write to.
246 * @reg: Register to write to.
247 * @mask: Mask of bits to set.
248 * @val: Value to set (unshifted)
249 */
250int wm831x_set_bits(struct wm831x *wm831x, unsigned short reg,
251 unsigned short mask, unsigned short val)
252{
253 int ret;
Mark Brownd2bedfe2009-07-27 14:45:52 +0100254
255 mutex_lock(&wm831x->io_lock);
256
Mark Brown1df59812011-06-10 19:28:10 +0100257 if (!wm831x_reg_locked(wm831x, reg))
258 ret = regmap_update_bits(wm831x->regmap, reg, mask, val);
259 else
260 ret = -EPERM;
Mark Brownd2bedfe2009-07-27 14:45:52 +0100261
Mark Brownd2bedfe2009-07-27 14:45:52 +0100262 mutex_unlock(&wm831x->io_lock);
263
264 return ret;
265}
266EXPORT_SYMBOL_GPL(wm831x_set_bits);
267
268static struct resource wm831x_dcdc1_resources[] = {
269 {
270 .start = WM831X_DC1_CONTROL_1,
271 .end = WM831X_DC1_DVS_CONTROL,
272 .flags = IORESOURCE_IO,
273 },
274 {
275 .name = "UV",
276 .start = WM831X_IRQ_UV_DC1,
277 .end = WM831X_IRQ_UV_DC1,
278 .flags = IORESOURCE_IRQ,
279 },
280 {
281 .name = "HC",
282 .start = WM831X_IRQ_HC_DC1,
283 .end = WM831X_IRQ_HC_DC1,
284 .flags = IORESOURCE_IRQ,
285 },
286};
287
288
289static struct resource wm831x_dcdc2_resources[] = {
290 {
291 .start = WM831X_DC2_CONTROL_1,
292 .end = WM831X_DC2_DVS_CONTROL,
293 .flags = IORESOURCE_IO,
294 },
295 {
296 .name = "UV",
297 .start = WM831X_IRQ_UV_DC2,
298 .end = WM831X_IRQ_UV_DC2,
299 .flags = IORESOURCE_IRQ,
300 },
301 {
302 .name = "HC",
303 .start = WM831X_IRQ_HC_DC2,
304 .end = WM831X_IRQ_HC_DC2,
305 .flags = IORESOURCE_IRQ,
306 },
307};
308
309static struct resource wm831x_dcdc3_resources[] = {
310 {
311 .start = WM831X_DC3_CONTROL_1,
312 .end = WM831X_DC3_SLEEP_CONTROL,
313 .flags = IORESOURCE_IO,
314 },
315 {
316 .name = "UV",
317 .start = WM831X_IRQ_UV_DC3,
318 .end = WM831X_IRQ_UV_DC3,
319 .flags = IORESOURCE_IRQ,
320 },
321};
322
323static struct resource wm831x_dcdc4_resources[] = {
324 {
325 .start = WM831X_DC4_CONTROL,
326 .end = WM831X_DC4_SLEEP_CONTROL,
327 .flags = IORESOURCE_IO,
328 },
329 {
330 .name = "UV",
331 .start = WM831X_IRQ_UV_DC4,
332 .end = WM831X_IRQ_UV_DC4,
333 .flags = IORESOURCE_IRQ,
334 },
335};
336
Mark Brownd4e0a892009-10-01 15:41:07 +0100337static struct resource wm8320_dcdc4_buck_resources[] = {
338 {
339 .start = WM831X_DC4_CONTROL,
340 .end = WM832X_DC4_SLEEP_CONTROL,
341 .flags = IORESOURCE_IO,
342 },
343 {
344 .name = "UV",
345 .start = WM831X_IRQ_UV_DC4,
346 .end = WM831X_IRQ_UV_DC4,
347 .flags = IORESOURCE_IRQ,
348 },
349};
350
Mark Brownd2bedfe2009-07-27 14:45:52 +0100351static struct resource wm831x_gpio_resources[] = {
352 {
353 .start = WM831X_IRQ_GPIO_1,
354 .end = WM831X_IRQ_GPIO_16,
355 .flags = IORESOURCE_IRQ,
356 },
357};
358
359static struct resource wm831x_isink1_resources[] = {
360 {
361 .start = WM831X_CURRENT_SINK_1,
362 .end = WM831X_CURRENT_SINK_1,
363 .flags = IORESOURCE_IO,
364 },
365 {
366 .start = WM831X_IRQ_CS1,
367 .end = WM831X_IRQ_CS1,
368 .flags = IORESOURCE_IRQ,
369 },
370};
371
372static struct resource wm831x_isink2_resources[] = {
373 {
374 .start = WM831X_CURRENT_SINK_2,
375 .end = WM831X_CURRENT_SINK_2,
376 .flags = IORESOURCE_IO,
377 },
378 {
379 .start = WM831X_IRQ_CS2,
380 .end = WM831X_IRQ_CS2,
381 .flags = IORESOURCE_IRQ,
382 },
383};
384
385static struct resource wm831x_ldo1_resources[] = {
386 {
387 .start = WM831X_LDO1_CONTROL,
388 .end = WM831X_LDO1_SLEEP_CONTROL,
389 .flags = IORESOURCE_IO,
390 },
391 {
392 .name = "UV",
393 .start = WM831X_IRQ_UV_LDO1,
394 .end = WM831X_IRQ_UV_LDO1,
395 .flags = IORESOURCE_IRQ,
396 },
397};
398
399static struct resource wm831x_ldo2_resources[] = {
400 {
401 .start = WM831X_LDO2_CONTROL,
402 .end = WM831X_LDO2_SLEEP_CONTROL,
403 .flags = IORESOURCE_IO,
404 },
405 {
406 .name = "UV",
407 .start = WM831X_IRQ_UV_LDO2,
408 .end = WM831X_IRQ_UV_LDO2,
409 .flags = IORESOURCE_IRQ,
410 },
411};
412
413static struct resource wm831x_ldo3_resources[] = {
414 {
415 .start = WM831X_LDO3_CONTROL,
416 .end = WM831X_LDO3_SLEEP_CONTROL,
417 .flags = IORESOURCE_IO,
418 },
419 {
420 .name = "UV",
421 .start = WM831X_IRQ_UV_LDO3,
422 .end = WM831X_IRQ_UV_LDO3,
423 .flags = IORESOURCE_IRQ,
424 },
425};
426
427static struct resource wm831x_ldo4_resources[] = {
428 {
429 .start = WM831X_LDO4_CONTROL,
430 .end = WM831X_LDO4_SLEEP_CONTROL,
431 .flags = IORESOURCE_IO,
432 },
433 {
434 .name = "UV",
435 .start = WM831X_IRQ_UV_LDO4,
436 .end = WM831X_IRQ_UV_LDO4,
437 .flags = IORESOURCE_IRQ,
438 },
439};
440
441static struct resource wm831x_ldo5_resources[] = {
442 {
443 .start = WM831X_LDO5_CONTROL,
444 .end = WM831X_LDO5_SLEEP_CONTROL,
445 .flags = IORESOURCE_IO,
446 },
447 {
448 .name = "UV",
449 .start = WM831X_IRQ_UV_LDO5,
450 .end = WM831X_IRQ_UV_LDO5,
451 .flags = IORESOURCE_IRQ,
452 },
453};
454
455static struct resource wm831x_ldo6_resources[] = {
456 {
457 .start = WM831X_LDO6_CONTROL,
458 .end = WM831X_LDO6_SLEEP_CONTROL,
459 .flags = IORESOURCE_IO,
460 },
461 {
462 .name = "UV",
463 .start = WM831X_IRQ_UV_LDO6,
464 .end = WM831X_IRQ_UV_LDO6,
465 .flags = IORESOURCE_IRQ,
466 },
467};
468
469static struct resource wm831x_ldo7_resources[] = {
470 {
471 .start = WM831X_LDO7_CONTROL,
472 .end = WM831X_LDO7_SLEEP_CONTROL,
473 .flags = IORESOURCE_IO,
474 },
475 {
476 .name = "UV",
477 .start = WM831X_IRQ_UV_LDO7,
478 .end = WM831X_IRQ_UV_LDO7,
479 .flags = IORESOURCE_IRQ,
480 },
481};
482
483static struct resource wm831x_ldo8_resources[] = {
484 {
485 .start = WM831X_LDO8_CONTROL,
486 .end = WM831X_LDO8_SLEEP_CONTROL,
487 .flags = IORESOURCE_IO,
488 },
489 {
490 .name = "UV",
491 .start = WM831X_IRQ_UV_LDO8,
492 .end = WM831X_IRQ_UV_LDO8,
493 .flags = IORESOURCE_IRQ,
494 },
495};
496
497static struct resource wm831x_ldo9_resources[] = {
498 {
499 .start = WM831X_LDO9_CONTROL,
500 .end = WM831X_LDO9_SLEEP_CONTROL,
501 .flags = IORESOURCE_IO,
502 },
503 {
504 .name = "UV",
505 .start = WM831X_IRQ_UV_LDO9,
506 .end = WM831X_IRQ_UV_LDO9,
507 .flags = IORESOURCE_IRQ,
508 },
509};
510
511static struct resource wm831x_ldo10_resources[] = {
512 {
513 .start = WM831X_LDO10_CONTROL,
514 .end = WM831X_LDO10_SLEEP_CONTROL,
515 .flags = IORESOURCE_IO,
516 },
517 {
518 .name = "UV",
519 .start = WM831X_IRQ_UV_LDO10,
520 .end = WM831X_IRQ_UV_LDO10,
521 .flags = IORESOURCE_IRQ,
522 },
523};
524
525static struct resource wm831x_ldo11_resources[] = {
526 {
527 .start = WM831X_LDO11_ON_CONTROL,
528 .end = WM831X_LDO11_SLEEP_CONTROL,
529 .flags = IORESOURCE_IO,
530 },
531};
532
533static struct resource wm831x_on_resources[] = {
534 {
535 .start = WM831X_IRQ_ON,
536 .end = WM831X_IRQ_ON,
537 .flags = IORESOURCE_IRQ,
538 },
539};
540
541
542static struct resource wm831x_power_resources[] = {
543 {
544 .name = "SYSLO",
545 .start = WM831X_IRQ_PPM_SYSLO,
546 .end = WM831X_IRQ_PPM_SYSLO,
547 .flags = IORESOURCE_IRQ,
548 },
549 {
550 .name = "PWR SRC",
551 .start = WM831X_IRQ_PPM_PWR_SRC,
552 .end = WM831X_IRQ_PPM_PWR_SRC,
553 .flags = IORESOURCE_IRQ,
554 },
555 {
556 .name = "USB CURR",
557 .start = WM831X_IRQ_PPM_USB_CURR,
558 .end = WM831X_IRQ_PPM_USB_CURR,
559 .flags = IORESOURCE_IRQ,
560 },
561 {
562 .name = "BATT HOT",
563 .start = WM831X_IRQ_CHG_BATT_HOT,
564 .end = WM831X_IRQ_CHG_BATT_HOT,
565 .flags = IORESOURCE_IRQ,
566 },
567 {
568 .name = "BATT COLD",
569 .start = WM831X_IRQ_CHG_BATT_COLD,
570 .end = WM831X_IRQ_CHG_BATT_COLD,
571 .flags = IORESOURCE_IRQ,
572 },
573 {
574 .name = "BATT FAIL",
575 .start = WM831X_IRQ_CHG_BATT_FAIL,
576 .end = WM831X_IRQ_CHG_BATT_FAIL,
577 .flags = IORESOURCE_IRQ,
578 },
579 {
580 .name = "OV",
581 .start = WM831X_IRQ_CHG_OV,
582 .end = WM831X_IRQ_CHG_OV,
583 .flags = IORESOURCE_IRQ,
584 },
585 {
586 .name = "END",
587 .start = WM831X_IRQ_CHG_END,
588 .end = WM831X_IRQ_CHG_END,
589 .flags = IORESOURCE_IRQ,
590 },
591 {
592 .name = "TO",
593 .start = WM831X_IRQ_CHG_TO,
594 .end = WM831X_IRQ_CHG_TO,
595 .flags = IORESOURCE_IRQ,
596 },
597 {
598 .name = "MODE",
599 .start = WM831X_IRQ_CHG_MODE,
600 .end = WM831X_IRQ_CHG_MODE,
601 .flags = IORESOURCE_IRQ,
602 },
603 {
604 .name = "START",
605 .start = WM831X_IRQ_CHG_START,
606 .end = WM831X_IRQ_CHG_START,
607 .flags = IORESOURCE_IRQ,
608 },
609};
610
611static struct resource wm831x_rtc_resources[] = {
612 {
613 .name = "PER",
614 .start = WM831X_IRQ_RTC_PER,
615 .end = WM831X_IRQ_RTC_PER,
616 .flags = IORESOURCE_IRQ,
617 },
618 {
619 .name = "ALM",
620 .start = WM831X_IRQ_RTC_ALM,
621 .end = WM831X_IRQ_RTC_ALM,
622 .flags = IORESOURCE_IRQ,
623 },
624};
625
626static struct resource wm831x_status1_resources[] = {
627 {
628 .start = WM831X_STATUS_LED_1,
629 .end = WM831X_STATUS_LED_1,
630 .flags = IORESOURCE_IO,
631 },
632};
633
634static struct resource wm831x_status2_resources[] = {
635 {
636 .start = WM831X_STATUS_LED_2,
637 .end = WM831X_STATUS_LED_2,
638 .flags = IORESOURCE_IO,
639 },
640};
641
642static struct resource wm831x_touch_resources[] = {
643 {
644 .name = "TCHPD",
645 .start = WM831X_IRQ_TCHPD,
646 .end = WM831X_IRQ_TCHPD,
647 .flags = IORESOURCE_IRQ,
648 },
649 {
650 .name = "TCHDATA",
651 .start = WM831X_IRQ_TCHDATA,
652 .end = WM831X_IRQ_TCHDATA,
653 .flags = IORESOURCE_IRQ,
654 },
655};
656
657static struct resource wm831x_wdt_resources[] = {
658 {
659 .start = WM831X_IRQ_WDOG_TO,
660 .end = WM831X_IRQ_WDOG_TO,
661 .flags = IORESOURCE_IRQ,
662 },
663};
664
665static struct mfd_cell wm8310_devs[] = {
666 {
Mark Brownc26964e2009-10-01 15:41:06 +0100667 .name = "wm831x-backup",
668 },
669 {
Mark Brownd2bedfe2009-07-27 14:45:52 +0100670 .name = "wm831x-buckv",
671 .id = 1,
672 .num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
673 .resources = wm831x_dcdc1_resources,
674 },
675 {
676 .name = "wm831x-buckv",
677 .id = 2,
678 .num_resources = ARRAY_SIZE(wm831x_dcdc2_resources),
679 .resources = wm831x_dcdc2_resources,
680 },
681 {
682 .name = "wm831x-buckp",
683 .id = 3,
684 .num_resources = ARRAY_SIZE(wm831x_dcdc3_resources),
685 .resources = wm831x_dcdc3_resources,
686 },
687 {
688 .name = "wm831x-boostp",
689 .id = 4,
690 .num_resources = ARRAY_SIZE(wm831x_dcdc4_resources),
691 .resources = wm831x_dcdc4_resources,
692 },
693 {
Mark Browna5e06782011-06-24 12:17:07 +0100694 .name = "wm831x-clk",
695 },
696 {
Mark Brownd2bedfe2009-07-27 14:45:52 +0100697 .name = "wm831x-epe",
698 .id = 1,
699 },
700 {
701 .name = "wm831x-epe",
702 .id = 2,
703 },
704 {
705 .name = "wm831x-gpio",
706 .num_resources = ARRAY_SIZE(wm831x_gpio_resources),
707 .resources = wm831x_gpio_resources,
708 },
709 {
710 .name = "wm831x-hwmon",
711 },
712 {
713 .name = "wm831x-isink",
714 .id = 1,
715 .num_resources = ARRAY_SIZE(wm831x_isink1_resources),
716 .resources = wm831x_isink1_resources,
717 },
718 {
719 .name = "wm831x-isink",
720 .id = 2,
721 .num_resources = ARRAY_SIZE(wm831x_isink2_resources),
722 .resources = wm831x_isink2_resources,
723 },
724 {
725 .name = "wm831x-ldo",
726 .id = 1,
727 .num_resources = ARRAY_SIZE(wm831x_ldo1_resources),
728 .resources = wm831x_ldo1_resources,
729 },
730 {
731 .name = "wm831x-ldo",
732 .id = 2,
733 .num_resources = ARRAY_SIZE(wm831x_ldo2_resources),
734 .resources = wm831x_ldo2_resources,
735 },
736 {
737 .name = "wm831x-ldo",
738 .id = 3,
739 .num_resources = ARRAY_SIZE(wm831x_ldo3_resources),
740 .resources = wm831x_ldo3_resources,
741 },
742 {
743 .name = "wm831x-ldo",
744 .id = 4,
745 .num_resources = ARRAY_SIZE(wm831x_ldo4_resources),
746 .resources = wm831x_ldo4_resources,
747 },
748 {
749 .name = "wm831x-ldo",
750 .id = 5,
751 .num_resources = ARRAY_SIZE(wm831x_ldo5_resources),
752 .resources = wm831x_ldo5_resources,
753 },
754 {
755 .name = "wm831x-ldo",
756 .id = 6,
757 .num_resources = ARRAY_SIZE(wm831x_ldo6_resources),
758 .resources = wm831x_ldo6_resources,
759 },
760 {
761 .name = "wm831x-aldo",
762 .id = 7,
763 .num_resources = ARRAY_SIZE(wm831x_ldo7_resources),
764 .resources = wm831x_ldo7_resources,
765 },
766 {
767 .name = "wm831x-aldo",
768 .id = 8,
769 .num_resources = ARRAY_SIZE(wm831x_ldo8_resources),
770 .resources = wm831x_ldo8_resources,
771 },
772 {
773 .name = "wm831x-aldo",
774 .id = 9,
775 .num_resources = ARRAY_SIZE(wm831x_ldo9_resources),
776 .resources = wm831x_ldo9_resources,
777 },
778 {
779 .name = "wm831x-aldo",
780 .id = 10,
781 .num_resources = ARRAY_SIZE(wm831x_ldo10_resources),
782 .resources = wm831x_ldo10_resources,
783 },
784 {
785 .name = "wm831x-alive-ldo",
786 .id = 11,
787 .num_resources = ARRAY_SIZE(wm831x_ldo11_resources),
788 .resources = wm831x_ldo11_resources,
789 },
790 {
791 .name = "wm831x-on",
792 .num_resources = ARRAY_SIZE(wm831x_on_resources),
793 .resources = wm831x_on_resources,
794 },
795 {
796 .name = "wm831x-power",
797 .num_resources = ARRAY_SIZE(wm831x_power_resources),
798 .resources = wm831x_power_resources,
799 },
800 {
Mark Brownd2bedfe2009-07-27 14:45:52 +0100801 .name = "wm831x-status",
802 .id = 1,
803 .num_resources = ARRAY_SIZE(wm831x_status1_resources),
804 .resources = wm831x_status1_resources,
805 },
806 {
807 .name = "wm831x-status",
808 .id = 2,
809 .num_resources = ARRAY_SIZE(wm831x_status2_resources),
810 .resources = wm831x_status2_resources,
811 },
812 {
813 .name = "wm831x-watchdog",
814 .num_resources = ARRAY_SIZE(wm831x_wdt_resources),
815 .resources = wm831x_wdt_resources,
816 },
817};
818
819static struct mfd_cell wm8311_devs[] = {
820 {
Mark Brownc26964e2009-10-01 15:41:06 +0100821 .name = "wm831x-backup",
822 },
823 {
Mark Brownd2bedfe2009-07-27 14:45:52 +0100824 .name = "wm831x-buckv",
825 .id = 1,
826 .num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
827 .resources = wm831x_dcdc1_resources,
828 },
829 {
830 .name = "wm831x-buckv",
831 .id = 2,
832 .num_resources = ARRAY_SIZE(wm831x_dcdc2_resources),
833 .resources = wm831x_dcdc2_resources,
834 },
835 {
836 .name = "wm831x-buckp",
837 .id = 3,
838 .num_resources = ARRAY_SIZE(wm831x_dcdc3_resources),
839 .resources = wm831x_dcdc3_resources,
840 },
841 {
842 .name = "wm831x-boostp",
843 .id = 4,
844 .num_resources = ARRAY_SIZE(wm831x_dcdc4_resources),
845 .resources = wm831x_dcdc4_resources,
846 },
847 {
Mark Browna5e06782011-06-24 12:17:07 +0100848 .name = "wm831x-clk",
849 },
850 {
Mark Brownd2bedfe2009-07-27 14:45:52 +0100851 .name = "wm831x-epe",
852 .id = 1,
853 },
854 {
855 .name = "wm831x-epe",
856 .id = 2,
857 },
858 {
859 .name = "wm831x-gpio",
860 .num_resources = ARRAY_SIZE(wm831x_gpio_resources),
861 .resources = wm831x_gpio_resources,
862 },
863 {
864 .name = "wm831x-hwmon",
865 },
866 {
867 .name = "wm831x-isink",
868 .id = 1,
869 .num_resources = ARRAY_SIZE(wm831x_isink1_resources),
870 .resources = wm831x_isink1_resources,
871 },
872 {
873 .name = "wm831x-isink",
874 .id = 2,
875 .num_resources = ARRAY_SIZE(wm831x_isink2_resources),
876 .resources = wm831x_isink2_resources,
877 },
878 {
879 .name = "wm831x-ldo",
880 .id = 1,
881 .num_resources = ARRAY_SIZE(wm831x_ldo1_resources),
882 .resources = wm831x_ldo1_resources,
883 },
884 {
885 .name = "wm831x-ldo",
886 .id = 2,
887 .num_resources = ARRAY_SIZE(wm831x_ldo2_resources),
888 .resources = wm831x_ldo2_resources,
889 },
890 {
891 .name = "wm831x-ldo",
892 .id = 3,
893 .num_resources = ARRAY_SIZE(wm831x_ldo3_resources),
894 .resources = wm831x_ldo3_resources,
895 },
896 {
897 .name = "wm831x-ldo",
898 .id = 4,
899 .num_resources = ARRAY_SIZE(wm831x_ldo4_resources),
900 .resources = wm831x_ldo4_resources,
901 },
902 {
903 .name = "wm831x-ldo",
904 .id = 5,
905 .num_resources = ARRAY_SIZE(wm831x_ldo5_resources),
906 .resources = wm831x_ldo5_resources,
907 },
908 {
909 .name = "wm831x-aldo",
910 .id = 7,
911 .num_resources = ARRAY_SIZE(wm831x_ldo7_resources),
912 .resources = wm831x_ldo7_resources,
913 },
914 {
915 .name = "wm831x-alive-ldo",
916 .id = 11,
917 .num_resources = ARRAY_SIZE(wm831x_ldo11_resources),
918 .resources = wm831x_ldo11_resources,
919 },
920 {
921 .name = "wm831x-on",
922 .num_resources = ARRAY_SIZE(wm831x_on_resources),
923 .resources = wm831x_on_resources,
924 },
925 {
926 .name = "wm831x-power",
927 .num_resources = ARRAY_SIZE(wm831x_power_resources),
928 .resources = wm831x_power_resources,
929 },
930 {
Mark Brownd2bedfe2009-07-27 14:45:52 +0100931 .name = "wm831x-status",
932 .id = 1,
933 .num_resources = ARRAY_SIZE(wm831x_status1_resources),
934 .resources = wm831x_status1_resources,
935 },
936 {
937 .name = "wm831x-status",
938 .id = 2,
939 .num_resources = ARRAY_SIZE(wm831x_status2_resources),
940 .resources = wm831x_status2_resources,
941 },
942 {
Mark Brownd2bedfe2009-07-27 14:45:52 +0100943 .name = "wm831x-watchdog",
944 .num_resources = ARRAY_SIZE(wm831x_wdt_resources),
945 .resources = wm831x_wdt_resources,
946 },
947};
948
949static struct mfd_cell wm8312_devs[] = {
950 {
Mark Brownc26964e2009-10-01 15:41:06 +0100951 .name = "wm831x-backup",
952 },
953 {
Mark Brownd2bedfe2009-07-27 14:45:52 +0100954 .name = "wm831x-buckv",
955 .id = 1,
956 .num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
957 .resources = wm831x_dcdc1_resources,
958 },
959 {
960 .name = "wm831x-buckv",
961 .id = 2,
962 .num_resources = ARRAY_SIZE(wm831x_dcdc2_resources),
963 .resources = wm831x_dcdc2_resources,
964 },
965 {
966 .name = "wm831x-buckp",
967 .id = 3,
968 .num_resources = ARRAY_SIZE(wm831x_dcdc3_resources),
969 .resources = wm831x_dcdc3_resources,
970 },
971 {
972 .name = "wm831x-boostp",
973 .id = 4,
974 .num_resources = ARRAY_SIZE(wm831x_dcdc4_resources),
975 .resources = wm831x_dcdc4_resources,
976 },
977 {
Mark Browna5e06782011-06-24 12:17:07 +0100978 .name = "wm831x-clk",
979 },
980 {
Mark Brownd2bedfe2009-07-27 14:45:52 +0100981 .name = "wm831x-epe",
982 .id = 1,
983 },
984 {
985 .name = "wm831x-epe",
986 .id = 2,
987 },
988 {
989 .name = "wm831x-gpio",
990 .num_resources = ARRAY_SIZE(wm831x_gpio_resources),
991 .resources = wm831x_gpio_resources,
992 },
993 {
994 .name = "wm831x-hwmon",
995 },
996 {
997 .name = "wm831x-isink",
998 .id = 1,
999 .num_resources = ARRAY_SIZE(wm831x_isink1_resources),
1000 .resources = wm831x_isink1_resources,
1001 },
1002 {
1003 .name = "wm831x-isink",
1004 .id = 2,
1005 .num_resources = ARRAY_SIZE(wm831x_isink2_resources),
1006 .resources = wm831x_isink2_resources,
1007 },
1008 {
1009 .name = "wm831x-ldo",
1010 .id = 1,
1011 .num_resources = ARRAY_SIZE(wm831x_ldo1_resources),
1012 .resources = wm831x_ldo1_resources,
1013 },
1014 {
1015 .name = "wm831x-ldo",
1016 .id = 2,
1017 .num_resources = ARRAY_SIZE(wm831x_ldo2_resources),
1018 .resources = wm831x_ldo2_resources,
1019 },
1020 {
1021 .name = "wm831x-ldo",
1022 .id = 3,
1023 .num_resources = ARRAY_SIZE(wm831x_ldo3_resources),
1024 .resources = wm831x_ldo3_resources,
1025 },
1026 {
1027 .name = "wm831x-ldo",
1028 .id = 4,
1029 .num_resources = ARRAY_SIZE(wm831x_ldo4_resources),
1030 .resources = wm831x_ldo4_resources,
1031 },
1032 {
1033 .name = "wm831x-ldo",
1034 .id = 5,
1035 .num_resources = ARRAY_SIZE(wm831x_ldo5_resources),
1036 .resources = wm831x_ldo5_resources,
1037 },
1038 {
1039 .name = "wm831x-ldo",
1040 .id = 6,
1041 .num_resources = ARRAY_SIZE(wm831x_ldo6_resources),
1042 .resources = wm831x_ldo6_resources,
1043 },
1044 {
1045 .name = "wm831x-aldo",
1046 .id = 7,
1047 .num_resources = ARRAY_SIZE(wm831x_ldo7_resources),
1048 .resources = wm831x_ldo7_resources,
1049 },
1050 {
1051 .name = "wm831x-aldo",
1052 .id = 8,
1053 .num_resources = ARRAY_SIZE(wm831x_ldo8_resources),
1054 .resources = wm831x_ldo8_resources,
1055 },
1056 {
1057 .name = "wm831x-aldo",
1058 .id = 9,
1059 .num_resources = ARRAY_SIZE(wm831x_ldo9_resources),
1060 .resources = wm831x_ldo9_resources,
1061 },
1062 {
1063 .name = "wm831x-aldo",
1064 .id = 10,
1065 .num_resources = ARRAY_SIZE(wm831x_ldo10_resources),
1066 .resources = wm831x_ldo10_resources,
1067 },
1068 {
1069 .name = "wm831x-alive-ldo",
1070 .id = 11,
1071 .num_resources = ARRAY_SIZE(wm831x_ldo11_resources),
1072 .resources = wm831x_ldo11_resources,
1073 },
1074 {
1075 .name = "wm831x-on",
1076 .num_resources = ARRAY_SIZE(wm831x_on_resources),
1077 .resources = wm831x_on_resources,
1078 },
1079 {
1080 .name = "wm831x-power",
1081 .num_resources = ARRAY_SIZE(wm831x_power_resources),
1082 .resources = wm831x_power_resources,
1083 },
1084 {
Mark Brownd2bedfe2009-07-27 14:45:52 +01001085 .name = "wm831x-status",
1086 .id = 1,
1087 .num_resources = ARRAY_SIZE(wm831x_status1_resources),
1088 .resources = wm831x_status1_resources,
1089 },
1090 {
1091 .name = "wm831x-status",
1092 .id = 2,
1093 .num_resources = ARRAY_SIZE(wm831x_status2_resources),
1094 .resources = wm831x_status2_resources,
1095 },
1096 {
Mark Brownd2bedfe2009-07-27 14:45:52 +01001097 .name = "wm831x-watchdog",
1098 .num_resources = ARRAY_SIZE(wm831x_wdt_resources),
1099 .resources = wm831x_wdt_resources,
1100 },
1101};
1102
Mark Brownd4e0a892009-10-01 15:41:07 +01001103static struct mfd_cell wm8320_devs[] = {
1104 {
1105 .name = "wm831x-backup",
1106 },
1107 {
1108 .name = "wm831x-buckv",
1109 .id = 1,
1110 .num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
1111 .resources = wm831x_dcdc1_resources,
1112 },
1113 {
1114 .name = "wm831x-buckv",
1115 .id = 2,
1116 .num_resources = ARRAY_SIZE(wm831x_dcdc2_resources),
1117 .resources = wm831x_dcdc2_resources,
1118 },
1119 {
1120 .name = "wm831x-buckp",
1121 .id = 3,
1122 .num_resources = ARRAY_SIZE(wm831x_dcdc3_resources),
1123 .resources = wm831x_dcdc3_resources,
1124 },
1125 {
1126 .name = "wm831x-buckp",
1127 .id = 4,
1128 .num_resources = ARRAY_SIZE(wm8320_dcdc4_buck_resources),
1129 .resources = wm8320_dcdc4_buck_resources,
1130 },
1131 {
Mark Browna5e06782011-06-24 12:17:07 +01001132 .name = "wm831x-clk",
1133 },
1134 {
Mark Brownd4e0a892009-10-01 15:41:07 +01001135 .name = "wm831x-gpio",
1136 .num_resources = ARRAY_SIZE(wm831x_gpio_resources),
1137 .resources = wm831x_gpio_resources,
1138 },
1139 {
1140 .name = "wm831x-hwmon",
1141 },
1142 {
1143 .name = "wm831x-ldo",
1144 .id = 1,
1145 .num_resources = ARRAY_SIZE(wm831x_ldo1_resources),
1146 .resources = wm831x_ldo1_resources,
1147 },
1148 {
1149 .name = "wm831x-ldo",
1150 .id = 2,
1151 .num_resources = ARRAY_SIZE(wm831x_ldo2_resources),
1152 .resources = wm831x_ldo2_resources,
1153 },
1154 {
1155 .name = "wm831x-ldo",
1156 .id = 3,
1157 .num_resources = ARRAY_SIZE(wm831x_ldo3_resources),
1158 .resources = wm831x_ldo3_resources,
1159 },
1160 {
1161 .name = "wm831x-ldo",
1162 .id = 4,
1163 .num_resources = ARRAY_SIZE(wm831x_ldo4_resources),
1164 .resources = wm831x_ldo4_resources,
1165 },
1166 {
1167 .name = "wm831x-ldo",
1168 .id = 5,
1169 .num_resources = ARRAY_SIZE(wm831x_ldo5_resources),
1170 .resources = wm831x_ldo5_resources,
1171 },
1172 {
1173 .name = "wm831x-ldo",
1174 .id = 6,
1175 .num_resources = ARRAY_SIZE(wm831x_ldo6_resources),
1176 .resources = wm831x_ldo6_resources,
1177 },
1178 {
1179 .name = "wm831x-aldo",
1180 .id = 7,
1181 .num_resources = ARRAY_SIZE(wm831x_ldo7_resources),
1182 .resources = wm831x_ldo7_resources,
1183 },
1184 {
1185 .name = "wm831x-aldo",
1186 .id = 8,
1187 .num_resources = ARRAY_SIZE(wm831x_ldo8_resources),
1188 .resources = wm831x_ldo8_resources,
1189 },
1190 {
1191 .name = "wm831x-aldo",
1192 .id = 9,
1193 .num_resources = ARRAY_SIZE(wm831x_ldo9_resources),
1194 .resources = wm831x_ldo9_resources,
1195 },
1196 {
1197 .name = "wm831x-aldo",
1198 .id = 10,
1199 .num_resources = ARRAY_SIZE(wm831x_ldo10_resources),
1200 .resources = wm831x_ldo10_resources,
1201 },
1202 {
1203 .name = "wm831x-alive-ldo",
1204 .id = 11,
1205 .num_resources = ARRAY_SIZE(wm831x_ldo11_resources),
1206 .resources = wm831x_ldo11_resources,
1207 },
1208 {
1209 .name = "wm831x-on",
1210 .num_resources = ARRAY_SIZE(wm831x_on_resources),
1211 .resources = wm831x_on_resources,
1212 },
1213 {
Mark Brownd4e0a892009-10-01 15:41:07 +01001214 .name = "wm831x-status",
1215 .id = 1,
1216 .num_resources = ARRAY_SIZE(wm831x_status1_resources),
1217 .resources = wm831x_status1_resources,
1218 },
1219 {
1220 .name = "wm831x-status",
1221 .id = 2,
1222 .num_resources = ARRAY_SIZE(wm831x_status2_resources),
1223 .resources = wm831x_status2_resources,
1224 },
1225 {
1226 .name = "wm831x-watchdog",
1227 .num_resources = ARRAY_SIZE(wm831x_wdt_resources),
1228 .resources = wm831x_wdt_resources,
1229 },
1230};
1231
Mark Brown266a5e02011-06-02 19:18:49 +01001232static struct mfd_cell touch_devs[] = {
1233 {
1234 .name = "wm831x-touch",
1235 .num_resources = ARRAY_SIZE(wm831x_touch_resources),
1236 .resources = wm831x_touch_resources,
1237 },
1238};
1239
Mark Brownb9d03d92011-06-02 19:18:50 +01001240static struct mfd_cell rtc_devs[] = {
1241 {
1242 .name = "wm831x-rtc",
1243 .num_resources = ARRAY_SIZE(wm831x_rtc_resources),
1244 .resources = wm831x_rtc_resources,
1245 },
1246};
Mark Brown266a5e02011-06-02 19:18:49 +01001247
Mark Brown63aed852009-07-27 14:45:55 +01001248static struct mfd_cell backlight_devs[] = {
1249 {
1250 .name = "wm831x-backlight",
1251 },
1252};
1253
Mark Brown1df59812011-06-10 19:28:10 +01001254struct regmap_config wm831x_regmap_config = {
1255 .reg_bits = 16,
1256 .val_bits = 16,
1257};
1258EXPORT_SYMBOL_GPL(wm831x_regmap_config);
1259
Mark Brownd2bedfe2009-07-27 14:45:52 +01001260/*
1261 * Instantiate the generic non-control parts of the device.
1262 */
Mark Browne5b48682010-10-19 23:57:56 +02001263int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
Mark Brownd2bedfe2009-07-27 14:45:52 +01001264{
1265 struct wm831x_pdata *pdata = wm831x->dev->platform_data;
Mark Browneb503dc2011-06-02 19:18:48 +01001266 int rev, wm831x_num;
Mark Brownd2bedfe2009-07-27 14:45:52 +01001267 enum wm831x_parent parent;
Mark Brown0b14c222011-04-04 11:04:42 +09001268 int ret, i;
Mark Brownd2bedfe2009-07-27 14:45:52 +01001269
1270 mutex_init(&wm831x->io_lock);
1271 mutex_init(&wm831x->key_lock);
1272 dev_set_drvdata(wm831x->dev, wm831x);
1273
1274 ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID);
1275 if (ret < 0) {
1276 dev_err(wm831x->dev, "Failed to read parent ID: %d\n", ret);
Mark Brown1df59812011-06-10 19:28:10 +01001277 goto err_regmap;
Mark Brownd2bedfe2009-07-27 14:45:52 +01001278 }
Mark Brownb93cef52010-12-02 16:25:43 +00001279 switch (ret) {
1280 case 0x6204:
1281 case 0x6246:
1282 break;
1283 default:
Mark Brownd2bedfe2009-07-27 14:45:52 +01001284 dev_err(wm831x->dev, "Device is not a WM831x: ID %x\n", ret);
1285 ret = -EINVAL;
Mark Brown1df59812011-06-10 19:28:10 +01001286 goto err_regmap;
Mark Brownd2bedfe2009-07-27 14:45:52 +01001287 }
1288
1289 ret = wm831x_reg_read(wm831x, WM831X_REVISION);
1290 if (ret < 0) {
1291 dev_err(wm831x->dev, "Failed to read revision: %d\n", ret);
Mark Brown1df59812011-06-10 19:28:10 +01001292 goto err_regmap;
Mark Brownd2bedfe2009-07-27 14:45:52 +01001293 }
1294 rev = (ret & WM831X_PARENT_REV_MASK) >> WM831X_PARENT_REV_SHIFT;
1295
1296 ret = wm831x_reg_read(wm831x, WM831X_RESET_ID);
1297 if (ret < 0) {
1298 dev_err(wm831x->dev, "Failed to read device ID: %d\n", ret);
Mark Brown1df59812011-06-10 19:28:10 +01001299 goto err_regmap;
Mark Brownd2bedfe2009-07-27 14:45:52 +01001300 }
1301
Mark Brown894362f2009-10-01 15:41:04 +01001302 /* Some engineering samples do not have the ID set, rely on
1303 * the device being registered correctly.
1304 */
1305 if (ret == 0) {
1306 dev_info(wm831x->dev, "Device is an engineering sample\n");
1307 ret = id;
1308 }
1309
Mark Brownd2bedfe2009-07-27 14:45:52 +01001310 switch (ret) {
Mark Brown894362f2009-10-01 15:41:04 +01001311 case WM8310:
Mark Brownd2bedfe2009-07-27 14:45:52 +01001312 parent = WM8310;
Mark Brown6f2ecaa2009-10-01 15:41:05 +01001313 wm831x->num_gpio = 16;
Mark Brownb03b4d72010-04-08 10:02:39 +02001314 wm831x->charger_irq_wake = 1;
Mark Brownf92e8f82010-02-17 18:45:25 +00001315 if (rev > 0) {
1316 wm831x->has_gpio_ena = 1;
1317 wm831x->has_cs_sts = 1;
1318 }
1319
Mark Brown894362f2009-10-01 15:41:04 +01001320 dev_info(wm831x->dev, "WM8310 revision %c\n", 'A' + rev);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001321 break;
1322
Mark Brown894362f2009-10-01 15:41:04 +01001323 case WM8311:
Mark Brownd2bedfe2009-07-27 14:45:52 +01001324 parent = WM8311;
Mark Brown6f2ecaa2009-10-01 15:41:05 +01001325 wm831x->num_gpio = 16;
Mark Brownb03b4d72010-04-08 10:02:39 +02001326 wm831x->charger_irq_wake = 1;
Mark Brownf92e8f82010-02-17 18:45:25 +00001327 if (rev > 0) {
1328 wm831x->has_gpio_ena = 1;
1329 wm831x->has_cs_sts = 1;
1330 }
1331
Mark Brown894362f2009-10-01 15:41:04 +01001332 dev_info(wm831x->dev, "WM8311 revision %c\n", 'A' + rev);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001333 break;
1334
Mark Brown894362f2009-10-01 15:41:04 +01001335 case WM8312:
Mark Brownd2bedfe2009-07-27 14:45:52 +01001336 parent = WM8312;
Mark Brown6f2ecaa2009-10-01 15:41:05 +01001337 wm831x->num_gpio = 16;
Mark Brownb03b4d72010-04-08 10:02:39 +02001338 wm831x->charger_irq_wake = 1;
Mark Brownf92e8f82010-02-17 18:45:25 +00001339 if (rev > 0) {
1340 wm831x->has_gpio_ena = 1;
1341 wm831x->has_cs_sts = 1;
1342 }
1343
Mark Brown894362f2009-10-01 15:41:04 +01001344 dev_info(wm831x->dev, "WM8312 revision %c\n", 'A' + rev);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001345 break;
1346
Mark Brownd4e0a892009-10-01 15:41:07 +01001347 case WM8320:
1348 parent = WM8320;
1349 wm831x->num_gpio = 12;
1350 dev_info(wm831x->dev, "WM8320 revision %c\n", 'A' + rev);
1351 break;
1352
Mark Brown88913522010-07-21 14:23:37 +01001353 case WM8321:
1354 parent = WM8321;
1355 wm831x->num_gpio = 12;
1356 dev_info(wm831x->dev, "WM8321 revision %c\n", 'A' + rev);
1357 break;
1358
Mark Brown0b315882010-09-28 09:13:39 -07001359 case WM8325:
1360 parent = WM8325;
1361 wm831x->num_gpio = 12;
1362 dev_info(wm831x->dev, "WM8325 revision %c\n", 'A' + rev);
1363 break;
1364
Mark Brown412dc112010-11-24 18:01:41 +00001365 case WM8326:
1366 parent = WM8326;
1367 wm831x->num_gpio = 12;
1368 dev_info(wm831x->dev, "WM8326 revision %c\n", 'A' + rev);
1369 break;
1370
Mark Brownd2bedfe2009-07-27 14:45:52 +01001371 default:
1372 dev_err(wm831x->dev, "Unknown WM831x device %04x\n", ret);
1373 ret = -EINVAL;
Mark Brown1df59812011-06-10 19:28:10 +01001374 goto err_regmap;
Mark Brownd2bedfe2009-07-27 14:45:52 +01001375 }
1376
1377 /* This will need revisiting in future but is OK for all
1378 * current parts.
1379 */
1380 if (parent != id)
Mark Brown894362f2009-10-01 15:41:04 +01001381 dev_warn(wm831x->dev, "Device was registered as a WM%lx\n",
Mark Brownd2bedfe2009-07-27 14:45:52 +01001382 id);
1383
1384 /* Bootstrap the user key */
1385 ret = wm831x_reg_read(wm831x, WM831X_SECURITY_KEY);
1386 if (ret < 0) {
1387 dev_err(wm831x->dev, "Failed to read security key: %d\n", ret);
Mark Brown1df59812011-06-10 19:28:10 +01001388 goto err_regmap;
Mark Brownd2bedfe2009-07-27 14:45:52 +01001389 }
1390 if (ret != 0) {
1391 dev_warn(wm831x->dev, "Security key had non-zero value %x\n",
1392 ret);
1393 wm831x_reg_write(wm831x, WM831X_SECURITY_KEY, 0);
1394 }
1395 wm831x->locked = 1;
1396
1397 if (pdata && pdata->pre_init) {
1398 ret = pdata->pre_init(wm831x);
1399 if (ret != 0) {
1400 dev_err(wm831x->dev, "pre_init() failed: %d\n", ret);
Mark Brown1df59812011-06-10 19:28:10 +01001401 goto err_regmap;
Mark Brownd2bedfe2009-07-27 14:45:52 +01001402 }
1403 }
1404
Mark Brown0b14c222011-04-04 11:04:42 +09001405 if (pdata) {
1406 for (i = 0; i < ARRAY_SIZE(pdata->gpio_defaults); i++) {
1407 if (!pdata->gpio_defaults[i])
1408 continue;
1409
1410 wm831x_reg_write(wm831x,
1411 WM831X_GPIO1_CONTROL + i,
1412 pdata->gpio_defaults[i] & 0xffff);
1413 }
1414 }
1415
Mark Browneb503dc2011-06-02 19:18:48 +01001416 /* Multiply by 10 as we have many subdevices of the same type */
1417 if (pdata && pdata->wm831x_num)
1418 wm831x_num = pdata->wm831x_num * 10;
1419 else
1420 wm831x_num = -1;
1421
Mark Brown7d4d0a32009-07-27 14:45:53 +01001422 ret = wm831x_irq_init(wm831x, irq);
1423 if (ret != 0)
Mark Brown1df59812011-06-10 19:28:10 +01001424 goto err_regmap;
Mark Brown7d4d0a32009-07-27 14:45:53 +01001425
Mark Browne69b6de2011-06-02 19:18:53 +01001426 wm831x_auxadc_init(wm831x);
Mark Brown473fe732010-02-23 11:08:06 +00001427
Mark Brownd2bedfe2009-07-27 14:45:52 +01001428 /* The core device is up, instantiate the subdevices. */
1429 switch (parent) {
1430 case WM8310:
Mark Browneb503dc2011-06-02 19:18:48 +01001431 ret = mfd_add_devices(wm831x->dev, wm831x_num,
Mark Brownd2bedfe2009-07-27 14:45:52 +01001432 wm8310_devs, ARRAY_SIZE(wm8310_devs),
Mark Brown5fb4d382009-11-11 16:10:22 +00001433 NULL, wm831x->irq_base);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001434 break;
1435
1436 case WM8311:
Mark Browneb503dc2011-06-02 19:18:48 +01001437 ret = mfd_add_devices(wm831x->dev, wm831x_num,
Mark Brownd2bedfe2009-07-27 14:45:52 +01001438 wm8311_devs, ARRAY_SIZE(wm8311_devs),
Mark Brown5fb4d382009-11-11 16:10:22 +00001439 NULL, wm831x->irq_base);
Mark Brown266a5e02011-06-02 19:18:49 +01001440 if (!pdata || !pdata->disable_touch)
1441 mfd_add_devices(wm831x->dev, wm831x_num,
1442 touch_devs, ARRAY_SIZE(touch_devs),
1443 NULL, wm831x->irq_base);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001444 break;
1445
1446 case WM8312:
Mark Browneb503dc2011-06-02 19:18:48 +01001447 ret = mfd_add_devices(wm831x->dev, wm831x_num,
Mark Brownd2bedfe2009-07-27 14:45:52 +01001448 wm8312_devs, ARRAY_SIZE(wm8312_devs),
Mark Brown5fb4d382009-11-11 16:10:22 +00001449 NULL, wm831x->irq_base);
Mark Brown266a5e02011-06-02 19:18:49 +01001450 if (!pdata || !pdata->disable_touch)
1451 mfd_add_devices(wm831x->dev, wm831x_num,
1452 touch_devs, ARRAY_SIZE(touch_devs),
1453 NULL, wm831x->irq_base);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001454 break;
1455
Mark Brownd4e0a892009-10-01 15:41:07 +01001456 case WM8320:
Mark Brown88913522010-07-21 14:23:37 +01001457 case WM8321:
Mark Brown0b315882010-09-28 09:13:39 -07001458 case WM8325:
Mark Brown412dc112010-11-24 18:01:41 +00001459 case WM8326:
Mark Browneb503dc2011-06-02 19:18:48 +01001460 ret = mfd_add_devices(wm831x->dev, wm831x_num,
Mark Brown0b315882010-09-28 09:13:39 -07001461 wm8320_devs, ARRAY_SIZE(wm8320_devs),
Mark Brownbd7c72e2010-11-24 18:01:39 +00001462 NULL, wm831x->irq_base);
Mark Brown0b315882010-09-28 09:13:39 -07001463 break;
1464
Mark Brownd2bedfe2009-07-27 14:45:52 +01001465 default:
1466 /* If this happens the bus probe function is buggy */
1467 BUG();
1468 }
1469
1470 if (ret != 0) {
1471 dev_err(wm831x->dev, "Failed to add children\n");
Mark Brown7d4d0a32009-07-27 14:45:53 +01001472 goto err_irq;
Mark Brownd2bedfe2009-07-27 14:45:52 +01001473 }
1474
Mark Brownb9d03d92011-06-02 19:18:50 +01001475 /* The RTC can only be used if the 32.768kHz crystal is
1476 * enabled; this can't be controlled by software at runtime.
1477 */
1478 ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2);
1479 if (ret < 0) {
1480 dev_err(wm831x->dev, "Failed to read clock status: %d\n", ret);
1481 goto err_irq;
1482 }
1483
1484 if (ret & WM831X_XTAL_ENA) {
1485 ret = mfd_add_devices(wm831x->dev, wm831x_num,
1486 rtc_devs, ARRAY_SIZE(rtc_devs),
1487 NULL, wm831x->irq_base);
1488 if (ret != 0) {
1489 dev_err(wm831x->dev, "Failed to add RTC: %d\n", ret);
1490 goto err_irq;
1491 }
1492 } else {
1493 dev_info(wm831x->dev, "32.768kHz clock disabled, no RTC\n");
1494 }
1495
Mark Brown63aed852009-07-27 14:45:55 +01001496 if (pdata && pdata->backlight) {
1497 /* Treat errors as non-critical */
Mark Browneb503dc2011-06-02 19:18:48 +01001498 ret = mfd_add_devices(wm831x->dev, wm831x_num, backlight_devs,
Mark Brown5fb4d382009-11-11 16:10:22 +00001499 ARRAY_SIZE(backlight_devs), NULL,
1500 wm831x->irq_base);
Mark Brown63aed852009-07-27 14:45:55 +01001501 if (ret < 0)
1502 dev_err(wm831x->dev, "Failed to add backlight: %d\n",
1503 ret);
1504 }
1505
Mark Brown6704e512009-07-27 14:45:56 +01001506 wm831x_otp_init(wm831x);
1507
Mark Brownd2bedfe2009-07-27 14:45:52 +01001508 if (pdata && pdata->post_init) {
1509 ret = pdata->post_init(wm831x);
1510 if (ret != 0) {
1511 dev_err(wm831x->dev, "post_init() failed: %d\n", ret);
Mark Brown7d4d0a32009-07-27 14:45:53 +01001512 goto err_irq;
Mark Brownd2bedfe2009-07-27 14:45:52 +01001513 }
1514 }
1515
1516 return 0;
1517
Mark Brown7d4d0a32009-07-27 14:45:53 +01001518err_irq:
1519 wm831x_irq_exit(wm831x);
Mark Brown1df59812011-06-10 19:28:10 +01001520err_regmap:
Mark Brownd2bedfe2009-07-27 14:45:52 +01001521 mfd_remove_devices(wm831x->dev);
Mark Brown1df59812011-06-10 19:28:10 +01001522 regmap_exit(wm831x->regmap);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001523 kfree(wm831x);
1524 return ret;
1525}
1526
Mark Browne5b48682010-10-19 23:57:56 +02001527void wm831x_device_exit(struct wm831x *wm831x)
Mark Brownd2bedfe2009-07-27 14:45:52 +01001528{
Mark Brown6704e512009-07-27 14:45:56 +01001529 wm831x_otp_exit(wm831x);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001530 mfd_remove_devices(wm831x->dev);
Mark Brown473fe732010-02-23 11:08:06 +00001531 if (wm831x->irq_base)
1532 free_irq(wm831x->irq_base + WM831X_IRQ_AUXADC_DATA, wm831x);
Mark Brown7d4d0a32009-07-27 14:45:53 +01001533 wm831x_irq_exit(wm831x);
Mark Brown1df59812011-06-10 19:28:10 +01001534 regmap_exit(wm831x->regmap);
Mark Brownd2bedfe2009-07-27 14:45:52 +01001535 kfree(wm831x);
1536}
1537
Mark Browne5b48682010-10-19 23:57:56 +02001538int wm831x_device_suspend(struct wm831x *wm831x)
Mark Brownb03b4d72010-04-08 10:02:39 +02001539{
1540 int reg, mask;
1541
1542 /* If the charger IRQs are a wake source then make sure we ack
1543 * them even if they're not actively being used (eg, no power
1544 * driver or no IRQ line wired up) then acknowledge the
1545 * interrupts otherwise suspend won't last very long.
1546 */
1547 if (wm831x->charger_irq_wake) {
1548 reg = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_2_MASK);
1549
1550 mask = WM831X_CHG_BATT_HOT_EINT |
1551 WM831X_CHG_BATT_COLD_EINT |
1552 WM831X_CHG_BATT_FAIL_EINT |
1553 WM831X_CHG_OV_EINT | WM831X_CHG_END_EINT |
1554 WM831X_CHG_TO_EINT | WM831X_CHG_MODE_EINT |
1555 WM831X_CHG_START_EINT;
1556
1557 /* If any of the interrupts are masked read the statuses */
1558 if (reg & mask)
1559 reg = wm831x_reg_read(wm831x,
1560 WM831X_INTERRUPT_STATUS_2);
1561
1562 if (reg & mask) {
1563 dev_info(wm831x->dev,
1564 "Acknowledging masked charger IRQs: %x\n",
1565 reg & mask);
1566 wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_2,
1567 reg & mask);
1568 }
1569 }
1570
1571 return 0;
1572}
1573
Mark Browne5b48682010-10-19 23:57:56 +02001574MODULE_DESCRIPTION("Core support for the WM831X AudioPlus PMIC");
Mark Brownd2bedfe2009-07-27 14:45:52 +01001575MODULE_LICENSE("GPL");
1576MODULE_AUTHOR("Mark Brown");