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 <hal_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 #define WCH_RCC_SYSCLK DT_PROP(DT_NODELABEL(cpu0), clock_frequency)
22
23 #if DT_NODE_HAS_COMPAT(DT_INST_CLOCKS_CTLR(0), wch_ch32v00x_pll_clock) || \
24 DT_NODE_HAS_COMPAT(DT_INST_CLOCKS_CTLR(0), wch_ch32v20x_30x_pll_clock)
25 #define WCH_RCC_SRC_IS_PLL 1
26 #if DT_NODE_HAS_COMPAT(DT_CLOCKS_CTLR(DT_INST_CLOCKS_CTLR(0)), wch_ch32v00x_hse_clock)
27 #define WCH_RCC_PLL_SRC_IS_HSE 1
28 #elif DT_NODE_HAS_COMPAT(DT_CLOCKS_CTLR(DT_INST_CLOCKS_CTLR(0)), wch_ch32v00x_hsi_clock)
29 #define WCH_RCC_PLL_SRC_IS_HSI 1
30 #endif
31 #elif DT_NODE_HAS_COMPAT(DT_INST_CLOCKS_CTLR(0), wch_ch32v00x_hse_clock)
32 #define WCH_RCC_SRC_IS_HSE 1
33 #elif DT_NODE_HAS_COMPAT(DT_INST_CLOCKS_CTLR(0), wch_ch32v00x_hsi_clock)
34 #define WCH_RCC_SRC_IS_HSI 1
35 #endif
36
37 struct clock_control_wch_rcc_config {
38 RCC_TypeDef *regs;
39 uint8_t mul;
40 };
41
clock_control_wch_rcc_on(const struct device * dev,clock_control_subsys_t sys)42 static int clock_control_wch_rcc_on(const struct device *dev, clock_control_subsys_t sys)
43 {
44 const struct clock_control_wch_rcc_config *config = dev->config;
45 RCC_TypeDef *regs = config->regs;
46 uint8_t id = (uintptr_t)sys;
47 uint32_t reg = (uint32_t)(®s->AHBPCENR + WCH_RCC_CLOCK_ID_OFFSET(id));
48 uint32_t val = sys_read32(reg);
49
50 val |= BIT(WCH_RCC_CLOCK_ID_BIT(id));
51 sys_write32(val, reg);
52
53 return 0;
54 }
55
clock_control_wch_rcc_get_rate(const struct device * dev,clock_control_subsys_t sys,uint32_t * rate)56 static int clock_control_wch_rcc_get_rate(const struct device *dev, clock_control_subsys_t sys,
57 uint32_t *rate)
58 {
59 const struct clock_control_wch_rcc_config *config = dev->config;
60 RCC_TypeDef *regs = config->regs;
61 uint32_t cfgr0 = regs->CFGR0;
62 uint32_t sysclk = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC;
63 uint32_t ahbclk = sysclk;
64
65 if ((cfgr0 & RCC_HPRE_3) != 0) {
66 /* The range 0b1000 divides by a power of 2, where 0b1000 is /2, 0b1001 is /4, etc.
67 */
68 ahbclk /= 2 << ((cfgr0 & (RCC_HPRE_0 | RCC_HPRE_1 | RCC_HPRE_2)) >> 4);
69 } else {
70 /* The range 0b0nnn divides by n + 1, where 0b0000 is /1, 0b001 is /2, etc. */
71 ahbclk /= ((cfgr0 & (RCC_HPRE_0 | RCC_HPRE_1 | RCC_HPRE_2)) >> 4) + 1;
72 }
73
74 /* The datasheet says that AHB == APB1 == APB2, but the registers imply that APB1 and APB2
75 * can be divided from the AHB clock. Assume that the clock tree diagram is correct and
76 * always return AHB.
77 */
78 *rate = ahbclk;
79 return 0;
80 }
81
clock_control_wch_rcc_setup_flash(void)82 static void clock_control_wch_rcc_setup_flash(void)
83 {
84 #if defined(FLASH_ACTLR_LATENCY)
85 uint32_t latency;
86
87 #if defined(CONFIG_SOC_CH32V003)
88 if (WCH_RCC_SYSCLK <= 24000000) {
89 latency = FLASH_ACTLR_LATENCY_0;
90 } else {
91 latency = FLASH_ACTLR_LATENCY_1;
92 }
93 #elif defined(CONFIG_SOC_SERIES_CH32V00X)
94 if (WCH_RCC_SYSCLK <= 15000000) {
95 latency = FLASH_ACTLR_LATENCY_0;
96 } else if (WCH_RCC_SYSCLK <= 24000000) {
97 latency = FLASH_ACTLR_LATENCY_1;
98 } else {
99 latency = FLASH_ACTLR_LATENCY_2;
100 }
101 #else
102 #error Unrecognised SOC family
103 #endif
104 FLASH->ACTLR = (FLASH->ACTLR & ~FLASH_ACTLR_LATENCY) | latency;
105 #endif
106 }
107
108 static DEVICE_API(clock_control, clock_control_wch_rcc_api) = {
109 .on = clock_control_wch_rcc_on,
110 .get_rate = clock_control_wch_rcc_get_rate,
111 };
112
clock_control_wch_rcc_init(const struct device * dev)113 static int clock_control_wch_rcc_init(const struct device *dev)
114 {
115 const struct clock_control_wch_rcc_config *config = dev->config;
116
117 clock_control_wch_rcc_setup_flash();
118
119 if (IS_ENABLED(CONFIG_DT_HAS_WCH_CH32V00X_PLL_CLOCK_ENABLED) ||
120 IS_ENABLED(CONFIG_DT_HAS_WCH_CH32V20X_30X_PLL_CLOCK_ENABLED)) {
121 /* Disable the PLL before potentially changing the input clocks. */
122 RCC->CTLR &= ~RCC_PLLON;
123 }
124
125 /* Always enable the LSI. */
126 RCC->RSTSCKR |= RCC_LSION;
127 while ((RCC->RSTSCKR & RCC_LSIRDY) == 0) {
128 }
129
130 if (IS_ENABLED(CONFIG_DT_HAS_WCH_CH32V00X_HSI_CLOCK_ENABLED)) {
131 RCC->CTLR |= RCC_HSION;
132 while ((RCC->CTLR & RCC_HSIRDY) == 0) {
133 }
134 }
135
136 if (IS_ENABLED(CONFIG_DT_HAS_WCH_CH32V00X_HSE_CLOCK_ENABLED)) {
137 RCC->CTLR |= RCC_HSEON;
138 while ((RCC->CTLR & RCC_HSERDY) == 0) {
139 }
140 }
141
142 if (IS_ENABLED(CONFIG_DT_HAS_WCH_CH32V00X_PLL_CLOCK_ENABLED)) {
143 if (IS_ENABLED(WCH_RCC_PLL_SRC_IS_HSE)) {
144 RCC->CFGR0 |= RCC_PLLSRC;
145 } else if (IS_ENABLED(WCH_RCC_PLL_SRC_IS_HSI)) {
146 RCC->CFGR0 &= ~RCC_PLLSRC;
147 }
148 RCC->CTLR |= RCC_PLLON;
149 while ((RCC->CTLR & RCC_PLLRDY) == 0) {
150 }
151 }
152 if (IS_ENABLED(CONFIG_DT_HAS_WCH_CH32V20X_30X_PLL_CLOCK_ENABLED)) {
153 if (IS_ENABLED(WCH_RCC_PLL_SRC_IS_HSE)) {
154 RCC->CFGR0 |= RCC_PLLSRC;
155 } else if (IS_ENABLED(WCH_RCC_PLL_SRC_IS_HSI)) {
156 RCC->CFGR0 &= ~RCC_PLLSRC;
157 }
158 RCC->CFGR0 |= (config->mul == 18 ? 0xF : (config->mul - 2)) << 0x12;
159 RCC->CTLR |= RCC_PLLON;
160 while ((RCC->CTLR & RCC_PLLRDY) == 0) {
161 }
162 }
163
164 if (IS_ENABLED(WCH_RCC_SRC_IS_HSI)) {
165 RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_HSI;
166 } else if (IS_ENABLED(WCH_RCC_SRC_IS_HSE)) {
167 RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_HSE;
168 } else if (IS_ENABLED(WCH_RCC_SRC_IS_PLL)) {
169 RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_PLL;
170 }
171 RCC->CTLR |= RCC_CSSON;
172
173 /* Clear the interrupt flags. */
174 RCC->INTR = RCC_CSSC | RCC_PLLRDYC | RCC_HSERDYC | RCC_LSIRDYC;
175 /* HCLK = SYSCLK = APB1 */
176 RCC->CFGR0 = (RCC->CFGR0 & ~RCC_HPRE) | RCC_HPRE_DIV1;
177
178 return 0;
179 }
180
181 #define CLOCK_CONTROL_WCH_RCC_INIT(idx) \
182 static const struct clock_control_wch_rcc_config clock_control_wch_rcc_##idx##_config = { \
183 .regs = (RCC_TypeDef *)DT_INST_REG_ADDR(idx), \
184 .mul = DT_PROP_OR(DT_INST_CLOCKS_CTLR(idx), mul, 1), \
185 }; \
186 DEVICE_DT_INST_DEFINE(idx, clock_control_wch_rcc_init, NULL, NULL, \
187 &clock_control_wch_rcc_##idx##_config, PRE_KERNEL_1, \
188 CONFIG_CLOCK_CONTROL_INIT_PRIORITY, &clock_control_wch_rcc_api);
189
190 /* There is only ever one RCC */
191 CLOCK_CONTROL_WCH_RCC_INIT(0)
192