diff options
author | Vineet Gupta <vgupta@synopsys.com> | 2013-01-18 04:42:22 -0500 |
---|---|---|
committer | Vineet Gupta <vgupta@synopsys.com> | 2013-02-15 12:46:01 -0500 |
commit | 4788a5942bc896803c87005be8c6dd14c373a2d3 (patch) | |
tree | 0999ce29d94a3517a30df1b1463fdd9c9d200b28 /arch/arc | |
parent | 769bc1fd7b8591a312d4c5c8834bc6510272938e (diff) |
ARC: Support for high priority interrupts in the in-core intc
There is a bit of hack/kludge right now where we disable preemption if a
L2 (High prio) IRQ is taken while L1 (Low prio) is active.
Need to revisit this
Signed-off-by: Vineet Gupta <vgupta@synopsys.com>
Diffstat (limited to 'arch/arc')
-rw-r--r-- | arch/arc/Kconfig | 19 | ||||
-rw-r--r-- | arch/arc/include/asm/entry.h | 95 | ||||
-rw-r--r-- | arch/arc/include/asm/irqflags.h | 6 | ||||
-rw-r--r-- | arch/arc/kernel/entry.S | 117 | ||||
-rw-r--r-- | arch/arc/kernel/irq.c | 104 |
5 files changed, 339 insertions, 2 deletions
diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 405ea7a756b8..68350aa3d297 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig | |||
@@ -208,6 +208,25 @@ config ARC_PAGE_SIZE_4K | |||
208 | 208 | ||
209 | endchoice | 209 | endchoice |
210 | 210 | ||
211 | config ARC_COMPACT_IRQ_LEVELS | ||
212 | bool "ARCompact IRQ Priorities: High(2)/Low(1)" | ||
213 | default n | ||
214 | # Timer HAS to be high priority, for any other high priority config | ||
215 | select ARC_IRQ3_LV2 | ||
216 | |||
217 | if ARC_COMPACT_IRQ_LEVELS | ||
218 | |||
219 | config ARC_IRQ3_LV2 | ||
220 | bool | ||
221 | |||
222 | config ARC_IRQ5_LV2 | ||
223 | bool | ||
224 | |||
225 | config ARC_IRQ6_LV2 | ||
226 | bool | ||
227 | |||
228 | endif | ||
229 | |||
211 | config ARC_FPU_SAVE_RESTORE | 230 | config ARC_FPU_SAVE_RESTORE |
212 | bool "Enable FPU state persistence across context switch" | 231 | bool "Enable FPU state persistence across context switch" |
213 | default n | 232 | default n |
diff --git a/arch/arc/include/asm/entry.h b/arch/arc/include/asm/entry.h index 716f4f7b5cd2..23ef2de1e09f 100644 --- a/arch/arc/include/asm/entry.h +++ b/arch/arc/include/asm/entry.h | |||
@@ -5,6 +5,12 @@ | |||
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
6 | * published by the Free Software Foundation. | 6 | * published by the Free Software Foundation. |
7 | * | 7 | * |
8 | * Vineetg: March 2009 (Supporting 2 levels of Interrupts) | ||
9 | * Stack switching code can no longer reliably rely on the fact that | ||
10 | * if we are NOT in user mode, stack is switched to kernel mode. | ||
11 | * e.g. L2 IRQ interrupted a L1 ISR which had not yet completed | ||
12 | * it's prologue including stack switching from user mode | ||
13 | * | ||
8 | * Vineetg: Aug 28th 2008: Bug #94984 | 14 | * Vineetg: Aug 28th 2008: Bug #94984 |
9 | * -Zero Overhead Loop Context shd be cleared when entering IRQ/EXcp/Trap | 15 | * -Zero Overhead Loop Context shd be cleared when entering IRQ/EXcp/Trap |
10 | * Normally CPU does this automatically, however when doing FAKE rtie, | 16 | * Normally CPU does this automatically, however when doing FAKE rtie, |
@@ -268,6 +274,33 @@ | |||
268 | * assume SP is kernel mode SP. _NO_ need to do any stack switching | 274 | * assume SP is kernel mode SP. _NO_ need to do any stack switching |
269 | */ | 275 | */ |
270 | 276 | ||
277 | #ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS | ||
278 | /* However.... | ||
279 | * If Level 2 Interrupts enabled, we may end up with a corner case: | ||
280 | * 1. User Task executing | ||
281 | * 2. L1 IRQ taken, ISR starts (CPU auto-switched to KERNEL mode) | ||
282 | * 3. But before it could switch SP from USER to KERNEL stack | ||
283 | * a L2 IRQ "Interrupts" L1 | ||
284 | * Thay way although L2 IRQ happened in Kernel mode, stack is still | ||
285 | * not switched. | ||
286 | * To handle this, we may need to switch stack even if in kernel mode | ||
287 | * provided SP has values in range of USER mode stack ( < 0x7000_0000 ) | ||
288 | */ | ||
289 | brlo sp, VMALLOC_START, 88f | ||
290 | |||
291 | /* TODO: vineetg: | ||
292 | * We need to be a bit more cautious here. What if a kernel bug in | ||
293 | * L1 ISR, caused SP to go whaco (some small value which looks like | ||
294 | * USER stk) and then we take L2 ISR. | ||
295 | * Above brlo alone would treat it as a valid L1-L2 sceanrio | ||
296 | * instead of shouting alound | ||
297 | * The only feasible way is to make sure this L2 happened in | ||
298 | * L1 prelogue ONLY i.e. ilink2 is less than a pre-set marker in | ||
299 | * L1 ISR before it switches stack | ||
300 | */ | ||
301 | |||
302 | #endif | ||
303 | |||
271 | /* Save Pre Intr/Exception KERNEL MODE SP on kernel stack | 304 | /* Save Pre Intr/Exception KERNEL MODE SP on kernel stack |
272 | * safe-keeping not really needed, but it keeps the epilogue code | 305 | * safe-keeping not really needed, but it keeps the epilogue code |
273 | * (SP restore) simpler/uniform. | 306 | * (SP restore) simpler/uniform. |
@@ -503,6 +536,42 @@ | |||
503 | sub sp, sp, 4 | 536 | sub sp, sp, 4 |
504 | .endm | 537 | .endm |
505 | 538 | ||
539 | .macro SAVE_ALL_INT2 | ||
540 | |||
541 | /* TODO-vineetg: SMP we can't use global nor can we use | ||
542 | * SCRATCH0 as we do for int1 because while int1 is using | ||
543 | * it, int2 can come | ||
544 | */ | ||
545 | /* retsore original r9 , saved in sys_saved_r9 */ | ||
546 | ld r9, [@int2_saved_reg] | ||
547 | |||
548 | /* now we are ready to save the remaining context :) */ | ||
549 | st orig_r8_IS_IRQ2, [sp, 8] /* Event Type */ | ||
550 | st 0, [sp, 4] /* orig_r0 , N/A for IRQ */ | ||
551 | SAVE_CALLER_SAVED | ||
552 | st.a r26, [sp, -4] /* gp */ | ||
553 | st.a fp, [sp, -4] | ||
554 | st.a blink, [sp, -4] | ||
555 | st.a ilink2, [sp, -4] | ||
556 | lr r9, [status32_l2] | ||
557 | st.a r9, [sp, -4] | ||
558 | st.a lp_count, [sp, -4] | ||
559 | lr r9, [lp_end] | ||
560 | st.a r9, [sp, -4] | ||
561 | lr r9, [lp_start] | ||
562 | st.a r9, [sp, -4] | ||
563 | lr r9, [bta_l2] | ||
564 | st.a r9, [sp, -4] | ||
565 | |||
566 | #ifdef PT_REGS_CANARY | ||
567 | mov r9, 0xdeadbee2 | ||
568 | st r9, [sp, -4] | ||
569 | #endif | ||
570 | |||
571 | /* move up by 1 word to "create" pt_regs->"stack_place_holder" */ | ||
572 | sub sp, sp, 4 | ||
573 | .endm | ||
574 | |||
506 | /*-------------------------------------------------------------- | 575 | /*-------------------------------------------------------------- |
507 | * Restore all registers used by interrupt handlers. | 576 | * Restore all registers used by interrupt handlers. |
508 | * | 577 | * |
@@ -537,6 +606,32 @@ | |||
537 | /* orig_r0 and orig_r8 skipped automatically */ | 606 | /* orig_r0 and orig_r8 skipped automatically */ |
538 | .endm | 607 | .endm |
539 | 608 | ||
609 | .macro RESTORE_ALL_INT2 | ||
610 | add sp, sp, 4 /* hop over unused "pt_regs->stack_place_holder" */ | ||
611 | |||
612 | ld.ab r9, [sp, 4] | ||
613 | sr r9, [bta_l2] | ||
614 | ld.ab r9, [sp, 4] | ||
615 | sr r9, [lp_start] | ||
616 | ld.ab r9, [sp, 4] | ||
617 | sr r9, [lp_end] | ||
618 | ld.ab r9, [sp, 4] | ||
619 | mov lp_count, r9 | ||
620 | ld.ab r9, [sp, 4] | ||
621 | sr r9, [status32_l2] | ||
622 | ld.ab r9, [sp, 4] | ||
623 | mov ilink2, r9 | ||
624 | ld.ab blink, [sp, 4] | ||
625 | ld.ab fp, [sp, 4] | ||
626 | ld.ab r26, [sp, 4] /* gp */ | ||
627 | RESTORE_CALLER_SAVED | ||
628 | |||
629 | ld sp, [sp] /* restore original sp */ | ||
630 | /* orig_r0 and orig_r8 skipped automatically */ | ||
631 | |||
632 | .endm | ||
633 | |||
634 | |||
540 | /* Get CPU-ID of this core */ | 635 | /* Get CPU-ID of this core */ |
541 | .macro GET_CPU_ID reg | 636 | .macro GET_CPU_ID reg |
542 | lr \reg, [identity] | 637 | lr \reg, [identity] |
diff --git a/arch/arc/include/asm/irqflags.h b/arch/arc/include/asm/irqflags.h index 5cc1080d7c26..ccd84806b62f 100644 --- a/arch/arc/include/asm/irqflags.h +++ b/arch/arc/include/asm/irqflags.h | |||
@@ -95,7 +95,11 @@ static inline long arch_local_save_flags(void) | |||
95 | */ | 95 | */ |
96 | static inline int arch_irqs_disabled_flags(unsigned long flags) | 96 | static inline int arch_irqs_disabled_flags(unsigned long flags) |
97 | { | 97 | { |
98 | return !(flags & (STATUS_E1_MASK)); | 98 | return !(flags & (STATUS_E1_MASK |
99 | #ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS | ||
100 | | STATUS_E2_MASK | ||
101 | #endif | ||
102 | )); | ||
99 | } | 103 | } |
100 | 104 | ||
101 | static inline int arch_irqs_disabled(void) | 105 | static inline int arch_irqs_disabled(void) |
diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S index 76697aecd165..e33a0bf45589 100644 --- a/arch/arc/kernel/entry.S +++ b/arch/arc/kernel/entry.S | |||
@@ -31,6 +31,8 @@ | |||
31 | * exception. Thus FAKE RTIE needed in low level Priv-Violation handler. | 31 | * exception. Thus FAKE RTIE needed in low level Priv-Violation handler. |
32 | * Instr Error could also cause similar scenario, so same there as well. | 32 | * Instr Error could also cause similar scenario, so same there as well. |
33 | * | 33 | * |
34 | * Vineetg: March 2009 (Supporting 2 levels of Interrupts) | ||
35 | * | ||
34 | * Vineetg: Aug 28th 2008: Bug #94984 | 36 | * Vineetg: Aug 28th 2008: Bug #94984 |
35 | * -Zero Overhead Loop Context shd be cleared when entering IRQ/EXcp/Trap | 37 | * -Zero Overhead Loop Context shd be cleared when entering IRQ/EXcp/Trap |
36 | * Normally CPU does this automatically, however when doing FAKE rtie, | 38 | * Normally CPU does this automatically, however when doing FAKE rtie, |
@@ -96,13 +98,25 @@ VECTOR mem_service ; 0x8, Mem exception (0x1) | |||
96 | VECTOR instr_service ; 0x10, Instrn Error (0x2) | 98 | VECTOR instr_service ; 0x10, Instrn Error (0x2) |
97 | 99 | ||
98 | ; ******************** Device ISRs ********************** | 100 | ; ******************** Device ISRs ********************** |
101 | #ifdef CONFIG_ARC_IRQ3_LV2 | ||
102 | VECTOR handle_interrupt_level2 | ||
103 | #else | ||
99 | VECTOR handle_interrupt_level1 | 104 | VECTOR handle_interrupt_level1 |
105 | #endif | ||
100 | 106 | ||
101 | VECTOR handle_interrupt_level1 | 107 | VECTOR handle_interrupt_level1 |
102 | 108 | ||
109 | #ifdef CONFIG_ARC_IRQ5_LV2 | ||
110 | VECTOR handle_interrupt_level2 | ||
111 | #else | ||
103 | VECTOR handle_interrupt_level1 | 112 | VECTOR handle_interrupt_level1 |
113 | #endif | ||
104 | 114 | ||
115 | #ifdef CONFIG_ARC_IRQ6_LV2 | ||
116 | VECTOR handle_interrupt_level2 | ||
117 | #else | ||
105 | VECTOR handle_interrupt_level1 | 118 | VECTOR handle_interrupt_level1 |
119 | #endif | ||
106 | 120 | ||
107 | .rept 25 | 121 | .rept 25 |
108 | VECTOR handle_interrupt_level1 ; Other devices | 122 | VECTOR handle_interrupt_level1 ; Other devices |
@@ -139,6 +153,17 @@ VECTOR reserved ; Reserved Exceptions | |||
139 | int1_saved_reg: | 153 | int1_saved_reg: |
140 | .zero 4 | 154 | .zero 4 |
141 | 155 | ||
156 | /* Each Interrupt level needs it's own scratch */ | ||
157 | #ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS | ||
158 | |||
159 | .section .data ; NOT .global | ||
160 | .type int2_saved_reg, @object | ||
161 | .size int2_saved_reg, 4 | ||
162 | int2_saved_reg: | ||
163 | .zero 4 | ||
164 | |||
165 | #endif | ||
166 | |||
142 | ; --------------------------------------------- | 167 | ; --------------------------------------------- |
143 | .section .text, "ax",@progbits | 168 | .section .text, "ax",@progbits |
144 | 169 | ||
@@ -152,6 +177,55 @@ reserved: ; processor restart | |||
152 | 177 | ||
153 | ;##################### Interrupt Handling ############################## | 178 | ;##################### Interrupt Handling ############################## |
154 | 179 | ||
180 | #ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS | ||
181 | ; --------------------------------------------- | ||
182 | ; Level 2 ISR: Can interrupt a Level 1 ISR | ||
183 | ; --------------------------------------------- | ||
184 | ARC_ENTRY handle_interrupt_level2 | ||
185 | |||
186 | ; TODO-vineetg for SMP this wont work | ||
187 | ; free up r9 as scratchpad | ||
188 | st r9, [@int2_saved_reg] | ||
189 | |||
190 | ;Which mode (user/kernel) was the system in when intr occured | ||
191 | lr r9, [status32_l2] | ||
192 | |||
193 | SWITCH_TO_KERNEL_STK | ||
194 | SAVE_ALL_INT2 | ||
195 | |||
196 | ;------------------------------------------------------ | ||
197 | ; if L2 IRQ interrupted a L1 ISR, disable preemption | ||
198 | ;------------------------------------------------------ | ||
199 | |||
200 | ld r9, [sp, PT_status32] ; get statu32_l2 (saved in pt_regs) | ||
201 | bbit0 r9, STATUS_A1_BIT, 1f ; L1 not active when L2 IRQ, so normal | ||
202 | |||
203 | ; A1 is set in status32_l2 | ||
204 | ; bump thread_info->preempt_count (Disable preemption) | ||
205 | GET_CURR_THR_INFO_FROM_SP r10 | ||
206 | ld r9, [r10, THREAD_INFO_PREEMPT_COUNT] | ||
207 | add r9, r9, 1 | ||
208 | st r9, [r10, THREAD_INFO_PREEMPT_COUNT] | ||
209 | |||
210 | 1: | ||
211 | ;------------------------------------------------------ | ||
212 | ; setup params for Linux common ISR and invoke it | ||
213 | ;------------------------------------------------------ | ||
214 | lr r0, [icause2] | ||
215 | and r0, r0, 0x1f | ||
216 | |||
217 | bl.d @arch_do_IRQ | ||
218 | mov r1, sp | ||
219 | |||
220 | mov r8,0x2 | ||
221 | sr r8, [AUX_IRQ_LV12] ; clear bit in Sticky Status Reg | ||
222 | |||
223 | b ret_from_exception | ||
224 | |||
225 | ARC_EXIT handle_interrupt_level2 | ||
226 | |||
227 | #endif | ||
228 | |||
155 | ; --------------------------------------------- | 229 | ; --------------------------------------------- |
156 | ; Level 1 ISR | 230 | ; Level 1 ISR |
157 | ; --------------------------------------------- | 231 | ; --------------------------------------------- |
@@ -619,6 +693,49 @@ restore_regs : | |||
619 | 693 | ||
620 | not_exception: | 694 | not_exception: |
621 | 695 | ||
696 | #ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS | ||
697 | |||
698 | bbit0 r10, STATUS_A2_BIT, not_level2_interrupt | ||
699 | |||
700 | ;------------------------------------------------------------------ | ||
701 | ; if L2 IRQ interrupted a L1 ISR, we'd disbaled preemption earlier | ||
702 | ; so that sched doesnt move to new task, causing L1 to be delayed | ||
703 | ; undeterministically. Now that we've achieved that, lets reset | ||
704 | ; things to what they were, before returning from L2 context | ||
705 | ;---------------------------------------------------------------- | ||
706 | |||
707 | ld r9, [sp, PT_orig_r8] ; get orig_r8 to make sure it is | ||
708 | brne r9, orig_r8_IS_IRQ2, 149f ; infact a L2 ISR ret path | ||
709 | |||
710 | ld r9, [sp, PT_status32] ; get statu32_l2 (saved in pt_regs) | ||
711 | bbit0 r9, STATUS_A1_BIT, 149f ; L1 not active when L2 IRQ, so normal | ||
712 | |||
713 | ; A1 is set in status32_l2 | ||
714 | ; decrement thread_info->preempt_count (re-enable preemption) | ||
715 | GET_CURR_THR_INFO_FROM_SP r10 | ||
716 | ld r9, [r10, THREAD_INFO_PREEMPT_COUNT] | ||
717 | |||
718 | ; paranoid check, given A1 was active when A2 happened, preempt count | ||
719 | ; must not be 0 beccause we would have incremented it. | ||
720 | ; If this does happen we simply HALT as it means a BUG !!! | ||
721 | cmp r9, 0 | ||
722 | bnz 2f | ||
723 | flag 1 | ||
724 | |||
725 | 2: | ||
726 | sub r9, r9, 1 | ||
727 | st r9, [r10, THREAD_INFO_PREEMPT_COUNT] | ||
728 | |||
729 | 149: | ||
730 | ;return from level 2 | ||
731 | RESTORE_ALL_INT2 | ||
732 | debug_marker_l2: | ||
733 | rtie | ||
734 | |||
735 | not_level2_interrupt: | ||
736 | |||
737 | #endif | ||
738 | |||
622 | bbit0 r10, STATUS_A1_BIT, not_level1_interrupt | 739 | bbit0 r10, STATUS_A1_BIT, not_level1_interrupt |
623 | 740 | ||
624 | ;return from level 1 | 741 | ;return from level 1 |
diff --git a/arch/arc/kernel/irq.c b/arch/arc/kernel/irq.c index 3c18e66386c7..ca70894e2309 100644 --- a/arch/arc/kernel/irq.c +++ b/arch/arc/kernel/irq.c | |||
@@ -23,15 +23,32 @@ | |||
23 | * what it does ? | 23 | * what it does ? |
24 | * -setup Vector Table Base Reg - in case Linux not linked at 0x8000_0000 | 24 | * -setup Vector Table Base Reg - in case Linux not linked at 0x8000_0000 |
25 | * -Disable all IRQs (on CPU side) | 25 | * -Disable all IRQs (on CPU side) |
26 | * -Optionally, setup the High priority Interrupts as Level 2 IRQs | ||
26 | */ | 27 | */ |
27 | void __init arc_init_IRQ(void) | 28 | void __init arc_init_IRQ(void) |
28 | { | 29 | { |
29 | int level_mask = level_mask; | 30 | int level_mask = 0; |
30 | 31 | ||
31 | write_aux_reg(AUX_INTR_VEC_BASE, _int_vec_base_lds); | 32 | write_aux_reg(AUX_INTR_VEC_BASE, _int_vec_base_lds); |
32 | 33 | ||
33 | /* Disable all IRQs: enable them as devices request */ | 34 | /* Disable all IRQs: enable them as devices request */ |
34 | write_aux_reg(AUX_IENABLE, 0); | 35 | write_aux_reg(AUX_IENABLE, 0); |
36 | |||
37 | /* setup any high priority Interrupts (Level2 in ARCompact jargon) */ | ||
38 | #ifdef CONFIG_ARC_IRQ3_LV2 | ||
39 | level_mask |= (1 << 3); | ||
40 | #endif | ||
41 | #ifdef CONFIG_ARC_IRQ5_LV2 | ||
42 | level_mask |= (1 << 5); | ||
43 | #endif | ||
44 | #ifdef CONFIG_ARC_IRQ6_LV2 | ||
45 | level_mask |= (1 << 6); | ||
46 | #endif | ||
47 | |||
48 | if (level_mask) { | ||
49 | pr_info("Level-2 interrupts bitset %x\n", level_mask); | ||
50 | write_aux_reg(AUX_IRQ_LEV, level_mask); | ||
51 | } | ||
35 | } | 52 | } |
36 | 53 | ||
37 | /* | 54 | /* |
@@ -141,6 +158,90 @@ int __init get_hw_config_num_irq(void) | |||
141 | return 0; | 158 | return 0; |
142 | } | 159 | } |
143 | 160 | ||
161 | /* | ||
162 | * arch_local_irq_enable - Enable interrupts. | ||
163 | * | ||
164 | * 1. Explicitly called to re-enable interrupts | ||
165 | * 2. Implicitly called from spin_unlock_irq, write_unlock_irq etc | ||
166 | * which maybe in hard ISR itself | ||
167 | * | ||
168 | * Semantics of this function change depending on where it is called from: | ||
169 | * | ||
170 | * -If called from hard-ISR, it must not invert interrupt priorities | ||
171 | * e.g. suppose TIMER is high priority (Level 2) IRQ | ||
172 | * Time hard-ISR, timer_interrupt( ) calls spin_unlock_irq several times. | ||
173 | * Here local_irq_enable( ) shd not re-enable lower priority interrupts | ||
174 | * -If called from soft-ISR, it must re-enable all interrupts | ||
175 | * soft ISR are low prioity jobs which can be very slow, thus all IRQs | ||
176 | * must be enabled while they run. | ||
177 | * Now hardware context wise we may still be in L2 ISR (not done rtie) | ||
178 | * still we must re-enable both L1 and L2 IRQs | ||
179 | * Another twist is prev scenario with flow being | ||
180 | * L1 ISR ==> interrupted by L2 ISR ==> L2 soft ISR | ||
181 | * here we must not re-enable Ll as prev Ll Interrupt's h/w context will get | ||
182 | * over-written (this is deficiency in ARC700 Interrupt mechanism) | ||
183 | */ | ||
184 | |||
185 | #ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS /* Complex version for 2 IRQ levels */ | ||
186 | |||
187 | void arch_local_irq_enable(void) | ||
188 | { | ||
189 | |||
190 | unsigned long flags; | ||
191 | flags = arch_local_save_flags(); | ||
192 | |||
193 | /* Allow both L1 and L2 at the onset */ | ||
194 | flags |= (STATUS_E1_MASK | STATUS_E2_MASK); | ||
195 | |||
196 | /* Called from hard ISR (between irq_enter and irq_exit) */ | ||
197 | if (in_irq()) { | ||
198 | |||
199 | /* If in L2 ISR, don't re-enable any further IRQs as this can | ||
200 | * cause IRQ priorities to get upside down. e.g. it could allow | ||
201 | * L1 be taken while in L2 hard ISR which is wrong not only in | ||
202 | * theory, it can also cause the dreaded L1-L2-L1 scenario | ||
203 | */ | ||
204 | if (flags & STATUS_A2_MASK) | ||
205 | flags &= ~(STATUS_E1_MASK | STATUS_E2_MASK); | ||
206 | |||
207 | /* Even if in L1 ISR, allowe Higher prio L2 IRQs */ | ||
208 | else if (flags & STATUS_A1_MASK) | ||
209 | flags &= ~(STATUS_E1_MASK); | ||
210 | } | ||
211 | |||
212 | /* called from soft IRQ, ideally we want to re-enable all levels */ | ||
213 | |||
214 | else if (in_softirq()) { | ||
215 | |||
216 | /* However if this is case of L1 interrupted by L2, | ||
217 | * re-enabling both may cause whaco L1-L2-L1 scenario | ||
218 | * because ARC700 allows level 1 to interrupt an active L2 ISR | ||
219 | * Thus we disable both | ||
220 | * However some code, executing in soft ISR wants some IRQs | ||
221 | * to be enabled so we re-enable L2 only | ||
222 | * | ||
223 | * How do we determine L1 intr by L2 | ||
224 | * -A2 is set (means in L2 ISR) | ||
225 | * -E1 is set in this ISR's pt_regs->status32 which is | ||
226 | * saved copy of status32_l2 when l2 ISR happened | ||
227 | */ | ||
228 | struct pt_regs *pt = get_irq_regs(); | ||
229 | if ((flags & STATUS_A2_MASK) && pt && | ||
230 | (pt->status32 & STATUS_A1_MASK)) { | ||
231 | /*flags &= ~(STATUS_E1_MASK | STATUS_E2_MASK); */ | ||
232 | flags &= ~(STATUS_E1_MASK); | ||
233 | } | ||
234 | } | ||
235 | |||
236 | arch_local_irq_restore(flags); | ||
237 | } | ||
238 | |||
239 | #else /* ! CONFIG_ARC_COMPACT_IRQ_LEVELS */ | ||
240 | |||
241 | /* | ||
242 | * Simpler version for only 1 level of interrupt | ||
243 | * Here we only Worry about Level 1 Bits | ||
244 | */ | ||
144 | void arch_local_irq_enable(void) | 245 | void arch_local_irq_enable(void) |
145 | { | 246 | { |
146 | unsigned long flags; | 247 | unsigned long flags; |
@@ -158,4 +259,5 @@ void arch_local_irq_enable(void) | |||
158 | flags |= (STATUS_E1_MASK | STATUS_E2_MASK); | 259 | flags |= (STATUS_E1_MASK | STATUS_E2_MASK); |
159 | arch_local_irq_restore(flags); | 260 | arch_local_irq_restore(flags); |
160 | } | 261 | } |
262 | #endif | ||
161 | EXPORT_SYMBOL(arch_local_irq_enable); | 263 | EXPORT_SYMBOL(arch_local_irq_enable); |