2025-08-29 06:07:21 -07:00
|
|
|
/*
|
2026-06-26 10:20:52 -07:00
|
|
|
* SPDX-FileCopyrightText: Copyright (c) 2026 Infineon Technologies AG,
|
|
|
|
|
* SPDX-FileCopyrightText: or an affiliate of Infineon Technologies AG. All rights reserved.
|
2025-08-29 06:07:21 -07:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* Counter driver for Infineon TCPWM peripheral. */
|
|
|
|
|
|
|
|
|
|
#define DT_DRV_COMPAT infineon_tcpwm_counter
|
|
|
|
|
|
|
|
|
|
#include <zephyr/drivers/counter.h>
|
|
|
|
|
#include <zephyr/drivers/pinctrl.h>
|
2026-06-26 10:20:52 -07:00
|
|
|
#include <zephyr/pm/device.h>
|
2025-12-23 12:44:55 +05:30
|
|
|
#include <infineon_kconfig.h>
|
2025-08-29 06:07:21 -07:00
|
|
|
#include <zephyr/drivers/timer/ifx_tcpwm.h>
|
|
|
|
|
#include <zephyr/dt-bindings/pinctrl/ifx_cat1-pinctrl.h>
|
|
|
|
|
#include <zephyr/drivers/clock_control/clock_control_ifx_cat1.h>
|
|
|
|
|
#include <zephyr/irq.h>
|
2025-12-23 12:44:55 +05:30
|
|
|
#include <zephyr/sys/util.h>
|
2025-08-29 06:07:21 -07:00
|
|
|
|
|
|
|
|
#include <zephyr/logging/log.h>
|
|
|
|
|
LOG_MODULE_REGISTER(ifx_tcpwm_counter, CONFIG_COUNTER_LOG_LEVEL);
|
|
|
|
|
|
|
|
|
|
#include <cy_sysclk.h>
|
|
|
|
|
#include <cy_tcpwm_counter.h>
|
|
|
|
|
|
|
|
|
|
struct ifx_tcpwm_counter_config {
|
|
|
|
|
struct counter_config_info counter_info;
|
2025-12-18 17:05:24 -08:00
|
|
|
TCPWM_Type *reg_base;
|
2025-08-29 06:07:21 -07:00
|
|
|
uint32_t index;
|
|
|
|
|
bool resolution_32_bits;
|
|
|
|
|
IRQn_Type irq_num;
|
|
|
|
|
cy_en_divider_types_t divider_type;
|
|
|
|
|
uint32_t divider_sel;
|
|
|
|
|
uint32_t divider_val;
|
2026-01-13 13:33:41 -08:00
|
|
|
en_clk_dst_t clk_dst;
|
2025-08-29 06:07:21 -07:00
|
|
|
void (*irq_enable_func)(const struct device *dev);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct ifx_tcpwm_counter_data {
|
|
|
|
|
bool alarm_irq_flag;
|
|
|
|
|
cy_israddress callback;
|
|
|
|
|
/* Timer/counter comparison value */
|
|
|
|
|
uint32_t compare_value;
|
|
|
|
|
/* Default value of the timer/counter. */
|
|
|
|
|
uint32_t value;
|
|
|
|
|
struct counter_alarm_cfg alarm_cfg;
|
|
|
|
|
struct counter_top_cfg top_value_cfg_counter;
|
|
|
|
|
uint32_t guard_period;
|
|
|
|
|
struct ifx_cat1_clock clock;
|
2026-07-08 17:03:07 -07:00
|
|
|
/* Counter input frequency, cached at init (see ifx_tcpwm_counter_get_freq) */
|
|
|
|
|
uint32_t freq;
|
2026-06-26 10:20:52 -07:00
|
|
|
#ifdef CONFIG_PM_DEVICE
|
|
|
|
|
/* Whether the counter was running when suspend was entered, so
|
|
|
|
|
* resume only restarts a counter that was previously active.
|
|
|
|
|
*/
|
|
|
|
|
bool was_running;
|
|
|
|
|
#endif /* CONFIG_PM_DEVICE */
|
2025-08-29 06:07:21 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const cy_stc_tcpwm_counter_config_t counter_default_config = {
|
|
|
|
|
.period = 32768,
|
|
|
|
|
.clockPrescaler = CY_TCPWM_COUNTER_PRESCALER_DIVBY_1,
|
|
|
|
|
.runMode = CY_TCPWM_COUNTER_CONTINUOUS,
|
|
|
|
|
.countDirection = CY_TCPWM_COUNTER_COUNT_UP,
|
|
|
|
|
.compareOrCapture = CY_TCPWM_COUNTER_MODE_COMPARE,
|
|
|
|
|
.compare0 = 16384,
|
|
|
|
|
.compare1 = 16384,
|
|
|
|
|
.enableCompareSwap = false,
|
2025-12-23 12:44:55 +05:30
|
|
|
#if defined(CONFIG_SOC_FAMILY_INFINEON_PSOC4)
|
|
|
|
|
.interruptSources = CY_TCPWM_INT_ON_CC_OR_TC,
|
|
|
|
|
#else
|
2025-08-29 06:07:21 -07:00
|
|
|
.interruptSources = CY_TCPWM_INT_NONE,
|
2025-12-23 12:44:55 +05:30
|
|
|
#endif
|
2025-08-29 06:07:21 -07:00
|
|
|
.captureInputMode = 0x3U,
|
|
|
|
|
.captureInput = CY_TCPWM_INPUT_0,
|
|
|
|
|
.reloadInputMode = 0x3U,
|
|
|
|
|
.reloadInput = CY_TCPWM_INPUT_0,
|
|
|
|
|
.startInputMode = 0x3U,
|
|
|
|
|
.startInput = CY_TCPWM_INPUT_0,
|
|
|
|
|
.stopInputMode = 0x3U,
|
|
|
|
|
.stopInput = CY_TCPWM_INPUT_0,
|
|
|
|
|
.countInputMode = 0x3U,
|
|
|
|
|
.countInput = CY_TCPWM_INPUT_1,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
|
/* No interrupt handled */
|
|
|
|
|
COUNTER_IRQ_NONE = 0,
|
|
|
|
|
/* Interrupt when terminal count is reached */
|
|
|
|
|
COUNTER_IRQ_TERMINAL_COUNT = 1 << 0,
|
|
|
|
|
/* Interrupt when Compare/Capture value is reached */
|
|
|
|
|
COUNTER_IRQ_CAPTURE_COMPARE = 1 << 1,
|
|
|
|
|
/* Interrupt on terminal count and Compare/Capture values */
|
|
|
|
|
COUNTER_IRQ_ALL = (1 << 2) - 1,
|
|
|
|
|
} counter_event_t;
|
|
|
|
|
|
|
|
|
|
static void counter_enable_event(const struct device *dev, counter_event_t event, bool enable)
|
|
|
|
|
{
|
|
|
|
|
const struct ifx_tcpwm_counter_config *const config = dev->config;
|
|
|
|
|
uint32_t savedIntrStatus = Cy_SysLib_EnterCriticalSection();
|
2025-12-18 17:05:24 -08:00
|
|
|
uint32_t old_mask = Cy_TCPWM_GetInterruptMask(config->reg_base, config->index);
|
2025-08-29 06:07:21 -07:00
|
|
|
uint32_t new_event;
|
|
|
|
|
|
|
|
|
|
if (enable) {
|
|
|
|
|
/* Clear any newly enabled events so that old IRQs don't trigger ISRs */
|
2025-12-18 17:05:24 -08:00
|
|
|
Cy_TCPWM_ClearInterrupt(config->reg_base, config->index, ~old_mask & event);
|
2025-08-29 06:07:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
new_event = enable ? (old_mask | event) : (old_mask & ~event);
|
2025-12-18 17:05:24 -08:00
|
|
|
Cy_TCPWM_SetInterruptMask(config->reg_base, config->index, new_event);
|
2025-08-29 06:07:21 -07:00
|
|
|
|
|
|
|
|
Cy_SysLib_ExitCriticalSection(savedIntrStatus);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void counter_isr_handler(const struct device *dev)
|
|
|
|
|
{
|
|
|
|
|
struct ifx_tcpwm_counter_data *const data = dev->data;
|
|
|
|
|
const struct ifx_tcpwm_counter_config *const config = dev->config;
|
|
|
|
|
uint32_t pending_int;
|
|
|
|
|
|
2025-12-18 17:05:24 -08:00
|
|
|
pending_int = Cy_TCPWM_GetInterruptStatusMasked(config->reg_base, config->index);
|
|
|
|
|
Cy_TCPWM_ClearInterrupt(config->reg_base, config->index, pending_int);
|
2025-08-29 06:07:21 -07:00
|
|
|
NVIC_ClearPendingIRQ(config->irq_num);
|
|
|
|
|
|
|
|
|
|
/* Alarm compare/capture interrupt */
|
|
|
|
|
if ((data->alarm_cfg.callback != NULL) &&
|
|
|
|
|
(((COUNTER_IRQ_CAPTURE_COMPARE & pending_int) == COUNTER_IRQ_CAPTURE_COMPARE) ||
|
|
|
|
|
data->alarm_irq_flag)) {
|
|
|
|
|
/* Alarm works as one-shot, so disable interrupt */
|
|
|
|
|
counter_enable_event(dev, COUNTER_IRQ_CAPTURE_COMPARE, false);
|
|
|
|
|
|
|
|
|
|
/* Call User callback for Alarm */
|
2025-12-18 17:05:24 -08:00
|
|
|
data->alarm_cfg.callback(
|
|
|
|
|
dev, 1, Cy_TCPWM_Counter_GetCounter(config->reg_base, config->index),
|
|
|
|
|
data->alarm_cfg.user_data);
|
2025-08-29 06:07:21 -07:00
|
|
|
data->alarm_irq_flag = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Top_value terminal count interrupt */
|
|
|
|
|
if ((data->top_value_cfg_counter.callback != NULL) &&
|
|
|
|
|
((COUNTER_IRQ_TERMINAL_COUNT & pending_int) == COUNTER_IRQ_TERMINAL_COUNT)) {
|
|
|
|
|
|
|
|
|
|
/* Call User callback for top value */
|
|
|
|
|
data->top_value_cfg_counter.callback(dev, data->top_value_cfg_counter.user_data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ifx_tcpwm_counter_init(const struct device *dev)
|
|
|
|
|
{
|
|
|
|
|
__ASSERT_NO_MSG(dev != NULL);
|
|
|
|
|
|
|
|
|
|
cy_rslt_t rslt;
|
|
|
|
|
struct ifx_tcpwm_counter_data *const data = dev->data;
|
|
|
|
|
const struct ifx_tcpwm_counter_config *config = dev->config;
|
|
|
|
|
cy_stc_tcpwm_counter_config_t counter_config = counter_default_config;
|
|
|
|
|
|
|
|
|
|
/* Initialize counter structure */
|
|
|
|
|
data->alarm_irq_flag = false;
|
|
|
|
|
data->top_value_cfg_counter.ticks = config->counter_info.max_top_value;
|
|
|
|
|
data->compare_value = 0;
|
|
|
|
|
data->value = 0;
|
|
|
|
|
|
|
|
|
|
/* Configure timer */
|
|
|
|
|
counter_config.period = data->top_value_cfg_counter.ticks;
|
|
|
|
|
counter_config.compare0 = data->compare_value;
|
|
|
|
|
|
|
|
|
|
/* DeInit will clear the interrupt mask; save it now and restore after we re-nit */
|
2025-12-18 17:05:24 -08:00
|
|
|
uint32_t old_mask = Cy_TCPWM_GetInterruptMask(config->reg_base, config->index);
|
2025-08-29 06:07:21 -07:00
|
|
|
|
2025-12-18 17:05:24 -08:00
|
|
|
Cy_TCPWM_Counter_DeInit(config->reg_base, config->index, &counter_config);
|
2025-08-29 06:07:21 -07:00
|
|
|
|
2026-01-13 13:33:41 -08:00
|
|
|
/* Connect this TCPWM to the peripheral clock */
|
|
|
|
|
rslt = ifx_cat1_utils_peri_pclk_assign_divider(config->clk_dst, &data->clock);
|
|
|
|
|
if (rslt != CY_RSLT_SUCCESS) {
|
|
|
|
|
return -EIO;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-18 17:05:24 -08:00
|
|
|
rslt = (cy_rslt_t)Cy_TCPWM_Counter_Init(config->reg_base, config->index, &counter_config);
|
2025-08-29 06:07:21 -07:00
|
|
|
if (rslt != CY_RSLT_SUCCESS) {
|
|
|
|
|
return -EIO;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-18 17:05:24 -08:00
|
|
|
Cy_TCPWM_Counter_Enable(config->reg_base, config->index);
|
|
|
|
|
Cy_TCPWM_SetInterruptMask(config->reg_base, config->index, old_mask);
|
2025-08-29 06:07:21 -07:00
|
|
|
|
2025-12-18 17:05:24 -08:00
|
|
|
/* This must be called after Cy_TCPWM_Counter_Init */
|
|
|
|
|
Cy_TCPWM_Counter_SetCounter(config->reg_base, config->index, data->value);
|
2025-08-29 06:07:21 -07:00
|
|
|
|
2026-07-08 17:03:07 -07:00
|
|
|
/*
|
|
|
|
|
* Cache the fixed input frequency here (POST_KERNEL, thread context) so
|
|
|
|
|
* get_freq() can return it from ISR context. On non-secure builds this
|
|
|
|
|
* clock query is a secure round-trip; on the CM55 it is relayed over IPC
|
|
|
|
|
* and blocks on a semaphore, so it must run after the kernel and that
|
|
|
|
|
* relay are up rather than at PRE_KERNEL_1.
|
|
|
|
|
*/
|
|
|
|
|
data->freq = ifx_cat1_utils_peri_pclk_get_frequency(config->clk_dst, &data->clock);
|
|
|
|
|
if (data->freq == 0U) {
|
|
|
|
|
return -EIO;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-29 06:07:21 -07:00
|
|
|
/* enable the counter interrupt */
|
|
|
|
|
config->irq_enable_func(dev);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ifx_tcpwm_counter_start(const struct device *dev)
|
|
|
|
|
{
|
|
|
|
|
__ASSERT_NO_MSG(dev != NULL);
|
|
|
|
|
|
|
|
|
|
const struct ifx_tcpwm_counter_config *config = dev->config;
|
|
|
|
|
|
2025-12-18 17:05:24 -08:00
|
|
|
Cy_TCPWM_Counter_Enable(config->reg_base, config->index);
|
2025-12-23 12:44:55 +05:30
|
|
|
|
|
|
|
|
#if defined(CONFIG_SOC_FAMILY_INFINEON_PSOC4)
|
|
|
|
|
Cy_TCPWM_TriggerStart(config->reg_base, BIT(config->index));
|
|
|
|
|
#else
|
2025-12-18 17:05:24 -08:00
|
|
|
Cy_TCPWM_TriggerStart_Single(config->reg_base, config->index);
|
2025-12-23 12:44:55 +05:30
|
|
|
#endif
|
2025-08-29 06:07:21 -07:00
|
|
|
|
2026-06-26 10:20:52 -07:00
|
|
|
#ifdef CONFIG_PM_DEVICE
|
|
|
|
|
{
|
|
|
|
|
struct ifx_tcpwm_counter_data *const data = dev->data;
|
|
|
|
|
|
|
|
|
|
data->was_running = true;
|
|
|
|
|
}
|
|
|
|
|
#endif /* CONFIG_PM_DEVICE */
|
|
|
|
|
|
2025-08-29 06:07:21 -07:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ifx_tcpwm_counter_stop(const struct device *dev)
|
|
|
|
|
{
|
|
|
|
|
__ASSERT_NO_MSG(dev != NULL);
|
|
|
|
|
|
|
|
|
|
const struct ifx_tcpwm_counter_config *config = dev->config;
|
|
|
|
|
|
2025-12-18 17:05:24 -08:00
|
|
|
Cy_TCPWM_Counter_Disable(config->reg_base, config->index);
|
2025-08-29 06:07:21 -07:00
|
|
|
|
2026-06-26 10:20:52 -07:00
|
|
|
#ifdef CONFIG_PM_DEVICE
|
|
|
|
|
{
|
|
|
|
|
struct ifx_tcpwm_counter_data *const data = dev->data;
|
|
|
|
|
|
|
|
|
|
data->was_running = false;
|
|
|
|
|
}
|
|
|
|
|
#endif /* CONFIG_PM_DEVICE */
|
|
|
|
|
|
2025-08-29 06:07:21 -07:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-13 13:09:33 -07:00
|
|
|
static uint32_t ifx_tcpwm_counter_get_freq(const struct device *dev)
|
|
|
|
|
{
|
|
|
|
|
struct ifx_tcpwm_counter_data *const data = dev->data;
|
|
|
|
|
|
2026-07-08 17:03:07 -07:00
|
|
|
/*
|
|
|
|
|
* Cached at init: on non-secure builds the PDL clock query is a secure
|
|
|
|
|
* round-trip (IPC-relayed on the CM55) that is not allowed from the ISR
|
|
|
|
|
* context this API may run in.
|
|
|
|
|
*/
|
|
|
|
|
return data->freq;
|
2025-10-13 13:09:33 -07:00
|
|
|
}
|
|
|
|
|
|
2025-08-29 06:07:21 -07:00
|
|
|
static int ifx_tcpwm_counter_get_value(const struct device *dev, uint32_t *ticks)
|
|
|
|
|
{
|
|
|
|
|
__ASSERT_NO_MSG(dev != NULL);
|
|
|
|
|
__ASSERT_NO_MSG(ticks != NULL);
|
|
|
|
|
|
|
|
|
|
const struct ifx_tcpwm_counter_config *config = dev->config;
|
|
|
|
|
|
2025-12-18 17:05:24 -08:00
|
|
|
*ticks = Cy_TCPWM_Counter_GetCounter(config->reg_base, config->index);
|
2025-08-29 06:07:21 -07:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ifx_tcpwm_counter_set_top_value(const struct device *dev,
|
|
|
|
|
const struct counter_top_cfg *cfg)
|
|
|
|
|
{
|
|
|
|
|
__ASSERT_NO_MSG(dev != NULL);
|
|
|
|
|
__ASSERT_NO_MSG(cfg != NULL);
|
|
|
|
|
|
|
|
|
|
struct ifx_tcpwm_counter_data *const data = dev->data;
|
|
|
|
|
const struct ifx_tcpwm_counter_config *const config = dev->config;
|
2026-06-08 17:27:12 -07:00
|
|
|
int ret = 0;
|
2025-08-29 06:07:21 -07:00
|
|
|
|
|
|
|
|
if (cfg->ticks > config->counter_info.max_top_value) {
|
|
|
|
|
return -ENOTSUP;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-08 17:27:12 -07:00
|
|
|
data->top_value_cfg_counter = *cfg;
|
2025-08-29 06:07:21 -07:00
|
|
|
|
2025-12-23 12:44:55 +05:30
|
|
|
#if defined(CONFIG_SOC_FAMILY_INFINEON_PSOC4)
|
|
|
|
|
Cy_TCPWM_Counter_SetPeriod(config->reg_base, config->index, cfg->ticks);
|
|
|
|
|
#else
|
2025-12-18 17:05:24 -08:00
|
|
|
Cy_TCPWM_Block_SetPeriod(config->reg_base, config->index, cfg->ticks);
|
2025-12-23 12:44:55 +05:30
|
|
|
#endif
|
2025-08-29 06:07:21 -07:00
|
|
|
|
2026-06-08 17:27:12 -07:00
|
|
|
if (!(cfg->flags & COUNTER_TOP_CFG_DONT_RESET)) {
|
|
|
|
|
data->value = 0u;
|
|
|
|
|
Cy_TCPWM_Counter_SetCounter(config->reg_base, config->index, 0u);
|
|
|
|
|
} else {
|
|
|
|
|
uint32_t current =
|
|
|
|
|
Cy_TCPWM_Counter_GetCounter(config->reg_base, config->index);
|
|
|
|
|
|
|
|
|
|
if (current >= cfg->ticks) {
|
|
|
|
|
if (cfg->flags & COUNTER_TOP_CFG_RESET_WHEN_LATE) {
|
|
|
|
|
data->value = 0u;
|
|
|
|
|
Cy_TCPWM_Counter_SetCounter(config->reg_base,
|
|
|
|
|
config->index, 0u);
|
|
|
|
|
}
|
|
|
|
|
ret = -ETIME;
|
|
|
|
|
} else {
|
|
|
|
|
data->value = current;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-29 06:07:21 -07:00
|
|
|
if (cfg->callback != NULL) {
|
|
|
|
|
counter_enable_event(dev, COUNTER_IRQ_TERMINAL_COUNT, true);
|
2025-12-23 12:44:55 +05:30
|
|
|
} else {
|
|
|
|
|
counter_enable_event(dev, COUNTER_IRQ_TERMINAL_COUNT, false);
|
2025-08-29 06:07:21 -07:00
|
|
|
}
|
|
|
|
|
|
2026-06-08 17:27:12 -07:00
|
|
|
return ret;
|
2025-08-29 06:07:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint32_t ifx_tcpwm_counter_get_top_value(const struct device *dev)
|
|
|
|
|
{
|
|
|
|
|
__ASSERT_NO_MSG(dev != NULL);
|
|
|
|
|
|
|
|
|
|
struct ifx_tcpwm_counter_data *const data = dev->data;
|
|
|
|
|
|
|
|
|
|
return data->top_value_cfg_counter.ticks;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline bool counter_is_bit_mask(uint32_t val)
|
|
|
|
|
{
|
|
|
|
|
/* Return true if value equals 2^n - 1 */
|
|
|
|
|
return !(val & (val + 1U));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint32_t counter_ticks_add(uint32_t val1, uint32_t val2, uint32_t top)
|
|
|
|
|
{
|
|
|
|
|
uint32_t to_top;
|
|
|
|
|
|
|
|
|
|
/* refer to https://tbrindus.ca/how-builtin-expect-works/ for 'likely' usage */
|
|
|
|
|
if (likely(counter_is_bit_mask(top))) {
|
|
|
|
|
return (val1 + val2) & top;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
to_top = top - val1;
|
|
|
|
|
|
|
|
|
|
return (val2 <= to_top) ? (val1 + val2) : (val2 - to_top - 1U);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint32_t counter_ticks_sub(uint32_t val, uint32_t old, uint32_t top)
|
|
|
|
|
{
|
|
|
|
|
/* refer to https://tbrindus.ca/how-builtin-expect-works/ for 'likely' usage */
|
|
|
|
|
if (likely(counter_is_bit_mask(top))) {
|
|
|
|
|
return (val - old) & top;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* if top is not 2^n-1 */
|
|
|
|
|
return (val >= old) ? (val - old) : (val + top + 1U - old);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ifx_tcpwm_counter_set_alarm(const struct device *dev, uint8_t chan_id,
|
|
|
|
|
const struct counter_alarm_cfg *alarm_cfg)
|
|
|
|
|
{
|
|
|
|
|
ARG_UNUSED(chan_id);
|
|
|
|
|
__ASSERT_NO_MSG(dev != NULL);
|
|
|
|
|
__ASSERT_NO_MSG(alarm_cfg != NULL);
|
|
|
|
|
|
|
|
|
|
struct ifx_tcpwm_counter_data *const data = dev->data;
|
|
|
|
|
const struct ifx_tcpwm_counter_config *const config = dev->config;
|
|
|
|
|
|
|
|
|
|
uint32_t compare_value = alarm_cfg->ticks;
|
|
|
|
|
uint32_t top_val = ifx_tcpwm_counter_get_top_value(dev);
|
|
|
|
|
uint32_t flags = alarm_cfg->flags;
|
|
|
|
|
uint32_t max_rel_val;
|
|
|
|
|
bool absolute = ((flags & COUNTER_ALARM_CFG_ABSOLUTE) == 0) ? false : true;
|
|
|
|
|
bool irq_on_late;
|
|
|
|
|
|
|
|
|
|
data->alarm_cfg = *alarm_cfg;
|
|
|
|
|
|
|
|
|
|
/* Checks if compare value is not less then period value */
|
|
|
|
|
if (alarm_cfg->ticks > top_val) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (absolute) {
|
|
|
|
|
max_rel_val = top_val - data->guard_period;
|
|
|
|
|
irq_on_late = ((flags & COUNTER_ALARM_CFG_EXPIRE_WHEN_LATE) == 0) ? false : true;
|
|
|
|
|
} else {
|
|
|
|
|
/* If relative value is smaller than half of the counter range it is assumed
|
|
|
|
|
* that there is a risk of setting value too late and late detection algorithm
|
|
|
|
|
* must be applied. When late setting is detected, interrupt shall be
|
|
|
|
|
* triggered for immediate expiration of the timer. Detection is performed
|
|
|
|
|
* by limiting relative distance between CC and counter.
|
|
|
|
|
*
|
|
|
|
|
* Note that half of counter range is an arbitrary value.
|
|
|
|
|
*/
|
|
|
|
|
irq_on_late = compare_value < (top_val / 2U);
|
|
|
|
|
|
|
|
|
|
/* limit max to detect short relative being set too late. */
|
|
|
|
|
max_rel_val = irq_on_late ? (top_val / 2U) : top_val;
|
2025-12-18 17:05:24 -08:00
|
|
|
compare_value = counter_ticks_add(
|
|
|
|
|
Cy_TCPWM_Counter_GetCounter(config->reg_base, config->index), compare_value,
|
|
|
|
|
top_val);
|
2025-08-29 06:07:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Decrement value to detect the case when compare_value == counter_read(dev). Otherwise,
|
|
|
|
|
* condition would need to include comparing diff against 0.
|
|
|
|
|
*/
|
2025-12-18 17:05:24 -08:00
|
|
|
uint32_t curr = Cy_TCPWM_Counter_GetCounter(config->reg_base, config->index);
|
2025-08-29 06:07:21 -07:00
|
|
|
uint32_t diff = counter_ticks_sub((compare_value - 1), curr, top_val);
|
|
|
|
|
|
|
|
|
|
if ((absolute && (compare_value < curr)) || (diff > max_rel_val)) {
|
|
|
|
|
|
|
|
|
|
/* Interrupt is triggered always for relative alarm and for absolute depending
|
|
|
|
|
* on the flag.
|
|
|
|
|
*/
|
|
|
|
|
if (irq_on_late) {
|
|
|
|
|
data->alarm_irq_flag = true;
|
|
|
|
|
counter_enable_event(dev, COUNTER_IRQ_CAPTURE_COMPARE, true);
|
2025-12-18 17:05:24 -08:00
|
|
|
Cy_TCPWM_SetInterrupt(config->reg_base, config->index,
|
|
|
|
|
COUNTER_IRQ_CAPTURE_COMPARE);
|
2025-08-29 06:07:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (absolute) {
|
|
|
|
|
return -ETIME;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
/* Setting new compare value
|
|
|
|
|
*
|
|
|
|
|
* timer_configure resets timer counter register to value
|
|
|
|
|
* defined in config structure 'data->value', so update
|
|
|
|
|
* counter value with current value of counter (read by
|
2025-12-18 17:05:24 -08:00
|
|
|
* Cy_TCPWM_Counter_GetCounter function).
|
2025-08-29 06:07:21 -07:00
|
|
|
*/
|
2025-12-18 17:05:24 -08:00
|
|
|
data->value = Cy_TCPWM_Counter_GetCounter(config->reg_base, config->index);
|
2025-08-29 06:07:21 -07:00
|
|
|
data->compare_value = compare_value;
|
|
|
|
|
|
|
|
|
|
/* Reconfigure timer */
|
2025-12-23 12:44:55 +05:30
|
|
|
#if defined(CONFIG_SOC_FAMILY_INFINEON_PSOC4)
|
|
|
|
|
Cy_TCPWM_Counter_SetCompare0(config->reg_base, config->index, compare_value);
|
|
|
|
|
#else
|
2025-12-18 17:05:24 -08:00
|
|
|
Cy_TCPWM_Block_SetCC0Val(config->reg_base, config->index, compare_value);
|
2025-12-23 12:44:55 +05:30
|
|
|
#endif
|
2025-08-29 06:07:21 -07:00
|
|
|
counter_enable_event(dev, COUNTER_IRQ_CAPTURE_COMPARE, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ifx_tcpwm_counter_cancel_alarm(const struct device *dev, uint8_t chan_id)
|
|
|
|
|
{
|
|
|
|
|
ARG_UNUSED(chan_id);
|
|
|
|
|
__ASSERT_NO_MSG(dev != NULL);
|
|
|
|
|
|
|
|
|
|
counter_enable_event(dev, COUNTER_IRQ_CAPTURE_COMPARE, false);
|
2025-12-23 12:44:55 +05:30
|
|
|
|
2025-08-29 06:07:21 -07:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint32_t ifx_tcpwm_counter_get_pending_int(const struct device *dev)
|
|
|
|
|
{
|
|
|
|
|
__ASSERT_NO_MSG(dev != NULL);
|
|
|
|
|
|
|
|
|
|
const struct ifx_tcpwm_counter_config *const config = dev->config;
|
2025-12-23 12:44:55 +05:30
|
|
|
uint32_t pending = 0U;
|
|
|
|
|
|
|
|
|
|
pending = Cy_TCPWM_GetInterruptStatusMasked(config->reg_base, config->index);
|
2025-08-29 06:07:21 -07:00
|
|
|
|
2025-12-23 12:44:55 +05:30
|
|
|
#if defined(CONFIG_SOC_FAMILY_INFINEON_PSOC4)
|
|
|
|
|
return (pending & CY_TCPWM_INT_ON_CC) ? COUNTER_IRQ_CAPTURE_COMPARE : 0U;
|
|
|
|
|
#else
|
2025-08-29 06:07:21 -07:00
|
|
|
return NVIC_GetPendingIRQ(config->irq_num);
|
2025-12-23 12:44:55 +05:30
|
|
|
#endif
|
2025-08-29 06:07:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint32_t ifx_tcpwm_counter_get_guard_period(const struct device *dev, uint32_t flags)
|
|
|
|
|
{
|
|
|
|
|
ARG_UNUSED(flags);
|
|
|
|
|
__ASSERT_NO_MSG(dev != NULL);
|
|
|
|
|
|
|
|
|
|
struct ifx_tcpwm_counter_data *const data = dev->data;
|
|
|
|
|
|
|
|
|
|
return data->guard_period;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ifx_tcpwm_counter_set_guard_period(const struct device *dev, uint32_t guard,
|
|
|
|
|
uint32_t flags)
|
|
|
|
|
{
|
|
|
|
|
ARG_UNUSED(flags);
|
|
|
|
|
__ASSERT_NO_MSG(dev != NULL);
|
|
|
|
|
__ASSERT_NO_MSG(guard < ifx_tcpwm_counter_get_top_value(dev));
|
|
|
|
|
|
|
|
|
|
struct ifx_tcpwm_counter_data *const data = dev->data;
|
|
|
|
|
|
|
|
|
|
data->guard_period = guard;
|
2025-12-23 12:44:55 +05:30
|
|
|
|
2025-08-29 06:07:21 -07:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-26 10:20:52 -07:00
|
|
|
#ifdef CONFIG_PM_DEVICE
|
|
|
|
|
static int ifx_tcpwm_counter_pm_action(const struct device *dev, enum pm_device_action action)
|
|
|
|
|
{
|
|
|
|
|
const struct ifx_tcpwm_counter_config *config = dev->config;
|
|
|
|
|
struct ifx_tcpwm_counter_data *const data = dev->data;
|
|
|
|
|
|
|
|
|
|
switch (action) {
|
|
|
|
|
case PM_DEVICE_ACTION_SUSPEND:
|
|
|
|
|
/* Clock gate the block; clock tree left untouched. */
|
|
|
|
|
Cy_TCPWM_Counter_Disable(config->reg_base, config->index);
|
|
|
|
|
break;
|
|
|
|
|
case PM_DEVICE_ACTION_RESUME:
|
|
|
|
|
Cy_TCPWM_Counter_Enable(config->reg_base, config->index);
|
|
|
|
|
/* Restart the counter if it was running before suspend. */
|
|
|
|
|
if (data->was_running) {
|
|
|
|
|
(void)ifx_tcpwm_counter_start(dev);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
#if defined(CONFIG_PM_S2RAM) || defined(CONFIG_PM_DEVICE_POWER_DOMAIN)
|
|
|
|
|
case PM_DEVICE_ACTION_TURN_ON: {
|
|
|
|
|
/* Power was removed so re-initialize the peripheral. */
|
|
|
|
|
int ret = ifx_tcpwm_counter_init(dev);
|
|
|
|
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
#endif /* CONFIG_PM_S2RAM || CONFIG_PM_DEVICE_POWER_DOMAIN */
|
|
|
|
|
default:
|
|
|
|
|
return -ENOTSUP;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
#endif /* CONFIG_PM_DEVICE */
|
|
|
|
|
|
2025-08-29 06:07:21 -07:00
|
|
|
static DEVICE_API(counter, counter_api) = {
|
|
|
|
|
.start = ifx_tcpwm_counter_start,
|
|
|
|
|
.stop = ifx_tcpwm_counter_stop,
|
2025-10-13 13:09:33 -07:00
|
|
|
.get_freq = ifx_tcpwm_counter_get_freq,
|
2025-08-29 06:07:21 -07:00
|
|
|
.get_value = ifx_tcpwm_counter_get_value,
|
|
|
|
|
.set_alarm = ifx_tcpwm_counter_set_alarm,
|
|
|
|
|
.cancel_alarm = ifx_tcpwm_counter_cancel_alarm,
|
|
|
|
|
.set_top_value = ifx_tcpwm_counter_set_top_value,
|
|
|
|
|
.get_pending_int = ifx_tcpwm_counter_get_pending_int,
|
|
|
|
|
.get_top_value = ifx_tcpwm_counter_get_top_value,
|
|
|
|
|
.get_guard_period = ifx_tcpwm_counter_get_guard_period,
|
|
|
|
|
.set_guard_period = ifx_tcpwm_counter_set_guard_period,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define DT_INST_GET_CYHAL_GPIO_OR(inst, gpios_prop, default) \
|
|
|
|
|
COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, gpios_prop), \
|
|
|
|
|
(DT_GET_CYHAL_GPIO_FROM_DT_GPIOS(DT_INST(inst, DT_DRV_COMPAT), gpios_prop)), \
|
|
|
|
|
(default))
|
|
|
|
|
|
2025-10-13 13:09:33 -07:00
|
|
|
#if defined(CONFIG_SOC_FAMILY_INFINEON_EDGE)
|
|
|
|
|
#define COUNTER_PERI_CLOCK_INSTANCE(n) DT_PROP_BY_IDX(DT_INST_PHANDLE(n, clocks), peri_group, 0),
|
|
|
|
|
#else
|
|
|
|
|
#define COUNTER_PERI_CLOCK_INSTANCE(n)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(CONFIG_SOC_FAMILY_INFINEON_EDGE)
|
|
|
|
|
#define COUNTER_PERI_CLOCK_INIT(n) \
|
2025-12-23 12:44:55 +05:30
|
|
|
.clock = { \
|
|
|
|
|
.block = IFX_CAT1_PERIPHERAL_GROUP_ADJUST( \
|
|
|
|
|
DT_PROP_BY_IDX(DT_INST_PHANDLE(n, clocks), peri_group, 0), \
|
|
|
|
|
DT_PROP_BY_IDX(DT_INST_PHANDLE(n, clocks), peri_group, 1), \
|
|
|
|
|
DT_INST_PROP_BY_PHANDLE(n, clocks, div_type)), \
|
|
|
|
|
.channel = DT_INST_PROP_BY_PHANDLE(n, clocks, channel), \
|
|
|
|
|
}
|
2025-10-13 13:09:33 -07:00
|
|
|
#else
|
|
|
|
|
#define COUNTER_PERI_CLOCK_INIT(n) \
|
2025-12-23 12:44:55 +05:30
|
|
|
.clock = { \
|
|
|
|
|
.block = IFX_CAT1_PERIPHERAL_GROUP_ADJUST( \
|
|
|
|
|
DT_PROP_BY_IDX(DT_INST_PHANDLE(n, clocks), peri_group, 1), \
|
|
|
|
|
DT_INST_PROP_BY_PHANDLE(n, clocks, div_type)), \
|
|
|
|
|
.channel = DT_INST_PROP_BY_PHANDLE(n, clocks, channel), \
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(CONFIG_SOC_FAMILY_INFINEON_PSOC4)
|
|
|
|
|
#define TCPWM_CNT_IDX(n) .index = DT_NODE_CHILD_IDX(DT_INST_PARENT(n))
|
|
|
|
|
#else
|
|
|
|
|
#define TCPWM_CNT_IDX(n) \
|
|
|
|
|
.index = (DT_REG_ADDR(DT_INST_PARENT(n)) - DT_REG_ADDR(DT_PARENT(DT_INST_PARENT(n)))) / \
|
|
|
|
|
DT_REG_SIZE(DT_INST_PARENT(n))
|
2025-10-13 13:09:33 -07:00
|
|
|
#endif
|
|
|
|
|
|
2025-08-29 06:07:21 -07:00
|
|
|
/* Counter driver init macros */
|
|
|
|
|
#define INFINEON_TCPWM_COUNTER_INIT(n) \
|
|
|
|
|
\
|
2025-10-13 13:09:33 -07:00
|
|
|
static void ifx_counter_irq_enable_func_##n(const struct device *dev) \
|
2025-08-29 06:07:21 -07:00
|
|
|
{ \
|
|
|
|
|
IRQ_CONNECT(DT_IRQN(DT_INST_PARENT(n)), DT_IRQ(DT_INST_PARENT(n), priority), \
|
|
|
|
|
counter_isr_handler, DEVICE_DT_INST_GET(n), 0); \
|
|
|
|
|
irq_enable(DT_IRQN(DT_INST_PARENT(n))); \
|
|
|
|
|
} \
|
|
|
|
|
\
|
2025-12-23 12:44:55 +05:30
|
|
|
static struct ifx_tcpwm_counter_data ifx_tcpwm_counter##n##_data = { \
|
|
|
|
|
COUNTER_PERI_CLOCK_INIT(n)}; \
|
2025-08-29 06:07:21 -07:00
|
|
|
\
|
2026-06-26 10:20:52 -07:00
|
|
|
PM_DEVICE_DT_INST_DEFINE(n, ifx_tcpwm_counter_pm_action); \
|
|
|
|
|
\
|
2025-08-29 06:07:21 -07:00
|
|
|
static const struct ifx_tcpwm_counter_config ifx_tcpwm_counter##n##_config = { \
|
|
|
|
|
.counter_info = {.max_top_value = (DT_PROP(DT_INST_PARENT(n), resolution) == 32) \
|
|
|
|
|
? UINT32_MAX \
|
|
|
|
|
: UINT16_MAX, \
|
|
|
|
|
.flags = COUNTER_CONFIG_INFO_COUNT_UP, \
|
|
|
|
|
.channels = 1}, \
|
2025-12-23 12:44:55 +05:30
|
|
|
.reg_base = (TCPWM_Type *)(DT_REG_ADDR(DT_PARENT(DT_INST_PARENT(n)))), \
|
|
|
|
|
TCPWM_CNT_IDX(n), \
|
2025-08-29 06:07:21 -07:00
|
|
|
.irq_num = DT_IRQN(DT_INST_PARENT(n)), \
|
|
|
|
|
.resolution_32_bits = \
|
|
|
|
|
(DT_PROP(DT_INST_PARENT(n), resolution) == 32) ? true : false, \
|
2026-01-13 13:33:41 -08:00
|
|
|
.clk_dst = DT_PROP(DT_INST_PARENT(n), clk_dst), \
|
2025-10-13 13:09:33 -07:00
|
|
|
.irq_enable_func = ifx_counter_irq_enable_func_##n, \
|
2025-08-29 06:07:21 -07:00
|
|
|
}; \
|
|
|
|
|
\
|
2026-06-26 10:20:52 -07:00
|
|
|
DEVICE_DT_INST_DEFINE(n, ifx_tcpwm_counter_init, PM_DEVICE_DT_INST_GET(n), \
|
|
|
|
|
&ifx_tcpwm_counter##n##_data, &ifx_tcpwm_counter##n##_config, \
|
|
|
|
|
POST_KERNEL, CONFIG_COUNTER_INIT_PRIORITY, &counter_api);
|
2025-08-29 06:07:21 -07:00
|
|
|
|
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(INFINEON_TCPWM_COUNTER_INIT);
|