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