Initial commit, code based on LPC1224 support and LPC812-MAX CMSIS-DAP interface...
[lpc11u3x] / 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_11u3x.h"
29 #include "core/lpc_core_cm0.h"
30 #include "core/system.h"
31 #include "core/systick.h"
34 /* Static variables */
35 static volatile uint32_t sleep_count = 0;
36 static volatile uint32_t tick_ms = 0;
37 static volatile uint32_t systick_running = 0;
38 static volatile uint32_t tick_reload = 0;
39 static uint32_t usleep_us_count = 0;
41 /* Wraps every 50 days or so with a 1ms tick */
42 static volatile uint32_t global_wrapping_system_ticks = 0;
43 /* The systick cycles run at get_main_clock(), and would wrap more often! */
44 static volatile uint32_t global_wrapping_system_clock_cycles = 0;
47 struct systick_callback {
48         void (*callback) (uint32_t);
49         uint16_t period;
50         uint16_t countdown;
51 };
52 static volatile struct systick_callback cbs[MAX_SYSTICK_CALLBACKS] = {};
54 /* System Tick Timer Interrupt Handler */
55 void SysTick_Handler(void)
56 {
57         int i = 0;
58         global_wrapping_system_ticks++;
59         global_wrapping_system_clock_cycles += tick_reload;
60         if (sleep_count != 0) {
61                 sleep_count--;
62         }
63         for (i = 0; i < MAX_SYSTICK_CALLBACKS; i++) {
64                 if (cbs[i].callback != NULL) {
65                         cbs[i].countdown--;
66                         if (cbs[i].countdown == 0) {
67                                 cbs[i].countdown = cbs[i].period;
68                                 cbs[i].callback(global_wrapping_system_ticks);
69                         }
70                 }
71         }
72 }
75 /* Register a callback to be called every 'period' system ticks.
76  * returns the callback number if registration was OK.
77  * returns negative value on error.
78  * The callback will get the "global_wrapping_system_ticks" as argument, which wraps every 50 days
79  *   or so with a 1ms tick
80  */
81 int add_systick_callback(void (*callback) (uint32_t), uint16_t period)
82 {
83         int i = 0;
84         if (period == 0) {
85                 return -EINVAL;
86         }
87         for (i = 0; i < MAX_SYSTICK_CALLBACKS; i++) {
88                 if (cbs[i].callback == NULL) {
89                         cbs[i].callback = callback;
90                         cbs[i].period = period;
91                         cbs[i].countdown = period;
92                         return i;
93                 }
94         }
95         return -EBUSY;
96 }
98 int remove_systick_callback(void (*callback) (uint32_t))
99 {
100         int i = 0;
101         for (i = 0; i < MAX_SYSTICK_CALLBACKS; i++) {
102                 if (cbs[i].callback == callback) {
103                         cbs[i].callback = NULL;
104                         return 0;
105                 }
106         }
107         return -EINVAL;
110 /***************************************************************************** */
111 /* systick timer control function */
113 /* Start the system tick timer
114  * Starting the systick timer also resets the internal tick counters.
115  * If you need a value that goes beyond one start/stop cycle and accross resets,
116  *    then it's up to you to keep track of this using systick_get_tick_count() and/or
117  *    systick_get_clock_cycles().
118  */
119 void systick_start(void)
121         struct lpc_system_tick* systick = LPC_SYSTICK;
122         systick->value = 0;
123         systick_running = 1;
124         global_wrapping_system_ticks = 0;
125         global_wrapping_system_clock_cycles = tick_reload;
126         systick->control |= LPC_SYSTICK_CTRL_ENABLE;
128 /* Stop the system tick timer */
129 void systick_stop(void)
131         struct lpc_system_tick* systick = LPC_SYSTICK;
132         systick->control &= ~(LPC_SYSTICK_CTRL_ENABLE);
133         systick_running = 0;
134         systick->value = 0;
136 /* Reset the system tick timer, making it count down from the reload value again
137  * Reseting the systick timer also resets the internal tick counters.
138  * If you need a value that goes beyond one start/stop cycle and accross resets,
139  *    then it's up to you to keep track of this using systick_get_tick_count() and/or
140  *    systick_get_clock_cycles().
141  */
142 void systick_reset(void)
144         struct lpc_system_tick* systick = LPC_SYSTICK;
145         systick->value = 0;
146         global_wrapping_system_ticks = 0;
147         global_wrapping_system_clock_cycles = tick_reload;
150 /* Get system tick timer current value (counts at get_main_clock() !)
151  * systick_get_timer_val returns a value between 0 and systick_get_timer_reload_val()
152  */
153 uint32_t systick_get_timer_val(void)
155         struct lpc_system_tick* systick = LPC_SYSTICK;
156         return systick->value;
158 /* Get system tick timer reload value */
159 uint32_t systick_get_timer_reload_val(void)
161         return tick_reload;
164 /* Check if systick is running (return 1) or not (return 0) */
165 uint32_t is_systick_running(void)
167         return systick_running;
170 /* Get the system tick period in ms
171  * A vaue of 0 means the system tick timer has not been configured.
172  * Note : calls to msleep() or usleep() will configure the system tick timer
173  *        with a value of 1ms if it was not configured yet.
174  */
175 uint32_t systick_get_tick_ms_period(void)
177         return tick_ms;
180 /* Get the "timer wrapped" indicator.
181  * Used in usleep() function.
182  * Note : the first to call this function will get the right information.
183  * All subsequent calls will get wrong indication.
184  * Thus this function is not exported to user space, user should compare global
185  * ticks values or tick counter values. */
186 static uint32_t systick_counted_to_zero(void)
188         struct lpc_system_tick* systick = LPC_SYSTICK;
189         return (systick->control & LPC_SYSTICK_CTRL_COUNTFLAG);
193 /* Get the number of system ticks ... since last wrapping of the counter, which
194  * is about 50 days with a 1ms system tick. */
195 uint32_t systick_get_tick_count(void)
197         return global_wrapping_system_ticks;
200 /* Get the number of clock cycles ... since last wrapping of the counter. */
201 uint32_t systick_get_clock_cycles(void)
203         struct lpc_system_tick* systick = LPC_SYSTICK;
204         /* global_wrapping_system_clock_cycles has been initialised to reload value, thus there is
205          * no need to add it here, making the call quicker */
206         return global_wrapping_system_clock_cycles - systick->value;
209 /***************************************************************************** */
210 /* Power up the system tick timer.
211  * ms is the interval between system tick timer interrupts. If set to 0, the default
212  *     value is used, which should provide a 1ms period.
213  * Note : The LPC11U3x allows use of the core clock instead of half of the core clock a clock
214  *     source. It is not used here. To activate it remove the reload division (shift) and set
215  *     systick->control to the right value (set bit 2 : LPC_SYSTICK_CTRL_CLK_MAIN)
216  */
217 void systick_timer_on(uint32_t ms)
219         struct lpc_system_tick* systick = LPC_SYSTICK;
220         uint32_t reload; /* The reload value for the counter */
222         /* Set the reload value */
223         if (ms != 0) {
224                 reload = ((get_main_clock() / 1000) * ms) - 1;
225         } else {
226                 reload = (get_main_clock() / 1000) - 1;
227                 ms = 1;
228         }
229         /* We use half the frequency of the system clock as input to the system tick clock */
230         reload = reload >> 1; /* Divide by 2 */
231         systick->reload_val = (reload & 0xffffff);
232         tick_ms = ms;
233         tick_reload = systick->reload_val;
235         /* Start counting from the reload value, writting anything would do ... */
236         systick->value = reload;
237         /* Consider we already counted one cycle, making further reading of this count easier */
238         global_wrapping_system_clock_cycles = tick_reload;
240         /* And enable counter interrupt */
241         systick->control = LPC_SYSTICK_CTRL_TICKINT;
242         systick_running = 0;
244         /* Perform this division now for the usleep function. */
245         usleep_us_count = get_main_clock() / (1000 * 1000);
246         /* We use half the frequency of the system clock as input to the system tick clock */
247         usleep_us_count = (usleep_us_count >> 1); /* Divide by two */
249         /* FIXME : document this */
250         NVIC_SetPriority(SYSTICK_IRQ, ((1 << LPC_NVIC_PRIO_BITS) - 1));
253 /* Removes the main clock from the selected timer block */
254 void systick_timer_off(void)
256         struct lpc_system_tick* systick = LPC_SYSTICK;
257         systick->control = 0;
258         systick->reload_val = 0;
259         tick_ms = 0;
260         tick_reload = 0;
261         systick_running = 0;
265 /***************************************************************************** */
266 /* Sleeping functions */
268 /* Set the sleep countdown value
269  * A sleep will end when this value reaches 0
270  * Note that calls to this function while a sleep() has been initiated will change the
271  *   sleep duration ....
272  */
273 static inline void set_sleep(uint32_t ticks)
275         sleep_count = ticks;
278 /* Actual sleep function, checks that system tick counter is configured to generate
279  * an interrupt and to move sleep_count down to 0
280  */
281 #define SYSTICK_CAN_SLEEP   (LPC_SYSTICK_CTRL_TICKINT | LPC_SYSTICK_CTRL_ENABLE)
282 static uint32_t sleep(void)
284         struct lpc_system_tick* systick = LPC_SYSTICK;
285         if ((systick->control & SYSTICK_CAN_SLEEP) != SYSTICK_CAN_SLEEP) {
286                 return -1;
287         }
288         do { } while (sleep_count != 0);
289         return 0;
292 /* This msleep sleeps less than the required amount of time as it forgets about
293  * the already elapsed time of the systick timer since last tick. */
294 void msleep(uint32_t ms)
296         uint32_t ticks = 0;
298         if (tick_ms == 0) {
299                 systick_timer_on(1);
300                 ticks = ms;
301         } else {
302                 ticks = ms / tick_ms;
303         }
304         set_sleep(ticks);
305         if (systick_running == 0) {
306                 systick_start();
307         }
308         sleep();
311 /* This usleep function tries to sleep at most the required amount of time.
312  * The setup is so long that it cannot sleep for less than 10us when running at 48MHz
313  */
314 void usleep(uint32_t us)
316         struct lpc_system_tick* systick = LPC_SYSTICK;
317         uint32_t start = systick->value; /* Grab the starting (call time) value now */
318         uint32_t count = 0;
319         uint32_t end = 0;
321         end = systick_counted_to_zero(); /* erase loop indicator */
322         if (us > 1000) {
323                 msleep(us / 1000);
324                 us = us % 1000;
325         } else {
326                 if (systick_running == 0) {
327                         if (tick_ms == 0) {
328                                 systick_timer_on(1);
329                         }
330                         systick_start();
331                 }
332         }
333         count = usleep_us_count * us;
334          /* Remember that systick is a decrementing counter */
335         if (count > start) {
336                 end = (systick->reload_val - (count - start));
337                 do { } while (systick_counted_to_zero() == 0); /* Wait for timer loop */
338                 do { } while (systick->value > end); /* Wait for remaining part of sleep duration */
339         } else {
340                 end = start - count;
341                 /* Wait for sleep duration.
342                  * If the counter looped, it means we already waited too much */
343                 do { } while ((systick->value > end) && (systick_counted_to_zero() == 0));
344         }
347 void usleep_short(uint32_t us)
349         struct lpc_system_tick* systick = LPC_SYSTICK;
350         uint32_t start = systick->value; /* Grab the starting (call time) value now */
351         uint32_t count = usleep_us_count * us;
352         uint32_t end = systick_counted_to_zero(); /* Erase loop indicator */
354          /* Remember that systick is a decrementing counter */
355         if (count > start) {
356                 end = (systick->reload_val - (count - start));
357                 do { } while (systick_counted_to_zero() == 0); /* Wait for timer loop */
358                 do { } while (systick->value > end); /* Wait for remaining part of sleep duration */
359         } else {
360                 end = start - count;
361                 /* Wait for sleep duration.
362                  * If the counter looped, it means we already waited too much */
363                 do { } while ((systick->value > end) && (systick_counted_to_zero() == 0));
364         }