Skip to content

Commit 2e8a08a

Browse files
henrikbrixandersenkartben
authored andcommitted
drivers: counter: add NEORV32 GPTMR driver
Add counter driver for the NEORV32 General Purpose Timer (GPTMR). Signed-off-by: Henrik Brix Andersen <henrik@brixandersen.dk>
1 parent bd409ab commit 2e8a08a

File tree

4 files changed

+291
-0
lines changed

4 files changed

+291
-0
lines changed

drivers/counter/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,4 @@ zephyr_library_sources_ifdef(CONFIG_COUNTER_RA_AGT counter_renesas_
5757
zephyr_library_sources_ifdef(CONFIG_COUNTER_RENESAS_RZ_GTM counter_renesas_rz_gtm.c)
5858
zephyr_library_sources_ifdef(CONFIG_COUNTER_REALTEK_RTS5912_SLWTMR counter_realtek_rts5912_slwtmr.c)
5959
zephyr_library_sources_ifdef(CONFIG_COUNTER_REALTEK_RTS5912 counter_realtek_rts5912.c)
60+
zephyr_library_sources_ifdef(CONFIG_COUNTER_NEORV32_GPTMR counter_neorv32_gptmr.c)

drivers/counter/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,6 @@ source "drivers/counter/Kconfig.rts5912_slwtmr"
112112

113113
source "drivers/counter/Kconfig.rts5912"
114114

115+
source "drivers/counter/Kconfig.neorv32"
116+
115117
endif # COUNTER

drivers/counter/Kconfig.neorv32

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# NEORV32 General Purpose Timer (GPTMR)
2+
#
3+
# Copyright (c) 2025 Henrik Brix Andersen <henrik@brixandersen.dk>
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
config COUNTER_NEORV32_GPTMR
7+
bool "NEORV32 GPTMR driver"
8+
default y
9+
depends on DT_HAS_NEORV32_GPTMR_ENABLED
10+
depends on SYSCON
11+
help
12+
Enable NEORV32 General Purpose Timer (GPTMR) driver.
Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
/*
2+
* Copyright (c) 2025 Henrik Brix Andersen <henrik@brixandersen.dk>
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT neorv32_gptmr
8+
9+
#include <zephyr/drivers/counter.h>
10+
#include <zephyr/drivers/syscon.h>
11+
#include <zephyr/irq.h>
12+
#include <zephyr/spinlock.h>
13+
#include <zephyr/sys/sys_io.h>
14+
#include <zephyr/logging/log.h>
15+
16+
#include <soc.h>
17+
18+
LOG_MODULE_REGISTER(neorv32_gptmr, CONFIG_COUNTER_LOG_LEVEL);
19+
20+
/* Registers */
21+
#define NEORV32_GPTMR_CTRL 0x00
22+
#define NEORV32_GPTMR_CTRL_EN BIT(0)
23+
#define NEORV32_GPTMR_CTRL_PRSC GENMASK(3, 1)
24+
#define NEORV32_GPTMR_CTRL_IRQ_CLR BIT(30)
25+
#define NEORV32_GPTMR_CTRL_IRQ_PND BIT(31)
26+
27+
#define NEORV32_GPTMR_THRES 0x04
28+
#define NEORV32_GPTMR_COUNT 0x08
29+
30+
struct neorv32_gptmr_config {
31+
struct counter_config_info info;
32+
const struct device *syscon;
33+
mm_reg_t base;
34+
uint8_t prescaler;
35+
void (*irq_config_func)(void);
36+
};
37+
38+
struct neorv32_gptmr_data {
39+
struct k_spinlock lock;
40+
counter_top_callback_t top_callback;
41+
void *top_user_data;
42+
};
43+
44+
static inline uint32_t neorv32_gptmr_read(const struct device *dev, uint16_t reg)
45+
{
46+
const struct neorv32_gptmr_config *config = dev->config;
47+
48+
return sys_read32(config->base + reg);
49+
}
50+
51+
static inline void neorv32_gptmr_write(const struct device *dev, uint16_t reg, uint32_t val)
52+
{
53+
const struct neorv32_gptmr_config *config = dev->config;
54+
55+
sys_write32(val, config->base + reg);
56+
}
57+
58+
static int neorv32_gptmr_start(const struct device *dev)
59+
{
60+
struct neorv32_gptmr_data *data = dev->data;
61+
k_spinlock_key_t key;
62+
uint32_t ctrl;
63+
64+
key = k_spin_lock(&data->lock);
65+
66+
ctrl = neorv32_gptmr_read(dev, NEORV32_GPTMR_CTRL);
67+
ctrl |= NEORV32_GPTMR_CTRL_EN;
68+
neorv32_gptmr_write(dev, NEORV32_GPTMR_CTRL, ctrl);
69+
70+
k_spin_unlock(&data->lock, key);
71+
72+
return 0;
73+
}
74+
75+
static int neorv32_gptmr_stop(const struct device *dev)
76+
{
77+
struct neorv32_gptmr_data *data = dev->data;
78+
k_spinlock_key_t key;
79+
uint32_t ctrl;
80+
81+
key = k_spin_lock(&data->lock);
82+
83+
ctrl = neorv32_gptmr_read(dev, NEORV32_GPTMR_CTRL);
84+
ctrl &= ~NEORV32_GPTMR_CTRL_EN;
85+
neorv32_gptmr_write(dev, NEORV32_GPTMR_CTRL, ctrl);
86+
87+
k_spin_unlock(&data->lock, key);
88+
89+
return 0;
90+
}
91+
92+
static int neorv32_gptmr_get_value(const struct device *dev, uint32_t *ticks)
93+
{
94+
*ticks = neorv32_gptmr_read(dev, NEORV32_GPTMR_COUNT);
95+
96+
return 0;
97+
}
98+
99+
static int neorv32_gptmr_set_top_value(const struct device *dev, const struct counter_top_cfg *cfg)
100+
{
101+
struct neorv32_gptmr_data *data = dev->data;
102+
k_spinlock_key_t key;
103+
bool restart = false;
104+
uint32_t count;
105+
uint32_t ctrl;
106+
int err = 0;
107+
108+
__ASSERT_NO_MSG(cfg != NULL);
109+
110+
if (cfg->ticks == 0) {
111+
return -EINVAL;
112+
}
113+
114+
if ((cfg->flags & ~(COUNTER_TOP_CFG_DONT_RESET | COUNTER_TOP_CFG_RESET_WHEN_LATE)) != 0U) {
115+
LOG_ERR("unsupported flags 0x%08x", cfg->flags);
116+
return -ENOTSUP;
117+
}
118+
119+
key = k_spin_lock(&data->lock);
120+
121+
data->top_callback = cfg->callback;
122+
data->top_user_data = cfg->user_data;
123+
124+
ctrl = neorv32_gptmr_read(dev, NEORV32_GPTMR_CTRL);
125+
count = neorv32_gptmr_read(dev, NEORV32_GPTMR_COUNT);
126+
127+
if ((ctrl & NEORV32_GPTMR_CTRL_EN) != 0U) {
128+
if ((cfg->flags & COUNTER_TOP_CFG_DONT_RESET) == 0U) {
129+
neorv32_gptmr_write(dev, NEORV32_GPTMR_CTRL, ctrl & ~NEORV32_GPTMR_CTRL_EN);
130+
restart = true;
131+
} else if (count >= cfg->ticks) {
132+
if ((cfg->flags & COUNTER_TOP_CFG_RESET_WHEN_LATE) != 0U) {
133+
neorv32_gptmr_write(dev, NEORV32_GPTMR_CTRL,
134+
ctrl & ~NEORV32_GPTMR_CTRL_EN);
135+
restart = true;
136+
}
137+
138+
err = -ETIME;
139+
}
140+
}
141+
142+
neorv32_gptmr_write(dev, NEORV32_GPTMR_THRES, cfg->ticks);
143+
144+
if (restart) {
145+
neorv32_gptmr_write(dev, NEORV32_GPTMR_CTRL, ctrl);
146+
}
147+
148+
k_spin_unlock(&data->lock, key);
149+
150+
return err;
151+
}
152+
153+
static uint32_t neorv32_gptmr_get_pending_int(const struct device *dev)
154+
{
155+
uint32_t ctrl;
156+
157+
ctrl = neorv32_gptmr_read(dev, NEORV32_GPTMR_CTRL);
158+
159+
return (ctrl & NEORV32_GPTMR_CTRL_IRQ_PND) != 0 ? 1 : 0;
160+
}
161+
162+
static uint32_t neorv32_gptmr_get_top_value(const struct device *dev)
163+
{
164+
return neorv32_gptmr_read(dev, NEORV32_GPTMR_THRES);
165+
}
166+
167+
static uint32_t neorv32_gptmr_get_freq(const struct device *dev)
168+
{
169+
static const uint16_t prescalers[] = { 2U, 4U, 8U, 64U, 128U, 1024U, 2048U, 4096U };
170+
const struct neorv32_gptmr_config *config = dev->config;
171+
uint32_t clk;
172+
int err;
173+
174+
err = syscon_read_reg(config->syscon, NEORV32_SYSINFO_CLK, &clk);
175+
if (err < 0) {
176+
LOG_ERR("failed to determine clock rate (err %d)", err);
177+
return 0U;
178+
}
179+
180+
return clk / prescalers[config->prescaler];
181+
}
182+
183+
static void neorv32_gptmr_isr(const struct device *dev)
184+
{
185+
struct neorv32_gptmr_data *data = dev->data;
186+
counter_top_callback_t top_callback;
187+
void *top_user_data;
188+
k_spinlock_key_t key;
189+
uint32_t ctrl;
190+
191+
key = k_spin_lock(&data->lock);
192+
193+
ctrl = neorv32_gptmr_read(dev, NEORV32_GPTMR_CTRL);
194+
ctrl |= NEORV32_GPTMR_CTRL_IRQ_CLR;
195+
neorv32_gptmr_write(dev, NEORV32_GPTMR_CTRL, ctrl);
196+
197+
top_callback = data->top_callback;
198+
top_user_data = data->top_user_data;
199+
200+
k_spin_unlock(&data->lock, key);
201+
202+
if (top_callback != NULL) {
203+
top_callback(dev, top_user_data);
204+
}
205+
}
206+
207+
static int neorv32_gptmr_init(const struct device *dev)
208+
{
209+
const struct neorv32_gptmr_config *config = dev->config;
210+
uint32_t features;
211+
uint32_t ctrl;
212+
int err;
213+
214+
if (!device_is_ready(config->syscon)) {
215+
LOG_ERR("syscon device not ready");
216+
return -EINVAL;
217+
}
218+
219+
err = syscon_read_reg(config->syscon, NEORV32_SYSINFO_SOC, &features);
220+
if (err < 0) {
221+
LOG_ERR("failed to determine implemented features (err %d)", err);
222+
return -EIO;
223+
}
224+
225+
if ((features & NEORV32_SYSINFO_SOC_IO_GPTMR) == 0) {
226+
LOG_ERR("neorv32 gptmr not supported");
227+
return -ENODEV;
228+
}
229+
230+
/* Stop timer, set prescaler, clear any pending interrupt */
231+
ctrl = FIELD_PREP(NEORV32_GPTMR_CTRL_PRSC, config->prescaler) | NEORV32_GPTMR_CTRL_IRQ_CLR;
232+
neorv32_gptmr_write(dev, NEORV32_GPTMR_CTRL, ctrl);
233+
234+
config->irq_config_func();
235+
236+
return 0;
237+
}
238+
239+
static DEVICE_API(counter, neorv32_gptmr_driver_api) = {
240+
.start = neorv32_gptmr_start,
241+
.stop = neorv32_gptmr_stop,
242+
.get_value = neorv32_gptmr_get_value,
243+
.set_top_value = neorv32_gptmr_set_top_value,
244+
.get_pending_int = neorv32_gptmr_get_pending_int,
245+
.get_top_value = neorv32_gptmr_get_top_value,
246+
.get_freq = neorv32_gptmr_get_freq,
247+
};
248+
249+
#define COUNTER_NEORV32_GPTMR_INIT(n) \
250+
static void neorv32_gptmr_config_func_##n(void) \
251+
{ \
252+
IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), neorv32_gptmr_isr, \
253+
DEVICE_DT_INST_GET(n), 0); \
254+
irq_enable(DT_INST_IRQN(n)); \
255+
} \
256+
\
257+
static struct neorv32_gptmr_data neorv32_gptmr_data_##n; \
258+
\
259+
static struct neorv32_gptmr_config neorv32_gptmr_config_##n = { \
260+
.info = { \
261+
.max_top_value = UINT32_MAX, \
262+
.freq = 0U, \
263+
.flags = COUNTER_CONFIG_INFO_COUNT_UP, \
264+
.channels = 0, \
265+
}, \
266+
.syscon = DEVICE_DT_GET(DT_INST_PHANDLE(n, syscon)), \
267+
.base = DT_INST_REG_ADDR(n), \
268+
.prescaler = DT_INST_ENUM_IDX(n, prescaler), \
269+
.irq_config_func = neorv32_gptmr_config_func_##n, \
270+
}; \
271+
\
272+
DEVICE_DT_INST_DEFINE(n, &neorv32_gptmr_init, NULL, &neorv32_gptmr_data_##n, \
273+
&neorv32_gptmr_config_##n, POST_KERNEL, \
274+
CONFIG_COUNTER_INIT_PRIORITY, &neorv32_gptmr_driver_api);
275+
276+
DT_INST_FOREACH_STATUS_OKAY(COUNTER_NEORV32_GPTMR_INIT)

0 commit comments

Comments
 (0)