Skip to content

Commit 352c97f

Browse files
committed
drivers: timer: Add timer driver for or1k
The OpenRISC 1000 Tick Timer is tightly coupled to the or1k CPU core, and is explicitly designed to facilitate task scheduling and high-resolution timing. The timer is documented in Chapter 14 of the OpenRISC 1000 Architecture Manual: https://openrisc.io/or1k.html#__RefHeading__504849_595890882 Signed-off-by: Joel Holdsworth <jholdsworth@nvidia.com>
1 parent 4e80deb commit 352c97f

File tree

5 files changed

+152
-0
lines changed

5 files changed

+152
-0
lines changed

MAINTAINERS.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5576,6 +5576,7 @@ OpenRISC Arch:
55765576
- jhol
55775577
files:
55785578
- arch/openrisc/
5579+
- drivers/timer/*openrisc*
55795580
- include/zephyr/arch/openrisc/
55805581
labels:
55815582
- "area: OpenRISC"

drivers/timer/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ zephyr_library_sources_ifdef(CONFIG_NATIVE_POSIX_TIMER native_posix_timer.c)
3030
zephyr_library_sources_ifdef(CONFIG_NPCX_ITIM_TIMER npcx_itim_timer.c)
3131
zephyr_library_sources_ifdef(CONFIG_NRF_GRTC_TIMER nrf_grtc_timer.c)
3232
zephyr_library_sources_ifdef(CONFIG_NRF_RTC_TIMER nrf_rtc_timer.c)
33+
zephyr_library_sources_ifdef(CONFIG_OPENRISC_TICK_TIMER openrisc_tick_timer.c)
3334
zephyr_library_sources_ifdef(CONFIG_RCAR_CMT_TIMER rcar_cmt_timer.c)
3435
zephyr_library_sources_ifdef(CONFIG_RISCV_MACHINE_TIMER riscv_machine_timer.c)
3536
zephyr_library_sources_ifdef(CONFIG_RV32M1_LPTMR_TIMER rv32m1_lptmr_timer.c)

drivers/timer/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ source "drivers/timer/Kconfig.npcx_itim"
8888
source "drivers/timer/Kconfig.nrf_rtc"
8989
source "drivers/timer/Kconfig.nrf_grtc"
9090
source "drivers/timer/Kconfig.nrf_xrtc"
91+
source "drivers/timer/Kconfig.openrisc_tick"
9192
source "drivers/timer/Kconfig.rcar_cmt"
9293
source "drivers/timer/Kconfig.riscv_machine"
9394
source "drivers/timer/Kconfig.rv32m1_lptmr"

drivers/timer/Kconfig.openrisc_tick

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#
2+
# Copyright (c) 2025 NVIDIA Corporation <jholdsworth@nvidia.com>
3+
#
4+
# SPDX-License-Identifier: Apache-2.0
5+
#
6+
7+
config OPENRISC_TICK_TIMER
8+
bool "OpenRISC Tick Timer"
9+
depends on OPENRISC
10+
select TICKLESS_CAPABLE
11+
help
12+
This module implements a kernel device driver for the OpenRISC Tick timer.

drivers/timer/openrisc_tick_timer.c

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*
2+
* Copyright (c) 2025 NVIDIA Corporation <jholdsworth@nvidia.com>
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/drivers/timer/system_timer.h>
8+
#include <zephyr/init.h>
9+
#include <zephyr/kernel.h>
10+
#include <zephyr/spinlock.h>
11+
#include <zephyr/sys_clock.h>
12+
13+
#include <openrisc/openriscregs.h>
14+
15+
#define MAX_CYC SPR_TTMR_TP
16+
17+
static struct k_spinlock lock;
18+
static uint32_t last_count;
19+
static uint64_t last_ticks;
20+
static uint32_t last_elapsed;
21+
static uint32_t cyc_per_tick;
22+
23+
static ALWAYS_INLINE void set_compare(uint32_t time)
24+
{
25+
openrisc_write_spr(SPR_TTMR, SPR_TTMR_IE | SPR_TTMR_CR | time);
26+
}
27+
28+
static ALWAYS_INLINE void clear_compare(void)
29+
{
30+
openrisc_write_spr(SPR_TTMR, SPR_TTMR_CR);
31+
}
32+
33+
static ALWAYS_INLINE uint32_t get_count(void)
34+
{
35+
return openrisc_read_spr(SPR_TTCR);
36+
}
37+
38+
void z_openrisc_timer_isr(void)
39+
{
40+
if (IS_ENABLED(CONFIG_TRACING_ISR)) {
41+
sys_trace_isr_enter();
42+
}
43+
44+
const k_spinlock_key_t key = k_spin_lock(&lock);
45+
46+
const uint32_t current_count = get_count();
47+
const uint32_t delta_count = current_count - last_count;
48+
const uint32_t delta_ticks = delta_count / cyc_per_tick;
49+
50+
last_count += delta_ticks * cyc_per_tick;
51+
last_ticks += delta_ticks;
52+
last_elapsed = 0;
53+
54+
if (IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
55+
clear_compare();
56+
} else {
57+
set_compare((last_count + cyc_per_tick) & MAX_CYC);
58+
}
59+
60+
k_spin_unlock(&lock, key);
61+
sys_clock_announce(delta_ticks);
62+
63+
if (IS_ENABLED(CONFIG_TRACING_ISR)) {
64+
sys_trace_isr_exit();
65+
}
66+
}
67+
68+
void sys_clock_set_timeout(int32_t ticks, bool idle)
69+
{
70+
#if defined(CONFIG_TICKLESS_KERNEL)
71+
if (ticks == K_TICKS_FOREVER) {
72+
if (idle) {
73+
return;
74+
}
75+
76+
ticks = INT32_MAX;
77+
}
78+
79+
/*
80+
* Clamp the max period length to a number of cycles that can fit
81+
* in half the range of a cycle_diff_t for native width divisions
82+
* to be usable elsewhere. The half range gives us extra room to cope
83+
* with the unavoidable IRQ servicing latency.
84+
*/
85+
ticks = CLAMP(ticks, 0, MAX_CYC / 2 / cyc_per_tick);
86+
87+
const uint32_t compare =
88+
((last_ticks + last_elapsed + (uint32_t)ticks) * cyc_per_tick) & MAX_CYC;
89+
const k_spinlock_key_t key = k_spin_lock(&lock);
90+
91+
set_compare(compare);
92+
k_spin_unlock(&lock, key);
93+
94+
#else /* CONFIG_TICKLESS_KERNEL */
95+
ARG_UNUSED(ticks);
96+
ARG_UNUSED(idle);
97+
#endif
98+
}
99+
100+
uint32_t sys_clock_elapsed(void)
101+
{
102+
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
103+
return 0;
104+
}
105+
106+
const k_spinlock_key_t key = k_spin_lock(&lock);
107+
108+
const uint32_t current_count = get_count();
109+
const uint32_t delta_count = current_count - last_count;
110+
const uint32_t delta_ticks = delta_count / cyc_per_tick;
111+
112+
last_elapsed = delta_ticks;
113+
k_spin_unlock(&lock, key);
114+
return delta_ticks;
115+
}
116+
117+
uint32_t sys_clock_cycle_get_32(void)
118+
{
119+
return get_count();
120+
}
121+
122+
static int sys_clock_driver_init(void)
123+
{
124+
cyc_per_tick = (uint32_t)((uint64_t)sys_clock_hw_cycles_per_sec() /
125+
(uint64_t)CONFIG_SYS_CLOCK_TICKS_PER_SEC);
126+
127+
last_ticks = get_count() / cyc_per_tick;
128+
last_count = last_ticks * cyc_per_tick;
129+
last_elapsed = 0;
130+
131+
set_compare((last_count + cyc_per_tick) & MAX_CYC);
132+
133+
return 0;
134+
}
135+
136+
SYS_INIT(sys_clock_driver_init, PRE_KERNEL_2,
137+
CONFIG_SYSTEM_CLOCK_INIT_PRIORITY);

0 commit comments

Comments
 (0)