ch56x_sys.c 7.3 KB

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