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_STATUS_OKAY(DT_NODELABEL(pll)) && DT_NODE_HAS_PROP(DT_NODELABEL(pll), clocks)
23 #define DT_PLL_CLOCKS_CTRL DT_CLOCKS_CTLR(DT_NODELABEL(pll))
24 #if DT_SAME_NODE(DT_PLL_CLOCKS_CTRL, DT_NODELABEL(clk_hsi))
25 #define WCH_RCC_PLL_SRC_IS_HSI 1
26 #endif
27 #if DT_SAME_NODE(DT_PLL_CLOCKS_CTRL, DT_NODELABEL(clk_hse))
28 #define WCH_RCC_PLL_SRC_IS_HSE 1
29 #endif
30 #endif
31
32 #define DT_RCC_CLOCKS_CTRL DT_CLOCKS_CTLR(DT_NODELABEL(rcc))
33 #if DT_SAME_NODE(DT_RCC_CLOCKS_CTRL, DT_NODELABEL(pll))
34 #define WCH_RCC_SRC_IS_PLL 1
35 #endif
36 #if DT_SAME_NODE(DT_RCC_CLOCKS_CTRL, DT_NODELABEL(clk_hsi))
37 #define WCH_RCC_SRC_IS_HSI 1
38 #endif
39 #if DT_SAME_NODE(DT_RCC_CLOCKS_CTRL, DT_NODELABEL(clk_hse))
40 #define WCH_RCC_SRC_IS_HSE 1
41 #endif
42
43 struct clock_control_wch_rcc_config {
44 RCC_TypeDef *regs;
45 };
46
clock_control_wch_rcc_on(const struct device * dev,clock_control_subsys_t sys)47 static int clock_control_wch_rcc_on(const struct device *dev, clock_control_subsys_t sys)
48 {
49 const struct clock_control_wch_rcc_config *config = dev->config;
50 RCC_TypeDef *regs = config->regs;
51 uint8_t id = (uintptr_t)sys;
52 uint32_t reg = (uint32_t)(®s->AHBPCENR + WCH_RCC_CLOCK_ID_OFFSET(id));
53 uint32_t val = sys_read32(reg);
54
55 val |= BIT(WCH_RCC_CLOCK_ID_BIT(id));
56 sys_write32(val, reg);
57
58 return 0;
59 }
60
clock_control_wch_rcc_get_rate(const struct device * dev,clock_control_subsys_t sys,uint32_t * rate)61 static int clock_control_wch_rcc_get_rate(const struct device *dev, clock_control_subsys_t sys,
62 uint32_t *rate)
63 {
64 const struct clock_control_wch_rcc_config *config = dev->config;
65 RCC_TypeDef *regs = config->regs;
66 uint32_t cfgr0 = regs->CFGR0;
67 uint32_t sysclk = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC;
68 uint32_t ahbclk = sysclk;
69
70 if ((cfgr0 & RCC_HPRE_3) != 0) {
71 /* The range 0b1000 divides by a power of 2, where 0b1000 is /2, 0b1001 is /4, etc.
72 */
73 ahbclk /= 2 << ((cfgr0 & (RCC_HPRE_0 | RCC_HPRE_1 | RCC_HPRE_2)) >> 4);
74 } else {
75 /* The range 0b0nnn divides by n + 1, where 0b0000 is /1, 0b001 is /2, etc. */
76 ahbclk /= ((cfgr0 & (RCC_HPRE_0 | RCC_HPRE_1 | RCC_HPRE_2)) >> 4) + 1;
77 }
78
79 /* The datasheet says that AHB == APB1 == APB2, but the registers imply that APB1 and APB2
80 * can be divided from the AHB clock. Assume that the clock tree diagram is correct and
81 * always return AHB.
82 */
83 *rate = ahbclk;
84 return 0;
85 }
86
87 static DEVICE_API(clock_control, clock_control_wch_rcc_api) = {
88 .on = clock_control_wch_rcc_on,
89 .get_rate = clock_control_wch_rcc_get_rate,
90 };
91
clock_control_wch_rcc_init(const struct device * dev)92 static int clock_control_wch_rcc_init(const struct device *dev)
93 {
94 if (IS_ENABLED(CONFIG_DT_HAS_WCH_CH32V00X_PLL_CLOCK_ENABLED)) {
95 /* Disable the PLL before potentially changing the input clocks. */
96 RCC->CTLR &= ~RCC_PLLON;
97 }
98
99 /* Always enable the LSI. */
100 RCC->RSTSCKR |= RCC_LSION;
101 while ((RCC->RSTSCKR & RCC_LSIRDY) == 0) {
102 }
103
104 if (IS_ENABLED(CONFIG_DT_HAS_WCH_CH32V00X_HSI_CLOCK_ENABLED)) {
105 RCC->CTLR |= RCC_HSION;
106 while ((RCC->CTLR & RCC_HSIRDY) == 0) {
107 }
108 }
109
110 if (IS_ENABLED(CONFIG_DT_HAS_WCH_CH32V00X_HSE_CLOCK_ENABLED)) {
111 RCC->CTLR |= RCC_HSEON;
112 while ((RCC->CTLR & RCC_HSERDY) == 0) {
113 }
114 }
115
116 if (IS_ENABLED(CONFIG_DT_HAS_WCH_CH32V00X_PLL_CLOCK_ENABLED)) {
117 if (IS_ENABLED(WCH_RCC_PLL_SRC_IS_HSE)) {
118 RCC->CFGR0 |= RCC_PLLSRC;
119 } else if (IS_ENABLED(WCH_RCC_PLL_SRC_IS_HSI)) {
120 RCC->CFGR0 &= ~RCC_PLLSRC;
121 }
122 RCC->CTLR |= RCC_PLLON;
123 while ((RCC->CTLR & RCC_PLLRDY) == 0) {
124 }
125 }
126
127 if (IS_ENABLED(WCH_RCC_SRC_IS_HSI)) {
128 RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_HSI;
129 } else if (IS_ENABLED(WCH_RCC_SRC_IS_HSE)) {
130 RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_HSE;
131 } else if (IS_ENABLED(WCH_RCC_SRC_IS_PLL)) {
132 RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_PLL;
133 }
134 RCC->CTLR |= RCC_CSSON;
135
136 /* Clear the interrupt flags. */
137 RCC->INTR = RCC_CSSC | RCC_PLLRDYC | RCC_HSERDYC | RCC_LSIRDYC;
138 /* HCLK = SYSCLK = APB1 */
139 RCC->CFGR0 = (RCC->CFGR0 & ~RCC_HPRE) | RCC_HPRE_DIV1;
140 /* Set the Flash to 0 wait state */
141 FLASH->ACTLR = (FLASH->ACTLR & ~FLASH_ACTLR_LATENCY) | FLASH_ACTLR_LATENCY_1;
142
143 return 0;
144 }
145
146 #define CLOCK_CONTROL_WCH_RCC_INIT(idx) \
147 static const struct clock_control_wch_rcc_config clock_control_wch_rcc_##idx##_config = { \
148 .regs = (RCC_TypeDef *)DT_INST_REG_ADDR(idx), \
149 }; \
150 DEVICE_DT_INST_DEFINE(idx, clock_control_wch_rcc_init, NULL, NULL, \
151 &clock_control_wch_rcc_##idx##_config, PRE_KERNEL_1, \
152 CONFIG_CLOCK_CONTROL_INIT_PRIORITY, &clock_control_wch_rcc_api);
153
154 DT_INST_FOREACH_STATUS_OKAY(CLOCK_CONTROL_WCH_RCC_INIT)
155