cortex-m3: add basic support for the lm3s1968 board.
[bertos.git] / bertos / cpu / irq.h
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2004, 2005, 2006, 2007 Develer S.r.l. (http://www.develer.com/)
30  * Copyright 2004 Giovanni Bajo
31  *
32  * -->
33  *
34  * \brief CPU-specific IRQ definitions.
35  *
36  * \author Giovanni Bajo <rasky@develer.com>
37  * \author Bernie Innocenti <bernie@codewiz.org>
38  * \author Stefano Fedrigo <aleph@develer.com>
39  * \author Francesco Sacchi <batt@develer.com>
40  */
41 #ifndef CPU_IRQ_H
42 #define CPU_IRQ_H
43
44 #include "detect.h"
45 #include "types.h"
46
47 #include <kern/proc.h> /* proc_needPreempt() / proc_preempt() */
48
49 #include <cfg/compiler.h> /* for uintXX_t */
50 #include "cfg/cfg_proc.h" /* CONFIG_KERN_PREEMPT */
51
52 #if CPU_I196
53         #define IRQ_DISABLE             disable_interrupt()
54         #define IRQ_ENABLE              enable_interrupt()
55 #elif CPU_X86
56
57         /* Get IRQ_* definitions from the hosting environment. */
58         #include <cfg/os.h>
59         #if OS_EMBEDDED
60                 #define IRQ_DISABLE             FIXME
61                 #define IRQ_ENABLE              FIXME
62                 #define IRQ_SAVE_DISABLE(x)     FIXME
63                 #define IRQ_RESTORE(x)          FIXME
64         #endif /* OS_EMBEDDED */
65
66 #elif CPU_ARM_LM3S1968
67
68         #define IRQ_DISABLE asm volatile ("cpsid i" : : : "memory", "cc")
69         #define IRQ_ENABLE asm volatile ("cpsie i" : : : "memory", "cc")
70
71         #define IRQ_SAVE_DISABLE(x)                                     \
72         ({                                                              \
73                 asm volatile (                                          \
74                         "mrs %0, PRIMASK\n"                             \
75                         "cpsid i"                                       \
76                         : "=r" (x) : : "memory", "cc");                 \
77         })
78
79         #define IRQ_RESTORE(x)                                          \
80         ({                                                              \
81                 if (x)                                                  \
82                         IRQ_DISABLE;                                    \
83                 else                                                    \
84                         IRQ_ENABLE;                                     \
85         })
86
87         #define CPU_READ_FLAGS()                                        \
88         ({                                                              \
89                 cpu_flags_t sreg;                                       \
90                 asm volatile (                                          \
91                         "mrs %0, PRIMASK\n\t"                           \
92                         : "=r" (sreg) : : "memory", "cc");              \
93                 sreg;                                                   \
94         })
95
96         #define IRQ_ENABLED() (!CPU_READ_FLAGS())
97
98 #elif CPU_ARM
99
100         #ifdef __IAR_SYSTEMS_ICC__
101
102                 #include <inarm.h>
103
104                 #if __CPU_MODE__ == 1 /* Thumb */
105                         /* Use stubs */
106                         extern cpu_flags_t get_CPSR(void);
107                         extern void set_CPSR(cpu_flags_t flags);
108                 #else
109                         #define get_CPSR __get_CPSR
110                         #define set_CPSR __set_CPSR
111                 #endif
112
113                 #define IRQ_DISABLE __disable_interrupt()
114                 #define IRQ_ENABLE  __enable_interrupt()
115
116                 #define IRQ_SAVE_DISABLE(x) \
117                 do { \
118                         (x) = get_CPSR(); \
119                         __disable_interrupt(); \
120                 } while (0)
121
122                 #define IRQ_RESTORE(x) \
123                 do { \
124                         set_CPSR(x); \
125                 } while (0)
126
127                 #define IRQ_ENABLED() \
128                         ((bool)(get_CPSR() & 0xb0))
129
130         #else /* !__IAR_SYSTEMS_ICC__ */
131
132                 #define IRQ_DISABLE \
133                 do { \
134                         asm volatile ( \
135                                 "mrs r0, cpsr\n\t" \
136                                 "orr r0, r0, #0xc0\n\t" \
137                                 "msr cpsr_c, r0" \
138                                 ::: "r0" \
139                         ); \
140                 } while (0)
141
142                 #define IRQ_ENABLE \
143                 do { \
144                         asm volatile ( \
145                                 "mrs r0, cpsr\n\t" \
146                                 "bic r0, r0, #0xc0\n\t" \
147                                 "msr cpsr_c, r0" \
148                                 ::: "r0" \
149                         ); \
150                 } while (0)
151
152                 #define IRQ_SAVE_DISABLE(x) \
153                 do { \
154                         asm volatile ( \
155                                 "mrs %0, cpsr\n\t" \
156                                 "orr r0, %0, #0xc0\n\t" \
157                                 "msr cpsr_c, r0" \
158                                 : "=r" (x) \
159                                 : /* no inputs */ \
160                                 : "r0" \
161                         ); \
162                 } while (0)
163
164                 #define IRQ_RESTORE(x) \
165                 do { \
166                         asm volatile ( \
167                                 "msr cpsr_c, %0" \
168                                 : /* no outputs */ \
169                                 : "r" (x) \
170                         ); \
171                 } while (0)
172
173                 #define CPU_READ_FLAGS() \
174                 ({ \
175                         cpu_flags_t sreg; \
176                         asm volatile ( \
177                                 "mrs %0, cpsr\n\t" \
178                                 : "=r" (sreg) \
179                                 : /* no inputs */ \
180                         ); \
181                         sreg; \
182                 })
183
184                 #define IRQ_ENABLED() ((CPU_READ_FLAGS() & 0xc0) != 0xc0)
185
186                 #if CONFIG_KERN_PREEMPT
187                         EXTERN_C void asm_irq_switch_context(void);
188
189                         /**
190                          * At the beginning of any ISR immediately ajust the
191                          * return address and store all the caller-save
192                          * registers (the ISR may change these registers that
193                          * are shared with the user-context).
194                          */
195                         #define IRQ_ENTRY() asm volatile ( \
196                                                 "sub    lr, lr, #4\n\t" \
197                                                 "stmfd  sp!, {r0-r3, ip, lr}\n\t")
198                         #define IRQ_EXIT()  asm volatile ( \
199                                                 "b      asm_irq_switch_context\n\t")
200                         /**
201                          * Function attribute to declare an interrupt service
202                          * routine.
203                          *
204                          * An ISR function must be declared as naked because we
205                          * want to add our IRQ_ENTRY() prologue and IRQ_EXIT()
206                          * epilogue code to handle the context switch and save
207                          * all the registers (not only the callee-save).
208                          *
209                          */
210                         #define ISR_FUNC __attribute__((naked))
211
212                         /**
213                          * The compiler cannot establish which
214                          * registers actually need to be saved, because
215                          * the interrupt can happen at any time, so the
216                          * "normal" prologue and epilogue used for a
217                          * generic function call are not suitable for
218                          * the ISR.
219                          *
220                          * Using a naked function has the drawback that
221                          * the stack is not automatically adjusted at
222                          * this point, like a "normal" function call.
223                          *
224                          * So, an ISR can _only_ contain other function
225                          * calls and they can't use the stack in any
226                          * other way.
227                          *
228                          * NOTE: we need to explicitly disable IRQs after
229                          * IRQ_ENTRY(), because the IRQ status flag is not
230                          * masked by the hardware and an IRQ ack inside the ISR
231                          * may cause the triggering of another IRQ before
232                          * exiting from the current ISR.
233                          *
234                          * The respective IRQ_ENABLE is not necessary, because
235                          * IRQs will be automatically re-enabled when restoring
236                          * the context of the user task.
237                          */
238                         #define DECLARE_ISR_CONTEXT_SWITCH(func)        \
239                                 void ISR_FUNC func(void);               \
240                                 static void __isr_##func(void);         \
241                                 void ISR_FUNC func(void)                \
242                                 {                                       \
243                                         IRQ_ENTRY();                    \
244                                         IRQ_DISABLE;                    \
245                                         __isr_##func();                 \
246                                         IRQ_EXIT();                     \
247                                 }                                       \
248                                 static void __isr_##func(void)
249                         /**
250                          * Interrupt service routine prototype: can be used for
251                          * forward declarations.
252                          */
253                         #define ISR_PROTO_CONTEXT_SWITCH(func)  \
254                                 void ISR_FUNC func(void)
255                         /**
256                          * With task priorities enabled each ISR is used a point to
257                          * check if we need to perform a context switch.
258                          *
259                          * Instead, without priorities a context switch can occur only
260                          * when the running task expires its time quantum. In this last
261                          * case, the context switch can only occur in the timer
262                          * ISR, that must be always declared with the
263                          * DECLARE_ISR_CONTEXT_SWITCH() macro.
264                          */
265                         #if CONFIG_KERN_PRI
266                                 #define DECLARE_ISR(func) \
267                                         DECLARE_ISR_CONTEXT_SWITCH(func)
268
269                                 #define ISR_PROTO(func) \
270                                         ISR_PROTO_CONTEXT_SWITCH(func)
271                         #endif /* !CONFIG_KERN_PRI */
272                 #endif /* CONFIG_KERN_PREEMPT */
273
274                 #ifndef DECLARE_ISR
275                         #define DECLARE_ISR(func) \
276                                 void __attribute__((interrupt)) func(void)
277                 #endif
278                 #ifndef DECLARE_ISR_CONTEXT_SWITCH
279                         #define DECLARE_ISR_CONTEXT_SWITCH(func) \
280                                 void __attribute__((interrupt)) func(void)
281                 #endif
282                 #ifndef ISR_PROTO
283                         #define ISR_PROTO(func) \
284                                 void __attribute__((interrupt)) func(void)
285                 #endif
286                 #ifndef ISR_PROTO_CONTEXT_SWITCH
287                         #define ISR_PROTO_CONTEXT_SWITCH(func)  \
288                                 void __attribute__((interrupt)) func(void)
289                 #endif
290
291         #endif /* !__IAR_SYSTEMS_ICC_ */
292
293 #elif CPU_PPC
294
295         /* Get IRQ_* definitions from the hosting environment. */
296         #include <cfg/os.h>
297         #if OS_EMBEDDED
298                 #define IRQ_DISABLE         FIXME
299                 #define IRQ_ENABLE          FIXME
300                 #define IRQ_SAVE_DISABLE(x) FIXME
301                 #define IRQ_RESTORE(x)      FIXME
302                 #define IRQ_ENABLED()       FIXME
303         #endif /* OS_EMBEDDED */
304
305 #elif CPU_DSP56K
306
307         #define IRQ_DISABLE             do { asm(bfset #0x0200,SR); asm(nop); } while (0)
308         #define IRQ_ENABLE              do { asm(bfclr #0x0200,SR); asm(nop); } while (0)
309
310         #define IRQ_SAVE_DISABLE(x)  \
311                 do { (void)x; asm(move SR,x); asm(bfset #0x0200,SR); } while (0)
312         #define IRQ_RESTORE(x)  \
313                 do { (void)x; asm(move x,SR); } while (0)
314
315         static inline bool irq_running(void)
316         {
317                 extern void *user_sp;
318                 return !!user_sp;
319         }
320         #define IRQ_RUNNING() irq_running()
321
322         static inline bool irq_enabled(void)
323         {
324                 uint16_t x;
325                 asm(move SR,x);
326                 return !(x & 0x0200);
327         }
328         #define IRQ_ENABLED() irq_enabled()
329
330 #elif CPU_AVR
331
332         #define IRQ_DISABLE   asm volatile ("cli" ::)
333         #define IRQ_ENABLE    asm volatile ("sei" ::)
334
335         #define IRQ_SAVE_DISABLE(x) \
336         do { \
337                 __asm__ __volatile__( \
338                         "in %0,__SREG__\n\t" \
339                         "cli" \
340                         : "=r" (x) : /* no inputs */ : "cc" \
341                 ); \
342         } while (0)
343
344         #define IRQ_RESTORE(x) \
345         do { \
346                 __asm__ __volatile__( \
347                         "out __SREG__,%0" : /* no outputs */ : "r" (x) : "cc" \
348                 ); \
349         } while (0)
350
351         #define IRQ_ENABLED() \
352         ({ \
353                 uint8_t sreg; \
354                 __asm__ __volatile__( \
355                         "in %0,__SREG__\n\t" \
356                         : "=r" (sreg)  /* no inputs & no clobbers */ \
357                 ); \
358                 (bool)(sreg & 0x80); \
359         })
360         #if CONFIG_KERN_PREEMPT
361                 #define DECLARE_ISR_CONTEXT_SWITCH(vect)                \
362                         INLINE void __isr_##vect(void);                 \
363                         ISR(vect)                                       \
364                         {                                               \
365                                 __isr_##vect();                         \
366                                 IRQ_PREEMPT_HANDLER();                  \
367                         }                                               \
368                         INLINE void __isr_##vect(void)
369
370                 /**
371                  * With task priorities enabled each ISR is used a point to
372                  * check if we need to perform a context switch.
373                  *
374                  * Instead, without priorities a context switch can occur only
375                  * when the running task expires its time quantum. In this last
376                  * case, the context switch can only occur in the timer ISR,
377                  * that must be always declared with the
378                  * DECLARE_ISR_CONTEXT_SWITCH() macro.
379                  */
380                 #if CONFIG_KERN_PRI
381                         #define DECLARE_ISR(func) \
382                                 DECLARE_ISR_CONTEXT_SWITCH(func)
383                         /**
384                          * Interrupt service routine prototype: can be used for
385                          * forward declarations.
386                          */
387                         #define ISR_PROTO(func) \
388                                 ISR_PROTO_CONTEXT_SWITCH(func)
389                 #endif /* !CONFIG_KERN_PRI */
390         #endif
391
392         #ifndef ISR_PROTO
393                 #define ISR_PROTO(vect) ISR(vect)
394         #endif
395         #ifndef DECLARE_ISR
396                 #define DECLARE_ISR(vect) ISR(vect)
397         #endif
398         #ifndef DECLARE_ISR_CONTEXT_SWITCH
399                 #define DECLARE_ISR_CONTEXT_SWITCH(vect) ISR(vect)
400         #endif
401         #ifndef ISR_PROTO_CONTEXT_SWITCH
402                 #define ISR_PROTO_CONTEXT_SWITCH(func) ISR(vect)
403         #endif
404
405 #else
406         #error No CPU_... defined.
407 #endif
408
409 #ifdef IRQ_RUNNING
410         /// Ensure callee is running within an interrupt
411         #define ASSERT_IRQ_CONTEXT()  ASSERT(IRQ_RUNNING())
412
413         /// Ensure callee is not running within an interrupt
414         #define ASSERT_USER_CONTEXT() ASSERT(!IRQ_RUNNING())
415 #else
416         #define ASSERT_USER_CONTEXT()  do {} while(0)
417         #define ASSERT_IRQ_CONTEXT()   do {} while(0)
418 #endif
419
420 #ifdef IRQ_ENABLED
421         /// Ensure interrupts are enabled
422         #define IRQ_ASSERT_ENABLED()  ASSERT(IRQ_ENABLED())
423
424         /// Ensure interrupts are not enabled
425         #define IRQ_ASSERT_DISABLED() ASSERT(!IRQ_ENABLED())
426 #else
427         #define IRQ_ASSERT_ENABLED() do {} while(0)
428         #define IRQ_ASSERT_DISABLED() do {} while(0)
429 #endif
430
431
432 #ifndef IRQ_PREEMPT_HANDLER
433         #if CONFIG_KERN_PREEMPT
434                 /**
435                  * Handle preemptive context switch inside timer IRQ.
436                  */
437                 INLINE void IRQ_PREEMPT_HANDLER(void)
438                 {
439                         if (proc_needPreempt())
440                                 proc_preempt();
441                 }
442         #else
443                 #define IRQ_PREEMPT_HANDLER() /* Nothing */
444         #endif
445 #endif
446
447 /**
448  * Execute \a CODE atomically with respect to interrupts.
449  *
450  * \see IRQ_SAVE_DISABLE IRQ_RESTORE
451  */
452 #define ATOMIC(CODE) \
453         do { \
454                 cpu_flags_t __flags; \
455                 IRQ_SAVE_DISABLE(__flags); \
456                 CODE; \
457                 IRQ_RESTORE(__flags); \
458         } while (0)
459
460 #endif /* CPU_IRQ_H */