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