From 02f2beab29fb791ccbae2dea38831f9b6ac325f4 Mon Sep 17 00:00:00 2001 From: Sreeram Tatapudi Date: Thu, 22 May 2025 15:10:18 -0700 Subject: [PATCH] drivers: timer: Add support for IFX Low power timer Adding support for low power timer to enable low power modes Signed-off-by: Sreeram Tatapudi --- .../clock_control/clock_control_ifx_cat1.c | 2 - drivers/timer/CMakeLists.txt | 1 + drivers/timer/Kconfig | 1 + drivers/timer/Kconfig.ifx_cat1_lp | 17 ++ drivers/timer/ifx_cat1_lp_timer.c | 186 ++++++++++++++++++ .../timer/infineon,cat1-lp-timer.yaml | 14 ++ 6 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 drivers/timer/Kconfig.ifx_cat1_lp create mode 100644 drivers/timer/ifx_cat1_lp_timer.c create mode 100644 dts/bindings/timer/infineon,cat1-lp-timer.yaml diff --git a/drivers/clock_control/clock_control_ifx_cat1.c b/drivers/clock_control/clock_control_ifx_cat1.c index ad08170e318..cebefaed45e 100644 --- a/drivers/clock_control/clock_control_ifx_cat1.c +++ b/drivers/clock_control/clock_control_ifx_cat1.c @@ -388,8 +388,6 @@ static cy_rslt_t _configure_path_mux(cyhal_clock_t *clock_obj, cyhal_clock_t *cl { cy_rslt_t rslt; - ARG_UNUSED(clock_source_obj); - rslt = cyhal_clock_reserve(clock_obj, reserve_obj); if (rslt == CY_RSLT_SUCCESS) { diff --git a/drivers/timer/CMakeLists.txt b/drivers/timer/CMakeLists.txt index 2dc631c0d6d..abc98e484ea 100644 --- a/drivers/timer/CMakeLists.txt +++ b/drivers/timer/CMakeLists.txt @@ -48,3 +48,4 @@ zephyr_library_sources_ifdef(CONFIG_SMARTBOND_TIMER smartbond_timer.c) zephyr_library_sources_ifdef(CONFIG_MTK_ADSP_TIMER mtk_adsp_timer.c) zephyr_library_sources_ifdef(CONFIG_SY1XX_SYS_TIMER sy1xx_sys_timer.c) zephyr_library_sources_ifdef(CONFIG_RZA2M_OS_TIMER renesas_rza2m_os_timer.c) +zephyr_library_sources_ifdef(CONFIG_INFINEON_CAT1_LP_TIMER ifx_cat1_lp_timer.c) diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig index 7a23d910ef9..8927777ce26 100644 --- a/drivers/timer/Kconfig +++ b/drivers/timer/Kconfig @@ -106,6 +106,7 @@ source "drivers/timer/Kconfig.mtk_adsp" source "drivers/timer/Kconfig.sy1xx_sys_timer" source "drivers/timer/Kconfig.renesas_ra_ulpt" source "drivers/timer/Kconfig.renesas_rza2m" +source "drivers/timer/Kconfig.ifx_cat1_lp" endmenu diff --git a/drivers/timer/Kconfig.ifx_cat1_lp b/drivers/timer/Kconfig.ifx_cat1_lp new file mode 100644 index 00000000000..dc05bc7a1a8 --- /dev/null +++ b/drivers/timer/Kconfig.ifx_cat1_lp @@ -0,0 +1,17 @@ +# Infineon CAT1 LPTIMER configuration options + +# Copyright (c) 2025 Cypress Semiconductor Corporation (an Infineon company) or +# an affiliate of Cypress Semiconductor Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +config INFINEON_CAT1_LP_TIMER + bool "Infineon CAT1 Low Power Timer driver" + default y + depends on DT_HAS_INFINEON_CAT1_LP_TIMER_ENABLED + depends on PM + select USE_INFINEON_LPTIMER + select TICKLESS_CAPABLE + help + This module implements a kernel device driver for the LowPower Timer + and provides the standard "system clock driver" interfaces. diff --git a/drivers/timer/ifx_cat1_lp_timer.c b/drivers/timer/ifx_cat1_lp_timer.c new file mode 100644 index 00000000000..6b49be7a20f --- /dev/null +++ b/drivers/timer/ifx_cat1_lp_timer.c @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2025 Cypress Semiconductor Corporation (an Infineon company) or + * an affiliate of Cypress Semiconductor Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief Low Power timer driver for Infineon CAT1 MCU family. + */ + +#define DT_DRV_COMPAT infineon_cat1_lp_timer + +#include +#include +#include +#include +#include +#include + +#include + +#include +LOG_MODULE_REGISTER(ifx_cat1_lp_timer, CONFIG_KERNEL_LOG_LEVEL); + +/* The application only needs one lptimer. Report an error if more than one is selected. */ +#if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) > 1 +#error Only one LPTIMER instance should be enabled +#endif + +#define LPTIMER_INTR_PRIORITY (3u) +#define LPTIMER_FREQ (32768u) + +/* We need to know the number of MCWDT instances. Unfortunately, this information is not available + * in a header in the HAL code. This was extracted from the cyhal_lptimer.c file in the HAL + */ +#if (defined(CY_IP_MXS40SRSS) || defined(CY_IP_MXS40SSRSS) || defined(CY_IP_MXS28SRSS) || \ + defined(CY_IP_MXS22SRSS)) && \ + !((defined(CY_IP_MXS40SRSS) && (CY_IP_MXS40SRSS_VERSION >= 3)) || \ + ((SRSS_NUM_MCWDT_B) > 0)) +#define NUM_LPTIMERS SRSS_NUM_MCWDT +#else +#error "Selected device doesn't support low power timers at this time." +#endif + +cyhal_lptimer_t lptimer_obj; +static uint32_t last_lptimer_value; +static struct k_spinlock lock; + +static void lptimer_interrupt_handler(void *handler_arg, cyhal_lptimer_event_t event) +{ + CY_UNUSED_PARAMETER(handler_arg); + CY_UNUSED_PARAMETER(event); + + k_spinlock_key_t key = k_spin_lock(&lock); + + /* announce the elapsed time in ms */ + uint32_t lptimer_value = cyhal_lptimer_read(&lptimer_obj); + uint32_t delta_ticks = + ((uint64_t)(lptimer_value - last_lptimer_value) * CONFIG_SYS_CLOCK_TICKS_PER_SEC) / + LPTIMER_FREQ; + sys_clock_announce(IS_ENABLED(CONFIG_TICKLESS_KERNEL) ? delta_ticks : (delta_ticks > 0)); + last_lptimer_value += (delta_ticks * LPTIMER_FREQ) / CONFIG_SYS_CLOCK_TICKS_PER_SEC; + + k_spin_unlock(&lock, key); +} + +void sys_clock_set_timeout(int32_t ticks, bool idle) +{ + ARG_UNUSED(idle); + + k_spinlock_key_t key = {0}; + + if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { + return; + } + + if (ticks == K_TICKS_FOREVER) { + key = k_spin_lock(&lock); + /* Disable the LPTIMER events */ + cyhal_lptimer_enable_event(&lptimer_obj, CYHAL_LPTIMER_COMPARE_MATCH, + LPTIMER_INTR_PRIORITY, false); + k_spin_unlock(&lock, key); + return; + } + + /* passing ticks==1 means "announce the next tick", ticks value of zero (or even negative) + * is legal and treated identically: it simply indicates the kernel would like the next + * tick announcement as soon as possible. + */ + if (ticks < 1) { + ticks = 1; + } + + uint32_t set_ticks = ((uint32_t)(ticks)*LPTIMER_FREQ) / CONFIG_SYS_CLOCK_TICKS_PER_SEC; + + key = k_spin_lock(&lock); + + /* Configure and Enable the LPTIMER events */ + cyhal_lptimer_enable_event(&lptimer_obj, CYHAL_LPTIMER_COMPARE_MATCH, LPTIMER_INTR_PRIORITY, + true); + /* Set the delay value for the next wakeup interrupt */ + cyhal_lptimer_set_delay(&lptimer_obj, set_ticks); + + k_spin_unlock(&lock, key); +} + +uint32_t sys_clock_elapsed(void) +{ + if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { + return 0; + } + + k_spinlock_key_t key = k_spin_lock(&lock); + + uint32_t lptimer_value = cyhal_lptimer_read(&lptimer_obj); + + k_spin_unlock(&lock, key); + + /* gives the value of LPTIM counter (ms) since the previous 'announce' */ + uint64_t ret = (((uint64_t)(lptimer_value - last_lptimer_value)) * + CONFIG_SYS_CLOCK_TICKS_PER_SEC) / + LPTIMER_FREQ; + + return (uint32_t)ret; +} + +uint32_t sys_clock_cycle_get_32(void) +{ + /* just gives the accumulated count in a number of hw cycles */ + + k_spinlock_key_t key = k_spin_lock(&lock); + + uint32_t lp_time = cyhal_lptimer_read(&lptimer_obj); + + k_spin_unlock(&lock, key); + + /* convert lptim count in a nb of hw cycles with precision */ + uint64_t ret = ((uint64_t)lp_time * sys_clock_hw_cycles_per_sec()) / LPTIMER_FREQ; + + /* convert in hw cycles (keeping 32bit value) */ + return (uint32_t)ret; +} + +static int sys_clock_driver_init(void) +{ + cy_rslt_t result; + cyhal_lptimer_t lptimer_objs[NUM_LPTIMERS]; + + /* Currently with the HAL, there is no way to directly/explicitly select the MCWDT + * enabled in the .dts file. So, instead, initialize LPTIMERs until we find + * the one from the .dts file. Free the others when done. + */ + for (int32_t lptimer_index = 0; lptimer_index < NUM_LPTIMERS; lptimer_index++) { + /* Initialize the LPTIMER with default configuration */ + result = cyhal_lptimer_init(&lptimer_obj); + + if (result != CY_RSLT_SUCCESS) { + LOG_ERR("LPTimer instance not found. Error: 0x%08X\n", + (unsigned int)result); + return -EIO; + } + + if ((uint32_t)lptimer_obj.base == DT_INST_REG_ADDR(0)) { + for (lptimer_index--; lptimer_index >= 0; lptimer_index--) { + cyhal_lptimer_free(&lptimer_objs[lptimer_index]); + } + break; + } + + cyhal_lptimer_free(&lptimer_obj); + cyhal_lptimer_init(&lptimer_objs[lptimer_index]); + } + + /* Register the callback handler which will be invoked when the interrupt triggers */ + cyhal_lptimer_register_callback(&lptimer_obj, lptimer_interrupt_handler, NULL); + + if (result != CY_RSLT_SUCCESS) { + LOG_ERR("Sys Clock initialization failed. Error: 0x%08X\n", (unsigned int)result); + return -EIO; + } + + return 0; +} + +SYS_INIT(sys_clock_driver_init, PRE_KERNEL_2, CONFIG_SYSTEM_CLOCK_INIT_PRIORITY); diff --git a/dts/bindings/timer/infineon,cat1-lp-timer.yaml b/dts/bindings/timer/infineon,cat1-lp-timer.yaml new file mode 100644 index 00000000000..15fae8c9b5d --- /dev/null +++ b/dts/bindings/timer/infineon,cat1-lp-timer.yaml @@ -0,0 +1,14 @@ +# Copyright (c) 2025 Cypress Semiconductor Corporation (an Infineon company) or +# an affiliate of Cypress Semiconductor Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +description: Infineon Cat1 low power timer + +compatible: "infineon,cat1-lp-timer" + +include: [base.yaml, "infineon,system-interrupts.yaml"] + +properties: + reg: + required: true