Skip to content

Commit dae0f67

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 9757ffa commit dae0f67

File tree

7 files changed

+529
-0
lines changed

7 files changed

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

0 commit comments

Comments
 (0)