1 /*
2 * Copyright (c) 2006-2023, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2022-07-15 Emuzit first version
9 */
10 #include <rthw.h>
11 #include "ch56x_sys.h"
12
13 static uint32_t hclk_freq;
14
_slp_clk_off0_irqn_bit(uint8_t irqn)15 rt_inline uint8_t _slp_clk_off0_irqn_bit(uint8_t irqn)
16 {
17 uint8_t bitpos;
18
19 switch (irqn)
20 {
21 case TMR0_IRQn: bitpos = RB_SLP_CLK_TMR0; break;
22 case TMR1_IRQn: bitpos = RB_SLP_CLK_TMR1; break;
23 case TMR2_IRQn: bitpos = RB_SLP_CLK_TMR2; break;
24 /* special case to control PWMX clock in irqn way */
25 case PWMX_OFFn: bitpos = RB_SLP_CLK_PWMX; break;
26 case UART0_IRQn: bitpos = RB_SLP_CLK_UART0; break;
27 case UART1_IRQn: bitpos = RB_SLP_CLK_UART1; break;
28 case UART2_IRQn: bitpos = RB_SLP_CLK_UART2; break;
29 case UART3_IRQn: bitpos = RB_SLP_CLK_UART3; break;
30 default:
31 bitpos = 0;
32 }
33
34 return bitpos;
35 }
36
_slp_clk_off1_irqn_bit(uint8_t irqn)37 rt_inline uint8_t _slp_clk_off1_irqn_bit(uint8_t irqn)
38 {
39 uint8_t bitpos;
40
41 switch (irqn)
42 {
43 case SPI0_IRQn: bitpos = RB_SLP_CLK_SPI0; break;
44 case SPI1_IRQn: bitpos = RB_SLP_CLK_SPI1; break;
45 #if defined(SOC_CH567)
46 case SDC_IRQn: bitpos = RB_SLP_CLK_SDC; break;
47 case LED_IRQn: bitpos = RB_SLP_CLK_LED; break;
48 case USB0_IRQn: bitpos = RB_SLP_CLK_USB0; break;
49 case USB1_IRQn: bitpos = RB_SLP_CLK_USB1; break;
50 case ECDC_IRQn: bitpos = RB_SLP_CLK_ECDC; break;
51 #elif defined(SOC_CH568)
52 case SDC_IRQn: bitpos = RB_SLP_CLK_SDC; break;
53 case LED_IRQn: bitpos = RB_SLP_CLK_LED; break;
54 case USB1_IRQn: bitpos = RB_SLP_CLK_USB1; break;
55 case USB0_IRQn: bitpos = RB_SLP_CLK_SATA; break;
56 case ECDC_IRQn: bitpos = RB_SLP_CLK_ECDC; break;
57 #else
58 case EMMC_IRQn: bitpos = RB_SLP_CLK_EMMC; break;
59 case HSPI_IRQn: bitpos = RB_SLP_CLK_HSPI; break;
60 case USBHS_IRQn: bitpos = RB_SLP_CLK_USBHS; break;
61 case USBSS_IRQn: bitpos = RB_SLP_CLK_USBSS; break;
62 case SerDes_IRQn: bitpos = RB_SLP_CLK_SERD; break;
63 case DVP_IRQn: bitpos = RB_SLP_CLK_DVP; break;
64 #endif
65 default:
66 bitpos = 0;
67 }
68
69 return bitpos;
70 }
71
72 #if defined(SOC_SERIES_CH569)
_wake_clk_off_irqn_bit(uint8_t irqn)73 rt_inline uint8_t _wake_clk_off_irqn_bit(uint8_t irqn)
74 {
75 uint8_t bitpos;
76
77 switch (irqn)
78 {
79 case ETH_IRQn: bitpos = RB_SLP_CLK_ETH; break;
80 case ECDC_IRQn: bitpos = RB_SLP_CLK_ECDC; break;
81 default:
82 bitpos = 0;
83 }
84
85 return bitpos;
86 }
87 #endif
88
89 /**
90 * @brief Turn on/off device clock for group clk_off0.
91 *
92 * @param bits is a bit mask to select corresponding devices.
93 *
94 * @param off is to turn off the clock (1) or trun on (0).
95 */
sys_slp_clk_off0(uint8_t bits,int off)96 void sys_slp_clk_off0(uint8_t bits, int off)
97 {
98 volatile struct sys_registers *sys = (void *)SYS_REG_BASE;
99 rt_base_t level;
100 uint8_t u8v;
101
102 u8v = sys->SLP_CLK_OFF0.reg;
103 if ((u8v & bits) != (off ? bits : 0))
104 {
105 u8v = off ? (u8v | bits) : (u8v & ~bits);
106 level = rt_hw_interrupt_disable();
107 sys_safe_access_enter(sys);
108 sys->SLP_CLK_OFF0.reg = u8v;
109 sys_safe_access_leave(sys);
110 rt_hw_interrupt_enable(level);
111 }
112 }
113
114 /**
115 * @brief Turn on/off device clock for group clk_off1.
116 *
117 * @param bits is a bit mask to select corresponding devices.
118 *
119 * @param off is to turn off the clock (1) or trun on (0).
120 */
sys_slp_clk_off1(uint8_t bits,int off)121 void sys_slp_clk_off1(uint8_t bits, int off)
122 {
123 volatile struct sys_registers *sys = (void *)SYS_REG_BASE;
124 rt_base_t level;
125 uint8_t u8v;
126
127 u8v = sys->SLP_CLK_OFF1.reg;
128 if ((u8v & bits) != (off ? bits : 0))
129 {
130 u8v = off ? (u8v | bits) : (u8v & ~bits);
131 level = rt_hw_interrupt_disable();
132 sys_safe_access_enter(sys);
133 sys->SLP_CLK_OFF1.reg = u8v;
134 sys_safe_access_leave(sys);
135 rt_hw_interrupt_enable(level);
136 }
137 }
138
139 /**
140 * @brief Turn on/off device clock, specified by its irq number.
141 *
142 * @param irqn is the irq number of the target device.
143 * PWMX does not have irqn, use special PWMX_OFFn number.
144 *
145 * @param off is to turn off the clock (1) or trun on (0).
146 *
147 * @return Returns if irqn-device has corresponding clk off bit :
148 * 0 if device not found; otherwise bit position of off0/off1.
149 */
sys_clk_off_by_irqn(uint8_t irqn,int off)150 int sys_clk_off_by_irqn(uint8_t irqn, int off)
151 {
152 volatile struct sys_registers *sys = (void *)SYS_REG_BASE;
153 uint8_t u8v;
154 size_t offset;
155
156 uint8_t bitpos = 0;
157
158 if (irqn < END_OF_IRQn)
159 {
160 if ((bitpos = _slp_clk_off0_irqn_bit(irqn)) != 0)
161 {
162 offset = offsetof(struct sys_registers, SLP_CLK_OFF0);
163 }
164 else if ((bitpos = _slp_clk_off1_irqn_bit(irqn)) != 0)
165 {
166 offset = offsetof(struct sys_registers, SLP_CLK_OFF1);
167 }
168 #if defined(SOC_SERIES_CH569)
169 else if ((bitpos = _wake_clk_off_irqn_bit(irqn)) != 0)
170 {
171 offset = offsetof(struct sys_registers, SLP_WAKE_CTRL);
172 }
173 #endif
174 if (bitpos)
175 {
176 volatile uint8_t *cxreg = (void *)sys;
177 rt_base_t level;
178 u8v = cxreg[offset];
179 if ((u8v & bitpos) != (off ? bitpos : 0))
180 {
181 u8v = off ? (u8v | bitpos) : (u8v & ~bitpos);
182 level = rt_hw_interrupt_disable();
183 sys_safe_access_enter(sys);
184 cxreg[offset] = u8v;
185 sys_safe_access_leave(sys);
186 rt_hw_interrupt_enable(level);
187 }
188 }
189 }
190
191 return bitpos;
192 }
193
194 /**
195 * @brief Setup HCLK frequency.
196 *
197 * @param freq is the desired hclk frequency.
198 * supported : 120/96/80/60/48/40/32/30/15/10/6/3/2 MHz
199 *
200 * @return Returns 0 if hclk is successfully set.
201 */
sys_hclk_set(uint32_t freq)202 int sys_hclk_set(uint32_t freq)
203 {
204 volatile struct sys_registers *sys = (void *)SYS_REG_BASE;
205
206 uint8_t plldiv;
207
208 int clksel = -1;
209
210 if (freq >= 30000000)
211 {
212 if (freq <= 120000000)
213 {
214 /* supported : 120/96/80/60/48/40/32/30 MHz */
215 plldiv = 480000000 / freq; // 30M => 16 & 0xf => 0
216 clksel = RB_CLK_SEL_PLL;
217 }
218 }
219 else if (freq >= 2000000)
220 {
221 /* supported : 15/10/6/3/2 MHz */
222 plldiv = 30000000 / freq;
223 clksel = 0;
224 }
225
226 if (clksel >= 0)
227 {
228 rt_base_t level = rt_hw_interrupt_disable();
229 sys_safe_access_enter(sys);
230 sys->CLK_PLL_DIV.reg = clk_pll_div_wdat(plldiv);
231 sys->CLK_CFG_CTRL.reg = clk_cfg_ctrl_wdat(clksel);
232 sys_safe_access_leave(sys);
233 rt_hw_interrupt_enable(level);
234 /* save to hclk_freq for quick report */
235 sys_hclk_calc();
236 clksel = 0;
237 }
238
239 return clksel;
240 }
241
242 /**
243 * @brief Get saved HCLK frequency.
244 *
245 * Valid only if HCLK is set strickly with sys_hclk_set().
246 * Use sys_hclk_calc() otherwise.
247 *
248 * @return Returns saved HCLK frequency (Hz, 0 if not set yet).
249 */
sys_hclk_get(void)250 uint32_t sys_hclk_get(void)
251 {
252 return hclk_freq;
253 }
254
255 /**
256 * @brief Get current HCLK frequency, calculated from hw setting.
257 *
258 * @return Returns current HCLK frequency (Hz).
259 */
sys_hclk_calc(void)260 uint32_t sys_hclk_calc(void)
261 {
262 volatile struct sys_registers *sys = (void *)SYS_REG_BASE;
263
264 uint8_t plldiv = sys->CLK_PLL_DIV.pll_div;
265
266 if (sys->CLK_CFG_CTRL.sel_pll == CLK_SEL_PLL_USB_480M)
267 {
268 hclk_freq = plldiv ? 480000000 / plldiv : 30000000;
269 }
270 else
271 {
272 hclk_freq = plldiv ? 30000000 / plldiv : 2000000;
273 }
274
275 return hclk_freq;
276 }
277