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