1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2002 by Ulf Ralberg
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
30 unsigned int macsr
; /* EMAC status register */
31 unsigned int d
[6]; /* d2-d7 */
32 unsigned int a
[5]; /* a2-a6 */
33 void *sp
; /* Stack pointer (a7) */
34 void *start
; /* Thread start address, or NULL when started */
36 #elif CONFIG_CPU == SH7034
39 unsigned int r
[7]; /* Registers r8 thru r14 */
40 void *sp
; /* Stack pointer (r15) */
41 void *pr
; /* Procedure register */
42 void *start
; /* Thread start address, or NULL when started */
44 #elif defined(CPU_ARM)
47 unsigned int r
[9]; /* Registers r4-r12 */
48 void *sp
; /* Stack pointer (r13) */
49 unsigned int lr
; /* r14 (lr) */
50 void *start
; /* Thread start address, or NULL when started */
52 #elif CONFIG_CPU == TCC730
55 void *sp
; /* Stack pointer (a15) */
56 void *start
; /* Thread start address */
57 int started
; /* 0 when not started */
61 #define DEADBEEF ((unsigned int)0xdeadbeef)
62 /* Cast to the the machine int type, whose size could be < 4. */
66 static volatile int num_sleepers
;
67 static int current_thread
;
68 static struct regs thread_contexts
[MAXTHREADS
] IBSS_ATTR
;
69 const char *thread_name
[MAXTHREADS
];
70 void *thread_stack
[MAXTHREADS
];
71 int thread_stack_size
[MAXTHREADS
];
72 static const char main_thread_name
[] = "main";
74 extern int stackbegin
[];
75 extern int stackend
[];
77 void switch_thread(void) ICODE_ATTR
;
78 static inline void store_context(void* addr
) __attribute__ ((always_inline
));
79 static inline void load_context(const void* addr
) __attribute__ ((always_inline
));
82 /*---------------------------------------------------------------------------
83 * Store non-volatile context.
84 *---------------------------------------------------------------------------
86 static inline void store_context(void* addr
)
89 "stmia %0, { r4-r14 } \n"
94 /*---------------------------------------------------------------------------
95 * Load non-volatile context.
96 *---------------------------------------------------------------------------
98 static inline void load_context(const void* addr
)
101 "ldmia %0, { r4-r14 } \n" /* load regs r4 to r14 from context */
102 "ldr r0, [%0, #44] \n" /* load start pointer */
104 "cmp r0, r1 \n" /* check for NULL */
105 "strne r1, [%0, #44] \n" /* if it's NULL, we're already running */
106 "movne pc, r0 \n" /* not already running, so jump to start */
107 : : "r" (addr
) : "r0", "r1"
112 #elif defined(CPU_COLDFIRE)
113 /*---------------------------------------------------------------------------
114 * Store non-volatile context.
115 *---------------------------------------------------------------------------
117 static inline void store_context(void* addr
)
120 "move.l %%macsr,%%d0 \n"
121 "movem.l %%d0/%%d2-%%d7/%%a2-%%a7,(%0) \n"
122 : : "a" (addr
) : "d0" /* only! */
126 /*---------------------------------------------------------------------------
127 * Load non-volatile context.
128 *---------------------------------------------------------------------------
130 static inline void load_context(const void* addr
)
133 "movem.l (%0),%%d0/%%d2-%%d7/%%a2-%%a7 \n" /* Load context */
134 "move.l %%d0,%%macsr \n"
135 "move.l (52,%0),%%d0 \n" /* Get start address */
136 "beq.b .running \n" /* NULL -> already running */
137 "clr.l (52,%0) \n" /* Clear start address.. */
139 "jmp (%0) \n" /* ..and start the thread */
141 : : "a" (addr
) : "d0" /* only! */
145 #elif CONFIG_CPU == SH7034
146 /*---------------------------------------------------------------------------
147 * Store non-volatile context.
148 *---------------------------------------------------------------------------
150 static inline void store_context(void* addr
)
167 /*---------------------------------------------------------------------------
168 * Load non-volatile context.
169 *---------------------------------------------------------------------------
171 static inline void load_context(const void* addr
)
183 "mov.l @%0,r0 \n" /* Get start address */
185 "bt .running \n" /* NULL -> already running */
188 "rts \n" /* Start the thread */
189 "mov.l r0,@%0 \n" /* Clear start address */
191 : : "r" (addr
) : "r0" /* only! */
195 #elif CONFIG_CPU == TCC730
196 /*---------------------------------------------------------------------------
197 * Store non-volatile context.
198 *---------------------------------------------------------------------------
200 #define store_context(addr) \
210 "ldw @[%0+0], a15\n\t" : : "a" (addr) );
212 /*---------------------------------------------------------------------------
213 * Load non-volatile context.
214 *---------------------------------------------------------------------------
216 #define load_context(addr) \
218 if (!(addr)->started) { \
219 (addr)->started = 1; \
221 "ldw a15, @[%0+0]\n\t" \
222 "ldw a14, @[%0+4]\n\t" \
223 "jmp a14\n\t" : : "a" (addr) \
227 "ldw a15, @[%0+0]\n\t" \
235 "pop r1,r0\n\t" : : "a" (addr) \
242 /*---------------------------------------------------------------------------
243 * Switch thread in round robin fashion.
244 *---------------------------------------------------------------------------
246 void switch_thread(void)
249 unsigned int *stackptr
;
254 while (num_sleepers
== num_threads
)
256 /* Enter sleep mode, woken up on interrupt */
258 asm volatile ("stop #0x2000");
259 #elif CONFIG_CPU == SH7034
261 asm volatile ("sleep");
262 #elif CONFIG_CPU == TCC730
263 /* Sleep mode is triggered by the SYS instr on CalmRisc16.
264 * Unfortunately, the manual doesn't specify which arg to use.
265 __asm__ volatile ("sys #0x0f");
266 0x1f seems to trigger a reset;
267 0x0f is the only one other argument used by Archos.
272 current
= current_thread
;
273 store_context(&thread_contexts
[current
]);
275 #if CONFIG_CPU != TCC730
276 /* Check if the current thread stack is overflown */
277 stackptr
= thread_stack
[current
];
278 if(stackptr
[0] != DEADBEEF
)
279 panicf("Stkov %s", thread_name
[current
]);
282 if (++current
>= num_threads
)
285 current_thread
= current
;
286 load_context(&thread_contexts
[current
]);
289 void sleep_thread(void)
295 void wake_up_thread(void)
300 /*---------------------------------------------------------------------------
302 * Return ID if context area could be allocated, else -1.
303 *---------------------------------------------------------------------------
305 int create_thread(void (*function
)(void), void* stack
, int stack_size
,
309 unsigned int stacklen
;
310 unsigned int *stackptr
;
313 if (num_threads
>= MAXTHREADS
)
316 /* Munge the stack to make it easy to spot stack overflows */
317 stacklen
= stack_size
/ sizeof(int);
319 for(i
= 0;i
< stacklen
;i
++)
321 stackptr
[i
] = DEADBEEF
;
324 /* Store interesting information */
325 thread_name
[num_threads
] = name
;
326 thread_stack
[num_threads
] = stack
;
327 thread_stack_size
[num_threads
] = stack_size
;
328 regs
= &thread_contexts
[num_threads
];
329 #if defined(CPU_COLDFIRE) || (CONFIG_CPU == SH7034) || defined(CPU_ARM)
330 /* Align stack to an even 32 bit boundary */
331 regs
->sp
= (void*)(((unsigned int)stack
+ stack_size
) & ~3);
332 #elif CONFIG_CPU == TCC730
333 /* Align stack on word boundary */
334 regs
->sp
= (void*)(((unsigned long)stack
+ stack_size
- 2) & ~1);
337 regs
->start
= (void*)function
;
340 return num_threads
++; /* return the current ID, e.g for remove_thread() */
343 /*---------------------------------------------------------------------------
344 * Remove a thread from the scheduler.
345 * Parameter is the ID as returned from create_thread().
346 *---------------------------------------------------------------------------
348 void remove_thread(int threadnum
)
352 if(threadnum
>= num_threads
)
356 for (i
=threadnum
; i
<num_threads
-1; i
++)
357 { /* move all entries which are behind */
358 thread_name
[i
] = thread_name
[i
+1];
359 thread_stack
[i
] = thread_stack
[i
+1];
360 thread_stack_size
[i
] = thread_stack_size
[i
+1];
361 thread_contexts
[i
] = thread_contexts
[i
+1];
364 if (current_thread
== threadnum
) /* deleting the current one? */
365 current_thread
= num_threads
; /* set beyond last, avoid store harm */
366 else if (current_thread
> threadnum
) /* within the moved positions? */
367 current_thread
--; /* adjust it, point to same context again */
370 void init_threads(void)
372 num_threads
= 1; /* We have 1 thread to begin with */
373 current_thread
= 0; /* The current thread is number 0 */
374 thread_name
[0] = main_thread_name
;
375 thread_stack
[0] = stackbegin
;
376 thread_stack_size
[0] = (int)stackend
- (int)stackbegin
;
377 #if CONFIG_CPU == TCC730
378 thread_contexts
[0].started
= 1;
380 thread_contexts
[0].start
= 0; /* thread 0 already running */
385 int thread_stack_usage(int threadnum
)
388 unsigned int *stackptr
= thread_stack
[threadnum
];
390 if(threadnum
>= num_threads
)
393 for(i
= 0;i
< thread_stack_size
[threadnum
]/sizeof(int);i
++)
395 if(stackptr
[i
] != DEADBEEF
)
399 return ((thread_stack_size
[threadnum
] - i
* sizeof(int)) * 100) /
400 thread_stack_size
[threadnum
];