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