Change macro name to IRQ_ENABLED.
[bertos.git] / drv / timer.c
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 2003, 2004, 2005, 2006 Develer S.r.l. (http://www.develer.com/)
30  * Copyright 2000 Bernardo Innocenti <bernie@develer.com>
31  *
32  * -->
33  *
34  * \brief Hardware independent timer driver (implementation)
35  *
36  * \version $Id$
37  * \author Bernardo Innocenti <bernie@develer.com>
38  */
39
40 #include "timer.h"
41 #include <cpu/cpu.h>
42 #include <cfg/os.h>
43 #include <cfg/debug.h>
44 #include <cfg/module.h>
45 #include <appconfig.h>
46
47 /*
48  * Include platform-specific binding code if we're hosted.
49  * Try the CPU specific one for bare-metal environments.
50  */
51 #if OS_HOSTED
52         #include OS_CSOURCE(timer)
53 #else
54         #include CPU_CSOURCE(timer)
55 #endif
56
57 /*
58  * Sanity check for config parameters required by this module.
59  */
60 #if !defined(CONFIG_KERNEL) || ((CONFIG_KERNEL != 0) && CONFIG_KERNEL != 1)
61         #error CONFIG_KERNEL must be set to either 0 or 1 in config.h
62 #endif
63 #if !defined(CONFIG_WATCHDOG) || ((CONFIG_WATCHDOG != 0) && CONFIG_WATCHDOG != 1)
64         #error CONFIG_WATCHDOG must be set to either 0 or 1 in config.h
65 #endif
66
67 #if CONFIG_WATCHDOG
68         #include <drv/wdt.h>
69 #endif
70
71 #if CONFIG_KERNEL
72         #include <config_kern.h>
73         #if CONFIG_KERN_SIGNALS
74                 #include <kern/signal.h> /* sig_wait(), sig_check() */
75                 #include <kern/proc.h>   /* proc_current() */
76                 #include <cfg/macros.h>  /* BV() */
77         #endif
78 #endif
79
80
81 /**
82  * \def CONFIG_TIMER_STROBE
83  *
84  * This is a debug facility that can be used to
85  * monitor timer interrupt activity on an external pin.
86  *
87  * To use strobes, redefine the macros TIMER_STROBE_ON,
88  * TIMER_STROBE_OFF and TIMER_STROBE_INIT and set
89  * CONFIG_TIMER_STROBE to 1.
90  */
91 #if !defined(CONFIG_TIMER_STROBE) || !CONFIG_TIMER_STROBE
92         #define TIMER_STROBE_ON    do {/*nop*/} while(0)
93         #define TIMER_STROBE_OFF   do {/*nop*/} while(0)
94         #define TIMER_STROBE_INIT  do {/*nop*/} while(0)
95 #endif
96
97
98 /// Master system clock (1 tick accuracy)
99 volatile ticks_t _clock;
100
101
102 #ifndef CONFIG_TIMER_DISABLE_EVENTS
103
104 /**
105  * List of active asynchronous timers.
106  */
107 REGISTER static List timers_queue;
108
109
110 /**
111  * Add the specified timer to the software timer service queue.
112  * When the delay indicated by the timer expires, the timer
113  * device will execute the event associated with it.
114  *
115  * \note Interrupt safe
116  */
117 void timer_add(Timer *timer)
118 {
119         Timer *node;
120         cpuflags_t flags;
121
122
123         /* Inserting timers twice causes mayhem. */
124         ASSERT(timer->magic != TIMER_MAGIC_ACTIVE);
125         DB(timer->magic = TIMER_MAGIC_ACTIVE;)
126
127         IRQ_SAVE_DISABLE(flags);
128
129         /* Calculate expiration time for this timer */
130         timer->tick = _clock + timer->_delay;
131
132         /*
133          * Search for the first node whose expiration time is
134          * greater than the timer we want to add.
135          */
136         node = (Timer *)LIST_HEAD(&timers_queue);
137         while (node->link.succ)
138         {
139                 /*
140                  * Stop just after the insertion point.
141                  * (this fancy compare takes care of wrap-arounds).
142                  */
143                 if (node->tick - timer->tick > 0)
144                         break;
145
146                 /* Go to next node */
147                 node = (Timer *)node->link.succ;
148         }
149
150         /* Enqueue timer request into the list */
151         INSERT_BEFORE(&timer->link, &node->link);
152
153         IRQ_RESTORE(flags);
154 }
155
156
157 /**
158  * Remove a timer from the timer queue before it has expired.
159  */
160 Timer *timer_abort(Timer *timer)
161 {
162         ATOMIC(REMOVE(&timer->link));
163         DB(timer->magic = TIMER_MAGIC_INACTIVE;)
164
165         return timer;
166 }
167
168 #endif /* CONFIG_TIMER_DISABLE_EVENTS */
169
170
171 /**
172  * Wait for the specified amount of timer ticks.
173  */
174 void timer_delayTicks(ticks_t delay)
175 {
176 #if defined(IRQ_ENABLED)
177         /* We shouldn't sleep with interrupts disabled */
178         ASSERT(IRQ_ENABLED());
179 #endif
180
181 #if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS
182         Timer t;
183
184         ASSERT(!sig_check(SIG_SINGLE));
185         timer_set_event_signal(&t, proc_current(), SIG_SINGLE);
186         timer_setDelay(&t, delay);
187         timer_add(&t);
188         sig_wait(SIG_SINGLE);
189
190 #else /* !CONFIG_KERN_SIGNALS */
191
192         ticks_t start = timer_clock();
193
194         /* Busy wait */
195         while (timer_clock() - start < delay)
196         {
197 #if CONFIG_WATCHDOG
198                 wdt_reset();
199 #endif
200         }
201
202 #endif /* !CONFIG_KERN_SIGNALS */
203 }
204
205
206 #ifndef CONFIG_TIMER_DISABLE_UDELAY
207
208 /**
209  * Busy wait until the specified amount of high-precision ticks have elapsed.
210  *
211  * \note This function is interrupt safe, the only
212  *       requirement is a running hardware timer.
213  */
214 void timer_busyWait(hptime_t delay)
215 {
216         hptime_t now, prev = timer_hw_hpread();
217         hptime_t delta;
218
219         for(;;)
220         {
221                 now = timer_hw_hpread();
222                 /*
223                  * We rely on hptime_t being unsigned here to
224                  * reduce the modulo to an AND in the common
225                  * case of TIMER_HW_CNT.
226                  */
227                 delta = (now - prev) % TIMER_HW_CNT;
228                 if (delta >= delay)
229                         break;
230                 delay -= delta;
231                 prev = now;
232         }
233 }
234
235 /**
236  * Wait for the specified amount of time (expressed in microseconds).
237  *
238  * \bug In AVR arch the maximum amount of time that can be used as
239  *      delay could be very limited, depending on the hardware timer
240  *      used. Check timer_avr.h, and what register is used as hptime_t.
241  */
242 void timer_delayHp(hptime_t delay)
243 {
244         if (UNLIKELY(delay > us_to_hptime(1000)))
245         {
246                 timer_delayTicks(delay / (TIMER_HW_HPTICKS_PER_SEC / TIMER_TICKS_PER_SEC));
247                 delay %= (TIMER_HW_HPTICKS_PER_SEC / TIMER_TICKS_PER_SEC);
248         }
249
250         timer_busyWait(delay);
251 }
252 #endif /* CONFIG_TIMER_DISABLE_UDELAY */
253
254
255 /**
256  * Timer interrupt handler. Find soft timers expired and
257  * trigger corresponding events.
258  */
259 DEFINE_TIMER_ISR
260 {
261         /*
262          * With the Metrowerks compiler, the only way to force the compiler generate
263          * an interrupt service routine is to put a pragma directive within the function
264          * body.
265          */
266         #ifdef __MWERKS__
267         #pragma interrupt saveall
268         #endif
269
270 #ifndef CONFIG_TIMER_DISABLE_EVENTS
271         Timer *timer;
272 #endif
273         /*
274          * On systems sharing IRQ line and vector, this check is needed
275          * to ensure that IRQ is generated by timer source.
276          */
277         if (!timer_hw_triggered())
278                 return;
279
280         TIMER_STROBE_ON;
281
282         /* Perform hw IRQ handling */
283         timer_hw_irq();
284
285         /* Update the master ms counter */
286         ++_clock;
287
288 #ifndef CONFIG_TIMER_DISABLE_EVENTS
289         /*
290          * Check the first timer request in the list and process
291          * it when it has expired. Repeat this check until the
292          * first node has not yet expired. Since the list is sorted
293          * by expiry time, all the following requests are guaranteed
294          * to expire later.
295          */
296         while ((timer = (Timer *)LIST_HEAD(&timers_queue))->link.succ)
297         {
298                 /* This request in list has not yet expired? */
299                 if (_clock - timer->tick < 0)
300                         break;
301
302                 /* Retreat the expired timer */
303                 REMOVE(&timer->link);
304                 DB(timer->magic = TIMER_MAGIC_INACTIVE;)
305
306                 /* Execute the associated event */
307                 event_do(&timer->expire);
308         }
309 #endif /* CONFIG_TIMER_DISABLE_EVENTS */
310
311         TIMER_STROBE_OFF;
312 }
313
314 MOD_DEFINE(timer)
315
316 /**
317  * Initialize timer
318  */
319 void timer_init(void)
320 {
321         TIMER_STROBE_INIT;
322
323 #ifndef CONFIG_TIMER_DISABLE_EVENTS
324         LIST_INIT(&timers_queue);
325 #endif
326
327         _clock = 0;
328
329         timer_hw_init();
330
331         MOD_INIT(timer);
332 }