1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - libraries - OS
3   File:     os_irqHandler.c
4 
5   Copyright 2003-2009 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:: 2009-06-04#$
14   $Rev: 10698 $
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         add     r0, r0, #0x34           // 0x34 is size of r2-r14(13 resisters)
268         stmib   r0!, { r14           }  // Put R14_irq  // *r0=context:R15+4
269 #ifdef  SDK_CONTEXT_HAS_SP_SVC
270         mov     r3, #HW_PSR_SVC_MODE|HW_PSR_FIQ_DISABLE|HW_PSR_IRQ_DISABLE|HW_PSR_ARM_STATE
271         msr     cpsr_c, r3
272         stmib   r0!, { sp }
273 #endif
274 
275         // ---- OS_LoadContext
276 #if defined(SDK_ARM9)
277         // First, load CP context
278         stmfd   sp!, { r1 }
279         add     r0, r1, #OS_THREAD_OFFSET_CONTEXT
280         add     r0, r0, #OS_CONTEXT_CP_CONTEXT
281         ldr     r1, =CPi_RestoreContext
282         blx     r1
283 
284         ldmfd   sp!, { r1 }
285 
286 #endif // If defined(SDK_ARM9)
287 
288 
289 #ifdef  SDK_CONTEXT_HAS_SP_SVC
290         ldr     sp, [ r1, #OS_THREAD_OFFSET_CONTEXT+OS_CONTEXT_SP_SVC ]
291         mov     r3, #HW_PSR_IRQ_MODE|HW_PSR_FIQ_DISABLE|HW_PSR_IRQ_DISABLE|HW_PSR_ARM_STATE
292         msr     cpsr_c, r3
293 #endif
294 
295         ldr     r2, [ r1, #OS_THREAD_OFFSET_CONTEXT ]!  // *r1=context:CPSR
296         msr     SPSR, r2                                // Put SPSR
297 
298         ldr     r14, [ r1, #OS_CONTEXT_PC_PLUS4 - OS_CONTEXT_CPSR ]   // Get R15
299         ldmib   r1, { r0-r14 }^         // Get R0-R14^  // *r1=over written
300         nop
301         stmda   sp!, { r0-r3,r12,r14 }  // Put R0-R3,R12,LR / *sp=stack:LR
302 
303         ldmfd   sp!, { pc }             // Return to irq master handler
304 }
305 
306 #ifdef      SDK_ARM9
307 #include    <nitro/itcm_end.h>
308 #endif
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     OSIntrMode enabled = OS_DisableInterrupts();
331 
332     //---- Clear interrupt check flags (if needed)
333     if (clear)
334     {
335         (void)OS_ClearIrqCheckFlag(irqFlags);
336     }
337 
338     (void)OS_RestoreInterrupts(enabled);
339 
340     //---- Sleep till required interrupts
341     while (!(OS_GetIrqCheckFlag() & irqFlags))
342     {
343         OS_SleepThread(&OSi_IrqThreadQueue);
344     }
345 }
346 
347 #if defined(SDK_TWL) && defined(SDK_ARM7)
OS_WaitIrqEx(BOOL clear,OSIrqMask irqFlags)348 void OS_WaitIrqEx(BOOL clear, OSIrqMask irqFlags)
349 {
350     OSIntrMode enabled = OS_DisableInterrupts();
351 
352     //---- Clear interrupt check flags (if needed)
353     if (clear)
354     {
355         (void)OS_ClearIrqCheckFlagEx(irqFlags);
356     }
357 
358     (void)OS_RestoreInterrupts(enabled);
359 
360     //---- Sleep till required interrupts
361     while (!(OS_GetIrqCheckFlagEx() & irqFlags))
362     {
363         OS_SleepThread(&OSi_IrqThreadQueue);
364     }
365 }
366 #endif // defined(SDK_TWL) && defined(SDK_ARM7)
367 
368 /*---------------------------------------------------------------------------*
369   Name:         OS_WaitAnyIrq
370 
371   Description:  Waits for any interrupt.
372 
373   Arguments:    None.
374 
375   Returns:      None.
376  *---------------------------------------------------------------------------*/
OS_WaitAnyIrq(void)377 void OS_WaitAnyIrq(void)
378 {
379     OS_SleepThread(&OSi_IrqThreadQueue);
380 }
381 #if defined(SDK_TWL) && defined(SDK_ARM7)
OS_WaitAnyIrqEx(void)382 inline void OS_WaitAnyIrqEx(void)
383 {
384 	OS_WaitAnyIrq();
385 }
386 #endif // defined(SDK_TWL) && defined(SDK_ARM7)
387