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