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