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