1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - libraries - OS
3   File:     os_irqHandler_inTCM.c
4 
5   Copyright 2003-2008 Nintendo. All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law. They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Date:: 2008-09-18#$
14   $Rev: 8573 $
15   $Author: okubata_ryoma $
16  *---------------------------------------------------------------------------*/
17 #include        <nitro/code32.h>
18 #include        <nitro/types.h>
19 #include        <nitro/os/common/interrupt.h>
20 #include        <nitro/os/common/thread.h>
21 #include        <nitro/os/common/systemCall.h>
22 
23 #ifdef SDK_ARM9
24 #include        <nitro/hw/ARM9/mmap_global.h>
25 #include        <nitro/hw/ARM9/ioreg_OS.h>
26 #else // SDK_ARM9
27 #include        <nitro/hw/ARM7/mmap_global.h>
28 #include        <nitro/hw/ARM7/ioreg_OS.h>
29 #endif // SDK_ARM9
30 
31 
32 //---- Thread queue for interrupt
33 OSThreadQueue  OSi_IrqThreadQueue;
34 
35 /*---------------------------------------------------------------------------*
36   Name:         OS_IRQHandler_inTCM
37 
38   Description:  Interrupts branch processing (uses the OS_InterruptTable).
39 
40   Arguments:    None.
41 
42   Returns:      None.
43  *---------------------------------------------------------------------------*/
OS_IrqHandler(void)44 asm void OS_IrqHandler( void )
45 {
46 #ifdef  SDK_NO_THREAD
47 #else
48         stmfd   sp!, { lr }                     // Save LR
49 #endif
50         // Get IE address
51         mov     r12,      #HW_REG_BASE
52         add     r12, r12, #REG_IE_OFFSET        // r12: REG_IE address
53 
54         // Get IME
55         ldr     r1, [ r12, #REG_IME_ADDR - REG_IE_ADDR ]  // r1: IME
56 
57         // If IME==0 then return (without changing IF)
58         cmp     r1, #0
59 #ifdef  SDK_NO_THREAD
60         bxeq    lr
61 #else
62         ldmeqfd sp!, { pc }
63 #endif
64 
65         // Get IE&IF
66         ldmia   r12, { r1-r2 }                  // r1: IE, r2: IF
67         ands    r1, r1, r2                      // r1: IE & IF
68 
69         // If IE&IF==0 then return (without changing IF)
70 #ifdef  SDK_NO_THREAD
71         bxeq    lr
72 #else
73         ldmeqfd sp!, { pc }
74 #endif
75 
76 
77 #if     defined(SDK_ARM9) && !defined(SDK_CWBUG_PROC_OPT)
78         //--------------------------------------------------
79         // IRQ HANDLING CODE for ARCHITECTURE VERSION 5
80         //--------------------------------------------------
81 
82         // Get lowest 1 bit
83         mov     r3, #1<<31
84 @1:     clz     r0, r1                  // Count zero of high bit
85         bics    r1, r1, r3, LSR r0
86         bne     @1
87 
88         // Clear IF
89         mov     r1, r3, LSR r0
90         str     r1, [ r12, #REG_IF_ADDR - REG_IE_ADDR ]
91 
92         rsbs    r0, r0, #31
93 
94 #else //defined(SDK_ARM9) && !defined(SDK_CWBUG_PROC_OPT)
95         //--------------------------------------------------
96         // IRQ HANDLING CODE for ARCHITECTURE VERSION 4
97         //--------------------------------------------------
98         mov     r3, #1
99         mov     r0, #0
100 @1:     ands    r2, r1, r3, LSL r0              // Count zero of high bit
101         addeq   r0, r0, #1
102         beq     @1
103 
104         // Clear IF
105         str     r2, [ r12, #REG_IF_ADDR - REG_IE_ADDR ]
106 #endif //defined(SDK_ARM9) && !defined(SDK_CWBUG_PROC_OPT)
107 
108         // Get jump vector
109 #ifdef  SDK_DEBUG
110         cmp     r0, #OS_IRQ_TABLE_MAX
111 @2:     bge     @2                              // Error Trap
112 #endif//SDK_DEBUG
113         ldr     r1, =OS_IRQTable
114         ldr     r0, [ r1, r0, LSL #2 ]
115 
116 #ifdef  SDK_NO_THREAD
117         bx      r0
118 #else //SDK_NO_THREAD
119         ldr     lr, =OS_IrqHandler_ThreadSwitch
120         bx      r0      // Set return address for thread rescheduling
121 #endif//SDK_NO_THREAD
122 }
123 
124 
125 
126 /*---------------------------------------------------------------------------*
127   Name:         OS_IRQHandler_ThreadSwitch
128 
129   Description:  Interrupts branch processing (uses the OS_InterruptTable).
130 
131   Arguments:    None.
132 
133   Returns:      None.
134  *---------------------------------------------------------------------------*/
OS_IrqHandler_ThreadSwitch(void)135 asm void OS_IrqHandler_ThreadSwitch(void)
136 {
137 
138 #ifdef  SDK_NO_THREAD
139 #else
140         //--------------------------------------------------
141         // Wakeup threads in OSi_IrqThreadQueue
142         //--------------------------------------------------
143         ldr     r12, =OSi_IrqThreadQueue
144 #if     ( OS_THREAD_MAX_NUM <= 16 )
145         ldrh    r3, [r12]                       // r3 = OSi_IrqThreadQueue
146         mov     r0, #0
147         cmp     r3, #0
148         beq     @thread_switch                  // If r3 == 0 exit
149         strh    r0, [r12]                       // OSi_IrqThreadQueue = 0
150 #else   //OS_THREAD_MAX_NUM
151         ldr     r3, [r12]
152         mov     r0, #0
153         cmp     r3, #0
154         beq     @thread_switch
155         str     r0, [r12]
156 #endif  //OS_THREAD_MAX_NUM
157 
158         ldr     r12, =OSi_ThreadInfo    // isNeedRescheduling=OS_THREADINFO_RESCHEDULING_DISABLE_LATER
159         mov     r1,  #1
160         strh    r1,  [ r12, #OS_THREADINFO_OFFSET_ISNEEDRESCHEDULING ]
161         ldr     r12, [ r12, #OS_THREADINFO_OFFSET_LIST ]    // r12 = OSi_ThreadInfo.list
162         mov     r2,  #OS_THREAD_STATE_READY
163 @1:
164         cmp     r12, #0
165         beq     @thread_switch
166         ldr     r0,  [r12, #OS_THREAD_OFFSET_ID]
167         tst     r3,  r1, LSL r0                      // OSi_IrqThreadQueue & (1<<thread->id)
168         strne   r2,  [r12, #OS_THREAD_OFFSET_STATE]
169         ldr     r12, [r12, #OS_THREAD_OFFSET_NEXT]
170         b       @1
171 @thread_switch:
172 
173         //--------------------------------------------------
174         // THREAD SWITCH
175         //--------------------------------------------------
176         // Pseudo-code
177         //
178         // if ( isNeedRescheduling == FALSE ) return;
179         // isNeedRescheduling = FALSE;
180         //
181         // // OS_SelectThread
182         // OSThread* t = OSi_ThreadInfo.list;
183         // while( t && ! OS_IsThreadRunnable( t ) ){ t = t->next; }
184         // return t;
185         //
186         // select:
187         // current = CurrentThread;
188         // if ( next == current ) return;
189         // CurrentThread = next;
190         // OS_SaveContext( current );
191         // OS_LoadContext( next );
192         //
193 
194         // [[[ new OS_SelectThread ]]]
195 
196         ldr     r12, =OSi_ThreadInfo
197         ldrh    r1, [ r12, #OS_THREADINFO_OFFSET_ISNEEDRESCHEDULING ]
198         cmp     r1, #0
199         ldreq   pc, [ sp ], #4          // Return if OSi_IsNeedResceduling == 0
200         mov     r1, #0
201         strh    r1, [ r12, #OS_THREADINFO_OFFSET_ISNEEDRESCHEDULING ]
202 
203         // ---- OS_SelectThread (disable FIQ to support IS-Debugger snooping thread information)
204         mov     r3, #HW_PSR_IRQ_MODE|HW_PSR_FIQ_DISABLE|HW_PSR_IRQ_DISABLE|HW_PSR_ARM_STATE
205         msr     cpsr_c, r3
206 
207         add     r2, r12, #OS_THREADINFO_OFFSET_LIST // r2 = &OSi_ThreadInfo.list
208         ldr     r1, [r2]                            // r1 = *r2 = TopOfList
209 @11:
210         cmp     r1, #0
211         ldrneh  r0, [ r1, #OS_THREAD_OFFSET_STATE ] // r0 = t->state
212         cmpne   r0, #OS_THREAD_STATE_READY
213         ldrne   r1, [ r1, #OS_THREAD_OFFSET_NEXT ]
214         bne     @11
215 
216         cmp     r1, #0
217         bne     @12
218 
219 _dont_switched_:
220         mov     r3, #HW_PSR_IRQ_MODE|HW_PSR_IRQ_DISABLE|HW_PSR_ARM_STATE
221         msr     cpsr_c, r3
222         ldr     pc, [ sp ], #4          // Return to irq master handler
223         // This part of the code is not reached
224 
225 @12:
226         // ---- OS_GetCurrentThread
227         ldr     r0, [ r12, #OS_THREADINFO_OFFSET_CURRENT ]
228         cmp     r1, r0
229         beq     _dont_switched_         // Return if no thread switching
230 
231         // Call thread switch callback (need to save register r0, r1, r12)
232         ldr     r3, [ r12, #OS_THREADINFO_OFFSET_SWITCHCALLBACK ]
233         cmp     r3, #0
234         beq     @13                     // Skip calling callback when callback == 0
235         stmfd   sp!, { r0, r1, r12 }
236         mov     lr, pc
237         bx      r3
238         ldmfd   sp!, { r0, r1, r12 }
239 
240 @13:
241         // ---- OS_SetCurrentThread
242         str     r1, [ r12, #OS_THREADINFO_OFFSET_CURRENT ]
243 
244         // ---- OS_SaveContext
245         // r0=currentThread  r1=nextThread
246         // stack=Lo[LR,R0,R1,R2,R3,R12,LR]Hi
247         mrs     r2, SPSR
248         str     r2, [ r0, #OS_THREAD_OFFSET_CONTEXT ]!  // *r0=context:CPSR
249 
250 #if defined(SDK_ARM9) && !defined(SDK_CP_NO_SAFE)
251         // First, save CP context
252         stmfd   sp!, { r0, r1 }
253         add     r0, r0, #OS_THREAD_OFFSET_CONTEXT
254         add     r0, r0, #OS_CONTEXT_CP_CONTEXT
255         ldr     r1, =CP_SaveContext
256         blx     r1
257         ldmfd   sp!, { r0, r1 }
258 #endif
259 
260         ldmib   sp!, { r2,r3 }          // Get R0,R1    // *sp=stack:R1
261         stmib   r0!, { r2,r3 }          // Put R0,R1    // *r0=context:R1
262 
263         ldmib   sp!, { r2,r3,r12,r14 }  // Get R2,R3,R12,LR / *sp=stack:LR
264         stmib   r0!, { r2-r14        }^ // Put R2-R14^  // *r0=context:R14
265         stmib   r0!, { r14           }  // Put R14_irq  // *r0=context:R15+4
266 #ifdef  SDK_CONTEXT_HAS_SP_SVC
267         mov     r3, #HW_PSR_SVC_MODE|HW_PSR_FIQ_DISABLE|HW_PSR_IRQ_DISABLE|HW_PSR_ARM_STATE
268         msr     cpsr_c, r3
269         stmib   r0!, { sp }
270 #endif
271 
272         // ---- OS_LoadContext
273 #if defined(SDK_ARM9) && !defined(SDK_CP_NO_SAFE)
274         // First, load CP context
275         stmfd   sp!, { r1 }
276         add     r0, r1, #OS_THREAD_OFFSET_CONTEXT
277         add     r0, r0, #OS_CONTEXT_CP_CONTEXT
278         ldr     r1, =CP_RestoreContext
279         blx     r1
280 
281 #if 0 // Don't need because we still spend more than 34 cycles for divider.
282     //---- CP_WaitDiv
283         ldr     r0, =REG_DIVCNT_ADDR
284 @00:
285         ldr     r1, [ r0 ]
286         and     r1, r1, #REG_CP_DIVCNT_BUSY_MASK
287         bne     @00
288 #endif // If 0
289         ldmfd   sp!, { r1 }
290 #endif // If defined(SDK_ARM9) && !defined(SDK_CP_NO_SAFE)
291 
292 
293 #ifdef  SDK_CONTEXT_HAS_SP_SVC
294         ldr     sp, [ r1, #OS_THREAD_OFFSET_CONTEXT+OS_CONTEXT_SP_SVC ]
295         mov     r3, #HW_PSR_IRQ_MODE|HW_PSR_FIQ_DISABLE|HW_PSR_IRQ_DISABLE|HW_PSR_ARM_STATE
296         msr     cpsr_c, r3
297 #endif
298         ldr     r2, [ r1, #OS_THREAD_OFFSET_CONTEXT ]!  // *r1=context:CPSR
299         msr     SPSR, r2                                // Put SPSR
300 
301         ldr     r14, [ r1, #OS_CONTEXT_PC_PLUS4 - OS_CONTEXT_CPSR ]   // Get R15
302         ldmib   r1!, { r0-r14 }^        // Get R0-R14^  // *r1=over written
303         nop
304         stmda   sp!, { r0-r3,r12,r14 }  // Put R0-R3,R12,LR / *sp=stack:LR
305         ldmfd   sp!, { pc }             // Return to irq master handler
306 #endif
307 }
308 
309 
310 /*---------------------------------------------------------------------------*
311   Name:         OS_WaitIrq
312 
313   Description:  Wait specified interrupt
314                 the difference between OS_WaitIrq and OS_WaitInterrupt,
315                 in waiting interrupt
316                 OS_WaitIrq does switch thread,
317                 OS_WaitInterrupt doesn't switch thread.
318                 OS_WaitIrq wait by using OS_SleepThread() with threadQueue,
319                 OS_WaitInterrupt wait by using OS_Halt().
320                 if SDK_NO_THREAD defined, 2 functions become same.
321 
322   Arguments:    clear:       TRUE if want to clear interrupt flag before wait
323                             FALSE if not
324                 irqFlags:    Bit of interrupts to wait for
325 
326   Returns:      None.
327  *---------------------------------------------------------------------------*/
OS_WaitIrq(BOOL clear,OSIrqMask irqFlags)328 void OS_WaitIrq( BOOL clear, OSIrqMask irqFlags )
329 {
330 #ifdef SDK_NO_THREAD
331   OS_WaitInterrupt( clear, irqFlags );
332 
333 #else
334   OSIntrMode enabled = OS_DisableInterrupts();
335 
336   //---- Clear interrupt check flags (if needed)
337   if ( clear )
338   {
339     (void)OS_ClearIrqCheckFlag( irqFlags );
340   }
341 
342   (void)OS_RestoreInterrupts( enabled );
343 
344   //---- Sleep till required interrupts
345   while( ! ( OS_GetIrqCheckFlag() & irqFlags ) )
346   {
347     OS_SleepThread( &OSi_IrqThreadQueue );
348   }
349 #endif // ifdef SDK_NO_THREAD
350 }
351 
352 /*---------------------------------------------------------------------------*
353   Name:         OS_WaitAnyIntrrupt
354 
355   Description:  Waits for any interrupt.
356 
357   Arguments:    None.
358 
359   Returns:      None.
360  *---------------------------------------------------------------------------*/
OS_WaitAnyIrq(void)361 void OS_WaitAnyIrq( void )
362 {
363 #ifdef SDK_NO_THREAD
364   OS_Halt();
365 #else
366   OS_SleepThread( &OSi_IrqThreadQueue );
367 #endif
368 }
369