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