aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd/arizona-core.c
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2012-06-19 11:31:53 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2012-06-23 08:30:40 -0400
commit3cc72986947501a6a8fd12330e0963b59ed2f964 (patch)
tree1bad5a4aa1521bde7f232784e5f6ace70ec177b1 /drivers/mfd/arizona-core.c
parentc46a019a7941ff92291cda1cc2774bf720552ad9 (diff)
mfd: arizona: Core driver
Several forthcoming Wolfson devices are based on a common platform known as Arizona allowing a great deal of reuse of driver code. This patch adds core support for these devices. In order to handle systems which do not use the generic clock API a simple wrapper for the 32kHz clock domain in the devices is provided. Once the generic clock API is widely available this code will be moved over to use that. For simplicity some WM5102 specific code is included in the core driver, the effort involved in splitting the device out isn't worth it. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'drivers/mfd/arizona-core.c')
-rw-r--r--drivers/mfd/arizona-core.c527
1 files changed, 527 insertions, 0 deletions
diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c
new file mode 100644
index 000000000000..42cb28b2b5c8
--- /dev/null
+++ b/drivers/mfd/arizona-core.c
@@ -0,0 +1,527 @@
1/*
2 * Arizona core driver
3 *
4 * Copyright 2012 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
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/delay.h>
14#include <linux/gpio.h>
15#include <linux/interrupt.h>
16#include <linux/mfd/core.h>
17#include <linux/module.h>
18#include <linux/pm_runtime.h>
19#include <linux/regmap.h>
20#include <linux/regulator/consumer.h>
21#include <linux/slab.h>
22
23#include <linux/mfd/arizona/core.h>
24#include <linux/mfd/arizona/registers.h>
25
26#include "arizona.h"
27
28static const char *wm5102_core_supplies[] = {
29 "AVDD",
30 "DBVDD1",
31 "DCVDD",
32};
33
34int arizona_clk32k_enable(struct arizona *arizona)
35{
36 int ret = 0;
37
38 mutex_lock(&arizona->clk_lock);
39
40 arizona->clk32k_ref++;
41
42 if (arizona->clk32k_ref == 1)
43 ret = regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
44 ARIZONA_CLK_32K_ENA,
45 ARIZONA_CLK_32K_ENA);
46
47 if (ret != 0)
48 arizona->clk32k_ref--;
49
50 mutex_unlock(&arizona->clk_lock);
51
52 return ret;
53}
54EXPORT_SYMBOL_GPL(arizona_clk32k_enable);
55
56int arizona_clk32k_disable(struct arizona *arizona)
57{
58 int ret = 0;
59
60 mutex_lock(&arizona->clk_lock);
61
62 BUG_ON(arizona->clk32k_ref <= 0);
63
64 arizona->clk32k_ref--;
65
66 if (arizona->clk32k_ref == 0)
67 regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
68 ARIZONA_CLK_32K_ENA, 0);
69
70 mutex_unlock(&arizona->clk_lock);
71
72 return ret;
73}
74EXPORT_SYMBOL_GPL(arizona_clk32k_disable);
75
76static irqreturn_t arizona_clkgen_err(int irq, void *data)
77{
78 struct arizona *arizona = data;
79
80 dev_err(arizona->dev, "CLKGEN error\n");
81
82 return IRQ_HANDLED;
83}
84
85static irqreturn_t arizona_underclocked(int irq, void *data)
86{
87 struct arizona *arizona = data;
88 unsigned int val;
89 int ret;
90
91 ret = regmap_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_8,
92 &val);
93 if (ret != 0) {
94 dev_err(arizona->dev, "Failed to read underclock status: %d\n",
95 ret);
96 return IRQ_NONE;
97 }
98
99 if (val & ARIZONA_AIF3_UNDERCLOCKED_STS)
100 dev_err(arizona->dev, "AIF3 underclocked\n");
101 if (val & ARIZONA_AIF3_UNDERCLOCKED_STS)
102 dev_err(arizona->dev, "AIF3 underclocked\n");
103 if (val & ARIZONA_AIF2_UNDERCLOCKED_STS)
104 dev_err(arizona->dev, "AIF1 underclocked\n");
105 if (val & ARIZONA_ISRC2_UNDERCLOCKED_STS)
106 dev_err(arizona->dev, "ISRC2 underclocked\n");
107 if (val & ARIZONA_ISRC1_UNDERCLOCKED_STS)
108 dev_err(arizona->dev, "ISRC1 underclocked\n");
109 if (val & ARIZONA_FX_UNDERCLOCKED_STS)
110 dev_err(arizona->dev, "FX underclocked\n");
111 if (val & ARIZONA_ASRC_UNDERCLOCKED_STS)
112 dev_err(arizona->dev, "ASRC underclocked\n");
113 if (val & ARIZONA_DAC_UNDERCLOCKED_STS)
114 dev_err(arizona->dev, "DAC underclocked\n");
115 if (val & ARIZONA_ADC_UNDERCLOCKED_STS)
116 dev_err(arizona->dev, "ADC underclocked\n");
117 if (val & ARIZONA_MIXER_UNDERCLOCKED_STS)
118 dev_err(arizona->dev, "Mixer underclocked\n");
119
120 return IRQ_HANDLED;
121}
122
123static irqreturn_t arizona_overclocked(int irq, void *data)
124{
125 struct arizona *arizona = data;
126 unsigned int val[2];
127 int ret;
128
129 ret = regmap_bulk_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_6,
130 &val[0], 2);
131 if (ret != 0) {
132 dev_err(arizona->dev, "Failed to read overclock status: %d\n",
133 ret);
134 return IRQ_NONE;
135 }
136
137 if (val[0] & ARIZONA_PWM_OVERCLOCKED_STS)
138 dev_err(arizona->dev, "PWM overclocked\n");
139 if (val[0] & ARIZONA_FX_CORE_OVERCLOCKED_STS)
140 dev_err(arizona->dev, "FX core overclocked\n");
141 if (val[0] & ARIZONA_DAC_SYS_OVERCLOCKED_STS)
142 dev_err(arizona->dev, "DAC SYS overclocked\n");
143 if (val[0] & ARIZONA_DAC_WARP_OVERCLOCKED_STS)
144 dev_err(arizona->dev, "DAC WARP overclocked\n");
145 if (val[0] & ARIZONA_ADC_OVERCLOCKED_STS)
146 dev_err(arizona->dev, "ADC overclocked\n");
147 if (val[0] & ARIZONA_MIXER_OVERCLOCKED_STS)
148 dev_err(arizona->dev, "Mixer overclocked\n");
149 if (val[0] & ARIZONA_AIF3_SYNC_OVERCLOCKED_STS)
150 dev_err(arizona->dev, "AIF3 overclocked\n");
151 if (val[0] & ARIZONA_AIF2_SYNC_OVERCLOCKED_STS)
152 dev_err(arizona->dev, "AIF2 overclocked\n");
153 if (val[0] & ARIZONA_AIF1_SYNC_OVERCLOCKED_STS)
154 dev_err(arizona->dev, "AIF1 overclocked\n");
155 if (val[0] & ARIZONA_PAD_CTRL_OVERCLOCKED_STS)
156 dev_err(arizona->dev, "Pad control overclocked\n");
157
158 if (val[1] & ARIZONA_SLIMBUS_SUBSYS_OVERCLOCKED_STS)
159 dev_err(arizona->dev, "Slimbus subsystem overclocked\n");
160 if (val[1] & ARIZONA_SLIMBUS_ASYNC_OVERCLOCKED_STS)
161 dev_err(arizona->dev, "Slimbus async overclocked\n");
162 if (val[1] & ARIZONA_SLIMBUS_SYNC_OVERCLOCKED_STS)
163 dev_err(arizona->dev, "Slimbus sync overclocked\n");
164 if (val[1] & ARIZONA_ASRC_ASYNC_SYS_OVERCLOCKED_STS)
165 dev_err(arizona->dev, "ASRC async system overclocked\n");
166 if (val[1] & ARIZONA_ASRC_ASYNC_WARP_OVERCLOCKED_STS)
167 dev_err(arizona->dev, "ASRC async WARP overclocked\n");
168 if (val[1] & ARIZONA_ASRC_SYNC_SYS_OVERCLOCKED_STS)
169 dev_err(arizona->dev, "ASRC sync system overclocked\n");
170 if (val[1] & ARIZONA_ASRC_SYNC_WARP_OVERCLOCKED_STS)
171 dev_err(arizona->dev, "ASRC sync WARP overclocked\n");
172 if (val[1] & ARIZONA_ADSP2_1_OVERCLOCKED_STS)
173 dev_err(arizona->dev, "DSP1 overclocked\n");
174 if (val[1] & ARIZONA_ISRC2_OVERCLOCKED_STS)
175 dev_err(arizona->dev, "ISRC2 overclocked\n");
176 if (val[1] & ARIZONA_ISRC1_OVERCLOCKED_STS)
177 dev_err(arizona->dev, "ISRC1 overclocked\n");
178
179 return IRQ_HANDLED;
180}
181
182static int arizona_wait_for_boot(struct arizona *arizona)
183{
184 unsigned int reg;
185 int ret, i;
186
187 /*
188 * We can't use an interrupt as we need to runtime resume to do so,
189 * we won't race with the interrupt handler as it'll be blocked on
190 * runtime resume.
191 */
192 for (i = 0; i < 5; i++) {
193 msleep(1);
194
195 ret = regmap_read(arizona->regmap,
196 ARIZONA_INTERRUPT_RAW_STATUS_5, &reg);
197 if (ret != 0) {
198 dev_err(arizona->dev, "Failed to read boot state: %d\n",
199 ret);
200 return ret;
201 }
202
203 if (reg & ARIZONA_BOOT_DONE_STS)
204 break;
205 }
206
207 if (reg & ARIZONA_BOOT_DONE_STS) {
208 regmap_write(arizona->regmap, ARIZONA_INTERRUPT_STATUS_5,
209 ARIZONA_BOOT_DONE_STS);
210 } else {
211 dev_err(arizona->dev, "Device boot timed out: %x\n", reg);
212 return -ETIMEDOUT;
213 }
214
215 pm_runtime_mark_last_busy(arizona->dev);
216
217 return 0;
218}
219
220#ifdef CONFIG_PM_RUNTIME
221static int arizona_runtime_resume(struct device *dev)
222{
223 struct arizona *arizona = dev_get_drvdata(dev);
224 int ret;
225
226 if (arizona->pdata.ldoena)
227 gpio_set_value_cansleep(arizona->pdata.ldoena, 1);
228
229 regcache_cache_only(arizona->regmap, false);
230
231 ret = arizona_wait_for_boot(arizona);
232 if (ret != 0)
233 return ret;
234
235 regcache_sync(arizona->regmap);
236
237 return 0;
238}
239
240static int arizona_runtime_suspend(struct device *dev)
241{
242 struct arizona *arizona = dev_get_drvdata(dev);
243
244 if (arizona->pdata.ldoena) {
245 gpio_set_value_cansleep(arizona->pdata.ldoena, 0);
246 regcache_cache_only(arizona->regmap, true);
247 regcache_mark_dirty(arizona->regmap);
248 }
249
250 return 0;
251}
252#endif
253
254const struct dev_pm_ops arizona_pm_ops = {
255 SET_RUNTIME_PM_OPS(arizona_runtime_suspend,
256 arizona_runtime_resume,
257 NULL)
258};
259EXPORT_SYMBOL_GPL(arizona_pm_ops);
260
261static struct mfd_cell early_devs[] = {
262 { .name = "arizona-ldo1" },
263};
264
265static struct mfd_cell wm5102_devs[] = {
266 { .name = "arizona-extcon" },
267 { .name = "arizona-gpio" },
268 { .name = "arizona-micsupp" },
269 { .name = "arizona-pwm" },
270 { .name = "wm5102-codec" },
271};
272
273int __devinit arizona_dev_init(struct arizona *arizona)
274{
275 struct device *dev = arizona->dev;
276 const char *type_name;
277 unsigned int reg, val;
278 int ret, i;
279
280 dev_set_drvdata(arizona->dev, arizona);
281 mutex_init(&arizona->clk_lock);
282
283 if (dev_get_platdata(arizona->dev))
284 memcpy(&arizona->pdata, dev_get_platdata(arizona->dev),
285 sizeof(arizona->pdata));
286
287 regcache_cache_only(arizona->regmap, true);
288
289 switch (arizona->type) {
290 case WM5102:
291 for (i = 0; i < ARRAY_SIZE(wm5102_core_supplies); i++)
292 arizona->core_supplies[i].supply
293 = wm5102_core_supplies[i];
294 arizona->num_core_supplies = ARRAY_SIZE(wm5102_core_supplies);
295 break;
296 default:
297 dev_err(arizona->dev, "Unknown device type %d\n",
298 arizona->type);
299 return -EINVAL;
300 }
301
302 ret = mfd_add_devices(arizona->dev, -1, early_devs,
303 ARRAY_SIZE(early_devs), NULL, 0);
304 if (ret != 0) {
305 dev_err(dev, "Failed to add early children: %d\n", ret);
306 return ret;
307 }
308
309 ret = devm_regulator_bulk_get(dev, arizona->num_core_supplies,
310 arizona->core_supplies);
311 if (ret != 0) {
312 dev_err(dev, "Failed to request core supplies: %d\n",
313 ret);
314 goto err_early;
315 }
316
317 ret = regulator_bulk_enable(arizona->num_core_supplies,
318 arizona->core_supplies);
319 if (ret != 0) {
320 dev_err(dev, "Failed to enable core supplies: %d\n",
321 ret);
322 goto err_early;
323 }
324
325 if (arizona->pdata.reset) {
326 /* Start out with /RESET low to put the chip into reset */
327 ret = gpio_request_one(arizona->pdata.reset,
328 GPIOF_DIR_OUT | GPIOF_INIT_LOW,
329 "arizona /RESET");
330 if (ret != 0) {
331 dev_err(dev, "Failed to request /RESET: %d\n", ret);
332 goto err_enable;
333 }
334
335 gpio_set_value_cansleep(arizona->pdata.reset, 1);
336 }
337
338 if (arizona->pdata.ldoena) {
339 ret = gpio_request_one(arizona->pdata.ldoena,
340 GPIOF_DIR_OUT | GPIOF_INIT_HIGH,
341 "arizona LDOENA");
342 if (ret != 0) {
343 dev_err(dev, "Failed to request LDOENA: %d\n", ret);
344 goto err_reset;
345 }
346 }
347
348 regcache_cache_only(arizona->regmap, false);
349
350 ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, &reg);
351 if (ret != 0) {
352 dev_err(dev, "Failed to read ID register: %d\n", ret);
353 goto err_ldoena;
354 }
355
356 ret = regmap_read(arizona->regmap, ARIZONA_DEVICE_REVISION,
357 &arizona->rev);
358 if (ret != 0) {
359 dev_err(dev, "Failed to read revision register: %d\n", ret);
360 goto err_ldoena;
361 }
362 arizona->rev &= ARIZONA_DEVICE_REVISION_MASK;
363
364 switch (reg) {
365 case 0x5102:
366 type_name = "WM5102";
367 if (arizona->type != WM5102) {
368 dev_err(arizona->dev, "WM5102 registered as %d\n",
369 arizona->type);
370 arizona->type = WM5102;
371 }
372 ret = wm5102_patch(arizona);
373 break;
374
375 default:
376 dev_err(arizona->dev, "Unknown device ID %x\n", reg);
377 goto err_ldoena;
378 }
379
380 dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A');
381
382 if (ret != 0)
383 dev_err(arizona->dev, "Failed to apply patch: %d\n", ret);
384
385 /* If we have a /RESET GPIO we'll already be reset */
386 if (!arizona->pdata.reset) {
387 ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0);
388 if (ret != 0) {
389 dev_err(dev, "Failed to reset device: %d\n", ret);
390 goto err_ldoena;
391 }
392 }
393
394 arizona_wait_for_boot(arizona);
395
396 for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
397 if (!arizona->pdata.gpio_defaults[i])
398 continue;
399
400 regmap_write(arizona->regmap, ARIZONA_GPIO1_CTRL + i,
401 arizona->pdata.gpio_defaults[i]);
402 }
403
404 pm_runtime_set_autosuspend_delay(arizona->dev, 100);
405 pm_runtime_use_autosuspend(arizona->dev);
406 pm_runtime_enable(arizona->dev);
407
408 /* Chip default */
409 if (!arizona->pdata.clk32k_src)
410 arizona->pdata.clk32k_src = ARIZONA_32KZ_MCLK2;
411
412 switch (arizona->pdata.clk32k_src) {
413 case ARIZONA_32KZ_MCLK1:
414 case ARIZONA_32KZ_MCLK2:
415 regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
416 ARIZONA_CLK_32K_SRC_MASK,
417 arizona->pdata.clk32k_src - 1);
418 break;
419 case ARIZONA_32KZ_NONE:
420 regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
421 ARIZONA_CLK_32K_SRC_MASK, 2);
422 break;
423 default:
424 dev_err(arizona->dev, "Invalid 32kHz clock source: %d\n",
425 arizona->pdata.clk32k_src);
426 ret = -EINVAL;
427 goto err_ldoena;
428 }
429
430 for (i = 0; i < ARIZONA_MAX_INPUT; i++) {
431 /* Default for both is 0 so noop with defaults */
432 val = arizona->pdata.dmic_ref[i]
433 << ARIZONA_IN1_DMIC_SUP_SHIFT;
434 val |= arizona->pdata.inmode[i] << ARIZONA_IN1_MODE_SHIFT;
435
436 regmap_update_bits(arizona->regmap,
437 ARIZONA_IN1L_CONTROL + (i * 8),
438 ARIZONA_IN1_DMIC_SUP_MASK |
439 ARIZONA_IN1_MODE_MASK, val);
440 }
441
442 for (i = 0; i < ARIZONA_MAX_OUTPUT; i++) {
443 /* Default is 0 so noop with defaults */
444 if (arizona->pdata.out_mono[i])
445 val = ARIZONA_OUT1_MONO;
446 else
447 val = 0;
448
449 regmap_update_bits(arizona->regmap,
450 ARIZONA_OUTPUT_PATH_CONFIG_1L + (i * 8),
451 ARIZONA_OUT1_MONO, val);
452 }
453
454 BUILD_BUG_ON(ARIZONA_MAX_PDM_SPK > 1);
455 for (i = 0; i < ARIZONA_MAX_PDM_SPK; i++) {
456 if (arizona->pdata.spk_mute[i])
457 regmap_update_bits(arizona->regmap,
458 ARIZONA_PDM_SPK1_CTRL_1,
459 ARIZONA_SPK1_MUTE_ENDIAN_MASK |
460 ARIZONA_SPK1_MUTE_SEQ1_MASK,
461 arizona->pdata.spk_mute[i]);
462
463 if (arizona->pdata.spk_fmt[i])
464 regmap_update_bits(arizona->regmap,
465 ARIZONA_PDM_SPK1_CTRL_2,
466 ARIZONA_SPK1_FMT_MASK,
467 arizona->pdata.spk_fmt[i]);
468 }
469
470 /* Set up for interrupts */
471 ret = arizona_irq_init(arizona);
472 if (ret != 0)
473 goto err_ldoena;
474
475 arizona_request_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, "CLKGEN error",
476 arizona_clkgen_err, arizona);
477 arizona_request_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, "Overclocked",
478 arizona_overclocked, arizona);
479 arizona_request_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, "Underclocked",
480 arizona_underclocked, arizona);
481
482 switch (arizona->type) {
483 case WM5102:
484 ret = mfd_add_devices(arizona->dev, -1, wm5102_devs,
485 ARRAY_SIZE(wm5102_devs), NULL, 0);
486 break;
487 }
488
489 if (ret != 0) {
490 dev_err(arizona->dev, "Failed to add subdevices: %d\n", ret);
491 goto err_irq;
492 }
493
494 return 0;
495
496err_irq:
497 arizona_irq_exit(arizona);
498err_ldoena:
499 if (arizona->pdata.ldoena) {
500 gpio_set_value_cansleep(arizona->pdata.ldoena, 0);
501 gpio_free(arizona->pdata.ldoena);
502 }
503err_reset:
504 if (arizona->pdata.reset) {
505 gpio_set_value_cansleep(arizona->pdata.reset, 1);
506 gpio_free(arizona->pdata.reset);
507 }
508err_enable:
509 regulator_bulk_disable(ARRAY_SIZE(arizona->core_supplies),
510 arizona->core_supplies);
511err_early:
512 mfd_remove_devices(dev);
513 return ret;
514}
515EXPORT_SYMBOL_GPL(arizona_dev_init);
516
517int __devexit arizona_dev_exit(struct arizona *arizona)
518{
519 mfd_remove_devices(arizona->dev);
520 arizona_free_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, arizona);
521 arizona_free_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, arizona);
522 arizona_free_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, arizona);
523 pm_runtime_disable(arizona->dev);
524 arizona_irq_exit(arizona);
525 return 0;
526}
527EXPORT_SYMBOL_GPL(arizona_dev_exit);