aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd/arizona-irq.c
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2012-06-19 11:34:23 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2012-06-23 08:30:41 -0400
commit966cdc96e15d113da80622bdddd63b461a7492f5 (patch)
treef08f79ca29ddeeba3cd65a3448acd5c9c698d5dc /drivers/mfd/arizona-irq.c
parent3cc72986947501a6a8fd12330e0963b59ed2f964 (diff)
mfd: arizona: Interrupt support
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 support for the interrupt controller on Arizona class devices. Since there are two interrupt domains in the device which share a single /IRQ pin by default we use two regmap IRQ domains with a trivial demux interrupt domain used to distribute the interrupts to the two devices. The devices do support multiple interrupt signals, future work will enable support for using this feature to avoid the demux. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'drivers/mfd/arizona-irq.c')
-rw-r--r--drivers/mfd/arizona-irq.c267
1 files changed, 267 insertions, 0 deletions
diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c
new file mode 100644
index 000000000000..4c7894046a39
--- /dev/null
+++ b/drivers/mfd/arizona-irq.c
@@ -0,0 +1,267 @@
1/*
2 * Arizona interrupt support
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/irq.h>
17#include <linux/irqdomain.h>
18#include <linux/module.h>
19#include <linux/pm_runtime.h>
20#include <linux/regmap.h>
21#include <linux/regulator/consumer.h>
22#include <linux/slab.h>
23
24#include <linux/mfd/arizona/core.h>
25#include <linux/mfd/arizona/registers.h>
26
27#include "arizona.h"
28
29static int arizona_map_irq(struct arizona *arizona, int irq)
30{
31 int ret;
32
33 ret = regmap_irq_get_virq(arizona->aod_irq_chip, irq);
34 if (ret < 0)
35 ret = regmap_irq_get_virq(arizona->irq_chip, irq);
36
37 return ret;
38}
39
40int arizona_request_irq(struct arizona *arizona, int irq, char *name,
41 irq_handler_t handler, void *data)
42{
43 irq = arizona_map_irq(arizona, irq);
44 if (irq < 0)
45 return irq;
46
47 return request_threaded_irq(irq, NULL, handler, IRQF_ONESHOT,
48 name, data);
49}
50EXPORT_SYMBOL_GPL(arizona_request_irq);
51
52void arizona_free_irq(struct arizona *arizona, int irq, void *data)
53{
54 irq = arizona_map_irq(arizona, irq);
55 if (irq < 0)
56 return;
57
58 free_irq(irq, data);
59}
60EXPORT_SYMBOL_GPL(arizona_free_irq);
61
62int arizona_set_irq_wake(struct arizona *arizona, int irq, int on)
63{
64 irq = arizona_map_irq(arizona, irq);
65 if (irq < 0)
66 return irq;
67
68 return irq_set_irq_wake(irq, on);
69}
70EXPORT_SYMBOL_GPL(arizona_set_irq_wake);
71
72static irqreturn_t arizona_boot_done(int irq, void *data)
73{
74 struct arizona *arizona = data;
75
76 dev_dbg(arizona->dev, "Boot done\n");
77
78 return IRQ_HANDLED;
79}
80
81static irqreturn_t arizona_ctrlif_err(int irq, void *data)
82{
83 struct arizona *arizona = data;
84
85 /*
86 * For pretty much all potential sources a register cache sync
87 * won't help, we've just got a software bug somewhere.
88 */
89 dev_err(arizona->dev, "Control interface error\n");
90
91 return IRQ_HANDLED;
92}
93
94static irqreturn_t arizona_irq_thread(int irq, void *data)
95{
96 struct arizona *arizona = data;
97 int i, ret;
98
99 ret = pm_runtime_get_sync(arizona->dev);
100 if (ret < 0) {
101 dev_err(arizona->dev, "Failed to resume device: %d\n", ret);
102 return IRQ_NONE;
103 }
104
105 /* Check both domains */
106 for (i = 0; i < 2; i++)
107 handle_nested_irq(irq_find_mapping(arizona->virq, i));
108
109 pm_runtime_mark_last_busy(arizona->dev);
110 pm_runtime_put_autosuspend(arizona->dev);
111
112 return IRQ_HANDLED;
113}
114
115static void arizona_irq_enable(struct irq_data *data)
116{
117}
118
119static void arizona_irq_disable(struct irq_data *data)
120{
121}
122
123static struct irq_chip arizona_irq_chip = {
124 .name = "arizona",
125 .irq_disable = arizona_irq_disable,
126 .irq_enable = arizona_irq_enable,
127};
128
129static int arizona_irq_map(struct irq_domain *h, unsigned int virq,
130 irq_hw_number_t hw)
131{
132 struct regmap_irq_chip_data *data = h->host_data;
133
134 irq_set_chip_data(virq, data);
135 irq_set_chip_and_handler(virq, &arizona_irq_chip, handle_edge_irq);
136 irq_set_nested_thread(virq, 1);
137
138 /* ARM needs us to explicitly flag the IRQ as valid
139 * and will set them noprobe when we do so. */
140#ifdef CONFIG_ARM
141 set_irq_flags(virq, IRQF_VALID);
142#else
143 irq_set_noprobe(virq);
144#endif
145
146 return 0;
147}
148
149static struct irq_domain_ops arizona_domain_ops = {
150 .map = arizona_irq_map,
151 .xlate = irq_domain_xlate_twocell,
152};
153
154int arizona_irq_init(struct arizona *arizona)
155{
156 int flags = IRQF_ONESHOT;
157 int ret, i;
158 const struct regmap_irq_chip *aod, *irq;
159
160 switch (arizona->type) {
161 case WM5102:
162 aod = &wm5102_aod;
163 irq = &wm5102_irq;
164 break;
165 default:
166 BUG_ON("Unknown Arizona class device" == NULL);
167 return -EINVAL;
168 }
169
170 if (arizona->pdata.irq_active_high) {
171 ret = regmap_update_bits(arizona->regmap, ARIZONA_IRQ_CTRL_1,
172 ARIZONA_IRQ_POL, 0);
173 if (ret != 0) {
174 dev_err(arizona->dev, "Couldn't set IRQ polarity: %d\n",
175 ret);
176 goto err;
177 }
178
179 flags |= IRQF_TRIGGER_HIGH;
180 } else {
181 flags |= IRQF_TRIGGER_LOW;
182 }
183
184 /* Allocate a virtual IRQ domain to distribute to the regmap domains */
185 arizona->virq = irq_domain_add_linear(NULL, 2, &arizona_domain_ops,
186 arizona);
187 if (!arizona->virq) {
188 ret = -EINVAL;
189 goto err;
190 }
191
192 ret = regmap_add_irq_chip(arizona->regmap,
193 irq_create_mapping(arizona->virq, 0),
194 IRQF_ONESHOT, -1, aod,
195 &arizona->aod_irq_chip);
196 if (ret != 0) {
197 dev_err(arizona->dev, "Failed to add AOD IRQs: %d\n", ret);
198 goto err_domain;
199 }
200
201 ret = regmap_add_irq_chip(arizona->regmap,
202 irq_create_mapping(arizona->virq, 1),
203 IRQF_ONESHOT, -1, irq,
204 &arizona->irq_chip);
205 if (ret != 0) {
206 dev_err(arizona->dev, "Failed to add AOD IRQs: %d\n", ret);
207 goto err_aod;
208 }
209
210 /* Make sure the boot done IRQ is unmasked for resumes */
211 i = arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE);
212 ret = request_threaded_irq(i, NULL, arizona_boot_done, IRQF_ONESHOT,
213 "Boot done", arizona);
214 if (ret != 0) {
215 dev_err(arizona->dev, "Failed to request boot done %d: %d\n",
216 arizona->irq, ret);
217 goto err_boot_done;
218 }
219
220 /* Handle control interface errors in the core */
221 i = arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR);
222 ret = request_threaded_irq(i, NULL, arizona_ctrlif_err, IRQF_ONESHOT,
223 "Control interface error", arizona);
224 if (ret != 0) {
225 dev_err(arizona->dev, "Failed to request boot done %d: %d\n",
226 arizona->irq, ret);
227 goto err_ctrlif;
228 }
229
230 ret = request_threaded_irq(arizona->irq, NULL, arizona_irq_thread,
231 flags, "arizona", arizona);
232
233 if (ret != 0) {
234 dev_err(arizona->dev, "Failed to request IRQ %d: %d\n",
235 arizona->irq, ret);
236 goto err_main_irq;
237 }
238
239 return 0;
240
241err_main_irq:
242 free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR), arizona);
243err_ctrlif:
244 free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE), arizona);
245err_boot_done:
246 regmap_del_irq_chip(irq_create_mapping(arizona->virq, 1),
247 arizona->irq_chip);
248err_aod:
249 regmap_del_irq_chip(irq_create_mapping(arizona->virq, 0),
250 arizona->aod_irq_chip);
251err_domain:
252err:
253 return ret;
254}
255
256int arizona_irq_exit(struct arizona *arizona)
257{
258 free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR), arizona);
259 free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE), arizona);
260 regmap_del_irq_chip(irq_create_mapping(arizona->virq, 1),
261 arizona->irq_chip);
262 regmap_del_irq_chip(irq_create_mapping(arizona->virq, 0),
263 arizona->aod_irq_chip);
264 free_irq(arizona->irq, arizona);
265
266 return 0;
267}