Skip to content

Commit 2bca8d4

Browse files
Titan-Realtekkartben
authored andcommitted
drivers: counter: rts5912: add support timer32 counter driver
Port rts5912 timer32 counter driver on Zephyr Signed-off-by: Titan Chen <[email protected]>
1 parent e219da1 commit 2bca8d4

File tree

7 files changed

+514
-0
lines changed

7 files changed

+514
-0
lines changed

drivers/counter/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,4 @@ zephyr_library_sources_ifdef(CONFIG_COUNTER_NXP_MRT counter_nxp_mrt.
5656
zephyr_library_sources_ifdef(CONFIG_COUNTER_RA_AGT counter_renesas_ra_agt.c)
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)
59+
zephyr_library_sources_ifdef(CONFIG_COUNTER_REALTEK_RTS5912 counter_realtek_rts5912.c)

drivers/counter/Kconfig

+2
Original file line numberDiff line numberDiff line change
@@ -110,4 +110,6 @@ source "drivers/counter/Kconfig.renesas_rz"
110110

111111
source "drivers/counter/Kconfig.rts5912_slwtmr"
112112

113+
source "drivers/counter/Kconfig.rts5912"
114+
113115
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.
+357
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
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->cnt = counter->ldcnt;
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+
uint32_t value;
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 > counter_cfg->info.max_top_value) {
125+
return -EINVAL;
126+
}
127+
/* disable timer */
128+
counter->ctrl &= ~TIMER32_CTRL_EN;
129+
/* disable interrupt */
130+
counter->ctrl |= TIMER32_CTRL_INTEN_DIS;
131+
/* set in one-shot mode */
132+
counter->ctrl &= ~TIMER32_CTRL_MDSELS_PERIOD;
133+
/* set load counter */
134+
counter->ldcnt = alarm_cfg->ticks;
135+
136+
data->alarm_cb = alarm_cfg->callback;
137+
data->user_data = alarm_cfg->user_data;
138+
/* w1c interrupt status */
139+
counter->intclr |= TIMER32_INTCLR_INTCLR;
140+
/* enable interrupt */
141+
counter->ctrl &= ~TIMER32_CTRL_INTEN_DIS;
142+
143+
LOG_DBG("%p Counter alarm set to %u ticks", dev, alarm_cfg->ticks);
144+
/* enable timer and re-load PRCNT to CNT */
145+
counter->ctrl |= TIMER32_CTRL_EN;
146+
/* read count value to update register */
147+
value = counter->cnt;
148+
149+
return 0;
150+
}
151+
152+
static int counter_rts5912_cancel_alarm(const struct device *dev, uint8_t chan_id)
153+
{
154+
struct counter_rts5912_data *data = dev->data;
155+
const struct counter_rts5912_config *config = dev->config;
156+
volatile struct timer32_type *counter = config->base_address;
157+
158+
if (chan_id != 0) {
159+
LOG_ERR("Invalid channel id %u", chan_id);
160+
return -ENOTSUP;
161+
}
162+
163+
counter->ctrl = 0;
164+
165+
data->alarm_cb = NULL;
166+
data->user_data = NULL;
167+
168+
LOG_DBG("%p Counter alarm canceled", dev);
169+
170+
return 0;
171+
}
172+
173+
static uint32_t counter_rts5912_get_pending_int(const struct device *dev)
174+
{
175+
const struct counter_rts5912_config *config = dev->config;
176+
volatile struct timer32_type *counter = config->base_address;
177+
178+
return counter->intsts;
179+
}
180+
181+
static uint32_t counter_rts5912_get_top_value(const struct device *dev)
182+
{
183+
const struct counter_rts5912_config *config = dev->config;
184+
volatile struct timer32_type *counter = config->base_address;
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+
const struct counter_rts5912_config *counter_cfg = dev->config;
193+
struct counter_rts5912_data *data = dev->data;
194+
volatile struct timer32_type *counter = counter_cfg->base_address;
195+
uint32_t value;
196+
int ret = 0;
197+
198+
if (data->alarm_cb) {
199+
return -EBUSY;
200+
}
201+
202+
if (cfg->ticks > counter_cfg->info.max_top_value) {
203+
return -EINVAL;
204+
}
205+
206+
counter->ctrl &= ~TIMER32_CTRL_EN;
207+
counter->ctrl |= TIMER32_CTRL_INTEN_DIS;
208+
209+
counter->ldcnt = cfg->ticks;
210+
211+
data->top_cb = cfg->callback;
212+
data->user_data = cfg->user_data;
213+
214+
if (data->top_cb) {
215+
/* w1c interrupt status */
216+
counter->intclr |= TIMER32_INTCLR_INTCLR;
217+
/* enable interrupt */
218+
counter->ctrl &= ~TIMER32_CTRL_INTEN_DIS;
219+
/* enable periodic alarm mode */
220+
counter->ctrl |= TIMER32_CTRL_MDSELS_PERIOD;
221+
} else {
222+
counter->ctrl = TIMER32_CTRL_INTEN_DIS;
223+
}
224+
225+
LOG_DBG("%p Counter top value was set to %u", dev, cfg->ticks);
226+
227+
counter->ctrl |= TIMER32_CTRL_EN;
228+
/* read count value to update register */
229+
value = counter->cnt;
230+
231+
return ret;
232+
}
233+
234+
static void counter_rts5912_isr(const struct device *dev)
235+
{
236+
struct counter_rts5912_data *data = dev->data;
237+
const struct counter_rts5912_config *config = dev->config;
238+
volatile struct timer32_type *counter = config->base_address;
239+
counter_alarm_callback_t alarm_cb;
240+
void *user_data;
241+
uint32_t value;
242+
243+
/* disable timer */
244+
counter->ctrl &= ~TIMER32_CTRL_EN;
245+
/* disable interrupt */
246+
counter->ctrl |= TIMER32_CTRL_INTEN_DIS;
247+
/* clear interrupt pending status */
248+
counter->intclr |= TIMER32_INTCLR_INTCLR;
249+
250+
LOG_DBG("%p Counter ISR", dev);
251+
252+
if (data->alarm_cb) {
253+
/* Alarm is one-shot, so disable callback */
254+
alarm_cb = data->alarm_cb;
255+
data->alarm_cb = NULL;
256+
user_data = data->user_data;
257+
258+
alarm_cb(dev, 0, counter->cnt + 1, user_data);
259+
} else if (data->top_cb) {
260+
data->top_cb(dev, data->user_data);
261+
/* periodic alarm mode, enable interrupt */
262+
counter->ctrl &= ~TIMER32_CTRL_INTEN_DIS;
263+
/* enable timer again */
264+
counter->ctrl |= TIMER32_CTRL_EN;
265+
/* read count value to update register */
266+
value = counter->cnt;
267+
}
268+
}
269+
270+
static uint32_t counter_rts5912_get_freq(const struct device *dev)
271+
{
272+
const struct counter_rts5912_config *counter_cfg = dev->config;
273+
274+
return counter_cfg->info.freq;
275+
}
276+
277+
static DEVICE_API(counter, counter_rts5912_api) = {
278+
.start = counter_rts5912_start,
279+
.stop = counter_rts5912_stop,
280+
.get_value = counter_rts5912_get_value,
281+
.set_alarm = counter_rts5912_set_alarm,
282+
.cancel_alarm = counter_rts5912_cancel_alarm,
283+
.set_top_value = counter_rts5912_set_top_value,
284+
.get_pending_int = counter_rts5912_get_pending_int,
285+
.get_top_value = counter_rts5912_get_top_value,
286+
.get_freq = counter_rts5912_get_freq,
287+
};
288+
289+
static int counter_rts5912_init(const struct device *dev)
290+
{
291+
const struct counter_rts5912_config *counter_cfg = dev->config;
292+
volatile struct timer32_type *counter = counter_cfg->base_address;
293+
int rc;
294+
struct rts5912_sccon_subsys sccon_subsys = {
295+
.clk_grp = counter_cfg->clk_grp,
296+
.clk_idx = counter_cfg->clk_idx,
297+
};
298+
299+
if (!device_is_ready(counter_cfg->clk_dev)) {
300+
LOG_ERR("device is not ready");
301+
return -ENODEV;
302+
}
303+
304+
rc = clock_control_on(counter_cfg->clk_dev, (clock_control_subsys_t)&sccon_subsys);
305+
if (rc != 0) {
306+
LOG_ERR("clock power on fail");
307+
return rc;
308+
}
309+
310+
counter_rts5912_stop(dev);
311+
312+
/* Set preload and actually pre-load the counter */
313+
counter->ldcnt = counter_cfg->info.max_top_value;
314+
counter->cnt = counter_cfg->info.max_top_value;
315+
316+
counter_cfg->config_func();
317+
LOG_DBG("Init complete!");
318+
return 0;
319+
}
320+
321+
#define DEV_CONFIG_CLK_DEV_INIT(n) \
322+
.clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
323+
.clk_grp = DT_INST_CLOCKS_CELL_BY_NAME(n, tmr32, clk_grp), \
324+
.clk_idx = DT_INST_CLOCKS_CELL_BY_NAME(n, tmr32, clk_idx),
325+
326+
#define COUNTER_RTS5912_INIT(inst) \
327+
static void counter_rts5912_irq_config_##inst(void); \
328+
\
329+
static struct counter_rts5912_data counter_rts5912_dev_data_##inst; \
330+
\
331+
static struct counter_rts5912_config counter_rts5912_dev_config_##inst = { \
332+
.info = \
333+
{ \
334+
.max_top_value = DT_INST_PROP(inst, max_value), \
335+
.freq = DT_INST_PROP(inst, clock_frequency) / \
336+
(1 << DT_INST_PROP(inst, prescaler)), \
337+
.flags = 0, \
338+
.channels = 1, \
339+
}, \
340+
\
341+
.config_func = counter_rts5912_irq_config_##inst, \
342+
.base_address = (struct timer32_type *)DT_INST_REG_ADDR(inst), \
343+
.prescaler = DT_INST_PROP(inst, prescaler), \
344+
DEV_CONFIG_CLK_DEV_INIT(inst)}; \
345+
\
346+
DEVICE_DT_INST_DEFINE(inst, counter_rts5912_init, NULL, &counter_rts5912_dev_data_##inst, \
347+
&counter_rts5912_dev_config_##inst, PRE_KERNEL_1, \
348+
CONFIG_COUNTER_INIT_PRIORITY, &counter_rts5912_api); \
349+
\
350+
static void counter_rts5912_irq_config_##inst(void) \
351+
{ \
352+
IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), counter_rts5912_isr, \
353+
DEVICE_DT_INST_GET(inst), 0); \
354+
irq_enable(DT_INST_IRQN(inst)); \
355+
}
356+
357+
DT_INST_FOREACH_STATUS_OKAY(COUNTER_RTS5912_INIT)

0 commit comments

Comments
 (0)