diff options
Diffstat (limited to 'kernel/irq/generic-chip.c')
-rw-r--r-- | kernel/irq/generic-chip.c | 187 |
1 files changed, 181 insertions, 6 deletions
diff --git a/kernel/irq/generic-chip.c b/kernel/irq/generic-chip.c index 3deb3333d53e..8743d62fded7 100644 --- a/kernel/irq/generic-chip.c +++ b/kernel/irq/generic-chip.c | |||
@@ -7,6 +7,7 @@ | |||
7 | #include <linux/irq.h> | 7 | #include <linux/irq.h> |
8 | #include <linux/slab.h> | 8 | #include <linux/slab.h> |
9 | #include <linux/export.h> | 9 | #include <linux/export.h> |
10 | #include <linux/irqdomain.h> | ||
10 | #include <linux/interrupt.h> | 11 | #include <linux/interrupt.h> |
11 | #include <linux/kernel_stat.h> | 12 | #include <linux/kernel_stat.h> |
12 | #include <linux/syscore_ops.h> | 13 | #include <linux/syscore_ops.h> |
@@ -244,6 +245,90 @@ irq_gc_init_mask_cache(struct irq_chip_generic *gc, enum irq_gc_flags flags) | |||
244 | } | 245 | } |
245 | } | 246 | } |
246 | 247 | ||
248 | /** | ||
249 | * irq_alloc_domain_generic_chip - Allocate generic chips for an irq domain | ||
250 | * @d: irq domain for which to allocate chips | ||
251 | * @irqs_per_chip: Number of interrupts each chip handles | ||
252 | * @num_ct: Number of irq_chip_type instances associated with this | ||
253 | * @name: Name of the irq chip | ||
254 | * @handler: Default flow handler associated with these chips | ||
255 | * @clr: IRQ_* bits to clear in the mapping function | ||
256 | * @set: IRQ_* bits to set in the mapping function | ||
257 | */ | ||
258 | int irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip, | ||
259 | int num_ct, const char *name, | ||
260 | irq_flow_handler_t handler, | ||
261 | unsigned int clr, unsigned int set, | ||
262 | enum irq_gc_flags gcflags) | ||
263 | { | ||
264 | struct irq_domain_chip_generic *dgc; | ||
265 | struct irq_chip_generic *gc; | ||
266 | int numchips, sz, i; | ||
267 | unsigned long flags; | ||
268 | void *tmp; | ||
269 | |||
270 | if (d->gc) | ||
271 | return -EBUSY; | ||
272 | |||
273 | if (d->revmap_type != IRQ_DOMAIN_MAP_LINEAR) | ||
274 | return -EINVAL; | ||
275 | |||
276 | numchips = d->revmap_data.linear.size / irqs_per_chip; | ||
277 | if (!numchips) | ||
278 | return -EINVAL; | ||
279 | |||
280 | /* Allocate a pointer, generic chip and chiptypes for each chip */ | ||
281 | sz = sizeof(*dgc) + numchips * sizeof(gc); | ||
282 | sz += numchips * (sizeof(*gc) + num_ct * sizeof(struct irq_chip_type)); | ||
283 | |||
284 | tmp = dgc = kzalloc(sz, GFP_KERNEL); | ||
285 | if (!dgc) | ||
286 | return -ENOMEM; | ||
287 | dgc->irqs_per_chip = irqs_per_chip; | ||
288 | dgc->num_chips = numchips; | ||
289 | dgc->irq_flags_to_set = set; | ||
290 | dgc->irq_flags_to_clear = clr; | ||
291 | dgc->gc_flags = gcflags; | ||
292 | d->gc = dgc; | ||
293 | |||
294 | /* Calc pointer to the first generic chip */ | ||
295 | tmp += sizeof(*dgc) + numchips * sizeof(gc); | ||
296 | for (i = 0; i < numchips; i++) { | ||
297 | /* Store the pointer to the generic chip */ | ||
298 | dgc->gc[i] = gc = tmp; | ||
299 | irq_init_generic_chip(gc, name, num_ct, i * irqs_per_chip, | ||
300 | NULL, handler); | ||
301 | gc->domain = d; | ||
302 | raw_spin_lock_irqsave(&gc_lock, flags); | ||
303 | list_add_tail(&gc->list, &gc_list); | ||
304 | raw_spin_unlock_irqrestore(&gc_lock, flags); | ||
305 | /* Calc pointer to the next generic chip */ | ||
306 | tmp += sizeof(*gc) + num_ct * sizeof(struct irq_chip_type); | ||
307 | } | ||
308 | return 0; | ||
309 | } | ||
310 | EXPORT_SYMBOL_GPL(irq_alloc_domain_generic_chips); | ||
311 | |||
312 | /** | ||
313 | * irq_get_domain_generic_chip - Get a pointer to the generic chip of a hw_irq | ||
314 | * @d: irq domain pointer | ||
315 | * @hw_irq: Hardware interrupt number | ||
316 | */ | ||
317 | struct irq_chip_generic * | ||
318 | irq_get_domain_generic_chip(struct irq_domain *d, unsigned int hw_irq) | ||
319 | { | ||
320 | struct irq_domain_chip_generic *dgc = d->gc; | ||
321 | int idx; | ||
322 | |||
323 | if (!dgc) | ||
324 | return NULL; | ||
325 | idx = hw_irq / dgc->irqs_per_chip; | ||
326 | if (idx >= dgc->num_chips) | ||
327 | return NULL; | ||
328 | return dgc->gc[idx]; | ||
329 | } | ||
330 | EXPORT_SYMBOL_GPL(irq_get_domain_generic_chip); | ||
331 | |||
247 | /* | 332 | /* |
248 | * Separate lockdep class for interrupt chip which can nest irq_desc | 333 | * Separate lockdep class for interrupt chip which can nest irq_desc |
249 | * lock. | 334 | * lock. |
@@ -251,6 +336,66 @@ irq_gc_init_mask_cache(struct irq_chip_generic *gc, enum irq_gc_flags flags) | |||
251 | static struct lock_class_key irq_nested_lock_class; | 336 | static struct lock_class_key irq_nested_lock_class; |
252 | 337 | ||
253 | /** | 338 | /** |
339 | * irq_map_generic_chip - Map a generic chip for an irq domain | ||
340 | */ | ||
341 | static int irq_map_generic_chip(struct irq_domain *d, unsigned int virq, | ||
342 | irq_hw_number_t hw_irq) | ||
343 | { | ||
344 | struct irq_data *data = irq_get_irq_data(virq); | ||
345 | struct irq_domain_chip_generic *dgc = d->gc; | ||
346 | struct irq_chip_generic *gc; | ||
347 | struct irq_chip_type *ct; | ||
348 | struct irq_chip *chip; | ||
349 | unsigned long flags; | ||
350 | int idx; | ||
351 | |||
352 | if (!d->gc) | ||
353 | return -ENODEV; | ||
354 | |||
355 | idx = hw_irq / dgc->irqs_per_chip; | ||
356 | if (idx >= dgc->num_chips) | ||
357 | return -EINVAL; | ||
358 | gc = dgc->gc[idx]; | ||
359 | |||
360 | idx = hw_irq % dgc->irqs_per_chip; | ||
361 | |||
362 | if (test_bit(idx, &gc->installed)) | ||
363 | return -EBUSY; | ||
364 | |||
365 | ct = gc->chip_types; | ||
366 | chip = &ct->chip; | ||
367 | |||
368 | /* We only init the cache for the first mapping of a generic chip */ | ||
369 | if (!gc->installed) { | ||
370 | raw_spin_lock_irqsave(&gc->lock, flags); | ||
371 | irq_gc_init_mask_cache(gc, dgc->gc_flags); | ||
372 | raw_spin_unlock_irqrestore(&gc->lock, flags); | ||
373 | } | ||
374 | |||
375 | /* Mark the interrupt as installed */ | ||
376 | set_bit(idx, &gc->installed); | ||
377 | |||
378 | if (dgc->gc_flags & IRQ_GC_INIT_NESTED_LOCK) | ||
379 | irq_set_lockdep_class(virq, &irq_nested_lock_class); | ||
380 | |||
381 | if (chip->irq_calc_mask) | ||
382 | chip->irq_calc_mask(data); | ||
383 | else | ||
384 | data->mask = 1 << idx; | ||
385 | |||
386 | irq_set_chip_and_handler(virq, chip, ct->handler); | ||
387 | irq_set_chip_data(virq, gc); | ||
388 | irq_modify_status(virq, dgc->irq_flags_to_clear, dgc->irq_flags_to_set); | ||
389 | return 0; | ||
390 | } | ||
391 | |||
392 | struct irq_domain_ops irq_generic_chip_ops = { | ||
393 | .map = irq_map_generic_chip, | ||
394 | .xlate = irq_domain_xlate_onetwocell, | ||
395 | }; | ||
396 | EXPORT_SYMBOL_GPL(irq_generic_chip_ops); | ||
397 | |||
398 | /** | ||
254 | * irq_setup_generic_chip - Setup a range of interrupts with a generic chip | 399 | * irq_setup_generic_chip - Setup a range of interrupts with a generic chip |
255 | * @gc: Generic irq chip holding all data | 400 | * @gc: Generic irq chip holding all data |
256 | * @msk: Bitmask holding the irqs to initialize relative to gc->irq_base | 401 | * @msk: Bitmask holding the irqs to initialize relative to gc->irq_base |
@@ -354,6 +499,24 @@ void irq_remove_generic_chip(struct irq_chip_generic *gc, u32 msk, | |||
354 | } | 499 | } |
355 | EXPORT_SYMBOL_GPL(irq_remove_generic_chip); | 500 | EXPORT_SYMBOL_GPL(irq_remove_generic_chip); |
356 | 501 | ||
502 | static struct irq_data *irq_gc_get_irq_data(struct irq_chip_generic *gc) | ||
503 | { | ||
504 | unsigned int virq; | ||
505 | |||
506 | if (!gc->domain) | ||
507 | return irq_get_irq_data(gc->irq_base); | ||
508 | |||
509 | /* | ||
510 | * We don't know which of the irqs has been actually | ||
511 | * installed. Use the first one. | ||
512 | */ | ||
513 | if (!gc->installed) | ||
514 | return NULL; | ||
515 | |||
516 | virq = irq_find_mapping(gc->domain, gc->irq_base + __ffs(gc->installed)); | ||
517 | return virq ? irq_get_irq_data(virq) : NULL; | ||
518 | } | ||
519 | |||
357 | #ifdef CONFIG_PM | 520 | #ifdef CONFIG_PM |
358 | static int irq_gc_suspend(void) | 521 | static int irq_gc_suspend(void) |
359 | { | 522 | { |
@@ -362,8 +525,12 @@ static int irq_gc_suspend(void) | |||
362 | list_for_each_entry(gc, &gc_list, list) { | 525 | list_for_each_entry(gc, &gc_list, list) { |
363 | struct irq_chip_type *ct = gc->chip_types; | 526 | struct irq_chip_type *ct = gc->chip_types; |
364 | 527 | ||
365 | if (ct->chip.irq_suspend) | 528 | if (ct->chip.irq_suspend) { |
366 | ct->chip.irq_suspend(irq_get_irq_data(gc->irq_base)); | 529 | struct irq_data *data = irq_gc_get_irq_data(gc); |
530 | |||
531 | if (data) | ||
532 | ct->chip.irq_suspend(data); | ||
533 | } | ||
367 | } | 534 | } |
368 | return 0; | 535 | return 0; |
369 | } | 536 | } |
@@ -375,8 +542,12 @@ static void irq_gc_resume(void) | |||
375 | list_for_each_entry(gc, &gc_list, list) { | 542 | list_for_each_entry(gc, &gc_list, list) { |
376 | struct irq_chip_type *ct = gc->chip_types; | 543 | struct irq_chip_type *ct = gc->chip_types; |
377 | 544 | ||
378 | if (ct->chip.irq_resume) | 545 | if (ct->chip.irq_resume) { |
379 | ct->chip.irq_resume(irq_get_irq_data(gc->irq_base)); | 546 | struct irq_data *data = irq_gc_get_irq_data(gc); |
547 | |||
548 | if (data) | ||
549 | ct->chip.irq_resume(data); | ||
550 | } | ||
380 | } | 551 | } |
381 | } | 552 | } |
382 | #else | 553 | #else |
@@ -391,8 +562,12 @@ static void irq_gc_shutdown(void) | |||
391 | list_for_each_entry(gc, &gc_list, list) { | 562 | list_for_each_entry(gc, &gc_list, list) { |
392 | struct irq_chip_type *ct = gc->chip_types; | 563 | struct irq_chip_type *ct = gc->chip_types; |
393 | 564 | ||
394 | if (ct->chip.irq_pm_shutdown) | 565 | if (ct->chip.irq_pm_shutdown) { |
395 | ct->chip.irq_pm_shutdown(irq_get_irq_data(gc->irq_base)); | 566 | struct irq_data *data = irq_gc_get_irq_data(gc); |
567 | |||
568 | if (data) | ||
569 | ct->chip.irq_pm_shutdown(data); | ||
570 | } | ||
396 | } | 571 | } |
397 | } | 572 | } |
398 | 573 | ||