From cfb2074a5e939bc6737fa9d63d211ff0f5a2dd94 Mon Sep 17 00:00:00 2001 From: Lin Yu-Cheng Date: Fri, 22 Nov 2024 18:27:29 +0800 Subject: [PATCH] driver: timer: Add timer driver initial version of RTS5912. Add timer driver for Realtek RTS5912. Signed-off-by: Lin Yu-Cheng --- drivers/timer/CMakeLists.txt | 1 + drivers/timer/Kconfig | 1 + drivers/timer/Kconfig.realtek_rts5912_rtmr | 13 + drivers/timer/realtek_rts5912_rtmr.c | 251 ++++++++++++++++++ dts/arm/realtek/ec/rts5912.dtsi | 19 ++ dts/bindings/timer/realtek,rts5912-rtmr.yaml | 17 ++ .../timer/realtek,rts5912-slwtimer.yaml | 30 +++ soc/realtek/ec/rts5912/reg/reg_rtmr.h | 31 +++ 8 files changed, 363 insertions(+) create mode 100644 drivers/timer/Kconfig.realtek_rts5912_rtmr create mode 100644 drivers/timer/realtek_rts5912_rtmr.c create mode 100644 dts/bindings/timer/realtek,rts5912-rtmr.yaml create mode 100644 dts/bindings/timer/realtek,rts5912-slwtimer.yaml create mode 100644 soc/realtek/ec/rts5912/reg/reg_rtmr.h diff --git a/drivers/timer/CMakeLists.txt b/drivers/timer/CMakeLists.txt index 2b837fba480..51b0e552f4a 100644 --- a/drivers/timer/CMakeLists.txt +++ b/drivers/timer/CMakeLists.txt @@ -33,6 +33,7 @@ zephyr_library_sources_ifdef(CONFIG_NRF_RTC_TIMER nrf_rtc_timer.c) zephyr_library_sources_ifdef(CONFIG_RCAR_CMT_TIMER rcar_cmt_timer.c) zephyr_library_sources_ifdef(CONFIG_RISCV_MACHINE_TIMER riscv_machine_timer.c) zephyr_library_sources_ifdef(CONFIG_RV32M1_LPTMR_TIMER rv32m1_lptmr_timer.c) +zephyr_library_sources_ifdef(CONFIG_REALTEK_RTS5912_RTMR realtek_rts5912_rtmr.c) zephyr_library_sources_ifdef(CONFIG_SAM0_RTC_TIMER sam0_rtc_timer.c) zephyr_library_sources_ifdef(CONFIG_SILABS_SLEEPTIMER_TIMER silabs_sleeptimer_timer.c) zephyr_library_sources_ifdef(CONFIG_STM32_LPTIM_TIMER stm32_lptim_timer.c) diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig index a43e666349c..f017589a60e 100644 --- a/drivers/timer/Kconfig +++ b/drivers/timer/Kconfig @@ -91,6 +91,7 @@ source "drivers/timer/Kconfig.nrf_xrtc" source "drivers/timer/Kconfig.rcar_cmt" source "drivers/timer/Kconfig.riscv_machine" source "drivers/timer/Kconfig.rv32m1_lptmr" +source "drivers/timer/Kconfig.realtek_rts5912_rtmr" source "drivers/timer/Kconfig.sam0_rtc" source "drivers/timer/Kconfig.silabs" source "drivers/timer/Kconfig.smartbond" diff --git a/drivers/timer/Kconfig.realtek_rts5912_rtmr b/drivers/timer/Kconfig.realtek_rts5912_rtmr new file mode 100644 index 00000000000..560ea7a7059 --- /dev/null +++ b/drivers/timer/Kconfig.realtek_rts5912_rtmr @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +config REALTEK_RTS5912_RTMR + bool "Realtek RTS5912 RTOS Timer" + default y if DT_HAS_REALTEK_RTS5912_RTMR_ENABLED + select TICKLESS_CAPABLE + select SYSTEM_TIMER_HAS_DISABLE_SUPPORT + help + This module implements a kernel device driver for the Realtek + RTS5912 series RTOS timer. diff --git a/drivers/timer/realtek_rts5912_rtmr.c b/drivers/timer/realtek_rts5912_rtmr.c new file mode 100644 index 00000000000..3476e730999 --- /dev/null +++ b/drivers/timer/realtek_rts5912_rtmr.c @@ -0,0 +1,251 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 + * Author: Lin Yu-Cheng + */ + +#define DT_DRV_COMPAT realtek_rts5912_rtmr + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define RTS5912_SCCON_REG_BASE ((SYSTEM_Type *)(DT_REG_ADDR(DT_NODELABEL(sccon)))) + +#define CYCLES_PER_TICK (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / CONFIG_SYS_CLOCK_TICKS_PER_SEC) + +BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1, + "Realtek RTOS timer is not supported multiple instances"); + +#define RTMR_REG ((RTOSTMR_Type *)DT_INST_REG_ADDR(0)) + +#define SLWTMR_REG \ + ((RTOSTMR_Type *)(DT_REG_ADDR(DT_COMPAT_GET_ANY_STATUS_OKAY(realtek_rts5912_slwtimer)))) + +#define SSCON_REG ((SYSTEM_Type *)(DT_REG_ADDR(DT_NODELABEL(sccon)))) + +#define RTMR_COUNTER_MAX 0x0ffffffful +#define RTMR_COUNTER_MSK 0x0ffffffful +#define RTMR_TIMER_STOPPED 0xf0000000ul + +#define MAX_TICKS ((k_ticks_t)(RTMR_COUNTER_MAX / CYCLES_PER_TICK) - 1) + +#define RTMR_ADJUST_LIMIT 2 +#define RTMR_ADJUST_CYCLES 1 + +static struct k_spinlock lock; +static uint32_t accumulated_cycles; +static uint32_t previous_cnt; /* Record the counter set into RTMR */ +static uint32_t last_announcement; /* Record the last tick announced to system */ + +static void rtmr_restart(uint32_t counter) +{ + RTMR_REG->CTRL = 0ul; + RTMR_REG->LDCNT = counter; + RTMR_REG->CTRL = RTOSTMR_CTRL_INTEN_Msk | RTOSTMR_CTRL_EN_Msk; +} + +static uint32_t rtmr_get_counter(void) +{ + uint32_t counter = RTMR_REG->CNT; + + if ((counter == 0) && (RTMR_REG->CTRL & RTOSTMR_CTRL_EN_Msk)) { + counter = previous_cnt; + } + + return counter; +} + +static void rtmr_isr(const void *arg) +{ + ARG_UNUSED(arg); + + uint32_t cycles; + int32_t ticks; + + k_spinlock_key_t key = k_spin_lock(&lock); + + rtmr_restart(RTMR_COUNTER_MAX * CYCLES_PER_TICK); + + cycles = previous_cnt; + previous_cnt = RTMR_COUNTER_MAX * CYCLES_PER_TICK; + + accumulated_cycles += cycles; + + if (accumulated_cycles > RTMR_COUNTER_MSK) { + accumulated_cycles &= RTMR_COUNTER_MSK; + } + + ticks = accumulated_cycles - last_announcement; + ticks &= RTMR_COUNTER_MSK; + ticks /= CYCLES_PER_TICK; + + last_announcement = accumulated_cycles; + + k_spin_unlock(&lock, key); + + sys_clock_announce(ticks); +} + +void sys_clock_set_timeout(int32_t ticks, bool idle) +{ + ARG_UNUSED(idle); + + uint32_t cur_cnt, temp; + int full_ticks; + uint32_t full_cycles; + uint32_t partial_cycles; + + if (idle && (ticks == K_TICKS_FOREVER)) { + RTMR_REG->CTRL = 0U; + previous_cnt = RTMR_TIMER_STOPPED; + return; + } + + if (ticks < 1) { + full_ticks = 0; + } else if ((ticks == K_TICKS_FOREVER) || (ticks > MAX_TICKS)) { + full_ticks = MAX_TICKS - 1; + } else { + full_ticks = ticks - 1; + } + + full_cycles = full_ticks * CYCLES_PER_TICK; + + k_spinlock_key_t key = k_spin_lock(&lock); + + cur_cnt = rtmr_get_counter(); + + RTMR_REG->CTRL = 0U; + + temp = accumulated_cycles; + temp += previous_cnt - cur_cnt; + temp &= RTMR_COUNTER_MSK; + accumulated_cycles = temp; + + partial_cycles = CYCLES_PER_TICK - (accumulated_cycles % CYCLES_PER_TICK); + previous_cnt = full_cycles + partial_cycles; + + temp = previous_cnt; + if (temp > RTMR_ADJUST_LIMIT) { + temp -= RTMR_ADJUST_CYCLES; + } + rtmr_restart(temp); + + k_spin_unlock(&lock, key); +} + +uint32_t sys_clock_elapsed(void) +{ + uint32_t cur_cnt; + uint32_t ticks; + int32_t elapsed; + + k_spinlock_key_t key = k_spin_lock(&lock); + + cur_cnt = rtmr_get_counter(); + + elapsed = (int32_t)accumulated_cycles - (int32_t)last_announcement; + if (elapsed < 0) { + elapsed = -1 * elapsed; + } + ticks = (uint32_t)elapsed; + ticks += previous_cnt - cur_cnt; + ticks /= CYCLES_PER_TICK; + ticks &= RTMR_COUNTER_MSK; + + k_spin_unlock(&lock, key); + + return ticks; +} + +void sys_clock_idle_exit(void) +{ + if (previous_cnt == RTMR_TIMER_STOPPED) { + previous_cnt = CYCLES_PER_TICK; + rtmr_restart(previous_cnt); + } +} + +void sys_clock_disable(void) +{ + /* Disable RTMR. */ + RTMR_REG->CTRL = 0ul; +} + +uint32_t sys_clock_cycle_get_32(void) +{ + uint32_t ret; + uint32_t cur_cnt; + + k_spinlock_key_t key = k_spin_lock(&lock); + + cur_cnt = rtmr_get_counter(); + ret = (accumulated_cycles + (previous_cnt - cur_cnt)) & RTMR_COUNTER_MSK; + + k_spin_unlock(&lock, key); + + return ret; +} + +#ifdef CONFIG_ARCH_HAS_CUSTOM_BUSY_WAIT + +void arch_busy_wait(uint32_t n_usec) +{ + if (n_usec == 0) { + return; + } + + uint32_t start = SLWTMR_REG->CNT; + + for (;;) { + uint32_t curr = SLWTMR_REG->CNT; + + if ((start - curr) >= n_usec) { + break; + } + } +} +#endif + +static int sys_clock_driver_init(void) +{ + /* Enable RTMR clock power */ + + SYSTEM_Type *sys_reg = RTS5912_SCCON_REG_BASE; + + sys_reg->PERICLKPWR1 |= SYSTEM_PERICLKPWR1_RTMRCLKPWR_Msk; + + /* Enable RTMR interrupt. */ + IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), rtmr_isr, 0, 0); + irq_enable(DT_INST_IRQN(0)); + + /* Trigger RTMR and wait it start to counting */ + previous_cnt = RTMR_COUNTER_MAX; + + rtmr_restart(previous_cnt); + while (RTMR_REG->CNT == 0) { + }; + +#ifdef CONFIG_ARCH_HAS_CUSTOM_BUSY_WAIT + /* Enable SLWTMR0 clock power */ + SSCON_REG->PERICLKPWR1 |= BIT(SYSTEM_PERICLKPWR1_SLWTMR0CLKPWR_Pos); + + /* Enable SLWTMR0 */ + SLWTMR_REG->LDCNT = UINT32_MAX; + SLWTMR_REG->CTRL = RTOSTMR_CTRL_MDSEL_Msk | RTOSTMR_CTRL_EN_Msk; +#endif + + return 0; +} + +SYS_INIT(sys_clock_driver_init, PRE_KERNEL_2, CONFIG_SYSTEM_CLOCK_INIT_PRIORITY); diff --git a/dts/arm/realtek/ec/rts5912.dtsi b/dts/arm/realtek/ec/rts5912.dtsi index b5f6daad4c7..bff47d6803a 100644 --- a/dts/arm/realtek/ec/rts5912.dtsi +++ b/dts/arm/realtek/ec/rts5912.dtsi @@ -73,6 +73,25 @@ clock-names = "rc25m", "pll"; }; + slwtmr0: slwtmr0@4000c200 { + compatible = "realtek,rts5912-slwtimer"; + reg = <0x4000c200 0x10>; + interrupts = <202 0>; + clocks = <&sccon RTS5912_SCCON_PERIPH_GRP1 PERIPH_GRP1_SLWTMR0_CLKPWR>; + clock-names = "slwtmr"; + max-value = <0xFFFFFFFF>; + clock-frequency = <1000000>; + prescaler = <0>; + status = "okay"; + }; + + rtmr: rtmr@4000c500 { + compatible = "realtek,rts5912-rtmr"; + reg = <0x4000c500 0x10>; + interrupts = <204 0>; + status = "okay"; + }; + pinctrl: pin-controller@40090000 { compatible = "realtek,rts5912-pinctrl"; #address-cells = <1>; diff --git a/dts/bindings/timer/realtek,rts5912-rtmr.yaml b/dts/bindings/timer/realtek,rts5912-rtmr.yaml new file mode 100644 index 00000000000..6e6c0d3a0a6 --- /dev/null +++ b/dts/bindings/timer/realtek,rts5912-rtmr.yaml @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +description: RTOS Timer on Realtek RTS5912 EC + +compatible: "realtek,rts5912-rtmr" + +include: base.yaml + +properties: + reg: + required: true + + interrupts: + required: true diff --git a/dts/bindings/timer/realtek,rts5912-slwtimer.yaml b/dts/bindings/timer/realtek,rts5912-slwtimer.yaml new file mode 100644 index 00000000000..aa8a3f40510 --- /dev/null +++ b/dts/bindings/timer/realtek,rts5912-slwtimer.yaml @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7 +# + +description: Realtek RTS5912 32-bit slow timer + +compatible: "realtek,rts5912-slwtimer" + +include: rtc.yaml + +properties: + reg: + required: true + + interrupts: + required: true + + max-value: + type: int + required: true + description: Maximum counter value the instance can handle + + clock-frequency: + required: true + + prescaler: + type: int + required: true + description: Timer frequency equals clock-frequency divided by the prescaler value diff --git a/soc/realtek/ec/rts5912/reg/reg_rtmr.h b/soc/realtek/ec/rts5912/reg/reg_rtmr.h new file mode 100644 index 00000000000..63957494728 --- /dev/null +++ b/soc/realtek/ec/rts5912/reg/reg_rtmr.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 Realtek, SIBG-SD7 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_SOC_REALTEK_RTS5912_REG_RTMR_H +#define ZEPHYR_SOC_REALTEK_RTS5912_REG_RTMR_H + +/* + * @brief RTOS Timer Controller (RTMR) + */ + +typedef struct { + volatile uint32_t LDCNT; + volatile uint32_t CNT; + volatile uint32_t CTRL; + volatile uint32_t INTSTS; +} RTOSTMR_Type; +/* CTRL */ +#define RTOSTMR_CTRL_EN_Pos (0UL) +#define RTOSTMR_CTRL_EN_Msk BIT(RTOSTMR_CTRL_EN_Pos) +#define RTOSTMR_CTRL_MDSEL_Pos (1UL) +#define RTOSTMR_CTRL_MDSEL_Msk BIT(RTOSTMR_CTRL_MDSEL_Pos) +#define RTOSTMR_CTRL_INTEN_Pos (2UL) +#define RTOSTMR_CTRL_INTEN_Msk BIT(RTOSTMR_CTRL_INTEN_Pos) +/* INTSTS */ +#define RTOSTMR_INTSTS_STS_Pos (0UL) +#define RTOSTMR_INTSTS_STS_Msk BIT(RTOSTMR_INTSTS_STS_Pos) + +#endif /* ZEPHYR_SOC_REALTEK_RTS5912_REG_RTMR_H */