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 <zephyr/drivers/counter.h>
9 #include <fsl_ctimer.h>
10 #include <zephyr/logging/log.h>
11 #include <zephyr/drivers/clock_control.h>
12 #include <zephyr/dt-bindings/clock/mcux_lpc_syscon_clock.h>
13 #include <zephyr/irq.h>
14
15 LOG_MODULE_REGISTER(mcux_ctimer, CONFIG_COUNTER_LOG_LEVEL);
16
17 #ifdef CONFIG_COUNTER_MCUX_CTIMER_RESERVE_CHANNEL_FOR_SETTOP
18 /* One of the CTimer channels is reserved to implement set_top_value API */
19 #define NUM_CHANNELS 3
20 #else
21 #define NUM_CHANNELS 4
22 #endif
23
24 struct mcux_lpc_ctimer_channel_data {
25 counter_alarm_callback_t alarm_callback;
26 void *alarm_user_data;
27 };
28
29 struct mcux_lpc_ctimer_data {
30 struct mcux_lpc_ctimer_channel_data channels[NUM_CHANNELS];
31 counter_top_callback_t top_callback;
32 void *top_user_data;
33 };
34
35 struct mcux_lpc_ctimer_config {
36 struct counter_config_info info;
37 CTIMER_Type *base;
38 const struct device *clock_dev;
39 clock_control_subsys_t clock_subsys;
40 ctimer_timer_mode_t mode;
41 ctimer_capture_channel_t input;
42 uint32_t prescale;
43 void (*irq_config_func)(const struct device *dev);
44 };
45
mcux_lpc_ctimer_start(const struct device * dev)46 static int mcux_lpc_ctimer_start(const struct device *dev)
47 {
48 const struct mcux_lpc_ctimer_config *config = dev->config;
49
50 CTIMER_StartTimer(config->base);
51
52 return 0;
53 }
54
mcux_lpc_ctimer_stop(const struct device * dev)55 static int mcux_lpc_ctimer_stop(const struct device *dev)
56 {
57 const struct mcux_lpc_ctimer_config *config = dev->config;
58
59 CTIMER_StopTimer(config->base);
60
61 return 0;
62 }
63
mcux_lpc_ctimer_read(CTIMER_Type * base)64 static uint32_t mcux_lpc_ctimer_read(CTIMER_Type *base)
65 {
66 return CTIMER_GetTimerCountValue(base);
67 }
68
mcux_lpc_ctimer_get_value(const struct device * dev,uint32_t * ticks)69 static int mcux_lpc_ctimer_get_value(const struct device *dev, uint32_t *ticks)
70 {
71 const struct mcux_lpc_ctimer_config *config = dev->config;
72 *ticks = mcux_lpc_ctimer_read(config->base);
73 return 0;
74 }
75
mcux_lpc_ctimer_get_top_value(const struct device * dev)76 static uint32_t mcux_lpc_ctimer_get_top_value(const struct device *dev)
77 {
78 const struct mcux_lpc_ctimer_config *config = dev->config;
79
80 #ifdef CONFIG_COUNTER_MCUX_CTIMER_RESERVE_CHANNEL_FOR_SETTOP
81 CTIMER_Type *base = config->base;
82
83 /* Return the top value if it has been set, else return the max top value */
84 if (base->MR[NUM_CHANNELS] != 0) {
85 return base->MR[NUM_CHANNELS];
86 } else {
87 return config->info.max_top_value;
88 }
89 #else
90 return config->info.max_top_value;
91 #endif
92 }
93
mcux_lpc_ctimer_set_alarm(const struct device * dev,uint8_t chan_id,const struct counter_alarm_cfg * alarm_cfg)94 static int mcux_lpc_ctimer_set_alarm(const struct device *dev, uint8_t chan_id,
95 const struct counter_alarm_cfg *alarm_cfg)
96 {
97 const struct mcux_lpc_ctimer_config *config = dev->config;
98 struct mcux_lpc_ctimer_data *data = dev->data;
99 uint32_t ticks = alarm_cfg->ticks;
100 uint32_t current = mcux_lpc_ctimer_read(config->base);
101 uint32_t top = mcux_lpc_ctimer_get_top_value(dev);
102
103 if (alarm_cfg->ticks > top) {
104 return -EINVAL;
105 }
106
107 if (data->channels[chan_id].alarm_callback != NULL) {
108 LOG_ERR("channel already in use");
109 return -EBUSY;
110 }
111
112 if ((alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) == 0) {
113 ticks += current;
114 if (ticks > top) {
115 ticks %= top;
116 }
117 }
118
119 data->channels[chan_id].alarm_callback = alarm_cfg->callback;
120 data->channels[chan_id].alarm_user_data = alarm_cfg->user_data;
121
122 ctimer_match_config_t match_config = { .matchValue = ticks,
123 .enableCounterReset = false,
124 .enableCounterStop = false,
125 .outControl = kCTIMER_Output_NoAction,
126 .outPinInitState = false,
127 .enableInterrupt = true };
128
129 CTIMER_SetupMatch(config->base, chan_id, &match_config);
130
131 return 0;
132 }
133
mcux_lpc_ctimer_cancel_alarm(const struct device * dev,uint8_t chan_id)134 static int mcux_lpc_ctimer_cancel_alarm(const struct device *dev, uint8_t chan_id)
135 {
136 const struct mcux_lpc_ctimer_config *config = dev->config;
137 struct mcux_lpc_ctimer_data *data = dev->data;
138
139 CTIMER_DisableInterrupts(config->base, (1 << chan_id));
140
141 data->channels[chan_id].alarm_callback = NULL;
142 data->channels[chan_id].alarm_user_data = NULL;
143
144 return 0;
145 }
146
mcux_lpc_ctimer_set_top_value(const struct device * dev,const struct counter_top_cfg * cfg)147 static int mcux_lpc_ctimer_set_top_value(const struct device *dev,
148 const struct counter_top_cfg *cfg)
149 {
150 const struct mcux_lpc_ctimer_config *config = dev->config;
151 struct mcux_lpc_ctimer_data *data = dev->data;
152
153 #ifndef CONFIG_COUNTER_MCUX_CTIMER_RESERVE_CHANNEL_FOR_SETTOP
154 /* Only allow max value when we do not reserve a ctimer channel for setting top value */
155 if (cfg->ticks != config->info.max_top_value) {
156 LOG_ERR("Wrap can only be set to 0x%x",
157 config->info.max_top_value);
158 return -ENOTSUP;
159 }
160 #endif
161
162 data->top_callback = cfg->callback;
163 data->top_user_data = cfg->user_data;
164
165 if (!(cfg->flags & COUNTER_TOP_CFG_DONT_RESET)) {
166 CTIMER_Reset(config->base);
167 } else if (mcux_lpc_ctimer_read(config->base) >= cfg->ticks) {
168 if (cfg->flags & COUNTER_TOP_CFG_RESET_WHEN_LATE) {
169 CTIMER_Reset(config->base);
170 }
171 return -ETIME;
172 }
173
174 #ifdef CONFIG_COUNTER_MCUX_CTIMER_RESERVE_CHANNEL_FOR_SETTOP
175 ctimer_match_config_t match_config = { .matchValue = cfg->ticks,
176 .enableCounterReset = true,
177 .enableCounterStop = false,
178 .outControl = kCTIMER_Output_NoAction,
179 .outPinInitState = false,
180 .enableInterrupt = true };
181
182 CTIMER_SetupMatch(config->base, NUM_CHANNELS, &match_config);
183 #endif
184
185 return 0;
186 }
187
mcux_lpc_ctimer_get_pending_int(const struct device * dev)188 static uint32_t mcux_lpc_ctimer_get_pending_int(const struct device *dev)
189 {
190 const struct mcux_lpc_ctimer_config *config = dev->config;
191
192 return (CTIMER_GetStatusFlags(config->base) & 0xF) != 0;
193 }
194
mcux_lpc_ctimer_get_freq(const struct device * dev)195 static uint32_t mcux_lpc_ctimer_get_freq(const struct device *dev)
196 {
197 /*
198 * The frequency of the timer is not known at compile time so we need to
199 * calculate at runtime when the frequency is known.
200 */
201 const struct mcux_lpc_ctimer_config *config = dev->config;
202
203 uint32_t clk_freq = 0;
204
205 if (clock_control_get_rate(config->clock_dev, config->clock_subsys,
206 &clk_freq)) {
207 LOG_ERR("unable to get clock frequency");
208 return 0;
209 }
210
211 /* prescale increments when the prescale counter is 0 so if prescale is 1
212 * the counter is incremented every 2 cycles of the clock so will actually
213 * divide by 2 hence the addition of 1 to the value here.
214 */
215 return (clk_freq / (config->prescale + 1));
216 }
217
mcux_lpc_ctimer_isr(const struct device * dev)218 static void mcux_lpc_ctimer_isr(const struct device *dev)
219 {
220 const struct mcux_lpc_ctimer_config *config = dev->config;
221 struct mcux_lpc_ctimer_data *data = dev->data;
222
223 uint32_t interrupt_stat = CTIMER_GetStatusFlags(config->base);
224
225 CTIMER_ClearStatusFlags(config->base, interrupt_stat);
226
227 uint32_t ticks = mcux_lpc_ctimer_read(config->base);
228
229 for (uint8_t chan = 0; chan < NUM_CHANNELS; chan++) {
230 uint8_t channel_mask = 0x01 << chan;
231
232 if (((interrupt_stat & channel_mask) != 0) &&
233 (data->channels[chan].alarm_callback != NULL)) {
234 counter_alarm_callback_t alarm_callback =
235 data->channels[chan].alarm_callback;
236 void *alarm_user_data = data->channels[chan].alarm_user_data;
237
238 data->channels[chan].alarm_callback = NULL;
239 data->channels[chan].alarm_user_data = NULL;
240 alarm_callback(dev, chan, ticks, alarm_user_data);
241 }
242 }
243
244 #ifdef CONFIG_COUNTER_MCUX_CTIMER_RESERVE_CHANNEL_FOR_SETTOP
245 if (((interrupt_stat & (0x01 << NUM_CHANNELS)) != 0) && data->top_callback) {
246 data->top_callback(dev, data->top_user_data);
247 }
248 #endif
249 }
250
mcux_lpc_ctimer_init(const struct device * dev)251 static int mcux_lpc_ctimer_init(const struct device *dev)
252 {
253 const struct mcux_lpc_ctimer_config *config = dev->config;
254 struct mcux_lpc_ctimer_data *data = dev->data;
255 ctimer_config_t ctimer_config;
256
257 if (!device_is_ready(config->clock_dev)) {
258 LOG_ERR("clock control device not ready");
259 return -ENODEV;
260 }
261
262 for (uint8_t chan = 0; chan < NUM_CHANNELS; chan++) {
263 data->channels[chan].alarm_callback = NULL;
264 data->channels[chan].alarm_user_data = NULL;
265 }
266
267 CTIMER_GetDefaultConfig(&ctimer_config);
268
269 ctimer_config.mode = config->mode;
270 ctimer_config.input = config->input;
271 ctimer_config.prescale = config->prescale;
272
273 CTIMER_Init(config->base, &ctimer_config);
274
275 config->irq_config_func(dev);
276
277 return 0;
278 }
279
280 static const struct counter_driver_api mcux_ctimer_driver_api = {
281 .start = mcux_lpc_ctimer_start,
282 .stop = mcux_lpc_ctimer_stop,
283 .get_value = mcux_lpc_ctimer_get_value,
284 .set_alarm = mcux_lpc_ctimer_set_alarm,
285 .cancel_alarm = mcux_lpc_ctimer_cancel_alarm,
286 .set_top_value = mcux_lpc_ctimer_set_top_value,
287 .get_pending_int = mcux_lpc_ctimer_get_pending_int,
288 .get_top_value = mcux_lpc_ctimer_get_top_value,
289 .get_freq = mcux_lpc_ctimer_get_freq,
290 };
291
292 #define COUNTER_LPC_CTIMER_DEVICE(id) \
293 static void mcux_lpc_ctimer_irq_config_##id(const struct device *dev); \
294 static struct mcux_lpc_ctimer_config mcux_lpc_ctimer_config_##id = { \
295 .info = { \
296 .max_top_value = UINT32_MAX, \
297 .flags = COUNTER_CONFIG_INFO_COUNT_UP, \
298 .channels = NUM_CHANNELS, \
299 },\
300 .base = (CTIMER_Type *)DT_INST_REG_ADDR(id), \
301 .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(id)), \
302 .clock_subsys = \
303 (clock_control_subsys_t)(DT_INST_CLOCKS_CELL(id, name) + MCUX_CTIMER_CLK_OFFSET),\
304 .mode = DT_INST_PROP(id, mode), \
305 .input = DT_INST_PROP(id, input), \
306 .prescale = DT_INST_PROP(id, prescale), \
307 .irq_config_func = mcux_lpc_ctimer_irq_config_##id, \
308 }; \
309 static struct mcux_lpc_ctimer_data mcux_lpc_ctimer_data_##id; \
310 DEVICE_DT_INST_DEFINE(id, &mcux_lpc_ctimer_init, NULL, &mcux_lpc_ctimer_data_##id, \
311 &mcux_lpc_ctimer_config_##id, POST_KERNEL, \
312 CONFIG_COUNTER_INIT_PRIORITY, &mcux_ctimer_driver_api); \
313 static void mcux_lpc_ctimer_irq_config_##id(const struct device *dev) \
314 { \
315 IRQ_CONNECT(DT_INST_IRQN(id), DT_INST_IRQ(id, priority), mcux_lpc_ctimer_isr, \
316 DEVICE_DT_INST_GET(id), 0); \
317 irq_enable(DT_INST_IRQN(id)); \
318 }
319
320 DT_INST_FOREACH_STATUS_OKAY(COUNTER_LPC_CTIMER_DEVICE)
321