Skip to content

Commit ae66d5e

Browse files
author
Andy Ross
committed
drivers/timer: New, tickless-capable RISC-V machine timer driver
Rewritten driver along the lines of all the other new drivers, implementing the new timer API. Structurally, the machine timer is an up-counter with comparator, so it works broadly the same way HPET and NRF do. The quirk here is that it's a 64 bit counter, which needs a little more care. Unlike the other timer reworks, this driver has grown by a few lines as it used to be very simple. But in exchange, we get full tickless support on the platform. Fixes #10609 in the process (the 64 bit timer registers are unlatched for sub-word transfers, so you have to use careful ordering). Signed-off-by: Andy Ross <[email protected]>
1 parent 05a2e58 commit ae66d5e

File tree

2 files changed

+105
-85
lines changed

2 files changed

+105
-85
lines changed

drivers/timer/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ config PULPINO_TIMER
169169
config RISCV_MACHINE_TIMER
170170
bool "RISCV Machine Timer"
171171
depends on SOC_FAMILY_RISCV_PRIVILEGE
172+
select TICKLESS_CAPABLE
172173
help
173174
This module implements a kernel device driver for the generic RISCV machine
174175
timer driver. It provides the standard "system clock driver" interfaces.

drivers/timer/riscv_machine_timer.c

Lines changed: 104 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,116 +1,135 @@
11
/*
2-
* Copyright (c) 2017 Jean-Paul Etienne <[email protected]>
2+
* Copyright (c) 2018 Intel Corporation
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
6+
#include <drivers/system_timer.h>
7+
#include <sys_clock.h>
8+
#include <spinlock.h>
9+
#include <soc.h>
610

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
1115

12-
#include "legacy_api.h"
16+
#define TICKLESS (IS_ENABLED(CONFIG_TICKLESS_KERNEL) && \
17+
!IS_ENABLED(CONFIG_QEMU_TICKLESS_WORKAROUND))
1318

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;
1821

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)
3223
{
33-
u64_t rtc;
24+
volatile u32_t *r = (u32_t *)RISCV_MTIMECMP_BASE;
3425

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.
3931
*/
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+
}
5236

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;
6041

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;
6349
}
6450

65-
static void riscv_machine_timer_irq_handler(void *unused)
51+
static void timer_isr(void *arg)
6652
{
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);
7254

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);
7458

75-
/* Rearm timer */
76-
riscv_machine_rearm_timer();
59+
last_count += dticks * CYC_PER_TICK;
7760

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;
8363

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+
}
8773

8874
int z_clock_driver_init(struct device *device)
8975
{
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+
}
9196

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);
9499

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;
97103

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;
99130
}
100131

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-
*/
110132
u32_t _timer_cycle_get_32(void)
111133
{
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();
116135
}

0 commit comments

Comments
 (0)