1 /*
2  * Copyright (c) 2022 KT-Elektronik, Klaucke und Partner GmbH
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 /**
7  * @file
8  *
9  * Counter driver for the Quad Timer through the MCUxpresso SDK. Based mainly on counter_mcux_gpt.c
10  *
11  * Each quad timer module has four channels (0-3) that can operate independently, but the Zephyr
12  * counter-API does not support starting or stopping different channels independently. Hence, each
13  * channel is represented as an independent counter device.
14  */
15 
16 #include <zephyr/drivers/counter.h>
17 #include <zephyr/drivers/clock_control.h>
18 #include <zephyr/irq.h>
19 #include <fsl_qtmr.h>
20 #include <zephyr/logging/log.h>
21 #include <zephyr/sys/barrier.h>
22 
23 LOG_MODULE_REGISTER(mcux_qtmr, CONFIG_COUNTER_LOG_LEVEL);
24 
25 struct mcux_qtmr_config {
26 	/* info must be first element */
27 	struct counter_config_info info;
28 	const struct device *clock_dev;
29 	clock_control_subsys_t clock_subsys;
30 	TMR_Type *base;
31 	clock_name_t clock_source;
32 	qtmr_channel_selection_t channel;
33 	qtmr_config_t qtmr_config;
34 	qtmr_counting_mode_t mode;
35 };
36 
37 struct mcux_qtmr_data {
38 	counter_alarm_callback_t alarm_callback;
39 	counter_top_callback_t top_callback;
40 	void *alarm_user_data;
41 	void *top_user_data;
42 	qtmr_status_flags_t interrupt_mask;
43 	uint32_t freq;
44 };
45 
46 /* Only one interrupt per QTMR module. Each of which has four timers. */
47 #define DT_DRV_COMPAT nxp_imx_qtmr
48 
49 /**
50  * @brief ISR for a specific timer channel
51  *
52  * @param dev	timer channel device
53  */
mcux_qtmr_timer_handler(const struct device * dev,uint32_t status)54 void mcux_qtmr_timer_handler(const struct device *dev, uint32_t status)
55 {
56 	const struct mcux_qtmr_config *config = dev->config;
57 	struct mcux_qtmr_data *data = dev->data;
58 	uint32_t current = QTMR_GetCurrentTimerCount(config->base, config->channel);
59 
60 	QTMR_ClearStatusFlags(config->base, config->channel, status);
61 	barrier_dsync_fence_full();
62 
63 	if ((status & kQTMR_Compare1Flag) && data->alarm_callback) {
64 		QTMR_DisableInterrupts(config->base, config->channel,
65 			kQTMR_Compare1InterruptEnable);
66 		data->interrupt_mask &= ~kQTMR_Compare1InterruptEnable;
67 		counter_alarm_callback_t alarm_cb = data->alarm_callback;
68 
69 		data->alarm_callback = NULL;
70 		alarm_cb(dev, config->channel, current, data->alarm_user_data);
71 	}
72 
73 	if ((status & kQTMR_OverflowFlag) && data->top_callback) {
74 		data->top_callback(dev, data->top_user_data);
75 	}
76 }
77 
78 /**
79  * @brief ISR for the QTMR
80  *
81  * @param timers	array containing the counter devices for each channel of the timer module
82  */
mcux_qtmr_isr(const struct device * timers[])83 static void mcux_qtmr_isr(const struct device *timers[])
84 {
85 	/* the interrupt can be triggered by any of the four channels of the QTMR. Check status
86 	 * of all channels and trigger the ISR for the channel(s) that has/have triggered the
87 	 * interrupt.
88 	 */
89 	for (qtmr_channel_selection_t ch = kQTMR_Channel_0; ch <= kQTMR_Channel_3 ; ch++) {
90 		if (timers[ch] != NULL) {
91 			const struct mcux_qtmr_config *config = timers[ch]->config;
92 			struct mcux_qtmr_data *data = timers[ch]->data;
93 
94 			uint32_t channel_status = QTMR_GetStatus(config->base, ch);
95 
96 			if ((channel_status & data->interrupt_mask) != 0) {
97 				mcux_qtmr_timer_handler(timers[ch], channel_status);
98 			}
99 		}
100 	}
101 }
102 
103 #define INIT_TIMER(node_id) [DT_PROP(node_id, channel)] = DEVICE_DT_GET(node_id),
104 
105 #define QTMR_DEVICE_INIT_MCUX(n)							\
106 	static const struct device *const timers_##n[4] = {				\
107 		DT_FOREACH_CHILD_STATUS_OKAY(DT_DRV_INST(n), INIT_TIMER)		\
108 	};										\
109 	static int init_irq_##n(void)							\
110 	{										\
111 		IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), mcux_qtmr_isr,	\
112 				timers_##n, 0);						\
113 		irq_enable(DT_INST_IRQN(n));						\
114 		return 0;								\
115 	}										\
116 											\
117 	SYS_INIT(init_irq_##n, POST_KERNEL, CONFIG_COUNTER_INIT_PRIORITY);
118 
DT_INST_FOREACH_STATUS_OKAY(QTMR_DEVICE_INIT_MCUX)119 DT_INST_FOREACH_STATUS_OKAY(QTMR_DEVICE_INIT_MCUX)
120 
121 #undef DT_DRV_COMPAT
122 #define DT_DRV_COMPAT nxp_imx_tmr
123 
124 static int mcux_qtmr_start(const struct device *dev)
125 {
126 	const struct mcux_qtmr_config *config = dev->config;
127 
128 	QTMR_StartTimer(config->base, config->channel, config->mode);
129 
130 	return 0;
131 }
132 
mcux_qtmr_stop(const struct device * dev)133 static int mcux_qtmr_stop(const struct device *dev)
134 {
135 	const struct mcux_qtmr_config *config = dev->config;
136 
137 	QTMR_StopTimer(config->base, config->channel);
138 
139 	return 0;
140 }
141 
mcux_qtmr_get_value(const struct device * dev,uint32_t * ticks)142 static int mcux_qtmr_get_value(const struct device *dev, uint32_t *ticks)
143 {
144 	const struct mcux_qtmr_config *config = dev->config;
145 
146 	*ticks = QTMR_GetCurrentTimerCount(config->base, config->channel);
147 	return 0;
148 }
149 
mcux_qtmr_set_alarm(const struct device * dev,uint8_t chan_id,const struct counter_alarm_cfg * alarm_cfg)150 static int mcux_qtmr_set_alarm(const struct device *dev, uint8_t chan_id,
151 			      const struct counter_alarm_cfg *alarm_cfg)
152 {
153 	const struct mcux_qtmr_config *config = dev->config;
154 	struct mcux_qtmr_data *data = dev->data;
155 	uint32_t current;
156 	uint32_t ticks;
157 
158 	if (chan_id != 0) {
159 		LOG_ERR("Invalid channel id");
160 		return -EINVAL;
161 	}
162 
163 	if (data->alarm_callback) {
164 		return -EBUSY;
165 	}
166 
167 	data->alarm_callback = alarm_cfg->callback;
168 	data->alarm_user_data = alarm_cfg->user_data;
169 
170 	current = QTMR_GetCurrentTimerCount(config->base, config->channel);
171 	ticks = alarm_cfg->ticks;
172 
173 	if ((alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) == 0) {
174 		ticks += current;
175 	}
176 
177 	/* this timer always counts up. */
178 	config->base->CHANNEL[config->channel].COMP1 = ticks;
179 
180 	data->interrupt_mask |= kQTMR_Compare1InterruptEnable;
181 	QTMR_EnableInterrupts(config->base, config->channel, data->interrupt_mask);
182 
183 	return 0;
184 }
185 
mcux_qtmr_cancel_alarm(const struct device * dev,uint8_t chan_id)186 static int mcux_qtmr_cancel_alarm(const struct device *dev, uint8_t chan_id)
187 {
188 	const struct mcux_qtmr_config *config = dev->config;
189 	struct mcux_qtmr_data *data = dev->data;
190 
191 	if (chan_id != 0) {
192 		LOG_ERR("Invalid channel id");
193 		return -EINVAL;
194 	}
195 
196 	QTMR_DisableInterrupts(config->base, config->channel, data->interrupt_mask);
197 	data->interrupt_mask &= ~kQTMR_Compare1InterruptEnable;
198 	data->alarm_callback = NULL;
199 
200 	return 0;
201 }
202 
mcux_qtmr_get_pending_int(const struct device * dev)203 static uint32_t mcux_qtmr_get_pending_int(const struct device *dev)
204 {
205 	const struct mcux_qtmr_config *config = dev->config;
206 
207 	return QTMR_GetStatus(config->base, config->channel);
208 }
209 
mcux_qtmr_set_top_value(const struct device * dev,const struct counter_top_cfg * cfg)210 static int mcux_qtmr_set_top_value(const struct device *dev,
211 				  const struct counter_top_cfg *cfg)
212 {
213 	const struct mcux_qtmr_config *config = dev->config;
214 	struct mcux_qtmr_data *data = dev->data;
215 
216 	if (cfg->ticks != config->info.max_top_value) {
217 		LOG_ERR("Wrap can only be set to 0x%x",
218 			config->info.max_top_value);
219 		return -ENOTSUP;
220 	}
221 
222 	if ((cfg->flags & COUNTER_TOP_CFG_DONT_RESET) == 0) {
223 		if ((config->base->CHANNEL[config->channel].CTRL & TMR_CTRL_DIR_MASK) != 0U) {
224 			/* counting down, reset to UINT16MAX */
225 			config->base->CHANNEL[config->channel].CNTR = UINT16_MAX;
226 		} else {
227 			/* counting up, reset to 0 */
228 			config->base->CHANNEL[config->channel].CNTR = 0;
229 		}
230 	}
231 
232 	if (cfg->callback != NULL) {
233 		data->top_callback = cfg->callback;
234 		data->top_user_data = cfg->user_data;
235 
236 		data->interrupt_mask |= kQTMR_OverflowInterruptEnable;
237 		QTMR_EnableInterrupts(config->base, config->channel, kQTMR_OverflowInterruptEnable);
238 	}
239 
240 	return 0;
241 }
242 
mcux_qtmr_get_top_value(const struct device * dev)243 static uint32_t mcux_qtmr_get_top_value(const struct device *dev)
244 {
245 	const struct mcux_qtmr_config *config = dev->config;
246 
247 	return config->info.max_top_value;
248 }
249 
mcux_qtmr_get_freq(const struct device * dev)250 static uint32_t mcux_qtmr_get_freq(const struct device *dev)
251 {
252 	struct mcux_qtmr_data *data = dev->data;
253 
254 	return data->freq;
255 }
256 
257 /**
258  * @brief look up table for dividers when using internal clock sources kQTMR_ClockDivide_1 to
259  * kQTMR_ClockDivide_128
260  */
261 static const uint8_t qtmr_primary_source_divider[] = {1, 2, 4, 8, 16, 32, 64, 128};
262 
mcux_qtmr_init(const struct device * dev)263 static int mcux_qtmr_init(const struct device *dev)
264 {
265 	const struct mcux_qtmr_config *config = dev->config;
266 	struct mcux_qtmr_data *data = dev->data;
267 
268 	if (config->qtmr_config.primarySource < kQTMR_ClockDivide_1) {
269 		/* for external sources, use the value from the dts (if given) */
270 		data->freq = config->info.freq;
271 	} else {
272 		/* bus clock with divider */
273 		if (!device_is_ready(config->clock_dev)) {
274 			LOG_ERR("clock control device not ready");
275 			return -ENODEV;
276 		}
277 
278 		if (clock_control_get_rate(config->clock_dev, config->clock_subsys,
279 					&data->freq)) {
280 			return -EINVAL;
281 		}
282 
283 		data->freq /= qtmr_primary_source_divider[config->qtmr_config.primarySource -
284 								kQTMR_ClockDivide_1];
285 	}
286 
287 	QTMR_Init(config->base, config->channel, &config->qtmr_config);
288 
289 	return 0;
290 }
291 
292 static const struct counter_driver_api mcux_qtmr_driver_api = {
293 	.start = mcux_qtmr_start,
294 	.stop = mcux_qtmr_stop,
295 	.get_value = mcux_qtmr_get_value,
296 	.set_alarm = mcux_qtmr_set_alarm,
297 	.cancel_alarm = mcux_qtmr_cancel_alarm,
298 	.set_top_value = mcux_qtmr_set_top_value,
299 	.get_pending_int = mcux_qtmr_get_pending_int,
300 	.get_top_value = mcux_qtmr_get_top_value,
301 	.get_freq = mcux_qtmr_get_freq,
302 };
303 
304 #define TMR_DEVICE_INIT_MCUX(n)									\
305 	static struct mcux_qtmr_data mcux_qtmr_data_ ## n;					\
306 												\
307 	static const struct mcux_qtmr_config mcux_qtmr_config_ ## n = {				\
308 		.base = (void *)DT_REG_ADDR(DT_INST_PARENT(n)),					\
309 		.clock_dev = DEVICE_DT_GET(DT_CLOCKS_CTLR(DT_INST_PARENT(n))),			\
310 		.clock_subsys =									\
311 			(clock_control_subsys_t)DT_CLOCKS_CELL(DT_INST_PARENT(n), name),	\
312 		.info = {									\
313 			.max_top_value = UINT16_MAX,						\
314 			.freq = DT_INST_PROP_OR(n, freq, 0),					\
315 			.channels = 1,								\
316 			.flags = COUNTER_CONFIG_INFO_COUNT_UP,					\
317 		},										\
318 		.channel = DT_INST_PROP(n, channel),						\
319 		.qtmr_config = {								\
320 			.debugMode = kQTMR_RunNormalInDebug,					\
321 			.enableExternalForce = false,						\
322 			.enableMasterMode = false,						\
323 			.faultFilterCount = DT_INST_PROP_OR(n, filter_count, 0),		\
324 			.faultFilterPeriod = DT_INST_PROP_OR(n, filter_count, 0),		\
325 			.primarySource = DT_INST_ENUM_IDX(n, primary_source),			\
326 			.secondarySource = DT_INST_ENUM_IDX_OR(n, secondary_source, 0),		\
327 		},										\
328 		.mode = DT_INST_ENUM_IDX(n, mode),						\
329 	};											\
330 												\
331 	DEVICE_DT_INST_DEFINE(n,								\
332 			    mcux_qtmr_init,							\
333 			    NULL,								\
334 			    &mcux_qtmr_data_ ## n,						\
335 			    &mcux_qtmr_config_ ## n,						\
336 			    POST_KERNEL,							\
337 			    CONFIG_COUNTER_INIT_PRIORITY,					\
338 			    &mcux_qtmr_driver_api);						\
339 
340 DT_INST_FOREACH_STATUS_OKAY(TMR_DEVICE_INIT_MCUX)
341