Skip to content

Commit 3621caa

Browse files
FelixWang47831jhedberg
authored andcommitted
drivers: Counter: STM Support on Zephyr
1.Add nxp,stm.yaml dts bindings 2.Provide counter driver based on FTM driver from mcux-sdk-ng. Signed-off-by: Felix Wang <[email protected]>
1 parent a115cb3 commit 3621caa

File tree

6 files changed

+309
-0
lines changed

6 files changed

+309
-0
lines changed

drivers/counter/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ zephyr_library_sources_ifdef(CONFIG_COUNTER_XEC counter_mchp_xec
3535
zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_LPTMR counter_mcux_lptmr.c)
3636
zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_LPIT counter_mcux_lpit.c)
3737
zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_FTM counter_mcux_ftm.c)
38+
zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_STM counter_mcux_stm.c)
3839
zephyr_library_sources_ifdef(CONFIG_COUNTER_MAXIM_DS3231 maxim_ds3231.c)
3940
zephyr_library_sources_ifdef(CONFIG_COUNTER_NATIVE_SIM counter_native_sim.c)
4041
zephyr_library_sources_ifdef(CONFIG_USERSPACE counter_handlers.c)

drivers/counter/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ source "drivers/counter/Kconfig.mcux_lpit"
7474

7575
source "drivers/counter/Kconfig.mcux_ftm"
7676

77+
source "drivers/counter/Kconfig.mcux_stm"
78+
7779
source "drivers/counter/Kconfig.maxim_ds3231"
7880

7981
source "drivers/counter/Kconfig.native_sim"

drivers/counter/Kconfig.mcux_stm

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Copyright 2025 NXP
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
# MCUXpresso SDK System Timer Module (STM)
6+
7+
config COUNTER_MCUX_STM
8+
bool "MCUX STM driver"
9+
default y
10+
depends on DT_HAS_NXP_STM_ENABLED
11+
help
12+
Enable support for the MCUX System Timer Module (STM).

drivers/counter/counter_mcux_stm.c

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
/*
2+
* Copyright (c) 2023-2025 NXP.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT nxp_stm
8+
9+
#include <zephyr/drivers/counter.h>
10+
#include <zephyr/drivers/clock_control.h>
11+
#include <zephyr/irq.h>
12+
#include <zephyr/logging/log.h>
13+
14+
#include <fsl_stm.h>
15+
16+
LOG_MODULE_REGISTER(mcux_stm, CONFIG_COUNTER_LOG_LEVEL);
17+
18+
struct mcux_stm_channel_data {
19+
counter_alarm_callback_t alarm_callback;
20+
void *alarm_user_data;
21+
};
22+
23+
struct mcux_stm_config {
24+
struct counter_config_info info;
25+
STM_Type *base;
26+
const struct device *clock_dev;
27+
clock_control_subsys_t clock_subsys;
28+
uint8_t prescale;
29+
void (*irq_config_func)(const struct device *dev);
30+
};
31+
32+
struct mcux_stm_data {
33+
uint32_t freq;
34+
struct mcux_stm_channel_data channels[STM_CHANNEL_COUNT];
35+
};
36+
37+
static int mcux_stm_start(const struct device *dev)
38+
{
39+
const struct mcux_stm_config *config = dev->config;
40+
41+
STM_StartTimer(config->base);
42+
43+
return 0;
44+
}
45+
46+
static int mcux_stm_stop(const struct device *dev)
47+
{
48+
const struct mcux_stm_config *config = dev->config;
49+
50+
STM_StopTimer(config->base);
51+
52+
return 0;
53+
}
54+
55+
static int mcux_stm_get_value(const struct device *dev, uint32_t *ticks)
56+
{
57+
const struct mcux_stm_config *config = dev->config;
58+
59+
*ticks = STM_GetTimerCount(config->base);
60+
61+
return 0;
62+
}
63+
64+
static uint32_t mcux_stm_get_top_value(const struct device *dev)
65+
{
66+
const struct mcux_stm_config *config = dev->config;
67+
68+
return config->info.max_top_value;
69+
}
70+
71+
static int mcux_stm_set_top_value(const struct device *dev, const struct counter_top_cfg *cfg)
72+
{
73+
const struct mcux_stm_config *config = dev->config;
74+
75+
if (cfg->ticks != config->info.max_top_value) {
76+
return -ENOTSUP;
77+
} else {
78+
return 0;
79+
}
80+
}
81+
82+
static int mcux_stm_set_alarm(const struct device *dev, uint8_t chan_id,
83+
const struct counter_alarm_cfg *alarm_cfg)
84+
{
85+
const struct mcux_stm_config *config = dev->config;
86+
struct mcux_stm_data *data = dev->data;
87+
uint32_t current = STM_GetTimerCount(config->base);
88+
uint32_t top_value = mcux_stm_get_top_value(dev);
89+
uint32_t ticks = alarm_cfg->ticks;
90+
91+
if (chan_id >= config->info.channels) {
92+
LOG_ERR("Invalid channel id");
93+
return -EINVAL;
94+
}
95+
96+
if (data->channels[chan_id].alarm_callback != NULL) {
97+
LOG_ERR("channel already in use");
98+
return -EBUSY;
99+
}
100+
101+
if (ticks > top_value) {
102+
return -EINVAL;
103+
}
104+
105+
if ((alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) == 0) {
106+
if (top_value - current >= ticks) {
107+
ticks += current;
108+
} else {
109+
ticks -= top_value - current;
110+
}
111+
}
112+
113+
data->channels[chan_id].alarm_callback = alarm_cfg->callback;
114+
data->channels[chan_id].alarm_user_data = alarm_cfg->user_data;
115+
116+
STM_SetCompare(config->base, (stm_channel_t)chan_id, ticks);
117+
118+
return 0;
119+
}
120+
121+
static int mcux_stm_cancel_alarm(const struct device *dev, uint8_t chan_id)
122+
{
123+
const struct mcux_stm_config *config = dev->config;
124+
struct mcux_stm_data *data = dev->data;
125+
126+
if (chan_id >= config->info.channels) {
127+
LOG_ERR("Invalid channel id");
128+
return -EINVAL;
129+
}
130+
131+
STM_DisableCompareChannel(config->base, (stm_channel_t)chan_id);
132+
133+
data->channels[chan_id].alarm_callback = NULL;
134+
data->channels[chan_id].alarm_user_data = NULL;
135+
136+
return 0;
137+
}
138+
139+
void mcux_stm_isr(const struct device *dev)
140+
{
141+
const struct mcux_stm_config *config = dev->config;
142+
struct mcux_stm_data *data = dev->data;
143+
uint32_t current = STM_GetTimerCount(config->base);
144+
uint32_t status;
145+
146+
/* Check the status flags for each channel */
147+
for (int chan_id = 0; chan_id < config->info.channels; chan_id++) {
148+
status = STM_GetStatusFlags(config->base, (stm_channel_t)chan_id);
149+
150+
if ((status & STM_CIR_CIF_MASK) != 0) {
151+
STM_ClearStatusFlags(config->base, (stm_channel_t)chan_id);
152+
} else {
153+
continue;
154+
}
155+
156+
if (data->channels[chan_id].alarm_callback != NULL) {
157+
158+
counter_alarm_callback_t alarm_callback =
159+
data->channels[chan_id].alarm_callback;
160+
void *alarm_user_data = data->channels[chan_id].alarm_user_data;
161+
162+
STM_DisableCompareChannel(config->base, (stm_channel_t)chan_id);
163+
data->channels[chan_id].alarm_callback = NULL;
164+
data->channels[chan_id].alarm_user_data = NULL;
165+
166+
alarm_callback(dev, chan_id, current, alarm_user_data);
167+
}
168+
}
169+
}
170+
171+
static uint32_t mcux_stm_get_pending_int(const struct device *dev)
172+
{
173+
const struct mcux_stm_config *config = dev->config;
174+
uint32_t pending = 0;
175+
176+
for (int i = 0; i < config->info.channels; i++) {
177+
pending |= STM_GetStatusFlags(config->base, (stm_channel_t)i);
178+
}
179+
180+
return pending != 0 ? 1 : 0;
181+
}
182+
183+
static uint32_t mcux_stm_get_freq(const struct device *dev)
184+
{
185+
struct mcux_stm_data *data = dev->data;
186+
187+
return data->freq;
188+
}
189+
190+
static int mcux_stm_init(const struct device *dev)
191+
{
192+
const struct mcux_stm_config *config = dev->config;
193+
struct mcux_stm_data *data = dev->data;
194+
stm_config_t stmConfig;
195+
uint32_t clock_freq;
196+
197+
if (!device_is_ready(config->clock_dev)) {
198+
LOG_ERR("clock control device not ready");
199+
return -ENODEV;
200+
}
201+
202+
for (uint8_t chan = 0; chan < config->info.channels; chan++) {
203+
data->channels[chan].alarm_callback = NULL;
204+
data->channels[chan].alarm_user_data = NULL;
205+
}
206+
207+
if (clock_control_get_rate(config->clock_dev, config->clock_subsys, &clock_freq)) {
208+
LOG_ERR("Could not get clock frequency");
209+
return -EINVAL;
210+
}
211+
212+
data->freq = clock_freq / (config->prescale + 1);
213+
214+
STM_GetDefaultConfig(&stmConfig);
215+
stmConfig.prescale = config->prescale;
216+
STM_Init(config->base, &stmConfig);
217+
218+
config->irq_config_func(dev);
219+
220+
return 0;
221+
}
222+
223+
static DEVICE_API(counter, mcux_stm_driver_api) = {
224+
.start = mcux_stm_start,
225+
.stop = mcux_stm_stop,
226+
.get_value = mcux_stm_get_value,
227+
.set_alarm = mcux_stm_set_alarm,
228+
.cancel_alarm = mcux_stm_cancel_alarm,
229+
.set_top_value = mcux_stm_set_top_value,
230+
.get_pending_int = mcux_stm_get_pending_int,
231+
.get_top_value = mcux_stm_get_top_value,
232+
.get_freq = mcux_stm_get_freq,
233+
};
234+
235+
#define COUNTER_MCUX_STM_DEVICE_INIT(n) \
236+
static struct mcux_stm_data mcux_stm_data_##n; \
237+
static void mcux_stm_irq_config_##n(const struct device *dev); \
238+
\
239+
static const struct mcux_stm_config mcux_stm_config_##n = { \
240+
.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
241+
.clock_subsys = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(n, name), \
242+
.info = { \
243+
.max_top_value = UINT32_MAX, \
244+
.channels = STM_CHANNEL_COUNT, \
245+
.flags = COUNTER_CONFIG_INFO_COUNT_UP, \
246+
}, \
247+
.base = (STM_Type *)DT_INST_REG_ADDR(n), \
248+
.prescale = DT_INST_PROP(n, prescaler), \
249+
.irq_config_func = mcux_stm_irq_config_##n, \
250+
}; \
251+
\
252+
DEVICE_DT_INST_DEFINE(n, mcux_stm_init, NULL, &mcux_stm_data_##n, \
253+
&mcux_stm_config_##n, POST_KERNEL, \
254+
CONFIG_COUNTER_INIT_PRIORITY, \
255+
&mcux_stm_driver_api); \
256+
\
257+
static void mcux_stm_irq_config_##n(const struct device *dev) \
258+
{ \
259+
IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), mcux_stm_isr, \
260+
DEVICE_DT_INST_GET(n), 0); \
261+
irq_enable(DT_INST_IRQN(n)); \
262+
}
263+
264+
DT_INST_FOREACH_STATUS_OKAY(COUNTER_MCUX_STM_DEVICE_INIT)

dts/bindings/counter/nxp,stm.yaml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Copyright 2025 NXP
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: NXP System Timer Module (STM)
5+
6+
compatible: "nxp,stm"
7+
8+
include: base.yaml
9+
10+
properties:
11+
reg:
12+
required: true
13+
14+
clocks:
15+
required: true
16+
17+
interrupts:
18+
required: true
19+
20+
prescaler:
21+
type: int
22+
default: 0
23+
required: true
24+
description: |
25+
Selects the module clock divide value for the prescaler (1–256).
26+
00h - Divide module clock by 1
27+
01h - Divide module clock by 2
28+
...
29+
FFh - Divide module clock by 256

modules/hal_nxp/mcux/mcux-sdk-ng/drivers/drivers.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ set_variable_ifdef(CONFIG_CAN_MCUX_FLEXCAN CONFIG_MCUX_COMPONENT_driver.fle
5252
set_variable_ifdef(CONFIG_CAN_MCUX_FLEXCAN_FD CONFIG_MCUX_COMPONENT_driver.flexcan)
5353
set_variable_ifdef(CONFIG_COUNTER_NXP_PIT CONFIG_MCUX_COMPONENT_driver.pit)
5454
set_variable_ifdef(CONFIG_COUNTER_MCUX_FTM CONFIG_MCUX_COMPONENT_driver.ftm)
55+
set_variable_ifdef(CONFIG_COUNTER_MCUX_STM CONFIG_MCUX_COMPONENT_driver.stm)
5556
set_variable_ifdef(CONFIG_COUNTER_MCUX_RTC CONFIG_MCUX_COMPONENT_driver.rtc)
5657
set_variable_ifdef(CONFIG_DAC_MCUX_DAC CONFIG_MCUX_COMPONENT_driver.dac)
5758
set_variable_ifdef(CONFIG_DAC_MCUX_DAC12 CONFIG_MCUX_COMPONENT_driver.dac12)

0 commit comments

Comments
 (0)