diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2013-05-06 10:30:27 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2013-05-29 04:57:11 -0400 |
commit | 088f40b7b027dad6519712ff224a5798dd62a204 (patch) | |
tree | bae6f2d67cc4516471d3b4a6a80da31962cb804b | |
parent | 3528d82b684680b72fa31881c8c572c5a98b51de (diff) |
genirq: Generic chip: Add linear irq domain support
Provide infrastructure for irq chip implementations which work on
linear irq domains.
- Interface to allocate multiple generic chips which are associated to
the irq domain.
- Interface to get the generic chip pointer for a particular hardware
interrupt in the domain.
- irq domain mapping function to install the chip for a particular
interrupt.
Note: This lacks a removal function for now.
[ Sebastian Hesselbarth: Mask cache and pointer math fixups ]
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Russell King - ARM Linux <linux@arm.linux.org.uk>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Jean-Francois Moine <moinejf@free.fr>
Cc: devicetree-discuss@lists.ozlabs.org
Cc: Rob Herring <rob.herring@calxeda.com>
Cc: Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
Cc: Gregory Clement <gregory.clement@free-electrons.com>
Cc: Gerlando Falauto <gerlando.falauto@keymile.com>
Cc: Rob Landley <rob@landley.net>
Acked-by: Grant Likely <grant.likely@linaro.org>
Cc: Maxime Ripard <maxime.ripard@free-electrons.com>
Cc: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
Link: http://lkml.kernel.org/r/20130506142539.450634298@linutronix.de
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | include/linux/irq.h | 30 | ||||
-rw-r--r-- | include/linux/irqdomain.h | 12 | ||||
-rw-r--r-- | kernel/irq/generic-chip.c | 187 | ||||
-rw-r--r-- | kernel/irq/irqdomain.c | 6 |
4 files changed, 223 insertions, 12 deletions
diff --git a/include/linux/irq.h b/include/linux/irq.h index ab8169faaa65..af7052c6a45c 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h | |||
@@ -678,6 +678,8 @@ struct irq_chip_type { | |||
678 | * @wake_active: Interrupt is marked as an wakeup from suspend source | 678 | * @wake_active: Interrupt is marked as an wakeup from suspend source |
679 | * @num_ct: Number of available irq_chip_type instances (usually 1) | 679 | * @num_ct: Number of available irq_chip_type instances (usually 1) |
680 | * @private: Private data for non generic chip callbacks | 680 | * @private: Private data for non generic chip callbacks |
681 | * @installed: bitfield to denote installed interrupts | ||
682 | * @domain: irq domain pointer | ||
681 | * @list: List head for keeping track of instances | 683 | * @list: List head for keeping track of instances |
682 | * @chip_types: Array of interrupt irq_chip_types | 684 | * @chip_types: Array of interrupt irq_chip_types |
683 | * | 685 | * |
@@ -699,6 +701,8 @@ struct irq_chip_generic { | |||
699 | u32 wake_active; | 701 | u32 wake_active; |
700 | unsigned int num_ct; | 702 | unsigned int num_ct; |
701 | void *private; | 703 | void *private; |
704 | unsigned long installed; | ||
705 | struct irq_domain *domain; | ||
702 | struct list_head list; | 706 | struct list_head list; |
703 | struct irq_chip_type chip_types[0]; | 707 | struct irq_chip_type chip_types[0]; |
704 | }; | 708 | }; |
@@ -719,6 +723,24 @@ enum irq_gc_flags { | |||
719 | IRQ_GC_NO_MASK = 1 << 3, | 723 | IRQ_GC_NO_MASK = 1 << 3, |
720 | }; | 724 | }; |
721 | 725 | ||
726 | /* | ||
727 | * struct irq_domain_chip_generic - Generic irq chip data structure for irq domains | ||
728 | * @irqs_per_chip: Number of interrupts per chip | ||
729 | * @num_chips: Number of chips | ||
730 | * @irq_flags_to_set: IRQ* flags to set on irq setup | ||
731 | * @irq_flags_to_clear: IRQ* flags to clear on irq setup | ||
732 | * @gc_flags: Generic chip specific setup flags | ||
733 | * @gc: Array of pointers to generic interrupt chips | ||
734 | */ | ||
735 | struct irq_domain_chip_generic { | ||
736 | unsigned int irqs_per_chip; | ||
737 | unsigned int num_chips; | ||
738 | unsigned int irq_flags_to_clear; | ||
739 | unsigned int irq_flags_to_set; | ||
740 | enum irq_gc_flags gc_flags; | ||
741 | struct irq_chip_generic *gc[0]; | ||
742 | }; | ||
743 | |||
722 | /* Generic chip callback functions */ | 744 | /* Generic chip callback functions */ |
723 | void irq_gc_noop(struct irq_data *d); | 745 | void irq_gc_noop(struct irq_data *d); |
724 | void irq_gc_mask_disable_reg(struct irq_data *d); | 746 | void irq_gc_mask_disable_reg(struct irq_data *d); |
@@ -742,6 +764,14 @@ int irq_setup_alt_chip(struct irq_data *d, unsigned int type); | |||
742 | void irq_remove_generic_chip(struct irq_chip_generic *gc, u32 msk, | 764 | void irq_remove_generic_chip(struct irq_chip_generic *gc, u32 msk, |
743 | unsigned int clr, unsigned int set); | 765 | unsigned int clr, unsigned int set); |
744 | 766 | ||
767 | struct irq_chip_generic *irq_get_domain_generic_chip(struct irq_domain *d, unsigned int hw_irq); | ||
768 | int irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip, | ||
769 | int num_ct, const char *name, | ||
770 | irq_flow_handler_t handler, | ||
771 | unsigned int clr, unsigned int set, | ||
772 | enum irq_gc_flags flags); | ||
773 | |||
774 | |||
745 | static inline struct irq_chip_type *irq_data_get_chip_type(struct irq_data *d) | 775 | static inline struct irq_chip_type *irq_data_get_chip_type(struct irq_data *d) |
746 | { | 776 | { |
747 | return container_of(d->chip, struct irq_chip_type, chip); | 777 | return container_of(d->chip, struct irq_chip_type, chip); |
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index 0d5b17bf5e51..ba2c708adcff 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h | |||
@@ -66,6 +66,10 @@ struct irq_domain_ops { | |||
66 | unsigned long *out_hwirq, unsigned int *out_type); | 66 | unsigned long *out_hwirq, unsigned int *out_type); |
67 | }; | 67 | }; |
68 | 68 | ||
69 | extern struct irq_domain_ops irq_generic_chip_ops; | ||
70 | |||
71 | struct irq_domain_chip_generic; | ||
72 | |||
69 | /** | 73 | /** |
70 | * struct irq_domain - Hardware interrupt number translation object | 74 | * struct irq_domain - Hardware interrupt number translation object |
71 | * @link: Element in global irq_domain list. | 75 | * @link: Element in global irq_domain list. |
@@ -109,8 +113,16 @@ struct irq_domain { | |||
109 | 113 | ||
110 | /* Optional device node pointer */ | 114 | /* Optional device node pointer */ |
111 | struct device_node *of_node; | 115 | struct device_node *of_node; |
116 | /* Optional pointer to generic interrupt chips */ | ||
117 | struct irq_domain_chip_generic *gc; | ||
112 | }; | 118 | }; |
113 | 119 | ||
120 | #define IRQ_DOMAIN_MAP_LEGACY 0 /* driver allocated fixed range of irqs. | ||
121 | * ie. legacy 8259, gets irqs 1..15 */ | ||
122 | #define IRQ_DOMAIN_MAP_NOMAP 1 /* no fast reverse mapping */ | ||
123 | #define IRQ_DOMAIN_MAP_LINEAR 2 /* linear map of interrupts */ | ||
124 | #define IRQ_DOMAIN_MAP_TREE 3 /* radix tree */ | ||
125 | |||
114 | #ifdef CONFIG_IRQ_DOMAIN | 126 | #ifdef CONFIG_IRQ_DOMAIN |
115 | struct irq_domain *irq_domain_add_simple(struct device_node *of_node, | 127 | struct irq_domain *irq_domain_add_simple(struct device_node *of_node, |
116 | unsigned int size, | 128 | unsigned int size, |
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 | ||
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 5a83dde8ca0c..1db9e70f5488 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c | |||
@@ -16,12 +16,6 @@ | |||
16 | #include <linux/smp.h> | 16 | #include <linux/smp.h> |
17 | #include <linux/fs.h> | 17 | #include <linux/fs.h> |
18 | 18 | ||
19 | #define IRQ_DOMAIN_MAP_LEGACY 0 /* driver allocated fixed range of irqs. | ||
20 | * ie. legacy 8259, gets irqs 1..15 */ | ||
21 | #define IRQ_DOMAIN_MAP_NOMAP 1 /* no fast reverse mapping */ | ||
22 | #define IRQ_DOMAIN_MAP_LINEAR 2 /* linear map of interrupts */ | ||
23 | #define IRQ_DOMAIN_MAP_TREE 3 /* radix tree */ | ||
24 | |||
25 | static LIST_HEAD(irq_domain_list); | 19 | static LIST_HEAD(irq_domain_list); |
26 | static DEFINE_MUTEX(irq_domain_mutex); | 20 | static DEFINE_MUTEX(irq_domain_mutex); |
27 | 21 | ||