1 /*
2  * Copyright 2020,2023-2024 NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nxp_pit
8 
9 #include <zephyr/drivers/counter.h>
10 #include <zephyr/drivers/clock_control.h>
11 #include <zephyr/irq.h>
12 #include <fsl_pit.h>
13 
14 #define LOG_MODULE_NAME counter_pit
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_COUNTER_LOG_LEVEL);
17 
18 /* Device holds a pointer to pointer to data */
19 #define PIT_CHANNEL_DATA(dev)					\
20 	(*(struct nxp_pit_channel_data *const *const)dev->data)
21 
22 /* Device config->data is an array of data pointers ordered by channel number,
23  * dev->data is a pointer to one of these pointers in that array,
24  * so the value of the dev->data - dev->config->data is the channel index
25  */
26 #define PIT_CHANNEL_ID(dev)					\
27 	(((struct nxp_pit_channel_data *const *)dev->data) -	\
28 	((const struct nxp_pit_config *)dev->config)->data)
29 
30 
31 struct nxp_pit_channel_data {
32 	uint32_t top;
33 	counter_top_callback_t top_callback;
34 	void *top_user_data;
35 };
36 
37 
38 struct nxp_pit_config {
39 	struct counter_config_info info;
40 	PIT_Type *base;
41 	bool enableRunInDebug;
42 	int num_channels;
43 #if DT_NODE_HAS_PROP(DT_COMPAT_GET_ANY_STATUS_OKAY(nxp_pit), interrupts)
44 	void (*irq_config_func)(const struct device *dev);
45 #else
46 	void (**irq_config_func)(const struct device *dev);
47 #endif
48 	const struct device *clock_dev;
49 	clock_control_subsys_t clock_subsys;
50 	struct nxp_pit_channel_data *const *data;
51 	const struct device *const *channels;
52 };
53 
nxp_pit_get_top_value(const struct device * dev)54 static uint32_t nxp_pit_get_top_value(const struct device *dev)
55 {
56 	const struct nxp_pit_config *config = dev->config;
57 	pit_chnl_t channel = PIT_CHANNEL_ID(dev);
58 
59 	/*
60 	 * According to RM, the LDVAL trigger = clock ticks -1
61 	 * The underlying HAL driver function PIT_SetTimerPeriod()
62 	 * automatically subtracted 1 from the value that ends up in
63 	 * LDVAL so for reporting purposes we need to add it back in
64 	 * here to by consistent.
65 	 */
66 	return (config->base->CHANNEL[channel].LDVAL + 1);
67 }
68 
nxp_pit_start(const struct device * dev)69 static int nxp_pit_start(const struct device *dev)
70 {
71 	const struct nxp_pit_config *config = dev->config;
72 	int channel_id = PIT_CHANNEL_ID(dev);
73 
74 	LOG_DBG("period is %d", nxp_pit_get_top_value(dev));
75 	PIT_EnableInterrupts(config->base, channel_id,
76 			     kPIT_TimerInterruptEnable);
77 	PIT_StartTimer(config->base, channel_id);
78 	return 0;
79 }
80 
nxp_pit_stop(const struct device * dev)81 static int nxp_pit_stop(const struct device *dev)
82 {
83 	const struct nxp_pit_config *config = dev->config;
84 	int channel_id = PIT_CHANNEL_ID(dev);
85 
86 	PIT_DisableInterrupts(config->base, channel_id,
87 			      kPIT_TimerInterruptEnable);
88 	PIT_StopTimer(config->base, channel_id);
89 
90 	return 0;
91 }
92 
nxp_pit_get_value(const struct device * dev,uint32_t * ticks)93 static int nxp_pit_get_value(const struct device *dev, uint32_t *ticks)
94 {
95 	const struct nxp_pit_config *config = dev->config;
96 	int channel_id = PIT_CHANNEL_ID(dev);
97 
98 	*ticks = PIT_GetCurrentTimerCount(config->base, channel_id);
99 
100 	return 0;
101 }
102 
nxp_pit_set_top_value(const struct device * dev,const struct counter_top_cfg * cfg)103 static int nxp_pit_set_top_value(const struct device *dev,
104 				  const struct counter_top_cfg *cfg)
105 {
106 	const struct nxp_pit_config *config = dev->config;
107 	struct nxp_pit_channel_data *data = PIT_CHANNEL_DATA(dev);
108 	pit_chnl_t channel = PIT_CHANNEL_ID(dev);
109 
110 	if (cfg->ticks == 0) {
111 		return -EINVAL;
112 	}
113 
114 	data->top_callback = cfg->callback;
115 	data->top_user_data = cfg->user_data;
116 
117 	if (config->base->CHANNEL[channel].TCTRL & PIT_TCTRL_TEN_MASK) {
118 		/* Timer already enabled, check flags before resetting */
119 		if (cfg->flags & COUNTER_TOP_CFG_DONT_RESET) {
120 			return -ENOTSUP;
121 		}
122 		PIT_StopTimer(config->base, channel);
123 		PIT_SetTimerPeriod(config->base, channel, cfg->ticks);
124 		PIT_StartTimer(config->base, channel);
125 	} else {
126 		PIT_SetTimerPeriod(config->base, channel, cfg->ticks);
127 	}
128 
129 	return 0;
130 }
131 
nxp_pit_get_pending_int(const struct device * dev)132 static uint32_t nxp_pit_get_pending_int(const struct device *dev)
133 {
134 	const struct nxp_pit_config *config = dev->config;
135 	uint32_t mask = PIT_TFLG_TIF_MASK;
136 	uint32_t flags;
137 	int channel_id = PIT_CHANNEL_ID(dev);
138 
139 	flags = PIT_GetStatusFlags(config->base, channel_id);
140 
141 	return ((flags & mask) == mask);
142 }
143 
nxp_pit_get_frequency(const struct device * dev)144 static uint32_t nxp_pit_get_frequency(const struct device *dev)
145 {
146 	const struct nxp_pit_config *config = dev->config;
147 	uint32_t clock_rate;
148 
149 	if (clock_control_get_rate(config->clock_dev, config->clock_subsys, &clock_rate)) {
150 		LOG_ERR("Failed to get clock rate");
151 		return 0;
152 	}
153 
154 	return clock_rate;
155 }
156 
157 #if DT_NODE_HAS_PROP(DT_COMPAT_GET_ANY_STATUS_OKAY(nxp_pit), interrupts)
nxp_pit_isr(const struct device * dev)158 static void nxp_pit_isr(const struct device *dev)
159 {
160 	const struct nxp_pit_config *config = dev->config;
161 	uint32_t flags;
162 
163 	LOG_DBG("pit counter isr");
164 
165 	for (int channel_index = 0;
166 			channel_index < config->num_channels;
167 			channel_index++) {
168 		flags = PIT_GetStatusFlags(config->base, channel_index);
169 		if (flags) {
170 			struct nxp_pit_channel_data *data =
171 				PIT_CHANNEL_DATA(config->channels[channel_index]);
172 			PIT_ClearStatusFlags(config->base, channel_index, flags);
173 			data->top_callback(dev, data->top_user_data);
174 		}
175 	}
176 }
177 #else
nxp_pit_isr(const struct device * dev)178 static void nxp_pit_isr(const struct device *dev)
179 {
180 	const struct nxp_pit_config *config = dev->config;
181 	struct nxp_pit_channel_data *data = PIT_CHANNEL_DATA(dev);
182 	pit_chnl_t channel = PIT_CHANNEL_ID(dev);
183 	uint32_t flags;
184 
185 	LOG_DBG("pit counter isr");
186 
187 	flags = PIT_GetStatusFlags(config->base, channel);
188 	if (flags) {
189 		PIT_ClearStatusFlags(config->base, channel, flags);
190 		if (data->top_callback) {
191 			data->top_callback(dev, data->top_user_data);
192 		}
193 	}
194 }
195 #endif /* DT_NODE_HAS_PROP(DT_COMPAT_GET_ANY_STATUS_OKAY(nxp_pit), interrupts) */
196 
nxp_pit_init(const struct device * dev)197 static int nxp_pit_init(const struct device *dev)
198 {
199 	const struct nxp_pit_config *config = dev->config;
200 	pit_config_t pit_config;
201 	uint32_t clock_rate;
202 
203 	if (!device_is_ready(config->clock_dev)) {
204 		LOG_ERR("Clock control device not ready");
205 		return -ENODEV;
206 	}
207 
208 	PIT_GetDefaultConfig(&pit_config);
209 	pit_config.enableRunInDebug = config->enableRunInDebug;
210 
211 	PIT_Init(config->base, &pit_config);
212 
213 	clock_rate = nxp_pit_get_frequency(dev);
214 
215 #if DT_NODE_HAS_PROP(DT_COMPAT_GET_ANY_STATUS_OKAY(nxp_pit), interrupts)
216 	config->irq_config_func(dev);
217 	for (int channel_index = 0;
218 		channel_index < config->num_channels;
219 		channel_index++) {
220 		PIT_SetTimerPeriod(config->base, channel_index,
221 			USEC_TO_COUNT(config->info.max_top_value, clock_rate));
222 	}
223 #else
224 	for (int channel_index = 0;
225 		channel_index < config->num_channels;
226 		channel_index++) {
227 		if (config->irq_config_func[channel_index]) {
228 			config->irq_config_func[channel_index](dev);
229 			PIT_SetTimerPeriod(config->base, channel_index,
230 				USEC_TO_COUNT(config->info.max_top_value, clock_rate));
231 		}
232 	}
233 #endif /* DT_NODE_HAS_PROP(DT_COMPAT_GET_ANY_STATUS_OKAY(nxp_pit), interrupts) */
234 	return 0;
235 }
236 
237 static DEVICE_API(counter, nxp_pit_driver_api) = {
238 	.start = nxp_pit_start,
239 	.stop = nxp_pit_stop,
240 	.get_value = nxp_pit_get_value,
241 	.set_top_value = nxp_pit_set_top_value,
242 	.get_pending_int = nxp_pit_get_pending_int,
243 	.get_top_value = nxp_pit_get_top_value,
244 	.get_freq = nxp_pit_get_frequency,
245 };
246 
247 
248 /* Creates a device for a channel (needed for counter API) */
249 #define NXP_PIT_CHANNEL_DEV_INIT(node, pit_inst)						\
250 	DEVICE_DT_DEFINE(node, NULL, NULL,							\
251 		(void *)									\
252 		&nxp_pit_##pit_inst##_channel_datas[DT_REG_ADDR(node)],				\
253 		&nxp_pit_##pit_inst##_config,							\
254 		POST_KERNEL, CONFIG_COUNTER_INIT_PRIORITY,					\
255 		&nxp_pit_driver_api);
256 
257 /* Creates a decleration for each pit channel */
258 #define NXP_PIT_CHANNEL_DECLARATIONS(node)  static struct nxp_pit_channel_data			\
259 	nxp_pit_channel_data_##node;
260 
261 /* Initializes an element of the channel data pointer array */
262 #define NXP_PIT_INSERT_CHANNEL_INTO_ARRAY(node)							\
263 	[DT_REG_ADDR(node)] =									\
264 		&nxp_pit_channel_data_##node,
265 
266 #define NXP_PIT_INSERT_CHANNEL_DEVICE_INTO_ARRAY(node)						\
267 	[DT_REG_ADDR(node)] = DEVICE_DT_GET(node),
268 
269 
270 #if DT_NODE_HAS_PROP(DT_COMPAT_GET_ANY_STATUS_OKAY(nxp_pit), interrupts)
271 #define NXP_PIT_IRQ_CONFIG_DECLARATIONS(n)							\
272 	static void nxp_pit_irq_config_func_##n(const struct device *dev)			\
273 	{											\
274 		IRQ_CONNECT(DT_INST_IRQ_BY_IDX(n, 0, irq),					\
275 			DT_INST_IRQ_BY_IDX(n, 0, priority),					\
276 			nxp_pit_isr,								\
277 			DEVICE_DT_INST_GET(n), 0);						\
278 		irq_enable(DT_INST_IRQN(n));							\
279 	};
280 
281 #define NXP_PIT_SETUP_IRQ_CONFIG(n) NXP_PIT_IRQ_CONFIG_DECLARATIONS(n);
282 #define NXP_PIT_SETUP_IRQ_ARRAY(ignored)
283 
284 #else
285 #define NXP_PIT_IRQ_CONFIG_DECLARATIONS(n)							\
286 	static void nxp_pit_irq_config_func_##n(const struct device *dev)			\
287 	{											\
288 		IRQ_CONNECT(DT_IRQN(n),								\
289 			DT_IRQ(n, priority),							\
290 			nxp_pit_isr,								\
291 			DEVICE_DT_GET(n), 0);							\
292 		irq_enable(DT_IRQN(n));								\
293 	};
294 
295 #define NXP_PIT_SETUP_IRQ_CONFIG(n)								\
296 	DT_INST_FOREACH_CHILD_STATUS_OKAY(n, NXP_PIT_IRQ_CONFIG_DECLARATIONS);
297 
298 #define NXP_PIT_INSERT_IRQ_CONFIG_INTO_ARRAY(n)							\
299 	[DT_REG_ADDR(n)] = &nxp_pit_irq_config_func_##n,
300 
301 #define NXP_PIT_SETUP_IRQ_ARRAY(n)								\
302 	/* Create Array of IRQs -> 1 irq func per channel */					\
303 	void (*nxp_pit_irq_config_array[DT_INST_FOREACH_CHILD_SEP_VARGS(n,			\
304 					DT_NODE_HAS_COMPAT, (+), nxp_pit_channel)])		\
305 				(const struct device *dev) = {					\
306 					DT_INST_FOREACH_CHILD_STATUS_OKAY(n,			\
307 					NXP_PIT_INSERT_IRQ_CONFIG_INTO_ARRAY)			\
308 	};
309 #endif
310 
311 #define COUNTER_NXP_PIT_DEVICE_INIT(n)								\
312 												\
313 	/* Setup the IRQ either for parent irq or per channel irq */				\
314 	NXP_PIT_SETUP_IRQ_CONFIG(n)								\
315 												\
316 	/* Create channel declarations */							\
317 	DT_INST_FOREACH_CHILD_STATUS_OKAY(n,							\
318 		NXP_PIT_CHANNEL_DECLARATIONS)							\
319 												\
320 	/* Array of channel devices */								\
321 	static struct nxp_pit_channel_data *const						\
322 		nxp_pit_##n##_channel_datas							\
323 			[DT_INST_FOREACH_CHILD_SEP_VARGS(					\
324 				n, DT_NODE_HAS_COMPAT, (+), nxp_pit_channel)] = {		\
325 				DT_INST_FOREACH_CHILD_STATUS_OKAY(n,				\
326 					NXP_PIT_INSERT_CHANNEL_INTO_ARRAY)			\
327 	};											\
328 												\
329 	/* forward declaration */								\
330 	static const struct nxp_pit_config nxp_pit_##n##_config;				\
331 												\
332 	/* Create all the channel/counter devices */						\
333 	DT_INST_FOREACH_CHILD_STATUS_OKAY_VARGS(n,						\
334 		NXP_PIT_CHANNEL_DEV_INIT, n)							\
335 												\
336 	/* This channel device array is needed by the module device ISR */			\
337 	const struct device *const nxp_pit_##n##_channels					\
338 				[DT_INST_FOREACH_CHILD_SEP_VARGS(				\
339 					n, DT_NODE_HAS_COMPAT, (+), nxp_pit_channel)] = {	\
340 				DT_INST_FOREACH_CHILD_STATUS_OKAY(n,				\
341 				NXP_PIT_INSERT_CHANNEL_DEVICE_INTO_ARRAY)			\
342 	};											\
343 												\
344 												\
345 	NXP_PIT_SETUP_IRQ_ARRAY(n)								\
346 												\
347 	/* This config struct is shared by all the channels and parent device */		\
348 	static const struct nxp_pit_config nxp_pit_##n##_config = {				\
349 		.info = {									\
350 			.max_top_value =							\
351 				DT_INST_PROP(n, max_load_value),				\
352 			.channels = 0,								\
353 		},										\
354 		.base = (PIT_Type *)DT_INST_REG_ADDR(n),					\
355 		.irq_config_func = COND_CODE_1(DT_NODE_HAS_PROP(				\
356 					DT_COMPAT_GET_ANY_STATUS_OKAY(nxp_pit), interrupts),	\
357 					(nxp_pit_irq_config_func_##n),				\
358 					(&nxp_pit_irq_config_array[0])),			\
359 		.num_channels = DT_INST_FOREACH_CHILD_SEP_VARGS(				\
360 			n, DT_NODE_HAS_COMPAT, (+), nxp_pit_channel),				\
361 		.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)),				\
362 		.clock_subsys = (clock_control_subsys_t)					\
363 				DT_INST_CLOCKS_CELL(n, name),					\
364 		.data = nxp_pit_##n##_channel_datas,						\
365 		.channels = nxp_pit_##n##_channels,						\
366 	};											\
367 												\
368 	/* Init parent device in order to handle ISR and init. */				\
369 	DEVICE_DT_INST_DEFINE(n, &nxp_pit_init, NULL,						\
370 			NULL, &nxp_pit_##n##_config, POST_KERNEL,				\
371 			CONFIG_COUNTER_INIT_PRIORITY, NULL);
372 
373 
374 DT_INST_FOREACH_STATUS_OKAY(COUNTER_NXP_PIT_DEVICE_INIT)
375