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:: 2010-05-19#$
14 $Rev: 11339 $
15 $Author: yada $
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 //---- Sleep till required interrupts
339 while (!(OS_GetIrqCheckFlag() & irqFlags))
340 {
341 OS_SleepThread(&OSi_IrqThreadQueue);
342 }
343
344 (void)OS_RestoreInterrupts(enabled);
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 //---- Sleep till required interrupts
359 while (!(OS_GetIrqCheckFlagEx() & irqFlags))
360 {
361 OS_SleepThread(&OSi_IrqThreadQueue);
362 }
363
364 (void)OS_RestoreInterrupts(enabled);
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