From c1c0413eed9e160f51fe72426170e8f708a2d882 Mon Sep 17 00:00:00 2001 From: Michael Hope Date: Sat, 1 Jun 2024 21:53:41 +0530 Subject: [PATCH] drivers: add the ch32v00x clock controller This commit adds the clock driver for WCH CH32V003. Signed-off-by: Michael Hope Signed-off-by: Dhiru Kholia --- drivers/clock_control/CMakeLists.txt | 1 + drivers/clock_control/Kconfig | 2 + drivers/clock_control/Kconfig.wch_rcc | 7 + drivers/clock_control/clock_control_wch_rcc.c | 154 ++++++++++++++++++ .../clock/wch,ch32v00x-hse-clock.yaml | 8 + .../clock/wch,ch32v00x-hsi-clock.yaml | 8 + .../clock/wch,ch32v00x-pll-clock.yaml | 16 ++ dts/bindings/clock/wch,rcc.yaml | 15 ++ dts/riscv/wch/ch32v00x.dtsi | 29 ++++ .../dt-bindings/clock/ch32v00x-clocks.h | 42 +++++ 10 files changed, 282 insertions(+) create mode 100644 drivers/clock_control/Kconfig.wch_rcc create mode 100644 drivers/clock_control/clock_control_wch_rcc.c create mode 100644 dts/bindings/clock/wch,ch32v00x-hse-clock.yaml create mode 100644 dts/bindings/clock/wch,ch32v00x-hsi-clock.yaml create mode 100644 dts/bindings/clock/wch,ch32v00x-pll-clock.yaml create mode 100644 dts/bindings/clock/wch,rcc.yaml create mode 100644 include/zephyr/dt-bindings/clock/ch32v00x-clocks.h diff --git a/drivers/clock_control/CMakeLists.txt b/drivers/clock_control/CMakeLists.txt index c9f0657dc45..b17dc7b86c5 100644 --- a/drivers/clock_control/CMakeLists.txt +++ b/drivers/clock_control/CMakeLists.txt @@ -98,3 +98,4 @@ endif() zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_AST10X0 clock_control_ast10x0.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_MAX32 clock_control_max32.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_AUXPLL clock_control_nrf_auxpll.c) +zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_WCH_RCC clock_control_wch_rcc.c) diff --git a/drivers/clock_control/Kconfig b/drivers/clock_control/Kconfig index de13e421e5c..040dab163c2 100644 --- a/drivers/clock_control/Kconfig +++ b/drivers/clock_control/Kconfig @@ -100,4 +100,6 @@ source "drivers/clock_control/Kconfig.arm_scmi" source "drivers/clock_control/Kconfig.silabs" +source "drivers/clock_control/Kconfig.wch_rcc" + endif # CLOCK_CONTROL diff --git a/drivers/clock_control/Kconfig.wch_rcc b/drivers/clock_control/Kconfig.wch_rcc new file mode 100644 index 00000000000..d58eff31e6a --- /dev/null +++ b/drivers/clock_control/Kconfig.wch_rcc @@ -0,0 +1,7 @@ +# Copyright (c) 2024 Michael Hope +# SPDX-License-Identifier: Apache-2.0 + +config CLOCK_CONTROL_WCH_RCC + bool "WCH CH32V00x Reset and Clock Control (RCC) driver" + default y + depends on DT_HAS_WCH_RCC_ENABLED diff --git a/drivers/clock_control/clock_control_wch_rcc.c b/drivers/clock_control/clock_control_wch_rcc.c new file mode 100644 index 00000000000..95bc004bd5e --- /dev/null +++ b/drivers/clock_control/clock_control_wch_rcc.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2024 Michael Hope + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT wch_rcc + +#include + +#include +#include +#include +#include +#include + +#include + +#define WCH_RCC_CLOCK_ID_OFFSET(id) (((id) >> 5) & 0xFF) +#define WCH_RCC_CLOCK_ID_BIT(id) ((id) & 0x1F) + +#if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(pll)) && DT_NODE_HAS_PROP(DT_NODELABEL(pll), clocks) +#define DT_PLL_CLOCKS_CTRL DT_CLOCKS_CTLR(DT_NODELABEL(pll)) +#if DT_SAME_NODE(DT_PLL_CLOCKS_CTRL, DT_NODELABEL(clk_hsi)) +#define WCH_RCC_PLL_SRC_IS_HSI 1 +#endif +#if DT_SAME_NODE(DT_PLL_CLOCKS_CTRL, DT_NODELABEL(clk_hse)) +#define WCH_RCC_PLL_SRC_IS_HSE 1 +#endif +#endif + +#define DT_RCC_CLOCKS_CTRL DT_CLOCKS_CTLR(DT_NODELABEL(rcc)) +#if DT_SAME_NODE(DT_RCC_CLOCKS_CTRL, DT_NODELABEL(pll)) +#define WCH_RCC_SRC_IS_PLL 1 +#endif +#if DT_SAME_NODE(DT_RCC_CLOCKS_CTRL, DT_NODELABEL(clk_hsi)) +#define WCH_RCC_SRC_IS_HSI 1 +#endif +#if DT_SAME_NODE(DT_RCC_CLOCKS_CTRL, DT_NODELABEL(clk_hse)) +#define WCH_RCC_SRC_IS_HSE 1 +#endif + +struct clock_control_wch_rcc_config { + RCC_TypeDef *regs; +}; + +static int clock_control_wch_rcc_on(const struct device *dev, clock_control_subsys_t sys) +{ + const struct clock_control_wch_rcc_config *config = dev->config; + RCC_TypeDef *regs = config->regs; + uint8_t id = (uintptr_t)sys; + uint32_t reg = (uint32_t)(®s->AHBPCENR + WCH_RCC_CLOCK_ID_OFFSET(id)); + uint32_t val = sys_read32(reg); + + val |= BIT(WCH_RCC_CLOCK_ID_BIT(id)); + sys_write32(val, reg); + + return 0; +} + +static int clock_control_wch_rcc_get_rate(const struct device *dev, clock_control_subsys_t sys, + uint32_t *rate) +{ + const struct clock_control_wch_rcc_config *config = dev->config; + RCC_TypeDef *regs = config->regs; + uint32_t cfgr0 = regs->CFGR0; + uint32_t sysclk = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC; + uint32_t ahbclk = sysclk; + + if ((cfgr0 & RCC_HPRE_3) != 0) { + /* The range 0b1000 divides by a power of 2, where 0b1000 is /2, 0b1001 is /4, etc. + */ + ahbclk /= 2 << ((cfgr0 & (RCC_HPRE_0 | RCC_HPRE_1 | RCC_HPRE_2)) >> 4); + } else { + /* The range 0b0nnn divides by n + 1, where 0b0000 is /1, 0b001 is /2, etc. */ + ahbclk /= ((cfgr0 & (RCC_HPRE_0 | RCC_HPRE_1 | RCC_HPRE_2)) >> 4) + 1; + } + + /* The datasheet says that AHB == APB1 == APB2, but the registers imply that APB1 and APB2 + * can be divided from the AHB clock. Assume that the clock tree diagram is correct and + * always return AHB. + */ + *rate = ahbclk; + return 0; +} + +static struct clock_control_driver_api clock_control_wch_rcc_api = { + .on = clock_control_wch_rcc_on, + .get_rate = clock_control_wch_rcc_get_rate, +}; + +static int clock_control_wch_rcc_init(const struct device *dev) +{ + if (IS_ENABLED(CONFIG_DT_HAS_WCH_CH32V00X_PLL_CLOCK_ENABLED)) { + /* Disable the PLL before potentially changing the input clocks. */ + RCC->CTLR &= ~RCC_PLLON; + } + + /* Always enable the LSI. */ + RCC->RSTSCKR |= RCC_LSION; + while ((RCC->RSTSCKR & RCC_LSIRDY) == 0) { + } + + if (IS_ENABLED(CONFIG_DT_HAS_WCH_CH32V00X_HSI_CLOCK_ENABLED)) { + RCC->CTLR |= RCC_HSION; + while ((RCC->CTLR & RCC_HSIRDY) == 0) { + } + } + + if (IS_ENABLED(CONFIG_DT_HAS_WCH_CH32V00X_HSE_CLOCK_ENABLED)) { + RCC->CTLR |= RCC_HSEON; + while ((RCC->CTLR & RCC_HSERDY) == 0) { + } + } + + if (IS_ENABLED(CONFIG_DT_HAS_WCH_CH32V00X_PLL_CLOCK_ENABLED)) { + if (IS_ENABLED(WCH_RCC_PLL_SRC_IS_HSE)) { + RCC->CFGR0 |= RCC_PLLSRC; + } else if (IS_ENABLED(WCH_RCC_PLL_SRC_IS_HSI)) { + RCC->CFGR0 &= ~RCC_PLLSRC; + } + RCC->CTLR |= RCC_PLLON; + while ((RCC->CTLR & RCC_PLLRDY) == 0) { + } + } + + if (IS_ENABLED(WCH_RCC_SRC_IS_HSI)) { + RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_HSI; + } else if (IS_ENABLED(WCH_RCC_SRC_IS_HSE)) { + RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_HSE; + } else if (IS_ENABLED(WCH_RCC_SRC_IS_PLL)) { + RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_PLL; + } + RCC->CTLR |= RCC_CSSON; + + /* Clear the interrupt flags. */ + RCC->INTR = RCC_CSSC | RCC_PLLRDYC | RCC_HSERDYC | RCC_LSIRDYC; + /* HCLK = SYSCLK = APB1 */ + RCC->CFGR0 = (RCC->CFGR0 & ~RCC_HPRE) | RCC_HPRE_DIV1; + /* Set the Flash to 0 wait state */ + FLASH->ACTLR = (FLASH->ACTLR & ~FLASH_ACTLR_LATENCY) | FLASH_ACTLR_LATENCY_1; + + return 0; +} + +#define CLOCK_CONTROL_WCH_RCC_INIT(idx) \ + static const struct clock_control_wch_rcc_config clock_control_wch_rcc_##idx##_config = { \ + .regs = (RCC_TypeDef *)DT_INST_REG_ADDR(idx), \ + }; \ + DEVICE_DT_INST_DEFINE(idx, clock_control_wch_rcc_init, NULL, NULL, \ + &clock_control_wch_rcc_##idx##_config, PRE_KERNEL_1, \ + CONFIG_CLOCK_CONTROL_INIT_PRIORITY, &clock_control_wch_rcc_api); + +DT_INST_FOREACH_STATUS_OKAY(CLOCK_CONTROL_WCH_RCC_INIT) diff --git a/dts/bindings/clock/wch,ch32v00x-hse-clock.yaml b/dts/bindings/clock/wch,ch32v00x-hse-clock.yaml new file mode 100644 index 00000000000..764dee2facf --- /dev/null +++ b/dts/bindings/clock/wch,ch32v00x-hse-clock.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Google LLC. +# SPDX-License-Identifier: Apache-2.0 + +description: WCH CH32V00x HSE Clock + +compatible: "wch,ch32v00x-hse-clock" + +include: [fixed-clock.yaml] diff --git a/dts/bindings/clock/wch,ch32v00x-hsi-clock.yaml b/dts/bindings/clock/wch,ch32v00x-hsi-clock.yaml new file mode 100644 index 00000000000..eed5dd57722 --- /dev/null +++ b/dts/bindings/clock/wch,ch32v00x-hsi-clock.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Google LLC. +# SPDX-License-Identifier: Apache-2.0 + +description: WCH CH32V00x HSI Clock + +compatible: "wch,ch32v00x-hsi-clock" + +include: [fixed-clock.yaml] diff --git a/dts/bindings/clock/wch,ch32v00x-pll-clock.yaml b/dts/bindings/clock/wch,ch32v00x-pll-clock.yaml new file mode 100644 index 00000000000..f1554c333c9 --- /dev/null +++ b/dts/bindings/clock/wch,ch32v00x-pll-clock.yaml @@ -0,0 +1,16 @@ +# Copyright (c) 2024 Google LLC. +# SPDX-License-Identifier: Apache-2.0 + +description: WCH CH32V00x PLL + +compatible: "wch,ch32v00x-pll-clock" + +include: [clock-controller.yaml, base.yaml] + +properties: + "#clock-cells": + const: 0 + + clocks: + type: phandle-array + required: true diff --git a/dts/bindings/clock/wch,rcc.yaml b/dts/bindings/clock/wch,rcc.yaml new file mode 100644 index 00000000000..549735a91e7 --- /dev/null +++ b/dts/bindings/clock/wch,rcc.yaml @@ -0,0 +1,15 @@ +# Copyright (c) 2024 Michael Hope +# SPDX-License-Identifier: Apache-2.0 + +description: WCH CH32V00x Reset and Clock Control (RCC) + +compatible: "wch,rcc" + +include: [clock-controller.yaml, base.yaml] + +properties: + "#clock-cells": + const: 1 + +clock-cells: + - id diff --git a/dts/riscv/wch/ch32v00x.dtsi b/dts/riscv/wch/ch32v00x.dtsi index df8266b4b91..0066dc1992d 100644 --- a/dts/riscv/wch/ch32v00x.dtsi +++ b/dts/riscv/wch/ch32v00x.dtsi @@ -4,6 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include #include #include #include @@ -25,6 +26,34 @@ }; }; + clocks { + clk_hse: clk-hse { + #clock-cells = <0>; + compatible = "wch,ch32v00x-hse-clock"; + status = "disabled"; + }; + + clk_hsi: clk-hsi { + #clock-cells = <0>; + compatible = "wch,ch32v00x-hsi-clock"; + clock-frequency = ; + status = "disabled"; + }; + + clk_lsi: clk-lsi { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = ; + status = "disabled"; + }; + + pll: pll { + #clock-cells = <0>; + compatible = "wch,ch32v00x-pll-clock"; + status = "disabled"; + }; + }; + soc { #address-cells = <1>; #size-cells = <1>; diff --git a/include/zephyr/dt-bindings/clock/ch32v00x-clocks.h b/include/zephyr/dt-bindings/clock/ch32v00x-clocks.h new file mode 100644 index 00000000000..128e370e611 --- /dev/null +++ b/include/zephyr/dt-bindings/clock/ch32v00x-clocks.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024 Michael Hope + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __CH32V00X_CLOCKS_H__ +#define __CH32V00X_CLOCKS_H__ + +#define CH32V00X_AHB_PCENR_OFFSET 0 +#define CH32V00X_APB2_PCENR_OFFSET 1 +#define CH32V00X_APB1_PCENR_OFFSET 2 + +#define CH32V00X_CLOCK_CONFIG(bus, bit) (((CH32V00X_##bus##_PCENR_OFFSET) << 5) | (bit)) + +#define CH32V00X_CLOCK_DMA1 CH32V00X_CLOCK_CONFIG(AHB, 0) +#define CH32V00X_CLOCK_SRAM CH32V00X_CLOCK_CONFIG(AHB, 2) +#define CH32V00X_CLOCK_FLITF CH32V00X_CLOCK_CONFIG(AHB, 4) +#define CH32V00X_CLOCK_CRC CH32V00X_CLOCK_CONFIG(AHB, 6) +#define CH32V00X_CLOCK_USB CH32V00X_CLOCK_CONFIG(AHB, 12) + +#define CH32V00X_CLOCK_AFIO CH32V00X_CLOCK_CONFIG(APB2, 0) +#define CH32V00X_CLOCK_IOPA CH32V00X_CLOCK_CONFIG(APB2, 2) +#define CH32V00X_CLOCK_IOPB CH32V00X_CLOCK_CONFIG(APB2, 3) +#define CH32V00X_CLOCK_IOPC CH32V00X_CLOCK_CONFIG(APB2, 4) +#define CH32V00X_CLOCK_IOPD CH32V00X_CLOCK_CONFIG(APB2, 5) +#define CH32V00X_CLOCK_ADC1 CH32V00X_CLOCK_CONFIG(APB2, 9) +#define CH32V00X_CLOCK_ADC2 CH32V00X_CLOCK_CONFIG(APB2, 10) +#define CH32V00X_CLOCK_TIM1 CH32V00X_CLOCK_CONFIG(APB2, 11) +#define CH32V00X_CLOCK_SPI1 CH32V00X_CLOCK_CONFIG(APB2, 12) +#define CH32V00X_CLOCK_USART1 CH32V00X_CLOCK_CONFIG(APB2, 14) + +#define CH32V00X_CLOCK_TIM2 CH32V00X_CLOCK_CONFIG(APB1, 0) +#define CH32V00X_CLOCK_TIM3 CH32V00X_CLOCK_CONFIG(APB1, 1) +#define CH32V00X_CLOCK_WWDG CH32V00X_CLOCK_CONFIG(APB1, 11) +#define CH32V00X_CLOCK_USART2 CH32V00X_CLOCK_CONFIG(APB1, 17) +#define CH32V00X_CLOCK_I2C1 CH32V00X_CLOCK_CONFIG(APB1, 21) +#define CH32V00X_CLOCK_BKP CH32V00X_CLOCK_CONFIG(APB1, 27) +#define CH32V00X_CLOCK_PWR CH32V00X_CLOCK_CONFIG(APB1, 28) +#define CH32V00X_CLOCK_USB CH32V00X_CLOCK_CONFIG(APB1, 23) + +#endif