diff options
author | Max Filippov <jcmvbkbc@gmail.com> | 2012-11-03 16:29:12 -0400 |
---|---|---|
committer | Chris Zankel <chris@zankel.net> | 2012-12-19 00:10:23 -0500 |
commit | 2206d5dd9a785a74afc6981f2b13b7a4d4da6f31 (patch) | |
tree | a2205c8d26f7b28d3f9cb9a66e18650cb7d48674 /arch/xtensa | |
parent | 0322cabd39e27614772595abb395871e86ebff66 (diff) |
xtensa: add IRQ domains support
IRQ domains provide a mechanism for conversion of linux IRQ numbers to
hardware IRQ numbers and vice versus. It is used by OpenFirmware for
linking device tree objects to their respective interrupt controllers.
Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
Signed-off-by: Chris Zankel <chris@zankel.net>
Diffstat (limited to 'arch/xtensa')
-rw-r--r-- | arch/xtensa/Kconfig | 1 | ||||
-rw-r--r-- | arch/xtensa/kernel/irq.c | 122 | ||||
-rw-r--r-- | arch/xtensa/kernel/time.c | 5 |
3 files changed, 94 insertions, 34 deletions
diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index 2481f267be29..1816abc5c8d5 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig | |||
@@ -17,6 +17,7 @@ config XTENSA | |||
17 | select GENERIC_KERNEL_EXECVE | 17 | select GENERIC_KERNEL_EXECVE |
18 | select ARCH_WANT_OPTIONAL_GPIOLIB | 18 | select ARCH_WANT_OPTIONAL_GPIOLIB |
19 | select CLONE_BACKWARDS | 19 | select CLONE_BACKWARDS |
20 | select IRQ_DOMAIN | ||
20 | help | 21 | help |
21 | Xtensa processors are 32-bit RISC machines designed by Tensilica | 22 | Xtensa processors are 32-bit RISC machines designed by Tensilica |
22 | primarily for embedded systems. These processors are both | 23 | primarily for embedded systems. These processors are both |
diff --git a/arch/xtensa/kernel/irq.c b/arch/xtensa/kernel/irq.c index a6ce3e563739..e90d78211195 100644 --- a/arch/xtensa/kernel/irq.c +++ b/arch/xtensa/kernel/irq.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/interrupt.h> | 18 | #include <linux/interrupt.h> |
19 | #include <linux/irq.h> | 19 | #include <linux/irq.h> |
20 | #include <linux/kernel_stat.h> | 20 | #include <linux/kernel_stat.h> |
21 | #include <linux/irqdomain.h> | ||
21 | 22 | ||
22 | #include <asm/uaccess.h> | 23 | #include <asm/uaccess.h> |
23 | #include <asm/platform.h> | 24 | #include <asm/platform.h> |
@@ -26,19 +27,22 @@ static unsigned int cached_irq_mask; | |||
26 | 27 | ||
27 | atomic_t irq_err_count; | 28 | atomic_t irq_err_count; |
28 | 29 | ||
30 | static struct irq_domain *root_domain; | ||
31 | |||
29 | /* | 32 | /* |
30 | * do_IRQ handles all normal device IRQ's (the special | 33 | * do_IRQ handles all normal device IRQ's (the special |
31 | * SMP cross-CPU interrupts have their own specific | 34 | * SMP cross-CPU interrupts have their own specific |
32 | * handlers). | 35 | * handlers). |
33 | */ | 36 | */ |
34 | 37 | ||
35 | asmlinkage void do_IRQ(int irq, struct pt_regs *regs) | 38 | asmlinkage void do_IRQ(int hwirq, struct pt_regs *regs) |
36 | { | 39 | { |
37 | struct pt_regs *old_regs = set_irq_regs(regs); | 40 | struct pt_regs *old_regs = set_irq_regs(regs); |
41 | int irq = irq_find_mapping(root_domain, hwirq); | ||
38 | 42 | ||
39 | if (irq >= NR_IRQS) { | 43 | if (hwirq >= NR_IRQS) { |
40 | printk(KERN_EMERG "%s: cannot handle IRQ %d\n", | 44 | printk(KERN_EMERG "%s: cannot handle IRQ %d\n", |
41 | __func__, irq); | 45 | __func__, hwirq); |
42 | } | 46 | } |
43 | 47 | ||
44 | irq_enter(); | 48 | irq_enter(); |
@@ -71,40 +75,39 @@ int arch_show_interrupts(struct seq_file *p, int prec) | |||
71 | 75 | ||
72 | static void xtensa_irq_mask(struct irq_data *d) | 76 | static void xtensa_irq_mask(struct irq_data *d) |
73 | { | 77 | { |
74 | cached_irq_mask &= ~(1 << d->irq); | 78 | cached_irq_mask &= ~(1 << d->hwirq); |
75 | set_sr (cached_irq_mask, intenable); | 79 | set_sr (cached_irq_mask, intenable); |
76 | } | 80 | } |
77 | 81 | ||
78 | static void xtensa_irq_unmask(struct irq_data *d) | 82 | static void xtensa_irq_unmask(struct irq_data *d) |
79 | { | 83 | { |
80 | cached_irq_mask |= 1 << d->irq; | 84 | cached_irq_mask |= 1 << d->hwirq; |
81 | set_sr (cached_irq_mask, intenable); | 85 | set_sr (cached_irq_mask, intenable); |
82 | } | 86 | } |
83 | 87 | ||
84 | static void xtensa_irq_enable(struct irq_data *d) | 88 | static void xtensa_irq_enable(struct irq_data *d) |
85 | { | 89 | { |
86 | variant_irq_enable(d->irq); | 90 | variant_irq_enable(d->hwirq); |
87 | xtensa_irq_unmask(d); | 91 | xtensa_irq_unmask(d); |
88 | } | 92 | } |
89 | 93 | ||
90 | static void xtensa_irq_disable(struct irq_data *d) | 94 | static void xtensa_irq_disable(struct irq_data *d) |
91 | { | 95 | { |
92 | xtensa_irq_mask(d); | 96 | xtensa_irq_mask(d); |
93 | variant_irq_disable(d->irq); | 97 | variant_irq_disable(d->hwirq); |
94 | } | 98 | } |
95 | 99 | ||
96 | static void xtensa_irq_ack(struct irq_data *d) | 100 | static void xtensa_irq_ack(struct irq_data *d) |
97 | { | 101 | { |
98 | set_sr(1 << d->irq, intclear); | 102 | set_sr(1 << d->hwirq, intclear); |
99 | } | 103 | } |
100 | 104 | ||
101 | static int xtensa_irq_retrigger(struct irq_data *d) | 105 | static int xtensa_irq_retrigger(struct irq_data *d) |
102 | { | 106 | { |
103 | set_sr (1 << d->irq, INTSET); | 107 | set_sr(1 << d->hwirq, intset); |
104 | return 1; | 108 | return 1; |
105 | } | 109 | } |
106 | 110 | ||
107 | |||
108 | static struct irq_chip xtensa_irq_chip = { | 111 | static struct irq_chip xtensa_irq_chip = { |
109 | .name = "xtensa", | 112 | .name = "xtensa", |
110 | .irq_enable = xtensa_irq_enable, | 113 | .irq_enable = xtensa_irq_enable, |
@@ -115,37 +118,90 @@ static struct irq_chip xtensa_irq_chip = { | |||
115 | .irq_retrigger = xtensa_irq_retrigger, | 118 | .irq_retrigger = xtensa_irq_retrigger, |
116 | }; | 119 | }; |
117 | 120 | ||
118 | void __init init_IRQ(void) | 121 | static int xtensa_irq_map(struct irq_domain *d, unsigned int irq, |
122 | irq_hw_number_t hw) | ||
119 | { | 123 | { |
120 | int index; | 124 | u32 mask = 1 << hw; |
121 | 125 | ||
122 | for (index = 0; index < XTENSA_NR_IRQS; index++) { | 126 | if (mask & XCHAL_INTTYPE_MASK_SOFTWARE) { |
123 | int mask = 1 << index; | 127 | irq_set_chip_and_handler_name(irq, &xtensa_irq_chip, |
124 | 128 | handle_simple_irq, "level"); | |
125 | if (mask & XCHAL_INTTYPE_MASK_SOFTWARE) | 129 | irq_set_status_flags(irq, IRQ_LEVEL); |
126 | irq_set_chip_and_handler(index, &xtensa_irq_chip, | 130 | } else if (mask & XCHAL_INTTYPE_MASK_EXTERN_EDGE) { |
127 | handle_simple_irq); | 131 | irq_set_chip_and_handler_name(irq, &xtensa_irq_chip, |
132 | handle_edge_irq, "edge"); | ||
133 | irq_clear_status_flags(irq, IRQ_LEVEL); | ||
134 | } else if (mask & XCHAL_INTTYPE_MASK_EXTERN_LEVEL) { | ||
135 | irq_set_chip_and_handler_name(irq, &xtensa_irq_chip, | ||
136 | handle_level_irq, "level"); | ||
137 | irq_set_status_flags(irq, IRQ_LEVEL); | ||
138 | } else if (mask & XCHAL_INTTYPE_MASK_TIMER) { | ||
139 | irq_set_chip_and_handler_name(irq, &xtensa_irq_chip, | ||
140 | handle_edge_irq, "edge"); | ||
141 | irq_clear_status_flags(irq, IRQ_LEVEL); | ||
142 | } else {/* XCHAL_INTTYPE_MASK_WRITE_ERROR */ | ||
143 | /* XCHAL_INTTYPE_MASK_NMI */ | ||
144 | |||
145 | irq_set_chip_and_handler_name(irq, &xtensa_irq_chip, | ||
146 | handle_level_irq, "level"); | ||
147 | irq_set_status_flags(irq, IRQ_LEVEL); | ||
148 | } | ||
149 | return 0; | ||
150 | } | ||
128 | 151 | ||
129 | else if (mask & XCHAL_INTTYPE_MASK_EXTERN_EDGE) | 152 | static unsigned map_ext_irq(unsigned ext_irq) |
130 | irq_set_chip_and_handler(index, &xtensa_irq_chip, | 153 | { |
131 | handle_edge_irq); | 154 | unsigned mask = XCHAL_INTTYPE_MASK_EXTERN_EDGE | |
155 | XCHAL_INTTYPE_MASK_EXTERN_LEVEL; | ||
156 | unsigned i; | ||
132 | 157 | ||
133 | else if (mask & XCHAL_INTTYPE_MASK_EXTERN_LEVEL) | 158 | for (i = 0; mask; ++i, mask >>= 1) { |
134 | irq_set_chip_and_handler(index, &xtensa_irq_chip, | 159 | if ((mask & 1) && ext_irq-- == 0) |
135 | handle_level_irq); | 160 | return i; |
161 | } | ||
162 | return XCHAL_NUM_INTERRUPTS; | ||
163 | } | ||
136 | 164 | ||
137 | else if (mask & XCHAL_INTTYPE_MASK_TIMER) | 165 | /* |
138 | irq_set_chip_and_handler(index, &xtensa_irq_chip, | 166 | * Device Tree IRQ specifier translation function which works with one or |
139 | handle_edge_irq); | 167 | * two cell bindings. First cell value maps directly to the hwirq number. |
168 | * Second cell if present specifies whether hwirq number is external (1) or | ||
169 | * internal (0). | ||
170 | */ | ||
171 | int xtensa_irq_domain_xlate(struct irq_domain *d, struct device_node *ctrlr, | ||
172 | const u32 *intspec, unsigned int intsize, | ||
173 | unsigned long *out_hwirq, unsigned int *out_type) | ||
174 | { | ||
175 | if (WARN_ON(intsize < 1 || intsize > 2)) | ||
176 | return -EINVAL; | ||
177 | if (intsize == 2 && intspec[1] == 1) { | ||
178 | unsigned int_irq = map_ext_irq(intspec[0]); | ||
179 | if (int_irq < XCHAL_NUM_INTERRUPTS) | ||
180 | *out_hwirq = int_irq; | ||
181 | else | ||
182 | return -EINVAL; | ||
183 | } else { | ||
184 | *out_hwirq = intspec[0]; | ||
185 | } | ||
186 | *out_type = IRQ_TYPE_NONE; | ||
187 | return 0; | ||
188 | } | ||
140 | 189 | ||
141 | else /* XCHAL_INTTYPE_MASK_WRITE_ERROR */ | 190 | static const struct irq_domain_ops xtensa_irq_domain_ops = { |
142 | /* XCHAL_INTTYPE_MASK_NMI */ | 191 | .xlate = xtensa_irq_domain_xlate, |
192 | .map = xtensa_irq_map, | ||
193 | }; | ||
143 | 194 | ||
144 | irq_set_chip_and_handler(index, &xtensa_irq_chip, | 195 | void __init init_IRQ(void) |
145 | handle_level_irq); | 196 | { |
146 | } | 197 | struct device_node *intc = NULL; |
147 | 198 | ||
148 | cached_irq_mask = 0; | 199 | cached_irq_mask = 0; |
200 | set_sr(~0, intclear); | ||
201 | |||
202 | root_domain = irq_domain_add_legacy(intc, NR_IRQS, 0, 0, | ||
203 | &xtensa_irq_domain_ops, NULL); | ||
204 | irq_set_default_host(root_domain); | ||
149 | 205 | ||
150 | variant_init_irq(); | 206 | variant_init_irq(); |
151 | } | 207 | } |
diff --git a/arch/xtensa/kernel/time.c b/arch/xtensa/kernel/time.c index 19b32a0eaebc..ffb474104311 100644 --- a/arch/xtensa/kernel/time.c +++ b/arch/xtensa/kernel/time.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/irq.h> | 22 | #include <linux/irq.h> |
23 | #include <linux/profile.h> | 23 | #include <linux/profile.h> |
24 | #include <linux/delay.h> | 24 | #include <linux/delay.h> |
25 | #include <linux/irqdomain.h> | ||
25 | 26 | ||
26 | #include <asm/timex.h> | 27 | #include <asm/timex.h> |
27 | #include <asm/platform.h> | 28 | #include <asm/platform.h> |
@@ -52,6 +53,7 @@ static struct irqaction timer_irqaction = { | |||
52 | 53 | ||
53 | void __init time_init(void) | 54 | void __init time_init(void) |
54 | { | 55 | { |
56 | unsigned int irq; | ||
55 | #ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT | 57 | #ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT |
56 | printk("Calibrating CPU frequency "); | 58 | printk("Calibrating CPU frequency "); |
57 | platform_calibrate_ccount(); | 59 | platform_calibrate_ccount(); |
@@ -62,7 +64,8 @@ void __init time_init(void) | |||
62 | 64 | ||
63 | /* Initialize the linux timer interrupt. */ | 65 | /* Initialize the linux timer interrupt. */ |
64 | 66 | ||
65 | setup_irq(LINUX_TIMER_INT, &timer_irqaction); | 67 | irq = irq_create_mapping(NULL, LINUX_TIMER_INT); |
68 | setup_irq(irq, &timer_irqaction); | ||
66 | set_linux_timer(get_ccount() + CCOUNT_PER_JIFFY); | 69 | set_linux_timer(get_ccount() + CCOUNT_PER_JIFFY); |
67 | } | 70 | } |
68 | 71 | ||