Skip to content

Commit c28e38b

Browse files
committed
drivers: counter: rts5912: add support timer32 counter driver
Port rts5912 timer32 counter driver on Zephyr Signed-off-by: Titan Chen <[email protected]>
1 parent 19c6240 commit c28e38b

File tree

7 files changed

+505
-0
lines changed

7 files changed

+505
-0
lines changed

drivers/counter/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,4 @@ zephyr_library_sources_ifdef(CONFIG_COUNTER_RTC_MAX32 counter_max32_rt
5454
zephyr_library_sources_ifdef(CONFIG_COUNTER_NXP_MRT counter_nxp_mrt.c)
5555
zephyr_library_sources_ifdef(CONFIG_COUNTER_RA_AGT counter_renesas_ra_agt.c)
5656
zephyr_library_sources_ifdef(CONFIG_COUNTER_RENESAS_RZ_GTM counter_renesas_rz_gtm.c)
57+
zephyr_library_sources_ifdef(CONFIG_COUNTER_REALTEK_RTS5912 counter_realtek_rts5912.c)

drivers/counter/Kconfig

+2
Original file line numberDiff line numberDiff line change
@@ -106,4 +106,6 @@ source "drivers/counter/Kconfig.renesas_ra"
106106

107107
source "drivers/counter/Kconfig.renesas_rz"
108108

109+
source "drivers/counter/Kconfig.rts5912"
110+
109111
endif # COUNTER

drivers/counter/Kconfig.rts5912

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Realtek counter configuration options
2+
3+
# Copyright (c) 2025 Realtek Corporation
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
config COUNTER_REALTEK_RTS5912
7+
bool "Realtek rts5912 series counter driver"
8+
default y
9+
depends on DT_HAS_REALTEK_RTS5912_TIMER_ENABLED
10+
help
11+
Enable counter driver for Realtek RTS5912 MCU series. Such driver
12+
will expose the basic timer devices present on the MCU.
+348
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,348 @@
1+
/*
2+
* Copyright (c) 2025 Realtek Corporation
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT realtek_rts5912_timer
8+
9+
/**
10+
* @file
11+
* @brief Realtek RTS5912 Counter driver
12+
*
13+
* This is the driver for the 32-bit counters on the Realtek SoCs.
14+
*
15+
* Notes:
16+
* - The counters are running in down counting mode.
17+
* - Interrupts are triggered (if enabled) when the counter
18+
* reaches zero.
19+
* - These are not free running counters where there are separate
20+
* compare values for interrupts. When setting single shot alarms,
21+
* the counter values are changed so that interrupts are triggered
22+
* when the counters reach zero.
23+
*/
24+
25+
#include <zephyr/kernel.h>
26+
#include <zephyr/drivers/counter.h>
27+
#include <zephyr/drivers/clock_control.h>
28+
#include <zephyr/drivers/clock_control/clock_control_rts5912.h>
29+
#include "reg/reg_timer.h"
30+
#include <zephyr/logging/log.h>
31+
LOG_MODULE_REGISTER(counter_realtek_rts5912, CONFIG_COUNTER_LOG_LEVEL);
32+
struct counter_rts5912_config {
33+
struct counter_config_info info;
34+
void (*config_func)(void);
35+
volatile struct timer32_type *base_address;
36+
uint16_t prescaler;
37+
uint32_t clk_grp;
38+
uint32_t clk_idx;
39+
const struct device *clk_dev;
40+
};
41+
42+
struct counter_rts5912_data {
43+
counter_alarm_callback_t alarm_cb;
44+
counter_top_callback_t top_cb;
45+
void *user_data;
46+
};
47+
48+
#define COUNTER_RTS5912_REG_BASE(_dev) \
49+
((const struct counter_rts5912_config *const)_dev->config)->base_address
50+
51+
static int counter_rts5912_start(const struct device *dev)
52+
{
53+
const struct counter_rts5912_config *config = dev->config;
54+
volatile struct timer32_type *counter = config->base_address;
55+
56+
if (counter->ctrl & TIMER32_CTRL_EN) {
57+
return 0;
58+
}
59+
60+
counter->ctrl |= (TIMER32_CTRL_EN);
61+
62+
LOG_DBG("%p Counter started", dev);
63+
64+
return 0;
65+
}
66+
67+
static int counter_rts5912_stop(const struct device *dev)
68+
{
69+
const struct counter_rts5912_config *config = dev->config;
70+
volatile struct timer32_type *counter = config->base_address;
71+
72+
if (!(counter->ctrl & TIMER32_CTRL_EN)) {
73+
/* Already stopped, nothing to do */
74+
return 0;
75+
}
76+
/* disable timer and disable interrupt. */
77+
counter->ctrl = TIMER32_CTRL_INTEN_DIS;
78+
counter->ldcnt = counter->cnt;
79+
/* w1c interrupt pending status */
80+
counter->intclr |= TIMER32_INTCLR_INTCLR;
81+
82+
LOG_DBG("%p Counter stopped", dev);
83+
84+
return 0;
85+
}
86+
87+
static int counter_rts5912_get_value(const struct device *dev, uint32_t *ticks)
88+
{
89+
const struct counter_rts5912_config *config = dev->config;
90+
volatile struct timer32_type *counter = config->base_address;
91+
92+
*ticks = counter->cnt + 1;
93+
return 0;
94+
}
95+
96+
static int counter_rts5912_set_alarm(const struct device *dev, uint8_t chan_id,
97+
const struct counter_alarm_cfg *alarm_cfg)
98+
{
99+
struct counter_rts5912_data *data = dev->data;
100+
const struct counter_rts5912_config *counter_cfg = dev->config;
101+
volatile struct timer32_type *counter = counter_cfg->base_address;
102+
103+
if (chan_id != 0) {
104+
LOG_ERR("Invalid channel id %u", chan_id);
105+
return -ENOTSUP;
106+
}
107+
108+
/* Interrupts are only triggered when the counter reaches 0.
109+
* So only relative alarms are supported.
110+
*/
111+
if (alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) {
112+
return -ENOTSUP;
113+
}
114+
115+
if (data->alarm_cb != NULL) {
116+
return -EBUSY;
117+
}
118+
119+
if (!alarm_cfg->callback) {
120+
return -EINVAL;
121+
}
122+
123+
if (alarm_cfg->ticks > counter_cfg->info.max_top_value) {
124+
return -EINVAL;
125+
}
126+
/* disable timer */
127+
counter->ctrl &= ~TIMER32_CTRL_EN;
128+
/* disable interrupt */
129+
counter->ctrl |= TIMER32_CTRL_INTEN_DIS;
130+
/* set in one-shot mode */
131+
counter->ctrl &= ~TIMER32_CTRL_MDSELS_PERIOD;
132+
/* set load counter */
133+
counter->ldcnt = alarm_cfg->ticks;
134+
135+
data->alarm_cb = alarm_cfg->callback;
136+
data->user_data = alarm_cfg->user_data;
137+
/* w1c interrupt status */
138+
counter->intclr |= TIMER32_INTCLR_INTCLR;
139+
/* enable interrupt */
140+
counter->ctrl &= ~TIMER32_CTRL_INTEN_DIS;
141+
142+
LOG_DBG("%p Counter alarm set to %u ticks", dev, alarm_cfg->ticks);
143+
/* enable timer and re-load PRCNT to CNT */
144+
counter->ctrl |= TIMER32_CTRL_EN;
145+
146+
return 0;
147+
}
148+
149+
static int counter_rts5912_cancel_alarm(const struct device *dev, uint8_t chan_id)
150+
{
151+
struct counter_rts5912_data *data = dev->data;
152+
const struct counter_rts5912_config *config = dev->config;
153+
volatile struct timer32_type *counter = config->base_address;
154+
155+
if (chan_id != 0) {
156+
LOG_ERR("Invalid channel id %u", chan_id);
157+
return -ENOTSUP;
158+
}
159+
160+
counter->ctrl = 0;
161+
162+
data->alarm_cb = NULL;
163+
data->user_data = NULL;
164+
165+
LOG_DBG("%p Counter alarm canceled", dev);
166+
167+
return 0;
168+
}
169+
170+
static uint32_t counter_rts5912_get_pending_int(const struct device *dev)
171+
{
172+
const struct counter_rts5912_config *config = dev->config;
173+
volatile struct timer32_type *counter = config->base_address;
174+
175+
return counter->intsts;
176+
}
177+
178+
static uint32_t counter_rts5912_get_top_value(const struct device *dev)
179+
{
180+
const struct counter_rts5912_config *config = dev->config;
181+
volatile struct timer32_type *counter = config->base_address;
182+
183+
return counter->ldcnt;
184+
}
185+
186+
static int counter_rts5912_set_top_value(const struct device *dev,
187+
const struct counter_top_cfg *cfg)
188+
{
189+
const struct counter_rts5912_config *counter_cfg = dev->config;
190+
struct counter_rts5912_data *data = dev->data;
191+
volatile struct timer32_type *counter = counter_cfg->base_address;
192+
int ret = 0;
193+
194+
if (data->alarm_cb) {
195+
return -EBUSY;
196+
}
197+
198+
if (cfg->ticks > counter_cfg->info.max_top_value) {
199+
return -EINVAL;
200+
}
201+
202+
counter->ctrl &= ~TIMER32_CTRL_EN;
203+
counter->ctrl |= TIMER32_CTRL_INTEN_DIS;
204+
205+
counter->ldcnt = cfg->ticks;
206+
207+
data->top_cb = cfg->callback;
208+
data->user_data = cfg->user_data;
209+
210+
if (data->top_cb) {
211+
/* w1c interrupt status */
212+
counter->intclr |= TIMER32_INTCLR_INTCLR;
213+
/* enable interrupt */
214+
counter->ctrl &= ~TIMER32_CTRL_INTEN_DIS;
215+
/* enable periodic alarm mode */
216+
counter->ctrl |= TIMER32_CTRL_MDSELS_PERIOD;
217+
} else {
218+
counter->ctrl = TIMER32_CTRL_INTEN_DIS;
219+
}
220+
221+
LOG_DBG("%p Counter top value was set to %u", dev, cfg->ticks);
222+
223+
counter->ctrl |= TIMER32_CTRL_EN;
224+
225+
return ret;
226+
}
227+
228+
static void counter_rts5912_isr(const struct device *dev)
229+
{
230+
struct counter_rts5912_data *data = dev->data;
231+
const struct counter_rts5912_config *config = dev->config;
232+
volatile struct timer32_type *counter = config->base_address;
233+
counter_alarm_callback_t alarm_cb;
234+
void *user_data;
235+
236+
/* disable timer */
237+
counter->ctrl &= ~TIMER32_CTRL_EN;
238+
/* disable interrupt */
239+
counter->ctrl |= TIMER32_CTRL_INTEN_DIS;
240+
/* clear interrupt pending status */
241+
counter->intclr |= TIMER32_INTCLR_INTCLR;
242+
243+
LOG_DBG("%p Counter ISR", dev);
244+
245+
if (data->alarm_cb) {
246+
/* Alarm is one-shot, so disable callback */
247+
alarm_cb = data->alarm_cb;
248+
data->alarm_cb = NULL;
249+
user_data = data->user_data;
250+
251+
alarm_cb(dev, 0, counter->cnt + 1, user_data);
252+
} else if (data->top_cb) {
253+
data->top_cb(dev, data->user_data);
254+
/* periodic alarm mode, enable interrupt */
255+
counter->ctrl &= ~TIMER32_CTRL_INTEN_DIS;
256+
/* enable timer again */
257+
counter->ctrl |= TIMER32_CTRL_EN;
258+
}
259+
}
260+
261+
static uint32_t counter_rts5912_get_freq(const struct device *dev)
262+
{
263+
const struct counter_rts5912_config *counter_cfg = dev->config;
264+
265+
return counter_cfg->info.freq;
266+
}
267+
268+
static DEVICE_API(counter, counter_rts5912_api) = {
269+
.start = counter_rts5912_start,
270+
.stop = counter_rts5912_stop,
271+
.get_value = counter_rts5912_get_value,
272+
.set_alarm = counter_rts5912_set_alarm,
273+
.cancel_alarm = counter_rts5912_cancel_alarm,
274+
.set_top_value = counter_rts5912_set_top_value,
275+
.get_pending_int = counter_rts5912_get_pending_int,
276+
.get_top_value = counter_rts5912_get_top_value,
277+
.get_freq = counter_rts5912_get_freq,
278+
};
279+
280+
static int counter_rts5912_init(const struct device *dev)
281+
{
282+
const struct counter_rts5912_config *counter_cfg = dev->config;
283+
volatile struct timer32_type *counter = counter_cfg->base_address;
284+
int rc;
285+
struct rts5912_sccon_subsys sccon_subsys = {
286+
.clk_grp = counter_cfg->clk_grp,
287+
.clk_idx = counter_cfg->clk_idx,
288+
};
289+
290+
if (!device_is_ready(counter_cfg->clk_dev)) {
291+
LOG_ERR("device is not ready");
292+
return -ENODEV;
293+
}
294+
295+
rc = clock_control_on(counter_cfg->clk_dev, (clock_control_subsys_t)&sccon_subsys);
296+
if (rc != 0) {
297+
LOG_ERR("clock power on fail");
298+
return rc;
299+
}
300+
301+
counter_rts5912_stop(dev);
302+
303+
/* Set preload and actually pre-load the counter */
304+
counter->ldcnt = counter_cfg->info.max_top_value;
305+
counter->cnt = counter_cfg->info.max_top_value;
306+
307+
counter_cfg->config_func();
308+
LOG_DBG("Init complete!");
309+
return 0;
310+
}
311+
312+
#define DEV_CONFIG_CLK_DEV_INIT(n) \
313+
.clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
314+
.clk_grp = DT_INST_CLOCKS_CELL_BY_NAME(n, tmr32, clk_grp), \
315+
.clk_idx = DT_INST_CLOCKS_CELL_BY_NAME(n, tmr32, clk_idx),
316+
317+
#define COUNTER_RTS5912_INIT(inst) \
318+
static void counter_rts5912_irq_config_##inst(void); \
319+
\
320+
static struct counter_rts5912_data counter_rts5912_dev_data_##inst; \
321+
\
322+
static struct counter_rts5912_config counter_rts5912_dev_config_##inst = { \
323+
.info = \
324+
{ \
325+
.max_top_value = DT_INST_PROP(inst, max_value), \
326+
.freq = DT_INST_PROP(inst, clock_frequency) / \
327+
(1 << DT_INST_PROP(inst, prescaler)), \
328+
.flags = 0, \
329+
.channels = 1, \
330+
}, \
331+
\
332+
.config_func = counter_rts5912_irq_config_##inst, \
333+
.base_address = (struct timer32_type *)DT_INST_REG_ADDR(inst), \
334+
.prescaler = DT_INST_PROP(inst, prescaler), \
335+
DEV_CONFIG_CLK_DEV_INIT(inst)}; \
336+
\
337+
DEVICE_DT_INST_DEFINE(inst, counter_rts5912_init, NULL, &counter_rts5912_dev_data_##inst, \
338+
&counter_rts5912_dev_config_##inst, PRE_KERNEL_1, \
339+
CONFIG_COUNTER_INIT_PRIORITY, &counter_rts5912_api); \
340+
\
341+
static void counter_rts5912_irq_config_##inst(void) \
342+
{ \
343+
IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), counter_rts5912_isr, \
344+
DEVICE_DT_INST_GET(inst), 0); \
345+
irq_enable(DT_INST_IRQN(inst)); \
346+
}
347+
348+
DT_INST_FOREACH_STATUS_OKAY(COUNTER_RTS5912_INIT)

0 commit comments

Comments
 (0)