Add dual-license information.
[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.2  2004/06/03 11:27:09  bernie
19  * Add dual-license information.
20  *
21  * Revision 1.1  2004/05/23 18:23:30  bernie
22  * Import drv/timer module.
23  *
24  */
25
26 #include "hw.h"
27 #include "kdebug.h"
28 #include "timer.h"
29
30 #ifdef CONFIG_KERN_SIGNALS
31 #include <kern/proc.h>
32 #endif
33
34 #if (ARCH & ARCH_EMUL)
35
36 # error To be recoded
37
38 #elif defined(__AVR__)
39 #       include "timer_avr.h"
40 #elif defined(__IAR_SYSTEMS_ICC) || defined(__IAR_SYSTEMS_ICC__) /* 80C196 */
41 #       include "timer_i196.h"
42 #elif defined (__m56800__)
43 #       include "timer_dsp56k.h"
44 #else
45 #       error Unknown system
46 #endif
47
48
49 /*! Number of available timers */
50 #define MAX_TIMERS 4
51
52
53 //! Master system clock (1ms accuracy)
54 volatile time_t _clock;
55
56 static Timer soft_timers[MAX_TIMERS];   /*!< Pool of Timer structures */
57 static List timers_pool;                /*!< Pool of free timers */
58 REGISTER static List timers_queue;      /*!< Active timers */
59
60
61 /*!
62  * Return a new timer picking and removing it from the available
63  * timers pool. Return NULL if no more timers are available.
64  */
65 Timer *timer_new(void)
66 {
67         Timer *timer;
68         cpuflags_t flags;
69
70         DISABLE_IRQSAVE(flags);
71
72         /* Should never happen */
73         if (ISLISTEMPTY(&timers_pool))
74         {
75                 ENABLE_IRQRESTORE(flags);
76                 DB(kprintf("Tmrspool empty\n");)
77                 return NULL;
78         }
79
80         /* Get a timer from the free pool */
81         timer = (Timer *)timers_pool.head;
82         REMOVE((Node *)timer);
83
84         ENABLE_IRQRESTORE(flags);
85
86         return timer;
87 }
88
89
90 /*!
91  * Delete a timer, putting it in the available timers queue.
92  */
93 void timer_delete(Timer *timer)
94 {
95         cpuflags_t flags;
96         DISABLE_IRQSAVE(flags);
97         ADDHEAD(&timers_pool, &timer->link);
98         ENABLE_IRQRESTORE(flags);
99 }
100
101
102 /*!
103  * Add the specified timer to the software timer service queue.
104  * When the delay indicated by the timer expires, the timer
105  * device will execute the event associated with it.
106  *
107  * \note Interrupt safe
108  */
109 void timer_add(Timer *timer)
110 {
111         Timer *node;
112         cpuflags_t flags;
113
114         DISABLE_IRQSAVE(flags);
115
116         /* Calculate expiration time for this timer */
117         timer->tick = _clock + timer->delay;
118
119         /* Search for the first node whose expiration time is
120          * greater than the timer we want to add.
121          */
122         node = (Timer *)timers_queue.head;
123         while (node->link.succ)
124         {
125                 /* Stop just after the insert point */
126                 if (node->tick > timer->tick)
127                         break;
128
129                 /* Go to next node */
130                 node = (Timer *)node->link.succ;
131         }
132
133         /* Enqueue timer request into the list */
134         INSERTBEFORE((Node *)timer, (Node *)node);
135
136         ENABLE_IRQRESTORE(flags);
137 }
138
139
140 /*!
141  * Remove a timer from the timer queue before it has expired
142  */
143 Timer *timer_abort(Timer *timer)
144 {
145         cpuflags_t flags;
146         DISABLE_IRQSAVE(flags);
147         REMOVE((Node *)timer);
148         ENABLE_IRQRESTORE(flags);
149
150         return timer;
151 }
152
153
154 /*!
155  * Wait for the specified amount of time (expressed in ms)
156  */
157 void timer_delay(time_t time)
158 {
159 #ifdef CONFIG_KERN_SIGNALS
160         Timer t;
161
162         ASSERT(!sig_check(SIG_SINGLE));
163         timer_set_event_signal(&t, proc_current(), SIG_SINGLE);
164         timer_set_delay(&t, time);
165         timer_add(&t);
166         sig_wait(SIG_SINGLE);
167 #else
168         time_t start = timer_gettick();
169
170         while (timer_gettick() - start < time) { /* nop */ }
171 #endif
172 }
173
174 /*!
175  * Wait for the specified amount of time (expressed in microseconds)
176  */
177 void timer_udelay(utime_t usec_delay)
178 {
179         if (usec_delay > 1000)
180         {
181                 timer_delay(usec_delay / 1000);
182                 usec_delay %= 1000;
183         }
184
185         // FIXME: This multiplication is too slow at run-time. We should try and move it
186         //  to compile-time by exposing the TIMER_HW_HPTICKS_PER_SEC in the header
187         //  file.
188         hptime_t start = timer_hw_hpread();
189         hptime_t delay = (uint32_t)usec_delay * TIMER_HW_HPTICKS_PER_SEC / 1000000ul;
190
191         while (timer_hw_hpread() - start < delay) 
192         {}
193 }
194
195
196 /*!
197  * Timer interrupt handler. Find soft timers expired and
198  * trigger corresponding events.
199  */
200 DEFINE_TIMER_ISR
201 {
202         /* With the Metrowerks compiler, the only way to force the compiler generate
203            an interrupt service routine is to put a pragma directive within the function
204            body. */
205         #ifdef __MWERKS__
206         #pragma interrupt saveall
207         #endif
208
209         Timer *timer;
210
211         timer_hw_irq();
212
213         /* Update the master ms counter */
214         ++_clock;
215
216         /*
217          * Check the first timer request in the list and process
218          * it when it has expired. Repeat this check until the
219          * first node has not yet expired. Since the list is sorted
220          * by expiry time, all the following requests are guaranteed
221          * to expire later.
222          */
223         while ((timer = (Timer *)timers_queue.head)->link.succ)
224         {
225                 /* This request in list has not yet expired? */
226                 if (_clock < timer->tick)
227                         break;
228
229                 /* Retreat the expired timer */
230                 REMOVE((Node *)timer);
231
232                 /* Execute the associated event */
233                 DOEVENT_INTR(&timer->expire);
234         }
235 }
236
237
238
239 /*!
240  * Initialize timer
241  */
242 void timer_init(void)
243 {
244         int i;
245
246         INITLIST(&timers_queue);
247         INITLIST(&timers_pool);
248
249         /* Init all software timers in the free pool */
250         for (i = 0; i < MAX_TIMERS; i++)
251                 ADDTAIL(&timers_pool, (Node *)&soft_timers[i]);
252
253         _clock = 0;
254
255         timer_hw_init();
256 }
257