Skip to content

Commit ebca0f0

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 ebca0f0

File tree

7 files changed

+503
-0
lines changed

7 files changed

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

0 commit comments

Comments
 (0)