aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arc
diff options
context:
space:
mode:
authorVineet Gupta <vgupta@synopsys.com>2013-01-18 04:42:22 -0500
committerVineet Gupta <vgupta@synopsys.com>2013-02-15 12:46:01 -0500
commit4788a5942bc896803c87005be8c6dd14c373a2d3 (patch)
tree0999ce29d94a3517a30df1b1463fdd9c9d200b28 /arch/arc
parent769bc1fd7b8591a312d4c5c8834bc6510272938e (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/Kconfig19
-rw-r--r--arch/arc/include/asm/entry.h95
-rw-r--r--arch/arc/include/asm/irqflags.h6
-rw-r--r--arch/arc/kernel/entry.S117
-rw-r--r--arch/arc/kernel/irq.c104
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
209endchoice 209endchoice
210 210
211config 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
217if ARC_COMPACT_IRQ_LEVELS
218
219config ARC_IRQ3_LV2
220 bool
221
222config ARC_IRQ5_LV2
223 bool
224
225config ARC_IRQ6_LV2
226 bool
227
228endif
229
211config ARC_FPU_SAVE_RESTORE 230config 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 */
96static inline int arch_irqs_disabled_flags(unsigned long flags) 96static 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
101static inline int arch_irqs_disabled(void) 105static 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)
96VECTOR instr_service ; 0x10, Instrn Error (0x2) 98VECTOR instr_service ; 0x10, Instrn Error (0x2)
97 99
98; ******************** Device ISRs ********************** 100; ******************** Device ISRs **********************
101#ifdef CONFIG_ARC_IRQ3_LV2
102VECTOR handle_interrupt_level2
103#else
99VECTOR handle_interrupt_level1 104VECTOR handle_interrupt_level1
105#endif
100 106
101VECTOR handle_interrupt_level1 107VECTOR handle_interrupt_level1
102 108
109#ifdef CONFIG_ARC_IRQ5_LV2
110VECTOR handle_interrupt_level2
111#else
103VECTOR handle_interrupt_level1 112VECTOR handle_interrupt_level1
113#endif
104 114
115#ifdef CONFIG_ARC_IRQ6_LV2
116VECTOR handle_interrupt_level2
117#else
105VECTOR handle_interrupt_level1 118VECTOR handle_interrupt_level1
119#endif
106 120
107.rept 25 121.rept 25
108VECTOR handle_interrupt_level1 ; Other devices 122VECTOR handle_interrupt_level1 ; Other devices
@@ -139,6 +153,17 @@ VECTOR reserved ; Reserved Exceptions
139int1_saved_reg: 153int1_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
162int2_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; ---------------------------------------------
184ARC_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
2101:
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
225ARC_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
620not_exception: 694not_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
7252:
726 sub r9, r9, 1
727 st r9, [r10, THREAD_INFO_PREEMPT_COUNT]
728
729149:
730 ;return from level 2
731 RESTORE_ALL_INT2
732debug_marker_l2:
733 rtie
734
735not_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 */
27void __init arc_init_IRQ(void) 28void __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
187void 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 */
144void arch_local_irq_enable(void) 245void 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
161EXPORT_SYMBOL(arch_local_irq_enable); 263EXPORT_SYMBOL(arch_local_irq_enable);