Skip to content

Commit 5a94a1c

Browse files
Titan-Realtekkartben
authored andcommitted
drivers: counter: rts5912: add support slow timer counter driver
Port rts5912 slow timer counter driver on Zephyr Signed-off-by: Titan Chen <[email protected]>
1 parent 8f2f97c commit 5a94a1c

File tree

6 files changed

+388
-1
lines changed

6 files changed

+388
-1
lines changed

drivers/counter/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
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_SLWTMR counter_realtek_rts5912_slwtmr.c)

drivers/counter/Kconfig

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

dts/bindings/timer/realtek,rts5912-slwtimer.yaml renamed to dts/bindings/counter/realtek,rts5912-slwtimer.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ description: Realtek RTS5912 32-bit slow timer
77

88
compatible: "realtek,rts5912-slwtimer"
99

10-
include: rtc.yaml
10+
include: base.yaml
1111

1212
properties:
1313
reg:
@@ -22,7 +22,9 @@ properties:
2222
description: Maximum counter value the instance can handle
2323

2424
clock-frequency:
25+
type: int
2526
required: true
27+
description: Clock frequency information for timer operation
2628

2729
prescaler:
2830
type: int

0 commit comments

Comments
 (0)