1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - libraries - OS
3   File:     os_irqHandler.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-17#$
14   $Rev: 8556 $
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/system.h>
22 
23 #ifdef SDK_NITRO
24 # ifdef SDK_ARM9
25 # include        <nitro/hw/ARM9/mmap_global.h>
26 # include        <nitro/hw/ARM9/ioreg_OS.h>
27 # else  // SDK_ARM9
28 # include        <nitro/hw/ARM7/mmap_global.h>
29 # include        <nitro/hw/ARM7/ioreg_OS.h>
30 # endif // SDK_ARM9
31 #else
32 # ifdef SDK_ARM9
33 # include        <twl/hw/ARM9/mmap_global.h>
34 # include        <twl/hw/ARM9/ioreg_OS.h>
35 # else  // SDK_ARM9
36 # include        <twl/hw/ARM7/mmap_global.h>
37 # include        <twl/hw/ARM7/ioreg_OS.h>
38 # endif // SDK_ARM9
39 #endif
40 
41 
42 #ifdef      SDK_ARM9
43 #include    <nitro/dtcm_begin.h>
44 #endif
45 
46 //---- Thread queue for interrupt
47 OSThreadQueue OSi_IrqThreadQueue = { NULL, NULL };
48 
49 #ifdef      SDK_ARM9
50 #include    <nitro/dtcm_end.h>
51 #include    <nitro/itcm_begin.h>
52 #endif
53 
54 /*---------------------------------------------------------------------------*
55   Name:         OS_IrqHandler
56 
57   Description:  IRQ handler. Call handler according to OS_InterruptTable.
58 
59   Arguments:    None.
60 
61   Returns:      None.
62  *---------------------------------------------------------------------------*/
OS_IrqHandler(void)63 asm void OS_IrqHandler( void )
64 {
65         stmfd   sp!, { lr }                     // Save LR
66 
67         // Get IE address
68         mov     r12,      #HW_REG_BASE
69         add     r12, r12, #REG_IE_OFFSET        // r12: REG_IE address
70 
71         // Get IME
72         ldr     r1, [ r12, #REG_IME_ADDR - REG_IE_ADDR ]  // r1: IME
73 
74         // If IME==0 then return (without changing IF)
75         cmp     r1, #0
76         ldmeqfd sp!, { pc }
77 
78         // Get IE&IF
79         ldmia   r12, { r1-r2 }                  // r1: IE, r2: IF
80         ands    r1, r1, r2                      // r1: IE & IF
81 
82         // If IE&IF==0 then return (without changing IF)
83         ldmeqfd sp!, { pc }
84 
85 
86 #if     defined(SDK_ARM9) && !defined(SDK_CWBUG_PROC_OPT)
87         //--------------------------------------------------
88         // IRQ HANDLING CODE for ARCHITECTURE VERSION 5
89         //--------------------------------------------------
90 
91         // Get lowest 1 bit
92         mov     r3, #1<<31
93 @1:     clz     r0, r1                  // Count zero of high bit
94         bics    r1, r1, r3, LSR r0
95         bne     @1
96 
97         // Clear IF
98         mov     r1, r3, LSR r0
99         str     r1, [ r12, #REG_IF_ADDR - REG_IE_ADDR ]
100 
101         rsbs    r0, r0, #31
102 
103 #else //defined(SDK_ARM9) && !defined(SDK_CWBUG_PROC_OPT)
104         //--------------------------------------------------
105         // IRQ HANDLING CODE for ARCHITECTURE VERSION 4
106         //--------------------------------------------------
107         mov     r3, #1
108         mov     r0, #0
109 @1:     ands    r2, r1, r3, LSL r0              // Count zero of high bit
110         addeq   r0, r0, #1
111         beq     @1
112 
113         // Clear IF
114         str     r2, [ r12, #REG_IF_ADDR - REG_IE_ADDR ]
115 #endif //defined(SDK_ARM9) && !defined(SDK_CWBUG_PROC_OPT)
116 
117         // Get jump vector
118 #ifdef  SDK_DEBUG
119         cmp     r0, #OS_IRQ_TABLE_MAX
120 @2:     bge     @2                              // Error Trap
121 #endif//SDK_DEBUG
122         ldr     r1, =OS_IRQTable
123         ldr     r0, [ r1, r0, LSL #2 ]
124 
125         ldr     lr, =OS_IrqHandler_ThreadSwitch
126         bx      r0      // Set return address for thread rescheduling
127 }
128 
129 
130 
131 /*---------------------------------------------------------------------------*
132   Name:         OS_IRQHandler_ThreadSwitch
133 
134   Description:  Interrupts branch processing (uses the OS_InterruptTable).
135 
136   Arguments:    None.
137 
138   Returns:      None.
139  *---------------------------------------------------------------------------*/
OS_IrqHandler_ThreadSwitch(void)140 asm void OS_IrqHandler_ThreadSwitch(void)
141 {
142         //--------------------------------------------------
143         // Wakeup threads in OSi_IrqThreadQueue
144         //--------------------------------------------------
145            ldr             r12, =OSi_IrqThreadQueue
146            mov             r3,  #0                      // Avoid stall
147            ldr             r12, [r12, #OSThreadQueue.head] // r12 = OSi_IrqThreadQueue.head
148            mov             r2,  #OS_THREAD_STATE_READY  // Avoid stall
149            cmp             r12, #0
150 
151            beq             @thread_switch                                  // if r12 == 0 exit
152 
153 @1:        str             r2,  [r12, #OSThread.state]
154            str             r3,  [r12, #OSThread.queue]
155            str             r3,  [r12, #OSThread.link.prev]
156            ldr             r0,  [r12, #OSThread.link.next]
157            str             r3,  [r12, #OSThread.link.next]
158            mov             r12,  r0
159 
160            cmp             r12, #0
161            bne             @1
162 
163            ldr             r12, =OSi_IrqThreadQueue
164            str             r3, [r12, #OSThreadQueue.head]  // Clear OSi_IrqThreadQueue.head
165            str             r3, [r12, #OSThreadQueue.tail]  // Clear OSi_IrqThreadQueue.tail
166 
167            ldr             r12, =OSi_ThreadInfo                    // Need to do scheduling
168            mov             r1, #1
169            strh            r1, [ r12, #OS_THREADINFO_OFFSET_ISNEEDRESCHEDULING ]
170 
171 
172 @thread_switch:
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         ldr     r12, =OSi_ThreadInfo
196         ldrh    r1, [ r12, #OS_THREADINFO_OFFSET_ISNEEDRESCHEDULING ]
197         cmp     r1, #0
198         ldreq   pc, [ sp ], #4          // Return if OSi_IsNeedResceduling == 0
199 
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 
223         ldr     pc, [ sp ], #4          // Return to irq master handler
224         // This part of the code is not reached
225 
226 
227 @12:
228         // ---- OS_GetCurrentThread
229         ldr     r0, [ r12, #OS_THREADINFO_OFFSET_CURRENT ]
230         cmp     r1, r0
231         beq     _dont_switched_         // Return if no thread switching
232 
233         // Call thread switch callback (need to save register r0, r1, r12)
234         ldr     r3, [ r12, #OS_THREADINFO_OFFSET_SWITCHCALLBACK ]
235         cmp     r3, #0
236         beq     @13                     // Skip calling callback when callback == 0
237         stmfd   sp!, { r0, r1, r12 }
238         mov     lr, pc
239         bx      r3
240         ldmfd   sp!, { r0, r1, r12 }
241 
242 @13:
243         // ---- OS_SetCurrentThread
244         str     r1, [ r12, #OS_THREADINFO_OFFSET_CURRENT ]
245 
246         // ---- OS_SaveContext
247         // r0=currentThread  r1=nextThread
248         // stack=Lo[LR,R0,R1,R2,R3,R12,LR]Hi
249         mrs     r2, SPSR
250         str     r2, [ r0, #OS_THREAD_OFFSET_CONTEXT ]!  // *r0=context:CPSR
251 
252 #if defined(SDK_ARM9)
253         // First, save CP context
254         stmfd   sp!, { r0, r1 }
255         add     r0, r0, #OS_THREAD_OFFSET_CONTEXT
256         add     r0, r0, #OS_CONTEXT_CP_CONTEXT
257         ldr     r1, =CP_SaveContext
258         blx     r1
259         ldmfd   sp!, { r0, r1 }
260 #endif
261 
262         ldmib   sp!, { r2,r3 }          // Get R0,R1    // *sp=stack:R1
263         stmib   r0!, { r2,r3 }          // Put R0,R1    // *r0=context:R1
264 
265         ldmib   sp!, { r2,r3,r12,r14 }  // Get R2,R3,R12,LR / *sp=stack:LR
266         stmib   r0!, { r2-r14        }^ // Put R2-R14^  // *r0=context:R14
267         stmib   r0!, { r14           }  // Put R14_irq  // *r0=context:R15+4
268 #ifdef  SDK_CONTEXT_HAS_SP_SVC
269         mov     r3, #HW_PSR_SVC_MODE|HW_PSR_FIQ_DISABLE|HW_PSR_IRQ_DISABLE|HW_PSR_ARM_STATE
270         msr     cpsr_c, r3
271         stmib   r0!, { sp }
272 #endif
273 
274         // ---- OS_LoadContext
275 #if defined(SDK_ARM9)
276         // First, load CP context
277         stmfd   sp!, { r1 }
278         add     r0, r1, #OS_THREAD_OFFSET_CONTEXT
279         add     r0, r0, #OS_CONTEXT_CP_CONTEXT
280         ldr     r1, =CPi_RestoreContext
281         blx     r1
282 
283         ldmfd   sp!, { r1 }
284 
285 #endif // If defined(SDK_ARM9)
286 
287 
288 #ifdef  SDK_CONTEXT_HAS_SP_SVC
289         ldr     sp, [ r1, #OS_THREAD_OFFSET_CONTEXT+OS_CONTEXT_SP_SVC ]
290         mov     r3, #HW_PSR_IRQ_MODE|HW_PSR_FIQ_DISABLE|HW_PSR_IRQ_DISABLE|HW_PSR_ARM_STATE
291         msr     cpsr_c, r3
292 #endif
293 
294         ldr     r2, [ r1, #OS_THREAD_OFFSET_CONTEXT ]!  // *r1=context:CPSR
295         msr     SPSR, r2                                // Put SPSR
296 
297         ldr     r14, [ r1, #OS_CONTEXT_PC_PLUS4 - OS_CONTEXT_CPSR ]   // Get R15
298         ldmib   r1, { r0-r14 }^         // Get R0-R14^  // *r1=over written
299         nop
300         stmda   sp!, { r0-r3,r12,r14 }  // Put R0-R3,R12,LR / *sp=stack:LR
301 
302         ldmfd   sp!, { pc }             // Return to irq master handler
303 }
304 
305 #ifdef      SDK_ARM9
306 #include    <nitro/itcm_end.h>
307 #endif
308 
309 /*---------------------------------------------------------------------------*
310   Name:         OS_WaitIrq
311 
312   Description:  Wait specified interrupt
313                 the difference between OS_WaitIrq and OS_WaitInterrupt,
314                 in waiting interrupt
315                 OS_WaitIrq does switch thread,
316                 OS_WaitInterrupt doesn't switch thread.
317                 OS_WaitIrq wait by using OS_SleepThread() with threadQueue,
318                 OS_WaitInterrupt wait by using OS_Halt().
319                 if SDK_NO_THREAD defined, 2 functions become same.
320 
321   Arguments:    clear:       TRUE if want to clear interrupt flag before wait
322                             FALSE if not
323                 irqFlags:    Bit of interrupts to wait for
324 
325   Returns:      None.
326  *---------------------------------------------------------------------------*/
OS_WaitIrq(BOOL clear,OSIrqMask irqFlags)327 void OS_WaitIrq(BOOL clear, OSIrqMask irqFlags)
328 {
329     OSIntrMode enabled = OS_DisableInterrupts();
330 
331     //---- Clear interrupt check flags (if needed)
332     if (clear)
333     {
334         (void)OS_ClearIrqCheckFlag(irqFlags);
335     }
336 
337     (void)OS_RestoreInterrupts(enabled);
338 
339     //---- Sleep till required interrupts
340     while (!(OS_GetIrqCheckFlag() & irqFlags))
341     {
342         OS_SleepThread(&OSi_IrqThreadQueue);
343     }
344 }
345 
346 #if defined(SDK_TWL) && defined(SDK_ARM7)
OS_WaitIrqEx(BOOL clear,OSIrqMask irqFlags)347 void OS_WaitIrqEx(BOOL clear, OSIrqMask irqFlags)
348 {
349     OSIntrMode enabled = OS_DisableInterrupts();
350 
351     //---- Clear interrupt check flags (if needed)
352     if (clear)
353     {
354         (void)OS_ClearIrqCheckFlagEx(irqFlags);
355     }
356 
357     (void)OS_RestoreInterrupts(enabled);
358 
359     //---- Sleep till required interrupts
360     while (!(OS_GetIrqCheckFlagEx() & irqFlags))
361     {
362         OS_SleepThread(&OSi_IrqThreadQueue);
363     }
364 }
365 #endif // defined(SDK_TWL) && defined(SDK_ARM7)
366 
367 /*---------------------------------------------------------------------------*
368   Name:         OS_WaitAnyIrq
369 
370   Description:  Waits for any interrupt.
371 
372   Arguments:    None.
373 
374   Returns:      None.
375  *---------------------------------------------------------------------------*/
OS_WaitAnyIrq(void)376 void OS_WaitAnyIrq(void)
377 {
378     OS_SleepThread(&OSi_IrqThreadQueue);
379 }
380 #if defined(SDK_TWL) && defined(SDK_ARM7)
OS_WaitAnyIrqEx(void)381 inline void OS_WaitAnyIrqEx(void)
382 {
383 	OS_WaitAnyIrq();
384 }
385 #endif // defined(SDK_TWL) && defined(SDK_ARM7)
386