diff options
author | Marc Gauthier <marc@tensilica.com> | 2013-01-05 04:57:17 +0400 |
---|---|---|
committer | Chris Zankel <chris@zankel.net> | 2013-02-23 19:12:52 -0800 |
commit | 2d1c645cc50b8f5a718b24bad9eb3931e7105d12 (patch) | |
tree | c385e5064cee10f79b9c359ddd99bd5d1b9f838a /arch/xtensa/kernel/traps.c | |
parent | d0b73b488c55df905ea8faaad079f8535629ed26 (diff) | |
download | linux-stable-2d1c645cc50b8f5a718b24bad9eb3931e7105d12.tar.gz linux-stable-2d1c645cc50b8f5a718b24bad9eb3931e7105d12.tar.bz2 linux-stable-2d1c645cc50b8f5a718b24bad9eb3931e7105d12.zip |
xtensa: dispatch medium-priority interrupts
Add support for dispatching medium-priority interrupts, that is,
interrupts of priority levels 2 to EXCM_LEVEL. IRQ handling may be
preempted by higher priority IRQ.
Signed-off-by: Marc Gauthier <marc@tensilica.com>
Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
Signed-off-by: Chris Zankel <chris@zankel.net>
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) } /* - * Level-1 interrupt. - * We currently have no priority encoding. + * IRQ handler. + * PS.INTLEVEL is the current IRQ priority level. */ -unsigned long ignored_level1_interrupts; extern void do_IRQ(int, struct pt_regs *); -void do_interrupt (struct pt_regs *regs) +void do_interrupt(struct pt_regs *regs) { - unsigned long intread = get_sr (interrupt); - unsigned long intenable = get_sr (intenable); - int i, mask; - - /* Handle all interrupts (no priorities). - * (Clear the interrupt before processing, in case it's - * edge-triggered or software-generated) - */ + static const unsigned int_level_mask[] = { + 0, + XCHAL_INTLEVEL1_MASK, + XCHAL_INTLEVEL2_MASK, + XCHAL_INTLEVEL3_MASK, + XCHAL_INTLEVEL4_MASK, + XCHAL_INTLEVEL5_MASK, + XCHAL_INTLEVEL6_MASK, + XCHAL_INTLEVEL7_MASK, + }; + unsigned level = get_sr(ps) & PS_INTLEVEL_MASK; + + if (WARN_ON_ONCE(level >= ARRAY_SIZE(int_level_mask))) + return; - for (i=0, mask = 1; i < XCHAL_NUM_INTERRUPTS; i++, mask <<= 1) { - if (mask & (intread & intenable)) { - set_sr (mask, intclear); - do_IRQ (i,regs); + for (;;) { + unsigned intread = get_sr(interrupt); + unsigned intenable = get_sr(intenable); + unsigned int_at_level = intread & intenable & + int_level_mask[level]; + + if (!int_at_level) + return; + + /* + * Clear the interrupt before processing, in case it's + * edge-triggered or software-generated + */ + while (int_at_level) { + unsigned i = __ffs(int_at_level); + unsigned mask = 1 << i; + + int_at_level ^= mask; + set_sr(mask, intclear); + do_IRQ(i, regs); } } } @@ -397,7 +418,7 @@ static inline void spill_registers(void) unsigned int a0, ps; __asm__ __volatile__ ( - "movi a14, " __stringify(PS_EXCM_BIT | 1) "\n\t" + "movi a14, " __stringify(PS_EXCM_BIT | LOCKLEVEL) "\n\t" "mov a12, a0\n\t" "rsr a13, sar\n\t" "xsr a14, ps\n\t" |