aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2010-05-20 06:00:43 -0400
committerTakashi Iwai <tiwai@suse.de>2010-05-20 06:00:43 -0400
commitd71f4cece4bd97d05592836202fc04ff2e7817e3 (patch)
tree6c877c7a938758b1323d9c97d46b9c536e618c69 /drivers/mfd
parent19008bdacb9f7841166ebafe0aef361ee582ffbf (diff)
parentad8332c1302bcb4f80d593fd3eb477be9d7f5604 (diff)
Merge branch 'topic/asoc' into for-linus
Conflicts: sound/soc/codecs/ad1938.c
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/Kconfig8
-rw-r--r--drivers/mfd/Makefile3
-rw-r--r--drivers/mfd/davinci_voicecodec.c190
-rw-r--r--drivers/mfd/twl-core.c4
-rw-r--r--drivers/mfd/wm831x-irq.c30
-rw-r--r--drivers/mfd/wm8994-core.c43
-rw-r--r--drivers/mfd/wm8994-irq.c310
7 files changed, 580 insertions, 8 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 2a5a0b78f84e..de3e74cde51c 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -53,6 +53,10 @@ config MFD_SH_MOBILE_SDHI
53 This driver supports the SDHI hardware block found in many 53 This driver supports the SDHI hardware block found in many
54 SuperH Mobile SoCs. 54 SuperH Mobile SoCs.
55 55
56config MFD_DAVINCI_VOICECODEC
57 tristate
58 select MFD_CORE
59
56config MFD_DM355EVM_MSP 60config MFD_DM355EVM_MSP
57 bool "DaVinci DM355 EVM microcontroller" 61 bool "DaVinci DM355 EVM microcontroller"
58 depends on I2C && MACH_DAVINCI_DM355_EVM 62 depends on I2C && MACH_DAVINCI_DM355_EVM
@@ -297,9 +301,9 @@ config MFD_WM8350_I2C
297 selected to enable support for the functionality of the chip. 301 selected to enable support for the functionality of the chip.
298 302
299config MFD_WM8994 303config MFD_WM8994
300 tristate "Support Wolfson Microelectronics WM8994" 304 bool "Support Wolfson Microelectronics WM8994"
301 select MFD_CORE 305 select MFD_CORE
302 depends on I2C 306 depends on I2C=y && GENERIC_HARDIRQS
303 help 307 help
304 The WM8994 is a highly integrated hi-fi CODEC designed for 308 The WM8994 is a highly integrated hi-fi CODEC designed for
305 smartphone applicatiosn. As well as audio functionality it 309 smartphone applicatiosn. As well as audio functionality it
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 22715add99a7..87935f967aa0 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o
12obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o 12obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o
13obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o 13obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o
14 14
15obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o
15obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o 16obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
16 17
17obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o 18obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o
@@ -25,7 +26,7 @@ wm8350-objs := wm8350-core.o wm8350-regmap.o wm8350-gpio.o
25wm8350-objs += wm8350-irq.o 26wm8350-objs += wm8350-irq.o
26obj-$(CONFIG_MFD_WM8350) += wm8350.o 27obj-$(CONFIG_MFD_WM8350) += wm8350.o
27obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o 28obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o
28obj-$(CONFIG_MFD_WM8994) += wm8994-core.o 29obj-$(CONFIG_MFD_WM8994) += wm8994-core.o wm8994-irq.o
29 30
30obj-$(CONFIG_TPS65010) += tps65010.o 31obj-$(CONFIG_TPS65010) += tps65010.o
31obj-$(CONFIG_MENELAUS) += menelaus.o 32obj-$(CONFIG_MENELAUS) += menelaus.o
diff --git a/drivers/mfd/davinci_voicecodec.c b/drivers/mfd/davinci_voicecodec.c
new file mode 100644
index 000000000000..3e75f02e4778
--- /dev/null
+++ b/drivers/mfd/davinci_voicecodec.c
@@ -0,0 +1,190 @@
1/*
2 * DaVinci Voice Codec Core Interface for TI platforms
3 *
4 * Copyright (C) 2010 Texas Instruments, Inc
5 *
6 * Author: Miguel Aguilar <miguel.aguilar@ridgerun.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 as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#include <linux/init.h>
24#include <linux/module.h>
25#include <linux/device.h>
26#include <linux/slab.h>
27#include <linux/delay.h>
28#include <linux/io.h>
29#include <linux/clk.h>
30
31#include <sound/pcm.h>
32
33#include <linux/mfd/davinci_voicecodec.h>
34
35u32 davinci_vc_read(struct davinci_vc *davinci_vc, int reg)
36{
37 return __raw_readl(davinci_vc->base + reg);
38}
39
40void davinci_vc_write(struct davinci_vc *davinci_vc,
41 int reg, u32 val)
42{
43 __raw_writel(val, davinci_vc->base + reg);
44}
45
46static int __init davinci_vc_probe(struct platform_device *pdev)
47{
48 struct davinci_vc *davinci_vc;
49 struct resource *res, *mem;
50 struct mfd_cell *cell = NULL;
51 int ret;
52
53 davinci_vc = kzalloc(sizeof(struct davinci_vc), GFP_KERNEL);
54 if (!davinci_vc) {
55 dev_dbg(&pdev->dev,
56 "could not allocate memory for private data\n");
57 return -ENOMEM;
58 }
59
60 davinci_vc->clk = clk_get(&pdev->dev, NULL);
61 if (IS_ERR(davinci_vc->clk)) {
62 dev_dbg(&pdev->dev,
63 "could not get the clock for voice codec\n");
64 ret = -ENODEV;
65 goto fail1;
66 }
67 clk_enable(davinci_vc->clk);
68
69 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
70 if (!res) {
71 dev_err(&pdev->dev, "no mem resource\n");
72 ret = -ENODEV;
73 goto fail2;
74 }
75
76 davinci_vc->pbase = res->start;
77 davinci_vc->base_size = resource_size(res);
78
79 mem = request_mem_region(davinci_vc->pbase, davinci_vc->base_size,
80 pdev->name);
81 if (!mem) {
82 dev_err(&pdev->dev, "VCIF region already claimed\n");
83 ret = -EBUSY;
84 goto fail2;
85 }
86
87 davinci_vc->base = ioremap(davinci_vc->pbase, davinci_vc->base_size);
88 if (!davinci_vc->base) {
89 dev_err(&pdev->dev, "can't ioremap mem resource.\n");
90 ret = -ENOMEM;
91 goto fail3;
92 }
93
94 res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
95 if (!res) {
96 dev_err(&pdev->dev, "no DMA resource\n");
97 return -ENXIO;
98 }
99
100 davinci_vc->davinci_vcif.dma_tx_channel = res->start;
101 davinci_vc->davinci_vcif.dma_tx_addr =
102 (dma_addr_t)(io_v2p(davinci_vc->base) + DAVINCI_VC_WFIFO);
103
104 res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
105 if (!res) {
106 dev_err(&pdev->dev, "no DMA resource\n");
107 return -ENXIO;
108 }
109
110 davinci_vc->davinci_vcif.dma_rx_channel = res->start;
111 davinci_vc->davinci_vcif.dma_rx_addr =
112 (dma_addr_t)(io_v2p(davinci_vc->base) + DAVINCI_VC_RFIFO);
113
114 davinci_vc->dev = &pdev->dev;
115 davinci_vc->pdev = pdev;
116
117 /* Voice codec interface client */
118 cell = &davinci_vc->cells[DAVINCI_VC_VCIF_CELL];
119 cell->name = "davinci_vcif";
120 cell->driver_data = davinci_vc;
121
122 /* Voice codec CQ93VC client */
123 cell = &davinci_vc->cells[DAVINCI_VC_CQ93VC_CELL];
124 cell->name = "cq93vc";
125 cell->driver_data = davinci_vc;
126
127 ret = mfd_add_devices(&pdev->dev, pdev->id, davinci_vc->cells,
128 DAVINCI_VC_CELLS, NULL, 0);
129 if (ret != 0) {
130 dev_err(&pdev->dev, "fail to register client devices\n");
131 goto fail4;
132 }
133
134 return 0;
135
136fail4:
137 iounmap(davinci_vc->base);
138fail3:
139 release_mem_region(davinci_vc->pbase, davinci_vc->base_size);
140fail2:
141 clk_disable(davinci_vc->clk);
142 clk_put(davinci_vc->clk);
143 davinci_vc->clk = NULL;
144fail1:
145 kfree(davinci_vc);
146
147 return ret;
148}
149
150static int __devexit davinci_vc_remove(struct platform_device *pdev)
151{
152 struct davinci_vc *davinci_vc = platform_get_drvdata(pdev);
153
154 mfd_remove_devices(&pdev->dev);
155
156 iounmap(davinci_vc->base);
157 release_mem_region(davinci_vc->pbase, davinci_vc->base_size);
158
159 clk_disable(davinci_vc->clk);
160 clk_put(davinci_vc->clk);
161 davinci_vc->clk = NULL;
162
163 kfree(davinci_vc);
164
165 return 0;
166}
167
168static struct platform_driver davinci_vc_driver = {
169 .driver = {
170 .name = "davinci_voicecodec",
171 .owner = THIS_MODULE,
172 },
173 .remove = __devexit_p(davinci_vc_remove),
174};
175
176static int __init davinci_vc_init(void)
177{
178 return platform_driver_probe(&davinci_vc_driver, davinci_vc_probe);
179}
180module_init(davinci_vc_init);
181
182static void __exit davinci_vc_exit(void)
183{
184 platform_driver_unregister(&davinci_vc_driver);
185}
186module_exit(davinci_vc_exit);
187
188MODULE_AUTHOR("Miguel Aguilar");
189MODULE_DESCRIPTION("Texas Instruments DaVinci Voice Codec Core Interface");
190MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index 562cd4935e17..720e099e506d 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -109,7 +109,7 @@
109#endif 109#endif
110 110
111#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE) ||\ 111#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE) ||\
112 defined(CONFIG_SND_SOC_TWL6030) || defined(CONFIG_SND_SOC_TWL6030_MODULE) 112 defined(CONFIG_SND_SOC_TWL6040) || defined(CONFIG_SND_SOC_TWL6040_MODULE)
113#define twl_has_codec() true 113#define twl_has_codec() true
114#else 114#else
115#define twl_has_codec() false 115#define twl_has_codec() false
@@ -708,7 +708,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
708 /* Phoenix*/ 708 /* Phoenix*/
709 if (twl_has_codec() && pdata->codec && twl_class_is_6030()) { 709 if (twl_has_codec() && pdata->codec && twl_class_is_6030()) {
710 sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid; 710 sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
711 child = add_child(sub_chip_id, "twl6030_codec", 711 child = add_child(sub_chip_id, "twl6040_codec",
712 pdata->codec, sizeof(*pdata->codec), 712 pdata->codec, sizeof(*pdata->codec),
713 false, 0, 0); 713 false, 0, 0);
714 if (IS_ERR(child)) 714 if (IS_ERR(child))
diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c
index 301327697117..4c1122ceb443 100644
--- a/drivers/mfd/wm831x-irq.c
+++ b/drivers/mfd/wm831x-irq.c
@@ -21,6 +21,7 @@
21 21
22#include <linux/mfd/wm831x/core.h> 22#include <linux/mfd/wm831x/core.h>
23#include <linux/mfd/wm831x/pdata.h> 23#include <linux/mfd/wm831x/pdata.h>
24#include <linux/mfd/wm831x/gpio.h>
24#include <linux/mfd/wm831x/irq.h> 25#include <linux/mfd/wm831x/irq.h>
25 26
26#include <linux/delay.h> 27#include <linux/delay.h>
@@ -388,12 +389,41 @@ static void wm831x_irq_mask(unsigned int irq)
388 wm831x->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask; 389 wm831x->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
389} 390}
390 391
392static int wm831x_irq_set_type(unsigned int irq, unsigned int type)
393{
394 struct wm831x *wm831x = get_irq_chip_data(irq);
395 int val;
396
397 irq = irq - wm831x->irq_base;
398
399 if (irq < WM831X_IRQ_GPIO_1 || irq > WM831X_IRQ_GPIO_11)
400 return -EINVAL;
401
402 switch (type) {
403 case IRQ_TYPE_EDGE_BOTH:
404 val = WM831X_GPN_INT_MODE;
405 break;
406 case IRQ_TYPE_EDGE_RISING:
407 val = WM831X_GPN_POL;
408 break;
409 case IRQ_TYPE_EDGE_FALLING:
410 val = 0;
411 break;
412 default:
413 return -EINVAL;
414 }
415
416 return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + irq,
417 WM831X_GPN_INT_MODE | WM831X_GPN_POL, val);
418}
419
391static struct irq_chip wm831x_irq_chip = { 420static struct irq_chip wm831x_irq_chip = {
392 .name = "wm831x", 421 .name = "wm831x",
393 .bus_lock = wm831x_irq_lock, 422 .bus_lock = wm831x_irq_lock,
394 .bus_sync_unlock = wm831x_irq_sync_unlock, 423 .bus_sync_unlock = wm831x_irq_sync_unlock,
395 .mask = wm831x_irq_mask, 424 .mask = wm831x_irq_mask,
396 .unmask = wm831x_irq_unmask, 425 .unmask = wm831x_irq_unmask,
426 .set_type = wm831x_irq_set_type,
397}; 427};
398 428
399/* The processing of the primary interrupt occurs in a thread so that 429/* The processing of the primary interrupt occurs in a thread so that
diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c
index cc524df10aa1..ec71c9368906 100644
--- a/drivers/mfd/wm8994-core.c
+++ b/drivers/mfd/wm8994-core.c
@@ -173,9 +173,34 @@ static struct mfd_cell wm8994_regulator_devs[] = {
173 { .name = "wm8994-ldo", .id = 2 }, 173 { .name = "wm8994-ldo", .id = 2 },
174}; 174};
175 175
176static struct resource wm8994_codec_resources[] = {
177 {
178 .start = WM8994_IRQ_TEMP_SHUT,
179 .end = WM8994_IRQ_TEMP_WARN,
180 .flags = IORESOURCE_IRQ,
181 },
182};
183
184static struct resource wm8994_gpio_resources[] = {
185 {
186 .start = WM8994_IRQ_GPIO(1),
187 .end = WM8994_IRQ_GPIO(11),
188 .flags = IORESOURCE_IRQ,
189 },
190};
191
176static struct mfd_cell wm8994_devs[] = { 192static struct mfd_cell wm8994_devs[] = {
177 { .name = "wm8994-codec" }, 193 {
178 { .name = "wm8994-gpio" }, 194 .name = "wm8994-codec",
195 .num_resources = ARRAY_SIZE(wm8994_codec_resources),
196 .resources = wm8994_codec_resources,
197 },
198
199 {
200 .name = "wm8994-gpio",
201 .num_resources = ARRAY_SIZE(wm8994_gpio_resources),
202 .resources = wm8994_gpio_resources,
203 },
179}; 204};
180 205
181/* 206/*
@@ -236,6 +261,11 @@ static int wm8994_device_resume(struct device *dev)
236 return ret; 261 return ret;
237 } 262 }
238 263
264 ret = wm8994_write(wm8994, WM8994_INTERRUPT_STATUS_1_MASK,
265 WM8994_NUM_IRQ_REGS * 2, &wm8994->irq_masks_cur);
266 if (ret < 0)
267 dev_err(dev, "Failed to restore interrupt masks: %d\n", ret);
268
239 ret = wm8994_write(wm8994, WM8994_LDO_1, WM8994_NUM_LDO_REGS * 2, 269 ret = wm8994_write(wm8994, WM8994_LDO_1, WM8994_NUM_LDO_REGS * 2,
240 &wm8994->ldo_regs); 270 &wm8994->ldo_regs);
241 if (ret < 0) 271 if (ret < 0)
@@ -348,6 +378,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, unsigned long id, int irq)
348 378
349 379
350 if (pdata) { 380 if (pdata) {
381 wm8994->irq_base = pdata->irq_base;
351 wm8994->gpio_base = pdata->gpio_base; 382 wm8994->gpio_base = pdata->gpio_base;
352 383
353 /* GPIO configuration is only applied if it's non-zero */ 384 /* GPIO configuration is only applied if it's non-zero */
@@ -375,16 +406,20 @@ static int wm8994_device_init(struct wm8994 *wm8994, unsigned long id, int irq)
375 WM8994_LDO1_DISCH, 0); 406 WM8994_LDO1_DISCH, 0);
376 } 407 }
377 408
409 wm8994_irq_init(wm8994);
410
378 ret = mfd_add_devices(wm8994->dev, -1, 411 ret = mfd_add_devices(wm8994->dev, -1,
379 wm8994_devs, ARRAY_SIZE(wm8994_devs), 412 wm8994_devs, ARRAY_SIZE(wm8994_devs),
380 NULL, 0); 413 NULL, 0);
381 if (ret != 0) { 414 if (ret != 0) {
382 dev_err(wm8994->dev, "Failed to add children: %d\n", ret); 415 dev_err(wm8994->dev, "Failed to add children: %d\n", ret);
383 goto err_enable; 416 goto err_irq;
384 } 417 }
385 418
386 return 0; 419 return 0;
387 420
421err_irq:
422 wm8994_irq_exit(wm8994);
388err_enable: 423err_enable:
389 regulator_bulk_disable(ARRAY_SIZE(wm8994_main_supplies), 424 regulator_bulk_disable(ARRAY_SIZE(wm8994_main_supplies),
390 wm8994->supplies); 425 wm8994->supplies);
@@ -401,6 +436,7 @@ err:
401static void wm8994_device_exit(struct wm8994 *wm8994) 436static void wm8994_device_exit(struct wm8994 *wm8994)
402{ 437{
403 mfd_remove_devices(wm8994->dev); 438 mfd_remove_devices(wm8994->dev);
439 wm8994_irq_exit(wm8994);
404 regulator_bulk_disable(ARRAY_SIZE(wm8994_main_supplies), 440 regulator_bulk_disable(ARRAY_SIZE(wm8994_main_supplies),
405 wm8994->supplies); 441 wm8994->supplies);
406 regulator_bulk_free(ARRAY_SIZE(wm8994_main_supplies), wm8994->supplies); 442 regulator_bulk_free(ARRAY_SIZE(wm8994_main_supplies), wm8994->supplies);
@@ -469,6 +505,7 @@ static int wm8994_i2c_probe(struct i2c_client *i2c,
469 wm8994->control_data = i2c; 505 wm8994->control_data = i2c;
470 wm8994->read_dev = wm8994_i2c_read_device; 506 wm8994->read_dev = wm8994_i2c_read_device;
471 wm8994->write_dev = wm8994_i2c_write_device; 507 wm8994->write_dev = wm8994_i2c_write_device;
508 wm8994->irq = i2c->irq;
472 509
473 return wm8994_device_init(wm8994, id->driver_data, i2c->irq); 510 return wm8994_device_init(wm8994, id->driver_data, i2c->irq);
474} 511}
diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c
new file mode 100644
index 000000000000..8400eb1ee5db
--- /dev/null
+++ b/drivers/mfd/wm8994-irq.c
@@ -0,0 +1,310 @@
1/*
2 * wm8994-irq.c -- Interrupt controller support for Wolfson WM8994
3 *
4 * Copyright 2010 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/irq.h>
19#include <linux/mfd/core.h>
20#include <linux/interrupt.h>
21
22#include <linux/mfd/wm8994/core.h>
23#include <linux/mfd/wm8994/registers.h>
24
25#include <linux/delay.h>
26
27struct wm8994_irq_data {
28 int reg;
29 int mask;
30};
31
32static struct wm8994_irq_data wm8994_irqs[] = {
33 [WM8994_IRQ_TEMP_SHUT] = {
34 .reg = 2,
35 .mask = WM8994_TEMP_SHUT_EINT,
36 },
37 [WM8994_IRQ_MIC1_DET] = {
38 .reg = 2,
39 .mask = WM8994_MIC1_DET_EINT,
40 },
41 [WM8994_IRQ_MIC1_SHRT] = {
42 .reg = 2,
43 .mask = WM8994_MIC1_SHRT_EINT,
44 },
45 [WM8994_IRQ_MIC2_DET] = {
46 .reg = 2,
47 .mask = WM8994_MIC2_DET_EINT,
48 },
49 [WM8994_IRQ_MIC2_SHRT] = {
50 .reg = 2,
51 .mask = WM8994_MIC2_SHRT_EINT,
52 },
53 [WM8994_IRQ_FLL1_LOCK] = {
54 .reg = 2,
55 .mask = WM8994_FLL1_LOCK_EINT,
56 },
57 [WM8994_IRQ_FLL2_LOCK] = {
58 .reg = 2,
59 .mask = WM8994_FLL2_LOCK_EINT,
60 },
61 [WM8994_IRQ_SRC1_LOCK] = {
62 .reg = 2,
63 .mask = WM8994_SRC1_LOCK_EINT,
64 },
65 [WM8994_IRQ_SRC2_LOCK] = {
66 .reg = 2,
67 .mask = WM8994_SRC2_LOCK_EINT,
68 },
69 [WM8994_IRQ_AIF1DRC1_SIG_DET] = {
70 .reg = 2,
71 .mask = WM8994_AIF1DRC1_SIG_DET,
72 },
73 [WM8994_IRQ_AIF1DRC2_SIG_DET] = {
74 .reg = 2,
75 .mask = WM8994_AIF1DRC2_SIG_DET_EINT,
76 },
77 [WM8994_IRQ_AIF2DRC_SIG_DET] = {
78 .reg = 2,
79 .mask = WM8994_AIF2DRC_SIG_DET_EINT,
80 },
81 [WM8994_IRQ_FIFOS_ERR] = {
82 .reg = 2,
83 .mask = WM8994_FIFOS_ERR_EINT,
84 },
85 [WM8994_IRQ_WSEQ_DONE] = {
86 .reg = 2,
87 .mask = WM8994_WSEQ_DONE_EINT,
88 },
89 [WM8994_IRQ_DCS_DONE] = {
90 .reg = 2,
91 .mask = WM8994_DCS_DONE_EINT,
92 },
93 [WM8994_IRQ_TEMP_WARN] = {
94 .reg = 2,
95 .mask = WM8994_TEMP_WARN_EINT,
96 },
97 [WM8994_IRQ_GPIO(1)] = {
98 .reg = 1,
99 .mask = WM8994_GP1_EINT,
100 },
101 [WM8994_IRQ_GPIO(2)] = {
102 .reg = 1,
103 .mask = WM8994_GP2_EINT,
104 },
105 [WM8994_IRQ_GPIO(3)] = {
106 .reg = 1,
107 .mask = WM8994_GP3_EINT,
108 },
109 [WM8994_IRQ_GPIO(4)] = {
110 .reg = 1,
111 .mask = WM8994_GP4_EINT,
112 },
113 [WM8994_IRQ_GPIO(5)] = {
114 .reg = 1,
115 .mask = WM8994_GP5_EINT,
116 },
117 [WM8994_IRQ_GPIO(6)] = {
118 .reg = 1,
119 .mask = WM8994_GP6_EINT,
120 },
121 [WM8994_IRQ_GPIO(7)] = {
122 .reg = 1,
123 .mask = WM8994_GP7_EINT,
124 },
125 [WM8994_IRQ_GPIO(8)] = {
126 .reg = 1,
127 .mask = WM8994_GP8_EINT,
128 },
129 [WM8994_IRQ_GPIO(9)] = {
130 .reg = 1,
131 .mask = WM8994_GP8_EINT,
132 },
133 [WM8994_IRQ_GPIO(10)] = {
134 .reg = 1,
135 .mask = WM8994_GP10_EINT,
136 },
137 [WM8994_IRQ_GPIO(11)] = {
138 .reg = 1,
139 .mask = WM8994_GP11_EINT,
140 },
141};
142
143static inline int irq_data_to_status_reg(struct wm8994_irq_data *irq_data)
144{
145 return WM8994_INTERRUPT_STATUS_1 - 1 + irq_data->reg;
146}
147
148static inline int irq_data_to_mask_reg(struct wm8994_irq_data *irq_data)
149{
150 return WM8994_INTERRUPT_STATUS_1_MASK - 1 + irq_data->reg;
151}
152
153static inline struct wm8994_irq_data *irq_to_wm8994_irq(struct wm8994 *wm8994,
154 int irq)
155{
156 return &wm8994_irqs[irq - wm8994->irq_base];
157}
158
159static void wm8994_irq_lock(unsigned int irq)
160{
161 struct wm8994 *wm8994 = get_irq_chip_data(irq);
162
163 mutex_lock(&wm8994->irq_lock);
164}
165
166static void wm8994_irq_sync_unlock(unsigned int irq)
167{
168 struct wm8994 *wm8994 = get_irq_chip_data(irq);
169 int i;
170
171 for (i = 0; i < ARRAY_SIZE(wm8994->irq_masks_cur); i++) {
172 /* If there's been a change in the mask write it back
173 * to the hardware. */
174 if (wm8994->irq_masks_cur[i] != wm8994->irq_masks_cache[i]) {
175 wm8994->irq_masks_cache[i] = wm8994->irq_masks_cur[i];
176 wm8994_reg_write(wm8994,
177 WM8994_INTERRUPT_STATUS_1_MASK + i,
178 wm8994->irq_masks_cur[i]);
179 }
180 }
181
182 mutex_unlock(&wm8994->irq_lock);
183}
184
185static void wm8994_irq_unmask(unsigned int irq)
186{
187 struct wm8994 *wm8994 = get_irq_chip_data(irq);
188 struct wm8994_irq_data *irq_data = irq_to_wm8994_irq(wm8994, irq);
189
190 wm8994->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
191}
192
193static void wm8994_irq_mask(unsigned int irq)
194{
195 struct wm8994 *wm8994 = get_irq_chip_data(irq);
196 struct wm8994_irq_data *irq_data = irq_to_wm8994_irq(wm8994, irq);
197
198 wm8994->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
199}
200
201static struct irq_chip wm8994_irq_chip = {
202 .name = "wm8994",
203 .bus_lock = wm8994_irq_lock,
204 .bus_sync_unlock = wm8994_irq_sync_unlock,
205 .mask = wm8994_irq_mask,
206 .unmask = wm8994_irq_unmask,
207};
208
209/* The processing of the primary interrupt occurs in a thread so that
210 * we can interact with the device over I2C or SPI. */
211static irqreturn_t wm8994_irq_thread(int irq, void *data)
212{
213 struct wm8994 *wm8994 = data;
214 unsigned int i;
215 u16 status[WM8994_NUM_IRQ_REGS];
216 int ret;
217
218 ret = wm8994_bulk_read(wm8994, WM8994_INTERRUPT_STATUS_1,
219 WM8994_NUM_IRQ_REGS, status);
220 if (ret < 0) {
221 dev_err(wm8994->dev, "Failed to read interrupt status: %d\n",
222 ret);
223 return IRQ_NONE;
224 }
225
226 /* Apply masking */
227 for (i = 0; i < WM8994_NUM_IRQ_REGS; i++)
228 status[i] &= ~wm8994->irq_masks_cur[i];
229
230 /* Report */
231 for (i = 0; i < ARRAY_SIZE(wm8994_irqs); i++) {
232 if (status[wm8994_irqs[i].reg - 1] & wm8994_irqs[i].mask)
233 handle_nested_irq(wm8994->irq_base + i);
234 }
235
236 /* Ack any unmasked IRQs */
237 for (i = 0; i < ARRAY_SIZE(status); i++) {
238 if (status[i])
239 wm8994_reg_write(wm8994, WM8994_INTERRUPT_STATUS_1 + i,
240 status[i]);
241 }
242
243 return IRQ_HANDLED;
244}
245
246int wm8994_irq_init(struct wm8994 *wm8994)
247{
248 int i, cur_irq, ret;
249
250 mutex_init(&wm8994->irq_lock);
251
252 /* Mask the individual interrupt sources */
253 for (i = 0; i < ARRAY_SIZE(wm8994->irq_masks_cur); i++) {
254 wm8994->irq_masks_cur[i] = 0xffff;
255 wm8994->irq_masks_cache[i] = 0xffff;
256 wm8994_reg_write(wm8994, WM8994_INTERRUPT_STATUS_1_MASK + i,
257 0xffff);
258 }
259
260 if (!wm8994->irq) {
261 dev_warn(wm8994->dev,
262 "No interrupt specified, no interrupts\n");
263 wm8994->irq_base = 0;
264 return 0;
265 }
266
267 if (!wm8994->irq_base) {
268 dev_err(wm8994->dev,
269 "No interrupt base specified, no interrupts\n");
270 return 0;
271 }
272
273 /* Register them with genirq */
274 for (cur_irq = wm8994->irq_base;
275 cur_irq < ARRAY_SIZE(wm8994_irqs) + wm8994->irq_base;
276 cur_irq++) {
277 set_irq_chip_data(cur_irq, wm8994);
278 set_irq_chip_and_handler(cur_irq, &wm8994_irq_chip,
279 handle_edge_irq);
280 set_irq_nested_thread(cur_irq, 1);
281
282 /* ARM needs us to explicitly flag the IRQ as valid
283 * and will set them noprobe when we do so. */
284#ifdef CONFIG_ARM
285 set_irq_flags(cur_irq, IRQF_VALID);
286#else
287 set_irq_noprobe(cur_irq);
288#endif
289 }
290
291 ret = request_threaded_irq(wm8994->irq, NULL, wm8994_irq_thread,
292 IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
293 "wm8994", wm8994);
294 if (ret != 0) {
295 dev_err(wm8994->dev, "Failed to request IRQ %d: %d\n",
296 wm8994->irq, ret);
297 return ret;
298 }
299
300 /* Enable top level interrupt if it was masked */
301 wm8994_reg_write(wm8994, WM8994_INTERRUPT_CONTROL, 0);
302
303 return 0;
304}
305
306void wm8994_irq_exit(struct wm8994 *wm8994)
307{
308 if (wm8994->irq)
309 free_irq(wm8994->irq, wm8994);
310}