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)(&regs->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