1 /*
2  * Copyright (c) 2018-2021 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT intel_hpet
8 #include <drivers/timer/system_timer.h>
9 #include <sys_clock.h>
10 #include <spinlock.h>
11 #include <irq.h>
12 #include <linker/sections.h>
13 
14 #include <dt-bindings/interrupt-controller/intel-ioapic.h>
15 
16 #include <soc.h>
17 
18 /**
19  * @file
20  * @brief HPET (High Precision Event Timers) driver
21  *
22  * HPET hardware contains a number of timers which can be used by
23  * the operating system, where the number of timers is implementation
24  * specific. The timers are implemented as a single up-counter with
25  * a set of comparators where the counter increases monotonically.
26  * Each timer has a match register and a comparator, and can generate
27  * an interrupt when the value in the match register equals the value of
28  * the free running counter. Some of these timers can be enabled to
29  * generate periodic interrupt.
30  *
31  * The HPET registers are usually mapped to memory space on x86
32  * hardware. If this is not the case, custom register access functions
33  * can be used by defining macro HPET_USE_CUSTOM_REG_ACCESS_FUNCS in
34  * soc.h, and implementing necessary initialization and access
35  * functions as described below.
36  *
37  * HPET_COUNTER_CLK_PERIOD can be overridden in soc.h if
38  * COUNTER_CLK_PERIOD is not in femtoseconds (1e-15 sec).
39  *
40  * HPET_CMP_MIN_DELAY can be overridden in soc.h to better match
41  * the frequency of the timers. Default is 1000 where the value
42  * written to the comparator must be 1000 larger than the current
43  * main counter value.
44  */
45 
46 /* General Configuration register */
47 #define GCONF_ENABLE			BIT(0)
48 #define GCONF_LR			BIT(1) /* legacy interrupt routing, */
49 					       /* disables PIT              */
50 
51 /* General Interrupt Status register */
52 #define TIMER0_INT_STS			BIT(0)
53 
54 /* Timer Configuration and Capabilities register */
55 #define TIMER_CONF_INT_LEVEL		BIT(1)
56 #define TIMER_CONF_INT_ENABLE		BIT(2)
57 #define TIMER_CONF_PERIODIC		BIT(3)
58 #define TIMER_CONF_VAL_SET		BIT(6)
59 #define TIMER_CONF_MODE32		BIT(8)
60 #define TIMER_CONF_FSB_EN		BIT(14) /* FSB interrupt delivery   */
61 						/* enable                   */
62 
63 DEVICE_MMIO_TOPLEVEL_STATIC(hpet_regs, DT_DRV_INST(0));
64 
65 #define HPET_REG_ADDR(off)			\
66 	((mm_reg_t)(DEVICE_MMIO_TOPLEVEL_GET(hpet_regs) + (off)))
67 
68 /* High dword of General Capabilities and ID register */
69 #define CLK_PERIOD_REG			HPET_REG_ADDR(0x04)
70 
71 /* General Configuration register */
72 #define GCONF_REG			HPET_REG_ADDR(0x10)
73 
74 /* General Interrupt Status register */
75 #define INTR_STATUS_REG			HPET_REG_ADDR(0x20)
76 
77 /* Main Counter Register */
78 #define MAIN_COUNTER_LOW_REG		HPET_REG_ADDR(0xf0)
79 #define MAIN_COUNTER_HIGH_REG		HPET_REG_ADDR(0xf4)
80 
81 /* Timer 0 Configuration and Capabilities register */
82 #define TIMER0_CONF_REG			HPET_REG_ADDR(0x100)
83 
84 /* Timer 0 Comparator Register */
85 #define TIMER0_COMPARATOR_LOW_REG	HPET_REG_ADDR(0x108)
86 #define TIMER0_COMPARATOR_HIGH_REG	HPET_REG_ADDR(0x10c)
87 
88 /**
89  * @brief Return the value of the main counter.
90  *
91  * @return Value of Main Counter
92  */
hpet_counter_get(void)93 static inline uint64_t hpet_counter_get(void)
94 {
95 	uint32_t high;
96 	uint32_t low;
97 
98 	do {
99 		high = sys_read32(MAIN_COUNTER_HIGH_REG);
100 		low = sys_read32(MAIN_COUNTER_LOW_REG);
101 	} while (high != sys_read32(MAIN_COUNTER_HIGH_REG));
102 
103 	return ((uint64_t)high << 32) | low;
104 }
105 
106 /**
107  * @brief Get COUNTER_CLK_PERIOD
108  *
109  * Read and return the COUNTER_CLK_PERIOD, which is the high
110  * 32-bit of the General Capabilities and ID Register. This can
111  * be used to calculate the frequency of the main counter.
112  *
113  * Usually the period is in femtoseconds. If this is not
114  * the case, define HPET_COUNTER_CLK_PERIOD in soc.h so
115  * it can be used to calculate frequency.
116  *
117  * @return COUNTER_CLK_PERIOD
118  */
hpet_counter_clk_period_get(void)119 static inline uint32_t hpet_counter_clk_period_get(void)
120 {
121 	return sys_read32(CLK_PERIOD_REG);
122 }
123 
124 /**
125  * @brief Return the value of the General Configuration Register
126  *
127  * @return Value of the General Configuration Register
128  */
hpet_gconf_get(void)129 static inline uint32_t hpet_gconf_get(void)
130 {
131 	return sys_read32(GCONF_REG);
132 }
133 
134 /**
135  * @brief Write to General Configuration Register
136  *
137  * @param val Value to be written to the register
138  */
hpet_gconf_set(uint32_t val)139 static inline void hpet_gconf_set(uint32_t val)
140 {
141 	sys_write32(val, GCONF_REG);
142 }
143 
144 /**
145  * @brief Write to General Interrupt Status Register
146  *
147  * This is used to acknowledge and clear interrupt bits.
148  *
149  * @param val Value to be written to the register
150  */
hpet_int_sts_set(uint32_t val)151 static inline void hpet_int_sts_set(uint32_t val)
152 {
153 	sys_write32(val, INTR_STATUS_REG);
154 }
155 
156 /**
157  * @brief Return the value of the Timer Configuration Register
158  *
159  * This reads and returns the value of the Timer Configuration
160  * Register of Timer #0.
161  *
162  * @return Value of the Timer Configuration Register
163  */
hpet_timer_conf_get(void)164 static inline uint32_t hpet_timer_conf_get(void)
165 {
166 	return sys_read32(TIMER0_CONF_REG);
167 }
168 
169 /**
170  * @brief Write to the Timer Configuration Register
171  *
172  * This writes the specified value to the Timer Configuration
173  * Register of Timer #0.
174  *
175  * @param val Value to be written to the register
176  */
hpet_timer_conf_set(uint32_t val)177 static inline void hpet_timer_conf_set(uint32_t val)
178 {
179 	sys_write32(val, TIMER0_CONF_REG);
180 }
181 
182 /*
183  * The following register access functions should work on generic x86
184  * hardware. If the targeted SoC requires special handling of HPET
185  * registers, these functions will need to be implemented in the SoC
186  * layer by first defining the macro HPET_USE_CUSTOM_REG_ACCESS_FUNCS
187  * in soc.h to signal such intent.
188  *
189  * This is a list of functions which must be implemented in the SoC
190  * layer:
191  *   void hpet_timer_comparator_set(uint32_t val)
192  */
193 #ifndef HPET_USE_CUSTOM_REG_ACCESS_FUNCS
194 
195 /**
196  * @brief Write to the Timer Comparator Value Register
197  *
198  * This writes the specified value to the Timer Comparator
199  * Value Register of Timer #0.
200  *
201  * @param val Value to be written to the register
202  */
hpet_timer_comparator_set(uint64_t val)203 static inline void hpet_timer_comparator_set(uint64_t val)
204 {
205 #if CONFIG_X86_64
206 	sys_write64(val, TIMER0_COMPARATOR_LOW_REG);
207 #else
208 	sys_write32((uint32_t)val, TIMER0_COMPARATOR_LOW_REG);
209 	sys_write32((uint32_t)(val >> 32), TIMER0_COMPARATOR_HIGH_REG);
210 #endif
211 }
212 #endif /* HPET_USE_CUSTOM_REG_ACCESS_FUNCS */
213 
214 #ifndef HPET_COUNTER_CLK_PERIOD
215 /* COUNTER_CLK_PERIOD (CLK_PERIOD_REG) is in femtoseconds (1e-15 sec) */
216 #define HPET_COUNTER_CLK_PERIOD		(1000000000000000ULL)
217 #endif
218 
219 #ifndef HPET_CMP_MIN_DELAY
220 /* Minimal delay for comparator before the next timer event */
221 #define HPET_CMP_MIN_DELAY		(1000)
222 #endif
223 
224 /*
225  * HPET_INT_LEVEL_TRIGGER is used to set HPET interrupt as level trigger
226  * for ARM CPU with NVIC like EHL PSE, whose DTS interrupt setting
227  * has no "sense" cell.
228  */
229 #if (DT_INST_IRQ_HAS_CELL(0, sense))
230 #ifdef HPET_INT_LEVEL_TRIGGER
231 __WARN("HPET_INT_LEVEL_TRIGGER has no effect, DTS setting is used instead")
232 #undef HPET_INT_LEVEL_TRIGGER
233 #endif
234 #if ((DT_INST_IRQ(0, sense) & IRQ_TYPE_LEVEL) == IRQ_TYPE_LEVEL)
235 #define HPET_INT_LEVEL_TRIGGER
236 #endif
237 #endif /* (DT_INST_IRQ_HAS_CELL(0, sense)) */
238 
239 static __pinned_bss struct k_spinlock lock;
240 static __pinned_bss uint64_t last_count;
241 
242 #ifdef CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME
243 static __pinned_bss unsigned int cyc_per_tick;
244 #else
245 #define cyc_per_tick			\
246 	(CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / CONFIG_SYS_CLOCK_TICKS_PER_SEC)
247 #endif /* CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME */
248 
249 #define HPET_MAX_TICKS ((int32_t)0x7fffffff)
250 
251 __isr
hpet_isr(const void * arg)252 static void hpet_isr(const void *arg)
253 {
254 	ARG_UNUSED(arg);
255 
256 	k_spinlock_key_t key = k_spin_lock(&lock);
257 
258 	uint64_t now = hpet_counter_get();
259 
260 #ifdef HPET_INT_LEVEL_TRIGGER
261 	/*
262 	 * Clear interrupt only if level trigger is selected.
263 	 * When edge trigger is selected, spec says only 0 can
264 	 * be written.
265 	 */
266 	hpet_int_sts_set(TIMER0_INT_STS);
267 #endif
268 
269 	if (IS_ENABLED(CONFIG_SMP) &&
270 	    IS_ENABLED(CONFIG_QEMU_TARGET)) {
271 		/* Qemu in SMP mode has observed the clock going
272 		 * "backwards" relative to interrupts already received
273 		 * on the other CPU, despite the HPET being
274 		 * theoretically a global device.
275 		 */
276 		int64_t diff = (int64_t)(now - last_count);
277 
278 		if (last_count && diff < 0) {
279 			now = last_count;
280 		}
281 	}
282 	uint32_t dticks = (uint32_t)((now - last_count) / cyc_per_tick);
283 
284 	last_count += (uint64_t)dticks * cyc_per_tick;
285 
286 	if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
287 		uint64_t next = last_count + cyc_per_tick;
288 
289 		if ((int64_t)(next - now) < HPET_CMP_MIN_DELAY) {
290 			next = now + HPET_CMP_MIN_DELAY;
291 		}
292 		hpet_timer_comparator_set(next);
293 	}
294 
295 	k_spin_unlock(&lock, key);
296 	sys_clock_announce(dticks);
297 }
298 
299 __pinned_func
config_timer0(unsigned int irq)300 static void config_timer0(unsigned int irq)
301 {
302 	uint32_t val = hpet_timer_conf_get();
303 
304 	/* 5-bit IRQ field starting at bit 9 */
305 	val = (val & ~(0x1f << 9)) | ((irq & 0x1f) << 9);
306 
307 #ifdef HPET_INT_LEVEL_TRIGGER
308 	/* Set level trigger if selected */
309 	val |= TIMER_CONF_INT_LEVEL;
310 #endif
311 
312 	val &=  ~((uint32_t)(TIMER_CONF_MODE32 | TIMER_CONF_PERIODIC |
313 			TIMER_CONF_FSB_EN));
314 	val |= TIMER_CONF_INT_ENABLE;
315 
316 	hpet_timer_conf_set(val);
317 }
318 
319 __boot_func
sys_clock_driver_init(const struct device * dev)320 int sys_clock_driver_init(const struct device *dev)
321 {
322 	extern int z_clock_hw_cycles_per_sec;
323 	uint32_t hz, reg;
324 
325 	ARG_UNUSED(dev);
326 	ARG_UNUSED(hz);
327 	ARG_UNUSED(z_clock_hw_cycles_per_sec);
328 
329 	DEVICE_MMIO_TOPLEVEL_MAP(hpet_regs, K_MEM_CACHE_NONE);
330 
331 #if DT_INST_IRQ_HAS_CELL(0, sense)
332 	IRQ_CONNECT(DT_INST_IRQN(0),
333 		    DT_INST_IRQ(0, priority),
334 		    hpet_isr, 0, DT_INST_IRQ(0, sense));
335 #else
336 	IRQ_CONNECT(DT_INST_IRQN(0),
337 		    DT_INST_IRQ(0, priority),
338 		    hpet_isr, 0, 0);
339 #endif
340 	config_timer0(DT_INST_IRQN(0));
341 	irq_enable(DT_INST_IRQN(0));
342 
343 #ifdef CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME
344 	hz = (uint32_t)(HPET_COUNTER_CLK_PERIOD / hpet_counter_clk_period_get());
345 	z_clock_hw_cycles_per_sec = hz;
346 	cyc_per_tick = hz / CONFIG_SYS_CLOCK_TICKS_PER_SEC;
347 #endif
348 
349 	/* Note: we set the legacy routing bit, because otherwise
350 	 * nothing in Zephyr disables the PIT which then fires
351 	 * interrupts into the same IRQ.  But that means we're then
352 	 * forced to use IRQ2 contra the way the kconfig IRQ selection
353 	 * is supposed to work.  Should fix this.
354 	 */
355 	reg = hpet_gconf_get();
356 	reg |= GCONF_LR | GCONF_ENABLE;
357 	hpet_gconf_set(reg);
358 
359 	last_count = hpet_counter_get();
360 	if (cyc_per_tick >= HPET_CMP_MIN_DELAY) {
361 		hpet_timer_comparator_set(last_count + cyc_per_tick);
362 	} else {
363 		hpet_timer_comparator_set(last_count + HPET_CMP_MIN_DELAY);
364 	}
365 
366 	return 0;
367 }
368 
369 __boot_func
smp_timer_init(void)370 void smp_timer_init(void)
371 {
372 	/* Noop, the HPET is a single system-wide device and it's
373 	 * configured to deliver interrupts to every CPU, so there's
374 	 * nothing to do at initialization on auxiliary CPUs.
375 	 */
376 }
377 
378 __pinned_func
sys_clock_set_timeout(int32_t ticks,bool idle)379 void sys_clock_set_timeout(int32_t ticks, bool idle)
380 {
381 	ARG_UNUSED(idle);
382 
383 #if defined(CONFIG_TICKLESS_KERNEL)
384 	uint32_t reg;
385 
386 	if (ticks == K_TICKS_FOREVER && idle) {
387 		reg = hpet_gconf_get();
388 		reg &= ~GCONF_ENABLE;
389 		hpet_gconf_set(reg);
390 		return;
391 	}
392 
393 	ticks = ticks == K_TICKS_FOREVER ? HPET_MAX_TICKS : ticks;
394 	ticks = CLAMP(ticks - 1, 0, HPET_MAX_TICKS);
395 
396 	k_spinlock_key_t key = k_spin_lock(&lock);
397 	uint64_t now = hpet_counter_get(), cyc, adj;
398 	uint64_t max_cyc = (uint64_t)HPET_MAX_TICKS * cyc_per_tick;
399 
400 	/* Round up to next tick boundary. */
401 	cyc = (uint64_t)ticks * cyc_per_tick;
402 	adj = (now - last_count) + (cyc_per_tick - 1);
403 	if (cyc <= max_cyc - adj) {
404 		cyc += adj;
405 	} else {
406 		cyc = max_cyc;
407 	}
408 	cyc = (cyc / cyc_per_tick) * cyc_per_tick;
409 	cyc += last_count;
410 
411 	if ((int64_t)(cyc - now) < HPET_CMP_MIN_DELAY) {
412 		cyc = now + HPET_CMP_MIN_DELAY;
413 	}
414 
415 	hpet_timer_comparator_set(cyc);
416 	k_spin_unlock(&lock, key);
417 #endif
418 }
419 
420 __pinned_func
sys_clock_elapsed(void)421 uint32_t sys_clock_elapsed(void)
422 {
423 	if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
424 		return 0;
425 	}
426 
427 	k_spinlock_key_t key = k_spin_lock(&lock);
428 	uint64_t now = hpet_counter_get();
429 	uint32_t ret = (uint32_t)((now - last_count) / cyc_per_tick);
430 
431 	k_spin_unlock(&lock, key);
432 	return ret;
433 }
434 
435 __pinned_func
sys_clock_cycle_get_32(void)436 uint32_t sys_clock_cycle_get_32(void)
437 {
438 	return (uint32_t)hpet_counter_get();
439 }
440 
441 __pinned_func
sys_clock_idle_exit(void)442 void sys_clock_idle_exit(void)
443 {
444 	uint32_t reg;
445 
446 	reg = hpet_gconf_get();
447 	reg |= GCONF_ENABLE;
448 	hpet_gconf_set(reg);
449 }
450