1 /*
2 * Copyright (c) 2020 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <drivers/timer/system_timer.h>
8 #include <sys_clock.h>
9 #include <spinlock.h>
10
11 /**
12 * @file
13 * @brief CAVS DSP Wall Clock Timer driver
14 *
15 * The CAVS DSP on Intel SoC has a timer with one counter and two compare
16 * registers that is external to the CPUs. This timer is accessible from
17 * all available CPU cores and provides a synchronized timer under SMP.
18 */
19
20 #define TIMER 0
21 #define TIMER_IRQ DSP_WCT_IRQ(TIMER)
22 #define CYC_PER_TICK (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC \
23 / CONFIG_SYS_CLOCK_TICKS_PER_SEC)
24 #define MAX_CYC 0xFFFFFFFFUL
25 #define MAX_TICKS ((MAX_CYC - CYC_PER_TICK) / CYC_PER_TICK)
26 #define MIN_DELAY (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / 100000)
27
28 BUILD_ASSERT(MIN_DELAY < CYC_PER_TICK);
29
30 static struct k_spinlock lock;
31 static uint64_t last_count;
32
33 static volatile struct soc_dsp_shim_regs *shim_regs =
34 (volatile struct soc_dsp_shim_regs *)SOC_DSP_SHIM_REG_BASE;
35
set_compare(uint64_t time)36 static void set_compare(uint64_t time)
37 {
38 /* Disarm the comparator to prevent spurious triggers */
39 shim_regs->dspwctcs &= ~DSP_WCT_CS_TA(TIMER);
40
41 #if (TIMER == 0)
42 /* Set compare register */
43 shim_regs->dspwct0c = time;
44 #elif (TIMER == 1)
45 /* Set compare register */
46 shim_regs->dspwct1c = time;
47 #else
48 #error "TIMER has to be 0 or 1!"
49 #endif
50
51 /* Arm the timer */
52 shim_regs->dspwctcs |= DSP_WCT_CS_TA(TIMER);
53 }
54
count(void)55 static uint64_t count(void)
56 {
57 /* The count register is 64 bits, but we're a 32 bit CPU that
58 * can only read four bytes at a time, so a bit of care is
59 * needed to prevent racing against a wraparound of the low
60 * word. Wrap the low read between two reads of the high word
61 * and make sure it didn't change.
62 */
63 volatile uint32_t *wc = (void *)&shim_regs->walclk;
64 uint32_t hi0, hi1, lo;
65
66 do {
67 hi0 = wc[1];
68 lo = wc[0];
69 hi1 = wc[1];
70 } while (hi0 != hi1);
71
72 return (((uint64_t)hi0) << 32) | lo;
73 }
74
count32(void)75 static uint32_t count32(void)
76 {
77 return shim_regs->walclk32_lo;
78 }
79
compare_isr(const void * arg)80 static void compare_isr(const void *arg)
81 {
82 ARG_UNUSED(arg);
83 uint64_t curr;
84 uint32_t dticks;
85
86 k_spinlock_key_t key = k_spin_lock(&lock);
87
88 curr = count();
89
90 #ifdef CONFIG_SMP
91 /* If we are too soon since last_count,
92 * this interrupt is likely the same interrupt
93 * event but being processed by another CPU.
94 * Since it has already been processed and
95 * ticks announced, skip it.
96 */
97 if ((count32() - (uint32_t)last_count) < MIN_DELAY) {
98 k_spin_unlock(&lock, key);
99 return;
100 }
101 #endif
102
103 dticks = (uint32_t)((curr - last_count) / CYC_PER_TICK);
104
105 /* Clear the triggered bit */
106 shim_regs->dspwctcs |= DSP_WCT_CS_TT(TIMER);
107
108 last_count += dticks * CYC_PER_TICK;
109
110 #ifndef CONFIG_TICKLESS_KERNEL
111 uint64_t next = last_count + CYC_PER_TICK;
112
113 if ((int64_t)(next - curr) < MIN_DELAY) {
114 next += CYC_PER_TICK;
115 }
116 set_compare(next);
117 #endif
118
119 k_spin_unlock(&lock, key);
120
121 sys_clock_announce(dticks);
122 }
123
124 /* Runs on core 0 only */
sys_clock_driver_init(const struct device * dev)125 int sys_clock_driver_init(const struct device *dev)
126 {
127 uint64_t curr = count();
128
129 IRQ_CONNECT(TIMER_IRQ, 0, compare_isr, 0, 0);
130 set_compare(curr + CYC_PER_TICK);
131 last_count = curr;
132 irq_enable(TIMER_IRQ);
133 return 0;
134 }
135
sys_clock_set_timeout(int32_t ticks,bool idle)136 void sys_clock_set_timeout(int32_t ticks, bool idle)
137 {
138 ARG_UNUSED(idle);
139
140 #ifdef CONFIG_TICKLESS_KERNEL
141 ticks = ticks == K_TICKS_FOREVER ? MAX_TICKS : ticks;
142 ticks = CLAMP(ticks - 1, 0, (int32_t)MAX_TICKS);
143
144 k_spinlock_key_t key = k_spin_lock(&lock);
145 uint64_t curr = count();
146 uint64_t next;
147 uint32_t adj, cyc = ticks * CYC_PER_TICK;
148
149 /* Round up to next tick boundary */
150 adj = (uint32_t)(curr - last_count) + (CYC_PER_TICK - 1);
151 if (cyc <= MAX_CYC - adj) {
152 cyc += adj;
153 } else {
154 cyc = MAX_CYC;
155 }
156 cyc = (cyc / CYC_PER_TICK) * CYC_PER_TICK;
157 next = last_count + cyc;
158
159 if (((uint32_t)next - (uint32_t)curr) < MIN_DELAY) {
160 next += CYC_PER_TICK;
161 }
162
163 set_compare(next);
164 k_spin_unlock(&lock, key);
165 #endif
166 }
167
sys_clock_elapsed(void)168 uint32_t sys_clock_elapsed(void)
169 {
170 if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
171 return 0;
172 }
173 k_spinlock_key_t key = k_spin_lock(&lock);
174 uint32_t ret = (count32() - (uint32_t)last_count) / CYC_PER_TICK;
175
176 k_spin_unlock(&lock, key);
177 return ret;
178 }
179
sys_clock_cycle_get_32(void)180 uint32_t sys_clock_cycle_get_32(void)
181 {
182 return count32();
183 }
184
185 /* Runs on secondary cores */
smp_timer_init(void)186 void smp_timer_init(void)
187 {
188 /* This enables the Timer 0 (or 1) interrupt for CPU n.
189 *
190 * FIXME: Done in this way because we don't have an API
191 * to enable interrupts per CPU.
192 */
193 sys_set_bit(DT_REG_ADDR(DT_NODELABEL(cavs0))
194 + CAVS_ICTL_INT_CPU_OFFSET(arch_curr_cpu()->id)
195 + 0x04,
196 22 + TIMER);
197 irq_enable(TIMER_IRQ);
198 }
199