1 /*
2 * Copyright (c) 2024 Michael Hope
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT wch_rcc
8
9 #include <stdint.h>
10
11 #include <zephyr/arch/cpu.h>
12 #include <zephyr/device.h>
13 #include <zephyr/devicetree.h>
14 #include <zephyr/drivers/clock_control.h>
15 #include <zephyr/sys/util_macro.h>
16
17 #include <ch32fun.h>
18
19 #define WCH_RCC_CLOCK_ID_OFFSET(id) (((id) >> 5) & 0xFF)
20 #define WCH_RCC_CLOCK_ID_BIT(id) ((id) & 0x1F)
21
22 #if DT_NODE_HAS_COMPAT(DT_INST_CLOCKS_CTLR(0), wch_ch32v00x_pll_clock) || \
23 DT_NODE_HAS_COMPAT(DT_INST_CLOCKS_CTLR(0), wch_ch32v20x_30x_pll_clock)
24 #define WCH_RCC_SRC_IS_PLL 1
25 #if DT_NODE_HAS_COMPAT(DT_CLOCKS_CTLR(DT_INST_CLOCKS_CTLR(0)), wch_ch32v00x_hse_clock)
26 #define WCH_RCC_PLL_SRC_IS_HSE 1
27 #elif DT_NODE_HAS_COMPAT(DT_CLOCKS_CTLR(DT_INST_CLOCKS_CTLR(0)), wch_ch32v00x_hsi_clock)
28 #define WCH_RCC_PLL_SRC_IS_HSI 1
29 #endif
30 #elif DT_NODE_HAS_COMPAT(DT_INST_CLOCKS_CTLR(0), wch_ch32v00x_hse_clock)
31 #define WCH_RCC_SRC_IS_HSE 1
32 #elif DT_NODE_HAS_COMPAT(DT_INST_CLOCKS_CTLR(0), wch_ch32v00x_hsi_clock)
33 #define WCH_RCC_SRC_IS_HSI 1
34 #endif
35
36 struct clock_control_wch_rcc_config {
37 RCC_TypeDef *regs;
38 uint8_t mul;
39 };
40
clock_control_wch_rcc_on(const struct device * dev,clock_control_subsys_t sys)41 static int clock_control_wch_rcc_on(const struct device *dev, clock_control_subsys_t sys)
42 {
43 const struct clock_control_wch_rcc_config *config = dev->config;
44 RCC_TypeDef *regs = config->regs;
45 uint8_t id = (uintptr_t)sys;
46 uint32_t reg = (uint32_t)(®s->AHBPCENR + WCH_RCC_CLOCK_ID_OFFSET(id));
47 uint32_t val = sys_read32(reg);
48
49 val |= BIT(WCH_RCC_CLOCK_ID_BIT(id));
50 sys_write32(val, reg);
51
52 return 0;
53 }
54
clock_control_wch_rcc_get_rate(const struct device * dev,clock_control_subsys_t sys,uint32_t * rate)55 static int clock_control_wch_rcc_get_rate(const struct device *dev, clock_control_subsys_t sys,
56 uint32_t *rate)
57 {
58 const struct clock_control_wch_rcc_config *config = dev->config;
59 RCC_TypeDef *regs = config->regs;
60 uint32_t cfgr0 = regs->CFGR0;
61 uint32_t sysclk = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC;
62 uint32_t ahbclk = sysclk;
63
64 if ((cfgr0 & RCC_HPRE_3) != 0) {
65 /* The range 0b1000 divides by a power of 2, where 0b1000 is /2, 0b1001 is /4, etc.
66 */
67 ahbclk /= 2 << ((cfgr0 & (RCC_HPRE_0 | RCC_HPRE_1 | RCC_HPRE_2)) >> 4);
68 } else {
69 /* The range 0b0nnn divides by n + 1, where 0b0000 is /1, 0b001 is /2, etc. */
70 ahbclk /= ((cfgr0 & (RCC_HPRE_0 | RCC_HPRE_1 | RCC_HPRE_2)) >> 4) + 1;
71 }
72
73 /* The datasheet says that AHB == APB1 == APB2, but the registers imply that APB1 and APB2
74 * can be divided from the AHB clock. Assume that the clock tree diagram is correct and
75 * always return AHB.
76 */
77 *rate = ahbclk;
78 return 0;
79 }
80
81 static DEVICE_API(clock_control, clock_control_wch_rcc_api) = {
82 .on = clock_control_wch_rcc_on,
83 .get_rate = clock_control_wch_rcc_get_rate,
84 };
85
clock_control_wch_rcc_init(const struct device * dev)86 static int clock_control_wch_rcc_init(const struct device *dev)
87 {
88 const struct clock_control_wch_rcc_config *config = dev->config;
89
90 if (IS_ENABLED(CONFIG_DT_HAS_WCH_CH32V00X_PLL_CLOCK_ENABLED) ||
91 IS_ENABLED(CONFIG_DT_HAS_WCH_CH32V20X_30X_PLL_CLOCK_ENABLED)) {
92 /* Disable the PLL before potentially changing the input clocks. */
93 RCC->CTLR &= ~RCC_PLLON;
94 }
95
96 /* Always enable the LSI. */
97 RCC->RSTSCKR |= RCC_LSION;
98 while ((RCC->RSTSCKR & RCC_LSIRDY) == 0) {
99 }
100
101 if (IS_ENABLED(CONFIG_DT_HAS_WCH_CH32V00X_HSI_CLOCK_ENABLED)) {
102 RCC->CTLR |= RCC_HSION;
103 while ((RCC->CTLR & RCC_HSIRDY) == 0) {
104 }
105 }
106
107 if (IS_ENABLED(CONFIG_DT_HAS_WCH_CH32V00X_HSE_CLOCK_ENABLED)) {
108 RCC->CTLR |= RCC_HSEON;
109 while ((RCC->CTLR & RCC_HSERDY) == 0) {
110 }
111 }
112
113 if (IS_ENABLED(CONFIG_DT_HAS_WCH_CH32V00X_PLL_CLOCK_ENABLED)) {
114 if (IS_ENABLED(WCH_RCC_PLL_SRC_IS_HSE)) {
115 RCC->CFGR0 |= RCC_PLLSRC;
116 } else if (IS_ENABLED(WCH_RCC_PLL_SRC_IS_HSI)) {
117 RCC->CFGR0 &= ~RCC_PLLSRC;
118 }
119 RCC->CTLR |= RCC_PLLON;
120 while ((RCC->CTLR & RCC_PLLRDY) == 0) {
121 }
122 }
123 if (IS_ENABLED(CONFIG_DT_HAS_WCH_CH32V20X_30X_PLL_CLOCK_ENABLED)) {
124 if (IS_ENABLED(WCH_RCC_PLL_SRC_IS_HSE)) {
125 RCC->CFGR0 |= RCC_PLLSRC;
126 } else if (IS_ENABLED(WCH_RCC_PLL_SRC_IS_HSI)) {
127 RCC->CFGR0 &= ~RCC_PLLSRC;
128 }
129 RCC->CFGR0 |= (config->mul == 18 ? 0xF : (config->mul - 2)) << 0x12;
130 RCC->CTLR |= RCC_PLLON;
131 while ((RCC->CTLR & RCC_PLLRDY) == 0) {
132 }
133 }
134
135 if (IS_ENABLED(WCH_RCC_SRC_IS_HSI)) {
136 RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_HSI;
137 } else if (IS_ENABLED(WCH_RCC_SRC_IS_HSE)) {
138 RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_HSE;
139 } else if (IS_ENABLED(WCH_RCC_SRC_IS_PLL)) {
140 RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_PLL;
141 }
142 RCC->CTLR |= RCC_CSSON;
143
144 /* Clear the interrupt flags. */
145 RCC->INTR = RCC_CSSC | RCC_PLLRDYC | RCC_HSERDYC | RCC_LSIRDYC;
146 /* HCLK = SYSCLK = APB1 */
147 RCC->CFGR0 = (RCC->CFGR0 & ~RCC_HPRE) | RCC_HPRE_DIV1;
148 #if defined(CONFIG_SOC_CH32V003)
149 /* Set the Flash to 0 wait state */
150 FLASH->ACTLR = (FLASH->ACTLR & ~FLASH_ACTLR_LATENCY) | FLASH_ACTLR_LATENCY_1;
151 #endif
152
153 return 0;
154 }
155
156 #define CLOCK_CONTROL_WCH_RCC_INIT(idx) \
157 static const struct clock_control_wch_rcc_config clock_control_wch_rcc_##idx##_config = { \
158 .regs = (RCC_TypeDef *)DT_INST_REG_ADDR(idx), \
159 .mul = DT_PROP_OR(DT_INST_CLOCKS_CTLR(idx), mul, 1), \
160 }; \
161 DEVICE_DT_INST_DEFINE(idx, clock_control_wch_rcc_init, NULL, NULL, \
162 &clock_control_wch_rcc_##idx##_config, PRE_KERNEL_1, \
163 CONFIG_CLOCK_CONTROL_INIT_PRIORITY, &clock_control_wch_rcc_api);
164
165 /* There is only ever one RCC */
166 CLOCK_CONTROL_WCH_RCC_INIT(0)
167