1 /*
2 * Copyright (c) 2023 Cypress Semiconductor Corporation (an Infineon company) or
3 * an affiliate of Cypress Semiconductor Corporation
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 /**
9 * @brief Counter driver for Infineon CAT1 MCU family.
10 */
11
12 #define DT_DRV_COMPAT infineon_cat1_counter
13
14 #include <zephyr/drivers/counter.h>
15 #include <zephyr/drivers/pinctrl.h>
16 #include <cyhal_timer.h>
17 #include <cyhal_gpio.h>
18
19 #include <cyhal_tcpwm_common.h>
20 #include <zephyr/dt-bindings/pinctrl/ifx_cat1-pinctrl.h>
21
22 #include <zephyr/logging/log.h>
23 LOG_MODULE_REGISTER(ifx_cat1_counter, CONFIG_COUNTER_LOG_LEVEL);
24
25 struct ifx_cat1_counter_config {
26 struct counter_config_info counter_info;
27 TCPWM_CNT_Type *reg_addr;
28 cyhal_gpio_t external_pin;
29 IRQn_Type irqn;
30 uint8_t irq_priority;
31 };
32
33 struct ifx_cat1_counter_data {
34 cyhal_timer_t counter_obj;
35 cyhal_timer_cfg_t counter_cfg;
36 struct counter_alarm_cfg alarm_cfg_counter;
37 struct counter_top_cfg top_value_cfg_counter;
38 uint32_t guard_period;
39 cyhal_resource_inst_t hw_resource;
40 cyhal_source_t signal_source;
41 bool alarm_irq_flag;
42 };
43
44 static const cy_stc_tcpwm_counter_config_t cyhal_timer_default_config = {
45 .period = 32768,
46 .clockPrescaler = CY_TCPWM_COUNTER_PRESCALER_DIVBY_1,
47 .runMode = CY_TCPWM_COUNTER_CONTINUOUS,
48 .countDirection = CY_TCPWM_COUNTER_COUNT_UP,
49 .compareOrCapture = CY_TCPWM_COUNTER_MODE_CAPTURE,
50 .compare0 = 16384,
51 .compare1 = 16384,
52 .enableCompareSwap = false,
53 .interruptSources = CY_TCPWM_INT_NONE,
54 .captureInputMode = 0x3U,
55 .captureInput = CY_TCPWM_INPUT_0,
56 .reloadInputMode = 0x3U,
57 .reloadInput = CY_TCPWM_INPUT_0,
58 .startInputMode = 0x3U,
59 .startInput = CY_TCPWM_INPUT_0,
60 .stopInputMode = 0x3U,
61 .stopInput = CY_TCPWM_INPUT_0,
62 .countInputMode = 0x3U,
63 .countInput = CY_TCPWM_INPUT_1,
64 };
65
get_hw_block_info(TCPWM_CNT_Type * reg_addr,cyhal_resource_inst_t * hw_resource)66 static int get_hw_block_info(TCPWM_CNT_Type *reg_addr, cyhal_resource_inst_t *hw_resource)
67 {
68 uint32_t i;
69
70 for (i = 0u; i < _CYHAL_TCPWM_INSTANCES; i++) {
71 uintptr_t base = POINTER_TO_UINT(_CYHAL_TCPWM_DATA[i].base);
72 uintptr_t cnt = POINTER_TO_UINT(_CYHAL_TCPWM_DATA[i].base->CNT);
73 uintptr_t reg_addr_ptr = POINTER_TO_UINT(reg_addr);
74 uintptr_t end_addr = base + sizeof(TCPWM_Type);
75
76 if ((reg_addr_ptr > base) && (reg_addr_ptr < end_addr)) {
77
78 hw_resource->type = CYHAL_RSC_TCPWM;
79 hw_resource->block_num = i;
80 hw_resource->channel_num = ((reg_addr_ptr - cnt) / sizeof(TCPWM_CNT_Type));
81
82 if (hw_resource->channel_num >= _CYHAL_TCPWM_DATA[i].num_channels) {
83 return -EINVAL;
84 }
85 return 0;
86 }
87 }
88 return -EINVAL;
89 }
90
ifx_cat1_counter_event_callback(void * callback_arg,cyhal_timer_event_t event)91 static void ifx_cat1_counter_event_callback(void *callback_arg, cyhal_timer_event_t event)
92 {
93 const struct device *dev = (const struct device *)callback_arg;
94 struct ifx_cat1_counter_data *const data = dev->data;
95 const struct ifx_cat1_counter_config *const config = dev->config;
96
97 /* Alarm compare/capture event */
98 if ((data->alarm_cfg_counter.callback != NULL) &&
99 (((CYHAL_TIMER_IRQ_CAPTURE_COMPARE & event) == CYHAL_TIMER_IRQ_CAPTURE_COMPARE) ||
100 data->alarm_irq_flag)) {
101 /* Alarm works as one-shot, so disable event */
102 cyhal_timer_enable_event(&data->counter_obj, CYHAL_TIMER_IRQ_CAPTURE_COMPARE,
103 config->irq_priority, false);
104
105 /* Call User callback for Alarm */
106 data->alarm_cfg_counter.callback(dev, 1, cyhal_timer_read(&data->counter_obj),
107 data->alarm_cfg_counter.user_data);
108 data->alarm_irq_flag = false;
109 }
110
111 /* Top_value terminal count event */
112 if ((data->top_value_cfg_counter.callback != NULL) &&
113 ((CYHAL_TIMER_IRQ_TERMINAL_COUNT & event) == CYHAL_TIMER_IRQ_TERMINAL_COUNT)) {
114
115 /* Call User callback for top value */
116 data->top_value_cfg_counter.callback(dev, data->top_value_cfg_counter.user_data);
117 }
118
119 /* NOTE: cyhal handles cleaning of interrupts */
120 }
121
ifx_cat1_counter_set_int_pending(const struct device * dev)122 static void ifx_cat1_counter_set_int_pending(const struct device *dev)
123 {
124 __ASSERT_NO_MSG(dev != NULL);
125
126 struct ifx_cat1_counter_data *const data = dev->data;
127 const struct ifx_cat1_counter_config *const config = dev->config;
128
129 cyhal_timer_enable_event(&data->counter_obj, CYHAL_TIMER_IRQ_CAPTURE_COMPARE,
130 config->irq_priority, true);
131 Cy_TCPWM_SetInterrupt(data->counter_obj.tcpwm.base,
132 _CYHAL_TCPWM_CNT_NUMBER(data->counter_obj.tcpwm.resource),
133 CY_TCPWM_INT_ON_CC0);
134 }
135
ifx_cat1_counter_init(const struct device * dev)136 static int ifx_cat1_counter_init(const struct device *dev)
137 {
138 __ASSERT_NO_MSG(dev != NULL);
139
140 cy_rslt_t rslt;
141 struct ifx_cat1_counter_data *data = dev->data;
142 const struct ifx_cat1_counter_config *config = dev->config;
143
144 /* Dedicate Counter HW resource */
145 if (get_hw_block_info(config->reg_addr, &data->hw_resource) != 0) {
146 return -EIO;
147 }
148
149 cyhal_timer_configurator_t timer_configurator = {
150 .resource = &data->hw_resource,
151 .config = &cyhal_timer_default_config,
152 };
153
154 /* Initialize timer */
155 rslt = cyhal_timer_init_cfg(&data->counter_obj, &timer_configurator);
156 if (rslt != CY_RSLT_SUCCESS) {
157 return -EIO;
158 }
159
160 /* Initialize counter structure */
161 data->alarm_irq_flag = false;
162 data->counter_cfg.compare_value = 0;
163 data->counter_cfg.period = config->counter_info.max_top_value;
164 data->counter_cfg.direction = CYHAL_TIMER_DIR_UP;
165 data->counter_cfg.is_compare = true;
166 data->counter_cfg.is_continuous = true;
167 data->counter_cfg.value = 0;
168
169 /* Configure timer */
170 rslt = cyhal_timer_configure(&data->counter_obj, &data->counter_cfg);
171 if (rslt != CY_RSLT_SUCCESS) {
172 return -EIO;
173 }
174
175 if (config->external_pin == NC) {
176 /* Configure frequency */
177 rslt = cyhal_timer_set_frequency(&data->counter_obj, config->counter_info.freq);
178 if (rslt != CY_RSLT_SUCCESS) {
179 return -EIO;
180 }
181 } else {
182 rslt = cyhal_gpio_init(config->external_pin, CYHAL_GPIO_DIR_INPUT,
183 CYHAL_GPIO_DRIVE_NONE, 0);
184 if (rslt != CY_RSLT_SUCCESS) {
185 LOG_ERR("External pin configuration error");
186 return -EIO;
187 }
188
189 rslt = cyhal_gpio_enable_output(config->external_pin, CYHAL_SIGNAL_TYPE_EDGE,
190 (cyhal_source_t *)&data->signal_source);
191 if (rslt != CY_RSLT_SUCCESS) {
192 if (rslt != CY_RSLT_SUCCESS) {
193 LOG_ERR("error in the enabling of Counter input pin output");
194 return -EIO;
195 }
196 }
197
198 rslt = cyhal_timer_connect_digital(&data->counter_obj, data->signal_source,
199 CYHAL_TIMER_INPUT_COUNT);
200 if (rslt != CY_RSLT_SUCCESS) {
201 LOG_ERR("Error connecting signal source");
202 return -EIO;
203 }
204 }
205
206 /* Register timer event callback */
207 cyhal_timer_register_callback(&data->counter_obj, ifx_cat1_counter_event_callback,
208 (void *)dev);
209
210 return 0;
211 }
212
ifx_cat1_counter_start(const struct device * dev)213 static int ifx_cat1_counter_start(const struct device *dev)
214 {
215 __ASSERT_NO_MSG(dev != NULL);
216
217 struct ifx_cat1_counter_data *const data = dev->data;
218
219 if (cyhal_timer_start(&data->counter_obj) != CY_RSLT_SUCCESS) {
220 return -EIO;
221 }
222 return 0;
223 }
224
ifx_cat1_counter_stop(const struct device * dev)225 static int ifx_cat1_counter_stop(const struct device *dev)
226 {
227 __ASSERT_NO_MSG(dev != NULL);
228
229 struct ifx_cat1_counter_data *const data = dev->data;
230
231 if (cyhal_timer_stop(&data->counter_obj) != CY_RSLT_SUCCESS) {
232 return -EIO;
233 }
234 return 0;
235 }
236
ifx_cat1_counter_get_value(const struct device * dev,uint32_t * ticks)237 static int ifx_cat1_counter_get_value(const struct device *dev, uint32_t *ticks)
238 {
239 __ASSERT_NO_MSG(dev != NULL);
240 __ASSERT_NO_MSG(ticks != NULL);
241
242 struct ifx_cat1_counter_data *const data = dev->data;
243
244 *ticks = cyhal_timer_read(&data->counter_obj);
245
246 return 0;
247 }
248
ifx_cat1_counter_set_top_value(const struct device * dev,const struct counter_top_cfg * cfg)249 static int ifx_cat1_counter_set_top_value(const struct device *dev,
250 const struct counter_top_cfg *cfg)
251 {
252 __ASSERT_NO_MSG(dev != NULL);
253 __ASSERT_NO_MSG(cfg != NULL);
254
255 cy_rslt_t rslt;
256 struct ifx_cat1_counter_data *const data = dev->data;
257 const struct ifx_cat1_counter_config *const config = dev->config;
258 bool ticks_gt_period;
259
260 data->top_value_cfg_counter = *cfg;
261 data->counter_cfg.period = cfg->ticks;
262
263 /* Check new top value limit */
264 if (cfg->ticks > config->counter_info.max_top_value) {
265 return -ENOTSUP;
266 }
267
268 ticks_gt_period = cfg->ticks > data->counter_cfg.period;
269 /* Checks if new period value is not less then old period value */
270 if (!(cfg->flags & COUNTER_TOP_CFG_DONT_RESET)) {
271 data->counter_cfg.value = 0u;
272 } else if (ticks_gt_period && (cfg->flags & COUNTER_TOP_CFG_RESET_WHEN_LATE)) {
273 data->counter_cfg.value = 0u;
274 } else {
275 /* cyhal_timer_configure resets timer counter register to value
276 * defined in config structure 'counter_cfg.value', so update
277 * counter value with current value of counter (read by
278 * cyhal_timer_read function).
279 */
280 data->counter_cfg.value = cyhal_timer_read(&data->counter_obj);
281 }
282
283 if ((ticks_gt_period == false) ||
284 ((ticks_gt_period == true) && (cfg->flags & COUNTER_TOP_CFG_RESET_WHEN_LATE))) {
285
286 /* Reconfigure timer */
287 if (config->external_pin == NC) {
288 rslt = cyhal_timer_configure(&data->counter_obj, &data->counter_cfg);
289 if (rslt != CY_RSLT_SUCCESS) {
290 return -EIO;
291 }
292 } else {
293 TCPWM_CNT_PERIOD(data->counter_obj.tcpwm.base,
294 _CYHAL_TCPWM_CNT_NUMBER(
295 data->counter_obj.tcpwm.resource)) = cfg->ticks;
296 }
297
298 /* Register an top_value terminal count event callback handler if
299 * callback is not NULL.
300 */
301 if (cfg->callback != NULL) {
302 cyhal_timer_enable_event(&data->counter_obj, CYHAL_TIMER_IRQ_TERMINAL_COUNT,
303 config->irq_priority, true);
304 }
305 }
306 return 0;
307 }
308
ifx_cat1_counter_get_top_value(const struct device * dev)309 static uint32_t ifx_cat1_counter_get_top_value(const struct device *dev)
310 {
311 __ASSERT_NO_MSG(dev != NULL);
312
313 struct ifx_cat1_counter_data *const data = dev->data;
314
315 return data->counter_cfg.period;
316 }
317
counter_is_bit_mask(uint32_t val)318 static inline bool counter_is_bit_mask(uint32_t val)
319 {
320 /* Return true if value equals 2^n - 1 */
321 return !(val & (val + 1U));
322 }
323
ifx_cat1_counter_ticks_add(uint32_t val1,uint32_t val2,uint32_t top)324 static uint32_t ifx_cat1_counter_ticks_add(uint32_t val1, uint32_t val2, uint32_t top)
325 {
326 uint32_t to_top;
327
328 /* refer to https://tbrindus.ca/how-builtin-expect-works/ for 'likely' usage */
329 if (likely(counter_is_bit_mask(top))) {
330 return (val1 + val2) & top;
331 }
332
333 to_top = top - val1;
334
335 return (val2 <= to_top) ? (val1 + val2) : (val2 - to_top - 1U);
336 }
337
ifx_cat1_counter_ticks_sub(uint32_t val,uint32_t old,uint32_t top)338 static uint32_t ifx_cat1_counter_ticks_sub(uint32_t val, uint32_t old, uint32_t top)
339 {
340 /* refer to https://tbrindus.ca/how-builtin-expect-works/ for 'likely' usage */
341 if (likely(counter_is_bit_mask(top))) {
342 return (val - old) & top;
343 }
344
345 /* if top is not 2^n-1 */
346 return (val >= old) ? (val - old) : (val + top + 1U - old);
347 }
348
ifx_cat1_counter_set_alarm(const struct device * dev,uint8_t chan_id,const struct counter_alarm_cfg * alarm_cfg)349 static int ifx_cat1_counter_set_alarm(const struct device *dev, uint8_t chan_id,
350 const struct counter_alarm_cfg *alarm_cfg)
351 {
352 ARG_UNUSED(chan_id);
353 __ASSERT_NO_MSG(dev != NULL);
354 __ASSERT_NO_MSG(alarm_cfg != NULL);
355
356 struct ifx_cat1_counter_data *const data = dev->data;
357 const struct ifx_cat1_counter_config *const config = dev->config;
358
359 uint32_t val = alarm_cfg->ticks;
360 uint32_t top_val = ifx_cat1_counter_get_top_value(dev);
361 uint32_t flags = alarm_cfg->flags;
362 uint32_t max_rel_val;
363 bool absolute = ((flags & COUNTER_ALARM_CFG_ABSOLUTE) == 0) ? false : true;
364 bool irq_on_late;
365
366 /* Checks if compare value is not less then period value */
367 if (alarm_cfg->ticks > top_val) {
368 return -EINVAL;
369 }
370
371 if (absolute) {
372 max_rel_val = top_val - data->guard_period;
373 irq_on_late = ((flags & COUNTER_ALARM_CFG_EXPIRE_WHEN_LATE) == 0) ? false : true;
374 } else {
375 /* If relative value is smaller than half of the counter range it is assumed
376 * that there is a risk of setting value too late and late detection algorithm
377 * must be applied. When late setting is detected, interrupt shall be
378 * triggered for immediate expiration of the timer. Detection is performed
379 * by limiting relative distance between CC and counter.
380 *
381 * Note that half of counter range is an arbitrary value.
382 */
383 irq_on_late = val < (top_val / 2U);
384
385 /* limit max to detect short relative being set too late. */
386 max_rel_val = irq_on_late ? (top_val / 2U) : top_val;
387 val = ifx_cat1_counter_ticks_add(cyhal_timer_read(&data->counter_obj), val,
388 top_val);
389 }
390
391 /* Decrement value to detect also case when val == counter_read(dev). Otherwise,
392 * condition would need to include comparing diff against 0.
393 */
394 uint32_t curr = cyhal_timer_read(&data->counter_obj);
395 uint32_t diff = ifx_cat1_counter_ticks_sub((val - 1), curr, top_val);
396
397 if ((absolute && (val < curr)) || (diff > max_rel_val)) {
398
399 /* Interrupt is triggered always for relative alarm and for absolute depending
400 * on the flag.
401 */
402 if (irq_on_late) {
403 data->alarm_irq_flag = true;
404 ifx_cat1_counter_set_int_pending(dev);
405 }
406
407 if (absolute) {
408 return -ETIME;
409 }
410 } else {
411 /* Setting new compare value */
412 cy_rslt_t rslt;
413
414 data->alarm_cfg_counter = *alarm_cfg;
415 data->counter_cfg.compare_value = val;
416
417 /* cyhal_timer_configure resets timer counter register to value
418 * defined in config structure 'counter_cfg.value', so update
419 * counter value with current value of counter (read by
420 * cyhal_timer_read function).
421 */
422 data->counter_cfg.value = cyhal_timer_read(&data->counter_obj);
423
424 /* Reconfigure timer */
425 if (config->external_pin == NC) {
426 rslt = cyhal_timer_configure(&data->counter_obj, &data->counter_cfg);
427 if (rslt != CY_RSLT_SUCCESS) {
428 return -EINVAL;
429 }
430 } else {
431 TCPWM_CNT_CC(data->counter_obj.tcpwm.base,
432 _CYHAL_TCPWM_CNT_NUMBER(data->counter_obj.tcpwm.resource)) =
433 data->counter_cfg.compare_value;
434 }
435
436 cyhal_timer_enable_event(&data->counter_obj, CYHAL_TIMER_IRQ_CAPTURE_COMPARE,
437 config->irq_priority, true);
438 }
439
440 return 0;
441 }
442
ifx_cat1_counter_cancel_alarm(const struct device * dev,uint8_t chan_id)443 static int ifx_cat1_counter_cancel_alarm(const struct device *dev, uint8_t chan_id)
444 {
445 ARG_UNUSED(chan_id);
446 __ASSERT_NO_MSG(dev != NULL);
447
448 struct ifx_cat1_counter_data *const data = dev->data;
449 const struct ifx_cat1_counter_config *const config = dev->config;
450
451 cyhal_timer_enable_event(&data->counter_obj, CYHAL_TIMER_IRQ_CAPTURE_COMPARE,
452 config->irq_priority, false);
453 return 0;
454 }
455
ifx_cat1_counter_get_pending_int(const struct device * dev)456 static uint32_t ifx_cat1_counter_get_pending_int(const struct device *dev)
457 {
458 __ASSERT_NO_MSG(dev != NULL);
459
460 const struct ifx_cat1_counter_config *const config = dev->config;
461
462 return NVIC_GetPendingIRQ(config->irqn);
463 }
464
ifx_cat1_counter_get_guard_period(const struct device * dev,uint32_t flags)465 static uint32_t ifx_cat1_counter_get_guard_period(const struct device *dev, uint32_t flags)
466 {
467 ARG_UNUSED(flags);
468 __ASSERT_NO_MSG(dev != NULL);
469
470 struct ifx_cat1_counter_data *const data = dev->data;
471
472 return data->guard_period;
473 }
474
ifx_cat1_counter_set_guard_period(const struct device * dev,uint32_t guard,uint32_t flags)475 static int ifx_cat1_counter_set_guard_period(const struct device *dev, uint32_t guard,
476 uint32_t flags)
477 {
478 ARG_UNUSED(flags);
479 __ASSERT_NO_MSG(dev != NULL);
480 __ASSERT_NO_MSG(guard < ifx_cat1_counter_get_top_value(dev));
481
482 struct ifx_cat1_counter_data *const data = dev->data;
483
484 data->guard_period = guard;
485 return 0;
486 }
487
488 static DEVICE_API(counter, counter_api) = {
489 .start = ifx_cat1_counter_start,
490 .stop = ifx_cat1_counter_stop,
491 .get_value = ifx_cat1_counter_get_value,
492 .set_alarm = ifx_cat1_counter_set_alarm,
493 .cancel_alarm = ifx_cat1_counter_cancel_alarm,
494 .set_top_value = ifx_cat1_counter_set_top_value,
495 .get_pending_int = ifx_cat1_counter_get_pending_int,
496 .get_top_value = ifx_cat1_counter_get_top_value,
497 .get_guard_period = ifx_cat1_counter_get_guard_period,
498 .set_guard_period = ifx_cat1_counter_set_guard_period,
499 };
500
501 #define DT_INST_GET_CYHAL_GPIO_OR(inst, gpios_prop, default) \
502 COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, gpios_prop), \
503 (DT_GET_CYHAL_GPIO_FROM_DT_GPIOS(DT_INST(inst, DT_DRV_COMPAT), gpios_prop)), \
504 (default))
505
506 /* Counter driver init macros */
507 #define INFINEON_CAT1_COUNTER_INIT(n) \
508 \
509 static struct ifx_cat1_counter_data ifx_cat1_counter##n##_data; \
510 \
511 static const struct ifx_cat1_counter_config ifx_cat1_counter##n##_config = { \
512 .counter_info = {.max_top_value = (DT_INST_PROP(n, resolution) == 32) \
513 ? UINT32_MAX \
514 : UINT16_MAX, \
515 .freq = DT_INST_PROP_OR(n, clock_frequency, 10000), \
516 .flags = COUNTER_CONFIG_INFO_COUNT_UP, \
517 .channels = 1}, \
518 .reg_addr = (TCPWM_CNT_Type *)DT_INST_REG_ADDR(n), \
519 .irq_priority = DT_INST_IRQ(n, priority), \
520 .irqn = DT_INST_IRQN(n), \
521 .external_pin = \
522 (cyhal_gpio_t)DT_INST_GET_CYHAL_GPIO_OR(n, external_trigger_gpios, NC)}; \
523 DEVICE_DT_INST_DEFINE(n, ifx_cat1_counter_init, NULL, &ifx_cat1_counter##n##_data, \
524 &ifx_cat1_counter##n##_config, PRE_KERNEL_1, \
525 CONFIG_COUNTER_INIT_PRIORITY, &counter_api);
526
527 DT_INST_FOREACH_STATUS_OKAY(INFINEON_CAT1_COUNTER_INIT);
528