0986e0a4ea0c5658ecbeb540d8d78a43b3cb2159
[bertos.git] / drv / timer.c
1 /*!
2  * \file
3  * <!--
4  * Copyright 2003, 2004 Develer S.r.l. (http://www.develer.com/)
5  * Copyright 2000 Bernardo Innocenti <bernie@develer.com>
6  * This file is part of DevLib - See devlib/README for information.
7  * -->
8  *
9  * \brief Hardware independent timer driver (implementation)
10  *
11  * \version $Id$
12  * \author Bernardo Innocenti <bernie@develer.com>
13  */
14
15 /*#*
16  *#* $Log$
17  *#* Revision 1.25  2005/07/19 07:26:37  bernie
18  *#* Refactor to decouple timer ticks from milliseconds.
19  *#*
20  *#* Revision 1.24  2005/04/11 19:10:28  bernie
21  *#* Include top-level headers from cfg/ subdir.
22  *#*
23  *#* Revision 1.23  2004/12/13 12:07:06  bernie
24  *#* DISABLE_IRQSAVE/ENABLE_IRQRESTORE: Convert to IRQ_SAVE_DISABLE/IRQ_RESTORE.
25  *#*
26  *#* Revision 1.22  2004/12/08 09:12:09  bernie
27  *#* Rename time_t to mtime_t.
28  *#*
29  *#* Revision 1.21  2004/11/28 23:20:25  bernie
30  *#* Remove obsolete INITLIST macro.
31  *#*
32  *#* Revision 1.20  2004/11/16 20:59:06  bernie
33  *#* Add watchdog timer support.
34  *#*/
35
36 #include "timer.h"
37 #include <cfg/cpu.h>
38 #include <hw.h>
39 #include <cfg/debug.h>
40 #include <appconfig.h>
41
42 #include CPU_CSOURCE(timer)
43
44 /*
45  * Sanity check for config parameters required by this module.
46  */
47 #if !defined(CONFIG_KERNEL) || ((CONFIG_KERNEL != 0) && CONFIG_KERNEL != 1)
48         #error CONFIG_KERNEL must be set to either 0 or 1 in config.h
49 #endif
50 #if !defined(CONFIG_WATCHDOG) || ((CONFIG_WATCHDOG != 0) && CONFIG_WATCHDOG != 1)
51         #error CONFIG_WATCHDOG must be set to either 0 or 1 in config.h
52 #endif
53
54 #if CONFIG_WATCHDOG
55         #include <drv/wdt.h>
56 #endif
57
58 #if CONFIG_KERNEL && CONFIG_KERN_SIGNALS
59         #include <kern/proc.h>
60 #endif
61
62
63 /*!
64  * \def CONFIG_TIMER_STROBE
65  *
66  * This is a debug facility that can be used to
67  * monitor timer interrupt activity on an external pin.
68  *
69  * To use strobes, redefine the macros TIMER_STROBE_ON,
70  * TIMER_STROBE_OFF and TIMER_STROBE_INIT and set
71  * CONFIG_TIMER_STROBE to 1.
72  */
73 #if !defined(CONFIG_TIMER_STROBE) || !CONFIG_TIMER_STROBE
74         #define TIMER_STROBE_ON    do {/*nop*/} while(0)
75         #define TIMER_STROBE_OFF   do {/*nop*/} while(0)
76         #define TIMER_STROBE_INIT  do {/*nop*/} while(0)
77 #endif
78
79
80 //! Master system clock (1 tick accuracy)
81 volatile ticks_t _clock;
82
83
84 #ifndef CONFIG_TIMER_DISABLE_EVENTS
85
86 /*!
87  * List of active asynchronous timers.
88  */
89 REGISTER static List timers_queue;
90
91
92 /*!
93  * Add the specified timer to the software timer service queue.
94  * When the delay indicated by the timer expires, the timer
95  * device will execute the event associated with it.
96  *
97  * \note Interrupt safe
98  */
99 void timer_add(Timer *timer)
100 {
101         Timer *node;
102         cpuflags_t flags;
103
104
105         /* Inserting timers twice causes mayhem. */
106         ASSERT(timer->magic != TIMER_MAGIC_ACTIVE);
107         DB(timer->magic = TIMER_MAGIC_ACTIVE;)
108
109         IRQ_SAVE_DISABLE(flags);
110
111         /* Calculate expiration time for this timer */
112         timer->tick = _clock + timer->_delay;
113
114         /*
115          * Search for the first node whose expiration time is
116          * greater than the timer we want to add.
117          */
118         node = (Timer *)LIST_HEAD(&timers_queue);
119         while (node->link.succ)
120         {
121                 /*
122                  * Stop just after the insertion point.
123                  * (this fancy compare takes care of wrap-arounds).
124                  */
125                 if (node->tick - timer->tick > 0)
126                         break;
127
128                 /* Go to next node */
129                 node = (Timer *)node->link.succ;
130         }
131
132         /* Enqueue timer request into the list */
133         INSERTBEFORE(&timer->link, &node->link);
134
135         IRQ_RESTORE(flags);
136 }
137
138
139 /*!
140  * Remove a timer from the timer queue before it has expired.
141  */
142 Timer *timer_abort(Timer *timer)
143 {
144         ATOMIC(REMOVE(&timer->link));
145         DB(timer->magic = TIMER_MAGIC_INACTIVE;)
146
147         return timer;
148 }
149
150 #endif /* CONFIG_TIMER_DISABLE_EVENTS */
151
152
153 /*!
154  * Wait for the specified amount of time (expressed in ms).
155  */
156 void timer_delayTicks(ticks_t delay)
157 {
158 #if defined(IRQ_GETSTATE)
159         /* We shouldn't sleep with interrupts disabled */
160         ASSERT(IRQ_GETSTATE());
161 #endif
162
163 #if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS
164         Timer t;
165
166         ASSERT(!sig_check(SIG_SINGLE));
167         timer_set_event_signal(&t, proc_current(), SIG_SINGLE);
168         timer_set_delay(&t, delay);
169         timer_add(&t);
170         sig_wait(SIG_SINGLE);
171
172 #else /* !CONFIG_KERN_SIGNALS */
173
174         ticks_t start = timer_clock();
175
176         /* Busy wait */
177         while (timer_clock() - start < delay)
178         {
179 #if CONFIG_WATCHDOG
180                 wdt_reset();
181 #endif
182         }
183
184 #endif /* !CONFIG_KERN_SIGNALS */
185 }
186
187
188 #ifndef CONFIG_TIMER_DISABLE_UDELAY
189
190 /*!
191  * Busy wait until the specified amount of high-precision ticks have elapsed.
192  *
193  * \note This function is interrupt safe, the only
194  *       requirement is a running hardware timer.
195  */
196 void timer_busyWait(hptime_t delay)
197 {
198         hptime_t now, prev = timer_hw_hpread();
199         hptime_t delta;
200
201         for(;;)
202         {
203                 now = timer_hw_hpread();
204                 /*
205                  * We rely on hptime_t being unsigned here to
206                  * reduce the modulo to an AND in the common
207                  * case of TIMER_HW_CNT.
208                  */
209                 delta = (now - prev) % TIMER_HW_CNT;
210                 if (delta >= delay)
211                         break;
212                 delay -= delta;
213                 prev = now;
214         }
215 }
216
217 /*!
218  * Wait for the specified amount of time (expressed in microseconds).
219  *
220  * \bug In AVR arch the maximum amount of time that can be used as
221  *      delay could be very limited, depending on the hardware timer
222  *      used. Check timer_avr.h, and what register is used as hptime_t.
223  */
224 void timer_delayHp(hptime_t delay)
225 {
226         if (UNLIKELY(delay > us_to_hptime(1000)))
227         {
228                 timer_delayTicks(delay / (TIMER_HW_HPTICKS_PER_SEC / TIMER_TICKS_PER_SEC));
229                 delay %= (TIMER_HW_HPTICKS_PER_SEC / TIMER_TICKS_PER_SEC);
230         }
231
232         timer_busyWait(delay);
233 }
234 #endif /* CONFIG_TIMER_DISABLE_UDELAY */
235
236
237 /*!
238  * Timer interrupt handler. Find soft timers expired and
239  * trigger corresponding events.
240  */
241 DEFINE_TIMER_ISR
242 {
243         /*
244          * With the Metrowerks compiler, the only way to force the compiler generate
245          * an interrupt service routine is to put a pragma directive within the function
246          * body.
247          */
248         #ifdef __MWERKS__
249         #pragma interrupt saveall
250         #endif
251
252 #ifndef CONFIG_TIMER_DISABLE_EVENTS
253         Timer *timer;
254 #endif
255
256         TIMER_STROBE_ON;
257
258         timer_hw_irq();
259
260         /* Update the master ms counter */
261         ++_clock;
262
263 #ifndef CONFIG_TIMER_DISABLE_EVENTS
264         /*
265          * Check the first timer request in the list and process
266          * it when it has expired. Repeat this check until the
267          * first node has not yet expired. Since the list is sorted
268          * by expiry time, all the following requests are guaranteed
269          * to expire later.
270          */
271         while ((timer = (Timer *)LIST_HEAD(&timers_queue))->link.succ)
272         {
273                 /* This request in list has not yet expired? */
274                 if (_clock - timer->tick < 0)
275                         break;
276
277                 /* Retreat the expired timer */
278                 REMOVE(&timer->link);
279                 DB(timer->magic = TIMER_MAGIC_INACTIVE;)
280
281                 /* Execute the associated event */
282                 event_do(&timer->expire);
283         }
284 #endif /* CONFIG_TIMER_DISABLE_EVENTS */
285
286         TIMER_STROBE_OFF;
287 }
288
289
290 /*!
291  * Initialize timer
292  */
293 void timer_init(void)
294 {
295         TIMER_STROBE_INIT;
296
297 #ifndef CONFIG_TIMER_DISABLE_EVENTS
298         LIST_INIT(&timers_queue);
299 #endif
300
301         _clock = 0;
302
303         timer_hw_init();
304 }
305
306
307 #if CONFIG_TEST
308
309 static void timer_test_constants(void)
310 {
311         kprintf("TIMER_PRESCALER=%d\n", TIMER_PRESCALER);
312         kprintf("TIMER_HW_HPTICKS_PER_SEC=%lu\n", TIMER_HW_HPTICKS_PER_SEC);
313         #ifdef TIMER1_OVF_COUNT
314                 kprintf("TIMER1_OVF_COUNT=%d\n", (int)TIMER1_OVF_COUNT);
315         #endif
316         kprintf("TIMER_TICKS_PER_MSEC=%d\n", (int)TIMER_TICKS_PER_MSEC);
317         kprintf("\n");
318         kprintf("ms_to_ticks(100)=%lu\n", ms_to_ticks(100));
319         kprintf("ms_to_ticks(10000)=%lu\n", ms_to_ticks(10000));
320         kprintf("us_to_ticks(100)=%lu\n", us_to_ticks(100));
321         kprintf("us_to_ticks(10000)=%lu\n", us_to_ticks(10000));
322         kprintf("\n");
323         kprintf("ticks_to_ms(100)=%lu\n", ticks_to_ms(100));
324         kprintf("ticks_to_ms(10000)=%lu\n", ticks_to_ms(10000));
325         kprintf("ticks_to_us(100)=%lu\n", ticks_to_us(100));
326         kprintf("ticks_to_us(10000)=%lu\n", ticks_to_us(10000));
327         kprintf("\n");
328         kprintf("hptime_to_us(100)=%lu\n", hptime_to_us(100));
329         kprintf("hptime_to_us(10000)=%lu\n", hptime_to_us(10000));
330         kprintf("us_to_hptime(100)=%lu\n", us_to_hptime(100));
331         kprintf("us_to_hptime(10000)=%lu\n", us_to_hptime(10000));
332 }
333
334 static void timer_test_delay(void)
335 {
336         int i;
337
338         kputs("Delay test\n");
339         for (i = 0; i < 1000; i += 100)
340         {
341                 kprintf("delay %d...", i);
342                 timer_delay(i);
343                 kputs("done\n");
344         }
345 }
346
347 static void timer_test_hook(iptr_t _timer)
348 {
349         Timer *timer = (Timer *)(void *)_timer;
350
351         kprintf("Timer %ld expired\n", ticks_to_ms(timer->_delay));
352         timer_add(timer);
353 }
354
355 static void timer_test_async(void)
356 {
357         static Timer test_timers[5];
358         static const mtime_t test_delays[5] = { 170, 50, 310, 1500, 310 };
359         size_t i;
360
361         for (i = 0; i < countof(test_timers); ++i)
362         {
363                 Timer *timer = &test_timers[i];
364                 timer_setDelay(timer, ms_to_ticks(test_delays[i]));
365                 timer_set_event_softint(timer, timer_test_hook, (iptr_t)timer);
366                 timer_add(timer);
367         }
368 }
369
370 static void timer_test_poll(void)
371 {
372         int secs = 0;
373         mtime_t start_time = ticks_to_ms(timer_clock());
374         mtime_t now;
375
376         while (secs <= 10)
377         {
378                 now = ticks_to_ms(timer_clock());
379                 if (now - start_time >= 1000)
380                 {
381                         ++secs;
382                         start_time += 1000;
383                         kprintf("seconds = %d, ticks=%ld\n", secs, now);
384                 }
385         }
386 }
387
388 void timer_test(void)
389 {
390         timer_test_constants();
391         timer_test_delay();
392         timer_test_async();
393         timer_test_poll();
394 }
395
396 #endif /* CONFIG_TEST */