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