diff --git a/drivers/timer/CMakeLists.txt b/drivers/timer/CMakeLists.txt index b642faa8b97..5e783902ae4 100644 --- a/drivers/timer/CMakeLists.txt +++ b/drivers/timer/CMakeLists.txt @@ -32,6 +32,7 @@ zephyr_library_sources_ifdef(CONFIG_NATIVE_SIM_TIMER native_sim_timer.c) zephyr_library_sources_ifdef(CONFIG_NPCX_ITIM_TIMER npcx_itim_timer.c) zephyr_library_sources_ifdef(CONFIG_NRF_GRTC_TIMER nrf_grtc_timer.c) zephyr_library_sources_ifdef(CONFIG_NRF_RTC_TIMER nrf_rtc_timer.c) +zephyr_library_sources_ifdef(CONFIG_RENESAS_RX_TIMER_CMT renesas_rx_cmt.c) zephyr_library_sources_ifdef(CONFIG_RCAR_CMT_TIMER rcar_cmt_timer.c) zephyr_library_sources_ifdef(CONFIG_RENESAS_RA_ULPT_TIMER renesas_ra_ulpt_timer.c) zephyr_library_sources_ifdef(CONFIG_RISCV_MACHINE_TIMER riscv_machine_timer.c) diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig index 225f23fef35..165d85212af 100644 --- a/drivers/timer/Kconfig +++ b/drivers/timer/Kconfig @@ -90,6 +90,7 @@ source "drivers/timer/Kconfig.npcx_itim" source "drivers/timer/Kconfig.nrf_rtc" source "drivers/timer/Kconfig.nrf_grtc" source "drivers/timer/Kconfig.nrf_xrtc" +source "drivers/timer/Kconfig.renesas_rx" source "drivers/timer/Kconfig.rcar_cmt" source "drivers/timer/Kconfig.riscv_machine" source "drivers/timer/Kconfig.rv32m1_lptmr" diff --git a/drivers/timer/Kconfig.renesas_rx b/drivers/timer/Kconfig.renesas_rx new file mode 100644 index 00000000000..7ddaf232352 --- /dev/null +++ b/drivers/timer/Kconfig.renesas_rx @@ -0,0 +1,13 @@ +# Copyright (c) 2024 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +config RENESAS_RX_TIMER_CMT + bool "Renesas RX timer CMT" + default y + depends on DT_HAS_RENESAS_RX_TIMER_CMT_ENABLED + select TIMER_HAS_64BIT_CYCLE_COUNTER + select TICKLESS_CAPABLE + help + This module implements a kernel device driver for the Renesas RX + platform provides the standard "system clock driver" interfaces. + If unchecked, no timer will be used. diff --git a/drivers/timer/renesas_rx_cmt.c b/drivers/timer/renesas_rx_cmt.c new file mode 100644 index 00000000000..ff44000edeb --- /dev/null +++ b/drivers/timer/renesas_rx_cmt.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2024 Renesas Electronics Corporation + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "zephyr/sys_clock.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#define DT_DRV_COMPAT renesas_rx_timer_cmt + +#define CMT_NODE DT_NODELABEL(cmt) +#define CMT0_NODE DT_NODELABEL(cmt0) +#define CMT1_NODE DT_NODELABEL(cmt1) + +#define ICU_NODE DT_NODELABEL(icu) + +#define ICU_IR_ADDR DT_REG_ADDR_BY_NAME(ICU_NODE, IR) +#define ICU_IER_ADDR DT_REG_ADDR_BY_NAME(ICU_NODE, IER) +#define ICU_IPR_ADDR DT_REG_ADDR_BY_NAME(ICU_NODE, IPR) +#define ICU_FIR_ADDR DT_REG_ADDR_BY_NAME(ICU_NODE, FIR) +#define ICU_IR ((volatile uint8_t *)ICU_IR_ADDR) + +#define CMT0_IRQ_NUM DT_IRQ_BY_NAME(CMT0_NODE, cmi, irq) +#define CMT1_IRQ_NUM DT_IRQ_BY_NAME(CMT1_NODE, cmi, irq) + +#define COUNTER_MAX 0x0000ffff + +#define CYCLES_PER_SEC (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC) +#define TICKS_PER_SEC (CONFIG_SYS_CLOCK_TICKS_PER_SEC) +#define CYCLES_PER_TICK (CYCLES_PER_SEC / TICKS_PER_SEC) + +#define CYCLES_CYCLE_TIMER (COUNTER_MAX + 1) +#define MSTPCR_ADDR ((volatile uint32_t *)0x00080010) /* MSTPCR register address */ +#define MSTP_CMT0_BIT (1 << 15) /* Bit 15 controls MSTPA15 (CMT0) */ + +static const struct clock_control_rx_subsys_cfg cmt_clk_cfg = { + .mstp = DT_CLOCKS_CELL_BY_IDX(DT_INST_PARENT(0), 0, mstp), + .stop_bit = DT_CLOCKS_CELL_BY_IDX(DT_INST_PARENT(0), 0, stop_bit), +}; + +struct timer_rx_cfg { + volatile uint16_t *cmstr; + volatile uint16_t *cmcr; + volatile uint16_t *cmcnt; + volatile uint16_t *cmcor; +}; + +static const struct timer_rx_cfg tick_timer_cfg = { + .cmstr = (uint16_t *)DT_REG_ADDR_BY_NAME(CMT_NODE, CMSTR0), + .cmcr = (uint16_t *)DT_REG_ADDR_BY_NAME(CMT0_NODE, CMCR), + .cmcnt = (uint16_t *)DT_REG_ADDR_BY_NAME(CMT0_NODE, CMCNT), + .cmcor = (uint16_t *)DT_REG_ADDR_BY_NAME(CMT0_NODE, CMCOR)}; + +static const struct timer_rx_cfg cycle_timer_cfg = { + .cmstr = (uint16_t *)DT_REG_ADDR_BY_NAME(CMT_NODE, CMSTR0), + .cmcr = (uint16_t *)DT_REG_ADDR_BY_NAME(CMT1_NODE, CMCR), + .cmcnt = (uint16_t *)DT_REG_ADDR_BY_NAME(CMT1_NODE, CMCNT), + .cmcor = (uint16_t *)DT_REG_ADDR_BY_NAME(CMT1_NODE, CMCOR)}; + +#ifdef CONFIG_TIMER_HAS_64BIT_CYCLE_COUNTER +typedef uint64_t cycle_t; +#define CYCLE_COUNT_MAX (0xffffffffffffffff) +#else +typedef uint32_t cycle_t; +#define CYCLE_COUNT_MAX (0xffffffff) +#endif +static cycle_t cycle_count; + +static uint16_t clock_cycles_per_tick; + +static volatile cycle_t announced_cycle_count; + +static struct k_spinlock lock; + +#if defined(CONFIG_TEST) +const int32_t z_sys_timer_irq_for_test = CMT0_IRQ_NUM; +#endif + +static cycle_t cmt1_elapsed(void) +{ + uint32_t val1 = (uint32_t)(*cycle_timer_cfg.cmcnt); + uint8_t cmt_ir = ICU_IR[29]; + uint32_t val2 = (uint32_t)(*cycle_timer_cfg.cmcnt); + + if ((1 == cmt_ir) || (val1 > val2)) { + cycle_count += CYCLES_CYCLE_TIMER; + + ICU_IR[29] = 0; + } + + return (val2 + cycle_count); +} + +uint32_t sys_clock_cycle_get_32(void) +{ + k_spinlock_key_t key = k_spin_lock(&lock); + + uint32_t ret = (uint32_t)cmt1_elapsed(); + + k_spin_unlock(&lock, key); + + return ret; +} + +#ifdef CONFIG_TIMER_HAS_64BIT_CYCLE_COUNTER +uint64_t sys_clock_cycle_get_64(void) +{ + k_spinlock_key_t key = k_spin_lock(&lock); + + uint64_t ret = (uint64_t)cmt1_elapsed(); + + k_spin_unlock(&lock, key); + + return ret; +} +#endif /* CONFIG_TIMER_HAS_64BIT_CYCLE_COUNTER */ + +uint32_t sys_clock_elapsed(void) +{ + if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { + /* Always return 0 for tickful operation */ + return 0; + } + + k_spinlock_key_t key = k_spin_lock(&lock); + + uint32_t ret; + cycle_t current_cycle_count; + + current_cycle_count = cmt1_elapsed(); + + if (current_cycle_count < announced_cycle_count) { + /* cycle_count overflowed */ + ret = (uint32_t)((current_cycle_count + + (CYCLE_COUNT_MAX - announced_cycle_count + 1)) / + CYCLES_PER_TICK); + } else { + ret = (uint32_t)((current_cycle_count - announced_cycle_count) / CYCLES_PER_TICK); + } + + k_spin_unlock(&lock, key); + + return ret; +} + +static void cmt0_isr(void) +{ + k_ticks_t dticks; + cycle_t current_cycle_count; + + k_spinlock_key_t key = k_spin_lock(&lock); + + current_cycle_count = cmt1_elapsed(); + + if (current_cycle_count < announced_cycle_count) { + /* cycle_count overflowed */ + dticks = (k_ticks_t)((current_cycle_count + + (CYCLE_COUNT_MAX - announced_cycle_count + 1)) / + CYCLES_PER_TICK); + } else { + dticks = (k_ticks_t)((current_cycle_count - announced_cycle_count) / + CYCLES_PER_TICK); + } + + announced_cycle_count = (current_cycle_count / CYCLES_PER_TICK) * CYCLES_PER_TICK; + + k_spin_unlock(&lock, key); + + if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { + sys_clock_announce(1); + } else { + sys_clock_announce(dticks); + } +} + +static int sys_clock_driver_init(void) +{ + const struct device *clk = DEVICE_DT_GET(DT_CLOCKS_CTLR(DT_INST_PARENT(0))); + int ret; + + if (!device_is_ready(clk)) { + return -ENODEV; + } + + ret = clock_control_on(clk, (clock_control_subsys_t)&cmt_clk_cfg); + if (ret < 0) { + return ret; + } + + *tick_timer_cfg.cmcr = 0x00C0; /* enable CMT0 interrupt */ + *cycle_timer_cfg.cmcr = 0x00C0; /* enable CMT1 interrupt */ + + clock_cycles_per_tick = (uint16_t)(CYCLES_PER_TICK); + *tick_timer_cfg.cmcor = clock_cycles_per_tick - 1; + *cycle_timer_cfg.cmcor = (uint16_t)COUNTER_MAX; + + IRQ_CONNECT(CMT0_IRQ_NUM, 0x01, cmt0_isr, NULL, 0); + irq_enable(CMT0_IRQ_NUM); + + *tick_timer_cfg.cmstr = 0x0003; /* start cmt0,1 */ + + return 0; +} + +void sys_clock_set_timeout(int32_t ticks, bool idle) +{ + if (IS_ENABLED(CONFIG_TICKLESS_KERNEL) && idle && ticks == K_TICKS_FOREVER) { + *tick_timer_cfg.cmstr = 0x0000; /* stop cmt0,1 */ + return; + } + +#if defined(CONFIG_TICKLESS_KERNEL) + /* + * By default, it is implemented as a no operation. + * Implement the processing as needed. + */ +#endif /* CONFIG_TICKLESS_KERNEL */ +} + +SYS_INIT(sys_clock_driver_init, PRE_KERNEL_2, CONFIG_SYSTEM_CLOCK_INIT_PRIORITY); diff --git a/dts/bindings/timer/renesas,rx-timer-cmt-start-control.yaml b/dts/bindings/timer/renesas,rx-timer-cmt-start-control.yaml new file mode 100644 index 00000000000..05c8289f5cd --- /dev/null +++ b/dts/bindings/timer/renesas,rx-timer-cmt-start-control.yaml @@ -0,0 +1,23 @@ +# Copyright (c) 2021 KT-Elektronik, Klaucke und Partner GmbH +# Copyright (c) 2024 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Renesas RX timer node + +compatible: "renesas,rx-timer-cmt-start-control" + +include: base.yaml + +properties: + reg: + required: true + + clocks: + required: true + + clock-frequency: + type: int + required: true + description: | + Clock frequency information for Timer operation + This value need to be set at PCLKB/8 diff --git a/dts/bindings/timer/renesas,rx-timer-cmt.yaml b/dts/bindings/timer/renesas,rx-timer-cmt.yaml new file mode 100644 index 00000000000..d6f1be03203 --- /dev/null +++ b/dts/bindings/timer/renesas,rx-timer-cmt.yaml @@ -0,0 +1,13 @@ +# Copyright (c) 2021 KT-Elektronik, Klaucke und Partner GmbH +# Copyright (c) 2024 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Renesas RX timer node + +compatible: "renesas,rx-timer-cmt" + +include: base.yaml + +properties: + reg: + required: true diff --git a/dts/rx/renesas/rx130-common.dtsi b/dts/rx/renesas/rx130-common.dtsi index 2eef76b22a0..1e3c69b4397 100644 --- a/dts/rx/renesas/rx130-common.dtsi +++ b/dts/rx/renesas/rx130-common.dtsi @@ -71,13 +71,38 @@ }; }; - cmt0: timer@88004 { - compatible = "renesas,rx-timer-cmt"; - reg = <0x00088004 0x02>, - <0x00088000 0x02>, - <0x00088002 0x02>, - <0x00088006 0x02>; - status = "disabled"; + cmt: timer@88000 { + compatible = "renesas,rx-timer-cmt-start-control"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x00088000 0x02>; + clocks = <&pclkb MSTPA 15>; + reg-names = "CMSTR0"; + status = "okay"; + + cmt0: timer@88002 { + compatible = "renesas,rx-timer-cmt"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x00088002 0x02>, + <0x00088004 0x02>, + <0x00088006 0x02>; + reg-names = "CMCR", "CMCNT", "CMCOR"; + interrupts = <28 1>; + interrupt-names = "cmi"; + status = "okay"; + }; + + cmt1: timer@88008 { + compatible = "renesas,rx-timer-cmt"; + reg = <0x00088008 0x02>, + <0x0008800A 0x02>, + <0x0008800C 0x02>; + reg-names = "CMCR", "CMCNT", "CMCOR"; + interrupts = <29 1>; + interrupt-names = "cmi"; + status = "disabled"; + }; }; ofsm: ofsm@ffffff80 { diff --git a/soc/renesas/rx/rx130/Kconfig.defconfig b/soc/renesas/rx/rx130/Kconfig.defconfig index ecfc7a86e34..8c942fd0627 100644 --- a/soc/renesas/rx/rx130/Kconfig.defconfig +++ b/soc/renesas/rx/rx130/Kconfig.defconfig @@ -3,6 +3,17 @@ if SOC_SERIES_RX130 +DT_CMT_PATH := $(dt_nodelabel_path,cmt) + +config SYS_CLOCK_HW_CYCLES_PER_SEC + default $(dt_node_int_prop_int,$(DT_CMT_PATH),clock-frequency) + +# SYS_CLOCK_TICKS_PER_SEC is set to 100 if PCLKB is 48MHz or less. +# (PCLKB = SYS_CLOCK_HW_CYCLES_PER_SEC * 8) +config SYS_CLOCK_TICKS_PER_SEC + default 100 if SYS_CLOCK_HW_CYCLES_PER_SEC <= 6000000 + default 1000 + config INITIALIZATION_STACK_SIZE default 512 diff --git a/soc/renesas/rx/rx62n/Kconfig.defconfig b/soc/renesas/rx/rx62n/Kconfig.defconfig index 5cb5b9a77c3..eaf02e0eb14 100644 --- a/soc/renesas/rx/rx62n/Kconfig.defconfig +++ b/soc/renesas/rx/rx62n/Kconfig.defconfig @@ -3,6 +3,17 @@ if SOC_SERIES_RX62N +DT_CMT_PATH := $(dt_nodelabel_path,cmt) + +config SYS_CLOCK_HW_CYCLES_PER_SEC + default $(dt_node_int_prop_int,$(DT_CMT_PATH),clock-frequency) + +# SYS_CLOCK_TICKS_PER_SEC is set to 100 if PCLKB is 48MHz or less. +# (PCLKB = SYS_CLOCK_HW_CYCLES_PER_SEC * 8) +config SYS_CLOCK_TICKS_PER_SEC + default 100 if SYS_CLOCK_HW_CYCLES_PER_SEC <= 6000000 + default 10000 + config INITIALIZATION_STACK_SIZE default 512