Commit | Line | Data |
---|---|---|
f8beab2b MB |
1 | /* |
2 | * regmap based irq_chip | |
3 | * | |
4 | * Copyright 2011 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 | ||
51990e82 | 13 | #include <linux/device.h> |
e1289207 | 14 | #include <linux/export.h> |
f8beab2b | 15 | #include <linux/interrupt.h> |
e1289207 | 16 | #include <linux/irq.h> |
4af8be67 | 17 | #include <linux/irqdomain.h> |
0c00c50b | 18 | #include <linux/pm_runtime.h> |
e1289207 | 19 | #include <linux/regmap.h> |
f8beab2b MB |
20 | #include <linux/slab.h> |
21 | ||
22 | #include "internal.h" | |
23 | ||
24 | struct regmap_irq_chip_data { | |
25 | struct mutex lock; | |
7ac140ec | 26 | struct irq_chip irq_chip; |
f8beab2b MB |
27 | |
28 | struct regmap *map; | |
b026ddbb | 29 | const struct regmap_irq_chip *chip; |
f8beab2b MB |
30 | |
31 | int irq_base; | |
4af8be67 | 32 | struct irq_domain *domain; |
f8beab2b | 33 | |
a43fd50d MB |
34 | int irq; |
35 | int wake_count; | |
36 | ||
a7440eaa | 37 | void *status_reg_buf; |
f8beab2b MB |
38 | unsigned int *status_buf; |
39 | unsigned int *mask_buf; | |
40 | unsigned int *mask_buf_def; | |
a43fd50d | 41 | unsigned int *wake_buf; |
7a78479f LD |
42 | unsigned int *type_buf; |
43 | unsigned int *type_buf_def; | |
022f926a GG |
44 | |
45 | unsigned int irq_reg_stride; | |
7a78479f | 46 | unsigned int type_reg_stride; |
f8beab2b MB |
47 | }; |
48 | ||
49 | static inline const | |
50 | struct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data, | |
51 | int irq) | |
52 | { | |
4af8be67 | 53 | return &data->chip->irqs[irq]; |
f8beab2b MB |
54 | } |
55 | ||
56 | static void regmap_irq_lock(struct irq_data *data) | |
57 | { | |
58 | struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); | |
59 | ||
60 | mutex_lock(&d->lock); | |
61 | } | |
62 | ||
63 | static void regmap_irq_sync_unlock(struct irq_data *data) | |
64 | { | |
65 | struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); | |
56806555 | 66 | struct regmap *map = d->map; |
f8beab2b | 67 | int i, ret; |
16032624 | 68 | u32 reg; |
7b7d1968 | 69 | u32 unmask_offset; |
f8beab2b | 70 | |
0c00c50b MB |
71 | if (d->chip->runtime_pm) { |
72 | ret = pm_runtime_get_sync(map->dev); | |
73 | if (ret < 0) | |
74 | dev_err(map->dev, "IRQ sync failed to resume: %d\n", | |
75 | ret); | |
76 | } | |
77 | ||
f8beab2b MB |
78 | /* |
79 | * If there's been a change in the mask write it back to the | |
80 | * hardware. We rely on the use of the regmap core cache to | |
81 | * suppress pointless writes. | |
82 | */ | |
83 | for (i = 0; i < d->chip->num_regs; i++) { | |
16032624 SW |
84 | reg = d->chip->mask_base + |
85 | (i * map->reg_stride * d->irq_reg_stride); | |
7b7d1968 | 86 | if (d->chip->mask_invert) { |
36ac914b XT |
87 | ret = regmap_update_bits(d->map, reg, |
88 | d->mask_buf_def[i], ~d->mask_buf[i]); | |
7b7d1968 GZ |
89 | } else if (d->chip->unmask_base) { |
90 | /* set mask with mask_base register */ | |
91 | ret = regmap_update_bits(d->map, reg, | |
92 | d->mask_buf_def[i], ~d->mask_buf[i]); | |
93 | if (ret < 0) | |
94 | dev_err(d->map->dev, | |
95 | "Failed to sync unmasks in %x\n", | |
96 | reg); | |
97 | unmask_offset = d->chip->unmask_base - | |
98 | d->chip->mask_base; | |
99 | /* clear mask with unmask_base register */ | |
100 | ret = regmap_update_bits(d->map, | |
101 | reg + unmask_offset, | |
102 | d->mask_buf_def[i], | |
103 | d->mask_buf[i]); | |
104 | } else { | |
36ac914b | 105 | ret = regmap_update_bits(d->map, reg, |
f8beab2b | 106 | d->mask_buf_def[i], d->mask_buf[i]); |
7b7d1968 | 107 | } |
f8beab2b MB |
108 | if (ret != 0) |
109 | dev_err(d->map->dev, "Failed to sync masks in %x\n", | |
16032624 | 110 | reg); |
33be4932 MB |
111 | |
112 | reg = d->chip->wake_base + | |
113 | (i * map->reg_stride * d->irq_reg_stride); | |
114 | if (d->wake_buf) { | |
9442490a MB |
115 | if (d->chip->wake_invert) |
116 | ret = regmap_update_bits(d->map, reg, | |
117 | d->mask_buf_def[i], | |
118 | ~d->wake_buf[i]); | |
119 | else | |
120 | ret = regmap_update_bits(d->map, reg, | |
121 | d->mask_buf_def[i], | |
122 | d->wake_buf[i]); | |
33be4932 MB |
123 | if (ret != 0) |
124 | dev_err(d->map->dev, | |
125 | "Failed to sync wakes in %x: %d\n", | |
126 | reg, ret); | |
127 | } | |
4bd7145b YZ |
128 | |
129 | if (!d->chip->init_ack_masked) | |
130 | continue; | |
131 | /* | |
7043f5fb | 132 | * Ack all the masked interrupts unconditionally, |
4bd7145b YZ |
133 | * OR if there is masked interrupt which hasn't been Acked, |
134 | * it'll be ignored in irq handler, then may introduce irq storm | |
135 | */ | |
d3233433 | 136 | if (d->mask_buf[i] && (d->chip->ack_base || d->chip->use_ack)) { |
4bd7145b YZ |
137 | reg = d->chip->ack_base + |
138 | (i * map->reg_stride * d->irq_reg_stride); | |
a650fdd9 GZ |
139 | /* some chips ack by write 0 */ |
140 | if (d->chip->ack_invert) | |
141 | ret = regmap_write(map, reg, ~d->mask_buf[i]); | |
142 | else | |
143 | ret = regmap_write(map, reg, d->mask_buf[i]); | |
4bd7145b YZ |
144 | if (ret != 0) |
145 | dev_err(d->map->dev, "Failed to ack 0x%x: %d\n", | |
146 | reg, ret); | |
147 | } | |
f8beab2b MB |
148 | } |
149 | ||
7a78479f LD |
150 | for (i = 0; i < d->chip->num_type_reg; i++) { |
151 | if (!d->type_buf_def[i]) | |
152 | continue; | |
153 | reg = d->chip->type_base + | |
154 | (i * map->reg_stride * d->type_reg_stride); | |
155 | if (d->chip->type_invert) | |
156 | ret = regmap_update_bits(d->map, reg, | |
157 | d->type_buf_def[i], ~d->type_buf[i]); | |
158 | else | |
159 | ret = regmap_update_bits(d->map, reg, | |
160 | d->type_buf_def[i], d->type_buf[i]); | |
161 | if (ret != 0) | |
162 | dev_err(d->map->dev, "Failed to sync type in %x\n", | |
163 | reg); | |
164 | } | |
165 | ||
0c00c50b MB |
166 | if (d->chip->runtime_pm) |
167 | pm_runtime_put(map->dev); | |
168 | ||
a43fd50d MB |
169 | /* If we've changed our wakeup count propagate it to the parent */ |
170 | if (d->wake_count < 0) | |
171 | for (i = d->wake_count; i < 0; i++) | |
172 | irq_set_irq_wake(d->irq, 0); | |
173 | else if (d->wake_count > 0) | |
174 | for (i = 0; i < d->wake_count; i++) | |
175 | irq_set_irq_wake(d->irq, 1); | |
176 | ||
177 | d->wake_count = 0; | |
178 | ||
f8beab2b MB |
179 | mutex_unlock(&d->lock); |
180 | } | |
181 | ||
182 | static void regmap_irq_enable(struct irq_data *data) | |
183 | { | |
184 | struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); | |
56806555 | 185 | struct regmap *map = d->map; |
4af8be67 | 186 | const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); |
f8beab2b | 187 | |
f01ee60f | 188 | d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~irq_data->mask; |
f8beab2b MB |
189 | } |
190 | ||
191 | static void regmap_irq_disable(struct irq_data *data) | |
192 | { | |
193 | struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); | |
56806555 | 194 | struct regmap *map = d->map; |
4af8be67 | 195 | const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); |
f8beab2b | 196 | |
f01ee60f | 197 | d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask; |
f8beab2b MB |
198 | } |
199 | ||
7a78479f LD |
200 | static int regmap_irq_set_type(struct irq_data *data, unsigned int type) |
201 | { | |
202 | struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); | |
203 | struct regmap *map = d->map; | |
204 | const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); | |
205 | int reg = irq_data->type_reg_offset / map->reg_stride; | |
206 | ||
207 | if (!(irq_data->type_rising_mask | irq_data->type_falling_mask)) | |
208 | return 0; | |
209 | ||
210 | d->type_buf[reg] &= ~(irq_data->type_falling_mask | | |
211 | irq_data->type_rising_mask); | |
212 | switch (type) { | |
213 | case IRQ_TYPE_EDGE_FALLING: | |
214 | d->type_buf[reg] |= irq_data->type_falling_mask; | |
215 | break; | |
216 | ||
217 | case IRQ_TYPE_EDGE_RISING: | |
218 | d->type_buf[reg] |= irq_data->type_rising_mask; | |
219 | break; | |
220 | ||
221 | case IRQ_TYPE_EDGE_BOTH: | |
222 | d->type_buf[reg] |= (irq_data->type_falling_mask | | |
223 | irq_data->type_rising_mask); | |
224 | break; | |
225 | ||
226 | default: | |
227 | return -EINVAL; | |
228 | } | |
229 | return 0; | |
230 | } | |
231 | ||
a43fd50d MB |
232 | static int regmap_irq_set_wake(struct irq_data *data, unsigned int on) |
233 | { | |
234 | struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); | |
235 | struct regmap *map = d->map; | |
236 | const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); | |
237 | ||
a43fd50d | 238 | if (on) { |
55ac85e9 LD |
239 | if (d->wake_buf) |
240 | d->wake_buf[irq_data->reg_offset / map->reg_stride] | |
241 | &= ~irq_data->mask; | |
a43fd50d MB |
242 | d->wake_count++; |
243 | } else { | |
55ac85e9 LD |
244 | if (d->wake_buf) |
245 | d->wake_buf[irq_data->reg_offset / map->reg_stride] | |
246 | |= irq_data->mask; | |
a43fd50d MB |
247 | d->wake_count--; |
248 | } | |
249 | ||
250 | return 0; | |
251 | } | |
252 | ||
7ac140ec | 253 | static const struct irq_chip regmap_irq_chip = { |
f8beab2b MB |
254 | .irq_bus_lock = regmap_irq_lock, |
255 | .irq_bus_sync_unlock = regmap_irq_sync_unlock, | |
256 | .irq_disable = regmap_irq_disable, | |
257 | .irq_enable = regmap_irq_enable, | |
7a78479f | 258 | .irq_set_type = regmap_irq_set_type, |
a43fd50d | 259 | .irq_set_wake = regmap_irq_set_wake, |
f8beab2b MB |
260 | }; |
261 | ||
262 | static irqreturn_t regmap_irq_thread(int irq, void *d) | |
263 | { | |
264 | struct regmap_irq_chip_data *data = d; | |
b026ddbb | 265 | const struct regmap_irq_chip *chip = data->chip; |
f8beab2b MB |
266 | struct regmap *map = data->map; |
267 | int ret, i; | |
d23511f9 | 268 | bool handled = false; |
16032624 | 269 | u32 reg; |
f8beab2b | 270 | |
ccc12561 LD |
271 | if (chip->handle_pre_irq) |
272 | chip->handle_pre_irq(chip->irq_drv_data); | |
273 | ||
0c00c50b MB |
274 | if (chip->runtime_pm) { |
275 | ret = pm_runtime_get_sync(map->dev); | |
276 | if (ret < 0) { | |
277 | dev_err(map->dev, "IRQ thread failed to resume: %d\n", | |
278 | ret); | |
283189d3 | 279 | pm_runtime_put(map->dev); |
ccc12561 | 280 | goto exit; |
0c00c50b MB |
281 | } |
282 | } | |
283 | ||
a7440eaa MB |
284 | /* |
285 | * Read in the statuses, using a single bulk read if possible | |
286 | * in order to reduce the I/O overheads. | |
287 | */ | |
67921a1a | 288 | if (!map->use_single_read && map->reg_stride == 1 && |
a7440eaa MB |
289 | data->irq_reg_stride == 1) { |
290 | u8 *buf8 = data->status_reg_buf; | |
291 | u16 *buf16 = data->status_reg_buf; | |
292 | u32 *buf32 = data->status_reg_buf; | |
022f926a | 293 | |
a7440eaa MB |
294 | BUG_ON(!data->status_reg_buf); |
295 | ||
296 | ret = regmap_bulk_read(map, chip->status_base, | |
297 | data->status_reg_buf, | |
298 | chip->num_regs); | |
022f926a GG |
299 | if (ret != 0) { |
300 | dev_err(map->dev, "Failed to read IRQ status: %d\n", | |
a7440eaa | 301 | ret); |
ccc12561 | 302 | goto exit; |
f8beab2b | 303 | } |
a7440eaa MB |
304 | |
305 | for (i = 0; i < data->chip->num_regs; i++) { | |
306 | switch (map->format.val_bytes) { | |
307 | case 1: | |
308 | data->status_buf[i] = buf8[i]; | |
309 | break; | |
310 | case 2: | |
311 | data->status_buf[i] = buf16[i]; | |
312 | break; | |
313 | case 4: | |
314 | data->status_buf[i] = buf32[i]; | |
315 | break; | |
316 | default: | |
317 | BUG(); | |
ccc12561 | 318 | goto exit; |
a7440eaa MB |
319 | } |
320 | } | |
321 | ||
322 | } else { | |
323 | for (i = 0; i < data->chip->num_regs; i++) { | |
324 | ret = regmap_read(map, chip->status_base + | |
325 | (i * map->reg_stride | |
326 | * data->irq_reg_stride), | |
327 | &data->status_buf[i]); | |
328 | ||
329 | if (ret != 0) { | |
330 | dev_err(map->dev, | |
331 | "Failed to read IRQ status: %d\n", | |
332 | ret); | |
333 | if (chip->runtime_pm) | |
334 | pm_runtime_put(map->dev); | |
ccc12561 | 335 | goto exit; |
a7440eaa MB |
336 | } |
337 | } | |
bbae92ca | 338 | } |
f8beab2b | 339 | |
bbae92ca MB |
340 | /* |
341 | * Ignore masked IRQs and ack if we need to; we ack early so | |
342 | * there is no race between handling and acknowleding the | |
343 | * interrupt. We assume that typically few of the interrupts | |
344 | * will fire simultaneously so don't worry about overhead from | |
345 | * doing a write per register. | |
346 | */ | |
347 | for (i = 0; i < data->chip->num_regs; i++) { | |
f8beab2b MB |
348 | data->status_buf[i] &= ~data->mask_buf[i]; |
349 | ||
d3233433 | 350 | if (data->status_buf[i] && (chip->ack_base || chip->use_ack)) { |
16032624 SW |
351 | reg = chip->ack_base + |
352 | (i * map->reg_stride * data->irq_reg_stride); | |
353 | ret = regmap_write(map, reg, data->status_buf[i]); | |
f8beab2b MB |
354 | if (ret != 0) |
355 | dev_err(map->dev, "Failed to ack 0x%x: %d\n", | |
16032624 | 356 | reg, ret); |
f8beab2b MB |
357 | } |
358 | } | |
359 | ||
360 | for (i = 0; i < chip->num_irqs; i++) { | |
f01ee60f SW |
361 | if (data->status_buf[chip->irqs[i].reg_offset / |
362 | map->reg_stride] & chip->irqs[i].mask) { | |
4af8be67 | 363 | handle_nested_irq(irq_find_mapping(data->domain, i)); |
d23511f9 | 364 | handled = true; |
f8beab2b MB |
365 | } |
366 | } | |
367 | ||
0c00c50b MB |
368 | if (chip->runtime_pm) |
369 | pm_runtime_put(map->dev); | |
370 | ||
ccc12561 LD |
371 | exit: |
372 | if (chip->handle_post_irq) | |
373 | chip->handle_post_irq(chip->irq_drv_data); | |
374 | ||
d23511f9 MB |
375 | if (handled) |
376 | return IRQ_HANDLED; | |
377 | else | |
378 | return IRQ_NONE; | |
f8beab2b MB |
379 | } |
380 | ||
4af8be67 MB |
381 | static int regmap_irq_map(struct irq_domain *h, unsigned int virq, |
382 | irq_hw_number_t hw) | |
383 | { | |
384 | struct regmap_irq_chip_data *data = h->host_data; | |
385 | ||
386 | irq_set_chip_data(virq, data); | |
81380739 | 387 | irq_set_chip(virq, &data->irq_chip); |
4af8be67 | 388 | irq_set_nested_thread(virq, 1); |
58a53362 | 389 | irq_set_parent(virq, data->irq); |
4af8be67 | 390 | irq_set_noprobe(virq); |
4af8be67 MB |
391 | |
392 | return 0; | |
393 | } | |
394 | ||
77f5f3e9 | 395 | static const struct irq_domain_ops regmap_domain_ops = { |
4af8be67 MB |
396 | .map = regmap_irq_map, |
397 | .xlate = irq_domain_xlate_twocell, | |
398 | }; | |
399 | ||
f8beab2b MB |
400 | /** |
401 | * regmap_add_irq_chip(): Use standard regmap IRQ controller handling | |
402 | * | |
403 | * map: The regmap for the device. | |
404 | * irq: The IRQ the device uses to signal interrupts | |
405 | * irq_flags: The IRQF_ flags to use for the primary interrupt. | |
406 | * chip: Configuration for the interrupt controller. | |
407 | * data: Runtime data structure for the controller, allocated on success | |
408 | * | |
409 | * Returns 0 on success or an errno on failure. | |
410 | * | |
411 | * In order for this to be efficient the chip really should use a | |
412 | * register cache. The chip driver is responsible for restoring the | |
413 | * register values used by the IRQ controller over suspend and resume. | |
414 | */ | |
415 | int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, | |
b026ddbb | 416 | int irq_base, const struct regmap_irq_chip *chip, |
f8beab2b MB |
417 | struct regmap_irq_chip_data **data) |
418 | { | |
419 | struct regmap_irq_chip_data *d; | |
4af8be67 | 420 | int i; |
f8beab2b | 421 | int ret = -ENOMEM; |
16032624 | 422 | u32 reg; |
7b7d1968 | 423 | u32 unmask_offset; |
f8beab2b | 424 | |
e1289207 XL |
425 | if (chip->num_regs <= 0) |
426 | return -EINVAL; | |
427 | ||
f01ee60f SW |
428 | for (i = 0; i < chip->num_irqs; i++) { |
429 | if (chip->irqs[i].reg_offset % map->reg_stride) | |
430 | return -EINVAL; | |
431 | if (chip->irqs[i].reg_offset / map->reg_stride >= | |
432 | chip->num_regs) | |
433 | return -EINVAL; | |
434 | } | |
435 | ||
4af8be67 MB |
436 | if (irq_base) { |
437 | irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0); | |
438 | if (irq_base < 0) { | |
439 | dev_warn(map->dev, "Failed to allocate IRQs: %d\n", | |
440 | irq_base); | |
441 | return irq_base; | |
442 | } | |
f8beab2b MB |
443 | } |
444 | ||
445 | d = kzalloc(sizeof(*d), GFP_KERNEL); | |
446 | if (!d) | |
447 | return -ENOMEM; | |
448 | ||
eeda1bd6 | 449 | d->status_buf = kcalloc(chip->num_regs, sizeof(unsigned int), |
f8beab2b MB |
450 | GFP_KERNEL); |
451 | if (!d->status_buf) | |
452 | goto err_alloc; | |
453 | ||
eeda1bd6 | 454 | d->mask_buf = kcalloc(chip->num_regs, sizeof(unsigned int), |
f8beab2b MB |
455 | GFP_KERNEL); |
456 | if (!d->mask_buf) | |
457 | goto err_alloc; | |
458 | ||
eeda1bd6 | 459 | d->mask_buf_def = kcalloc(chip->num_regs, sizeof(unsigned int), |
f8beab2b MB |
460 | GFP_KERNEL); |
461 | if (!d->mask_buf_def) | |
462 | goto err_alloc; | |
463 | ||
a43fd50d | 464 | if (chip->wake_base) { |
eeda1bd6 | 465 | d->wake_buf = kcalloc(chip->num_regs, sizeof(unsigned int), |
a43fd50d MB |
466 | GFP_KERNEL); |
467 | if (!d->wake_buf) | |
468 | goto err_alloc; | |
469 | } | |
470 | ||
7a78479f LD |
471 | if (chip->num_type_reg) { |
472 | d->type_buf_def = kcalloc(chip->num_type_reg, | |
473 | sizeof(unsigned int), GFP_KERNEL); | |
474 | if (!d->type_buf_def) | |
475 | goto err_alloc; | |
476 | ||
477 | d->type_buf = kcalloc(chip->num_type_reg, sizeof(unsigned int), | |
478 | GFP_KERNEL); | |
479 | if (!d->type_buf) | |
480 | goto err_alloc; | |
481 | } | |
482 | ||
7ac140ec | 483 | d->irq_chip = regmap_irq_chip; |
ca142750 | 484 | d->irq_chip.name = chip->name; |
a43fd50d | 485 | d->irq = irq; |
f8beab2b MB |
486 | d->map = map; |
487 | d->chip = chip; | |
488 | d->irq_base = irq_base; | |
022f926a GG |
489 | |
490 | if (chip->irq_reg_stride) | |
491 | d->irq_reg_stride = chip->irq_reg_stride; | |
492 | else | |
493 | d->irq_reg_stride = 1; | |
494 | ||
7a78479f LD |
495 | if (chip->type_reg_stride) |
496 | d->type_reg_stride = chip->type_reg_stride; | |
497 | else | |
498 | d->type_reg_stride = 1; | |
499 | ||
67921a1a | 500 | if (!map->use_single_read && map->reg_stride == 1 && |
a7440eaa | 501 | d->irq_reg_stride == 1) { |
549e08a0 | 502 | d->status_reg_buf = kmalloc_array(chip->num_regs, |
503 | map->format.val_bytes, | |
504 | GFP_KERNEL); | |
a7440eaa MB |
505 | if (!d->status_reg_buf) |
506 | goto err_alloc; | |
507 | } | |
508 | ||
f8beab2b MB |
509 | mutex_init(&d->lock); |
510 | ||
511 | for (i = 0; i < chip->num_irqs; i++) | |
f01ee60f | 512 | d->mask_buf_def[chip->irqs[i].reg_offset / map->reg_stride] |
f8beab2b MB |
513 | |= chip->irqs[i].mask; |
514 | ||
515 | /* Mask all the interrupts by default */ | |
516 | for (i = 0; i < chip->num_regs; i++) { | |
517 | d->mask_buf[i] = d->mask_buf_def[i]; | |
16032624 SW |
518 | reg = chip->mask_base + |
519 | (i * map->reg_stride * d->irq_reg_stride); | |
36ac914b XT |
520 | if (chip->mask_invert) |
521 | ret = regmap_update_bits(map, reg, | |
522 | d->mask_buf[i], ~d->mask_buf[i]); | |
7b7d1968 GZ |
523 | else if (d->chip->unmask_base) { |
524 | unmask_offset = d->chip->unmask_base - | |
525 | d->chip->mask_base; | |
526 | ret = regmap_update_bits(d->map, | |
527 | reg + unmask_offset, | |
528 | d->mask_buf[i], | |
529 | d->mask_buf[i]); | |
530 | } else | |
36ac914b | 531 | ret = regmap_update_bits(map, reg, |
0eb46ad0 | 532 | d->mask_buf[i], d->mask_buf[i]); |
f8beab2b MB |
533 | if (ret != 0) { |
534 | dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", | |
16032624 | 535 | reg, ret); |
f8beab2b MB |
536 | goto err_alloc; |
537 | } | |
2753e6f8 PZ |
538 | |
539 | if (!chip->init_ack_masked) | |
540 | continue; | |
541 | ||
542 | /* Ack masked but set interrupts */ | |
543 | reg = chip->status_base + | |
544 | (i * map->reg_stride * d->irq_reg_stride); | |
545 | ret = regmap_read(map, reg, &d->status_buf[i]); | |
546 | if (ret != 0) { | |
547 | dev_err(map->dev, "Failed to read IRQ status: %d\n", | |
548 | ret); | |
549 | goto err_alloc; | |
550 | } | |
551 | ||
d3233433 | 552 | if (d->status_buf[i] && (chip->ack_base || chip->use_ack)) { |
2753e6f8 PZ |
553 | reg = chip->ack_base + |
554 | (i * map->reg_stride * d->irq_reg_stride); | |
a650fdd9 GZ |
555 | if (chip->ack_invert) |
556 | ret = regmap_write(map, reg, | |
557 | ~(d->status_buf[i] & d->mask_buf[i])); | |
558 | else | |
559 | ret = regmap_write(map, reg, | |
2753e6f8 PZ |
560 | d->status_buf[i] & d->mask_buf[i]); |
561 | if (ret != 0) { | |
562 | dev_err(map->dev, "Failed to ack 0x%x: %d\n", | |
563 | reg, ret); | |
564 | goto err_alloc; | |
565 | } | |
566 | } | |
f8beab2b MB |
567 | } |
568 | ||
40052ca0 SW |
569 | /* Wake is disabled by default */ |
570 | if (d->wake_buf) { | |
571 | for (i = 0; i < chip->num_regs; i++) { | |
572 | d->wake_buf[i] = d->mask_buf_def[i]; | |
573 | reg = chip->wake_base + | |
574 | (i * map->reg_stride * d->irq_reg_stride); | |
9442490a MB |
575 | |
576 | if (chip->wake_invert) | |
577 | ret = regmap_update_bits(map, reg, | |
578 | d->mask_buf_def[i], | |
579 | 0); | |
580 | else | |
581 | ret = regmap_update_bits(map, reg, | |
582 | d->mask_buf_def[i], | |
583 | d->wake_buf[i]); | |
40052ca0 SW |
584 | if (ret != 0) { |
585 | dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", | |
586 | reg, ret); | |
587 | goto err_alloc; | |
588 | } | |
589 | } | |
590 | } | |
591 | ||
7a78479f LD |
592 | if (chip->num_type_reg) { |
593 | for (i = 0; i < chip->num_irqs; i++) { | |
594 | reg = chip->irqs[i].type_reg_offset / map->reg_stride; | |
595 | d->type_buf_def[reg] |= chip->irqs[i].type_rising_mask | | |
596 | chip->irqs[i].type_falling_mask; | |
597 | } | |
598 | for (i = 0; i < chip->num_type_reg; ++i) { | |
599 | if (!d->type_buf_def[i]) | |
600 | continue; | |
601 | ||
602 | reg = chip->type_base + | |
603 | (i * map->reg_stride * d->type_reg_stride); | |
604 | if (chip->type_invert) | |
605 | ret = regmap_update_bits(map, reg, | |
606 | d->type_buf_def[i], 0xFF); | |
607 | else | |
608 | ret = regmap_update_bits(map, reg, | |
609 | d->type_buf_def[i], 0x0); | |
610 | if (ret != 0) { | |
611 | dev_err(map->dev, | |
612 | "Failed to set type in 0x%x: %x\n", | |
613 | reg, ret); | |
614 | goto err_alloc; | |
615 | } | |
616 | } | |
617 | } | |
618 | ||
4af8be67 MB |
619 | if (irq_base) |
620 | d->domain = irq_domain_add_legacy(map->dev->of_node, | |
621 | chip->num_irqs, irq_base, 0, | |
622 | ®map_domain_ops, d); | |
623 | else | |
624 | d->domain = irq_domain_add_linear(map->dev->of_node, | |
625 | chip->num_irqs, | |
626 | ®map_domain_ops, d); | |
627 | if (!d->domain) { | |
628 | dev_err(map->dev, "Failed to create IRQ domain\n"); | |
629 | ret = -ENOMEM; | |
630 | goto err_alloc; | |
f8beab2b MB |
631 | } |
632 | ||
09cadf6e VR |
633 | ret = request_threaded_irq(irq, NULL, regmap_irq_thread, |
634 | irq_flags | IRQF_ONESHOT, | |
f8beab2b MB |
635 | chip->name, d); |
636 | if (ret != 0) { | |
eed456f9 MB |
637 | dev_err(map->dev, "Failed to request IRQ %d for %s: %d\n", |
638 | irq, chip->name, ret); | |
4af8be67 | 639 | goto err_domain; |
f8beab2b MB |
640 | } |
641 | ||
72a6a5df KK |
642 | *data = d; |
643 | ||
f8beab2b MB |
644 | return 0; |
645 | ||
4af8be67 MB |
646 | err_domain: |
647 | /* Should really dispose of the domain but... */ | |
f8beab2b | 648 | err_alloc: |
7a78479f LD |
649 | kfree(d->type_buf); |
650 | kfree(d->type_buf_def); | |
a43fd50d | 651 | kfree(d->wake_buf); |
f8beab2b MB |
652 | kfree(d->mask_buf_def); |
653 | kfree(d->mask_buf); | |
f8beab2b | 654 | kfree(d->status_buf); |
a7440eaa | 655 | kfree(d->status_reg_buf); |
f8beab2b MB |
656 | kfree(d); |
657 | return ret; | |
658 | } | |
659 | EXPORT_SYMBOL_GPL(regmap_add_irq_chip); | |
660 | ||
661 | /** | |
662 | * regmap_del_irq_chip(): Stop interrupt handling for a regmap IRQ chip | |
663 | * | |
664 | * @irq: Primary IRQ for the device | |
665 | * @d: regmap_irq_chip_data allocated by regmap_add_irq_chip() | |
46189518 LD |
666 | * |
667 | * This function also dispose all mapped irq on chip. | |
f8beab2b MB |
668 | */ |
669 | void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) | |
670 | { | |
46189518 LD |
671 | unsigned int virq; |
672 | int hwirq; | |
673 | ||
f8beab2b MB |
674 | if (!d) |
675 | return; | |
676 | ||
677 | free_irq(irq, d); | |
46189518 LD |
678 | |
679 | /* Dispose all virtual irq from irq domain before removing it */ | |
680 | for (hwirq = 0; hwirq < d->chip->num_irqs; hwirq++) { | |
681 | /* Ignore hwirq if holes in the IRQ list */ | |
682 | if (!d->chip->irqs[hwirq].mask) | |
683 | continue; | |
684 | ||
685 | /* | |
686 | * Find the virtual irq of hwirq on chip and if it is | |
687 | * there then dispose it | |
688 | */ | |
689 | virq = irq_find_mapping(d->domain, hwirq); | |
690 | if (virq) | |
691 | irq_dispose_mapping(virq); | |
692 | } | |
693 | ||
b5ab3e5c | 694 | irq_domain_remove(d->domain); |
7a78479f LD |
695 | kfree(d->type_buf); |
696 | kfree(d->type_buf_def); | |
a43fd50d | 697 | kfree(d->wake_buf); |
f8beab2b MB |
698 | kfree(d->mask_buf_def); |
699 | kfree(d->mask_buf); | |
a7440eaa | 700 | kfree(d->status_reg_buf); |
f8beab2b MB |
701 | kfree(d->status_buf); |
702 | kfree(d); | |
703 | } | |
704 | EXPORT_SYMBOL_GPL(regmap_del_irq_chip); | |
209a6006 | 705 | |
800c3a0e LD |
706 | static void devm_regmap_irq_chip_release(struct device *dev, void *res) |
707 | { | |
708 | struct regmap_irq_chip_data *d = *(struct regmap_irq_chip_data **)res; | |
709 | ||
710 | regmap_del_irq_chip(d->irq, d); | |
711 | } | |
712 | ||
713 | static int devm_regmap_irq_chip_match(struct device *dev, void *res, void *data) | |
714 | ||
715 | { | |
716 | struct regmap_irq_chip_data **r = res; | |
717 | ||
718 | if (!r || !*r) { | |
719 | WARN_ON(!r || !*r); | |
720 | return 0; | |
721 | } | |
722 | return *r == data; | |
723 | } | |
724 | ||
725 | /** | |
726 | * devm_regmap_add_irq_chip(): Resource manager regmap_add_irq_chip() | |
727 | * | |
728 | * @dev: The device pointer on which irq_chip belongs to. | |
729 | * @map: The regmap for the device. | |
730 | * @irq: The IRQ the device uses to signal interrupts | |
731 | * @irq_flags: The IRQF_ flags to use for the primary interrupt. | |
732 | * @chip: Configuration for the interrupt controller. | |
733 | * @data: Runtime data structure for the controller, allocated on success | |
734 | * | |
735 | * Returns 0 on success or an errno on failure. | |
736 | * | |
737 | * The regmap_irq_chip data automatically be released when the device is | |
738 | * unbound. | |
739 | */ | |
740 | int devm_regmap_add_irq_chip(struct device *dev, struct regmap *map, int irq, | |
741 | int irq_flags, int irq_base, | |
742 | const struct regmap_irq_chip *chip, | |
743 | struct regmap_irq_chip_data **data) | |
744 | { | |
745 | struct regmap_irq_chip_data **ptr, *d; | |
746 | int ret; | |
747 | ||
748 | ptr = devres_alloc(devm_regmap_irq_chip_release, sizeof(*ptr), | |
749 | GFP_KERNEL); | |
750 | if (!ptr) | |
751 | return -ENOMEM; | |
752 | ||
753 | ret = regmap_add_irq_chip(map, irq, irq_flags, irq_base, | |
754 | chip, &d); | |
755 | if (ret < 0) { | |
756 | devres_free(ptr); | |
757 | return ret; | |
758 | } | |
759 | ||
760 | *ptr = d; | |
761 | devres_add(dev, ptr); | |
762 | *data = d; | |
763 | return 0; | |
764 | } | |
765 | EXPORT_SYMBOL_GPL(devm_regmap_add_irq_chip); | |
766 | ||
767 | /** | |
768 | * devm_regmap_del_irq_chip(): Resource managed regmap_del_irq_chip() | |
769 | * | |
770 | * @dev: Device for which which resource was allocated. | |
771 | * @irq: Primary IRQ for the device | |
772 | * @d: regmap_irq_chip_data allocated by regmap_add_irq_chip() | |
773 | */ | |
774 | void devm_regmap_del_irq_chip(struct device *dev, int irq, | |
775 | struct regmap_irq_chip_data *data) | |
776 | { | |
777 | int rc; | |
778 | ||
779 | WARN_ON(irq != data->irq); | |
780 | rc = devres_release(dev, devm_regmap_irq_chip_release, | |
781 | devm_regmap_irq_chip_match, data); | |
782 | ||
783 | if (rc != 0) | |
784 | WARN_ON(rc); | |
785 | } | |
786 | EXPORT_SYMBOL_GPL(devm_regmap_del_irq_chip); | |
787 | ||
209a6006 MB |
788 | /** |
789 | * regmap_irq_chip_get_base(): Retrieve interrupt base for a regmap IRQ chip | |
790 | * | |
791 | * Useful for drivers to request their own IRQs. | |
792 | * | |
793 | * @data: regmap_irq controller to operate on. | |
794 | */ | |
795 | int regmap_irq_chip_get_base(struct regmap_irq_chip_data *data) | |
796 | { | |
4af8be67 | 797 | WARN_ON(!data->irq_base); |
209a6006 MB |
798 | return data->irq_base; |
799 | } | |
800 | EXPORT_SYMBOL_GPL(regmap_irq_chip_get_base); | |
4af8be67 MB |
801 | |
802 | /** | |
803 | * regmap_irq_get_virq(): Map an interrupt on a chip to a virtual IRQ | |
804 | * | |
805 | * Useful for drivers to request their own IRQs. | |
806 | * | |
807 | * @data: regmap_irq controller to operate on. | |
808 | * @irq: index of the interrupt requested in the chip IRQs | |
809 | */ | |
810 | int regmap_irq_get_virq(struct regmap_irq_chip_data *data, int irq) | |
811 | { | |
bfd6185d MB |
812 | /* Handle holes in the IRQ list */ |
813 | if (!data->chip->irqs[irq].mask) | |
814 | return -EINVAL; | |
815 | ||
4af8be67 MB |
816 | return irq_create_mapping(data->domain, irq); |
817 | } | |
818 | EXPORT_SYMBOL_GPL(regmap_irq_get_virq); | |
90f790d2 MB |
819 | |
820 | /** | |
821 | * regmap_irq_get_domain(): Retrieve the irq_domain for the chip | |
822 | * | |
823 | * Useful for drivers to request their own IRQs and for integration | |
824 | * with subsystems. For ease of integration NULL is accepted as a | |
825 | * domain, allowing devices to just call this even if no domain is | |
826 | * allocated. | |
827 | * | |
828 | * @data: regmap_irq controller to operate on. | |
829 | */ | |
830 | struct irq_domain *regmap_irq_get_domain(struct regmap_irq_chip_data *data) | |
831 | { | |
832 | if (data) | |
833 | return data->domain; | |
834 | else | |
835 | return NULL; | |
836 | } | |
837 | EXPORT_SYMBOL_GPL(regmap_irq_get_domain); |