Skip to content

Commit a717fac

Browse files
FelixWang47831MaureenHelm
authored andcommitted
drivers: Counter: LPIT Support on Zephyr
1.Add dts bindings nxp,lpit-channel.yaml and nxp,lpit.yaml 2.Provide counter driver based on lpit driver from NXP mcux-sdk-ng Signed-off-by: Felix Wang <[email protected]>
1 parent acad953 commit a717fac

File tree

7 files changed

+346
-0
lines changed

7 files changed

+346
-0
lines changed

drivers/counter/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_SNVS counter_mcux_snv
3333
zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_TPM counter_mcux_tpm.c)
3434
zephyr_library_sources_ifdef(CONFIG_COUNTER_XEC counter_mchp_xec.c)
3535
zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_LPTMR counter_mcux_lptmr.c)
36+
zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_LPIT counter_mcux_lpit.c)
3637
zephyr_library_sources_ifdef(CONFIG_COUNTER_MAXIM_DS3231 maxim_ds3231.c)
3738
zephyr_library_sources_ifdef(CONFIG_COUNTER_NATIVE_SIM counter_native_sim.c)
3839
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
@@ -70,6 +70,8 @@ source "drivers/counter/Kconfig.xec"
7070

7171
source "drivers/counter/Kconfig.mcux_lptmr"
7272

73+
source "drivers/counter/Kconfig.mcux_lpit"
74+
7375
source "drivers/counter/Kconfig.maxim_ds3231"
7476

7577
source "drivers/counter/Kconfig.native_sim"

drivers/counter/Kconfig.mcux_lpit

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Copyright 2025 NXP
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
# MCUXpresso SDK Low Power Periodic Interrupt Timer (LPIT)
5+
config COUNTER_MCUX_LPIT
6+
bool "NXP LPIT driver"
7+
default y
8+
depends on DT_HAS_NXP_LPIT_CHANNEL_ENABLED && \
9+
DT_HAS_NXP_LPIT_ENABLED
10+
help
11+
Enable support for the NXP Low Power Periodic Interrupt Timer (LPIT).

drivers/counter/counter_mcux_lpit.c

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
/*
2+
* Copyright 2023, 2025 NXP
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT nxp_lpit
8+
9+
#include <zephyr/drivers/counter.h>
10+
#include <zephyr/drivers/clock_control.h>
11+
#include <zephyr/irq.h>
12+
#include <fsl_lpit.h>
13+
#include <zephyr/logging/log.h>
14+
15+
LOG_MODULE_REGISTER(counter_lpit, CONFIG_COUNTER_LOG_LEVEL);
16+
17+
/* Device holds a pointer to pointer to data */
18+
#define LPIT_CHANNEL_DATA(dev) (*(struct mcux_lpit_channel_data *const *const)dev->data)
19+
20+
/* dev->config->data is an array of data pointers ordered by channel number,
21+
* dev->data is a pointer to one of these pointers in that array,
22+
* so the value of the dev->data - dev->config->data is the channel index
23+
*/
24+
#define LPIT_CHANNEL_ID(dev) \
25+
(((struct mcux_lpit_channel_data *const *)dev->data) - \
26+
((const struct mcux_lpit_config *)dev->config)->data)
27+
28+
struct mcux_lpit_channel_data {
29+
uint32_t top; /* Top value of the counter */
30+
counter_top_callback_t top_callback; /* Callback when counter turns around */
31+
void *top_user_data; /* User data for top callback */
32+
};
33+
34+
struct mcux_lpit_config {
35+
struct counter_config_info info;
36+
LPIT_Type *base; /* Poniter to LPIT peripheral instance base address*/
37+
lpit_config_t lpit_config; /* LPIT configuration structure */
38+
int num_channels; /* Number of channels for one LPIT instance*/
39+
void (*irq_config_func)(const struct device *dev);
40+
const struct device *clock_dev;
41+
clock_control_subsys_t clock_subsys;
42+
/* Point to array of channels data for this LPIT instance */
43+
struct mcux_lpit_channel_data *const *data;
44+
/* Point to array of channel devices for this LPIT instance */
45+
const struct device *const *channels;
46+
};
47+
48+
static uint32_t mcux_lpit_get_top_value(const struct device *dev)
49+
{
50+
const struct mcux_lpit_config *config = dev->config;
51+
uint8_t channel_id = LPIT_CHANNEL_ID(dev);
52+
53+
/*
54+
* The underlying HAL driver function LPIT_SetTimerPeriod()
55+
* automatically subtracted 1 from the value that ends up in
56+
* TVAL so for reporting purposes we need to add it back in
57+
* here to by consistent.
58+
*/
59+
return (config->base->CHANNEL[channel_id].TVAL + 1U);
60+
}
61+
62+
static int mcux_lpit_start(const struct device *dev)
63+
{
64+
const struct mcux_lpit_config *config = dev->config;
65+
uint8_t channel_id = LPIT_CHANNEL_ID(dev);
66+
67+
LOG_DBG("period is %d", mcux_lpit_get_top_value(dev));
68+
LPIT_EnableInterrupts(config->base, (1U << channel_id));
69+
LPIT_StartTimer(config->base, channel_id);
70+
return 0;
71+
}
72+
73+
static int mcux_lpit_stop(const struct device *dev)
74+
{
75+
const struct mcux_lpit_config *config = dev->config;
76+
uint8_t channel_id = LPIT_CHANNEL_ID(dev);
77+
78+
LPIT_DisableInterrupts(config->base, channel_id);
79+
LPIT_StopTimer(config->base, channel_id);
80+
return 0;
81+
}
82+
83+
static int mcux_lpit_get_value(const struct device *dev, uint32_t *ticks)
84+
{
85+
const struct mcux_lpit_config *config = dev->config;
86+
uint8_t channel_id = LPIT_CHANNEL_ID(dev);
87+
88+
*ticks = LPIT_GetCurrentTimerCount(config->base, channel_id);
89+
90+
return 0;
91+
}
92+
93+
static int mcux_lpit_set_top_value(const struct device *dev, const struct counter_top_cfg *cfg)
94+
{
95+
const struct mcux_lpit_config *config = dev->config;
96+
struct mcux_lpit_channel_data *data = LPIT_CHANNEL_DATA(dev);
97+
uint8_t channel_id = LPIT_CHANNEL_ID(dev);
98+
99+
/* Note: The underlying HAL driver function LPIT_SetTimerPeriod() assert(ticks > 2U) */
100+
if (cfg->ticks < 2U || cfg->ticks > config->info.max_top_value) {
101+
return -EINVAL;
102+
}
103+
104+
data->top_callback = cfg->callback;
105+
data->top_user_data = cfg->user_data;
106+
107+
if ((config->base->CHANNEL[channel_id].TCTRL & LPIT_TCTRL_T_EN_MASK) != 0) {
108+
/* Timer already enabled, check flags before resetting */
109+
if ((cfg->flags & COUNTER_TOP_CFG_DONT_RESET) != 0) {
110+
return -ENOTSUP;
111+
}
112+
mcux_lpit_stop(dev);
113+
LPIT_SetTimerPeriod(config->base, channel_id, cfg->ticks);
114+
mcux_lpit_start(dev);
115+
} else {
116+
LPIT_SetTimerPeriod(config->base, channel_id, cfg->ticks);
117+
}
118+
119+
return 0;
120+
}
121+
122+
static uint32_t mcux_lpit_get_pending_int(const struct device *dev)
123+
{
124+
const struct mcux_lpit_config *config = dev->config;
125+
uint8_t channel_id = LPIT_CHANNEL_ID(dev);
126+
127+
return (LPIT_GetStatusFlags(config->base) >> channel_id) & 0x1U;
128+
}
129+
130+
static uint32_t mcux_lpit_get_frequency(const struct device *dev)
131+
{
132+
const struct mcux_lpit_config *config = dev->config;
133+
uint32_t clock_rate;
134+
135+
if (clock_control_get_rate(config->clock_dev, config->clock_subsys, &clock_rate)) {
136+
LOG_ERR("Failed to get clock rate");
137+
return 0;
138+
}
139+
140+
return clock_rate;
141+
}
142+
143+
static void mcux_lpit_isr(const struct device *dev)
144+
{
145+
const struct mcux_lpit_config *config = dev->config;
146+
147+
LOG_DBG("lpit counter isr");
148+
149+
uint32_t flags = LPIT_GetStatusFlags(config->base);
150+
151+
for (int channel_index = 0; channel_index < config->num_channels; channel_index++) {
152+
153+
if ((flags & (1U << channel_index)) == 0) {
154+
continue;
155+
}
156+
157+
/* Clear interrupt flag */
158+
LPIT_ClearStatusFlags(config->base, (1U << channel_index));
159+
160+
struct mcux_lpit_channel_data *data =
161+
LPIT_CHANNEL_DATA(config->channels[channel_index]);
162+
163+
if (data->top_callback != NULL) {
164+
data->top_callback(dev, data->top_user_data);
165+
}
166+
}
167+
}
168+
169+
static int mcux_lpit_init(const struct device *dev)
170+
{
171+
const struct mcux_lpit_config *config = dev->config;
172+
lpit_config_t lpit_config;
173+
uint32_t clock_rate;
174+
175+
if (!device_is_ready(config->clock_dev)) {
176+
LOG_ERR("Clock control device not ready");
177+
return -ENODEV;
178+
}
179+
180+
LPIT_GetDefaultConfig(&lpit_config);
181+
lpit_config.enableRunInDebug = config->lpit_config.enableRunInDebug;
182+
lpit_config.enableRunInDoze = config->lpit_config.enableRunInDoze;
183+
184+
LPIT_Init(config->base, &lpit_config);
185+
186+
clock_rate = mcux_lpit_get_frequency(dev);
187+
188+
config->irq_config_func(dev);
189+
190+
for (int channel_index = 0U; channel_index < config->num_channels; channel_index++) {
191+
LPIT_SetTimerPeriod(config->base, channel_index, config->info.max_top_value);
192+
}
193+
194+
return 0;
195+
}
196+
197+
static DEVICE_API(counter, mcux_lpit_driver_api) = {
198+
.start = mcux_lpit_start,
199+
.stop = mcux_lpit_stop,
200+
.get_value = mcux_lpit_get_value,
201+
.set_top_value = mcux_lpit_set_top_value,
202+
.get_pending_int = mcux_lpit_get_pending_int,
203+
.get_top_value = mcux_lpit_get_top_value,
204+
.get_freq = mcux_lpit_get_frequency,
205+
};
206+
207+
/* Creates a device for a channel (needed for counter API) */
208+
#define MCUX_LPIT_CHANNEL_DEV_INIT(node, lpit_inst) \
209+
DEVICE_DT_DEFINE(node, NULL, NULL, \
210+
(void *)&mcux_lpit_##lpit_inst##_channel_datas[DT_REG_ADDR(node)], \
211+
&mcux_lpit_##lpit_inst##_config, POST_KERNEL, \
212+
CONFIG_COUNTER_INIT_PRIORITY, &mcux_lpit_driver_api);
213+
214+
/* Creates a decleration for each lpit channel */
215+
#define MCUX_LPIT_CHANNEL_DECLARATIONS(node) \
216+
static struct mcux_lpit_channel_data mcux_lpit_channel_data_##node;
217+
218+
/* Initializes an element of the channel data pointer array */
219+
#define MCUX_LPIT_INSERT_CHANNEL_INTO_ARRAY(node) \
220+
[DT_REG_ADDR(node)] = &mcux_lpit_channel_data_##node,
221+
222+
#define MCUX_LPIT_INSERT_CHANNEL_DEVICE_INTO_ARRAY(node) [DT_REG_ADDR(node)] = DEVICE_DT_GET(node),
223+
224+
#define MCUX_LPIT_IRQ_CONFIG_DECLARATIONS(n) \
225+
static void mcux_lpit_irq_config_func_##n(const struct device *dev) \
226+
{ \
227+
IRQ_CONNECT(DT_INST_IRQ_BY_IDX(n, 0, irq), DT_INST_IRQ_BY_IDX(n, 0, priority), \
228+
mcux_lpit_isr, DEVICE_DT_INST_GET(n), 0); \
229+
irq_enable(DT_INST_IRQN(n)); \
230+
};
231+
232+
#define MCUX_LPIT_SETUP_IRQ_CONFIG(n) MCUX_LPIT_IRQ_CONFIG_DECLARATIONS(n);
233+
#define MCUX_LPIT_SETUP_IRQ_ARRAY(ignored)
234+
235+
#define COUNTER_MCUX_LPIT_DEVICE_INIT(n) \
236+
\
237+
/* Setup the IRQ either for parent irq or per channel irq */ \
238+
MCUX_LPIT_SETUP_IRQ_CONFIG(n) \
239+
\
240+
/* Create channel declarations */ \
241+
DT_INST_FOREACH_CHILD_STATUS_OKAY(n, MCUX_LPIT_CHANNEL_DECLARATIONS) \
242+
\
243+
/* Array of channel devices */ \
244+
static struct mcux_lpit_channel_data \
245+
*const mcux_lpit_##n##_channel_datas[DT_INST_FOREACH_CHILD_SEP_VARGS( \
246+
n, DT_NODE_HAS_COMPAT, (+), nxp_lpit_channel)] = { \
247+
DT_INST_FOREACH_CHILD_STATUS_OKAY(n, \
248+
MCUX_LPIT_INSERT_CHANNEL_INTO_ARRAY)}; \
249+
\
250+
/* forward declaration */ \
251+
static const struct mcux_lpit_config mcux_lpit_##n##_config; \
252+
\
253+
/* Create all the channel/counter devices */ \
254+
DT_INST_FOREACH_CHILD_STATUS_OKAY_VARGS(n, MCUX_LPIT_CHANNEL_DEV_INIT, n) \
255+
\
256+
/* This channel device array is needed by the module device ISR */ \
257+
const struct device *const mcux_lpit_##n##_channels[DT_INST_FOREACH_CHILD_SEP_VARGS( \
258+
n, DT_NODE_HAS_COMPAT, (+), nxp_lpit_channel)] = { \
259+
DT_INST_FOREACH_CHILD_STATUS_OKAY(n, MCUX_LPIT_INSERT_CHANNEL_DEVICE_INTO_ARRAY)}; \
260+
\
261+
MCUX_LPIT_SETUP_IRQ_ARRAY(n) \
262+
\
263+
/* This config struct is shared by all the channels and parent device */ \
264+
static const struct mcux_lpit_config mcux_lpit_##n##_config = { \
265+
.info = \
266+
{ \
267+
.max_top_value = DT_INST_PROP(n, max_load_value), \
268+
.channels = 0U, \
269+
}, \
270+
.base = (LPIT_Type *)DT_INST_REG_ADDR(n), \
271+
.lpit_config = \
272+
{ \
273+
.enableRunInDebug = DT_INST_PROP(n, enable_run_in_debug), \
274+
.enableRunInDoze = DT_INST_PROP(n, enable_run_in_doze), \
275+
}, \
276+
.irq_config_func = mcux_lpit_irq_config_func_##n, \
277+
.num_channels = DT_INST_FOREACH_CHILD_SEP_VARGS(n, DT_NODE_HAS_COMPAT, (+), \
278+
nxp_lpit_channel), \
279+
.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
280+
.clock_subsys = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(n, name), \
281+
.data = mcux_lpit_##n##_channel_datas, \
282+
.channels = mcux_lpit_##n##_channels, \
283+
}; \
284+
\
285+
/* Init parent device in order to handle ISR and init. */ \
286+
DEVICE_DT_INST_DEFINE(n, &mcux_lpit_init, NULL, NULL, &mcux_lpit_##n##_config, \
287+
POST_KERNEL, CONFIG_COUNTER_INIT_PRIORITY, NULL);
288+
289+
DT_INST_FOREACH_STATUS_OKAY(COUNTER_MCUX_LPIT_DEVICE_INIT)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Copyright 2025 NXP
2+
# SPDX-License-Identifier Apache-2.0
3+
4+
description: |
5+
Child node for the Low Power Periodic Interrupt Timer node, intended
6+
for an individual timer channel
7+
8+
compatible: "nxp,lpit-channel"
9+
10+
include: base.yaml
11+
12+
properties:
13+
reg:
14+
required: true

dts/bindings/counter/nxp,lpit.yaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Copyright 2025 NXP
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: NXP Low Power Periodic Interrupt Timer (LPIT)
5+
6+
compatible: "nxp,lpit"
7+
8+
include: base.yaml
9+
10+
properties:
11+
reg:
12+
required: true
13+
14+
clocks:
15+
required: true
16+
17+
max-load-value:
18+
type: int
19+
required: true
20+
description: Maximum timeout ticks allowed for timer channels.
21+
22+
enable-run-in-debug:
23+
type: boolean
24+
description: Set 1 to allows timer channels to continue running when the chip enters Debug mode.
25+
26+
enable-run-in-doze:
27+
type: boolean
28+
description: Set 1 to allows timer channels to continue running when the chip enters Doze mode.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ set_variable_ifdef(CONFIG_WDT_MCUX_WDOG CONFIG_MCUX_COMPONENT_driver.wdo
8989
set_variable_ifdef(CONFIG_WDT_MCUX_WDOG32 CONFIG_MCUX_COMPONENT_driver.wdog32)
9090
set_variable_ifdef(CONFIG_COUNTER_MCUX_GPT CONFIG_MCUX_COMPONENT_driver.gpt)
9191
set_variable_ifdef(CONFIG_MCUX_GPT_TIMER CONFIG_MCUX_COMPONENT_driver.gpt)
92+
set_variable_ifdef(CONFIG_COUNTER_MCUX_LPIT CONFIG_MCUX_COMPONENT_driver.lpit)
9293
set_variable_ifdef(CONFIG_DISPLAY_MCUX_ELCDIF CONFIG_MCUX_COMPONENT_driver.elcdif)
9394
set_variable_ifdef(CONFIG_MCUX_PXP CONFIG_MCUX_COMPONENT_driver.pxp)
9495
set_variable_ifdef(CONFIG_LV_USE_GPU_NXP_PXP CONFIG_MCUX_COMPONENT_driver.pxp)

0 commit comments

Comments
 (0)