aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2013-05-06 10:30:27 -0400
committerThomas Gleixner <tglx@linutronix.de>2013-05-29 04:57:11 -0400
commit088f40b7b027dad6519712ff224a5798dd62a204 (patch)
treebae6f2d67cc4516471d3b4a6a80da31962cb804b
parent3528d82b684680b72fa31881c8c572c5a98b51de (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.h30
-rw-r--r--include/linux/irqdomain.h12
-rw-r--r--kernel/irq/generic-chip.c187
-rw-r--r--kernel/irq/irqdomain.c6
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 */
735struct 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 */
723void irq_gc_noop(struct irq_data *d); 745void irq_gc_noop(struct irq_data *d);
724void irq_gc_mask_disable_reg(struct irq_data *d); 746void 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);
742void irq_remove_generic_chip(struct irq_chip_generic *gc, u32 msk, 764void 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
767struct irq_chip_generic *irq_get_domain_generic_chip(struct irq_domain *d, unsigned int hw_irq);
768int 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
745static inline struct irq_chip_type *irq_data_get_chip_type(struct irq_data *d) 775static 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
69extern struct irq_domain_ops irq_generic_chip_ops;
70
71struct 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
115struct irq_domain *irq_domain_add_simple(struct device_node *of_node, 127struct 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 */
258int 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}
310EXPORT_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 */
317struct irq_chip_generic *
318irq_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}
330EXPORT_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)
251static struct lock_class_key irq_nested_lock_class; 336static 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 */
341static 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
392struct irq_domain_ops irq_generic_chip_ops = {
393 .map = irq_map_generic_chip,
394 .xlate = irq_domain_xlate_onetwocell,
395};
396EXPORT_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}
355EXPORT_SYMBOL_GPL(irq_remove_generic_chip); 500EXPORT_SYMBOL_GPL(irq_remove_generic_chip);
356 501
502static 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
358static int irq_gc_suspend(void) 521static 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
25static LIST_HEAD(irq_domain_list); 19static LIST_HEAD(irq_domain_list);
26static DEFINE_MUTEX(irq_domain_mutex); 20static DEFINE_MUTEX(irq_domain_mutex);
27 21