timer_gettick(): Rename to timer_ticks() and add backwards compatibility inline.
[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  * \version $Id$
10  *
11  * \author Bernardo Innocenti <bernie@develer.com>
12  *
13  * \brief Hardware independent timer driver (implementation)
14  */
15
16 /*
17  * $Log$
18  * Revision 1.13  2004/08/10 06:59:09  bernie
19  * timer_gettick(): Rename to timer_ticks() and add backwards compatibility inline.
20  *
21  * Revision 1.12  2004/08/08 05:59:37  bernie
22  * Remove a few useless casts.
23  *
24  * Revision 1.11  2004/08/02 20:20:29  aleph
25  * Merge from project_ks
26  *
27  * Revision 1.10  2004/07/30 14:15:53  rasky
28  * Nuovo supporto unificato per detect della CPU
29  *
30  * Revision 1.9  2004/07/21 00:15:13  bernie
31  * Put timer driver on diet.
32  *
33  * Revision 1.8  2004/07/18 21:57:07  bernie
34  * Fix preprocessor warning with potentially undefined symbol.
35  *
36  * Revision 1.6  2004/06/07 18:10:06  aleph
37  * Remove free pool of timers; use user-provided Timer structure instead
38  *
39  * Revision 1.5  2004/06/07 15:56:55  aleph
40  * Some tabs cleanup and add timer strobe macros
41  *
42  * Revision 1.4  2004/06/06 18:25:44  bernie
43  * Rename event macros to look like regular functions.
44  *
45  * Revision 1.3  2004/06/06 17:18:42  bernie
46  * Fix \!CONFIG_KERN_SIGNALS code paths.
47  *
48  * Revision 1.2  2004/06/03 11:27:09  bernie
49  * Add dual-license information.
50  *
51  * Revision 1.1  2004/05/23 18:23:30  bernie
52  * Import drv/timer module.
53  *
54  */
55
56 #include "hw.h"
57 #include "kdebug.h"
58 #include "timer.h"
59 #include CPU_HEADER(timer)
60
61 #if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS
62         #include <kern/proc.h>
63 #endif
64
65 //! Master system clock (1ms accuracy)
66 volatile time_t _clock;
67
68
69 #ifndef CONFIG_TIMER_DISABLE_EVENTS
70
71 /*!
72  * List of active asynchronous timers.
73  */
74 REGISTER static List timers_queue;
75
76
77 /*!
78  * Add the specified timer to the software timer service queue.
79  * When the delay indicated by the timer expires, the timer
80  * device will execute the event associated with it.
81  *
82  * \note Interrupt safe
83  */
84 void timer_add(Timer *timer)
85 {
86         Timer *node;
87         cpuflags_t flags;
88
89         DISABLE_IRQSAVE(flags);
90
91         /* Calculate expiration time for this timer */
92         timer->tick = _clock + timer->delay;
93
94         /* Search for the first node whose expiration time is
95          * greater than the timer we want to add.
96          */
97         node = (Timer *)timers_queue.head;
98         while (node->link.succ)
99         {
100                 /* Stop just after the insertion point */
101                 if (node->tick > timer->tick)
102                         break;
103
104                 /* Go to next node */
105                 node = (Timer *)node->link.succ;
106         }
107
108         /* Enqueue timer request into the list */
109         INSERTBEFORE(&timer->link, &node->link);
110
111         ENABLE_IRQRESTORE(flags);
112 }
113
114
115 /*!
116  * Remove a timer from the timer queue before it has expired
117  */
118 Timer *timer_abort(Timer *timer)
119 {
120         cpuflags_t flags;
121         DISABLE_IRQSAVE(flags);
122         REMOVE(&timer->link);
123         ENABLE_IRQRESTORE(flags);
124
125         return timer;
126 }
127
128 #endif /* CONFIG_TIMER_DISABLE_EVENTS */
129
130
131 /*!
132  * Wait for the specified amount of time (expressed in ms)
133  */
134 void timer_delay(time_t time)
135 {
136 #if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS
137         Timer t;
138
139         ASSERT(!sig_check(SIG_SINGLE));
140         timer_set_event_signal(&t, proc_current(), SIG_SINGLE);
141         timer_set_delay(&t, time);
142         timer_add(&t);
143         sig_wait(SIG_SINGLE);
144
145 #else /* !CONFIG_KERN_SIGNALS */
146
147         time_t start = timer_ticks();
148
149         /* Busy wait */
150         while (timer_ticks() - start < time) { /* nop */ }
151
152 #endif /* !CONFIG_KERN_SIGNALS */
153 }
154
155
156 #ifndef CONFIG_TIMER_DISABLE_UDELAY
157 /*!
158  * Wait for the specified amount of time (expressed in microseconds)
159  *
160  * \bug In AVR arch the maximum amount of time that can be used as
161  *      delay could be very limited, depending on the hardware timer
162  *      used. Check timer_avr.h, and what register is used as hptime_t.
163  */
164 void timer_udelay(utime_t usec_delay)
165 {
166         if (UNLIKELY(usec_delay > 1000))
167         {
168                 timer_delay(usec_delay / 1000);
169                 usec_delay %= 1000;
170         }
171
172         // FIXME: This multiplication is too slow at run-time. We should try and move it
173         //  to compile-time by exposing the TIMER_HW_HPTICKS_PER_SEC in the header
174         //  file.
175         hptime_t start = timer_hw_hpread();
176         hptime_t delay = (uint32_t)usec_delay * TIMER_HW_HPTICKS_PER_SEC / 1000000ul;
177
178         while (timer_hw_hpread() - start < delay)
179         {}
180 }
181 #endif /* CONFIG_TIMER_DISABLE_UDELAY */
182
183
184 /*!
185  * Timer interrupt handler. Find soft timers expired and
186  * trigger corresponding events.
187  */
188 DEFINE_TIMER_ISR
189 {
190         /*
191          * With the Metrowerks compiler, the only way to force the compiler generate
192          * an interrupt service routine is to put a pragma directive within the function
193          * body.
194          */
195         #ifdef __MWERKS__
196         #pragma interrupt saveall
197         #endif
198
199 #ifndef CONFIG_TIMER_DISABLE_EVENTS
200         Timer *timer;
201 #endif
202
203         TIMER_STROBE_ON;
204
205         timer_hw_irq();
206
207         /* Update the master ms counter */
208         ++_clock;
209
210 #ifndef CONFIG_TIMER_DISABLE_EVENTS
211         /*
212          * Check the first timer request in the list and process
213          * it when it has expired. Repeat this check until the
214          * first node has not yet expired. Since the list is sorted
215          * by expiry time, all the following requests are guaranteed
216          * to expire later.
217          */
218         while ((timer = (Timer *)timers_queue.head)->link.succ)
219         {
220                 /* This request in list has not yet expired? */
221                 if (_clock < timer->tick)
222                         break;
223
224                 /* Retreat the expired timer */
225                 REMOVE(&timer->link);
226
227                 /* Execute the associated event */
228                 event_do(&timer->expire);
229         }
230 #endif /* CONFIG_TIMER_DISABLE_EVENTS */
231
232         TIMER_STROBE_OFF;
233 }
234
235
236 /*!
237  * Initialize timer
238  */
239 void timer_init(void)
240 {
241         TIMER_STROBE_INIT;
242
243 #ifndef CONFIG_TIMER_DISABLE_EVENTS
244         INITLIST(&timers_queue);
245 #endif
246
247         _clock = 0;
248
249         timer_hw_init();
250 }