1 /*
2 * Copyright (c) 2012-2014 Travis Geiselbrecht
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8
9 /*
10 * Generic systick timer support for providing system time (current_time(), current_time_hires()),
11 * and a monotonic timer for the kernel.
12 */
13
14 #include <sys/types.h>
15 #include <string.h>
16 #include <stdlib.h>
17 #include <lk/debug.h>
18 #include <assert.h>
19 #include <lk/trace.h>
20 #include <lk/err.h>
21 #include <kernel/thread.h>
22 #include <arch/arm.h>
23 #include <arch/arm/cm.h>
24 #include <platform.h>
25 #include <platform/timer.h>
26
27 #define LOCAL_TRACE 0
28
29 static volatile uint64_t current_ticks;
30 static uint32_t tick_rate = 0;
31 static uint32_t tick_rate_mhz = 0;
32 static lk_time_t tick_interval_ms;
33 static lk_bigtime_t tick_interval_us;
34
35 static platform_timer_callback cb;
36 static void *cb_args;
37
arm_cm_systick_set_periodic(lk_time_t period)38 static void arm_cm_systick_set_periodic(lk_time_t period) {
39 LTRACEF("clk_freq %u, period %u\n", tick_rate, (uint)period);
40
41 uint32_t ticks = tick_rate / (1000 / period);
42 LTRACEF("ticks %d\n", ticks);
43
44 SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;
45 SysTick->VAL = 0;
46 SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk;
47 }
48
arm_cm_systick_cancel_periodic(void)49 static void arm_cm_systick_cancel_periodic(void) {
50 SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
51 }
52
53 /* main systick irq handler */
_systick(void)54 void _systick(void) {
55 current_ticks++;
56
57 arm_cm_irq_entry();
58
59 bool resched = false;
60 if (cb) {
61 lk_time_t now = current_time();
62 if (cb(cb_args, now) == INT_RESCHEDULE)
63 resched = true;
64 }
65
66 arm_cm_irq_exit(resched);
67 }
68
platform_set_periodic_timer(platform_timer_callback callback,void * arg,lk_time_t interval)69 status_t platform_set_periodic_timer(platform_timer_callback callback, void *arg, lk_time_t interval) {
70 LTRACEF("callback %p, arg %p, interval %u\n", callback, arg, interval);
71
72 DEBUG_ASSERT(tick_rate != 0 && tick_rate_mhz != 0);
73
74 cb = callback;
75 cb_args = arg;
76
77 tick_interval_ms = interval;
78 tick_interval_us = interval * 1000;
79 arm_cm_systick_set_periodic(interval);
80
81 return NO_ERROR;
82 }
83
current_time(void)84 lk_time_t current_time(void) {
85 uint32_t reload = SysTick->LOAD & SysTick_LOAD_RELOAD_Msk;
86
87 uint64_t t;
88 uint32_t delta;
89 do {
90 t = current_ticks;
91 delta = (volatile uint32_t)SysTick->VAL;
92 DMB;
93 } while (current_ticks != t);
94
95 /* convert ticks to msec */
96 delta = (reload - delta) / (tick_rate_mhz * 1000);
97 lk_time_t res = (t * tick_interval_ms) + delta;
98
99 return res;
100 }
101
current_time_hires(void)102 lk_bigtime_t current_time_hires(void) {
103 uint32_t reload = SysTick->LOAD & SysTick_LOAD_RELOAD_Msk;
104
105 uint64_t t;
106 uint32_t delta;
107 do {
108 t = current_ticks;
109 delta = (volatile uint32_t)SysTick->VAL;
110 DMB;
111 } while (current_ticks != t);
112
113 /* convert ticks to usec */
114 delta = (reload - delta) / tick_rate_mhz;
115 lk_bigtime_t res = (t * tick_interval_us) + delta;
116
117 return res;
118 }
119
arm_cm_systick_init(uint32_t mhz)120 void arm_cm_systick_init(uint32_t mhz) {
121 tick_rate = mhz;
122 tick_rate_mhz = mhz / 1000000;
123 }
124