From 9ab11e0ff1467d0c47ebca4a20f9c57dfcc17d31 Mon Sep 17 00:00:00 2001 From: Michael Hope Date: Sat, 12 Apr 2025 12:15:42 +0000 Subject: [PATCH 1/3] soc: wch: work-around a bug in `wfi` in the QingKe V2A The RISC-V Machine-Level ISA section 3.3.3 says that `wfi` will complete even if interrupts are masked, but the QingKe V2A does not do this. Work-around by enabling interrupts first. This is the same mitigation as used for the Nordic VPR. Signed-off-by: Michael Hope Co-authored-by: Pete Johanson --- drivers/interrupt_controller/intc_wch_pfic.c | 4 --- soc/wch/ch32v/CMakeLists.txt | 1 + soc/wch/ch32v/common/CMakeLists.txt | 6 +++++ soc/wch/ch32v/common/soc_idle.c | 28 ++++++++++++++++++++ soc/wch/ch32v/qingke_v2a/Kconfig | 3 +++ soc/wch/ch32v/qingke_v4b/Kconfig | 3 +++ soc/wch/ch32v/qingke_v4c/Kconfig | 3 +++ soc/wch/ch32v/qingke_v4f/Kconfig | 3 +++ soc/wch/ch32v/qingke_v4f/Kconfig.defconfig | 3 +++ soc/wch/ch32v/qingke_v4f/vector.S | 7 ++++- 10 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 soc/wch/ch32v/common/CMakeLists.txt create mode 100644 soc/wch/ch32v/common/soc_idle.c diff --git a/drivers/interrupt_controller/intc_wch_pfic.c b/drivers/interrupt_controller/intc_wch_pfic.c index 2d91ca29b9bc..8ccaf25a3973 100644 --- a/drivers/interrupt_controller/intc_wch_pfic.c +++ b/drivers/interrupt_controller/intc_wch_pfic.c @@ -34,10 +34,6 @@ int arch_irq_is_enabled(unsigned int irq) static int pfic_init(void) { - /* `wfi` is called with interrupts disabled. Configure the PFIC to wake up on any event, - * including any interrupt. - */ - PFIC->SCTLR = SEVONPEND | WFITOWFE; return 0; } diff --git a/soc/wch/ch32v/CMakeLists.txt b/soc/wch/ch32v/CMakeLists.txt index 0f127dd9c756..9a6c5da7f722 100644 --- a/soc/wch/ch32v/CMakeLists.txt +++ b/soc/wch/ch32v/CMakeLists.txt @@ -1,6 +1,7 @@ # Copyright (c) 2024 Michael Hope # SPDX-License-Identifier: Apache-2.0 +add_subdirectory(common) add_subdirectory(${SOC_SERIES}) set(SOC_LINKER_SCRIPT ${ZEPHYR_BASE}/include/zephyr/arch/riscv/common/linker.ld CACHE INTERNAL "") diff --git a/soc/wch/ch32v/common/CMakeLists.txt b/soc/wch/ch32v/common/CMakeLists.txt new file mode 100644 index 000000000000..1ddee8d24050 --- /dev/null +++ b/soc/wch/ch32v/common/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright (c) 2025 Pete Johanson +# SPDX-License-Identifier: Apache-2.0 + +zephyr_sources( + soc_idle.c +) diff --git a/soc/wch/ch32v/common/soc_idle.c b/soc/wch/ch32v/common/soc_idle.c new file mode 100644 index 000000000000..4123fcc9aedd --- /dev/null +++ b/soc/wch/ch32v/common/soc_idle.c @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2025 Michael Hope + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +void arch_cpu_idle(void) +{ + /* + * The RISC-V Machine-Level ISA section 3.3.3 says that `wfi` will complete even if + * interrupts are masked, but the QingKe V2A does not do this. Work-around by enabling + * interrupts first. + */ + sys_trace_idle(); + irq_unlock(MSTATUS_IEN); + __asm__ volatile("wfi"); + sys_trace_idle_exit(); +} + +void arch_cpu_atomic_idle(unsigned int key) +{ + sys_trace_idle(); + irq_unlock(key); + __asm__ volatile("wfi"); + sys_trace_idle_exit(); +} diff --git a/soc/wch/ch32v/qingke_v2a/Kconfig b/soc/wch/ch32v/qingke_v2a/Kconfig index de004227a473..62f8518f95a3 100644 --- a/soc/wch/ch32v/qingke_v2a/Kconfig +++ b/soc/wch/ch32v/qingke_v2a/Kconfig @@ -6,3 +6,6 @@ config SOC_SERIES_QINGKE_V2A select RISCV_ISA_EXT_ZICSR select RISCV_ISA_EXT_ZIFENCEI select RISCV_ISA_EXT_C + select RISCV_ALWAYS_SWITCH_THROUGH_ECALL + select ARCH_HAS_CUSTOM_CPU_IDLE + select ARCH_HAS_CUSTOM_CPU_ATOMIC_IDLE diff --git a/soc/wch/ch32v/qingke_v4b/Kconfig b/soc/wch/ch32v/qingke_v4b/Kconfig index 1706900e7cd9..765e1c42d56c 100644 --- a/soc/wch/ch32v/qingke_v4b/Kconfig +++ b/soc/wch/ch32v/qingke_v4b/Kconfig @@ -8,3 +8,6 @@ config SOC_SERIES_QINGKE_V4B select RISCV_ISA_EXT_C select RISCV_ISA_EXT_ZICSR select RISCV_ISA_EXT_ZIFENCEI + select RISCV_ALWAYS_SWITCH_THROUGH_ECALL + select ARCH_HAS_CUSTOM_CPU_IDLE + select ARCH_HAS_CUSTOM_CPU_ATOMIC_IDLE diff --git a/soc/wch/ch32v/qingke_v4c/Kconfig b/soc/wch/ch32v/qingke_v4c/Kconfig index 732a7d8a9ec0..0cba65e61888 100644 --- a/soc/wch/ch32v/qingke_v4c/Kconfig +++ b/soc/wch/ch32v/qingke_v4c/Kconfig @@ -8,3 +8,6 @@ config SOC_SERIES_QINGKE_V4C select RISCV_ISA_EXT_C select RISCV_ISA_EXT_ZICSR select RISCV_ISA_EXT_ZIFENCEI + select RISCV_ALWAYS_SWITCH_THROUGH_ECALL + select ARCH_HAS_CUSTOM_CPU_IDLE + select ARCH_HAS_CUSTOM_CPU_ATOMIC_IDLE diff --git a/soc/wch/ch32v/qingke_v4f/Kconfig b/soc/wch/ch32v/qingke_v4f/Kconfig index 3f6c61415485..79354a30719d 100644 --- a/soc/wch/ch32v/qingke_v4f/Kconfig +++ b/soc/wch/ch32v/qingke_v4f/Kconfig @@ -9,3 +9,6 @@ config SOC_SERIES_QINGKE_V4F select RISCV_ISA_EXT_F select RISCV_ISA_EXT_ZICSR select RISCV_ISA_EXT_ZIFENCEI + select RISCV_ALWAYS_SWITCH_THROUGH_ECALL + select ARCH_HAS_CUSTOM_CPU_IDLE + select ARCH_HAS_CUSTOM_CPU_ATOMIC_IDLE diff --git a/soc/wch/ch32v/qingke_v4f/Kconfig.defconfig b/soc/wch/ch32v/qingke_v4f/Kconfig.defconfig index 417c14237a56..a134f0cee319 100644 --- a/soc/wch/ch32v/qingke_v4f/Kconfig.defconfig +++ b/soc/wch/ch32v/qingke_v4f/Kconfig.defconfig @@ -9,6 +9,9 @@ config SYS_CLOCK_HW_CYCLES_PER_SEC config CLOCK_CONTROL default y +config ISR_TABLES_LOCAL_DECLARATION_SUPPORTED + default n + rsource "Kconfig.defconfig.*" endif # SOC_SERIES_QINGKE_V4F diff --git a/soc/wch/ch32v/qingke_v4f/vector.S b/soc/wch/ch32v/qingke_v4f/vector.S index 97766e820fdd..ef4419eabc98 100644 --- a/soc/wch/ch32v/qingke_v4f/vector.S +++ b/soc/wch/ch32v/qingke_v4f/vector.S @@ -22,11 +22,16 @@ SECTION_FUNC(vectors, ivt) lui x5, 0x8000 jr 0x8(x5) j __start +_irq_vector_table: .rept CONFIG_VECTOR_TABLE_SIZE .word _isr_wrapper .endr SECTION_FUNC(vectors, __start) - li a0, 0xf + li a0, 0x1f + csrw 0xbc0, a0 + li a0, 0x1e + csrw 0x804, a0 + li a0, 0xf csrw mtvec, a0 j __initialize From 0890140467181948fbdfde7e21d1971d7d0a2cd2 Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Fri, 20 Jun 2025 16:21:59 -0600 Subject: [PATCH 2/3] drivers: timer: fix a race in the WCH systick time monoticity To ensure proper monotonic system time, don't rely on SysTick reset, instead increasing the compare value as needed to trigger the next interrupt/tick. Update to support 64-bit cycles as well. Signed-off-by: Peter Johanson --- drivers/timer/Kconfig.wch_ch32v00x | 1 + drivers/timer/wch_systick_ch32v00x.c | 61 ++++++++++++++++++++++++---- 2 files changed, 55 insertions(+), 7 deletions(-) diff --git a/drivers/timer/Kconfig.wch_ch32v00x b/drivers/timer/Kconfig.wch_ch32v00x index abe9d808abe7..263c84c6a694 100644 --- a/drivers/timer/Kconfig.wch_ch32v00x +++ b/drivers/timer/Kconfig.wch_ch32v00x @@ -6,3 +6,4 @@ config CH32V00X_SYSTICK depends on SOC_SERIES_QINGKE_V2A || SOC_SERIES_QINGKE_V4C || SOC_SERIES_CH32V00X || SOC_SERIES_QINGKE_V4B || SOC_SERIES_QINGKE_V4F default y depends on DT_HAS_WCH_SYSTICK_ENABLED + select TIMER_HAS_64BIT_CYCLE_COUNTER diff --git a/drivers/timer/wch_systick_ch32v00x.c b/drivers/timer/wch_systick_ch32v00x.c index c2bf3865f32d..a93bbece3c5a 100644 --- a/drivers/timer/wch_systick_ch32v00x.c +++ b/drivers/timer/wch_systick_ch32v00x.c @@ -14,8 +14,6 @@ #include -#define STK_SWIE BIT(31) -#define STK_STRE BIT(3) #define STK_STCLK BIT(2) #define STK_STIE BIT(1) #define STK_STE BIT(0) @@ -27,20 +25,68 @@ #define SYSTICK ((SysTick_Type *)(DT_INST_REG_ADDR(0))) -static volatile uint32_t ch32v00x_systick_count; +static uint64_t last_cycles_announced; + +static inline bool cycles_close_to_next_cmp(uint32_t cycles) +{ + return (cycles % CYCLES_PER_TICK) > (9 * CYCLES_PER_TICK / 10); +} static void ch32v00x_systick_irq(const void *unused) { + uint64_t elapsed_cycles; + uint32_t ticks = 0; + uint64_t cnt = SYSTICK->CNT; + ARG_UNUSED(unused); + if (cnt < last_cycles_announced) { + elapsed_cycles = (UINT64_MAX - last_cycles_announced) + cnt; + ticks = elapsed_cycles / CYCLES_PER_TICK; + + /* If we're too close to the next tick, announce that tick early now rather than + * miss it + */ + if (cycles_close_to_next_cmp(elapsed_cycles % CYCLES_PER_TICK)) { + ticks++; + last_cycles_announced = (cnt % CYCLES_PER_TICK) + CYCLES_PER_TICK; + } else { + last_cycles_announced = cnt % CYCLES_PER_TICK; + } + } else { + ticks = (cnt - last_cycles_announced) / CYCLES_PER_TICK; + + /* If we're too close to the next tick, announce that tick early now rather than + * miss it + */ + if (cycles_close_to_next_cmp(cnt - last_cycles_announced)) { + ticks++; + } + + last_cycles_announced += ticks * CYCLES_PER_TICK; + } + + + /* Ensure we trigger when CNT resets to zero */ + if (UINT64_MAX - SYSTICK->CMP < CYCLES_PER_TICK) { + SYSTICK->CMP = SYSTICK->CMP % CYCLES_PER_TICK; + } else { + SYSTICK->CMP = (last_cycles_announced + CYCLES_PER_TICK); + } + SYSTICK->SR = 0; - ch32v00x_systick_count += CYCLES_PER_TICK; /* Track cycles. */ - sys_clock_announce(1); /* Poke the scheduler. */ + + sys_clock_announce(ticks); } uint32_t sys_clock_cycle_get_32(void) { - return ch32v00x_systick_count + SYSTICK->CNT; + return (uint32_t)SYSTICK->CNT; +} + +uint64_t sys_clock_cycle_get_64(void) +{ + return SYSTICK->CNT; } uint32_t sys_clock_elapsed(void) @@ -55,10 +101,11 @@ static int ch32v00x_systick_init(void) SYSTICK->SR = 0; SYSTICK->CMP = CYCLES_PER_TICK; SYSTICK->CNT = 0; - SYSTICK->CTLR = STK_STRE | STK_STCLK | STK_STIE | STK_STE; irq_enable(DT_INST_IRQN(0)); + SYSTICK->CTLR = STK_STE | STK_STCLK | STK_STIE; + return 0; } From 39015145ec1c21319856770800e4c199a92990b3 Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Fri, 20 Jun 2025 16:24:15 -0600 Subject: [PATCH 3/3] drivers: clock_control: disable the HSI PLL prescaler Ensure our clock isn't divided when using HSI. Signed-off-by: Peter Johanson --- drivers/clock_control/clock_control_wch_rcc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/clock_control/clock_control_wch_rcc.c b/drivers/clock_control/clock_control_wch_rcc.c index 039a66945606..d77b1dde3b14 100644 --- a/drivers/clock_control/clock_control_wch_rcc.c +++ b/drivers/clock_control/clock_control_wch_rcc.c @@ -154,6 +154,9 @@ static int clock_control_wch_rcc_init(const struct device *dev) RCC->CFGR0 |= RCC_PLLSRC; } else if (IS_ENABLED(WCH_RCC_PLL_SRC_IS_HSI)) { RCC->CFGR0 &= ~RCC_PLLSRC; +#if defined(EXTEN_PLL_HSI_PRE) + EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE; +#endif } RCC->CFGR0 |= (config->mul == 18 ? 0xF : (config->mul - 2)) << 0x12; RCC->CTLR |= RCC_PLLON;