1 /*
2  * Copyright (c) 2012-2014 Travis Geiselbrecht
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files
6  * (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 /*
25  * Generic systick timer support for providing system time (current_time(), current_time_hires()),
26  * and a monotonic timer for the kernel.
27  */
28 
29 #include <sys/types.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <debug.h>
33 #include <assert.h>
34 #include <trace.h>
35 #include <err.h>
36 #include <kernel/thread.h>
37 #include <arch/arm.h>
38 #include <arch/arm/cm.h>
39 #include <platform.h>
40 #include <platform/timer.h>
41 
42 #define LOCAL_TRACE 0
43 
44 static volatile uint64_t ticks;
45 static uint32_t tick_rate = 0;
46 static uint32_t tick_rate_mhz = 0;
47 static lk_time_t tick_interval_ms;
48 static lk_bigtime_t tick_interval_us;
49 
50 static platform_timer_callback cb;
51 static void *cb_args;
52 
arm_cm_systick_set_periodic(lk_time_t period)53 static void arm_cm_systick_set_periodic(lk_time_t period)
54 {
55     LTRACEF("clk_freq %u, period %u\n", tick_rate, (uint)period);
56 
57     uint32_t ticks = tick_rate / (1000 / period);
58     LTRACEF("ticks %d\n", ticks);
59 
60     SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;
61     SysTick->VAL = 0;
62     SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk;
63 }
64 
arm_cm_systick_cancel_periodic(void)65 static void arm_cm_systick_cancel_periodic(void)
66 {
67     SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
68 }
69 
70 /* main systick irq handler */
_systick(void)71 void _systick(void)
72 {
73     ticks++;
74 
75     arm_cm_irq_entry();
76 
77     bool resched = false;
78     if (cb) {
79         lk_time_t now = current_time();
80         if (cb(cb_args, now) == INT_RESCHEDULE)
81             resched = true;
82     }
83 
84     arm_cm_irq_exit(resched);
85 }
86 
platform_set_periodic_timer(platform_timer_callback callback,void * arg,lk_time_t interval)87 status_t platform_set_periodic_timer(platform_timer_callback callback, void *arg, lk_time_t interval)
88 {
89     LTRACEF("callback %p, arg %p, interval %u\n", callback, arg, interval);
90 
91     DEBUG_ASSERT(tick_rate != 0 && tick_rate_mhz != 0);
92 
93     cb = callback;
94     cb_args = arg;
95 
96     tick_interval_ms = interval;
97     tick_interval_us = interval * 1000;
98     arm_cm_systick_set_periodic(interval);
99 
100     return NO_ERROR;
101 }
102 
current_time(void)103 lk_time_t current_time(void)
104 {
105     uint32_t reload = SysTick->LOAD  & SysTick_LOAD_RELOAD_Msk;
106 
107     uint64_t t;
108     uint32_t delta;
109     do {
110         t = ticks;
111         delta = (volatile uint32_t)SysTick->VAL;
112         DMB;
113     } while (ticks != t);
114 
115     /* convert ticks to msec */
116     delta = (reload - delta) / (tick_rate_mhz * 1000);
117     lk_time_t res = (t * tick_interval_ms) + delta;
118 
119     return res;
120 }
121 
current_time_hires(void)122 lk_bigtime_t current_time_hires(void)
123 {
124     uint32_t reload = SysTick->LOAD  & SysTick_LOAD_RELOAD_Msk;
125 
126     uint64_t t;
127     uint32_t delta;
128     do {
129         t = ticks;
130         delta = (volatile uint32_t)SysTick->VAL;
131         DMB;
132     } while (ticks != t);
133 
134     /* convert ticks to usec */
135     delta = (reload - delta) / tick_rate_mhz;
136     lk_bigtime_t res = (t * tick_interval_us) + delta;
137 
138     return res;
139 }
140 
arm_cm_systick_init(uint32_t mhz)141 void arm_cm_systick_init(uint32_t mhz)
142 {
143     tick_rate = mhz;
144     tick_rate_mhz = mhz / 1000000;
145 }
146