aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGrant Likely <grant.likely@secretlab.ca>2011-07-26 05:19:06 -0400
committerGrant Likely <grant.likely@secretlab.ca>2011-07-28 03:32:04 -0400
commit08a543ad33fc188650801bd36eed4ffe272643e1 (patch)
treecf2b41b922e77190425f999c2268f1558dd52d18
parent5fd1a2ed0ec6fb5449c71a988cc15edb8671b3d0 (diff)
irq: add irq_domain translation infrastructure
This patch adds irq_domain infrastructure for translating from hardware irq numbers to linux irqs. This is particularly important for architectures adding device tree support because the current implementation (excluding PowerPC and SPARC) cannot handle translation for more than a single interrupt controller. irq_domain supports device tree translation for any number of interrupt controllers. This patch converts x86, Microblaze, ARM and MIPS to use irq_domain for device tree irq translation. x86 is untested beyond compiling it, irq_domain is enabled for MIPS and Microblaze, but the old behaviour is preserved until the core code is modified to actually register an irq_domain yet. On ARM it works and is required for much of the new ARM device tree board support. PowerPC has /not/ been converted to use this new infrastructure. It is still missing some features before it can replace the virq infrastructure already in powerpc (see documentation on irq_domain_map/unmap for details). Followup patches will add the missing pieces and migrate PowerPC to use irq_domain. SPARC has its own method of managing interrupts from the device tree and is unaffected by this change. Acked-by: Ralf Baechle <ralf@linux-mips.org> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
-rw-r--r--arch/arm/Kconfig1
-rw-r--r--arch/arm/include/asm/prom.h5
-rw-r--r--arch/arm/kernel/devtree.c14
-rw-r--r--include/linux/irq.h6
-rw-r--r--include/linux/irqdomain.h81
-rw-r--r--include/linux/of_irq.h4
-rw-r--r--kernel/irq/Kconfig4
-rw-r--r--kernel/irq/Makefile1
-rw-r--r--kernel/irq/irqdomain.c122
9 files changed, 219 insertions, 19 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 1478c6171b00..8ac7b996038c 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1682,6 +1682,7 @@ config USE_OF
1682 bool "Flattened Device Tree support" 1682 bool "Flattened Device Tree support"
1683 select OF 1683 select OF
1684 select OF_EARLY_FLATTREE 1684 select OF_EARLY_FLATTREE
1685 select IRQ_DOMAIN
1685 help 1686 help
1686 Include support for flattened device tree machine descriptions. 1687 Include support for flattened device tree machine descriptions.
1687 1688
diff --git a/arch/arm/include/asm/prom.h b/arch/arm/include/asm/prom.h
index 11b8708fc4db..6f65ca86a5ec 100644
--- a/arch/arm/include/asm/prom.h
+++ b/arch/arm/include/asm/prom.h
@@ -16,11 +16,6 @@
16#include <asm/setup.h> 16#include <asm/setup.h>
17#include <asm/irq.h> 17#include <asm/irq.h>
18 18
19static inline void irq_dispose_mapping(unsigned int virq)
20{
21 return;
22}
23
24extern struct machine_desc *setup_machine_fdt(unsigned int dt_phys); 19extern struct machine_desc *setup_machine_fdt(unsigned int dt_phys);
25extern void arm_dt_memblock_reserve(void); 20extern void arm_dt_memblock_reserve(void);
26 21
diff --git a/arch/arm/kernel/devtree.c b/arch/arm/kernel/devtree.c
index 0cdd7b456cb2..1a33e9d6bb1f 100644
--- a/arch/arm/kernel/devtree.c
+++ b/arch/arm/kernel/devtree.c
@@ -132,17 +132,3 @@ struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)
132 132
133 return mdesc_best; 133 return mdesc_best;
134} 134}
135
136/**
137 * irq_create_of_mapping - Hook to resolve OF irq specifier into a Linux irq#
138 *
139 * Currently the mapping mechanism is trivial; simple flat hwirq numbers are
140 * mapped 1:1 onto Linux irq numbers. Cascaded irq controllers are not
141 * supported.
142 */
143unsigned int irq_create_of_mapping(struct device_node *controller,
144 const u32 *intspec, unsigned int intsize)
145{
146 return intspec[0];
147}
148EXPORT_SYMBOL_GPL(irq_create_of_mapping);
diff --git a/include/linux/irq.h b/include/linux/irq.h
index 5f695041090c..87a06f345bd2 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -108,14 +108,18 @@ enum {
108}; 108};
109 109
110struct msi_desc; 110struct msi_desc;
111struct irq_domain;
111 112
112/** 113/**
113 * struct irq_data - per irq and irq chip data passed down to chip functions 114 * struct irq_data - per irq and irq chip data passed down to chip functions
114 * @irq: interrupt number 115 * @irq: interrupt number
116 * @hwirq: hardware interrupt number, local to the interrupt domain
115 * @node: node index useful for balancing 117 * @node: node index useful for balancing
116 * @state_use_accessors: status information for irq chip functions. 118 * @state_use_accessors: status information for irq chip functions.
117 * Use accessor functions to deal with it 119 * Use accessor functions to deal with it
118 * @chip: low level interrupt hardware access 120 * @chip: low level interrupt hardware access
121 * @domain: Interrupt translation domain; responsible for mapping
122 * between hwirq number and linux irq number.
119 * @handler_data: per-IRQ data for the irq_chip methods 123 * @handler_data: per-IRQ data for the irq_chip methods
120 * @chip_data: platform-specific per-chip private data for the chip 124 * @chip_data: platform-specific per-chip private data for the chip
121 * methods, to allow shared chip implementations 125 * methods, to allow shared chip implementations
@@ -128,9 +132,11 @@ struct msi_desc;
128 */ 132 */
129struct irq_data { 133struct irq_data {
130 unsigned int irq; 134 unsigned int irq;
135 unsigned long hwirq;
131 unsigned int node; 136 unsigned int node;
132 unsigned int state_use_accessors; 137 unsigned int state_use_accessors;
133 struct irq_chip *chip; 138 struct irq_chip *chip;
139 struct irq_domain *domain;
134 void *handler_data; 140 void *handler_data;
135 void *chip_data; 141 void *chip_data;
136 struct msi_desc *msi_desc; 142 struct msi_desc *msi_desc;
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
new file mode 100644
index 000000000000..8f2c10a784a4
--- /dev/null
+++ b/include/linux/irqdomain.h
@@ -0,0 +1,81 @@
1/*
2 * irq_domain - IRQ translation domains
3 *
4 * Translation infrastructure between hw and linux irq numbers. This is
5 * helpful for interrupt controllers to implement mapping between hardware
6 * irq numbers and the Linux irq number space.
7 *
8 * irq_domains also have a hook for translating device tree interrupt
9 * representation into a hardware irq number that can be mapped back to a
10 * Linux irq number without any extra platform support code.
11 *
12 * irq_domain is expected to be embedded in an interrupt controller's private
13 * data structure.
14 */
15#ifndef _LINUX_IRQDOMAIN_H
16#define _LINUX_IRQDOMAIN_H
17
18#include <linux/irq.h>
19
20#ifdef CONFIG_IRQ_DOMAIN
21struct device_node;
22struct irq_domain;
23
24/**
25 * struct irq_domain_ops - Methods for irq_domain objects
26 * @to_irq: (optional) given a local hardware irq number, return the linux
27 * irq number. If to_irq is not implemented, then the irq_domain
28 * will use this translation: irq = (domain->irq_base + hwirq)
29 * @dt_translate: Given a device tree node and interrupt specifier, decode
30 * the hardware irq number and linux irq type value.
31 */
32struct irq_domain_ops {
33 unsigned int (*to_irq)(struct irq_domain *d, unsigned long hwirq);
34
35#ifdef CONFIG_OF
36 int (*dt_translate)(struct irq_domain *d, struct device_node *node,
37 const u32 *intspec, unsigned int intsize,
38 unsigned long *out_hwirq, unsigned int *out_type);
39#endif /* CONFIG_OF */
40};
41
42/**
43 * struct irq_domain - Hardware interrupt number translation object
44 * @list: Element in global irq_domain list.
45 * @irq_base: Start of irq_desc range assigned to the irq_domain. The creator
46 * of the irq_domain is responsible for allocating the array of
47 * irq_desc structures.
48 * @nr_irq: Number of irqs managed by the irq domain
49 * @ops: pointer to irq_domain methods
50 * @priv: private data pointer for use by owner. Not touched by irq_domain
51 * core code.
52 * @of_node: (optional) Pointer to device tree nodes associated with the
53 * irq_domain. Used when decoding device tree interrupt specifiers.
54 */
55struct irq_domain {
56 struct list_head list;
57 unsigned int irq_base;
58 unsigned int nr_irq;
59 const struct irq_domain_ops *ops;
60 void *priv;
61 struct device_node *of_node;
62};
63
64/**
65 * irq_domain_to_irq() - Translate from a hardware irq to a linux irq number
66 *
67 * Returns the linux irq number associated with a hardware irq. By default,
68 * the mapping is irq == domain->irq_base + hwirq, but this mapping can
69 * be overridden if the irq_domain implements a .to_irq() hook.
70 */
71static inline unsigned int irq_domain_to_irq(struct irq_domain *d,
72 unsigned long hwirq)
73{
74 return d->ops->to_irq ? d->ops->to_irq(d, hwirq) : d->irq_base + hwirq;
75}
76
77extern void irq_domain_add(struct irq_domain *domain);
78extern void irq_domain_del(struct irq_domain *domain);
79#endif /* CONFIG_IRQ_DOMAIN */
80
81#endif /* _LINUX_IRQDOMAIN_H */
diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h
index e6955f5d1f08..cd2e61ce4e83 100644
--- a/include/linux/of_irq.h
+++ b/include/linux/of_irq.h
@@ -63,6 +63,9 @@ extern int of_irq_map_one(struct device_node *device, int index,
63extern unsigned int irq_create_of_mapping(struct device_node *controller, 63extern unsigned int irq_create_of_mapping(struct device_node *controller,
64 const u32 *intspec, 64 const u32 *intspec,
65 unsigned int intsize); 65 unsigned int intsize);
66#ifdef CONFIG_IRQ_DOMAIN
67extern void irq_dispose_mapping(unsigned int irq);
68#endif
66extern int of_irq_to_resource(struct device_node *dev, int index, 69extern int of_irq_to_resource(struct device_node *dev, int index,
67 struct resource *r); 70 struct resource *r);
68extern int of_irq_count(struct device_node *dev); 71extern int of_irq_count(struct device_node *dev);
@@ -70,6 +73,7 @@ extern int of_irq_to_resource_table(struct device_node *dev,
70 struct resource *res, int nr_irqs); 73 struct resource *res, int nr_irqs);
71extern struct device_node *of_irq_find_parent(struct device_node *child); 74extern struct device_node *of_irq_find_parent(struct device_node *child);
72 75
76
73#endif /* CONFIG_OF_IRQ */ 77#endif /* CONFIG_OF_IRQ */
74#endif /* CONFIG_OF */ 78#endif /* CONFIG_OF */
75#endif /* __OF_IRQ_H */ 79#endif /* __OF_IRQ_H */
diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig
index d1d051b38e0b..5a38bf4de641 100644
--- a/kernel/irq/Kconfig
+++ b/kernel/irq/Kconfig
@@ -52,6 +52,10 @@ config IRQ_EDGE_EOI_HANDLER
52config GENERIC_IRQ_CHIP 52config GENERIC_IRQ_CHIP
53 bool 53 bool
54 54
55# Generic irq_domain hw <--> linux irq number translation
56config IRQ_DOMAIN
57 bool
58
55# Support forced irq threading 59# Support forced irq threading
56config IRQ_FORCED_THREADING 60config IRQ_FORCED_THREADING
57 bool 61 bool
diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile
index 73290056cfb6..fff17381f0af 100644
--- a/kernel/irq/Makefile
+++ b/kernel/irq/Makefile
@@ -2,6 +2,7 @@
2obj-y := irqdesc.o handle.o manage.o spurious.o resend.o chip.o dummychip.o devres.o 2obj-y := irqdesc.o handle.o manage.o spurious.o resend.o chip.o dummychip.o devres.o
3obj-$(CONFIG_GENERIC_IRQ_CHIP) += generic-chip.o 3obj-$(CONFIG_GENERIC_IRQ_CHIP) += generic-chip.o
4obj-$(CONFIG_GENERIC_IRQ_PROBE) += autoprobe.o 4obj-$(CONFIG_GENERIC_IRQ_PROBE) += autoprobe.o
5obj-$(CONFIG_IRQ_DOMAIN) += irqdomain.o
5obj-$(CONFIG_PROC_FS) += proc.o 6obj-$(CONFIG_PROC_FS) += proc.o
6obj-$(CONFIG_GENERIC_PENDING_IRQ) += migration.o 7obj-$(CONFIG_GENERIC_PENDING_IRQ) += migration.o
7obj-$(CONFIG_PM_SLEEP) += pm.o 8obj-$(CONFIG_PM_SLEEP) += pm.o
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
new file mode 100644
index 000000000000..29c7bd42e25d
--- /dev/null
+++ b/kernel/irq/irqdomain.c
@@ -0,0 +1,122 @@
1#include <linux/irq.h>
2#include <linux/irqdomain.h>
3#include <linux/module.h>
4#include <linux/mutex.h>
5#include <linux/of.h>
6
7static LIST_HEAD(irq_domain_list);
8static DEFINE_MUTEX(irq_domain_mutex);
9
10/**
11 * irq_domain_add() - Register an irq_domain
12 * @domain: ptr to initialized irq_domain structure
13 *
14 * Registers an irq_domain structure. The irq_domain must at a minimum be
15 * initialized with an ops structure pointer, and either a ->to_irq hook or
16 * a valid irq_base value. Everything else is optional.
17 */
18void irq_domain_add(struct irq_domain *domain)
19{
20 struct irq_data *d;
21 int hwirq;
22
23 /*
24 * This assumes that the irq_domain owner has already allocated
25 * the irq_descs. This block will be removed when support for dynamic
26 * allocation of irq_descs is added to irq_domain.
27 */
28 for (hwirq = 0; hwirq < domain->nr_irq; hwirq++) {
29 d = irq_get_irq_data(irq_domain_to_irq(domain, hwirq));
30 if (d || d->domain) {
31 /* things are broken; just report, don't clean up */
32 WARN(1, "error: irq_desc already assigned to a domain");
33 return;
34 }
35 d->domain = domain;
36 d->hwirq = hwirq;
37 }
38
39 mutex_lock(&irq_domain_mutex);
40 list_add(&domain->list, &irq_domain_list);
41 mutex_unlock(&irq_domain_mutex);
42}
43
44/**
45 * irq_domain_del() - Unregister an irq_domain
46 * @domain: ptr to registered irq_domain.
47 */
48void irq_domain_del(struct irq_domain *domain)
49{
50 struct irq_data *d;
51 int hwirq;
52
53 mutex_lock(&irq_domain_mutex);
54 list_del(&domain->list);
55 mutex_unlock(&irq_domain_mutex);
56
57 /* Clear the irq_domain assignments */
58 for (hwirq = 0; hwirq < domain->nr_irq; hwirq++) {
59 d = irq_get_irq_data(irq_domain_to_irq(domain, hwirq));
60 d->domain = NULL;
61 }
62}
63
64#if defined(CONFIG_OF_IRQ)
65/**
66 * irq_create_of_mapping() - Map a linux irq number from a DT interrupt spec
67 *
68 * Used by the device tree interrupt mapping code to translate a device tree
69 * interrupt specifier to a valid linux irq number. Returns either a valid
70 * linux IRQ number or 0.
71 *
72 * When the caller no longer need the irq number returned by this function it
73 * should arrange to call irq_dispose_mapping().
74 */
75unsigned int irq_create_of_mapping(struct device_node *controller,
76 const u32 *intspec, unsigned int intsize)
77{
78 struct irq_domain *domain;
79 unsigned long hwirq;
80 unsigned int irq, type;
81 int rc = -EINVAL;
82
83 /* Find a domain which can translate the irq spec */
84 mutex_lock(&irq_domain_mutex);
85 list_for_each_entry(domain, &irq_domain_list, list) {
86 if (!domain->ops->dt_translate)
87 continue;
88 rc = domain->ops->dt_translate(domain, controller,
89 intspec, intsize, &hwirq, &type);
90 if (rc == 0)
91 break;
92 }
93 mutex_unlock(&irq_domain_mutex);
94
95 if (rc != 0)
96 return 0;
97
98 irq = irq_domain_to_irq(domain, hwirq);
99 if (type != IRQ_TYPE_NONE)
100 irq_set_irq_type(irq, type);
101 pr_debug("%s: mapped hwirq=%i to irq=%i, flags=%x\n",
102 controller->full_name, (int)hwirq, irq, type);
103 return irq;
104}
105EXPORT_SYMBOL_GPL(irq_create_of_mapping);
106
107/**
108 * irq_dispose_mapping() - Discard a mapping created by irq_create_of_mapping()
109 * @irq: linux irq number to be discarded
110 *
111 * Calling this function indicates the caller no longer needs a reference to
112 * the linux irq number returned by a prior call to irq_create_of_mapping().
113 */
114void irq_dispose_mapping(unsigned int irq)
115{
116 /*
117 * nothing yet; will be filled when support for dynamic allocation of
118 * irq_descs is added to irq_domain
119 */
120}
121EXPORT_SYMBOL_GPL(irq_dispose_mapping);
122#endif /* CONFIG_OF_IRQ */