Base code for dtplug : Core, drivers, lib and some external drivers.
[dtplug] / core / systick.c
1 /****************************************************************************
2  *  core/systick.c
3  *
4  * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
5  *
6  * This program 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, see <http://www.gnu.org/licenses/>.
18  *
19  *************************************************************************** */
23 /***************************************************************************** */
24 /*               System Tick Timer                                             */
25 /***************************************************************************** */
27 #include <stdint.h>
28 #include "core/lpc_regs_17xx.h"
29 #include "core/lpc_core_cm3.h"
30 #include "core/system.h"
31 #include "core/systick.h"
33 /* FIXME : Actual use of sleep_count is OK in single tasking environment.
34  *    for multitasking, change the sleep function behaviour.
35  */
37 /* Static variables */
38 static volatile uint32_t sleep_count = 0;
39 static volatile uint32_t tick_ms = 0;
40 static volatile uint32_t systick_running = 0;
41 static volatile uint32_t tick_reload = 0;
43 /* Wraps every 50 days or so with a 1ms tick */
44 static volatile uint32_t global_wrapping_system_ticks = 0;
45 /* The systick cycles run at get_main_clock(), and would wrap more often! */
46 static volatile uint32_t global_wrapping_system_clock_cycles = 0;
49 struct systick_callback {
50         void (*callback) (uint32_t);
51         uint16_t period;
52         uint16_t countdown;
53 };
54 static volatile struct systick_callback cbs[MAX_SYSTICK_CALLBACKS] = {};
56 /* System Tick Timer Interrupt Handler */
57 void SysTick_Handler(void)
58 {
59         int i = 0;
60         global_wrapping_system_ticks++;
61         global_wrapping_system_clock_cycles += tick_reload;
62         if (sleep_count != 0) {
63                 sleep_count--;
64         }
65         for (i = 0; i < MAX_SYSTICK_CALLBACKS; i++) {
66                 if (cbs[i].callback != NULL) {
67                         cbs[i].countdown--;
68                         if (cbs[i].countdown == 0) {
69                                 cbs[i].countdown = cbs[i].period;
70                                 cbs[i].callback(global_wrapping_system_ticks);
71                         }
72                 }
73         }
74 }
77 /* Register a callback to be called every 'period' system ticks.
78  * returns the callback number if registration was OK.
79  * returns negative value on error.
80  * The callback will get the "global_wrapping_system_ticks" as argument, which wraps every 50 days
81  *   or so with a 1ms tick
82  */
83 int add_systick_callback(void (*callback) (uint32_t), uint16_t period)
84 {
85         int i = 0;
86         if (period == 0) {
87                 return -EINVAL;
88         }
89         for (i = 0; i < MAX_SYSTICK_CALLBACKS; i++) {
90                 if (cbs[i].callback == NULL) {
91                         cbs[i].callback = callback;
92                         cbs[i].period = period;
93                         cbs[i].countdown = period;
94                         return i;
95                 }
96         }
97         return -EBUSY;
98 }
100 int remove_systick_callback(void (*callback) (uint32_t))
102         int i = 0;
103         for (i = 0; i < MAX_SYSTICK_CALLBACKS; i++) {
104                 if (cbs[i].callback == callback) {
105                         cbs[i].callback = NULL;
106                         return 0;
107                 }
108         }
109         return -EINVAL;
112 /***************************************************************************** */
113 /* systick timer control function */
115 /* Start the system tick timer
116  * Starting the systick timer also resets the internal tick counters.
117  * If you need a value that goes beyond one start/stop cycle and accross resets,
118  *    then it's up to you to keep track of this using systick_get_tick_count() and/or
119  *    systick_get_clock_cycles().
120  */
121 void systick_start(void)
123         struct lpc_system_tick* systick = LPC_SYSTICK;
124         systick->value = 0;
125         systick_running = 1;
126         global_wrapping_system_ticks = 0;
127         global_wrapping_system_clock_cycles = tick_reload;
128         systick->control |= LPC_SYSTICK_CTRL_ENABLE;
130 /* Stop the system tick timer */
131 void systick_stop(void)
133         struct lpc_system_tick* systick = LPC_SYSTICK;
134         systick->control &= ~(LPC_SYSTICK_CTRL_ENABLE);
135         systick_running = 0;
136         systick->value = 0;
138 /* Reset the system tick timer, making it count down from the reload value again
139  * Reseting the systick timer also resets the internal tick counters.
140  * If you need a value that goes beyond one start/stop cycle and accross resets,
141  *    then it's up to you to keep track of this using systick_get_tick_count() and/or
142  *    systick_get_clock_cycles().
143  */
144 void systick_reset(void)
146         struct lpc_system_tick* systick = LPC_SYSTICK;
147         systick->value = 0;
148         global_wrapping_system_ticks = 0;
149         global_wrapping_system_clock_cycles = tick_reload;
152 /* Get system tick timer current value (counts at get_main_clock() !) */
153 uint32_t systick_get_timer_val(void)
155         struct lpc_system_tick* systick = LPC_SYSTICK;
156         return systick->value;
159 /* Check if systick is running (return 1) or not (return 0) */
160 uint32_t is_systick_running(void)
162         return systick_running;
165 /* Get the system tick period in ms
166  * A vaue of 0 means the system tick timer has not been configured.
167  * Note : calls to msleep() or usleep() will configure the system tick timer
168  *        with a value of 1ms if it was not configured yet.
169  */
170 uint32_t systick_get_tick_ms_period(void)
172         return tick_ms;
175 /* Get the "timer wrapped" indicator.
176  * Used in usleep() function.
177  * Note : the first to call this function will get the right information.
178  * All subsequent calls will get wrong indication.
179  * Thus this function is not exported to user space, user should compare global
180  * ticks values or tick counter values. */
181 static uint32_t systick_counted_to_zero(void)
183         struct lpc_system_tick* systick = LPC_SYSTICK;
184         return (systick->control & LPC_SYSTICK_CTRL_COUNTFLAG);
188 /* Get the number of system ticks ... since last wrapping of the counter, which
189  * is about 50 days with a 1ms system tick. */
190 uint32_t systick_get_tick_count(void)
192         return global_wrapping_system_ticks;
195 /* Get the number of clock cycles ... since last wrapping of the counter. */
196 uint32_t systick_get_clock_cycles(void)
198         struct lpc_system_tick* systick = LPC_SYSTICK;
199         /* global_wrapping_system_clock_cycles has been initialised to reload value, thus there is
200          * no need to add it here, making the call quicker */
201         return global_wrapping_system_clock_cycles - systick->value;
204 /***************************************************************************** */
205 /* Power up the system tick timer.
206  * ms is the interval between system tick timer interrupts. If set to 0, the default
207  *     value is used, which should provide a 1ms period.
208  */
209 void systick_timer_on(uint32_t ms)
211         struct lpc_system_tick* systick = LPC_SYSTICK;
212         uint32_t reload; /* The reload value for the counter */
214         /* Set the reload value */
215         if (ms != 0) {
216                 reload = ((get_main_clock() / 1000) * ms) - 1;
217         } else {
218                 reload = (get_main_clock() / 1000) - 1;
219                 ms = 1;
220         }
221         /* For the LPC1224 the system tick clock is fixed to half the frequency of the system clock */
222         reload = reload >> 1; /* Divide by 2 */
223         systick->reload_val = (reload & 0xffffff);
224         tick_ms = ms;
225         tick_reload = systick->reload_val;
227         /* Start counting from the reload value, writting anything would do ... */
228         systick->value = reload;
229         /* Consider we already counted one cycle, making further reading of this count easier */
230         global_wrapping_system_clock_cycles = tick_reload;
232         /* And enable counter interrupt */
233         systick->control = LPC_SYSTICK_CTRL_TICKINT;
234         systick_running = 0;
236         /* FIXME : document this */
237         NVIC_SetPriority(SYSTICK_IRQ, ((1 << LPC_NVIC_PRIO_BITS) - 1));
240 /* Removes the main clock from the selected timer block */
241 void systick_timer_off(void)
243         struct lpc_system_tick* systick = LPC_SYSTICK;
244         systick->control = 0;
245         systick->reload_val = 0;
246         tick_ms = 0;
247         tick_reload = 0;
248         systick_running = 0;
252 /***************************************************************************** */
253 /* Sleeping functions */
255 /* Set the sleep countdown value
256  * A sleep will end when this value reaches 0
257  * Note that calls to this function while a sleep() has been initiated will change the
258  *   sleep duration ....
259  */
260 void set_sleep(uint32_t ticks)
262         sleep_count = ticks;
264 /* Return current sleep count_down counter */
265 uint32_t get_sleep(void)
267         return sleep_count;
270 /* Actual sleep function, checks that system tick counter is configured to generate
271  * an interrupt to move sleep_count down to 0
272  */
273 #define SYSTICK_CAN_SLEEP   (LPC_SYSTICK_CTRL_TICKINT | LPC_SYSTICK_CTRL_ENABLE)
274 static uint32_t sleep(void)
276         struct lpc_system_tick* systick = LPC_SYSTICK;
277         if ((systick->control & SYSTICK_CAN_SLEEP) != SYSTICK_CAN_SLEEP) {
278                 return -1;
279         }
280         do { } while (sleep_count != 0);
281         return 0;
284 /* This msleep sleeps less than the required amount of time as it forgets about
285  * the already elapsed time of the systick timer since last tick. */
286 void msleep(uint32_t ms)
288         uint32_t ticks = 0;
290         if (tick_ms == 0) {
291                 systick_timer_on(1);
292                 ticks = ms;
293         } else {
294                 ticks = ms / tick_ms;
295         }
296         set_sleep(ticks);
297         if (systick_running == 0) {
298                 systick_start();
299         }
300         sleep();
303 void usleep(uint32_t us)
305         struct lpc_system_tick* systick = LPC_SYSTICK;
306         uint32_t start = systick->value; /* Grab the starting (call time) value now */
307         uint32_t count = 0;
308         uint32_t end = 0;
310         end = systick_counted_to_zero(); /* erase loop indicator */
311         if (us > 1000) {
312                 msleep(us / 1000);
313                 us = us % 1000;
314         } else {
315                 if (systick_running == 0) {
316                         if (tick_ms == 0) {
317                                 systick_timer_on(1);
318                         }
319                         systick_start();
320                 }
321         }
322         count = get_main_clock() / (1000 * 1000) * us;
323         if (count > start) {
324                 end = (systick->reload_val - (count - start));
325                 do { } while (systick_counted_to_zero() == 0); /* Wait for timer loop */
326                 do { } while (systick->value > end); /* Wait for sleep duration */
327         } else {
328                 end = start - count;
329                 /* Wait for sleep duration.
330                  * If the counter looped, it means we already waited too much */
331                 do { } while ((systick->value > end) && (systick_counted_to_zero() == 0));
332         }