diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mfd/wm831x-core.c | 1357 |
1 files changed, 1357 insertions, 0 deletions
diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c new file mode 100644 index 000000000000..cc1040c9d46c --- /dev/null +++ b/drivers/mfd/wm831x-core.c | |||
@@ -0,0 +1,1357 @@ | |||
1 | /* | ||
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> | ||
17 | #include <linux/i2c.h> | ||
18 | #include <linux/mfd/core.h> | ||
19 | |||
20 | #include <linux/mfd/wm831x/core.h> | ||
21 | #include <linux/mfd/wm831x/pdata.h> | ||
22 | |||
23 | enum wm831x_parent { | ||
24 | WM8310 = 0, | ||
25 | WM8311 = 1, | ||
26 | WM8312 = 2, | ||
27 | }; | ||
28 | |||
29 | static int wm831x_reg_locked(struct wm831x *wm831x, unsigned short reg) | ||
30 | { | ||
31 | if (!wm831x->locked) | ||
32 | return 0; | ||
33 | |||
34 | switch (reg) { | ||
35 | case WM831X_WATCHDOG: | ||
36 | case WM831X_DC4_CONTROL: | ||
37 | case WM831X_ON_PIN_CONTROL: | ||
38 | case WM831X_BACKUP_CHARGER_CONTROL: | ||
39 | case WM831X_CHARGER_CONTROL_1: | ||
40 | case WM831X_CHARGER_CONTROL_2: | ||
41 | return 1; | ||
42 | |||
43 | default: | ||
44 | return 0; | ||
45 | } | ||
46 | } | ||
47 | |||
48 | /** | ||
49 | * wm831x_reg_unlock: Unlock user keyed registers | ||
50 | * | ||
51 | * The WM831x has a user key preventing writes to particularly | ||
52 | * critical registers. This function locks those registers, | ||
53 | * allowing writes to them. | ||
54 | */ | ||
55 | void wm831x_reg_lock(struct wm831x *wm831x) | ||
56 | { | ||
57 | int ret; | ||
58 | |||
59 | ret = wm831x_reg_write(wm831x, WM831X_SECURITY_KEY, 0); | ||
60 | if (ret == 0) { | ||
61 | dev_vdbg(wm831x->dev, "Registers locked\n"); | ||
62 | |||
63 | mutex_lock(&wm831x->io_lock); | ||
64 | WARN_ON(wm831x->locked); | ||
65 | wm831x->locked = 1; | ||
66 | mutex_unlock(&wm831x->io_lock); | ||
67 | } else { | ||
68 | dev_err(wm831x->dev, "Failed to lock registers: %d\n", ret); | ||
69 | } | ||
70 | |||
71 | } | ||
72 | EXPORT_SYMBOL_GPL(wm831x_reg_lock); | ||
73 | |||
74 | /** | ||
75 | * wm831x_reg_unlock: Unlock user keyed registers | ||
76 | * | ||
77 | * The WM831x has a user key preventing writes to particularly | ||
78 | * critical registers. This function locks those registers, | ||
79 | * preventing spurious writes. | ||
80 | */ | ||
81 | int wm831x_reg_unlock(struct wm831x *wm831x) | ||
82 | { | ||
83 | int ret; | ||
84 | |||
85 | /* 0x9716 is the value required to unlock the registers */ | ||
86 | ret = wm831x_reg_write(wm831x, WM831X_SECURITY_KEY, 0x9716); | ||
87 | if (ret == 0) { | ||
88 | dev_vdbg(wm831x->dev, "Registers unlocked\n"); | ||
89 | |||
90 | mutex_lock(&wm831x->io_lock); | ||
91 | WARN_ON(!wm831x->locked); | ||
92 | wm831x->locked = 0; | ||
93 | mutex_unlock(&wm831x->io_lock); | ||
94 | } | ||
95 | |||
96 | return ret; | ||
97 | } | ||
98 | EXPORT_SYMBOL_GPL(wm831x_reg_unlock); | ||
99 | |||
100 | static int wm831x_read(struct wm831x *wm831x, unsigned short reg, | ||
101 | int bytes, void *dest) | ||
102 | { | ||
103 | int ret, i; | ||
104 | u16 *buf = dest; | ||
105 | |||
106 | BUG_ON(bytes % 2); | ||
107 | BUG_ON(bytes <= 0); | ||
108 | |||
109 | ret = wm831x->read_dev(wm831x, reg, bytes, dest); | ||
110 | if (ret < 0) | ||
111 | return ret; | ||
112 | |||
113 | for (i = 0; i < bytes / 2; i++) { | ||
114 | buf[i] = be16_to_cpu(buf[i]); | ||
115 | |||
116 | dev_vdbg(wm831x->dev, "Read %04x from R%d(0x%x)\n", | ||
117 | buf[i], reg + i, reg + i); | ||
118 | } | ||
119 | |||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * wm831x_reg_read: Read a single WM831x register. | ||
125 | * | ||
126 | * @wm831x: Device to read from. | ||
127 | * @reg: Register to read. | ||
128 | */ | ||
129 | int wm831x_reg_read(struct wm831x *wm831x, unsigned short reg) | ||
130 | { | ||
131 | unsigned short val; | ||
132 | int ret; | ||
133 | |||
134 | mutex_lock(&wm831x->io_lock); | ||
135 | |||
136 | ret = wm831x_read(wm831x, reg, 2, &val); | ||
137 | |||
138 | mutex_unlock(&wm831x->io_lock); | ||
139 | |||
140 | if (ret < 0) | ||
141 | return ret; | ||
142 | else | ||
143 | return val; | ||
144 | } | ||
145 | EXPORT_SYMBOL_GPL(wm831x_reg_read); | ||
146 | |||
147 | /** | ||
148 | * wm831x_bulk_read: Read multiple WM831x registers | ||
149 | * | ||
150 | * @wm831x: Device to read from | ||
151 | * @reg: First register | ||
152 | * @count: Number of registers | ||
153 | * @buf: Buffer to fill. | ||
154 | */ | ||
155 | int wm831x_bulk_read(struct wm831x *wm831x, unsigned short reg, | ||
156 | int count, u16 *buf) | ||
157 | { | ||
158 | int ret; | ||
159 | |||
160 | mutex_lock(&wm831x->io_lock); | ||
161 | |||
162 | ret = wm831x_read(wm831x, reg, count * 2, buf); | ||
163 | |||
164 | mutex_unlock(&wm831x->io_lock); | ||
165 | |||
166 | return ret; | ||
167 | } | ||
168 | EXPORT_SYMBOL_GPL(wm831x_bulk_read); | ||
169 | |||
170 | static int wm831x_write(struct wm831x *wm831x, unsigned short reg, | ||
171 | int bytes, void *src) | ||
172 | { | ||
173 | u16 *buf = src; | ||
174 | int i; | ||
175 | |||
176 | BUG_ON(bytes % 2); | ||
177 | BUG_ON(bytes <= 0); | ||
178 | |||
179 | for (i = 0; i < bytes / 2; i++) { | ||
180 | if (wm831x_reg_locked(wm831x, reg)) | ||
181 | return -EPERM; | ||
182 | |||
183 | dev_vdbg(wm831x->dev, "Write %04x to R%d(0x%x)\n", | ||
184 | buf[i], reg + i, reg + i); | ||
185 | |||
186 | buf[i] = cpu_to_be16(buf[i]); | ||
187 | } | ||
188 | |||
189 | return wm831x->write_dev(wm831x, reg, bytes, src); | ||
190 | } | ||
191 | |||
192 | /** | ||
193 | * wm831x_reg_write: Write a single WM831x register. | ||
194 | * | ||
195 | * @wm831x: Device to write to. | ||
196 | * @reg: Register to write to. | ||
197 | * @val: Value to write. | ||
198 | */ | ||
199 | int wm831x_reg_write(struct wm831x *wm831x, unsigned short reg, | ||
200 | unsigned short val) | ||
201 | { | ||
202 | int ret; | ||
203 | |||
204 | mutex_lock(&wm831x->io_lock); | ||
205 | |||
206 | ret = wm831x_write(wm831x, reg, 2, &val); | ||
207 | |||
208 | mutex_unlock(&wm831x->io_lock); | ||
209 | |||
210 | return ret; | ||
211 | } | ||
212 | EXPORT_SYMBOL_GPL(wm831x_reg_write); | ||
213 | |||
214 | /** | ||
215 | * wm831x_set_bits: Set the value of a bitfield in a WM831x register | ||
216 | * | ||
217 | * @wm831x: Device to write to. | ||
218 | * @reg: Register to write to. | ||
219 | * @mask: Mask of bits to set. | ||
220 | * @val: Value to set (unshifted) | ||
221 | */ | ||
222 | int wm831x_set_bits(struct wm831x *wm831x, unsigned short reg, | ||
223 | unsigned short mask, unsigned short val) | ||
224 | { | ||
225 | int ret; | ||
226 | u16 r; | ||
227 | |||
228 | mutex_lock(&wm831x->io_lock); | ||
229 | |||
230 | ret = wm831x_read(wm831x, reg, 2, &r); | ||
231 | if (ret < 0) | ||
232 | goto out; | ||
233 | |||
234 | r &= ~mask; | ||
235 | r |= val; | ||
236 | |||
237 | ret = wm831x_write(wm831x, reg, 2, &r); | ||
238 | |||
239 | out: | ||
240 | mutex_unlock(&wm831x->io_lock); | ||
241 | |||
242 | return ret; | ||
243 | } | ||
244 | EXPORT_SYMBOL_GPL(wm831x_set_bits); | ||
245 | |||
246 | static struct resource wm831x_dcdc1_resources[] = { | ||
247 | { | ||
248 | .start = WM831X_DC1_CONTROL_1, | ||
249 | .end = WM831X_DC1_DVS_CONTROL, | ||
250 | .flags = IORESOURCE_IO, | ||
251 | }, | ||
252 | { | ||
253 | .name = "UV", | ||
254 | .start = WM831X_IRQ_UV_DC1, | ||
255 | .end = WM831X_IRQ_UV_DC1, | ||
256 | .flags = IORESOURCE_IRQ, | ||
257 | }, | ||
258 | { | ||
259 | .name = "HC", | ||
260 | .start = WM831X_IRQ_HC_DC1, | ||
261 | .end = WM831X_IRQ_HC_DC1, | ||
262 | .flags = IORESOURCE_IRQ, | ||
263 | }, | ||
264 | }; | ||
265 | |||
266 | |||
267 | static struct resource wm831x_dcdc2_resources[] = { | ||
268 | { | ||
269 | .start = WM831X_DC2_CONTROL_1, | ||
270 | .end = WM831X_DC2_DVS_CONTROL, | ||
271 | .flags = IORESOURCE_IO, | ||
272 | }, | ||
273 | { | ||
274 | .name = "UV", | ||
275 | .start = WM831X_IRQ_UV_DC2, | ||
276 | .end = WM831X_IRQ_UV_DC2, | ||
277 | .flags = IORESOURCE_IRQ, | ||
278 | }, | ||
279 | { | ||
280 | .name = "HC", | ||
281 | .start = WM831X_IRQ_HC_DC2, | ||
282 | .end = WM831X_IRQ_HC_DC2, | ||
283 | .flags = IORESOURCE_IRQ, | ||
284 | }, | ||
285 | }; | ||
286 | |||
287 | static struct resource wm831x_dcdc3_resources[] = { | ||
288 | { | ||
289 | .start = WM831X_DC3_CONTROL_1, | ||
290 | .end = WM831X_DC3_SLEEP_CONTROL, | ||
291 | .flags = IORESOURCE_IO, | ||
292 | }, | ||
293 | { | ||
294 | .name = "UV", | ||
295 | .start = WM831X_IRQ_UV_DC3, | ||
296 | .end = WM831X_IRQ_UV_DC3, | ||
297 | .flags = IORESOURCE_IRQ, | ||
298 | }, | ||
299 | }; | ||
300 | |||
301 | static struct resource wm831x_dcdc4_resources[] = { | ||
302 | { | ||
303 | .start = WM831X_DC4_CONTROL, | ||
304 | .end = WM831X_DC4_SLEEP_CONTROL, | ||
305 | .flags = IORESOURCE_IO, | ||
306 | }, | ||
307 | { | ||
308 | .name = "UV", | ||
309 | .start = WM831X_IRQ_UV_DC4, | ||
310 | .end = WM831X_IRQ_UV_DC4, | ||
311 | .flags = IORESOURCE_IRQ, | ||
312 | }, | ||
313 | }; | ||
314 | |||
315 | static struct resource wm831x_gpio_resources[] = { | ||
316 | { | ||
317 | .start = WM831X_IRQ_GPIO_1, | ||
318 | .end = WM831X_IRQ_GPIO_16, | ||
319 | .flags = IORESOURCE_IRQ, | ||
320 | }, | ||
321 | }; | ||
322 | |||
323 | static struct resource wm831x_isink1_resources[] = { | ||
324 | { | ||
325 | .start = WM831X_CURRENT_SINK_1, | ||
326 | .end = WM831X_CURRENT_SINK_1, | ||
327 | .flags = IORESOURCE_IO, | ||
328 | }, | ||
329 | { | ||
330 | .start = WM831X_IRQ_CS1, | ||
331 | .end = WM831X_IRQ_CS1, | ||
332 | .flags = IORESOURCE_IRQ, | ||
333 | }, | ||
334 | }; | ||
335 | |||
336 | static struct resource wm831x_isink2_resources[] = { | ||
337 | { | ||
338 | .start = WM831X_CURRENT_SINK_2, | ||
339 | .end = WM831X_CURRENT_SINK_2, | ||
340 | .flags = IORESOURCE_IO, | ||
341 | }, | ||
342 | { | ||
343 | .start = WM831X_IRQ_CS2, | ||
344 | .end = WM831X_IRQ_CS2, | ||
345 | .flags = IORESOURCE_IRQ, | ||
346 | }, | ||
347 | }; | ||
348 | |||
349 | static struct resource wm831x_ldo1_resources[] = { | ||
350 | { | ||
351 | .start = WM831X_LDO1_CONTROL, | ||
352 | .end = WM831X_LDO1_SLEEP_CONTROL, | ||
353 | .flags = IORESOURCE_IO, | ||
354 | }, | ||
355 | { | ||
356 | .name = "UV", | ||
357 | .start = WM831X_IRQ_UV_LDO1, | ||
358 | .end = WM831X_IRQ_UV_LDO1, | ||
359 | .flags = IORESOURCE_IRQ, | ||
360 | }, | ||
361 | }; | ||
362 | |||
363 | static struct resource wm831x_ldo2_resources[] = { | ||
364 | { | ||
365 | .start = WM831X_LDO2_CONTROL, | ||
366 | .end = WM831X_LDO2_SLEEP_CONTROL, | ||
367 | .flags = IORESOURCE_IO, | ||
368 | }, | ||
369 | { | ||
370 | .name = "UV", | ||
371 | .start = WM831X_IRQ_UV_LDO2, | ||
372 | .end = WM831X_IRQ_UV_LDO2, | ||
373 | .flags = IORESOURCE_IRQ, | ||
374 | }, | ||
375 | }; | ||
376 | |||
377 | static struct resource wm831x_ldo3_resources[] = { | ||
378 | { | ||
379 | .start = WM831X_LDO3_CONTROL, | ||
380 | .end = WM831X_LDO3_SLEEP_CONTROL, | ||
381 | .flags = IORESOURCE_IO, | ||
382 | }, | ||
383 | { | ||
384 | .name = "UV", | ||
385 | .start = WM831X_IRQ_UV_LDO3, | ||
386 | .end = WM831X_IRQ_UV_LDO3, | ||
387 | .flags = IORESOURCE_IRQ, | ||
388 | }, | ||
389 | }; | ||
390 | |||
391 | static struct resource wm831x_ldo4_resources[] = { | ||
392 | { | ||
393 | .start = WM831X_LDO4_CONTROL, | ||
394 | .end = WM831X_LDO4_SLEEP_CONTROL, | ||
395 | .flags = IORESOURCE_IO, | ||
396 | }, | ||
397 | { | ||
398 | .name = "UV", | ||
399 | .start = WM831X_IRQ_UV_LDO4, | ||
400 | .end = WM831X_IRQ_UV_LDO4, | ||
401 | .flags = IORESOURCE_IRQ, | ||
402 | }, | ||
403 | }; | ||
404 | |||
405 | static struct resource wm831x_ldo5_resources[] = { | ||
406 | { | ||
407 | .start = WM831X_LDO5_CONTROL, | ||
408 | .end = WM831X_LDO5_SLEEP_CONTROL, | ||
409 | .flags = IORESOURCE_IO, | ||
410 | }, | ||
411 | { | ||
412 | .name = "UV", | ||
413 | .start = WM831X_IRQ_UV_LDO5, | ||
414 | .end = WM831X_IRQ_UV_LDO5, | ||
415 | .flags = IORESOURCE_IRQ, | ||
416 | }, | ||
417 | }; | ||
418 | |||
419 | static struct resource wm831x_ldo6_resources[] = { | ||
420 | { | ||
421 | .start = WM831X_LDO6_CONTROL, | ||
422 | .end = WM831X_LDO6_SLEEP_CONTROL, | ||
423 | .flags = IORESOURCE_IO, | ||
424 | }, | ||
425 | { | ||
426 | .name = "UV", | ||
427 | .start = WM831X_IRQ_UV_LDO6, | ||
428 | .end = WM831X_IRQ_UV_LDO6, | ||
429 | .flags = IORESOURCE_IRQ, | ||
430 | }, | ||
431 | }; | ||
432 | |||
433 | static struct resource wm831x_ldo7_resources[] = { | ||
434 | { | ||
435 | .start = WM831X_LDO7_CONTROL, | ||
436 | .end = WM831X_LDO7_SLEEP_CONTROL, | ||
437 | .flags = IORESOURCE_IO, | ||
438 | }, | ||
439 | { | ||
440 | .name = "UV", | ||
441 | .start = WM831X_IRQ_UV_LDO7, | ||
442 | .end = WM831X_IRQ_UV_LDO7, | ||
443 | .flags = IORESOURCE_IRQ, | ||
444 | }, | ||
445 | }; | ||
446 | |||
447 | static struct resource wm831x_ldo8_resources[] = { | ||
448 | { | ||
449 | .start = WM831X_LDO8_CONTROL, | ||
450 | .end = WM831X_LDO8_SLEEP_CONTROL, | ||
451 | .flags = IORESOURCE_IO, | ||
452 | }, | ||
453 | { | ||
454 | .name = "UV", | ||
455 | .start = WM831X_IRQ_UV_LDO8, | ||
456 | .end = WM831X_IRQ_UV_LDO8, | ||
457 | .flags = IORESOURCE_IRQ, | ||
458 | }, | ||
459 | }; | ||
460 | |||
461 | static struct resource wm831x_ldo9_resources[] = { | ||
462 | { | ||
463 | .start = WM831X_LDO9_CONTROL, | ||
464 | .end = WM831X_LDO9_SLEEP_CONTROL, | ||
465 | .flags = IORESOURCE_IO, | ||
466 | }, | ||
467 | { | ||
468 | .name = "UV", | ||
469 | .start = WM831X_IRQ_UV_LDO9, | ||
470 | .end = WM831X_IRQ_UV_LDO9, | ||
471 | .flags = IORESOURCE_IRQ, | ||
472 | }, | ||
473 | }; | ||
474 | |||
475 | static struct resource wm831x_ldo10_resources[] = { | ||
476 | { | ||
477 | .start = WM831X_LDO10_CONTROL, | ||
478 | .end = WM831X_LDO10_SLEEP_CONTROL, | ||
479 | .flags = IORESOURCE_IO, | ||
480 | }, | ||
481 | { | ||
482 | .name = "UV", | ||
483 | .start = WM831X_IRQ_UV_LDO10, | ||
484 | .end = WM831X_IRQ_UV_LDO10, | ||
485 | .flags = IORESOURCE_IRQ, | ||
486 | }, | ||
487 | }; | ||
488 | |||
489 | static struct resource wm831x_ldo11_resources[] = { | ||
490 | { | ||
491 | .start = WM831X_LDO11_ON_CONTROL, | ||
492 | .end = WM831X_LDO11_SLEEP_CONTROL, | ||
493 | .flags = IORESOURCE_IO, | ||
494 | }, | ||
495 | }; | ||
496 | |||
497 | static struct resource wm831x_on_resources[] = { | ||
498 | { | ||
499 | .start = WM831X_IRQ_ON, | ||
500 | .end = WM831X_IRQ_ON, | ||
501 | .flags = IORESOURCE_IRQ, | ||
502 | }, | ||
503 | }; | ||
504 | |||
505 | |||
506 | static struct resource wm831x_power_resources[] = { | ||
507 | { | ||
508 | .name = "SYSLO", | ||
509 | .start = WM831X_IRQ_PPM_SYSLO, | ||
510 | .end = WM831X_IRQ_PPM_SYSLO, | ||
511 | .flags = IORESOURCE_IRQ, | ||
512 | }, | ||
513 | { | ||
514 | .name = "PWR SRC", | ||
515 | .start = WM831X_IRQ_PPM_PWR_SRC, | ||
516 | .end = WM831X_IRQ_PPM_PWR_SRC, | ||
517 | .flags = IORESOURCE_IRQ, | ||
518 | }, | ||
519 | { | ||
520 | .name = "USB CURR", | ||
521 | .start = WM831X_IRQ_PPM_USB_CURR, | ||
522 | .end = WM831X_IRQ_PPM_USB_CURR, | ||
523 | .flags = IORESOURCE_IRQ, | ||
524 | }, | ||
525 | { | ||
526 | .name = "BATT HOT", | ||
527 | .start = WM831X_IRQ_CHG_BATT_HOT, | ||
528 | .end = WM831X_IRQ_CHG_BATT_HOT, | ||
529 | .flags = IORESOURCE_IRQ, | ||
530 | }, | ||
531 | { | ||
532 | .name = "BATT COLD", | ||
533 | .start = WM831X_IRQ_CHG_BATT_COLD, | ||
534 | .end = WM831X_IRQ_CHG_BATT_COLD, | ||
535 | .flags = IORESOURCE_IRQ, | ||
536 | }, | ||
537 | { | ||
538 | .name = "BATT FAIL", | ||
539 | .start = WM831X_IRQ_CHG_BATT_FAIL, | ||
540 | .end = WM831X_IRQ_CHG_BATT_FAIL, | ||
541 | .flags = IORESOURCE_IRQ, | ||
542 | }, | ||
543 | { | ||
544 | .name = "OV", | ||
545 | .start = WM831X_IRQ_CHG_OV, | ||
546 | .end = WM831X_IRQ_CHG_OV, | ||
547 | .flags = IORESOURCE_IRQ, | ||
548 | }, | ||
549 | { | ||
550 | .name = "END", | ||
551 | .start = WM831X_IRQ_CHG_END, | ||
552 | .end = WM831X_IRQ_CHG_END, | ||
553 | .flags = IORESOURCE_IRQ, | ||
554 | }, | ||
555 | { | ||
556 | .name = "TO", | ||
557 | .start = WM831X_IRQ_CHG_TO, | ||
558 | .end = WM831X_IRQ_CHG_TO, | ||
559 | .flags = IORESOURCE_IRQ, | ||
560 | }, | ||
561 | { | ||
562 | .name = "MODE", | ||
563 | .start = WM831X_IRQ_CHG_MODE, | ||
564 | .end = WM831X_IRQ_CHG_MODE, | ||
565 | .flags = IORESOURCE_IRQ, | ||
566 | }, | ||
567 | { | ||
568 | .name = "START", | ||
569 | .start = WM831X_IRQ_CHG_START, | ||
570 | .end = WM831X_IRQ_CHG_START, | ||
571 | .flags = IORESOURCE_IRQ, | ||
572 | }, | ||
573 | }; | ||
574 | |||
575 | static struct resource wm831x_rtc_resources[] = { | ||
576 | { | ||
577 | .name = "PER", | ||
578 | .start = WM831X_IRQ_RTC_PER, | ||
579 | .end = WM831X_IRQ_RTC_PER, | ||
580 | .flags = IORESOURCE_IRQ, | ||
581 | }, | ||
582 | { | ||
583 | .name = "ALM", | ||
584 | .start = WM831X_IRQ_RTC_ALM, | ||
585 | .end = WM831X_IRQ_RTC_ALM, | ||
586 | .flags = IORESOURCE_IRQ, | ||
587 | }, | ||
588 | }; | ||
589 | |||
590 | static struct resource wm831x_status1_resources[] = { | ||
591 | { | ||
592 | .start = WM831X_STATUS_LED_1, | ||
593 | .end = WM831X_STATUS_LED_1, | ||
594 | .flags = IORESOURCE_IO, | ||
595 | }, | ||
596 | }; | ||
597 | |||
598 | static struct resource wm831x_status2_resources[] = { | ||
599 | { | ||
600 | .start = WM831X_STATUS_LED_2, | ||
601 | .end = WM831X_STATUS_LED_2, | ||
602 | .flags = IORESOURCE_IO, | ||
603 | }, | ||
604 | }; | ||
605 | |||
606 | static struct resource wm831x_touch_resources[] = { | ||
607 | { | ||
608 | .name = "TCHPD", | ||
609 | .start = WM831X_IRQ_TCHPD, | ||
610 | .end = WM831X_IRQ_TCHPD, | ||
611 | .flags = IORESOURCE_IRQ, | ||
612 | }, | ||
613 | { | ||
614 | .name = "TCHDATA", | ||
615 | .start = WM831X_IRQ_TCHDATA, | ||
616 | .end = WM831X_IRQ_TCHDATA, | ||
617 | .flags = IORESOURCE_IRQ, | ||
618 | }, | ||
619 | }; | ||
620 | |||
621 | static struct resource wm831x_wdt_resources[] = { | ||
622 | { | ||
623 | .start = WM831X_IRQ_WDOG_TO, | ||
624 | .end = WM831X_IRQ_WDOG_TO, | ||
625 | .flags = IORESOURCE_IRQ, | ||
626 | }, | ||
627 | }; | ||
628 | |||
629 | static struct mfd_cell wm8310_devs[] = { | ||
630 | { | ||
631 | .name = "wm831x-buckv", | ||
632 | .id = 1, | ||
633 | .num_resources = ARRAY_SIZE(wm831x_dcdc1_resources), | ||
634 | .resources = wm831x_dcdc1_resources, | ||
635 | }, | ||
636 | { | ||
637 | .name = "wm831x-buckv", | ||
638 | .id = 2, | ||
639 | .num_resources = ARRAY_SIZE(wm831x_dcdc2_resources), | ||
640 | .resources = wm831x_dcdc2_resources, | ||
641 | }, | ||
642 | { | ||
643 | .name = "wm831x-buckp", | ||
644 | .id = 3, | ||
645 | .num_resources = ARRAY_SIZE(wm831x_dcdc3_resources), | ||
646 | .resources = wm831x_dcdc3_resources, | ||
647 | }, | ||
648 | { | ||
649 | .name = "wm831x-boostp", | ||
650 | .id = 4, | ||
651 | .num_resources = ARRAY_SIZE(wm831x_dcdc4_resources), | ||
652 | .resources = wm831x_dcdc4_resources, | ||
653 | }, | ||
654 | { | ||
655 | .name = "wm831x-epe", | ||
656 | .id = 1, | ||
657 | }, | ||
658 | { | ||
659 | .name = "wm831x-epe", | ||
660 | .id = 2, | ||
661 | }, | ||
662 | { | ||
663 | .name = "wm831x-gpio", | ||
664 | .num_resources = ARRAY_SIZE(wm831x_gpio_resources), | ||
665 | .resources = wm831x_gpio_resources, | ||
666 | }, | ||
667 | { | ||
668 | .name = "wm831x-hwmon", | ||
669 | }, | ||
670 | { | ||
671 | .name = "wm831x-isink", | ||
672 | .id = 1, | ||
673 | .num_resources = ARRAY_SIZE(wm831x_isink1_resources), | ||
674 | .resources = wm831x_isink1_resources, | ||
675 | }, | ||
676 | { | ||
677 | .name = "wm831x-isink", | ||
678 | .id = 2, | ||
679 | .num_resources = ARRAY_SIZE(wm831x_isink2_resources), | ||
680 | .resources = wm831x_isink2_resources, | ||
681 | }, | ||
682 | { | ||
683 | .name = "wm831x-ldo", | ||
684 | .id = 1, | ||
685 | .num_resources = ARRAY_SIZE(wm831x_ldo1_resources), | ||
686 | .resources = wm831x_ldo1_resources, | ||
687 | }, | ||
688 | { | ||
689 | .name = "wm831x-ldo", | ||
690 | .id = 2, | ||
691 | .num_resources = ARRAY_SIZE(wm831x_ldo2_resources), | ||
692 | .resources = wm831x_ldo2_resources, | ||
693 | }, | ||
694 | { | ||
695 | .name = "wm831x-ldo", | ||
696 | .id = 3, | ||
697 | .num_resources = ARRAY_SIZE(wm831x_ldo3_resources), | ||
698 | .resources = wm831x_ldo3_resources, | ||
699 | }, | ||
700 | { | ||
701 | .name = "wm831x-ldo", | ||
702 | .id = 4, | ||
703 | .num_resources = ARRAY_SIZE(wm831x_ldo4_resources), | ||
704 | .resources = wm831x_ldo4_resources, | ||
705 | }, | ||
706 | { | ||
707 | .name = "wm831x-ldo", | ||
708 | .id = 5, | ||
709 | .num_resources = ARRAY_SIZE(wm831x_ldo5_resources), | ||
710 | .resources = wm831x_ldo5_resources, | ||
711 | }, | ||
712 | { | ||
713 | .name = "wm831x-ldo", | ||
714 | .id = 6, | ||
715 | .num_resources = ARRAY_SIZE(wm831x_ldo6_resources), | ||
716 | .resources = wm831x_ldo6_resources, | ||
717 | }, | ||
718 | { | ||
719 | .name = "wm831x-aldo", | ||
720 | .id = 7, | ||
721 | .num_resources = ARRAY_SIZE(wm831x_ldo7_resources), | ||
722 | .resources = wm831x_ldo7_resources, | ||
723 | }, | ||
724 | { | ||
725 | .name = "wm831x-aldo", | ||
726 | .id = 8, | ||
727 | .num_resources = ARRAY_SIZE(wm831x_ldo8_resources), | ||
728 | .resources = wm831x_ldo8_resources, | ||
729 | }, | ||
730 | { | ||
731 | .name = "wm831x-aldo", | ||
732 | .id = 9, | ||
733 | .num_resources = ARRAY_SIZE(wm831x_ldo9_resources), | ||
734 | .resources = wm831x_ldo9_resources, | ||
735 | }, | ||
736 | { | ||
737 | .name = "wm831x-aldo", | ||
738 | .id = 10, | ||
739 | .num_resources = ARRAY_SIZE(wm831x_ldo10_resources), | ||
740 | .resources = wm831x_ldo10_resources, | ||
741 | }, | ||
742 | { | ||
743 | .name = "wm831x-alive-ldo", | ||
744 | .id = 11, | ||
745 | .num_resources = ARRAY_SIZE(wm831x_ldo11_resources), | ||
746 | .resources = wm831x_ldo11_resources, | ||
747 | }, | ||
748 | { | ||
749 | .name = "wm831x-on", | ||
750 | .num_resources = ARRAY_SIZE(wm831x_on_resources), | ||
751 | .resources = wm831x_on_resources, | ||
752 | }, | ||
753 | { | ||
754 | .name = "wm831x-power", | ||
755 | .num_resources = ARRAY_SIZE(wm831x_power_resources), | ||
756 | .resources = wm831x_power_resources, | ||
757 | }, | ||
758 | { | ||
759 | .name = "wm831x-rtc", | ||
760 | .num_resources = ARRAY_SIZE(wm831x_rtc_resources), | ||
761 | .resources = wm831x_rtc_resources, | ||
762 | }, | ||
763 | { | ||
764 | .name = "wm831x-status", | ||
765 | .id = 1, | ||
766 | .num_resources = ARRAY_SIZE(wm831x_status1_resources), | ||
767 | .resources = wm831x_status1_resources, | ||
768 | }, | ||
769 | { | ||
770 | .name = "wm831x-status", | ||
771 | .id = 2, | ||
772 | .num_resources = ARRAY_SIZE(wm831x_status2_resources), | ||
773 | .resources = wm831x_status2_resources, | ||
774 | }, | ||
775 | { | ||
776 | .name = "wm831x-watchdog", | ||
777 | .num_resources = ARRAY_SIZE(wm831x_wdt_resources), | ||
778 | .resources = wm831x_wdt_resources, | ||
779 | }, | ||
780 | }; | ||
781 | |||
782 | static struct mfd_cell wm8311_devs[] = { | ||
783 | { | ||
784 | .name = "wm831x-buckv", | ||
785 | .id = 1, | ||
786 | .num_resources = ARRAY_SIZE(wm831x_dcdc1_resources), | ||
787 | .resources = wm831x_dcdc1_resources, | ||
788 | }, | ||
789 | { | ||
790 | .name = "wm831x-buckv", | ||
791 | .id = 2, | ||
792 | .num_resources = ARRAY_SIZE(wm831x_dcdc2_resources), | ||
793 | .resources = wm831x_dcdc2_resources, | ||
794 | }, | ||
795 | { | ||
796 | .name = "wm831x-buckp", | ||
797 | .id = 3, | ||
798 | .num_resources = ARRAY_SIZE(wm831x_dcdc3_resources), | ||
799 | .resources = wm831x_dcdc3_resources, | ||
800 | }, | ||
801 | { | ||
802 | .name = "wm831x-boostp", | ||
803 | .id = 4, | ||
804 | .num_resources = ARRAY_SIZE(wm831x_dcdc4_resources), | ||
805 | .resources = wm831x_dcdc4_resources, | ||
806 | }, | ||
807 | { | ||
808 | .name = "wm831x-epe", | ||
809 | .id = 1, | ||
810 | }, | ||
811 | { | ||
812 | .name = "wm831x-epe", | ||
813 | .id = 2, | ||
814 | }, | ||
815 | { | ||
816 | .name = "wm831x-gpio", | ||
817 | .num_resources = ARRAY_SIZE(wm831x_gpio_resources), | ||
818 | .resources = wm831x_gpio_resources, | ||
819 | }, | ||
820 | { | ||
821 | .name = "wm831x-hwmon", | ||
822 | }, | ||
823 | { | ||
824 | .name = "wm831x-isink", | ||
825 | .id = 1, | ||
826 | .num_resources = ARRAY_SIZE(wm831x_isink1_resources), | ||
827 | .resources = wm831x_isink1_resources, | ||
828 | }, | ||
829 | { | ||
830 | .name = "wm831x-isink", | ||
831 | .id = 2, | ||
832 | .num_resources = ARRAY_SIZE(wm831x_isink2_resources), | ||
833 | .resources = wm831x_isink2_resources, | ||
834 | }, | ||
835 | { | ||
836 | .name = "wm831x-ldo", | ||
837 | .id = 1, | ||
838 | .num_resources = ARRAY_SIZE(wm831x_ldo1_resources), | ||
839 | .resources = wm831x_ldo1_resources, | ||
840 | }, | ||
841 | { | ||
842 | .name = "wm831x-ldo", | ||
843 | .id = 2, | ||
844 | .num_resources = ARRAY_SIZE(wm831x_ldo2_resources), | ||
845 | .resources = wm831x_ldo2_resources, | ||
846 | }, | ||
847 | { | ||
848 | .name = "wm831x-ldo", | ||
849 | .id = 3, | ||
850 | .num_resources = ARRAY_SIZE(wm831x_ldo3_resources), | ||
851 | .resources = wm831x_ldo3_resources, | ||
852 | }, | ||
853 | { | ||
854 | .name = "wm831x-ldo", | ||
855 | .id = 4, | ||
856 | .num_resources = ARRAY_SIZE(wm831x_ldo4_resources), | ||
857 | .resources = wm831x_ldo4_resources, | ||
858 | }, | ||
859 | { | ||
860 | .name = "wm831x-ldo", | ||
861 | .id = 5, | ||
862 | .num_resources = ARRAY_SIZE(wm831x_ldo5_resources), | ||
863 | .resources = wm831x_ldo5_resources, | ||
864 | }, | ||
865 | { | ||
866 | .name = "wm831x-aldo", | ||
867 | .id = 7, | ||
868 | .num_resources = ARRAY_SIZE(wm831x_ldo7_resources), | ||
869 | .resources = wm831x_ldo7_resources, | ||
870 | }, | ||
871 | { | ||
872 | .name = "wm831x-alive-ldo", | ||
873 | .id = 11, | ||
874 | .num_resources = ARRAY_SIZE(wm831x_ldo11_resources), | ||
875 | .resources = wm831x_ldo11_resources, | ||
876 | }, | ||
877 | { | ||
878 | .name = "wm831x-on", | ||
879 | .num_resources = ARRAY_SIZE(wm831x_on_resources), | ||
880 | .resources = wm831x_on_resources, | ||
881 | }, | ||
882 | { | ||
883 | .name = "wm831x-power", | ||
884 | .num_resources = ARRAY_SIZE(wm831x_power_resources), | ||
885 | .resources = wm831x_power_resources, | ||
886 | }, | ||
887 | { | ||
888 | .name = "wm831x-rtc", | ||
889 | .num_resources = ARRAY_SIZE(wm831x_rtc_resources), | ||
890 | .resources = wm831x_rtc_resources, | ||
891 | }, | ||
892 | { | ||
893 | .name = "wm831x-status", | ||
894 | .id = 1, | ||
895 | .num_resources = ARRAY_SIZE(wm831x_status1_resources), | ||
896 | .resources = wm831x_status1_resources, | ||
897 | }, | ||
898 | { | ||
899 | .name = "wm831x-status", | ||
900 | .id = 2, | ||
901 | .num_resources = ARRAY_SIZE(wm831x_status2_resources), | ||
902 | .resources = wm831x_status2_resources, | ||
903 | }, | ||
904 | { | ||
905 | .name = "wm831x-touch", | ||
906 | .num_resources = ARRAY_SIZE(wm831x_touch_resources), | ||
907 | .resources = wm831x_touch_resources, | ||
908 | }, | ||
909 | { | ||
910 | .name = "wm831x-watchdog", | ||
911 | .num_resources = ARRAY_SIZE(wm831x_wdt_resources), | ||
912 | .resources = wm831x_wdt_resources, | ||
913 | }, | ||
914 | }; | ||
915 | |||
916 | static struct mfd_cell wm8312_devs[] = { | ||
917 | { | ||
918 | .name = "wm831x-buckv", | ||
919 | .id = 1, | ||
920 | .num_resources = ARRAY_SIZE(wm831x_dcdc1_resources), | ||
921 | .resources = wm831x_dcdc1_resources, | ||
922 | }, | ||
923 | { | ||
924 | .name = "wm831x-buckv", | ||
925 | .id = 2, | ||
926 | .num_resources = ARRAY_SIZE(wm831x_dcdc2_resources), | ||
927 | .resources = wm831x_dcdc2_resources, | ||
928 | }, | ||
929 | { | ||
930 | .name = "wm831x-buckp", | ||
931 | .id = 3, | ||
932 | .num_resources = ARRAY_SIZE(wm831x_dcdc3_resources), | ||
933 | .resources = wm831x_dcdc3_resources, | ||
934 | }, | ||
935 | { | ||
936 | .name = "wm831x-boostp", | ||
937 | .id = 4, | ||
938 | .num_resources = ARRAY_SIZE(wm831x_dcdc4_resources), | ||
939 | .resources = wm831x_dcdc4_resources, | ||
940 | }, | ||
941 | { | ||
942 | .name = "wm831x-epe", | ||
943 | .id = 1, | ||
944 | }, | ||
945 | { | ||
946 | .name = "wm831x-epe", | ||
947 | .id = 2, | ||
948 | }, | ||
949 | { | ||
950 | .name = "wm831x-gpio", | ||
951 | .num_resources = ARRAY_SIZE(wm831x_gpio_resources), | ||
952 | .resources = wm831x_gpio_resources, | ||
953 | }, | ||
954 | { | ||
955 | .name = "wm831x-hwmon", | ||
956 | }, | ||
957 | { | ||
958 | .name = "wm831x-isink", | ||
959 | .id = 1, | ||
960 | .num_resources = ARRAY_SIZE(wm831x_isink1_resources), | ||
961 | .resources = wm831x_isink1_resources, | ||
962 | }, | ||
963 | { | ||
964 | .name = "wm831x-isink", | ||
965 | .id = 2, | ||
966 | .num_resources = ARRAY_SIZE(wm831x_isink2_resources), | ||
967 | .resources = wm831x_isink2_resources, | ||
968 | }, | ||
969 | { | ||
970 | .name = "wm831x-ldo", | ||
971 | .id = 1, | ||
972 | .num_resources = ARRAY_SIZE(wm831x_ldo1_resources), | ||
973 | .resources = wm831x_ldo1_resources, | ||
974 | }, | ||
975 | { | ||
976 | .name = "wm831x-ldo", | ||
977 | .id = 2, | ||
978 | .num_resources = ARRAY_SIZE(wm831x_ldo2_resources), | ||
979 | .resources = wm831x_ldo2_resources, | ||
980 | }, | ||
981 | { | ||
982 | .name = "wm831x-ldo", | ||
983 | .id = 3, | ||
984 | .num_resources = ARRAY_SIZE(wm831x_ldo3_resources), | ||
985 | .resources = wm831x_ldo3_resources, | ||
986 | }, | ||
987 | { | ||
988 | .name = "wm831x-ldo", | ||
989 | .id = 4, | ||
990 | .num_resources = ARRAY_SIZE(wm831x_ldo4_resources), | ||
991 | .resources = wm831x_ldo4_resources, | ||
992 | }, | ||
993 | { | ||
994 | .name = "wm831x-ldo", | ||
995 | .id = 5, | ||
996 | .num_resources = ARRAY_SIZE(wm831x_ldo5_resources), | ||
997 | .resources = wm831x_ldo5_resources, | ||
998 | }, | ||
999 | { | ||
1000 | .name = "wm831x-ldo", | ||
1001 | .id = 6, | ||
1002 | .num_resources = ARRAY_SIZE(wm831x_ldo6_resources), | ||
1003 | .resources = wm831x_ldo6_resources, | ||
1004 | }, | ||
1005 | { | ||
1006 | .name = "wm831x-aldo", | ||
1007 | .id = 7, | ||
1008 | .num_resources = ARRAY_SIZE(wm831x_ldo7_resources), | ||
1009 | .resources = wm831x_ldo7_resources, | ||
1010 | }, | ||
1011 | { | ||
1012 | .name = "wm831x-aldo", | ||
1013 | .id = 8, | ||
1014 | .num_resources = ARRAY_SIZE(wm831x_ldo8_resources), | ||
1015 | .resources = wm831x_ldo8_resources, | ||
1016 | }, | ||
1017 | { | ||
1018 | .name = "wm831x-aldo", | ||
1019 | .id = 9, | ||
1020 | .num_resources = ARRAY_SIZE(wm831x_ldo9_resources), | ||
1021 | .resources = wm831x_ldo9_resources, | ||
1022 | }, | ||
1023 | { | ||
1024 | .name = "wm831x-aldo", | ||
1025 | .id = 10, | ||
1026 | .num_resources = ARRAY_SIZE(wm831x_ldo10_resources), | ||
1027 | .resources = wm831x_ldo10_resources, | ||
1028 | }, | ||
1029 | { | ||
1030 | .name = "wm831x-alive-ldo", | ||
1031 | .id = 11, | ||
1032 | .num_resources = ARRAY_SIZE(wm831x_ldo11_resources), | ||
1033 | .resources = wm831x_ldo11_resources, | ||
1034 | }, | ||
1035 | { | ||
1036 | .name = "wm831x-on", | ||
1037 | .num_resources = ARRAY_SIZE(wm831x_on_resources), | ||
1038 | .resources = wm831x_on_resources, | ||
1039 | }, | ||
1040 | { | ||
1041 | .name = "wm831x-power", | ||
1042 | .num_resources = ARRAY_SIZE(wm831x_power_resources), | ||
1043 | .resources = wm831x_power_resources, | ||
1044 | }, | ||
1045 | { | ||
1046 | .name = "wm831x-rtc", | ||
1047 | .num_resources = ARRAY_SIZE(wm831x_rtc_resources), | ||
1048 | .resources = wm831x_rtc_resources, | ||
1049 | }, | ||
1050 | { | ||
1051 | .name = "wm831x-status", | ||
1052 | .id = 1, | ||
1053 | .num_resources = ARRAY_SIZE(wm831x_status1_resources), | ||
1054 | .resources = wm831x_status1_resources, | ||
1055 | }, | ||
1056 | { | ||
1057 | .name = "wm831x-status", | ||
1058 | .id = 2, | ||
1059 | .num_resources = ARRAY_SIZE(wm831x_status2_resources), | ||
1060 | .resources = wm831x_status2_resources, | ||
1061 | }, | ||
1062 | { | ||
1063 | .name = "wm831x-touch", | ||
1064 | .num_resources = ARRAY_SIZE(wm831x_touch_resources), | ||
1065 | .resources = wm831x_touch_resources, | ||
1066 | }, | ||
1067 | { | ||
1068 | .name = "wm831x-watchdog", | ||
1069 | .num_resources = ARRAY_SIZE(wm831x_wdt_resources), | ||
1070 | .resources = wm831x_wdt_resources, | ||
1071 | }, | ||
1072 | }; | ||
1073 | |||
1074 | /* | ||
1075 | * Instantiate the generic non-control parts of the device. | ||
1076 | */ | ||
1077 | static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) | ||
1078 | { | ||
1079 | struct wm831x_pdata *pdata = wm831x->dev->platform_data; | ||
1080 | int rev; | ||
1081 | enum wm831x_parent parent; | ||
1082 | int ret; | ||
1083 | |||
1084 | mutex_init(&wm831x->io_lock); | ||
1085 | mutex_init(&wm831x->key_lock); | ||
1086 | dev_set_drvdata(wm831x->dev, wm831x); | ||
1087 | |||
1088 | ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID); | ||
1089 | if (ret < 0) { | ||
1090 | dev_err(wm831x->dev, "Failed to read parent ID: %d\n", ret); | ||
1091 | goto err; | ||
1092 | } | ||
1093 | if (ret != 0x6204) { | ||
1094 | dev_err(wm831x->dev, "Device is not a WM831x: ID %x\n", ret); | ||
1095 | ret = -EINVAL; | ||
1096 | goto err; | ||
1097 | } | ||
1098 | |||
1099 | ret = wm831x_reg_read(wm831x, WM831X_REVISION); | ||
1100 | if (ret < 0) { | ||
1101 | dev_err(wm831x->dev, "Failed to read revision: %d\n", ret); | ||
1102 | goto err; | ||
1103 | } | ||
1104 | rev = (ret & WM831X_PARENT_REV_MASK) >> WM831X_PARENT_REV_SHIFT; | ||
1105 | |||
1106 | ret = wm831x_reg_read(wm831x, WM831X_RESET_ID); | ||
1107 | if (ret < 0) { | ||
1108 | dev_err(wm831x->dev, "Failed to read device ID: %d\n", ret); | ||
1109 | goto err; | ||
1110 | } | ||
1111 | |||
1112 | switch (ret) { | ||
1113 | case 0x8310: | ||
1114 | parent = WM8310; | ||
1115 | switch (rev) { | ||
1116 | case 0: | ||
1117 | dev_info(wm831x->dev, "WM8310 revision %c\n", | ||
1118 | 'A' + rev); | ||
1119 | break; | ||
1120 | } | ||
1121 | break; | ||
1122 | |||
1123 | case 0x8311: | ||
1124 | parent = WM8311; | ||
1125 | switch (rev) { | ||
1126 | case 0: | ||
1127 | dev_info(wm831x->dev, "WM8311 revision %c\n", | ||
1128 | 'A' + rev); | ||
1129 | break; | ||
1130 | } | ||
1131 | break; | ||
1132 | |||
1133 | case 0x8312: | ||
1134 | parent = WM8312; | ||
1135 | switch (rev) { | ||
1136 | case 0: | ||
1137 | dev_info(wm831x->dev, "WM8312 revision %c\n", | ||
1138 | 'A' + rev); | ||
1139 | break; | ||
1140 | } | ||
1141 | break; | ||
1142 | |||
1143 | case 0: | ||
1144 | /* Some engineering samples do not have the ID set, | ||
1145 | * rely on the device being registered correctly. | ||
1146 | * This will need revisiting for future devices with | ||
1147 | * multiple dies. | ||
1148 | */ | ||
1149 | parent = id; | ||
1150 | switch (rev) { | ||
1151 | case 0: | ||
1152 | dev_info(wm831x->dev, "WM831%d ES revision %c\n", | ||
1153 | parent, 'A' + rev); | ||
1154 | break; | ||
1155 | } | ||
1156 | break; | ||
1157 | |||
1158 | default: | ||
1159 | dev_err(wm831x->dev, "Unknown WM831x device %04x\n", ret); | ||
1160 | ret = -EINVAL; | ||
1161 | goto err; | ||
1162 | } | ||
1163 | |||
1164 | /* This will need revisiting in future but is OK for all | ||
1165 | * current parts. | ||
1166 | */ | ||
1167 | if (parent != id) | ||
1168 | dev_warn(wm831x->dev, "Device was registered as a WM831%lu\n", | ||
1169 | id); | ||
1170 | |||
1171 | /* Bootstrap the user key */ | ||
1172 | ret = wm831x_reg_read(wm831x, WM831X_SECURITY_KEY); | ||
1173 | if (ret < 0) { | ||
1174 | dev_err(wm831x->dev, "Failed to read security key: %d\n", ret); | ||
1175 | goto err; | ||
1176 | } | ||
1177 | if (ret != 0) { | ||
1178 | dev_warn(wm831x->dev, "Security key had non-zero value %x\n", | ||
1179 | ret); | ||
1180 | wm831x_reg_write(wm831x, WM831X_SECURITY_KEY, 0); | ||
1181 | } | ||
1182 | wm831x->locked = 1; | ||
1183 | |||
1184 | if (pdata && pdata->pre_init) { | ||
1185 | ret = pdata->pre_init(wm831x); | ||
1186 | if (ret != 0) { | ||
1187 | dev_err(wm831x->dev, "pre_init() failed: %d\n", ret); | ||
1188 | goto err; | ||
1189 | } | ||
1190 | } | ||
1191 | |||
1192 | /* The core device is up, instantiate the subdevices. */ | ||
1193 | switch (parent) { | ||
1194 | case WM8310: | ||
1195 | ret = mfd_add_devices(wm831x->dev, -1, | ||
1196 | wm8310_devs, ARRAY_SIZE(wm8310_devs), | ||
1197 | NULL, 0); | ||
1198 | break; | ||
1199 | |||
1200 | case WM8311: | ||
1201 | ret = mfd_add_devices(wm831x->dev, -1, | ||
1202 | wm8311_devs, ARRAY_SIZE(wm8311_devs), | ||
1203 | NULL, 0); | ||
1204 | break; | ||
1205 | |||
1206 | case WM8312: | ||
1207 | ret = mfd_add_devices(wm831x->dev, -1, | ||
1208 | wm8312_devs, ARRAY_SIZE(wm8312_devs), | ||
1209 | NULL, 0); | ||
1210 | break; | ||
1211 | |||
1212 | default: | ||
1213 | /* If this happens the bus probe function is buggy */ | ||
1214 | BUG(); | ||
1215 | } | ||
1216 | |||
1217 | if (ret != 0) { | ||
1218 | dev_err(wm831x->dev, "Failed to add children\n"); | ||
1219 | goto err; | ||
1220 | } | ||
1221 | |||
1222 | if (pdata && pdata->post_init) { | ||
1223 | ret = pdata->post_init(wm831x); | ||
1224 | if (ret != 0) { | ||
1225 | dev_err(wm831x->dev, "post_init() failed: %d\n", ret); | ||
1226 | goto err; | ||
1227 | } | ||
1228 | } | ||
1229 | |||
1230 | return 0; | ||
1231 | |||
1232 | err: | ||
1233 | mfd_remove_devices(wm831x->dev); | ||
1234 | kfree(wm831x); | ||
1235 | return ret; | ||
1236 | } | ||
1237 | |||
1238 | static void wm831x_device_exit(struct wm831x *wm831x) | ||
1239 | { | ||
1240 | mfd_remove_devices(wm831x->dev); | ||
1241 | kfree(wm831x); | ||
1242 | } | ||
1243 | |||
1244 | static int wm831x_i2c_read_device(struct wm831x *wm831x, unsigned short reg, | ||
1245 | int bytes, void *dest) | ||
1246 | { | ||
1247 | struct i2c_client *i2c = wm831x->control_data; | ||
1248 | int ret; | ||
1249 | u16 r = cpu_to_be16(reg); | ||
1250 | |||
1251 | ret = i2c_master_send(i2c, (unsigned char *)&r, 2); | ||
1252 | if (ret < 0) | ||
1253 | return ret; | ||
1254 | if (ret != 2) | ||
1255 | return -EIO; | ||
1256 | |||
1257 | ret = i2c_master_recv(i2c, dest, bytes); | ||
1258 | if (ret < 0) | ||
1259 | return ret; | ||
1260 | if (ret != bytes) | ||
1261 | return -EIO; | ||
1262 | return 0; | ||
1263 | } | ||
1264 | |||
1265 | /* Currently we allocate the write buffer on the stack; this is OK for | ||
1266 | * small writes - if we need to do large writes this will need to be | ||
1267 | * revised. | ||
1268 | */ | ||
1269 | static int wm831x_i2c_write_device(struct wm831x *wm831x, unsigned short reg, | ||
1270 | int bytes, void *src) | ||
1271 | { | ||
1272 | struct i2c_client *i2c = wm831x->control_data; | ||
1273 | unsigned char msg[bytes + 2]; | ||
1274 | int ret; | ||
1275 | |||
1276 | reg = cpu_to_be16(reg); | ||
1277 | memcpy(&msg[0], ®, 2); | ||
1278 | memcpy(&msg[2], src, bytes); | ||
1279 | |||
1280 | ret = i2c_master_send(i2c, msg, bytes + 2); | ||
1281 | if (ret < 0) | ||
1282 | return ret; | ||
1283 | if (ret < bytes + 2) | ||
1284 | return -EIO; | ||
1285 | |||
1286 | return 0; | ||
1287 | } | ||
1288 | |||
1289 | static int wm831x_i2c_probe(struct i2c_client *i2c, | ||
1290 | const struct i2c_device_id *id) | ||
1291 | { | ||
1292 | struct wm831x *wm831x; | ||
1293 | |||
1294 | wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL); | ||
1295 | if (wm831x == NULL) { | ||
1296 | kfree(i2c); | ||
1297 | return -ENOMEM; | ||
1298 | } | ||
1299 | |||
1300 | i2c_set_clientdata(i2c, wm831x); | ||
1301 | wm831x->dev = &i2c->dev; | ||
1302 | wm831x->control_data = i2c; | ||
1303 | wm831x->read_dev = wm831x_i2c_read_device; | ||
1304 | wm831x->write_dev = wm831x_i2c_write_device; | ||
1305 | |||
1306 | return wm831x_device_init(wm831x, id->driver_data, i2c->irq); | ||
1307 | } | ||
1308 | |||
1309 | static int wm831x_i2c_remove(struct i2c_client *i2c) | ||
1310 | { | ||
1311 | struct wm831x *wm831x = i2c_get_clientdata(i2c); | ||
1312 | |||
1313 | wm831x_device_exit(wm831x); | ||
1314 | |||
1315 | return 0; | ||
1316 | } | ||
1317 | |||
1318 | static const struct i2c_device_id wm831x_i2c_id[] = { | ||
1319 | { "wm8310", WM8310 }, | ||
1320 | { "wm8311", WM8311 }, | ||
1321 | { "wm8312", WM8312 }, | ||
1322 | { } | ||
1323 | }; | ||
1324 | MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id); | ||
1325 | |||
1326 | |||
1327 | static struct i2c_driver wm831x_i2c_driver = { | ||
1328 | .driver = { | ||
1329 | .name = "wm831x", | ||
1330 | .owner = THIS_MODULE, | ||
1331 | }, | ||
1332 | .probe = wm831x_i2c_probe, | ||
1333 | .remove = wm831x_i2c_remove, | ||
1334 | .id_table = wm831x_i2c_id, | ||
1335 | }; | ||
1336 | |||
1337 | static int __init wm831x_i2c_init(void) | ||
1338 | { | ||
1339 | int ret; | ||
1340 | |||
1341 | ret = i2c_add_driver(&wm831x_i2c_driver); | ||
1342 | if (ret != 0) | ||
1343 | pr_err("Failed to register wm831x I2C driver: %d\n", ret); | ||
1344 | |||
1345 | return ret; | ||
1346 | } | ||
1347 | subsys_initcall(wm831x_i2c_init); | ||
1348 | |||
1349 | static void __exit wm831x_i2c_exit(void) | ||
1350 | { | ||
1351 | i2c_del_driver(&wm831x_i2c_driver); | ||
1352 | } | ||
1353 | module_exit(wm831x_i2c_exit); | ||
1354 | |||
1355 | MODULE_DESCRIPTION("I2C support for the WM831X AudioPlus PMIC"); | ||
1356 | MODULE_LICENSE("GPL"); | ||
1357 | MODULE_AUTHOR("Mark Brown"); | ||