diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2011-10-25 04:19:04 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2011-12-12 11:14:06 -0500 |
commit | 8ab30691826fc05efa47c4ffba19b80496bb3a2c (patch) | |
tree | b1c405e1eebfd3127ccb5aba5c71eb547bc59687 /drivers | |
parent | e292b578c9bd587ad8fe230aa0500bde7be3c68a (diff) |
mfd: Convert wm8994 to use generic regmap irq_chip
Factor out the irq_chip implementation, substantially reducing the code
size for the driver.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mfd/Kconfig | 1 | ||||
-rw-r--r-- | drivers/mfd/wm8994-irq.c | 196 |
2 files changed, 34 insertions, 163 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index f1391c21ef26..017f6dbab333 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig | |||
@@ -477,6 +477,7 @@ config MFD_WM8994 | |||
477 | bool "Support Wolfson Microelectronics WM8994" | 477 | bool "Support Wolfson Microelectronics WM8994" |
478 | select MFD_CORE | 478 | select MFD_CORE |
479 | select REGMAP_I2C | 479 | select REGMAP_I2C |
480 | select REGMAP_IRQ | ||
480 | depends on I2C=y && GENERIC_HARDIRQS | 481 | depends on I2C=y && GENERIC_HARDIRQS |
481 | help | 482 | help |
482 | The WM8994 is a highly integrated hi-fi CODEC designed for | 483 | The WM8994 is a highly integrated hi-fi CODEC designed for |
diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c index f9dd6b691258..46b20c445ecf 100644 --- a/drivers/mfd/wm8994-irq.c +++ b/drivers/mfd/wm8994-irq.c | |||
@@ -18,238 +18,127 @@ | |||
18 | #include <linux/irq.h> | 18 | #include <linux/irq.h> |
19 | #include <linux/mfd/core.h> | 19 | #include <linux/mfd/core.h> |
20 | #include <linux/interrupt.h> | 20 | #include <linux/interrupt.h> |
21 | #include <linux/regmap.h> | ||
21 | 22 | ||
22 | #include <linux/mfd/wm8994/core.h> | 23 | #include <linux/mfd/wm8994/core.h> |
23 | #include <linux/mfd/wm8994/registers.h> | 24 | #include <linux/mfd/wm8994/registers.h> |
24 | 25 | ||
25 | #include <linux/delay.h> | 26 | #include <linux/delay.h> |
26 | 27 | ||
27 | struct wm8994_irq_data { | 28 | static struct regmap_irq wm8994_irqs[] = { |
28 | int reg; | ||
29 | int mask; | ||
30 | }; | ||
31 | |||
32 | static struct wm8994_irq_data wm8994_irqs[] = { | ||
33 | [WM8994_IRQ_TEMP_SHUT] = { | 29 | [WM8994_IRQ_TEMP_SHUT] = { |
34 | .reg = 2, | 30 | .reg_offset = 1, |
35 | .mask = WM8994_TEMP_SHUT_EINT, | 31 | .mask = WM8994_TEMP_SHUT_EINT, |
36 | }, | 32 | }, |
37 | [WM8994_IRQ_MIC1_DET] = { | 33 | [WM8994_IRQ_MIC1_DET] = { |
38 | .reg = 2, | 34 | .reg_offset = 1, |
39 | .mask = WM8994_MIC1_DET_EINT, | 35 | .mask = WM8994_MIC1_DET_EINT, |
40 | }, | 36 | }, |
41 | [WM8994_IRQ_MIC1_SHRT] = { | 37 | [WM8994_IRQ_MIC1_SHRT] = { |
42 | .reg = 2, | 38 | .reg_offset = 1, |
43 | .mask = WM8994_MIC1_SHRT_EINT, | 39 | .mask = WM8994_MIC1_SHRT_EINT, |
44 | }, | 40 | }, |
45 | [WM8994_IRQ_MIC2_DET] = { | 41 | [WM8994_IRQ_MIC2_DET] = { |
46 | .reg = 2, | 42 | .reg_offset = 1, |
47 | .mask = WM8994_MIC2_DET_EINT, | 43 | .mask = WM8994_MIC2_DET_EINT, |
48 | }, | 44 | }, |
49 | [WM8994_IRQ_MIC2_SHRT] = { | 45 | [WM8994_IRQ_MIC2_SHRT] = { |
50 | .reg = 2, | 46 | .reg_offset = 1, |
51 | .mask = WM8994_MIC2_SHRT_EINT, | 47 | .mask = WM8994_MIC2_SHRT_EINT, |
52 | }, | 48 | }, |
53 | [WM8994_IRQ_FLL1_LOCK] = { | 49 | [WM8994_IRQ_FLL1_LOCK] = { |
54 | .reg = 2, | 50 | .reg_offset = 1, |
55 | .mask = WM8994_FLL1_LOCK_EINT, | 51 | .mask = WM8994_FLL1_LOCK_EINT, |
56 | }, | 52 | }, |
57 | [WM8994_IRQ_FLL2_LOCK] = { | 53 | [WM8994_IRQ_FLL2_LOCK] = { |
58 | .reg = 2, | 54 | .reg_offset = 1, |
59 | .mask = WM8994_FLL2_LOCK_EINT, | 55 | .mask = WM8994_FLL2_LOCK_EINT, |
60 | }, | 56 | }, |
61 | [WM8994_IRQ_SRC1_LOCK] = { | 57 | [WM8994_IRQ_SRC1_LOCK] = { |
62 | .reg = 2, | 58 | .reg_offset = 1, |
63 | .mask = WM8994_SRC1_LOCK_EINT, | 59 | .mask = WM8994_SRC1_LOCK_EINT, |
64 | }, | 60 | }, |
65 | [WM8994_IRQ_SRC2_LOCK] = { | 61 | [WM8994_IRQ_SRC2_LOCK] = { |
66 | .reg = 2, | 62 | .reg_offset = 1, |
67 | .mask = WM8994_SRC2_LOCK_EINT, | 63 | .mask = WM8994_SRC2_LOCK_EINT, |
68 | }, | 64 | }, |
69 | [WM8994_IRQ_AIF1DRC1_SIG_DET] = { | 65 | [WM8994_IRQ_AIF1DRC1_SIG_DET] = { |
70 | .reg = 2, | 66 | .reg_offset = 1, |
71 | .mask = WM8994_AIF1DRC1_SIG_DET, | 67 | .mask = WM8994_AIF1DRC1_SIG_DET, |
72 | }, | 68 | }, |
73 | [WM8994_IRQ_AIF1DRC2_SIG_DET] = { | 69 | [WM8994_IRQ_AIF1DRC2_SIG_DET] = { |
74 | .reg = 2, | 70 | .reg_offset = 1, |
75 | .mask = WM8994_AIF1DRC2_SIG_DET_EINT, | 71 | .mask = WM8994_AIF1DRC2_SIG_DET_EINT, |
76 | }, | 72 | }, |
77 | [WM8994_IRQ_AIF2DRC_SIG_DET] = { | 73 | [WM8994_IRQ_AIF2DRC_SIG_DET] = { |
78 | .reg = 2, | 74 | .reg_offset = 1, |
79 | .mask = WM8994_AIF2DRC_SIG_DET_EINT, | 75 | .mask = WM8994_AIF2DRC_SIG_DET_EINT, |
80 | }, | 76 | }, |
81 | [WM8994_IRQ_FIFOS_ERR] = { | 77 | [WM8994_IRQ_FIFOS_ERR] = { |
82 | .reg = 2, | 78 | .reg_offset = 1, |
83 | .mask = WM8994_FIFOS_ERR_EINT, | 79 | .mask = WM8994_FIFOS_ERR_EINT, |
84 | }, | 80 | }, |
85 | [WM8994_IRQ_WSEQ_DONE] = { | 81 | [WM8994_IRQ_WSEQ_DONE] = { |
86 | .reg = 2, | 82 | .reg_offset = 1, |
87 | .mask = WM8994_WSEQ_DONE_EINT, | 83 | .mask = WM8994_WSEQ_DONE_EINT, |
88 | }, | 84 | }, |
89 | [WM8994_IRQ_DCS_DONE] = { | 85 | [WM8994_IRQ_DCS_DONE] = { |
90 | .reg = 2, | 86 | .reg_offset = 1, |
91 | .mask = WM8994_DCS_DONE_EINT, | 87 | .mask = WM8994_DCS_DONE_EINT, |
92 | }, | 88 | }, |
93 | [WM8994_IRQ_TEMP_WARN] = { | 89 | [WM8994_IRQ_TEMP_WARN] = { |
94 | .reg = 2, | 90 | .reg_offset = 1, |
95 | .mask = WM8994_TEMP_WARN_EINT, | 91 | .mask = WM8994_TEMP_WARN_EINT, |
96 | }, | 92 | }, |
97 | [WM8994_IRQ_GPIO(1)] = { | 93 | [WM8994_IRQ_GPIO(1)] = { |
98 | .reg = 1, | ||
99 | .mask = WM8994_GP1_EINT, | 94 | .mask = WM8994_GP1_EINT, |
100 | }, | 95 | }, |
101 | [WM8994_IRQ_GPIO(2)] = { | 96 | [WM8994_IRQ_GPIO(2)] = { |
102 | .reg = 1, | ||
103 | .mask = WM8994_GP2_EINT, | 97 | .mask = WM8994_GP2_EINT, |
104 | }, | 98 | }, |
105 | [WM8994_IRQ_GPIO(3)] = { | 99 | [WM8994_IRQ_GPIO(3)] = { |
106 | .reg = 1, | ||
107 | .mask = WM8994_GP3_EINT, | 100 | .mask = WM8994_GP3_EINT, |
108 | }, | 101 | }, |
109 | [WM8994_IRQ_GPIO(4)] = { | 102 | [WM8994_IRQ_GPIO(4)] = { |
110 | .reg = 1, | ||
111 | .mask = WM8994_GP4_EINT, | 103 | .mask = WM8994_GP4_EINT, |
112 | }, | 104 | }, |
113 | [WM8994_IRQ_GPIO(5)] = { | 105 | [WM8994_IRQ_GPIO(5)] = { |
114 | .reg = 1, | ||
115 | .mask = WM8994_GP5_EINT, | 106 | .mask = WM8994_GP5_EINT, |
116 | }, | 107 | }, |
117 | [WM8994_IRQ_GPIO(6)] = { | 108 | [WM8994_IRQ_GPIO(6)] = { |
118 | .reg = 1, | ||
119 | .mask = WM8994_GP6_EINT, | 109 | .mask = WM8994_GP6_EINT, |
120 | }, | 110 | }, |
121 | [WM8994_IRQ_GPIO(7)] = { | 111 | [WM8994_IRQ_GPIO(7)] = { |
122 | .reg = 1, | ||
123 | .mask = WM8994_GP7_EINT, | 112 | .mask = WM8994_GP7_EINT, |
124 | }, | 113 | }, |
125 | [WM8994_IRQ_GPIO(8)] = { | 114 | [WM8994_IRQ_GPIO(8)] = { |
126 | .reg = 1, | ||
127 | .mask = WM8994_GP8_EINT, | 115 | .mask = WM8994_GP8_EINT, |
128 | }, | 116 | }, |
129 | [WM8994_IRQ_GPIO(9)] = { | 117 | [WM8994_IRQ_GPIO(9)] = { |
130 | .reg = 1, | ||
131 | .mask = WM8994_GP8_EINT, | 118 | .mask = WM8994_GP8_EINT, |
132 | }, | 119 | }, |
133 | [WM8994_IRQ_GPIO(10)] = { | 120 | [WM8994_IRQ_GPIO(10)] = { |
134 | .reg = 1, | ||
135 | .mask = WM8994_GP10_EINT, | 121 | .mask = WM8994_GP10_EINT, |
136 | }, | 122 | }, |
137 | [WM8994_IRQ_GPIO(11)] = { | 123 | [WM8994_IRQ_GPIO(11)] = { |
138 | .reg = 1, | ||
139 | .mask = WM8994_GP11_EINT, | 124 | .mask = WM8994_GP11_EINT, |
140 | }, | 125 | }, |
141 | }; | 126 | }; |
142 | 127 | ||
143 | static inline struct wm8994_irq_data *irq_to_wm8994_irq(struct wm8994 *wm8994, | 128 | static struct regmap_irq_chip wm8994_irq_chip = { |
144 | int irq) | 129 | .name = "wm8994", |
145 | { | 130 | .irqs = wm8994_irqs, |
146 | return &wm8994_irqs[irq - wm8994->irq_base]; | 131 | .num_irqs = ARRAY_SIZE(wm8994_irqs), |
147 | } | ||
148 | |||
149 | static void wm8994_irq_lock(struct irq_data *data) | ||
150 | { | ||
151 | struct wm8994 *wm8994 = irq_data_get_irq_chip_data(data); | ||
152 | |||
153 | mutex_lock(&wm8994->irq_lock); | ||
154 | } | ||
155 | |||
156 | static void wm8994_irq_sync_unlock(struct irq_data *data) | ||
157 | { | ||
158 | struct wm8994 *wm8994 = irq_data_get_irq_chip_data(data); | ||
159 | int i; | ||
160 | |||
161 | for (i = 0; i < ARRAY_SIZE(wm8994->irq_masks_cur); i++) { | ||
162 | /* If there's been a change in the mask write it back | ||
163 | * to the hardware. */ | ||
164 | if (wm8994->irq_masks_cur[i] != wm8994->irq_masks_cache[i]) { | ||
165 | wm8994->irq_masks_cache[i] = wm8994->irq_masks_cur[i]; | ||
166 | wm8994_reg_write(wm8994, | ||
167 | WM8994_INTERRUPT_STATUS_1_MASK + i, | ||
168 | wm8994->irq_masks_cur[i]); | ||
169 | } | ||
170 | } | ||
171 | |||
172 | mutex_unlock(&wm8994->irq_lock); | ||
173 | } | ||
174 | |||
175 | static void wm8994_irq_enable(struct irq_data *data) | ||
176 | { | ||
177 | struct wm8994 *wm8994 = irq_data_get_irq_chip_data(data); | ||
178 | struct wm8994_irq_data *irq_data = irq_to_wm8994_irq(wm8994, | ||
179 | data->irq); | ||
180 | |||
181 | wm8994->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask; | ||
182 | } | ||
183 | |||
184 | static void wm8994_irq_disable(struct irq_data *data) | ||
185 | { | ||
186 | struct wm8994 *wm8994 = irq_data_get_irq_chip_data(data); | ||
187 | struct wm8994_irq_data *irq_data = irq_to_wm8994_irq(wm8994, | ||
188 | data->irq); | ||
189 | |||
190 | wm8994->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask; | ||
191 | } | ||
192 | 132 | ||
193 | static struct irq_chip wm8994_irq_chip = { | 133 | .num_regs = 2, |
194 | .name = "wm8994", | 134 | .status_base = WM8994_INTERRUPT_STATUS_1, |
195 | .irq_bus_lock = wm8994_irq_lock, | 135 | .mask_base = WM8994_INTERRUPT_STATUS_1_MASK, |
196 | .irq_bus_sync_unlock = wm8994_irq_sync_unlock, | 136 | .ack_base = WM8994_INTERRUPT_STATUS_1, |
197 | .irq_disable = wm8994_irq_disable, | ||
198 | .irq_enable = wm8994_irq_enable, | ||
199 | }; | 137 | }; |
200 | 138 | ||
201 | /* The processing of the primary interrupt occurs in a thread so that | ||
202 | * we can interact with the device over I2C or SPI. */ | ||
203 | static irqreturn_t wm8994_irq_thread(int irq, void *data) | ||
204 | { | ||
205 | struct wm8994 *wm8994 = data; | ||
206 | unsigned int i; | ||
207 | u16 status[WM8994_NUM_IRQ_REGS]; | ||
208 | int ret; | ||
209 | |||
210 | ret = wm8994_bulk_read(wm8994, WM8994_INTERRUPT_STATUS_1, | ||
211 | WM8994_NUM_IRQ_REGS, status); | ||
212 | if (ret < 0) { | ||
213 | dev_err(wm8994->dev, "Failed to read interrupt status: %d\n", | ||
214 | ret); | ||
215 | return IRQ_NONE; | ||
216 | } | ||
217 | |||
218 | /* Bit swap and apply masking */ | ||
219 | for (i = 0; i < WM8994_NUM_IRQ_REGS; i++) { | ||
220 | status[i] = be16_to_cpu(status[i]); | ||
221 | status[i] &= ~wm8994->irq_masks_cur[i]; | ||
222 | } | ||
223 | |||
224 | /* Ack any unmasked IRQs */ | ||
225 | for (i = 0; i < ARRAY_SIZE(status); i++) { | ||
226 | if (status[i]) | ||
227 | wm8994_reg_write(wm8994, WM8994_INTERRUPT_STATUS_1 + i, | ||
228 | status[i]); | ||
229 | } | ||
230 | |||
231 | /* Report */ | ||
232 | for (i = 0; i < ARRAY_SIZE(wm8994_irqs); i++) { | ||
233 | if (status[wm8994_irqs[i].reg - 1] & wm8994_irqs[i].mask) | ||
234 | handle_nested_irq(wm8994->irq_base + i); | ||
235 | } | ||
236 | |||
237 | return IRQ_HANDLED; | ||
238 | } | ||
239 | |||
240 | int wm8994_irq_init(struct wm8994 *wm8994) | 139 | int wm8994_irq_init(struct wm8994 *wm8994) |
241 | { | 140 | { |
242 | int i, cur_irq, ret; | 141 | int ret; |
243 | |||
244 | mutex_init(&wm8994->irq_lock); | ||
245 | |||
246 | /* Mask the individual interrupt sources */ | ||
247 | for (i = 0; i < ARRAY_SIZE(wm8994->irq_masks_cur); i++) { | ||
248 | wm8994->irq_masks_cur[i] = 0xffff; | ||
249 | wm8994->irq_masks_cache[i] = 0xffff; | ||
250 | wm8994_reg_write(wm8994, WM8994_INTERRUPT_STATUS_1_MASK + i, | ||
251 | 0xffff); | ||
252 | } | ||
253 | 142 | ||
254 | if (!wm8994->irq) { | 143 | if (!wm8994->irq) { |
255 | dev_warn(wm8994->dev, | 144 | dev_warn(wm8994->dev, |
@@ -264,30 +153,12 @@ int wm8994_irq_init(struct wm8994 *wm8994) | |||
264 | return 0; | 153 | return 0; |
265 | } | 154 | } |
266 | 155 | ||
267 | /* Register them with genirq */ | 156 | ret = regmap_add_irq_chip(wm8994->regmap, wm8994->irq, |
268 | for (cur_irq = wm8994->irq_base; | 157 | IRQF_TRIGGER_HIGH | IRQF_ONESHOT, |
269 | cur_irq < ARRAY_SIZE(wm8994_irqs) + wm8994->irq_base; | 158 | wm8994->irq_base, &wm8994_irq_chip, |
270 | cur_irq++) { | 159 | &wm8994->irq_data); |
271 | irq_set_chip_data(cur_irq, wm8994); | ||
272 | irq_set_chip_and_handler(cur_irq, &wm8994_irq_chip, | ||
273 | handle_edge_irq); | ||
274 | irq_set_nested_thread(cur_irq, 1); | ||
275 | |||
276 | /* ARM needs us to explicitly flag the IRQ as valid | ||
277 | * and will set them noprobe when we do so. */ | ||
278 | #ifdef CONFIG_ARM | ||
279 | set_irq_flags(cur_irq, IRQF_VALID); | ||
280 | #else | ||
281 | irq_set_noprobe(cur_irq); | ||
282 | #endif | ||
283 | } | ||
284 | |||
285 | ret = request_threaded_irq(wm8994->irq, NULL, wm8994_irq_thread, | ||
286 | IRQF_TRIGGER_HIGH | IRQF_ONESHOT, | ||
287 | "wm8994", wm8994); | ||
288 | if (ret != 0) { | 160 | if (ret != 0) { |
289 | dev_err(wm8994->dev, "Failed to request IRQ %d: %d\n", | 161 | dev_err(wm8994->dev, "Failed to register IRQ chip: %d\n", ret); |
290 | wm8994->irq, ret); | ||
291 | return ret; | 162 | return ret; |
292 | } | 163 | } |
293 | 164 | ||
@@ -299,6 +170,5 @@ int wm8994_irq_init(struct wm8994 *wm8994) | |||
299 | 170 | ||
300 | void wm8994_irq_exit(struct wm8994 *wm8994) | 171 | void wm8994_irq_exit(struct wm8994 *wm8994) |
301 | { | 172 | { |
302 | if (wm8994->irq) | 173 | regmap_del_irq_chip(wm8994->irq, wm8994->irq_data); |
303 | free_irq(wm8994->irq, wm8994); | ||
304 | } | 174 | } |