Changes ported from modules, to support multiple tmp101 sensors.
[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  *************************************************************************** */
22 /***************************************************************************** */
23 /*               System Tick Timer                                             */
24 /***************************************************************************** */
26 /* Driver for the internal systick timer of the LPC176x.
27  * Refer to the LPC176x documentation (UM10360.pdf) for more information
28  */
30 #include <stdint.h>
31 #include "core/lpc_regs_17xx.h"
32 #include "core/lpc_core_cm3.h"
33 #include "core/system.h"
34 #include "core/systick.h"
36 /* FIXME : Actual use of sleep_count is OK in single tasking environment.
37  *    for multitasking, change the sleep function behaviour.
38  */
40 /* Static variables */
41 static volatile uint32_t sleep_count = 0;
42 static volatile uint32_t tick_ms = 0;
43 static volatile uint32_t systick_running = 0;
44 static volatile uint32_t tick_reload = 0;
45 static uint32_t usleep_us_count = 0;
47 /* Wraps every 50 days or so with a 1ms tick */
48 static volatile uint32_t global_wrapping_system_ticks = 0;
49 /* The systick cycles run at get_main_clock(), and would wrap more often! */
50 static volatile uint32_t global_wrapping_system_clock_cycles = 0;
53 struct systick_callback {
54         void (*callback) (uint32_t);
55         uint16_t period;
56         uint16_t countdown;
57 };
58 static volatile struct systick_callback cbs[MAX_SYSTICK_CALLBACKS] = {};
60 /* System Tick Timer Interrupt Handler */
61 void SysTick_Handler(void)
62 {
63         int i = 0;
64         global_wrapping_system_ticks++;
65         global_wrapping_system_clock_cycles += tick_reload;
66         if (sleep_count != 0) {
67                 sleep_count--;
68         }
69         for (i = 0; i < MAX_SYSTICK_CALLBACKS; i++) {
70                 if (cbs[i].callback != NULL) {
71                         cbs[i].countdown--;
72                         if (cbs[i].countdown == 0) {
73                                 cbs[i].countdown = cbs[i].period;
74                                 cbs[i].callback(global_wrapping_system_ticks);
75                         }
76                 }
77         }
78 }
81 /* Register a callback to be called every 'period' system ticks.
82  * returns the callback number if registration was OK.
83  * returns negative value on error.
84  * The callback will get the "global_wrapping_system_ticks" as argument, which wraps every 50 days
85  *   or so with a 1ms tick
86  */
87 int add_systick_callback(void (*callback) (uint32_t), uint16_t period)
88 {
89         int i = 0;
90         if (period == 0) {
91                 return -EINVAL;
92         }
93         for (i = 0; i < MAX_SYSTICK_CALLBACKS; i++) {
94                 if (cbs[i].callback == NULL) {
95                         cbs[i].callback = callback;
96                         cbs[i].period = period;
97                         cbs[i].countdown = period;
98                         return i;
99                 }
100         }
101         return -EBUSY;
104 int remove_systick_callback(void (*callback) (uint32_t))
106         int i = 0;
107         for (i = 0; i < MAX_SYSTICK_CALLBACKS; i++) {
108                 if (cbs[i].callback == callback) {
109                         cbs[i].callback = NULL;
110                         return 0;
111                 }
112         }
113         return -EINVAL;
116 /***************************************************************************** */
117 /* systick timer control function */
119 /* Start the system tick timer
120  * Starting the systick timer also resets the internal tick counters.
121  * If you need a value that goes beyond one start/stop cycle and accross resets,
122  *    then it's up to you to keep track of this using systick_get_tick_count() and/or
123  *    systick_get_clock_cycles().
124  */
125 void systick_start(void)
127         struct lpc_system_tick* systick = LPC_SYSTICK;
128         systick->value = 0;
129         systick_running = 1;
130         global_wrapping_system_ticks = 0;
131         global_wrapping_system_clock_cycles = tick_reload;
132         systick->control |= LPC_SYSTICK_CTRL_ENABLE;
134 /* Stop the system tick timer */
135 void systick_stop(void)
137         struct lpc_system_tick* systick = LPC_SYSTICK;
138         systick->control &= ~(LPC_SYSTICK_CTRL_ENABLE);
139         systick_running = 0;
140         systick->value = 0;
142 /* Reset the system tick timer, making it count down from the reload value again
143  * Reseting the systick timer also resets the internal tick counters.
144  * If you need a value that goes beyond one start/stop cycle and accross resets,
145  *    then it's up to you to keep track of this using systick_get_tick_count() and/or
146  *    systick_get_clock_cycles().
147  */
148 void systick_reset(void)
150         struct lpc_system_tick* systick = LPC_SYSTICK;
151         systick->value = 0;
152         global_wrapping_system_ticks = 0;
153         global_wrapping_system_clock_cycles = tick_reload;
156 /* Get system tick timer current value (counts at get_main_clock() !) */
157 uint32_t systick_get_timer_val(void)
159         struct lpc_system_tick* systick = LPC_SYSTICK;
160         return systick->value;
163 /* Check if systick is running (return 1) or not (return 0) */
164 uint32_t is_systick_running(void)
166         return systick_running;
169 /* Get the system tick period in ms
170  * A vaue of 0 means the system tick timer has not been configured.
171  * Note : calls to msleep() or usleep() will configure the system tick timer
172  *        with a value of 1ms if it was not configured yet.
173  */
174 uint32_t systick_get_tick_ms_period(void)
176         return tick_ms;
179 /* Get the "timer wrapped" indicator.
180  * Used in usleep() function.
181  * Note : the first to call this function will get the right information.
182  * All subsequent calls will get wrong indication.
183  * Thus this function is not exported to user space, user should compare global
184  * ticks values or tick counter values. */
185 static uint32_t systick_counted_to_zero(void)
187         struct lpc_system_tick* systick = LPC_SYSTICK;
188         return (systick->control & LPC_SYSTICK_CTRL_COUNTFLAG);
192 /* Get the number of system ticks ... since last wrapping of the counter, which
193  * is about 50 days with a 1ms system tick. */
194 uint32_t systick_get_tick_count(void)
196         return global_wrapping_system_ticks;
199 /* Get the number of clock cycles ... since last wrapping of the counter. */
200 uint32_t systick_get_clock_cycles(void)
202         struct lpc_system_tick* systick = LPC_SYSTICK;
203         /* global_wrapping_system_clock_cycles has been initialised to reload value, thus there is
204          * no need to add it here, making the call quicker */
205         return global_wrapping_system_clock_cycles - systick->value;
208 /***************************************************************************** */
209 /* Power up the system tick timer.
210  * ms is the interval between system tick timer interrupts. If set to 0, the default
211  *     value is used, which should provide a 1ms period.
212  */
213 void systick_timer_on(uint32_t ms)
215         struct lpc_system_tick* systick = LPC_SYSTICK;
216         uint32_t reload; /* The reload value for the counter */
218         /* Set the reload value */
219         if (ms != 0) {
220                 reload = ((get_main_clock() / 1000) * ms) - 1;
221         } else {
222                 reload = (get_main_clock() / 1000) - 1;
223                 ms = 1;
224         }
225         systick->reload_val = (reload & 0xffffff);
226         tick_ms = ms;
227         tick_reload = systick->reload_val;
229         /* Start counting from the reload value, writting anything would do ... */
230         systick->value = reload;
231         /* Consider we already counted one cycle, making further reading of this count easier */
232         global_wrapping_system_clock_cycles = tick_reload;
234         /* And enable counter interrupt */
235         systick->control = (LPC_SYSTICK_CTRL_TICKINT | LPC_SYSTICK_CTRL_CLKSRC_MAIN_CLK);
236         systick_running = 0;
238         /* Perform this division now for the usleep function. */
239         usleep_us_count = get_main_clock() / (1000 * 1000);
241         /* FIXME : document this */
242         NVIC_SetPriority(SYSTICK_IRQ, ((1 << LPC_NVIC_PRIO_BITS) - 1));
245 /* Removes the main clock from the selected timer block */
246 void systick_timer_off(void)
248         struct lpc_system_tick* systick = LPC_SYSTICK;
249         systick->control = 0;
250         systick->reload_val = 0;
251         tick_ms = 0;
252         tick_reload = 0;
253         systick_running = 0;
257 /***************************************************************************** */
258 /* Sleeping functions */
260 /* Set the sleep countdown value
261  * A sleep will end when this value reaches 0
262  * Note that calls to this function while a sleep() has been initiated will change the
263  *   sleep duration ....
264  */
265 void set_sleep(uint32_t ticks)
267         sleep_count = ticks;
269 /* Return current sleep count_down counter */
270 uint32_t get_sleep(void)
272         return sleep_count;
275 /* Actual sleep function, checks that system tick counter is configured to generate
276  * an interrupt to move sleep_count down to 0
277  */
278 #define SYSTICK_CAN_SLEEP   (LPC_SYSTICK_CTRL_TICKINT | LPC_SYSTICK_CTRL_ENABLE)
279 static uint32_t sleep(void)
281         struct lpc_system_tick* systick = LPC_SYSTICK;
282         if ((systick->control & SYSTICK_CAN_SLEEP) != SYSTICK_CAN_SLEEP) {
283                 return -1;
284         }
285         do { } while (sleep_count != 0);
286         return 0;
289 /* This msleep sleeps less than the required amount of time as it forgets about
290  * the already elapsed time of the systick timer since last tick. */
291 void msleep(uint32_t ms)
293         uint32_t ticks = 0;
295         if (tick_ms == 0) {
296                 systick_timer_on(1);
297                 ticks = ms;
298         } else {
299                 ticks = ms / tick_ms;
300         }
301         set_sleep(ticks);
302         if (systick_running == 0) {
303                 systick_start();
304         }
305         sleep();
308 /* This usleep function tries to sleep at most the required amount of time.
309  * The setup is so long that it cannot sleep for less than 10us when running at 48MHz
310  */
311 void usleep(uint32_t us)
313         struct lpc_system_tick* systick = LPC_SYSTICK;
314         uint32_t start = systick->value; /* Grab the starting (call time) value now */
315         uint32_t count = 0;
316         uint32_t end = 0;
318         end = systick_counted_to_zero(); /* erase loop indicator */
319         if (us > 1000) {
320                 msleep(us / 1000);
321                 us = us % 1000;
322         } else {
323                 if (systick_running == 0) {
324                         if (tick_ms == 0) {
325                                 systick_timer_on(1);
326                         }
327                         systick_start();
328                 }
329         }
330         count = usleep_us_count * us;
331          /* Remember that systick is a decrementing counter */
332         if (count > start) {
333                 end = (systick->reload_val - (count - start));
334                 do { } while (systick_counted_to_zero() == 0); /* Wait for timer loop */
335                 do { } while (systick->value > end); /* Wait for remaining part of sleep duration */
336         } else {
337                 end = start - count;
338                 /* Wait for sleep duration.
339                  * If the counter looped, it means we already waited too much */
340                 do { } while ((systick->value > end) && (systick_counted_to_zero() == 0));
341         }
344 void usleep_short(uint32_t us)
346         struct lpc_system_tick* systick = LPC_SYSTICK;
347         uint32_t start = systick->value; /* Grab the starting (call time) value now */
348         uint32_t count = usleep_us_count * us;
349         uint32_t end = systick_counted_to_zero(); /* Erase loop indicator */
351          /* Remember that systick is a decrementing counter */
352         if (count > start) {
353                 end = (systick->reload_val - (count - start));
354                 do { } while (systick_counted_to_zero() == 0); /* Wait for timer loop */
355                 do { } while (systick->value > end); /* Wait for remaining part of sleep duration */
356         } else {
357                 end = start - count;
358                 /* Wait for sleep duration.
359                  * If the counter looped, it means we already waited too much */
360                 do { } while ((systick->value > end) && (systick_counted_to_zero() == 0));
361         }