1 /*
2 * Copyright (c) 2020 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <zephyr/init.h>
7 #include <zephyr/drivers/timer/system_timer.h>
8 #include <zephyr/sys_clock.h>
9 #include <zephyr/spinlock.h>
10 #include <zephyr/drivers/interrupt_controller/dw_ace.h>
11
12 #include <cavs-idc.h>
13 #include <adsp_shim.h>
14 #include <adsp_interrupt.h>
15 #include <zephyr/irq.h>
16
17 #define DT_DRV_COMPAT intel_adsp_timer
18
19 /**
20 * @file
21 * @brief Intel Audio DSP Wall Clock Timer driver
22 *
23 * The Audio DSP on Intel SoC has a timer with one counter and two compare
24 * registers that is external to the CPUs. This timer is accessible from
25 * all available CPU cores and provides a synchronized timer under SMP.
26 */
27
28 #define COMPARATOR_IDX 0 /* 0 or 1 */
29
30 #ifdef CONFIG_SOC_SERIES_INTEL_ADSP_ACE
31 #define TIMER_IRQ ACE_IRQ_TO_ZEPHYR(ACE_INTL_TTS)
32 #else
33 #define TIMER_IRQ DSP_WCT_IRQ(COMPARATOR_IDX)
34 #endif
35
36 #define CYC_PER_TICK (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC \
37 / CONFIG_SYS_CLOCK_TICKS_PER_SEC)
38 #define MAX_CYC 0xFFFFFFFFUL
39 #define MAX_TICKS ((MAX_CYC - CYC_PER_TICK) / CYC_PER_TICK)
40 #define MIN_DELAY (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / 100000)
41
42 BUILD_ASSERT(MIN_DELAY < CYC_PER_TICK);
43 BUILD_ASSERT(COMPARATOR_IDX >= 0 && COMPARATOR_IDX <= 1);
44
45 #define DSP_WCT_CS_TT(x) BIT(4 + x)
46
47 static struct k_spinlock lock;
48 static uint64_t last_count;
49
50 /* Not using current syscon driver due to overhead due to MMU support */
51 #define SYSCON_REG_ADDR DT_REG_ADDR(DT_INST_PHANDLE(0, syscon))
52
53 #define DSPWCTCS_ADDR (SYSCON_REG_ADDR + ADSP_DSPWCTCS_OFFSET)
54 #define DSPWCT0C_LO_ADDR (SYSCON_REG_ADDR + ADSP_DSPWCT0C_OFFSET)
55 #define DSPWCT0C_HI_ADDR (SYSCON_REG_ADDR + ADSP_DSPWCT0C_OFFSET + 4)
56 #define DSPWC_LO_ADDR (SYSCON_REG_ADDR + ADSP_DSPWC_OFFSET)
57 #define DSPWC_HI_ADDR (SYSCON_REG_ADDR + ADSP_DSPWC_OFFSET + 4)
58
59 #if defined(CONFIG_TEST)
60 const int32_t z_sys_timer_irq_for_test = TIMER_IRQ; /* See tests/kernel/context */
61 #endif
62
set_compare(uint64_t time)63 static void set_compare(uint64_t time)
64 {
65 /* Disarm the comparator to prevent spurious triggers */
66 sys_write32(sys_read32(DSPWCTCS_ADDR) & (~DSP_WCT_CS_TA(COMPARATOR_IDX)),
67 SYSCON_REG_ADDR + ADSP_DSPWCTCS_OFFSET);
68
69 sys_write32((uint32_t)time, DSPWCT0C_LO_ADDR);
70 sys_write32((uint32_t)(time >> 32), DSPWCT0C_HI_ADDR);
71
72 /* Arm the timer */
73 sys_write32(sys_read32(DSPWCTCS_ADDR) | (DSP_WCT_CS_TA(COMPARATOR_IDX)),
74 DSPWCTCS_ADDR);
75 }
76
count(void)77 static uint64_t count(void)
78 {
79 /* The count register is 64 bits, but we're a 32 bit CPU that
80 * can only read four bytes at a time, so a bit of care is
81 * needed to prevent racing against a wraparound of the low
82 * word. Wrap the low read between two reads of the high word
83 * and make sure it didn't change.
84 */
85 uint32_t hi0, hi1, lo;
86
87 do {
88 hi0 = sys_read32(DSPWC_HI_ADDR);
89 lo = sys_read32(DSPWC_LO_ADDR);
90 hi1 = sys_read32(DSPWC_HI_ADDR);
91 } while (hi0 != hi1);
92 return (((uint64_t)hi0) << 32) | lo;
93 }
94
count32(void)95 static uint32_t count32(void)
96 {
97 uint32_t counter_lo;
98
99 counter_lo = sys_read32(DSPWC_LO_ADDR);
100 return counter_lo;
101 }
102
compare_isr(const void * arg)103 static void compare_isr(const void *arg)
104 {
105 ARG_UNUSED(arg);
106 uint64_t curr;
107 uint64_t dticks;
108
109 k_spinlock_key_t key = k_spin_lock(&lock);
110
111 curr = count();
112 dticks = (curr - last_count) / CYC_PER_TICK;
113
114 /* Clear the triggered bit */
115 sys_write32(sys_read32(DSPWCTCS_ADDR) | DSP_WCT_CS_TT(COMPARATOR_IDX),
116 DSPWCTCS_ADDR);
117
118 last_count += dticks * CYC_PER_TICK;
119
120 #ifndef CONFIG_TICKLESS_KERNEL
121 uint64_t next = last_count + CYC_PER_TICK;
122
123 if ((int64_t)(next - curr) < MIN_DELAY) {
124 next += CYC_PER_TICK;
125 }
126 set_compare(next);
127 #endif
128
129 k_spin_unlock(&lock, key);
130
131 sys_clock_announce((int32_t)dticks);
132 }
133
sys_clock_set_timeout(int32_t ticks,bool idle)134 void sys_clock_set_timeout(int32_t ticks, bool idle)
135 {
136 ARG_UNUSED(idle);
137
138 #ifdef CONFIG_TICKLESS_KERNEL
139 ticks = ticks == K_TICKS_FOREVER ? MAX_TICKS : ticks;
140 ticks = CLAMP(ticks - 1, 0, (int32_t)MAX_TICKS);
141
142 k_spinlock_key_t key = k_spin_lock(&lock);
143 uint64_t curr = count();
144 uint64_t next;
145 uint32_t adj, cyc = ticks * CYC_PER_TICK;
146
147 /* Round up to next tick boundary */
148 adj = (uint32_t)(curr - last_count) + (CYC_PER_TICK - 1);
149 if (cyc <= MAX_CYC - adj) {
150 cyc += adj;
151 } else {
152 cyc = MAX_CYC;
153 }
154 cyc = (cyc / CYC_PER_TICK) * CYC_PER_TICK;
155 next = last_count + cyc;
156
157 if (((uint32_t)next - (uint32_t)curr) < MIN_DELAY) {
158 next += CYC_PER_TICK;
159 }
160
161 set_compare(next);
162 k_spin_unlock(&lock, key);
163 #endif
164 }
165
sys_clock_elapsed(void)166 uint32_t sys_clock_elapsed(void)
167 {
168 if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
169 return 0;
170 }
171 k_spinlock_key_t key = k_spin_lock(&lock);
172 uint64_t ret = (count() - last_count) / CYC_PER_TICK;
173
174 k_spin_unlock(&lock, key);
175 return (uint32_t)ret;
176 }
177
sys_clock_cycle_get_32(void)178 uint32_t sys_clock_cycle_get_32(void)
179 {
180 return count32();
181 }
182
sys_clock_cycle_get_64(void)183 uint64_t sys_clock_cycle_get_64(void)
184 {
185 return count();
186 }
187
188 /* Interrupt setup is partially-cpu-local state, so needs to be
189 * repeated for each core when it starts. Note that this conforms to
190 * the Zephyr convention of sending timer interrupts to all cpus (for
191 * the benefit of timeslicing).
192 */
irq_init(void)193 static void irq_init(void)
194 {
195 int cpu = arch_curr_cpu()->id;
196
197 /* These platforms have an extra layer of interrupt masking
198 * (for per-core control) above the interrupt controller.
199 * Drivers need to do that part.
200 */
201 #ifdef CONFIG_SOC_SERIES_INTEL_ADSP_ACE
202 ACE_DINT[cpu].ie[ACE_INTL_TTS] |= BIT(COMPARATOR_IDX + 1);
203 sys_write32(sys_read32(DSPWCTCS_ADDR) | ADSP_SHIM_DSPWCTCS_TTIE(COMPARATOR_IDX),
204 DSPWCTCS_ADDR);
205 #else
206 CAVS_INTCTRL[cpu].l2.clear = CAVS_L2_DWCT0;
207 #endif
208 irq_enable(TIMER_IRQ);
209 }
210
smp_timer_init(void)211 void smp_timer_init(void)
212 {
213 }
214
sys_clock_driver_init(void)215 static int sys_clock_driver_init(void)
216 {
217 uint64_t curr = count();
218
219 IRQ_CONNECT(TIMER_IRQ, 0, compare_isr, 0, 0);
220 set_compare(curr + CYC_PER_TICK);
221 last_count = curr;
222 irq_init();
223 return 0;
224 }
225
226 /* Runs on core 0 only */
intel_adsp_clock_soft_off_exit(void)227 void intel_adsp_clock_soft_off_exit(void)
228 {
229 (void)sys_clock_driver_init();
230 }
231
232 SYS_INIT(sys_clock_driver_init, PRE_KERNEL_2,
233 CONFIG_SYSTEM_CLOCK_INIT_PRIORITY);
234