aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2010-01-29 13:20:29 -0500
committerSamuel Ortiz <sameo@linux.intel.com>2010-03-07 16:17:13 -0500
commit9e50108668a70a9927257298bd4e679300124420 (patch)
tree659100bfbfaf64a3b385573a4d4fb7fb50468eb7 /drivers/mfd
parent1c4d3b70a40c666331052adf77933e6994590b74 (diff)
mfd: Add initial WM8994 support
The WM8994 is a highly integrated ultra low power audio hub CODEC. Since it includes on-board regulators and GPIOs it is represented as a multi-function device, though the overwhelming majority of the functionality is provided by the ASoC CODEC driver. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/Kconfig12
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/wm8994-core.c537
3 files changed, 550 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index a760dbefd27a..64fbe3334eb3 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -295,6 +295,18 @@ config MFD_WM8350_I2C
295 I2C as the control interface. Additional options must be 295 I2C as the control interface. Additional options must be
296 selected to enable support for the functionality of the chip. 296 selected to enable support for the functionality of the chip.
297 297
298config MFD_WM8994
299 tristate "Support Wolfson Microelectronics WM8994"
300 select MFD_CORE
301 depends on I2C
302 help
303 The WM8994 is a highly integrated hi-fi CODEC designed for
304 smartphone applicatiosn. As well as audio functionality it
305 has on board GPIO and regulator functionality which is
306 supported via the relevant subsystems. This driver provides
307 core support for the WM8994, in order to use the actual
308 functionaltiy of the device other drivers must be enabled.
309
298config MFD_PCF50633 310config MFD_PCF50633
299 tristate "Support for NXP PCF50633" 311 tristate "Support for NXP PCF50633"
300 depends on I2C 312 depends on I2C
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 142d31202b14..878cac691067 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -25,6 +25,7 @@ wm8350-objs := wm8350-core.o wm8350-regmap.o wm8350-gpio.o
25wm8350-objs += wm8350-irq.o 25wm8350-objs += wm8350-irq.o
26obj-$(CONFIG_MFD_WM8350) += wm8350.o 26obj-$(CONFIG_MFD_WM8350) += wm8350.o
27obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o 27obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o
28obj-$(CONFIG_MFD_WM8994) += wm8994-core.o
28 29
29obj-$(CONFIG_TPS65010) += tps65010.o 30obj-$(CONFIG_TPS65010) += tps65010.o
30obj-$(CONFIG_MENELAUS) += menelaus.o 31obj-$(CONFIG_MENELAUS) += menelaus.o
diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c
new file mode 100644
index 000000000000..299c1af1334e
--- /dev/null
+++ b/drivers/mfd/wm8994-core.c
@@ -0,0 +1,537 @@
1/*
2 * wm8994-core.c -- Device access for Wolfson WM8994
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/delay.h>
19#include <linux/mfd/core.h>
20#include <linux/regulator/consumer.h>
21#include <linux/regulator/machine.h>
22
23#include <linux/mfd/wm8994/core.h>
24#include <linux/mfd/wm8994/pdata.h>
25#include <linux/mfd/wm8994/registers.h>
26
27static int wm8994_read(struct wm8994 *wm8994, unsigned short reg,
28 int bytes, void *dest)
29{
30 int ret, i;
31 u16 *buf = dest;
32
33 BUG_ON(bytes % 2);
34 BUG_ON(bytes <= 0);
35
36 ret = wm8994->read_dev(wm8994, reg, bytes, dest);
37 if (ret < 0)
38 return ret;
39
40 for (i = 0; i < bytes / 2; i++) {
41 buf[i] = be16_to_cpu(buf[i]);
42
43 dev_vdbg(wm8994->dev, "Read %04x from R%d(0x%x)\n",
44 buf[i], reg + i, reg + i);
45 }
46
47 return 0;
48}
49
50/**
51 * wm8994_reg_read: Read a single WM8994 register.
52 *
53 * @wm8994: Device to read from.
54 * @reg: Register to read.
55 */
56int wm8994_reg_read(struct wm8994 *wm8994, unsigned short reg)
57{
58 unsigned short val;
59 int ret;
60
61 mutex_lock(&wm8994->io_lock);
62
63 ret = wm8994_read(wm8994, reg, 2, &val);
64
65 mutex_unlock(&wm8994->io_lock);
66
67 if (ret < 0)
68 return ret;
69 else
70 return val;
71}
72EXPORT_SYMBOL_GPL(wm8994_reg_read);
73
74/**
75 * wm8994_bulk_read: Read multiple WM8994 registers
76 *
77 * @wm8994: Device to read from
78 * @reg: First register
79 * @count: Number of registers
80 * @buf: Buffer to fill.
81 */
82int wm8994_bulk_read(struct wm8994 *wm8994, unsigned short reg,
83 int count, u16 *buf)
84{
85 int ret;
86
87 mutex_lock(&wm8994->io_lock);
88
89 ret = wm8994_read(wm8994, reg, count * 2, buf);
90
91 mutex_unlock(&wm8994->io_lock);
92
93 return ret;
94}
95EXPORT_SYMBOL_GPL(wm8994_bulk_read);
96
97static int wm8994_write(struct wm8994 *wm8994, unsigned short reg,
98 int bytes, void *src)
99{
100 u16 *buf = src;
101 int i;
102
103 BUG_ON(bytes % 2);
104 BUG_ON(bytes <= 0);
105
106 for (i = 0; i < bytes / 2; i++) {
107 dev_vdbg(wm8994->dev, "Write %04x to R%d(0x%x)\n",
108 buf[i], reg + i, reg + i);
109
110 buf[i] = cpu_to_be16(buf[i]);
111 }
112
113 return wm8994->write_dev(wm8994, reg, bytes, src);
114}
115
116/**
117 * wm8994_reg_write: Write a single WM8994 register.
118 *
119 * @wm8994: Device to write to.
120 * @reg: Register to write to.
121 * @val: Value to write.
122 */
123int wm8994_reg_write(struct wm8994 *wm8994, unsigned short reg,
124 unsigned short val)
125{
126 int ret;
127
128 mutex_lock(&wm8994->io_lock);
129
130 ret = wm8994_write(wm8994, reg, 2, &val);
131
132 mutex_unlock(&wm8994->io_lock);
133
134 return ret;
135}
136EXPORT_SYMBOL_GPL(wm8994_reg_write);
137
138/**
139 * wm8994_set_bits: Set the value of a bitfield in a WM8994 register
140 *
141 * @wm8994: Device to write to.
142 * @reg: Register to write to.
143 * @mask: Mask of bits to set.
144 * @val: Value to set (unshifted)
145 */
146int wm8994_set_bits(struct wm8994 *wm8994, unsigned short reg,
147 unsigned short mask, unsigned short val)
148{
149 int ret;
150 u16 r;
151
152 mutex_lock(&wm8994->io_lock);
153
154 ret = wm8994_read(wm8994, reg, 2, &r);
155 if (ret < 0)
156 goto out;
157
158 r &= ~mask;
159 r |= val;
160
161 ret = wm8994_write(wm8994, reg, 2, &r);
162
163out:
164 mutex_unlock(&wm8994->io_lock);
165
166 return ret;
167}
168EXPORT_SYMBOL_GPL(wm8994_set_bits);
169
170static struct mfd_cell wm8994_regulator_devs[] = {
171 { .name = "wm8994-ldo", .id = 1 },
172 { .name = "wm8994-ldo", .id = 2 },
173};
174
175static struct mfd_cell wm8994_devs[] = {
176 { .name = "wm8994-codec" },
177 { .name = "wm8994-gpio" },
178};
179
180/*
181 * Supplies for the main bulk of CODEC; the LDO supplies are ignored
182 * and should be handled via the standard regulator API supply
183 * management.
184 */
185static const char *wm8994_main_supplies[] = {
186 "DBVDD",
187 "DCVDD",
188 "AVDD1",
189 "AVDD2",
190 "CPVDD",
191 "SPKVDD1",
192 "SPKVDD2",
193};
194
195#ifdef CONFIG_PM
196static int wm8994_device_suspend(struct device *dev)
197{
198 struct wm8994 *wm8994 = dev_get_drvdata(dev);
199 int ret;
200
201 /* GPIO configuration state is saved here since we may be configuring
202 * the GPIO alternate functions even if we're not using the gpiolib
203 * driver for them.
204 */
205 ret = wm8994_read(wm8994, WM8994_GPIO_1, WM8994_NUM_GPIO_REGS * 2,
206 &wm8994->gpio_regs);
207 if (ret < 0)
208 dev_err(dev, "Failed to save GPIO registers: %d\n", ret);
209
210 /* For similar reasons we also stash the regulator states */
211 ret = wm8994_read(wm8994, WM8994_LDO_1, WM8994_NUM_LDO_REGS * 2,
212 &wm8994->ldo_regs);
213 if (ret < 0)
214 dev_err(dev, "Failed to save LDO registers: %d\n", ret);
215
216 ret = regulator_bulk_disable(ARRAY_SIZE(wm8994_main_supplies),
217 wm8994->supplies);
218 if (ret != 0) {
219 dev_err(dev, "Failed to disable supplies: %d\n", ret);
220 return ret;
221 }
222
223 return 0;
224}
225
226static int wm8994_device_resume(struct device *dev)
227{
228 struct wm8994 *wm8994 = dev_get_drvdata(dev);
229 int ret;
230
231 ret = regulator_bulk_enable(ARRAY_SIZE(wm8994_main_supplies),
232 wm8994->supplies);
233 if (ret != 0) {
234 dev_err(dev, "Failed to enable supplies: %d\n", ret);
235 return ret;
236 }
237
238 ret = wm8994_write(wm8994, WM8994_LDO_1, WM8994_NUM_LDO_REGS * 2,
239 &wm8994->ldo_regs);
240 if (ret < 0)
241 dev_err(dev, "Failed to restore LDO registers: %d\n", ret);
242
243 ret = wm8994_write(wm8994, WM8994_GPIO_1, WM8994_NUM_GPIO_REGS * 2,
244 &wm8994->gpio_regs);
245 if (ret < 0)
246 dev_err(dev, "Failed to restore GPIO registers: %d\n", ret);
247
248 return 0;
249}
250#endif
251
252#ifdef CONFIG_REGULATOR
253static int wm8994_ldo_in_use(struct wm8994_pdata *pdata, int ldo)
254{
255 struct wm8994_ldo_pdata *ldo_pdata;
256
257 if (!pdata)
258 return 0;
259
260 ldo_pdata = &pdata->ldo[ldo];
261
262 if (!ldo_pdata->init_data)
263 return 0;
264
265 return ldo_pdata->init_data->num_consumer_supplies != 0;
266}
267#else
268static int wm8994_ldo_in_use(struct wm8994_pdata *pdata, int ldo)
269{
270 return 0;
271}
272#endif
273
274/*
275 * Instantiate the generic non-control parts of the device.
276 */
277static int wm8994_device_init(struct wm8994 *wm8994, unsigned long id, int irq)
278{
279 struct wm8994_pdata *pdata = wm8994->dev->platform_data;
280 int ret, i;
281
282 mutex_init(&wm8994->io_lock);
283 dev_set_drvdata(wm8994->dev, wm8994);
284
285 /* Add the on-chip regulators first for bootstrapping */
286 ret = mfd_add_devices(wm8994->dev, -1,
287 wm8994_regulator_devs,
288 ARRAY_SIZE(wm8994_regulator_devs),
289 NULL, 0);
290 if (ret != 0) {
291 dev_err(wm8994->dev, "Failed to add children: %d\n", ret);
292 goto err;
293 }
294
295 wm8994->supplies = kzalloc(sizeof(struct regulator_bulk_data) *
296 ARRAY_SIZE(wm8994_main_supplies),
297 GFP_KERNEL);
298 if (!wm8994->supplies)
299 goto err;
300
301 for (i = 0; i < ARRAY_SIZE(wm8994_main_supplies); i++)
302 wm8994->supplies[i].supply = wm8994_main_supplies[i];
303
304 ret = regulator_bulk_get(wm8994->dev, ARRAY_SIZE(wm8994_main_supplies),
305 wm8994->supplies);
306 if (ret != 0) {
307 dev_err(wm8994->dev, "Failed to get supplies: %d\n", ret);
308 goto err_get;
309 }
310
311 ret = regulator_bulk_enable(ARRAY_SIZE(wm8994_main_supplies),
312 wm8994->supplies);
313 if (ret != 0) {
314 dev_err(wm8994->dev, "Failed to enable supplies: %d\n", ret);
315 goto err_supplies;
316 }
317
318 ret = wm8994_reg_read(wm8994, WM8994_SOFTWARE_RESET);
319 if (ret < 0) {
320 dev_err(wm8994->dev, "Failed to read ID register\n");
321 goto err_enable;
322 }
323 if (ret != 0x8994) {
324 dev_err(wm8994->dev, "Device is not a WM8994, ID is %x\n",
325 ret);
326 ret = -EINVAL;
327 goto err_enable;
328 }
329
330 ret = wm8994_reg_read(wm8994, WM8994_CHIP_REVISION);
331 if (ret < 0) {
332 dev_err(wm8994->dev, "Failed to read revision register: %d\n",
333 ret);
334 goto err_enable;
335 }
336
337 switch (ret) {
338 case 0:
339 case 1:
340 dev_warn(wm8994->dev, "revision %c not fully supported\n",
341 'A' + ret);
342 break;
343 default:
344 dev_info(wm8994->dev, "revision %c\n", 'A' + ret);
345 break;
346 }
347
348
349 if (pdata) {
350 wm8994->gpio_base = pdata->gpio_base;
351
352 /* GPIO configuration is only applied if it's non-zero */
353 for (i = 0; i < ARRAY_SIZE(pdata->gpio_defaults); i++) {
354 if (pdata->gpio_defaults[i]) {
355 wm8994_set_bits(wm8994, WM8994_GPIO_1 + i,
356 0xffff,
357 pdata->gpio_defaults[i]);
358 }
359 }
360 }
361
362 /* In some system designs where the regulators are not in use,
363 * we can achieve a small reduction in leakage currents by
364 * floating LDO outputs. This bit makes no difference if the
365 * LDOs are enabled, it only affects cases where the LDOs were
366 * in operation and are then disabled.
367 */
368 for (i = 0; i < WM8994_NUM_LDO_REGS; i++) {
369 if (wm8994_ldo_in_use(pdata, i))
370 wm8994_set_bits(wm8994, WM8994_LDO_1 + i,
371 WM8994_LDO1_DISCH, WM8994_LDO1_DISCH);
372 else
373 wm8994_set_bits(wm8994, WM8994_LDO_1 + i,
374 WM8994_LDO1_DISCH, 0);
375 }
376
377 ret = mfd_add_devices(wm8994->dev, -1,
378 wm8994_devs, ARRAY_SIZE(wm8994_devs),
379 NULL, 0);
380 if (ret != 0) {
381 dev_err(wm8994->dev, "Failed to add children: %d\n", ret);
382 goto err_enable;
383 }
384
385 return 0;
386
387err_enable:
388 regulator_bulk_disable(ARRAY_SIZE(wm8994_main_supplies),
389 wm8994->supplies);
390err_get:
391 regulator_bulk_free(ARRAY_SIZE(wm8994_main_supplies), wm8994->supplies);
392err_supplies:
393 kfree(wm8994->supplies);
394err:
395 mfd_remove_devices(wm8994->dev);
396 kfree(wm8994);
397 return ret;
398}
399
400static void wm8994_device_exit(struct wm8994 *wm8994)
401{
402 mfd_remove_devices(wm8994->dev);
403 regulator_bulk_disable(ARRAY_SIZE(wm8994_main_supplies),
404 wm8994->supplies);
405 regulator_bulk_free(ARRAY_SIZE(wm8994_main_supplies), wm8994->supplies);
406 kfree(wm8994->supplies);
407 kfree(wm8994);
408}
409
410static int wm8994_i2c_read_device(struct wm8994 *wm8994, unsigned short reg,
411 int bytes, void *dest)
412{
413 struct i2c_client *i2c = wm8994->control_data;
414 int ret;
415 u16 r = cpu_to_be16(reg);
416
417 ret = i2c_master_send(i2c, (unsigned char *)&r, 2);
418 if (ret < 0)
419 return ret;
420 if (ret != 2)
421 return -EIO;
422
423 ret = i2c_master_recv(i2c, dest, bytes);
424 if (ret < 0)
425 return ret;
426 if (ret != bytes)
427 return -EIO;
428 return 0;
429}
430
431/* Currently we allocate the write buffer on the stack; this is OK for
432 * small writes - if we need to do large writes this will need to be
433 * revised.
434 */
435static int wm8994_i2c_write_device(struct wm8994 *wm8994, unsigned short reg,
436 int bytes, void *src)
437{
438 struct i2c_client *i2c = wm8994->control_data;
439 unsigned char msg[bytes + 2];
440 int ret;
441
442 reg = cpu_to_be16(reg);
443 memcpy(&msg[0], &reg, 2);
444 memcpy(&msg[2], src, bytes);
445
446 ret = i2c_master_send(i2c, msg, bytes + 2);
447 if (ret < 0)
448 return ret;
449 if (ret < bytes + 2)
450 return -EIO;
451
452 return 0;
453}
454
455static int wm8994_i2c_probe(struct i2c_client *i2c,
456 const struct i2c_device_id *id)
457{
458 struct wm8994 *wm8994;
459
460 wm8994 = kzalloc(sizeof(struct wm8994), GFP_KERNEL);
461 if (wm8994 == NULL) {
462 kfree(i2c);
463 return -ENOMEM;
464 }
465
466 i2c_set_clientdata(i2c, wm8994);
467 wm8994->dev = &i2c->dev;
468 wm8994->control_data = i2c;
469 wm8994->read_dev = wm8994_i2c_read_device;
470 wm8994->write_dev = wm8994_i2c_write_device;
471
472 return wm8994_device_init(wm8994, id->driver_data, i2c->irq);
473}
474
475static int wm8994_i2c_remove(struct i2c_client *i2c)
476{
477 struct wm8994 *wm8994 = i2c_get_clientdata(i2c);
478
479 wm8994_device_exit(wm8994);
480
481 return 0;
482}
483
484#ifdef CONFIG_PM
485static int wm8994_i2c_suspend(struct i2c_client *i2c, pm_message_t state)
486{
487 return wm8994_device_suspend(&i2c->dev);
488}
489
490static int wm8994_i2c_resume(struct i2c_client *i2c)
491{
492 return wm8994_device_resume(&i2c->dev);
493}
494#else
495#define wm8994_i2c_suspend NULL
496#define wm8994_i2c_resume NULL
497#endif
498
499static const struct i2c_device_id wm8994_i2c_id[] = {
500 { "wm8994", 0 },
501 { }
502};
503MODULE_DEVICE_TABLE(i2c, wm8994_i2c_id);
504
505static struct i2c_driver wm8994_i2c_driver = {
506 .driver = {
507 .name = "wm8994",
508 .owner = THIS_MODULE,
509 },
510 .probe = wm8994_i2c_probe,
511 .remove = wm8994_i2c_remove,
512 .suspend = wm8994_i2c_suspend,
513 .resume = wm8994_i2c_resume,
514 .id_table = wm8994_i2c_id,
515};
516
517static int __init wm8994_i2c_init(void)
518{
519 int ret;
520
521 ret = i2c_add_driver(&wm8994_i2c_driver);
522 if (ret != 0)
523 pr_err("Failed to register wm8994 I2C driver: %d\n", ret);
524
525 return ret;
526}
527module_init(wm8994_i2c_init);
528
529static void __exit wm8994_i2c_exit(void)
530{
531 i2c_del_driver(&wm8994_i2c_driver);
532}
533module_exit(wm8994_i2c_exit);
534
535MODULE_DESCRIPTION("Core support for the WM8994 audio CODEC");
536MODULE_LICENSE("GPL");
537MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");