diff --git a/drivers/timer/CMakeLists.txt b/drivers/timer/CMakeLists.txt index abc98e484ea..3c488da9a3e 100644 --- a/drivers/timer/CMakeLists.txt +++ b/drivers/timer/CMakeLists.txt @@ -47,5 +47,6 @@ zephyr_library_sources_ifdef(CONFIG_XTENSA_TIMER xtensa_sys_timer.c) 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_RZ_OS_TIMER renesas_rz_gtm_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 8927777ce26..c154370c456 100644 --- a/drivers/timer/Kconfig +++ b/drivers/timer/Kconfig @@ -105,6 +105,7 @@ source "drivers/timer/Kconfig.xtensa" 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_rz" source "drivers/timer/Kconfig.renesas_rza2m" source "drivers/timer/Kconfig.ifx_cat1_lp" diff --git a/drivers/timer/Kconfig.renesas_rz b/drivers/timer/Kconfig.renesas_rz new file mode 100644 index 00000000000..aa03451ffa7 --- /dev/null +++ b/drivers/timer/Kconfig.renesas_rz @@ -0,0 +1,14 @@ +# Copyright (c) 2024 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +config RZ_OS_TIMER + bool "Renesas RZ OS timer" + default y + depends on DT_HAS_RENESAS_RZ_GTM_OS_TIMER_ENABLED + select TIMER_READS_ITS_FREQUENCY_AT_RUNTIME + select SYSTEM_TIMER_HAS_DISABLE_SUPPORT + select TICKLESS_CAPABLE + select USE_RZ_FSP_GTM + help + This module implements a kernel device driver for the Renesas RZ + platform provides the standard "system clock driver" interfaces. diff --git a/drivers/timer/renesas_rz_gtm_timer.c b/drivers/timer/renesas_rz_gtm_timer.c new file mode 100644 index 00000000000..636f6ba3c25 --- /dev/null +++ b/drivers/timer/renesas_rz_gtm_timer.c @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2024 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(renesas_rz_gtm_timer); + +#define DT_DRV_COMPAT renesas_rz_gtm_os_timer +#define TIMER_NODE DT_INST_PARENT(0) + +#define cycle_diff_t uint32_t +#define CYCLE_DIFF_MAX (~(cycle_diff_t)0) + +/* + * We have two constraints on the maximum number of cycles we can wait for. + * + * 1) sys_clock_announce() accepts at most INT32_MAX ticks. + * + * 2) The number of cycles between two reports must fit in a cycle_diff_t + * variable before converting it to ticks. + * + * Then: + * + * 3) Pick the smallest between (1) and (2). + * + * 4) Take into account some room for the unavoidable IRQ servicing latency. + * Let's use 3/4 of the max range. + * + * Finally let's add the LSB value to the result so to clear out a bunch of + * consecutive set bits coming from the original max values to produce a + * nicer literal for assembly generation. + */ +#define CYCLES_MAX_1 ((uint64_t)INT32_MAX * (uint64_t)CYC_PER_TICK) +#define CYCLES_MAX_2 ((uint64_t)CYCLE_DIFF_MAX) +#define CYCLES_MAX_3 MIN(CYCLES_MAX_1, CYCLES_MAX_2) +#define CYCLES_MAX_4 (CYCLES_MAX_3 / 2 + CYCLES_MAX_3 / 4) +#define CYCLES_MAX_5 (CYCLES_MAX_4 + LSB_GET(CYCLES_MAX_4)) + +/* precompute CYCLES_MAX and CYC_PER_TICK at driver init to avoid runtime double divisions */ +static uint64_t cycles_max; +static uint32_t cyc_per_tick; +#define CYCLES_MAX cycles_max +#define CYC_PER_TICK cyc_per_tick + +static void ostm_irq_handler(timer_callback_args_t *arg); +void gtm_int_isr(void); +const struct device *g_os_timer_dev = DEVICE_DT_INST_GET(0); +extern unsigned int z_clock_hw_cycles_per_sec; + +struct rz_os_timer_config { + timer_cfg_t *fsp_cfg; + const timer_api_t *fsp_api; +}; + +struct rz_os_timer_data { + timer_ctrl_t *fsp_ctrl; + struct k_spinlock lock; + uint32_t last_cycle; + uint32_t last_tick; + uint32_t last_elapsed; +}; + +static void ostm_irq_handler(timer_callback_args_t *arg) +{ + ARG_UNUSED(arg); + + struct rz_os_timer_data *data = (struct rz_os_timer_data *)g_os_timer_dev->data; + + uint32_t delta_cycles = sys_clock_cycle_get_32() - data->last_cycle; + uint32_t delta_ticks = delta_cycles / CYC_PER_TICK; + + data->last_cycle += delta_ticks * CYC_PER_TICK; + data->last_tick += delta_ticks; + data->last_elapsed = 0; + + if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { + struct rz_os_timer_config *config = + (struct rz_os_timer_config *)g_os_timer_dev->config; + uint32_t next_cycle = data->last_cycle + CYC_PER_TICK; + + config->fsp_api->periodSet(data->fsp_ctrl, next_cycle); + } else { + irq_disable(DT_IRQN(TIMER_NODE)); + } + + /* Announce to the kernel */ + sys_clock_announce(delta_ticks); +} + +void sys_clock_set_timeout(int32_t ticks, bool idle) +{ + if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { + return; + } + + if (idle && ticks == K_TICKS_FOREVER) { + return; + } + + struct rz_os_timer_config *config = (struct rz_os_timer_config *)g_os_timer_dev->config; + struct rz_os_timer_data *data = (struct rz_os_timer_data *)g_os_timer_dev->data; + uint32_t next_cycle; + + k_spinlock_key_t key = k_spin_lock(&data->lock); + + if (ticks == K_TICKS_FOREVER) { + next_cycle = data->last_cycle + CYCLES_MAX; + } else { + next_cycle = (data->last_tick + data->last_elapsed + ticks) * CYC_PER_TICK; + if ((next_cycle - data->last_cycle) > CYCLES_MAX) { + next_cycle = data->last_cycle + CYCLES_MAX; + } + } + + config->fsp_api->periodSet(data->fsp_ctrl, next_cycle); + irq_enable(DT_IRQN(TIMER_NODE)); + + k_spin_unlock(&data->lock, key); +} + +uint32_t sys_clock_elapsed(void) +{ + if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { + return 0; + } + + struct rz_os_timer_data *data = (struct rz_os_timer_data *)g_os_timer_dev->data; + uint32_t delta_cycles = sys_clock_cycle_get_32() - data->last_cycle; + uint32_t delta_ticks = delta_cycles / CYC_PER_TICK; + + data->last_elapsed = delta_ticks; + + return delta_ticks; +} + +void sys_clock_disable(void) +{ + struct rz_os_timer_config *config = (struct rz_os_timer_config *)g_os_timer_dev->config; + struct rz_os_timer_data *data = (struct rz_os_timer_data *)g_os_timer_dev->data; + + config->fsp_api->close(data->fsp_ctrl); +} + +uint32_t sys_clock_cycle_get_32(void) +{ + struct rz_os_timer_config *config = (struct rz_os_timer_config *)g_os_timer_dev->config; + struct rz_os_timer_data *data = (struct rz_os_timer_data *)g_os_timer_dev->data; + timer_status_t timer_status; + k_spinlock_key_t key = k_spin_lock(&data->lock); + + config->fsp_api->statusGet(data->fsp_ctrl, &timer_status); + k_spin_unlock(&data->lock, key); + + return timer_status.counter; +} + +static int sys_clock_driver_init(void) +{ + fsp_err_t ret; + struct rz_os_timer_config *config = (struct rz_os_timer_config *)g_os_timer_dev->config; + struct rz_os_timer_data *data = (struct rz_os_timer_data *)g_os_timer_dev->data; + + IRQ_CONNECT(DT_IRQN(TIMER_NODE), DT_IRQ(TIMER_NODE, priority), gtm_int_isr, + DEVICE_DT_INST_GET(0), 0); + + data->last_tick = 0; + data->last_cycle = 0; + z_clock_hw_cycles_per_sec = R_FSP_SystemClockHzGet(FSP_PRIV_CLOCK_P0CLK); + cyc_per_tick = sys_clock_hw_cycles_per_sec() / CONFIG_SYS_CLOCK_TICKS_PER_SEC; + cycles_max = CYCLES_MAX_5; + config->fsp_cfg->period_counts = CYC_PER_TICK; + ret = config->fsp_api->open(data->fsp_ctrl, config->fsp_cfg); + if (ret != FSP_SUCCESS) { + LOG_ERR("timer initialize failed"); + return -EIO; + } + + ret = config->fsp_api->start(data->fsp_ctrl); + if (ret != FSP_SUCCESS) { + LOG_ERR("timer start failed"); + return -EIO; + } + + return 0; +} + +#define OS_TIMER_RZG_GTM_INIT() \ + const gtm_extended_cfg_t g_timer0_extend = { \ + .generate_interrupt_when_starts = GTM_GIWS_TYPE_DISABLED, \ + .gtm_mode = GTM_TIMER_MODE_FREERUN, \ + }; \ + \ + static timer_cfg_t g_timer0_cfg = { \ + .mode = TIMER_MODE_PERIODIC, \ + .period_counts = 0, \ + .channel = DT_PROP(TIMER_NODE, channel), \ + .p_callback = ostm_irq_handler, \ + .p_context = DEVICE_DT_INST_GET(0), \ + .p_extend = &g_timer0_extend, \ + .cycle_end_ipl = DT_IRQ(TIMER_NODE, priority), \ + .cycle_end_irq = DT_IRQN(TIMER_NODE), \ + }; \ + \ + static gtm_instance_ctrl_t g_timer0_ctrl; \ + \ + static struct rz_os_timer_data g_rz_os_timer_data = { \ + .fsp_ctrl = (timer_ctrl_t *)&g_timer0_ctrl, \ + }; \ + \ + struct rz_os_timer_config g_rz_os_timer_config = { \ + .fsp_cfg = &g_timer0_cfg, \ + .fsp_api = &g_timer_on_gtm, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(0, NULL, NULL, &g_rz_os_timer_data, &g_rz_os_timer_config, \ + PRE_KERNEL_2, CONFIG_SYSTEM_CLOCK_INIT_PRIORITY, NULL); \ + \ + SYS_INIT(sys_clock_driver_init, PRE_KERNEL_2, CONFIG_SYSTEM_CLOCK_INIT_PRIORITY); + +OS_TIMER_RZG_GTM_INIT(); diff --git a/dts/bindings/timer/renesas,rz-gtm-os-timer.yaml b/dts/bindings/timer/renesas,rz-gtm-os-timer.yaml new file mode 100644 index 00000000000..9eeec9dccb3 --- /dev/null +++ b/dts/bindings/timer/renesas,rz-gtm-os-timer.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Renesas RZ OS timer + +compatible: "renesas,rz-gtm-os-timer" + +include: base.yaml