diff options
author | Jiri Kosina <jkosina@suse.cz> | 2013-01-29 04:48:30 -0500 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2013-01-29 04:48:30 -0500 |
commit | 617677295b53a40d0e54aac4cbbc216ffbc755dd (patch) | |
tree | 51b9e87213243ed5efff252c8e8d8fec4eebc588 /arch/xtensa/kernel/irq.c | |
parent | 5c8d1b68e01a144813e38795fe6dbe7ebb506131 (diff) | |
parent | 6abb7c25775b7fb2225ad0508236d63ca710e65f (diff) |
Merge branch 'master' into for-next
Conflicts:
drivers/devfreq/exynos4_bus.c
Sync with Linus' tree to be able to apply patches that are
against newer code (mvneta).
Diffstat (limited to 'arch/xtensa/kernel/irq.c')
-rw-r--r-- | arch/xtensa/kernel/irq.c | 132 |
1 files changed, 99 insertions, 33 deletions
diff --git a/arch/xtensa/kernel/irq.c b/arch/xtensa/kernel/irq.c index a6ce3e563739..6f4f9749cff7 100644 --- a/arch/xtensa/kernel/irq.c +++ b/arch/xtensa/kernel/irq.c | |||
@@ -18,6 +18,8 @@ | |||
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> | ||
22 | #include <linux/of.h> | ||
21 | 23 | ||
22 | #include <asm/uaccess.h> | 24 | #include <asm/uaccess.h> |
23 | #include <asm/platform.h> | 25 | #include <asm/platform.h> |
@@ -26,19 +28,22 @@ static unsigned int cached_irq_mask; | |||
26 | 28 | ||
27 | atomic_t irq_err_count; | 29 | atomic_t irq_err_count; |
28 | 30 | ||
31 | static struct irq_domain *root_domain; | ||
32 | |||
29 | /* | 33 | /* |
30 | * do_IRQ handles all normal device IRQ's (the special | 34 | * do_IRQ handles all normal device IRQ's (the special |
31 | * SMP cross-CPU interrupts have their own specific | 35 | * SMP cross-CPU interrupts have their own specific |
32 | * handlers). | 36 | * handlers). |
33 | */ | 37 | */ |
34 | 38 | ||
35 | asmlinkage void do_IRQ(int irq, struct pt_regs *regs) | 39 | asmlinkage void do_IRQ(int hwirq, struct pt_regs *regs) |
36 | { | 40 | { |
37 | struct pt_regs *old_regs = set_irq_regs(regs); | 41 | struct pt_regs *old_regs = set_irq_regs(regs); |
42 | int irq = irq_find_mapping(root_domain, hwirq); | ||
38 | 43 | ||
39 | if (irq >= NR_IRQS) { | 44 | if (hwirq >= NR_IRQS) { |
40 | printk(KERN_EMERG "%s: cannot handle IRQ %d\n", | 45 | printk(KERN_EMERG "%s: cannot handle IRQ %d\n", |
41 | __func__, irq); | 46 | __func__, hwirq); |
42 | } | 47 | } |
43 | 48 | ||
44 | irq_enter(); | 49 | irq_enter(); |
@@ -71,40 +76,39 @@ int arch_show_interrupts(struct seq_file *p, int prec) | |||
71 | 76 | ||
72 | static void xtensa_irq_mask(struct irq_data *d) | 77 | static void xtensa_irq_mask(struct irq_data *d) |
73 | { | 78 | { |
74 | cached_irq_mask &= ~(1 << d->irq); | 79 | cached_irq_mask &= ~(1 << d->hwirq); |
75 | set_sr (cached_irq_mask, intenable); | 80 | set_sr (cached_irq_mask, intenable); |
76 | } | 81 | } |
77 | 82 | ||
78 | static void xtensa_irq_unmask(struct irq_data *d) | 83 | static void xtensa_irq_unmask(struct irq_data *d) |
79 | { | 84 | { |
80 | cached_irq_mask |= 1 << d->irq; | 85 | cached_irq_mask |= 1 << d->hwirq; |
81 | set_sr (cached_irq_mask, intenable); | 86 | set_sr (cached_irq_mask, intenable); |
82 | } | 87 | } |
83 | 88 | ||
84 | static void xtensa_irq_enable(struct irq_data *d) | 89 | static void xtensa_irq_enable(struct irq_data *d) |
85 | { | 90 | { |
86 | variant_irq_enable(d->irq); | 91 | variant_irq_enable(d->hwirq); |
87 | xtensa_irq_unmask(d); | 92 | xtensa_irq_unmask(d); |
88 | } | 93 | } |
89 | 94 | ||
90 | static void xtensa_irq_disable(struct irq_data *d) | 95 | static void xtensa_irq_disable(struct irq_data *d) |
91 | { | 96 | { |
92 | xtensa_irq_mask(d); | 97 | xtensa_irq_mask(d); |
93 | variant_irq_disable(d->irq); | 98 | variant_irq_disable(d->hwirq); |
94 | } | 99 | } |
95 | 100 | ||
96 | static void xtensa_irq_ack(struct irq_data *d) | 101 | static void xtensa_irq_ack(struct irq_data *d) |
97 | { | 102 | { |
98 | set_sr(1 << d->irq, intclear); | 103 | set_sr(1 << d->hwirq, intclear); |
99 | } | 104 | } |
100 | 105 | ||
101 | static int xtensa_irq_retrigger(struct irq_data *d) | 106 | static int xtensa_irq_retrigger(struct irq_data *d) |
102 | { | 107 | { |
103 | set_sr (1 << d->irq, INTSET); | 108 | set_sr(1 << d->hwirq, intset); |
104 | return 1; | 109 | return 1; |
105 | } | 110 | } |
106 | 111 | ||
107 | |||
108 | static struct irq_chip xtensa_irq_chip = { | 112 | static struct irq_chip xtensa_irq_chip = { |
109 | .name = "xtensa", | 113 | .name = "xtensa", |
110 | .irq_enable = xtensa_irq_enable, | 114 | .irq_enable = xtensa_irq_enable, |
@@ -115,37 +119,99 @@ static struct irq_chip xtensa_irq_chip = { | |||
115 | .irq_retrigger = xtensa_irq_retrigger, | 119 | .irq_retrigger = xtensa_irq_retrigger, |
116 | }; | 120 | }; |
117 | 121 | ||
118 | void __init init_IRQ(void) | 122 | static int xtensa_irq_map(struct irq_domain *d, unsigned int irq, |
123 | irq_hw_number_t hw) | ||
119 | { | 124 | { |
120 | int index; | 125 | u32 mask = 1 << hw; |
121 | 126 | ||
122 | for (index = 0; index < XTENSA_NR_IRQS; index++) { | 127 | if (mask & XCHAL_INTTYPE_MASK_SOFTWARE) { |
123 | int mask = 1 << index; | 128 | irq_set_chip_and_handler_name(irq, &xtensa_irq_chip, |
124 | 129 | handle_simple_irq, "level"); | |
125 | if (mask & XCHAL_INTTYPE_MASK_SOFTWARE) | 130 | irq_set_status_flags(irq, IRQ_LEVEL); |
126 | irq_set_chip_and_handler(index, &xtensa_irq_chip, | 131 | } else if (mask & XCHAL_INTTYPE_MASK_EXTERN_EDGE) { |
127 | handle_simple_irq); | 132 | irq_set_chip_and_handler_name(irq, &xtensa_irq_chip, |
133 | handle_edge_irq, "edge"); | ||
134 | irq_clear_status_flags(irq, IRQ_LEVEL); | ||
135 | } else if (mask & XCHAL_INTTYPE_MASK_EXTERN_LEVEL) { | ||
136 | irq_set_chip_and_handler_name(irq, &xtensa_irq_chip, | ||
137 | handle_level_irq, "level"); | ||
138 | irq_set_status_flags(irq, IRQ_LEVEL); | ||
139 | } else if (mask & XCHAL_INTTYPE_MASK_TIMER) { | ||
140 | irq_set_chip_and_handler_name(irq, &xtensa_irq_chip, | ||
141 | handle_edge_irq, "edge"); | ||
142 | irq_clear_status_flags(irq, IRQ_LEVEL); | ||
143 | } else {/* XCHAL_INTTYPE_MASK_WRITE_ERROR */ | ||
144 | /* XCHAL_INTTYPE_MASK_NMI */ | ||
145 | |||
146 | irq_set_chip_and_handler_name(irq, &xtensa_irq_chip, | ||
147 | handle_level_irq, "level"); | ||
148 | irq_set_status_flags(irq, IRQ_LEVEL); | ||
149 | } | ||
150 | return 0; | ||
151 | } | ||
128 | 152 | ||
129 | else if (mask & XCHAL_INTTYPE_MASK_EXTERN_EDGE) | 153 | static unsigned map_ext_irq(unsigned ext_irq) |
130 | irq_set_chip_and_handler(index, &xtensa_irq_chip, | 154 | { |
131 | handle_edge_irq); | 155 | unsigned mask = XCHAL_INTTYPE_MASK_EXTERN_EDGE | |
156 | XCHAL_INTTYPE_MASK_EXTERN_LEVEL; | ||
157 | unsigned i; | ||
132 | 158 | ||
133 | else if (mask & XCHAL_INTTYPE_MASK_EXTERN_LEVEL) | 159 | for (i = 0; mask; ++i, mask >>= 1) { |
134 | irq_set_chip_and_handler(index, &xtensa_irq_chip, | 160 | if ((mask & 1) && ext_irq-- == 0) |
135 | handle_level_irq); | 161 | return i; |
162 | } | ||
163 | return XCHAL_NUM_INTERRUPTS; | ||
164 | } | ||
136 | 165 | ||
137 | else if (mask & XCHAL_INTTYPE_MASK_TIMER) | 166 | /* |
138 | irq_set_chip_and_handler(index, &xtensa_irq_chip, | 167 | * Device Tree IRQ specifier translation function which works with one or |
139 | handle_edge_irq); | 168 | * two cell bindings. First cell value maps directly to the hwirq number. |
169 | * Second cell if present specifies whether hwirq number is external (1) or | ||
170 | * internal (0). | ||
171 | */ | ||
172 | int xtensa_irq_domain_xlate(struct irq_domain *d, struct device_node *ctrlr, | ||
173 | const u32 *intspec, unsigned int intsize, | ||
174 | unsigned long *out_hwirq, unsigned int *out_type) | ||
175 | { | ||
176 | if (WARN_ON(intsize < 1 || intsize > 2)) | ||
177 | return -EINVAL; | ||
178 | if (intsize == 2 && intspec[1] == 1) { | ||
179 | unsigned int_irq = map_ext_irq(intspec[0]); | ||
180 | if (int_irq < XCHAL_NUM_INTERRUPTS) | ||
181 | *out_hwirq = int_irq; | ||
182 | else | ||
183 | return -EINVAL; | ||
184 | } else { | ||
185 | *out_hwirq = intspec[0]; | ||
186 | } | ||
187 | *out_type = IRQ_TYPE_NONE; | ||
188 | return 0; | ||
189 | } | ||
140 | 190 | ||
141 | else /* XCHAL_INTTYPE_MASK_WRITE_ERROR */ | 191 | static const struct irq_domain_ops xtensa_irq_domain_ops = { |
142 | /* XCHAL_INTTYPE_MASK_NMI */ | 192 | .xlate = xtensa_irq_domain_xlate, |
193 | .map = xtensa_irq_map, | ||
194 | }; | ||
143 | 195 | ||
144 | irq_set_chip_and_handler(index, &xtensa_irq_chip, | 196 | void __init init_IRQ(void) |
145 | handle_level_irq); | 197 | { |
146 | } | 198 | struct device_node *intc = NULL; |
147 | 199 | ||
148 | cached_irq_mask = 0; | 200 | cached_irq_mask = 0; |
201 | set_sr(~0, intclear); | ||
202 | |||
203 | #ifdef CONFIG_OF | ||
204 | /* The interrupt controller device node is mandatory */ | ||
205 | intc = of_find_compatible_node(NULL, NULL, "xtensa,pic"); | ||
206 | BUG_ON(!intc); | ||
207 | |||
208 | root_domain = irq_domain_add_linear(intc, NR_IRQS, | ||
209 | &xtensa_irq_domain_ops, NULL); | ||
210 | #else | ||
211 | root_domain = irq_domain_add_legacy(intc, NR_IRQS, 0, 0, | ||
212 | &xtensa_irq_domain_ops, NULL); | ||
213 | #endif | ||
214 | irq_set_default_host(root_domain); | ||
149 | 215 | ||
150 | variant_init_irq(); | 216 | variant_init_irq(); |
151 | } | 217 | } |