Skip to content

Commit 893bfc0

Browse files
tobyfirthnashif
authored andcommitted
drivers: counter: added ctimer driver for lpcexpresso55s69
Added shim driver for the CTIMERs for the lpcexpresso55s69 board. Fixes: #22705 Signed-off-by: Toby Firth <[email protected]>
1 parent 7355204 commit 893bfc0

File tree

21 files changed

+474
-7
lines changed

21 files changed

+474
-7
lines changed

boards/arm/lpcxpresso55s69/doc/index.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ features:
8282
+-----------+------------+-------------------------------------+
8383
| USB | on-chip | USB device |
8484
+-----------+------------+-------------------------------------+
85+
| COUNTER | on-chip | counter |
86+
+-----------+------------+-------------------------------------+
8587

8688
Targets available
8789
==================

boards/arm/lpcxpresso55s69/lpcxpresso55s69_cpu0.dts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,23 @@
109109
zephyr_udc0: &usbhs {
110110
status = "okay";
111111
};
112+
113+
&ctimer0 {
114+
status = "okay";
115+
};
116+
117+
&ctimer1 {
118+
status = "okay";
119+
};
120+
121+
&ctimer2 {
122+
status = "okay";
123+
};
124+
125+
&ctimer3 {
126+
status = "okay";
127+
};
128+
129+
&ctimer4 {
130+
status = "okay";
131+
};

boards/arm/lpcxpresso55s69/lpcxpresso55s69_cpu0.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ supported:
1919
- arduino_i2c
2020
- arduino_serial
2121
- arduino_spi
22+
- counter
2223
- gpio
2324
- i2c
2425
- spi

boards/arm/lpcxpresso55s69/lpcxpresso55s69_ns.dts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,23 @@
107107
zephyr_udc0: &usbhs {
108108
status = "okay";
109109
};
110+
111+
&ctimer0 {
112+
status = "okay";
113+
};
114+
115+
&ctimer1 {
116+
status = "okay";
117+
};
118+
119+
&ctimer2 {
120+
status = "okay";
121+
};
122+
123+
&ctimer3 {
124+
status = "okay";
125+
};
126+
127+
&ctimer4 {
128+
status = "okay";
129+
};

boards/arm/lpcxpresso55s69/lpcxpresso55s69_ns.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ toolchain:
1717
supported:
1818
- adc
1919
- arduino_spi
20+
- counter
2021
- dma
2122
- gpio
2223
- spi

drivers/clock_control/clock_control_mcux_syscon.c

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ static int mcux_lpc_syscon_clock_control_get_subsys_rate(
3434
{
3535
#if defined(CONFIG_I2C_MCUX_FLEXCOMM) || \
3636
defined(CONFIG_SPI_MCUX_FLEXCOMM) || \
37-
defined(CONFIG_UART_MCUX_FLEXCOMM)
37+
defined(CONFIG_UART_MCUX_FLEXCOMM) || \
38+
defined(CONFIG_COUNTER_MCUX_CTIMER)
3839

3940
uint32_t clock_name = (uint32_t) sub_system;
4041

@@ -79,6 +80,23 @@ static int mcux_lpc_syscon_clock_control_get_subsys_rate(
7980
case MCUX_USDHC2_CLK:
8081
*rate = CLOCK_GetSdioClkFreq(1);
8182
break;
83+
#endif
84+
#if defined(CONFIG_COUNTER_MCUX_CTIMER)
85+
case (MCUX_CTIMER0_CLK + MCUX_CTIMER_CLK_OFFSET):
86+
*rate = CLOCK_GetCTimerClkFreq(0);
87+
break;
88+
case (MCUX_CTIMER1_CLK + MCUX_CTIMER_CLK_OFFSET):
89+
*rate = CLOCK_GetCTimerClkFreq(1);
90+
break;
91+
case (MCUX_CTIMER2_CLK + MCUX_CTIMER_CLK_OFFSET):
92+
*rate = CLOCK_GetCTimerClkFreq(2);
93+
break;
94+
case (MCUX_CTIMER3_CLK + MCUX_CTIMER_CLK_OFFSET):
95+
*rate = CLOCK_GetCTimerClkFreq(3);
96+
break;
97+
case (MCUX_CTIMER4_CLK + MCUX_CTIMER_CLK_OFFSET):
98+
*rate = CLOCK_GetCTimerClkFreq(4);
99+
break;
82100
#endif
83101
}
84102
#endif

drivers/counter/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ zephyr_library_sources_ifdef(CONFIG_TIMER_TMR_CMSDK_APB timer_tmr_cmsdk_
66
zephyr_library_sources_ifdef(CONFIG_TIMER_DTMR_CMSDK_APB timer_dtmr_cmsdk_apb.c)
77
zephyr_library_sources_ifdef(CONFIG_COUNTER_GECKO_RTCC counter_gecko_rtcc.c)
88
zephyr_library_sources_ifdef(CONFIG_COUNTER_IMX_EPIT counter_imx_epit.c)
9+
zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_CTIMER counter_mcux_ctimer.c)
910
zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_RTC counter_mcux_rtc.c)
1011
zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_LPC_RTC counter_mcux_lpc_rtc.c)
1112
zephyr_library_sources_ifdef(CONFIG_COUNTER_NRF_TIMER counter_nrfx_timer.c)

drivers/counter/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,6 @@ source "drivers/counter/Kconfig.esp32"
5454

5555
source "drivers/counter/Kconfig.mcp7940n"
5656

57+
source "drivers/counter/Kconfig.mcux_ctimer"
58+
5759
endif # COUNTER
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# MCUXpresso SDK CTIMER
2+
3+
# Copyright (c) 2021 Toby firth
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
config COUNTER_MCUX_CTIMER
7+
bool "MCUX CTIMER driver"
8+
depends on HAS_MCUX_CTIMER
9+
help
10+
Enable support for MCUX CTIMER driver.
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
/*
2+
* Copyright (c) 2021, Toby Firth.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
#define DT_DRV_COMPAT nxp_lpc_ctimer
7+
8+
#include <drivers/counter.h>
9+
#include <fsl_ctimer.h>
10+
#include <logging/log.h>
11+
#include <drivers/clock_control.h>
12+
#include <dt-bindings/clock/mcux_lpc_syscon_clock.h>
13+
LOG_MODULE_REGISTER(mcux_ctimer, CONFIG_COUNTER_LOG_LEVEL);
14+
15+
#define NUM_CHANNELS 4
16+
17+
struct mcux_lpc_ctimer_channel_data {
18+
counter_alarm_callback_t alarm_callback;
19+
void *alarm_user_data;
20+
};
21+
22+
struct mcux_lpc_ctimer_data {
23+
struct mcux_lpc_ctimer_channel_data channels[NUM_CHANNELS];
24+
};
25+
26+
struct mcux_lpc_ctimer_config {
27+
struct counter_config_info info;
28+
CTIMER_Type *base;
29+
const struct device *clock_dev;
30+
clock_control_subsys_t clock_subsys;
31+
ctimer_timer_mode_t mode;
32+
ctimer_capture_channel_t input;
33+
uint32_t prescale;
34+
void (*irq_config_func)(const struct device *dev);
35+
};
36+
37+
static int mcux_lpc_ctimer_start(const struct device *dev)
38+
{
39+
const struct mcux_lpc_ctimer_config *config = dev->config;
40+
41+
CTIMER_StartTimer(config->base);
42+
43+
return 0;
44+
}
45+
46+
static int mcux_lpc_ctimer_stop(const struct device *dev)
47+
{
48+
const struct mcux_lpc_ctimer_config *config = dev->config;
49+
50+
CTIMER_StopTimer(config->base);
51+
52+
return 0;
53+
}
54+
55+
static uint32_t mcux_lpc_ctimer_read(CTIMER_Type *base)
56+
{
57+
return CTIMER_GetTimerCountValue(base);
58+
}
59+
60+
static int mcux_lpc_ctimer_get_value(const struct device *dev, uint32_t *ticks)
61+
{
62+
const struct mcux_lpc_ctimer_config *config = dev->config;
63+
*ticks = mcux_lpc_ctimer_read(config->base);
64+
return 0;
65+
}
66+
67+
static int mcux_lpc_ctimer_set_alarm(const struct device *dev, uint8_t chan_id,
68+
const struct counter_alarm_cfg *alarm_cfg)
69+
{
70+
const struct mcux_lpc_ctimer_config *config = dev->config;
71+
struct mcux_lpc_ctimer_data *data = dev->data;
72+
73+
uint32_t ticks = alarm_cfg->ticks;
74+
uint32_t current = mcux_lpc_ctimer_read(config->base);
75+
76+
if (data->channels[chan_id].alarm_callback != NULL) {
77+
LOG_ERR("channel already in use");
78+
return -EBUSY;
79+
}
80+
81+
if ((alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) == 0) {
82+
ticks += current;
83+
}
84+
85+
data->channels[chan_id].alarm_callback = alarm_cfg->callback;
86+
data->channels[chan_id].alarm_user_data = alarm_cfg->user_data;
87+
88+
ctimer_match_config_t match_config = { .matchValue = ticks,
89+
.enableCounterReset = false,
90+
.enableCounterStop = false,
91+
.outControl = kCTIMER_Output_NoAction,
92+
.outPinInitState = false,
93+
.enableInterrupt = true };
94+
95+
CTIMER_SetupMatch(config->base, chan_id, &match_config);
96+
97+
return 0;
98+
}
99+
100+
static int mcux_lpc_ctimer_cancel_alarm(const struct device *dev, uint8_t chan_id)
101+
{
102+
const struct mcux_lpc_ctimer_config *config = dev->config;
103+
struct mcux_lpc_ctimer_data *data = dev->data;
104+
105+
CTIMER_DisableInterrupts(config->base, (1 << chan_id));
106+
107+
data->channels[chan_id].alarm_callback = NULL;
108+
data->channels[chan_id].alarm_user_data = NULL;
109+
110+
return 0;
111+
}
112+
113+
static int mcux_lpc_ctimer_set_top_value(const struct device *dev,
114+
const struct counter_top_cfg *cfg)
115+
{
116+
ARG_UNUSED(dev);
117+
ARG_UNUSED(cfg);
118+
return -ENOTSUP;
119+
}
120+
121+
static uint32_t mcux_lpc_ctimer_get_pending_int(const struct device *dev)
122+
{
123+
const struct mcux_lpc_ctimer_config *config = dev->config;
124+
125+
return (CTIMER_GetStatusFlags(config->base) & 0xF) != 0;
126+
}
127+
128+
static uint32_t mcux_lpc_ctimer_get_top_value(const struct device *dev)
129+
{
130+
const struct mcux_lpc_ctimer_config *config = dev->config;
131+
132+
return config->info.max_top_value;
133+
}
134+
135+
static void mcux_lpc_ctimer_isr(const struct device *dev)
136+
{
137+
const struct mcux_lpc_ctimer_config *config = dev->config;
138+
struct mcux_lpc_ctimer_data *data = dev->data;
139+
140+
uint32_t interrupt_stat = CTIMER_GetStatusFlags(config->base);
141+
142+
CTIMER_ClearStatusFlags(config->base, interrupt_stat);
143+
144+
uint32_t ticks = mcux_lpc_ctimer_read(config->base);
145+
146+
for (uint8_t chan = 0; chan < NUM_CHANNELS; chan++) {
147+
uint8_t channel_mask = 0x01 << chan;
148+
149+
if (((interrupt_stat & channel_mask) != 0) &&
150+
(data->channels[chan].alarm_callback != NULL)) {
151+
counter_alarm_callback_t alarm_callback =
152+
data->channels[chan].alarm_callback;
153+
void *alarm_user_data = data->channels[chan].alarm_user_data;
154+
155+
data->channels[chan].alarm_callback = NULL;
156+
data->channels[chan].alarm_user_data = NULL;
157+
alarm_callback(dev, chan, ticks, alarm_user_data);
158+
}
159+
}
160+
}
161+
162+
static int mcux_lpc_ctimer_init(const struct device *dev)
163+
{
164+
/*
165+
* The frequency of the timer is not known at compile time so we need to
166+
* modify the timer's config in the init function at runtime when the
167+
* frequency is known.
168+
*/
169+
struct mcux_lpc_ctimer_config *config = (struct mcux_lpc_ctimer_config *)dev->config;
170+
struct mcux_lpc_ctimer_data *data = dev->data;
171+
172+
ctimer_config_t ctimer_config;
173+
174+
uint32_t clk_freq = 0;
175+
176+
if (clock_control_get_rate(config->clock_dev, config->clock_subsys,
177+
&clk_freq)) {
178+
LOG_ERR("unable to get clock frequency");
179+
return -EINVAL;
180+
}
181+
182+
/* prescale increments when the prescale counter is 0 so if prescale is 1
183+
* the counter is incremented every 2 cycles of the clock so will actually
184+
* divide by 2 hence the addition of 1 to the value here.
185+
*/
186+
uint32_t freq = clk_freq / (config->prescale + 1);
187+
188+
config->info.freq = freq;
189+
190+
for (uint8_t chan = 0; chan < NUM_CHANNELS; chan++) {
191+
data->channels[chan].alarm_callback = NULL;
192+
data->channels[chan].alarm_user_data = NULL;
193+
}
194+
195+
CTIMER_GetDefaultConfig(&ctimer_config);
196+
197+
ctimer_config.mode = config->mode;
198+
ctimer_config.input = config->input;
199+
ctimer_config.prescale = config->prescale;
200+
201+
CTIMER_Init(config->base, &ctimer_config);
202+
203+
config->irq_config_func(dev);
204+
205+
return 0;
206+
}
207+
208+
static const struct counter_driver_api mcux_ctimer_driver_api = {
209+
.start = mcux_lpc_ctimer_start,
210+
.stop = mcux_lpc_ctimer_stop,
211+
.get_value = mcux_lpc_ctimer_get_value,
212+
.set_alarm = mcux_lpc_ctimer_set_alarm,
213+
.cancel_alarm = mcux_lpc_ctimer_cancel_alarm,
214+
.set_top_value = mcux_lpc_ctimer_set_top_value,
215+
.get_pending_int = mcux_lpc_ctimer_get_pending_int,
216+
.get_top_value = mcux_lpc_ctimer_get_top_value,
217+
};
218+
219+
#define CTIMER_CLOCK_SOURCE(id) TO_CTIMER_CLOCK_SOURCE(id, DT_INST_PROP(id, clk_source))
220+
#define TO_CTIMER_CLOCK_SOURCE(id, val) MUX_A(CM_CTIMERCLKSEL##id, val)
221+
222+
#define COUNTER_LPC_CTIMER_DEVICE(id) \
223+
static void mcux_lpc_ctimer_irq_config_##id(const struct device *dev); \
224+
static struct mcux_lpc_ctimer_config mcux_lpc_ctimer_config_##id = { \
225+
.info = { \
226+
.max_top_value = UINT32_MAX, \
227+
.freq = 1, \
228+
.flags = COUNTER_CONFIG_INFO_COUNT_UP, \
229+
.channels = NUM_CHANNELS, \
230+
},\
231+
.base = (CTIMER_Type *)DT_INST_REG_ADDR(id), \
232+
.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(id)), \
233+
.clock_subsys = \
234+
(clock_control_subsys_t)(DT_INST_CLOCKS_CELL(id, name) + MCUX_CTIMER_CLK_OFFSET),\
235+
.mode = DT_INST_PROP(id, mode), \
236+
.input = DT_INST_PROP(id, input), \
237+
.prescale = DT_INST_PROP(id, prescale), \
238+
.irq_config_func = mcux_lpc_ctimer_irq_config_##id, \
239+
}; \
240+
static struct mcux_lpc_ctimer_data mcux_lpc_ctimer_data_##id; \
241+
DEVICE_DT_INST_DEFINE(id, &mcux_lpc_ctimer_init, NULL, &mcux_lpc_ctimer_data_##id, \
242+
&mcux_lpc_ctimer_config_##id, POST_KERNEL, \
243+
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &mcux_ctimer_driver_api); \
244+
static void mcux_lpc_ctimer_irq_config_##id(const struct device *dev) \
245+
{ \
246+
IRQ_CONNECT(DT_INST_IRQN(id), DT_INST_IRQ(id, priority), mcux_lpc_ctimer_isr, \
247+
DEVICE_DT_INST_GET(id), 0); \
248+
irq_enable(DT_INST_IRQN(id)); \
249+
}
250+
251+
DT_INST_FOREACH_STATUS_OKAY(COUNTER_LPC_CTIMER_DEVICE)

0 commit comments

Comments
 (0)