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