|
1 | 1 | /*
|
2 |
| - * Copyright (c) 2017 Jean-Paul Etienne <[email protected]> |
| 2 | + * Copyright (c) 2018 Intel Corporation |
3 | 3 | *
|
4 | 4 | * SPDX-License-Identifier: Apache-2.0
|
5 | 5 | */
|
| 6 | +#include <drivers/system_timer.h> |
| 7 | +#include <sys_clock.h> |
| 8 | +#include <spinlock.h> |
| 9 | +#include <soc.h> |
6 | 10 |
|
7 |
| -#include <kernel.h> |
8 |
| -#include <arch/cpu.h> |
9 |
| -#include <device.h> |
10 |
| -#include <system_timer.h> |
| 11 | +#define CYC_PER_TICK ((u32_t)((u64_t)CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC \ |
| 12 | + / (u64_t)CONFIG_SYS_CLOCK_TICKS_PER_SEC)) |
| 13 | +#define MAX_TICKS ((0xffffffffu - CYC_PER_TICK) / CYC_PER_TICK) |
| 14 | +#define MIN_DELAY 1000 |
11 | 15 |
|
12 |
| -#include "legacy_api.h" |
| 16 | +#define TICKLESS (IS_ENABLED(CONFIG_TICKLESS_KERNEL) && \ |
| 17 | + !IS_ENABLED(CONFIG_QEMU_TICKLESS_WORKAROUND)) |
13 | 18 |
|
14 |
| -typedef struct { |
15 |
| - u32_t val_low; |
16 |
| - u32_t val_high; |
17 |
| -} riscv_machine_timer_t; |
| 19 | +static struct k_spinlock lock; |
| 20 | +static u64_t last_count; |
18 | 21 |
|
19 |
| -static volatile riscv_machine_timer_t *mtime = |
20 |
| - (riscv_machine_timer_t *)RISCV_MTIME_BASE; |
21 |
| -static volatile riscv_machine_timer_t *mtimecmp = |
22 |
| - (riscv_machine_timer_t *)RISCV_MTIMECMP_BASE; |
23 |
| - |
24 |
| -/* |
25 |
| - * The RISCV machine-mode timer is a one shot timer that needs to be rearm upon |
26 |
| - * every interrupt. Timer clock is a 64-bits ART. |
27 |
| - * To arm timer, we need to read the RTC value and update the |
28 |
| - * timer compare register by the RTC value + time interval we want timer |
29 |
| - * to interrupt. |
30 |
| - */ |
31 |
| -static ALWAYS_INLINE void riscv_machine_rearm_timer(void) |
| 22 | +static void set_mtimecmp(u64_t time) |
32 | 23 | {
|
33 |
| - u64_t rtc; |
| 24 | + volatile u32_t *r = (u32_t *)RISCV_MTIMECMP_BASE; |
34 | 25 |
|
35 |
| - /* |
36 |
| - * Disable timer interrupt while rearming the timer |
37 |
| - * to avoid generation of interrupts while setting |
38 |
| - * the mtimecmp->val_low register. |
| 26 | + /* Per spec, the RISC-V MTIME/MTIMECMP registers are 64 bit, |
| 27 | + * but are NOT internally latched for multiword transfers. So |
| 28 | + * we have to be careful about sequencing to avoid triggering |
| 29 | + * spurious interrupts: always set the high word to a max |
| 30 | + * value first. |
39 | 31 | */
|
40 |
| - irq_disable(RISCV_MACHINE_TIMER_IRQ); |
41 |
| - |
42 |
| - /* |
43 |
| - * Following machine-mode timer implementation in QEMU, the actual |
44 |
| - * RTC read is performed when reading low timer value register. |
45 |
| - * Reading high timer value just reads the most significant 32-bits |
46 |
| - * of a cache value, obtained from a previous read to the low |
47 |
| - * timer value register. Hence, always read timer->val_low first. |
48 |
| - * This also works for other implementations. |
49 |
| - */ |
50 |
| - rtc = mtime->val_low; |
51 |
| - rtc |= ((u64_t)mtime->val_high << 32); |
| 32 | + r[1] = 0xffffffff; |
| 33 | + r[0] = (u32_t)time; |
| 34 | + r[1] = (u32_t)(time >> 32); |
| 35 | +} |
52 | 36 |
|
53 |
| - /* |
54 |
| - * Rearm timer to generate an interrupt after |
55 |
| - * sys_clock_hw_cycles_per_tick() |
56 |
| - */ |
57 |
| - rtc += sys_clock_hw_cycles_per_tick(); |
58 |
| - mtimecmp->val_low = (u32_t)(rtc & 0xffffffff); |
59 |
| - mtimecmp->val_high = (u32_t)((rtc >> 32) & 0xffffffff); |
| 37 | +static u64_t mtime(void) |
| 38 | +{ |
| 39 | + volatile u32_t *r = (u32_t *)RISCV_MTIME_BASE; |
| 40 | + u32_t lo, hi; |
60 | 41 |
|
61 |
| - /* Enable timer interrupt */ |
62 |
| - irq_enable(RISCV_MACHINE_TIMER_IRQ); |
| 42 | + /* Likewise, must guard against rollover when reading */ |
| 43 | + do { |
| 44 | + hi = r[1]; |
| 45 | + lo = r[0]; |
| 46 | + } while (r[1] != hi); |
| 47 | + |
| 48 | + return (((u64_t)hi) << 32) | lo; |
63 | 49 | }
|
64 | 50 |
|
65 |
| -static void riscv_machine_timer_irq_handler(void *unused) |
| 51 | +static void timer_isr(void *arg) |
66 | 52 | {
|
67 |
| - ARG_UNUSED(unused); |
68 |
| -#ifdef CONFIG_EXECUTION_BENCHMARKING |
69 |
| - extern void read_timer_start_of_tick_handler(void); |
70 |
| - read_timer_start_of_tick_handler(); |
71 |
| -#endif |
| 53 | + ARG_UNUSED(arg); |
72 | 54 |
|
73 |
| - z_clock_announce(1); |
| 55 | + k_spinlock_key_t key = k_spin_lock(&lock); |
| 56 | + u64_t now = mtime(); |
| 57 | + u32_t dticks = (u32_t)((now - last_count) / CYC_PER_TICK); |
74 | 58 |
|
75 |
| - /* Rearm timer */ |
76 |
| - riscv_machine_rearm_timer(); |
| 59 | + last_count += dticks * CYC_PER_TICK; |
77 | 60 |
|
78 |
| -#ifdef CONFIG_EXECUTION_BENCHMARKING |
79 |
| - extern void read_timer_end_of_tick_handler(void); |
80 |
| - read_timer_end_of_tick_handler(); |
81 |
| -#endif |
82 |
| -} |
| 61 | + if (!TICKLESS) { |
| 62 | + u64_t next = last_count + CYC_PER_TICK; |
83 | 63 |
|
84 |
| -#ifdef CONFIG_TICKLESS_IDLE |
85 |
| -#error "Tickless idle not yet implemented for riscv-machine timer" |
86 |
| -#endif |
| 64 | + if ((s64_t)(next - now) < MIN_DELAY) { |
| 65 | + next += CYC_PER_TICK; |
| 66 | + } |
| 67 | + set_mtimecmp(next); |
| 68 | + } |
| 69 | + |
| 70 | + k_spin_unlock(&lock, key); |
| 71 | + z_clock_announce(dticks); |
| 72 | +} |
87 | 73 |
|
88 | 74 | int z_clock_driver_init(struct device *device)
|
89 | 75 | {
|
90 |
| - ARG_UNUSED(device); |
| 76 | + IRQ_CONNECT(RISCV_MACHINE_TIMER_IRQ, 0, timer_isr, NULL, 0); |
| 77 | + set_mtimecmp(mtime() + CYC_PER_TICK); |
| 78 | + irq_enable(RISCV_MACHINE_TIMER_IRQ); |
| 79 | + return 0; |
| 80 | +} |
| 81 | + |
| 82 | +void z_clock_set_timeout(s32_t ticks, bool idle) |
| 83 | +{ |
| 84 | + ARG_UNUSED(idle); |
| 85 | + |
| 86 | +#if defined(CONFIG_TICKLESS_KERNEL) && !defined(CONFIG_QEMU_TICKLESS_WORKAROUND) |
| 87 | + /* RISCV has no idle handler yet, so if we try to spin on the |
| 88 | + * logic below to reset the comparator, we'll always bump it |
| 89 | + * forward to the "next tick" due to MIN_DELAY handling and |
| 90 | + * the interrupt will never fire! Just rely on the fact that |
| 91 | + * the OS gave us the proper timeout already. |
| 92 | + */ |
| 93 | + if (idle) { |
| 94 | + return; |
| 95 | + } |
91 | 96 |
|
92 |
| - IRQ_CONNECT(RISCV_MACHINE_TIMER_IRQ, 0, |
93 |
| - riscv_machine_timer_irq_handler, NULL, 0); |
| 97 | + ticks = ticks == K_FOREVER ? MAX_TICKS : ticks; |
| 98 | + ticks = max(min(ticks - 1, (s32_t)MAX_TICKS), 0); |
94 | 99 |
|
95 |
| - /* Initialize timer, just call riscv_machine_rearm_timer */ |
96 |
| - riscv_machine_rearm_timer(); |
| 100 | + k_spinlock_key_t key = k_spin_lock(&lock); |
| 101 | + u64_t now = mtime(); |
| 102 | + u32_t cyc = ticks * CYC_PER_TICK; |
97 | 103 |
|
98 |
| - return 0; |
| 104 | + /* Round up to next tick boundary. Note use of 32 bit math, |
| 105 | + * max_ticks is calibrated to permit this. |
| 106 | + */ |
| 107 | + cyc += (u32_t)(now - last_count) + (CYC_PER_TICK - 1); |
| 108 | + cyc = (cyc / CYC_PER_TICK) * CYC_PER_TICK; |
| 109 | + |
| 110 | + if ((s32_t)(cyc + last_count - now) < MIN_DELAY) { |
| 111 | + cyc += CYC_PER_TICK; |
| 112 | + } |
| 113 | + |
| 114 | + set_mtimecmp(cyc + last_count); |
| 115 | + k_spin_unlock(&lock, key); |
| 116 | +#endif |
| 117 | +} |
| 118 | + |
| 119 | +u32_t z_clock_elapsed(void) |
| 120 | +{ |
| 121 | + if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { |
| 122 | + return 0; |
| 123 | + } |
| 124 | + |
| 125 | + k_spinlock_key_t key = k_spin_lock(&lock); |
| 126 | + u32_t ret = ((u32_t)mtime() - (u32_t)last_count) / CYC_PER_TICK; |
| 127 | + |
| 128 | + k_spin_unlock(&lock, key); |
| 129 | + return ret; |
99 | 130 | }
|
100 | 131 |
|
101 |
| -/** |
102 |
| - * |
103 |
| - * @brief Read the platform's timer hardware |
104 |
| - * |
105 |
| - * This routine returns the current time in terms of timer hardware clock |
106 |
| - * cycles. |
107 |
| - * |
108 |
| - * @return up counter of elapsed clock cycles |
109 |
| - */ |
110 | 132 | u32_t _timer_cycle_get_32(void)
|
111 | 133 | {
|
112 |
| - /* We just want a cycle count so just post what's in the low 32 |
113 |
| - * bits of the mtime real-time counter |
114 |
| - */ |
115 |
| - return mtime->val_low; |
| 134 | + return (u32_t)mtime(); |
116 | 135 | }
|
0 commit comments