ch56x_sys.c 7.3 KB

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