diff options
Diffstat (limited to 'arch/xtensa/kernel/traps.c')
-rw-r--r-- | arch/xtensa/kernel/traps.c | 55 |
1 files changed, 38 insertions, 17 deletions
diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c index 01e0111bf787..9b5c345d2b4f 100644 --- a/arch/xtensa/kernel/traps.c +++ b/arch/xtensa/kernel/traps.c | |||
@@ -193,28 +193,49 @@ void do_multihit(struct pt_regs *regs, unsigned long exccause) | |||
193 | } | 193 | } |
194 | 194 | ||
195 | /* | 195 | /* |
196 | * Level-1 interrupt. | 196 | * IRQ handler. |
197 | * We currently have no priority encoding. | 197 | * PS.INTLEVEL is the current IRQ priority level. |
198 | */ | 198 | */ |
199 | 199 | ||
200 | unsigned long ignored_level1_interrupts; | ||
201 | extern void do_IRQ(int, struct pt_regs *); | 200 | extern void do_IRQ(int, struct pt_regs *); |
202 | 201 | ||
203 | void do_interrupt (struct pt_regs *regs) | 202 | void do_interrupt(struct pt_regs *regs) |
204 | { | 203 | { |
205 | unsigned long intread = get_sr (interrupt); | 204 | static const unsigned int_level_mask[] = { |
206 | unsigned long intenable = get_sr (intenable); | 205 | 0, |
207 | int i, mask; | 206 | XCHAL_INTLEVEL1_MASK, |
208 | 207 | XCHAL_INTLEVEL2_MASK, | |
209 | /* Handle all interrupts (no priorities). | 208 | XCHAL_INTLEVEL3_MASK, |
210 | * (Clear the interrupt before processing, in case it's | 209 | XCHAL_INTLEVEL4_MASK, |
211 | * edge-triggered or software-generated) | 210 | XCHAL_INTLEVEL5_MASK, |
212 | */ | 211 | XCHAL_INTLEVEL6_MASK, |
212 | XCHAL_INTLEVEL7_MASK, | ||
213 | }; | ||
214 | unsigned level = get_sr(ps) & PS_INTLEVEL_MASK; | ||
215 | |||
216 | if (WARN_ON_ONCE(level >= ARRAY_SIZE(int_level_mask))) | ||
217 | return; | ||
213 | 218 | ||
214 | for (i=0, mask = 1; i < XCHAL_NUM_INTERRUPTS; i++, mask <<= 1) { | 219 | for (;;) { |
215 | if (mask & (intread & intenable)) { | 220 | unsigned intread = get_sr(interrupt); |
216 | set_sr (mask, intclear); | 221 | unsigned intenable = get_sr(intenable); |
217 | do_IRQ (i,regs); | 222 | unsigned int_at_level = intread & intenable & |
223 | int_level_mask[level]; | ||
224 | |||
225 | if (!int_at_level) | ||
226 | return; | ||
227 | |||
228 | /* | ||
229 | * Clear the interrupt before processing, in case it's | ||
230 | * edge-triggered or software-generated | ||
231 | */ | ||
232 | while (int_at_level) { | ||
233 | unsigned i = __ffs(int_at_level); | ||
234 | unsigned mask = 1 << i; | ||
235 | |||
236 | int_at_level ^= mask; | ||
237 | set_sr(mask, intclear); | ||
238 | do_IRQ(i, regs); | ||
218 | } | 239 | } |
219 | } | 240 | } |
220 | } | 241 | } |
@@ -397,7 +418,7 @@ static inline void spill_registers(void) | |||
397 | unsigned int a0, ps; | 418 | unsigned int a0, ps; |
398 | 419 | ||
399 | __asm__ __volatile__ ( | 420 | __asm__ __volatile__ ( |
400 | "movi a14, " __stringify(PS_EXCM_BIT | 1) "\n\t" | 421 | "movi a14, " __stringify(PS_EXCM_BIT | LOCKLEVEL) "\n\t" |
401 | "mov a12, a0\n\t" | 422 | "mov a12, a0\n\t" |
402 | "rsr a13, sar\n\t" | 423 | "rsr a13, sar\n\t" |
403 | "xsr a14, ps\n\t" | 424 | "xsr a14, ps\n\t" |