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 | |
0c00c50b MB |
271 | if (chip->runtime_pm) { |
272 | ret = pm_runtime_get_sync(map->dev); | |
273 | if (ret < 0) { | |
274 | dev_err(map->dev, "IRQ thread failed to resume: %d\n", | |
275 | ret); | |
283189d3 | 276 | pm_runtime_put(map->dev); |
0c00c50b MB |
277 | return IRQ_NONE; |
278 | } | |
279 | } | |
280 | ||
a7440eaa MB |
281 | /* |
282 | * Read in the statuses, using a single bulk read if possible | |
283 | * in order to reduce the I/O overheads. | |
284 | */ | |
67921a1a | 285 | if (!map->use_single_read && map->reg_stride == 1 && |
a7440eaa MB |
286 | data->irq_reg_stride == 1) { |
287 | u8 *buf8 = data->status_reg_buf; | |
288 | u16 *buf16 = data->status_reg_buf; | |
289 | u32 *buf32 = data->status_reg_buf; | |
022f926a | 290 | |
a7440eaa MB |
291 | BUG_ON(!data->status_reg_buf); |
292 | ||
293 | ret = regmap_bulk_read(map, chip->status_base, | |
294 | data->status_reg_buf, | |
295 | chip->num_regs); | |
022f926a GG |
296 | if (ret != 0) { |
297 | dev_err(map->dev, "Failed to read IRQ status: %d\n", | |
a7440eaa | 298 | ret); |
f8beab2b MB |
299 | return IRQ_NONE; |
300 | } | |
a7440eaa MB |
301 | |
302 | for (i = 0; i < data->chip->num_regs; i++) { | |
303 | switch (map->format.val_bytes) { | |
304 | case 1: | |
305 | data->status_buf[i] = buf8[i]; | |
306 | break; | |
307 | case 2: | |
308 | data->status_buf[i] = buf16[i]; | |
309 | break; | |
310 | case 4: | |
311 | data->status_buf[i] = buf32[i]; | |
312 | break; | |
313 | default: | |
314 | BUG(); | |
315 | return IRQ_NONE; | |
316 | } | |
317 | } | |
318 | ||
319 | } else { | |
320 | for (i = 0; i < data->chip->num_regs; i++) { | |
321 | ret = regmap_read(map, chip->status_base + | |
322 | (i * map->reg_stride | |
323 | * data->irq_reg_stride), | |
324 | &data->status_buf[i]); | |
325 | ||
326 | if (ret != 0) { | |
327 | dev_err(map->dev, | |
328 | "Failed to read IRQ status: %d\n", | |
329 | ret); | |
330 | if (chip->runtime_pm) | |
331 | pm_runtime_put(map->dev); | |
332 | return IRQ_NONE; | |
333 | } | |
334 | } | |
bbae92ca | 335 | } |
f8beab2b | 336 | |
bbae92ca MB |
337 | /* |
338 | * Ignore masked IRQs and ack if we need to; we ack early so | |
339 | * there is no race between handling and acknowleding the | |
340 | * interrupt. We assume that typically few of the interrupts | |
341 | * will fire simultaneously so don't worry about overhead from | |
342 | * doing a write per register. | |
343 | */ | |
344 | for (i = 0; i < data->chip->num_regs; i++) { | |
f8beab2b MB |
345 | data->status_buf[i] &= ~data->mask_buf[i]; |
346 | ||
d3233433 | 347 | if (data->status_buf[i] && (chip->ack_base || chip->use_ack)) { |
16032624 SW |
348 | reg = chip->ack_base + |
349 | (i * map->reg_stride * data->irq_reg_stride); | |
350 | ret = regmap_write(map, reg, data->status_buf[i]); | |
f8beab2b MB |
351 | if (ret != 0) |
352 | dev_err(map->dev, "Failed to ack 0x%x: %d\n", | |
16032624 | 353 | reg, ret); |
f8beab2b MB |
354 | } |
355 | } | |
356 | ||
357 | for (i = 0; i < chip->num_irqs; i++) { | |
f01ee60f SW |
358 | if (data->status_buf[chip->irqs[i].reg_offset / |
359 | map->reg_stride] & chip->irqs[i].mask) { | |
4af8be67 | 360 | handle_nested_irq(irq_find_mapping(data->domain, i)); |
d23511f9 | 361 | handled = true; |
f8beab2b MB |
362 | } |
363 | } | |
364 | ||
0c00c50b MB |
365 | if (chip->runtime_pm) |
366 | pm_runtime_put(map->dev); | |
367 | ||
d23511f9 MB |
368 | if (handled) |
369 | return IRQ_HANDLED; | |
370 | else | |
371 | return IRQ_NONE; | |
f8beab2b MB |
372 | } |
373 | ||
4af8be67 MB |
374 | static int regmap_irq_map(struct irq_domain *h, unsigned int virq, |
375 | irq_hw_number_t hw) | |
376 | { | |
377 | struct regmap_irq_chip_data *data = h->host_data; | |
378 | ||
379 | irq_set_chip_data(virq, data); | |
81380739 | 380 | irq_set_chip(virq, &data->irq_chip); |
4af8be67 | 381 | irq_set_nested_thread(virq, 1); |
58a53362 | 382 | irq_set_parent(virq, data->irq); |
4af8be67 | 383 | irq_set_noprobe(virq); |
4af8be67 MB |
384 | |
385 | return 0; | |
386 | } | |
387 | ||
77f5f3e9 | 388 | static const struct irq_domain_ops regmap_domain_ops = { |
4af8be67 MB |
389 | .map = regmap_irq_map, |
390 | .xlate = irq_domain_xlate_twocell, | |
391 | }; | |
392 | ||
f8beab2b MB |
393 | /** |
394 | * regmap_add_irq_chip(): Use standard regmap IRQ controller handling | |
395 | * | |
396 | * map: The regmap for the device. | |
397 | * irq: The IRQ the device uses to signal interrupts | |
398 | * irq_flags: The IRQF_ flags to use for the primary interrupt. | |
399 | * chip: Configuration for the interrupt controller. | |
400 | * data: Runtime data structure for the controller, allocated on success | |
401 | * | |
402 | * Returns 0 on success or an errno on failure. | |
403 | * | |
404 | * In order for this to be efficient the chip really should use a | |
405 | * register cache. The chip driver is responsible for restoring the | |
406 | * register values used by the IRQ controller over suspend and resume. | |
407 | */ | |
408 | int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, | |
b026ddbb | 409 | int irq_base, const struct regmap_irq_chip *chip, |
f8beab2b MB |
410 | struct regmap_irq_chip_data **data) |
411 | { | |
412 | struct regmap_irq_chip_data *d; | |
4af8be67 | 413 | int i; |
f8beab2b | 414 | int ret = -ENOMEM; |
16032624 | 415 | u32 reg; |
7b7d1968 | 416 | u32 unmask_offset; |
f8beab2b | 417 | |
e1289207 XL |
418 | if (chip->num_regs <= 0) |
419 | return -EINVAL; | |
420 | ||
f01ee60f SW |
421 | for (i = 0; i < chip->num_irqs; i++) { |
422 | if (chip->irqs[i].reg_offset % map->reg_stride) | |
423 | return -EINVAL; | |
424 | if (chip->irqs[i].reg_offset / map->reg_stride >= | |
425 | chip->num_regs) | |
426 | return -EINVAL; | |
427 | } | |
428 | ||
4af8be67 MB |
429 | if (irq_base) { |
430 | irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0); | |
431 | if (irq_base < 0) { | |
432 | dev_warn(map->dev, "Failed to allocate IRQs: %d\n", | |
433 | irq_base); | |
434 | return irq_base; | |
435 | } | |
f8beab2b MB |
436 | } |
437 | ||
438 | d = kzalloc(sizeof(*d), GFP_KERNEL); | |
439 | if (!d) | |
440 | return -ENOMEM; | |
441 | ||
eeda1bd6 | 442 | d->status_buf = kcalloc(chip->num_regs, sizeof(unsigned int), |
f8beab2b MB |
443 | GFP_KERNEL); |
444 | if (!d->status_buf) | |
445 | goto err_alloc; | |
446 | ||
eeda1bd6 | 447 | d->mask_buf = kcalloc(chip->num_regs, sizeof(unsigned int), |
f8beab2b MB |
448 | GFP_KERNEL); |
449 | if (!d->mask_buf) | |
450 | goto err_alloc; | |
451 | ||
eeda1bd6 | 452 | d->mask_buf_def = kcalloc(chip->num_regs, sizeof(unsigned int), |
f8beab2b MB |
453 | GFP_KERNEL); |
454 | if (!d->mask_buf_def) | |
455 | goto err_alloc; | |
456 | ||
a43fd50d | 457 | if (chip->wake_base) { |
eeda1bd6 | 458 | d->wake_buf = kcalloc(chip->num_regs, sizeof(unsigned int), |
a43fd50d MB |
459 | GFP_KERNEL); |
460 | if (!d->wake_buf) | |
461 | goto err_alloc; | |
462 | } | |
463 | ||
7a78479f LD |
464 | if (chip->num_type_reg) { |
465 | d->type_buf_def = kcalloc(chip->num_type_reg, | |
466 | sizeof(unsigned int), GFP_KERNEL); | |
467 | if (!d->type_buf_def) | |
468 | goto err_alloc; | |
469 | ||
470 | d->type_buf = kcalloc(chip->num_type_reg, sizeof(unsigned int), | |
471 | GFP_KERNEL); | |
472 | if (!d->type_buf) | |
473 | goto err_alloc; | |
474 | } | |
475 | ||
7ac140ec | 476 | d->irq_chip = regmap_irq_chip; |
ca142750 | 477 | d->irq_chip.name = chip->name; |
a43fd50d | 478 | d->irq = irq; |
f8beab2b MB |
479 | d->map = map; |
480 | d->chip = chip; | |
481 | d->irq_base = irq_base; | |
022f926a GG |
482 | |
483 | if (chip->irq_reg_stride) | |
484 | d->irq_reg_stride = chip->irq_reg_stride; | |
485 | else | |
486 | d->irq_reg_stride = 1; | |
487 | ||
7a78479f LD |
488 | if (chip->type_reg_stride) |
489 | d->type_reg_stride = chip->type_reg_stride; | |
490 | else | |
491 | d->type_reg_stride = 1; | |
492 | ||
67921a1a | 493 | if (!map->use_single_read && map->reg_stride == 1 && |
a7440eaa | 494 | d->irq_reg_stride == 1) { |
549e08a0 | 495 | d->status_reg_buf = kmalloc_array(chip->num_regs, |
496 | map->format.val_bytes, | |
497 | GFP_KERNEL); | |
a7440eaa MB |
498 | if (!d->status_reg_buf) |
499 | goto err_alloc; | |
500 | } | |
501 | ||
f8beab2b MB |
502 | mutex_init(&d->lock); |
503 | ||
504 | for (i = 0; i < chip->num_irqs; i++) | |
f01ee60f | 505 | d->mask_buf_def[chip->irqs[i].reg_offset / map->reg_stride] |
f8beab2b MB |
506 | |= chip->irqs[i].mask; |
507 | ||
508 | /* Mask all the interrupts by default */ | |
509 | for (i = 0; i < chip->num_regs; i++) { | |
510 | d->mask_buf[i] = d->mask_buf_def[i]; | |
16032624 SW |
511 | reg = chip->mask_base + |
512 | (i * map->reg_stride * d->irq_reg_stride); | |
36ac914b XT |
513 | if (chip->mask_invert) |
514 | ret = regmap_update_bits(map, reg, | |
515 | d->mask_buf[i], ~d->mask_buf[i]); | |
7b7d1968 GZ |
516 | else if (d->chip->unmask_base) { |
517 | unmask_offset = d->chip->unmask_base - | |
518 | d->chip->mask_base; | |
519 | ret = regmap_update_bits(d->map, | |
520 | reg + unmask_offset, | |
521 | d->mask_buf[i], | |
522 | d->mask_buf[i]); | |
523 | } else | |
36ac914b | 524 | ret = regmap_update_bits(map, reg, |
0eb46ad0 | 525 | d->mask_buf[i], d->mask_buf[i]); |
f8beab2b MB |
526 | if (ret != 0) { |
527 | dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", | |
16032624 | 528 | reg, ret); |
f8beab2b MB |
529 | goto err_alloc; |
530 | } | |
2753e6f8 PZ |
531 | |
532 | if (!chip->init_ack_masked) | |
533 | continue; | |
534 | ||
535 | /* Ack masked but set interrupts */ | |
536 | reg = chip->status_base + | |
537 | (i * map->reg_stride * d->irq_reg_stride); | |
538 | ret = regmap_read(map, reg, &d->status_buf[i]); | |
539 | if (ret != 0) { | |
540 | dev_err(map->dev, "Failed to read IRQ status: %d\n", | |
541 | ret); | |
542 | goto err_alloc; | |
543 | } | |
544 | ||
d3233433 | 545 | if (d->status_buf[i] && (chip->ack_base || chip->use_ack)) { |
2753e6f8 PZ |
546 | reg = chip->ack_base + |
547 | (i * map->reg_stride * d->irq_reg_stride); | |
a650fdd9 GZ |
548 | if (chip->ack_invert) |
549 | ret = regmap_write(map, reg, | |
550 | ~(d->status_buf[i] & d->mask_buf[i])); | |
551 | else | |
552 | ret = regmap_write(map, reg, | |
2753e6f8 PZ |
553 | d->status_buf[i] & d->mask_buf[i]); |
554 | if (ret != 0) { | |
555 | dev_err(map->dev, "Failed to ack 0x%x: %d\n", | |
556 | reg, ret); | |
557 | goto err_alloc; | |
558 | } | |
559 | } | |
f8beab2b MB |
560 | } |
561 | ||
40052ca0 SW |
562 | /* Wake is disabled by default */ |
563 | if (d->wake_buf) { | |
564 | for (i = 0; i < chip->num_regs; i++) { | |
565 | d->wake_buf[i] = d->mask_buf_def[i]; | |
566 | reg = chip->wake_base + | |
567 | (i * map->reg_stride * d->irq_reg_stride); | |
9442490a MB |
568 | |
569 | if (chip->wake_invert) | |
570 | ret = regmap_update_bits(map, reg, | |
571 | d->mask_buf_def[i], | |
572 | 0); | |
573 | else | |
574 | ret = regmap_update_bits(map, reg, | |
575 | d->mask_buf_def[i], | |
576 | d->wake_buf[i]); | |
40052ca0 SW |
577 | if (ret != 0) { |
578 | dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", | |
579 | reg, ret); | |
580 | goto err_alloc; | |
581 | } | |
582 | } | |
583 | } | |
584 | ||
7a78479f LD |
585 | if (chip->num_type_reg) { |
586 | for (i = 0; i < chip->num_irqs; i++) { | |
587 | reg = chip->irqs[i].type_reg_offset / map->reg_stride; | |
588 | d->type_buf_def[reg] |= chip->irqs[i].type_rising_mask | | |
589 | chip->irqs[i].type_falling_mask; | |
590 | } | |
591 | for (i = 0; i < chip->num_type_reg; ++i) { | |
592 | if (!d->type_buf_def[i]) | |
593 | continue; | |
594 | ||
595 | reg = chip->type_base + | |
596 | (i * map->reg_stride * d->type_reg_stride); | |
597 | if (chip->type_invert) | |
598 | ret = regmap_update_bits(map, reg, | |
599 | d->type_buf_def[i], 0xFF); | |
600 | else | |
601 | ret = regmap_update_bits(map, reg, | |
602 | d->type_buf_def[i], 0x0); | |
603 | if (ret != 0) { | |
604 | dev_err(map->dev, | |
605 | "Failed to set type in 0x%x: %x\n", | |
606 | reg, ret); | |
607 | goto err_alloc; | |
608 | } | |
609 | } | |
610 | } | |
611 | ||
4af8be67 MB |
612 | if (irq_base) |
613 | d->domain = irq_domain_add_legacy(map->dev->of_node, | |
614 | chip->num_irqs, irq_base, 0, | |
615 | ®map_domain_ops, d); | |
616 | else | |
617 | d->domain = irq_domain_add_linear(map->dev->of_node, | |
618 | chip->num_irqs, | |
619 | ®map_domain_ops, d); | |
620 | if (!d->domain) { | |
621 | dev_err(map->dev, "Failed to create IRQ domain\n"); | |
622 | ret = -ENOMEM; | |
623 | goto err_alloc; | |
f8beab2b MB |
624 | } |
625 | ||
09cadf6e VR |
626 | ret = request_threaded_irq(irq, NULL, regmap_irq_thread, |
627 | irq_flags | IRQF_ONESHOT, | |
f8beab2b MB |
628 | chip->name, d); |
629 | if (ret != 0) { | |
eed456f9 MB |
630 | dev_err(map->dev, "Failed to request IRQ %d for %s: %d\n", |
631 | irq, chip->name, ret); | |
4af8be67 | 632 | goto err_domain; |
f8beab2b MB |
633 | } |
634 | ||
72a6a5df KK |
635 | *data = d; |
636 | ||
f8beab2b MB |
637 | return 0; |
638 | ||
4af8be67 MB |
639 | err_domain: |
640 | /* Should really dispose of the domain but... */ | |
f8beab2b | 641 | err_alloc: |
7a78479f LD |
642 | kfree(d->type_buf); |
643 | kfree(d->type_buf_def); | |
a43fd50d | 644 | kfree(d->wake_buf); |
f8beab2b MB |
645 | kfree(d->mask_buf_def); |
646 | kfree(d->mask_buf); | |
f8beab2b | 647 | kfree(d->status_buf); |
a7440eaa | 648 | kfree(d->status_reg_buf); |
f8beab2b MB |
649 | kfree(d); |
650 | return ret; | |
651 | } | |
652 | EXPORT_SYMBOL_GPL(regmap_add_irq_chip); | |
653 | ||
654 | /** | |
655 | * regmap_del_irq_chip(): Stop interrupt handling for a regmap IRQ chip | |
656 | * | |
657 | * @irq: Primary IRQ for the device | |
658 | * @d: regmap_irq_chip_data allocated by regmap_add_irq_chip() | |
46189518 LD |
659 | * |
660 | * This function also dispose all mapped irq on chip. | |
f8beab2b MB |
661 | */ |
662 | void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) | |
663 | { | |
46189518 LD |
664 | unsigned int virq; |
665 | int hwirq; | |
666 | ||
f8beab2b MB |
667 | if (!d) |
668 | return; | |
669 | ||
670 | free_irq(irq, d); | |
46189518 LD |
671 | |
672 | /* Dispose all virtual irq from irq domain before removing it */ | |
673 | for (hwirq = 0; hwirq < d->chip->num_irqs; hwirq++) { | |
674 | /* Ignore hwirq if holes in the IRQ list */ | |
675 | if (!d->chip->irqs[hwirq].mask) | |
676 | continue; | |
677 | ||
678 | /* | |
679 | * Find the virtual irq of hwirq on chip and if it is | |
680 | * there then dispose it | |
681 | */ | |
682 | virq = irq_find_mapping(d->domain, hwirq); | |
683 | if (virq) | |
684 | irq_dispose_mapping(virq); | |
685 | } | |
686 | ||
b5ab3e5c | 687 | irq_domain_remove(d->domain); |
7a78479f LD |
688 | kfree(d->type_buf); |
689 | kfree(d->type_buf_def); | |
a43fd50d | 690 | kfree(d->wake_buf); |
f8beab2b MB |
691 | kfree(d->mask_buf_def); |
692 | kfree(d->mask_buf); | |
a7440eaa | 693 | kfree(d->status_reg_buf); |
f8beab2b MB |
694 | kfree(d->status_buf); |
695 | kfree(d); | |
696 | } | |
697 | EXPORT_SYMBOL_GPL(regmap_del_irq_chip); | |
209a6006 | 698 | |
800c3a0e LD |
699 | static void devm_regmap_irq_chip_release(struct device *dev, void *res) |
700 | { | |
701 | struct regmap_irq_chip_data *d = *(struct regmap_irq_chip_data **)res; | |
702 | ||
703 | regmap_del_irq_chip(d->irq, d); | |
704 | } | |
705 | ||
706 | static int devm_regmap_irq_chip_match(struct device *dev, void *res, void *data) | |
707 | ||
708 | { | |
709 | struct regmap_irq_chip_data **r = res; | |
710 | ||
711 | if (!r || !*r) { | |
712 | WARN_ON(!r || !*r); | |
713 | return 0; | |
714 | } | |
715 | return *r == data; | |
716 | } | |
717 | ||
718 | /** | |
719 | * devm_regmap_add_irq_chip(): Resource manager regmap_add_irq_chip() | |
720 | * | |
721 | * @dev: The device pointer on which irq_chip belongs to. | |
722 | * @map: The regmap for the device. | |
723 | * @irq: The IRQ the device uses to signal interrupts | |
724 | * @irq_flags: The IRQF_ flags to use for the primary interrupt. | |
725 | * @chip: Configuration for the interrupt controller. | |
726 | * @data: Runtime data structure for the controller, allocated on success | |
727 | * | |
728 | * Returns 0 on success or an errno on failure. | |
729 | * | |
730 | * The regmap_irq_chip data automatically be released when the device is | |
731 | * unbound. | |
732 | */ | |
733 | int devm_regmap_add_irq_chip(struct device *dev, struct regmap *map, int irq, | |
734 | int irq_flags, int irq_base, | |
735 | const struct regmap_irq_chip *chip, | |
736 | struct regmap_irq_chip_data **data) | |
737 | { | |
738 | struct regmap_irq_chip_data **ptr, *d; | |
739 | int ret; | |
740 | ||
741 | ptr = devres_alloc(devm_regmap_irq_chip_release, sizeof(*ptr), | |
742 | GFP_KERNEL); | |
743 | if (!ptr) | |
744 | return -ENOMEM; | |
745 | ||
746 | ret = regmap_add_irq_chip(map, irq, irq_flags, irq_base, | |
747 | chip, &d); | |
748 | if (ret < 0) { | |
749 | devres_free(ptr); | |
750 | return ret; | |
751 | } | |
752 | ||
753 | *ptr = d; | |
754 | devres_add(dev, ptr); | |
755 | *data = d; | |
756 | return 0; | |
757 | } | |
758 | EXPORT_SYMBOL_GPL(devm_regmap_add_irq_chip); | |
759 | ||
760 | /** | |
761 | * devm_regmap_del_irq_chip(): Resource managed regmap_del_irq_chip() | |
762 | * | |
763 | * @dev: Device for which which resource was allocated. | |
764 | * @irq: Primary IRQ for the device | |
765 | * @d: regmap_irq_chip_data allocated by regmap_add_irq_chip() | |
766 | */ | |
767 | void devm_regmap_del_irq_chip(struct device *dev, int irq, | |
768 | struct regmap_irq_chip_data *data) | |
769 | { | |
770 | int rc; | |
771 | ||
772 | WARN_ON(irq != data->irq); | |
773 | rc = devres_release(dev, devm_regmap_irq_chip_release, | |
774 | devm_regmap_irq_chip_match, data); | |
775 | ||
776 | if (rc != 0) | |
777 | WARN_ON(rc); | |
778 | } | |
779 | EXPORT_SYMBOL_GPL(devm_regmap_del_irq_chip); | |
780 | ||
209a6006 MB |
781 | /** |
782 | * regmap_irq_chip_get_base(): Retrieve interrupt base for a regmap IRQ chip | |
783 | * | |
784 | * Useful for drivers to request their own IRQs. | |
785 | * | |
786 | * @data: regmap_irq controller to operate on. | |
787 | */ | |
788 | int regmap_irq_chip_get_base(struct regmap_irq_chip_data *data) | |
789 | { | |
4af8be67 | 790 | WARN_ON(!data->irq_base); |
209a6006 MB |
791 | return data->irq_base; |
792 | } | |
793 | EXPORT_SYMBOL_GPL(regmap_irq_chip_get_base); | |
4af8be67 MB |
794 | |
795 | /** | |
796 | * regmap_irq_get_virq(): Map an interrupt on a chip to a virtual IRQ | |
797 | * | |
798 | * Useful for drivers to request their own IRQs. | |
799 | * | |
800 | * @data: regmap_irq controller to operate on. | |
801 | * @irq: index of the interrupt requested in the chip IRQs | |
802 | */ | |
803 | int regmap_irq_get_virq(struct regmap_irq_chip_data *data, int irq) | |
804 | { | |
bfd6185d MB |
805 | /* Handle holes in the IRQ list */ |
806 | if (!data->chip->irqs[irq].mask) | |
807 | return -EINVAL; | |
808 | ||
4af8be67 MB |
809 | return irq_create_mapping(data->domain, irq); |
810 | } | |
811 | EXPORT_SYMBOL_GPL(regmap_irq_get_virq); | |
90f790d2 MB |
812 | |
813 | /** | |
814 | * regmap_irq_get_domain(): Retrieve the irq_domain for the chip | |
815 | * | |
816 | * Useful for drivers to request their own IRQs and for integration | |
817 | * with subsystems. For ease of integration NULL is accepted as a | |
818 | * domain, allowing devices to just call this even if no domain is | |
819 | * allocated. | |
820 | * | |
821 | * @data: regmap_irq controller to operate on. | |
822 | */ | |
823 | struct irq_domain *regmap_irq_get_domain(struct regmap_irq_chip_data *data) | |
824 | { | |
825 | if (data) | |
826 | return data->domain; | |
827 | else | |
828 | return NULL; | |
829 | } | |
830 | EXPORT_SYMBOL_GPL(regmap_irq_get_domain); |