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 | ||
13 | #include <linux/export.h> | |
51990e82 | 14 | #include <linux/device.h> |
f8beab2b MB |
15 | #include <linux/regmap.h> |
16 | #include <linux/irq.h> | |
17 | #include <linux/interrupt.h> | |
4af8be67 | 18 | #include <linux/irqdomain.h> |
0c00c50b | 19 | #include <linux/pm_runtime.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; |
022f926a GG |
42 | |
43 | unsigned int irq_reg_stride; | |
f8beab2b MB |
44 | }; |
45 | ||
46 | static inline const | |
47 | struct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data, | |
48 | int irq) | |
49 | { | |
4af8be67 | 50 | return &data->chip->irqs[irq]; |
f8beab2b MB |
51 | } |
52 | ||
53 | static void regmap_irq_lock(struct irq_data *data) | |
54 | { | |
55 | struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); | |
56 | ||
57 | mutex_lock(&d->lock); | |
58 | } | |
59 | ||
60 | static void regmap_irq_sync_unlock(struct irq_data *data) | |
61 | { | |
62 | struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); | |
56806555 | 63 | struct regmap *map = d->map; |
f8beab2b | 64 | int i, ret; |
16032624 | 65 | u32 reg; |
f8beab2b | 66 | |
0c00c50b MB |
67 | if (d->chip->runtime_pm) { |
68 | ret = pm_runtime_get_sync(map->dev); | |
69 | if (ret < 0) | |
70 | dev_err(map->dev, "IRQ sync failed to resume: %d\n", | |
71 | ret); | |
72 | } | |
73 | ||
f8beab2b MB |
74 | /* |
75 | * If there's been a change in the mask write it back to the | |
76 | * hardware. We rely on the use of the regmap core cache to | |
77 | * suppress pointless writes. | |
78 | */ | |
79 | for (i = 0; i < d->chip->num_regs; i++) { | |
16032624 SW |
80 | reg = d->chip->mask_base + |
81 | (i * map->reg_stride * d->irq_reg_stride); | |
36ac914b XT |
82 | if (d->chip->mask_invert) |
83 | ret = regmap_update_bits(d->map, reg, | |
84 | d->mask_buf_def[i], ~d->mask_buf[i]); | |
85 | else | |
86 | ret = regmap_update_bits(d->map, reg, | |
f8beab2b MB |
87 | d->mask_buf_def[i], d->mask_buf[i]); |
88 | if (ret != 0) | |
89 | dev_err(d->map->dev, "Failed to sync masks in %x\n", | |
16032624 | 90 | reg); |
33be4932 MB |
91 | |
92 | reg = d->chip->wake_base + | |
93 | (i * map->reg_stride * d->irq_reg_stride); | |
94 | if (d->wake_buf) { | |
9442490a MB |
95 | if (d->chip->wake_invert) |
96 | ret = regmap_update_bits(d->map, reg, | |
97 | d->mask_buf_def[i], | |
98 | ~d->wake_buf[i]); | |
99 | else | |
100 | ret = regmap_update_bits(d->map, reg, | |
101 | d->mask_buf_def[i], | |
102 | d->wake_buf[i]); | |
33be4932 MB |
103 | if (ret != 0) |
104 | dev_err(d->map->dev, | |
105 | "Failed to sync wakes in %x: %d\n", | |
106 | reg, ret); | |
107 | } | |
f8beab2b MB |
108 | } |
109 | ||
0c00c50b MB |
110 | if (d->chip->runtime_pm) |
111 | pm_runtime_put(map->dev); | |
112 | ||
a43fd50d MB |
113 | /* If we've changed our wakeup count propagate it to the parent */ |
114 | if (d->wake_count < 0) | |
115 | for (i = d->wake_count; i < 0; i++) | |
116 | irq_set_irq_wake(d->irq, 0); | |
117 | else if (d->wake_count > 0) | |
118 | for (i = 0; i < d->wake_count; i++) | |
119 | irq_set_irq_wake(d->irq, 1); | |
120 | ||
121 | d->wake_count = 0; | |
122 | ||
f8beab2b MB |
123 | mutex_unlock(&d->lock); |
124 | } | |
125 | ||
126 | static void regmap_irq_enable(struct irq_data *data) | |
127 | { | |
128 | struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); | |
56806555 | 129 | struct regmap *map = d->map; |
4af8be67 | 130 | const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); |
f8beab2b | 131 | |
f01ee60f | 132 | d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~irq_data->mask; |
f8beab2b MB |
133 | } |
134 | ||
135 | static void regmap_irq_disable(struct irq_data *data) | |
136 | { | |
137 | struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); | |
56806555 | 138 | struct regmap *map = d->map; |
4af8be67 | 139 | const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); |
f8beab2b | 140 | |
f01ee60f | 141 | d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask; |
f8beab2b MB |
142 | } |
143 | ||
a43fd50d MB |
144 | static int regmap_irq_set_wake(struct irq_data *data, unsigned int on) |
145 | { | |
146 | struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); | |
147 | struct regmap *map = d->map; | |
148 | const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); | |
149 | ||
a43fd50d | 150 | if (on) { |
55ac85e9 LD |
151 | if (d->wake_buf) |
152 | d->wake_buf[irq_data->reg_offset / map->reg_stride] | |
153 | &= ~irq_data->mask; | |
a43fd50d MB |
154 | d->wake_count++; |
155 | } else { | |
55ac85e9 LD |
156 | if (d->wake_buf) |
157 | d->wake_buf[irq_data->reg_offset / map->reg_stride] | |
158 | |= irq_data->mask; | |
a43fd50d MB |
159 | d->wake_count--; |
160 | } | |
161 | ||
162 | return 0; | |
163 | } | |
164 | ||
7ac140ec | 165 | static const struct irq_chip regmap_irq_chip = { |
f8beab2b MB |
166 | .irq_bus_lock = regmap_irq_lock, |
167 | .irq_bus_sync_unlock = regmap_irq_sync_unlock, | |
168 | .irq_disable = regmap_irq_disable, | |
169 | .irq_enable = regmap_irq_enable, | |
a43fd50d | 170 | .irq_set_wake = regmap_irq_set_wake, |
f8beab2b MB |
171 | }; |
172 | ||
173 | static irqreturn_t regmap_irq_thread(int irq, void *d) | |
174 | { | |
175 | struct regmap_irq_chip_data *data = d; | |
b026ddbb | 176 | const struct regmap_irq_chip *chip = data->chip; |
f8beab2b MB |
177 | struct regmap *map = data->map; |
178 | int ret, i; | |
d23511f9 | 179 | bool handled = false; |
16032624 | 180 | u32 reg; |
f8beab2b | 181 | |
0c00c50b MB |
182 | if (chip->runtime_pm) { |
183 | ret = pm_runtime_get_sync(map->dev); | |
184 | if (ret < 0) { | |
185 | dev_err(map->dev, "IRQ thread failed to resume: %d\n", | |
186 | ret); | |
283189d3 | 187 | pm_runtime_put(map->dev); |
0c00c50b MB |
188 | return IRQ_NONE; |
189 | } | |
190 | } | |
191 | ||
a7440eaa MB |
192 | /* |
193 | * Read in the statuses, using a single bulk read if possible | |
194 | * in order to reduce the I/O overheads. | |
195 | */ | |
196 | if (!map->use_single_rw && map->reg_stride == 1 && | |
197 | data->irq_reg_stride == 1) { | |
198 | u8 *buf8 = data->status_reg_buf; | |
199 | u16 *buf16 = data->status_reg_buf; | |
200 | u32 *buf32 = data->status_reg_buf; | |
022f926a | 201 | |
a7440eaa MB |
202 | BUG_ON(!data->status_reg_buf); |
203 | ||
204 | ret = regmap_bulk_read(map, chip->status_base, | |
205 | data->status_reg_buf, | |
206 | chip->num_regs); | |
022f926a GG |
207 | if (ret != 0) { |
208 | dev_err(map->dev, "Failed to read IRQ status: %d\n", | |
a7440eaa | 209 | ret); |
f8beab2b MB |
210 | return IRQ_NONE; |
211 | } | |
a7440eaa MB |
212 | |
213 | for (i = 0; i < data->chip->num_regs; i++) { | |
214 | switch (map->format.val_bytes) { | |
215 | case 1: | |
216 | data->status_buf[i] = buf8[i]; | |
217 | break; | |
218 | case 2: | |
219 | data->status_buf[i] = buf16[i]; | |
220 | break; | |
221 | case 4: | |
222 | data->status_buf[i] = buf32[i]; | |
223 | break; | |
224 | default: | |
225 | BUG(); | |
226 | return IRQ_NONE; | |
227 | } | |
228 | } | |
229 | ||
230 | } else { | |
231 | for (i = 0; i < data->chip->num_regs; i++) { | |
232 | ret = regmap_read(map, chip->status_base + | |
233 | (i * map->reg_stride | |
234 | * data->irq_reg_stride), | |
235 | &data->status_buf[i]); | |
236 | ||
237 | if (ret != 0) { | |
238 | dev_err(map->dev, | |
239 | "Failed to read IRQ status: %d\n", | |
240 | ret); | |
241 | if (chip->runtime_pm) | |
242 | pm_runtime_put(map->dev); | |
243 | return IRQ_NONE; | |
244 | } | |
245 | } | |
bbae92ca | 246 | } |
f8beab2b | 247 | |
bbae92ca MB |
248 | /* |
249 | * Ignore masked IRQs and ack if we need to; we ack early so | |
250 | * there is no race between handling and acknowleding the | |
251 | * interrupt. We assume that typically few of the interrupts | |
252 | * will fire simultaneously so don't worry about overhead from | |
253 | * doing a write per register. | |
254 | */ | |
255 | for (i = 0; i < data->chip->num_regs; i++) { | |
f8beab2b MB |
256 | data->status_buf[i] &= ~data->mask_buf[i]; |
257 | ||
258 | if (data->status_buf[i] && chip->ack_base) { | |
16032624 SW |
259 | reg = chip->ack_base + |
260 | (i * map->reg_stride * data->irq_reg_stride); | |
261 | ret = regmap_write(map, reg, data->status_buf[i]); | |
f8beab2b MB |
262 | if (ret != 0) |
263 | dev_err(map->dev, "Failed to ack 0x%x: %d\n", | |
16032624 | 264 | reg, ret); |
f8beab2b MB |
265 | } |
266 | } | |
267 | ||
268 | for (i = 0; i < chip->num_irqs; i++) { | |
f01ee60f SW |
269 | if (data->status_buf[chip->irqs[i].reg_offset / |
270 | map->reg_stride] & chip->irqs[i].mask) { | |
4af8be67 | 271 | handle_nested_irq(irq_find_mapping(data->domain, i)); |
d23511f9 | 272 | handled = true; |
f8beab2b MB |
273 | } |
274 | } | |
275 | ||
0c00c50b MB |
276 | if (chip->runtime_pm) |
277 | pm_runtime_put(map->dev); | |
278 | ||
d23511f9 MB |
279 | if (handled) |
280 | return IRQ_HANDLED; | |
281 | else | |
282 | return IRQ_NONE; | |
f8beab2b MB |
283 | } |
284 | ||
4af8be67 MB |
285 | static int regmap_irq_map(struct irq_domain *h, unsigned int virq, |
286 | irq_hw_number_t hw) | |
287 | { | |
288 | struct regmap_irq_chip_data *data = h->host_data; | |
289 | ||
290 | irq_set_chip_data(virq, data); | |
81380739 | 291 | irq_set_chip(virq, &data->irq_chip); |
4af8be67 MB |
292 | irq_set_nested_thread(virq, 1); |
293 | ||
294 | /* ARM needs us to explicitly flag the IRQ as valid | |
295 | * and will set them noprobe when we do so. */ | |
296 | #ifdef CONFIG_ARM | |
297 | set_irq_flags(virq, IRQF_VALID); | |
298 | #else | |
299 | irq_set_noprobe(virq); | |
300 | #endif | |
301 | ||
302 | return 0; | |
303 | } | |
304 | ||
305 | static struct irq_domain_ops regmap_domain_ops = { | |
306 | .map = regmap_irq_map, | |
307 | .xlate = irq_domain_xlate_twocell, | |
308 | }; | |
309 | ||
f8beab2b MB |
310 | /** |
311 | * regmap_add_irq_chip(): Use standard regmap IRQ controller handling | |
312 | * | |
313 | * map: The regmap for the device. | |
314 | * irq: The IRQ the device uses to signal interrupts | |
315 | * irq_flags: The IRQF_ flags to use for the primary interrupt. | |
316 | * chip: Configuration for the interrupt controller. | |
317 | * data: Runtime data structure for the controller, allocated on success | |
318 | * | |
319 | * Returns 0 on success or an errno on failure. | |
320 | * | |
321 | * In order for this to be efficient the chip really should use a | |
322 | * register cache. The chip driver is responsible for restoring the | |
323 | * register values used by the IRQ controller over suspend and resume. | |
324 | */ | |
325 | int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, | |
b026ddbb | 326 | int irq_base, const struct regmap_irq_chip *chip, |
f8beab2b MB |
327 | struct regmap_irq_chip_data **data) |
328 | { | |
329 | struct regmap_irq_chip_data *d; | |
4af8be67 | 330 | int i; |
f8beab2b | 331 | int ret = -ENOMEM; |
16032624 | 332 | u32 reg; |
f8beab2b | 333 | |
f01ee60f SW |
334 | for (i = 0; i < chip->num_irqs; i++) { |
335 | if (chip->irqs[i].reg_offset % map->reg_stride) | |
336 | return -EINVAL; | |
337 | if (chip->irqs[i].reg_offset / map->reg_stride >= | |
338 | chip->num_regs) | |
339 | return -EINVAL; | |
340 | } | |
341 | ||
4af8be67 MB |
342 | if (irq_base) { |
343 | irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0); | |
344 | if (irq_base < 0) { | |
345 | dev_warn(map->dev, "Failed to allocate IRQs: %d\n", | |
346 | irq_base); | |
347 | return irq_base; | |
348 | } | |
f8beab2b MB |
349 | } |
350 | ||
351 | d = kzalloc(sizeof(*d), GFP_KERNEL); | |
352 | if (!d) | |
353 | return -ENOMEM; | |
354 | ||
2431d0a1 MB |
355 | *data = d; |
356 | ||
f8beab2b MB |
357 | d->status_buf = kzalloc(sizeof(unsigned int) * chip->num_regs, |
358 | GFP_KERNEL); | |
359 | if (!d->status_buf) | |
360 | goto err_alloc; | |
361 | ||
f8beab2b MB |
362 | d->mask_buf = kzalloc(sizeof(unsigned int) * chip->num_regs, |
363 | GFP_KERNEL); | |
364 | if (!d->mask_buf) | |
365 | goto err_alloc; | |
366 | ||
367 | d->mask_buf_def = kzalloc(sizeof(unsigned int) * chip->num_regs, | |
368 | GFP_KERNEL); | |
369 | if (!d->mask_buf_def) | |
370 | goto err_alloc; | |
371 | ||
a43fd50d MB |
372 | if (chip->wake_base) { |
373 | d->wake_buf = kzalloc(sizeof(unsigned int) * chip->num_regs, | |
374 | GFP_KERNEL); | |
375 | if (!d->wake_buf) | |
376 | goto err_alloc; | |
377 | } | |
378 | ||
7ac140ec | 379 | d->irq_chip = regmap_irq_chip; |
ca142750 | 380 | d->irq_chip.name = chip->name; |
a43fd50d | 381 | d->irq = irq; |
f8beab2b MB |
382 | d->map = map; |
383 | d->chip = chip; | |
384 | d->irq_base = irq_base; | |
022f926a GG |
385 | |
386 | if (chip->irq_reg_stride) | |
387 | d->irq_reg_stride = chip->irq_reg_stride; | |
388 | else | |
389 | d->irq_reg_stride = 1; | |
390 | ||
a7440eaa MB |
391 | if (!map->use_single_rw && map->reg_stride == 1 && |
392 | d->irq_reg_stride == 1) { | |
393 | d->status_reg_buf = kmalloc(map->format.val_bytes * | |
394 | chip->num_regs, GFP_KERNEL); | |
395 | if (!d->status_reg_buf) | |
396 | goto err_alloc; | |
397 | } | |
398 | ||
f8beab2b MB |
399 | mutex_init(&d->lock); |
400 | ||
401 | for (i = 0; i < chip->num_irqs; i++) | |
f01ee60f | 402 | d->mask_buf_def[chip->irqs[i].reg_offset / map->reg_stride] |
f8beab2b MB |
403 | |= chip->irqs[i].mask; |
404 | ||
405 | /* Mask all the interrupts by default */ | |
406 | for (i = 0; i < chip->num_regs; i++) { | |
407 | d->mask_buf[i] = d->mask_buf_def[i]; | |
16032624 SW |
408 | reg = chip->mask_base + |
409 | (i * map->reg_stride * d->irq_reg_stride); | |
36ac914b XT |
410 | if (chip->mask_invert) |
411 | ret = regmap_update_bits(map, reg, | |
412 | d->mask_buf[i], ~d->mask_buf[i]); | |
413 | else | |
414 | ret = regmap_update_bits(map, reg, | |
0eb46ad0 | 415 | d->mask_buf[i], d->mask_buf[i]); |
f8beab2b MB |
416 | if (ret != 0) { |
417 | dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", | |
16032624 | 418 | reg, ret); |
f8beab2b MB |
419 | goto err_alloc; |
420 | } | |
421 | } | |
422 | ||
40052ca0 SW |
423 | /* Wake is disabled by default */ |
424 | if (d->wake_buf) { | |
425 | for (i = 0; i < chip->num_regs; i++) { | |
426 | d->wake_buf[i] = d->mask_buf_def[i]; | |
427 | reg = chip->wake_base + | |
428 | (i * map->reg_stride * d->irq_reg_stride); | |
9442490a MB |
429 | |
430 | if (chip->wake_invert) | |
431 | ret = regmap_update_bits(map, reg, | |
432 | d->mask_buf_def[i], | |
433 | 0); | |
434 | else | |
435 | ret = regmap_update_bits(map, reg, | |
436 | d->mask_buf_def[i], | |
437 | d->wake_buf[i]); | |
40052ca0 SW |
438 | if (ret != 0) { |
439 | dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", | |
440 | reg, ret); | |
441 | goto err_alloc; | |
442 | } | |
443 | } | |
444 | } | |
445 | ||
4af8be67 MB |
446 | if (irq_base) |
447 | d->domain = irq_domain_add_legacy(map->dev->of_node, | |
448 | chip->num_irqs, irq_base, 0, | |
449 | ®map_domain_ops, d); | |
450 | else | |
451 | d->domain = irq_domain_add_linear(map->dev->of_node, | |
452 | chip->num_irqs, | |
453 | ®map_domain_ops, d); | |
454 | if (!d->domain) { | |
455 | dev_err(map->dev, "Failed to create IRQ domain\n"); | |
456 | ret = -ENOMEM; | |
457 | goto err_alloc; | |
f8beab2b MB |
458 | } |
459 | ||
460 | ret = request_threaded_irq(irq, NULL, regmap_irq_thread, irq_flags, | |
461 | chip->name, d); | |
462 | if (ret != 0) { | |
eed456f9 MB |
463 | dev_err(map->dev, "Failed to request IRQ %d for %s: %d\n", |
464 | irq, chip->name, ret); | |
4af8be67 | 465 | goto err_domain; |
f8beab2b MB |
466 | } |
467 | ||
468 | return 0; | |
469 | ||
4af8be67 MB |
470 | err_domain: |
471 | /* Should really dispose of the domain but... */ | |
f8beab2b | 472 | err_alloc: |
a43fd50d | 473 | kfree(d->wake_buf); |
f8beab2b MB |
474 | kfree(d->mask_buf_def); |
475 | kfree(d->mask_buf); | |
f8beab2b | 476 | kfree(d->status_buf); |
a7440eaa | 477 | kfree(d->status_reg_buf); |
f8beab2b MB |
478 | kfree(d); |
479 | return ret; | |
480 | } | |
481 | EXPORT_SYMBOL_GPL(regmap_add_irq_chip); | |
482 | ||
483 | /** | |
484 | * regmap_del_irq_chip(): Stop interrupt handling for a regmap IRQ chip | |
485 | * | |
486 | * @irq: Primary IRQ for the device | |
487 | * @d: regmap_irq_chip_data allocated by regmap_add_irq_chip() | |
488 | */ | |
489 | void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) | |
490 | { | |
491 | if (!d) | |
492 | return; | |
493 | ||
494 | free_irq(irq, d); | |
4af8be67 | 495 | /* We should unmap the domain but... */ |
a43fd50d | 496 | kfree(d->wake_buf); |
f8beab2b MB |
497 | kfree(d->mask_buf_def); |
498 | kfree(d->mask_buf); | |
a7440eaa | 499 | kfree(d->status_reg_buf); |
f8beab2b MB |
500 | kfree(d->status_buf); |
501 | kfree(d); | |
502 | } | |
503 | EXPORT_SYMBOL_GPL(regmap_del_irq_chip); | |
209a6006 MB |
504 | |
505 | /** | |
506 | * regmap_irq_chip_get_base(): Retrieve interrupt base for a regmap IRQ chip | |
507 | * | |
508 | * Useful for drivers to request their own IRQs. | |
509 | * | |
510 | * @data: regmap_irq controller to operate on. | |
511 | */ | |
512 | int regmap_irq_chip_get_base(struct regmap_irq_chip_data *data) | |
513 | { | |
4af8be67 | 514 | WARN_ON(!data->irq_base); |
209a6006 MB |
515 | return data->irq_base; |
516 | } | |
517 | EXPORT_SYMBOL_GPL(regmap_irq_chip_get_base); | |
4af8be67 MB |
518 | |
519 | /** | |
520 | * regmap_irq_get_virq(): Map an interrupt on a chip to a virtual IRQ | |
521 | * | |
522 | * Useful for drivers to request their own IRQs. | |
523 | * | |
524 | * @data: regmap_irq controller to operate on. | |
525 | * @irq: index of the interrupt requested in the chip IRQs | |
526 | */ | |
527 | int regmap_irq_get_virq(struct regmap_irq_chip_data *data, int irq) | |
528 | { | |
bfd6185d MB |
529 | /* Handle holes in the IRQ list */ |
530 | if (!data->chip->irqs[irq].mask) | |
531 | return -EINVAL; | |
532 | ||
4af8be67 MB |
533 | return irq_create_mapping(data->domain, irq); |
534 | } | |
535 | EXPORT_SYMBOL_GPL(regmap_irq_get_virq); | |
90f790d2 MB |
536 | |
537 | /** | |
538 | * regmap_irq_get_domain(): Retrieve the irq_domain for the chip | |
539 | * | |
540 | * Useful for drivers to request their own IRQs and for integration | |
541 | * with subsystems. For ease of integration NULL is accepted as a | |
542 | * domain, allowing devices to just call this even if no domain is | |
543 | * allocated. | |
544 | * | |
545 | * @data: regmap_irq controller to operate on. | |
546 | */ | |
547 | struct irq_domain *regmap_irq_get_domain(struct regmap_irq_chip_data *data) | |
548 | { | |
549 | if (data) | |
550 | return data->domain; | |
551 | else | |
552 | return NULL; | |
553 | } | |
554 | EXPORT_SYMBOL_GPL(regmap_irq_get_domain); |