hpm_pllctl_drv.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. /*
  2. * Copyright (c) 2021 HPMicro
  3. *
  4. * SPDX-License-Identifier: BSD-3-Clause
  5. *
  6. */
  7. #ifndef HPM_PLLCTL_DRV_H
  8. #define HPM_PLLCTL_DRV_H
  9. #include "hpm_common.h"
  10. #include "hpm_soc_feature.h"
  11. #include "hpm_pllctl_regs.h"
  12. /**
  13. *
  14. * @brief PLLCTL driver APIs
  15. * @defgroup pllctl_interface PLLCTL driver APIs
  16. * @{
  17. */
  18. #define PLLCTL_PLL_VCO_FREQ_MIN (375000000U)
  19. #define PLLCTL_PLL_VCO_FREQ_MAX (2200000000U)
  20. /*
  21. * @brief PLL parts with lock
  22. */
  23. #define PLLCTL_PLL_LOCK_SS_RESET PLLCTL_PLL_LOCK_LOCK_SS_RSTPTR_MASK
  24. #define PLLCTL_PLL_LOCK_REFDIV PLLCTL_PLL_LOCK_LOCK_REFDIV_MASK
  25. #define PLLCTL_PLL_LOCK_POSTDIV1 PLLCTL_PLL_LOCK_LOCK_POSTDIV1_MASK
  26. #define PLLCTL_PLL_LOCK_SS_SPREAD PLLCTL_PLL_LOCK_LOCK_SS_SPREAD_MASK
  27. #define PLLCTL_PLL_LOCK_SS_DIVVAL PLLCTL_PLL_LOCK_LOCK_SS_DIVVAL_MASK
  28. #define PLLCTL_PLL_LOCK_ALL (PLLCTL_PLL_LOCK_LOCK_SS_RSTPTR_MASK \
  29. | PLLCTL_PLL_LOCK_LOCK_REFDIV_MASK \
  30. | PLLCTL_PLL_LOCK_LOCK_POSTDIV1_MASK \
  31. | PLLCTL_PLL_LOCK_LOCK_SS_SPREAD_MASK \
  32. | PLLCTL_PLL_LOCK_LOCK_SS_DIVVAL_MASK \
  33. | PLLCTL_PLL_LOCK_LOCK_SS_DIVVAL_MASK)
  34. /*
  35. * @brief PLLCTL specific status
  36. */
  37. enum {
  38. status_pllctl_not_enabled = MAKE_STATUS(status_group_pllctl, 1),
  39. status_pllctl_out_of_range = MAKE_STATUS(status_group_pllctl, 2),
  40. };
  41. #ifdef __cplusplus
  42. extern "C" {
  43. #endif
  44. /**
  45. * @brief Unlock pll
  46. *
  47. * @param[in] ptr PLLCTL base address
  48. * @param[in] pll Target PLL index
  49. * @param[in] lock_mask Mask of PLL parts to be unlocked
  50. */
  51. static inline void pllctl_pll_unlock(PLLCTL_Type *ptr, uint8_t pll, uint32_t lock_mask)
  52. {
  53. ptr->PLL[pll].LOCK &= ~lock_mask;
  54. }
  55. /**
  56. * @brief Lock pll
  57. *
  58. * @param[in] ptr PLLCTL base address
  59. * @param[in] pll Target PLL index
  60. * @param[in] lock_mask Mask of PLL parts to be locked
  61. */
  62. static inline void pllctl_pll_lock(PLLCTL_Type *ptr, uint8_t pll, uint32_t lock_mask)
  63. {
  64. ptr->PLL[pll].LOCK = lock_mask;
  65. }
  66. /**
  67. * @brief Disable spread spectrum
  68. *
  69. * @param[in] ptr PLLCTL base address
  70. * @param[in] pll Target PLL index
  71. *
  72. * @return status_success if everything is okay
  73. */
  74. static inline hpm_stat_t pllctl_pll_ss_disable(PLLCTL_Type *ptr, uint8_t pll)
  75. {
  76. if (pll > (PLLCTL_SOC_PLL_MAX_COUNT - 1)) {
  77. return status_invalid_argument;
  78. }
  79. ptr->PLL[pll].CFG0 |= (PLLCTL_PLL_CFG0_SS_RSTPTR_MASK
  80. | PLLCTL_PLL_CFG0_SS_RESET_MASK);
  81. ptr->PLL[pll].CFG0 |= PLLCTL_PLL_CFG0_SS_DISABLE_SSCG_MASK;
  82. return status_success;
  83. }
  84. /**
  85. * @brief Power down target PLL
  86. *
  87. * @param[in] ptr PLLCTL base address
  88. * @param[in] pll Target PLL index
  89. *
  90. * @return status_success if everything is okay
  91. */
  92. static inline hpm_stat_t pllctl_pll_powerdown(PLLCTL_Type *ptr, uint8_t pll)
  93. {
  94. if (pll > (PLLCTL_SOC_PLL_MAX_COUNT - 1)) {
  95. return status_invalid_argument;
  96. }
  97. ptr->PLL[pll].CFG1 = (ptr->PLL[pll].CFG1 &
  98. ~(PLLCTL_PLL_CFG1_PLLCTRL_HW_EN_MASK | PLLCTL_PLL_CFG1_CLKEN_SW_MASK))
  99. | PLLCTL_PLL_CFG1_PLLPD_SW_MASK;
  100. return status_success;
  101. }
  102. /**
  103. * @brief Power on target PLL
  104. *
  105. * @param[in] ptr PLLCTL base address
  106. * @param[in] pll Target PLL index
  107. *
  108. * @return status_success if everything is okay
  109. */
  110. static inline hpm_stat_t pllctl_pll_poweron(PLLCTL_Type *ptr, uint8_t pll)
  111. {
  112. uint32_t cfg;
  113. if (pll > (PLLCTL_SOC_PLL_MAX_COUNT - 1)) {
  114. return status_invalid_argument;
  115. }
  116. cfg = ptr->PLL[pll].CFG1;
  117. if (!(cfg & PLLCTL_PLL_CFG1_PLLPD_SW_MASK)) {
  118. return status_success;
  119. }
  120. if (cfg & PLLCTL_PLL_CFG1_PLLCTRL_HW_EN_MASK) {
  121. ptr->PLL[pll].CFG1 &= ~PLLCTL_PLL_CFG1_PLLCTRL_HW_EN_MASK;
  122. }
  123. ptr->PLL[pll].CFG1 &= ~PLLCTL_PLL_CFG1_PLLPD_SW_MASK;
  124. /*
  125. * put back to hardware mode
  126. */
  127. ptr->PLL[pll].CFG1 |= PLLCTL_PLL_CFG1_PLLCTRL_HW_EN_MASK;
  128. return status_success;
  129. }
  130. /**
  131. * @brief Enable spread spectrum mode
  132. *
  133. * @param[in] ptr PLLCTL base address
  134. * @param[in] pll Target PLL index
  135. * @param[in] spread Spread spectrum depth (1-31, from 0.1% to 3.1%)
  136. * @param[in] div Spread spectrum divider (1-63, divide by 1 to 63)
  137. * @param[in] down_spread Set true if need down-spread, otherwise center-spread
  138. *
  139. * @return status_success if everything is okay
  140. */
  141. static inline hpm_stat_t pllctl_pll_ss_enable(PLLCTL_Type *ptr, uint8_t pll,
  142. uint8_t spread, uint8_t div,
  143. bool down_spread)
  144. {
  145. if ((pll > (PLLCTL_SOC_PLL_MAX_COUNT - 1))
  146. || (spread > (PLLCTL_PLL_CFG0_SS_SPREAD_MASK >> PLLCTL_PLL_CFG0_SS_SPREAD_SHIFT))
  147. || (div > (PLLCTL_PLL_CFG0_SS_DIVVAL_MASK >> PLLCTL_PLL_CFG0_SS_DIVVAL_SHIFT))) {
  148. return status_invalid_argument;
  149. }
  150. if (!(ptr->PLL[pll].CFG1 & PLLCTL_PLL_CFG1_PLLPD_SW_MASK)) {
  151. pllctl_pll_powerdown(ptr, pll);
  152. }
  153. ptr->PLL[pll].CFG0 &= ~(PLLCTL_PLL_CFG0_SS_RSTPTR_MASK
  154. | PLLCTL_PLL_CFG0_SS_RESET_MASK);
  155. ptr->PLL[pll].CFG0 &= ~PLLCTL_PLL_CFG0_SS_DISABLE_SSCG_MASK;
  156. ptr->PLL[pll].CFG0 = (ptr->PLL[pll].CFG0
  157. & ~(PLLCTL_PLL_CFG0_SS_SPREAD_MASK | PLLCTL_PLL_CFG0_SS_DIVVAL_MASK))
  158. | PLLCTL_PLL_CFG0_SS_SPREAD_SET(spread)
  159. | PLLCTL_PLL_CFG0_SS_DIVVAL_SET(div)
  160. | PLLCTL_PLL_CFG0_SS_DOWNSPREAD_SET(down_spread);
  161. pllctl_pll_poweron(ptr, pll);
  162. return status_success;
  163. }
  164. /**
  165. * @brief Set postdiv1 for PLL
  166. *
  167. * @param[in] ptr PLLCTL base address
  168. * @param[in] pll Target PLL index
  169. * @param[in] div Postdiv1 value (0x1~0x7)
  170. *
  171. * @return status_success if everything is okay
  172. */
  173. static inline hpm_stat_t pllctl_set_postdiv1(PLLCTL_Type *ptr, uint8_t pll, uint8_t div)
  174. {
  175. if ((pll > (PLLCTL_SOC_PLL_MAX_COUNT - 1))
  176. || (!div)
  177. || ((div) > (PLLCTL_PLL_CFG0_POSTDIV1_MASK >> PLLCTL_PLL_CFG0_POSTDIV1_SHIFT))) {
  178. return status_invalid_argument;
  179. }
  180. ptr->PLL[pll].CFG0 = ((ptr->PLL[pll].CFG0 & ~(PLLCTL_PLL_CFG0_POSTDIV1_MASK))) | PLLCTL_PLL_CFG0_POSTDIV1_SET(div);
  181. return status_success;
  182. }
  183. /**
  184. * @brief Set fbdiv for PLL integer mode
  185. *
  186. * Fout = Fref/refdiv * fbdiv / postdiv1
  187. *
  188. * @param[in] ptr PLLCTL base address
  189. * @param[in] pll Target PLL index
  190. * @param[in] fbdiv Fbdiv value (0x1~0x1000)
  191. * @note fbdiv value can not set too large, if Fref/refdiv * fbdiv > 2GHz, it might cause irrecoverable damage to that PLL
  192. *
  193. * @return status_success if everything is okay
  194. */
  195. static inline hpm_stat_t pllctl_set_fbdiv_int(PLLCTL_Type *ptr, uint8_t pll, uint16_t fbdiv)
  196. {
  197. if ((pll > (PLLCTL_SOC_PLL_MAX_COUNT - 1))
  198. || ((fbdiv - 1) > (PLLCTL_PLL_CFG2_FBDIV_INT_MASK >> PLLCTL_PLL_CFG2_FBDIV_INT_SHIFT))) {
  199. return status_invalid_argument;
  200. }
  201. ptr->PLL[pll].CFG2 = ((ptr->PLL[pll].CFG2 & ~(PLLCTL_PLL_CFG2_FBDIV_INT_MASK))) | PLLCTL_PLL_CFG2_FBDIV_INT_SET(fbdiv - 1);
  202. return status_success;
  203. }
  204. /**
  205. * @brief Set fbdiv for PLL fraction mode
  206. *
  207. * Fout = Fref/refdive * (fbdiv + frac/2^24)/postdiv1
  208. *
  209. * @param[in] ptr PLLCTL base address
  210. * @param[in] pll Target PLL index
  211. * @param[in] fbdiv Fbdiv value (0x1~0x1000)
  212. * @note fbdiv value can not set too large, if Fref/refdiv * fbdiv > 2GHz, it might cause irrecoverable damage to that PLL
  213. *
  214. * @return status_success if everything is okay
  215. */
  216. static inline hpm_stat_t pllctl_set_fbdiv_frac(PLLCTL_Type *ptr, uint8_t pll, uint16_t fbdiv)
  217. {
  218. if ((pll > (PLLCTL_SOC_PLL_MAX_COUNT - 1))
  219. || ((fbdiv - 1) > (PLLCTL_PLL_FREQ_FBDIV_FRAC_MASK >> PLLCTL_PLL_FREQ_FBDIV_FRAC_SHIFT))) {
  220. return status_invalid_argument;
  221. }
  222. ptr->PLL[pll].FREQ = (ptr->PLL[pll].FREQ & ~(PLLCTL_PLL_FREQ_FBDIV_FRAC_MASK))
  223. | PLLCTL_PLL_FREQ_FBDIV_FRAC_SET(fbdiv - 1);
  224. return status_success;
  225. }
  226. /**
  227. * @brief Set fraction for PLL fraction mode
  228. *
  229. * @param[in] ptr PLLCTL base address
  230. * @param[in] pll Target PLL index
  231. * @param[in] frac 24-bit fixed float point value
  232. *
  233. * @return
  234. */
  235. static inline hpm_stat_t pllctl_set_frac(PLLCTL_Type *ptr, uint8_t pll, uint32_t frac)
  236. {
  237. if ((pll > (PLLCTL_SOC_PLL_MAX_COUNT - 1))
  238. || (frac > (PLLCTL_PLL_FREQ_FRAC_MASK >> PLLCTL_PLL_FREQ_FRAC_SHIFT))) {
  239. return status_invalid_argument;
  240. }
  241. ptr->PLL[pll].FREQ = (ptr->PLL[pll].FREQ & ~(PLLCTL_PLL_FREQ_FRAC_MASK))
  242. | PLLCTL_PLL_FREQ_FRAC_SET(frac);
  243. return status_success;
  244. }
  245. /**
  246. * @brief Get PLL divx value
  247. *
  248. * @param[in] ptr PLLCTL base address
  249. * @param[in] pll Target PLL index
  250. * @param[in] div_index Target DIV to query
  251. *
  252. * @return Divider value of target DIV
  253. */
  254. static inline hpm_stat_t pllctl_get_div(PLLCTL_Type *ptr, uint8_t pll, uint8_t div_index)
  255. {
  256. if ((pll > (PLLCTL_SOC_PLL_MAX_COUNT - 1))
  257. || !(PLLCTL_SOC_PLL_HAS_DIV0(pll))) {
  258. return status_invalid_argument;
  259. }
  260. if (div_index) {
  261. return PLLCTL_PLL_DIV0_DIV_GET(ptr->PLL[pll].DIV1) + 1;
  262. } else {
  263. return PLLCTL_PLL_DIV0_DIV_GET(ptr->PLL[pll].DIV0) + 1;
  264. }
  265. }
  266. /**
  267. * @brief Set divider
  268. *
  269. * @param[in] ptr PLLCTL base address
  270. * @param[in] pll Target PLL index
  271. * @param[in] div_index DIV index
  272. * @param[in] div Divider value (starting from 1)
  273. *
  274. * @return status_success if everything is okay
  275. */
  276. static inline hpm_stat_t pllctl_set_div(PLLCTL_Type *ptr, uint8_t pll, uint8_t div_index, uint16_t div)
  277. {
  278. if ((pll > (PLLCTL_SOC_PLL_MAX_COUNT - 1))
  279. || !(PLLCTL_SOC_PLL_HAS_DIV0(pll))
  280. || ((div - 1) > (PLLCTL_PLL_DIV0_DIV_MASK >> PLLCTL_PLL_DIV0_DIV_SHIFT))) {
  281. return status_invalid_argument;
  282. }
  283. if (div_index) {
  284. ptr->PLL[pll].DIV1 = (ptr->PLL[pll].DIV1 & ~(PLLCTL_PLL_DIV1_DIV_MASK))
  285. | PLLCTL_PLL_DIV1_DIV_SET(div - 1);
  286. } else {
  287. ptr->PLL[pll].DIV0 = (ptr->PLL[pll].DIV0 & ~(PLLCTL_PLL_DIV0_DIV_MASK))
  288. | PLLCTL_PLL_DIV0_DIV_SET(div - 1);
  289. }
  290. return status_success;
  291. }
  292. /**
  293. * @brief Check if specific PLL DIV is stable
  294. *
  295. * @param[in] ptr PLLCTL base address
  296. * @param[in] pll Target PLL index
  297. * @param[in] div_index Target DIV to check
  298. *
  299. * @return true if target PLL DIV is stable
  300. */
  301. static inline bool pllctl_div_is_stable(PLLCTL_Type *ptr, uint8_t pll, uint8_t div_index)
  302. {
  303. if ((pll > (PLLCTL_SOC_PLL_MAX_COUNT - 1)) || !(PLLCTL_SOC_PLL_HAS_DIV0(pll))) {
  304. return false;
  305. }
  306. if (div_index) {
  307. return ptr->PLL[pll].DIV1 & PLLCTL_PLL_DIV0_RESPONSE_MASK;
  308. } else {
  309. return ptr->PLL[pll].DIV0 & PLLCTL_PLL_DIV0_RESPONSE_MASK;
  310. }
  311. }
  312. /**
  313. * @brief Check if target PLL is enabled
  314. *
  315. * @param[in] ptr PLLCTL base address
  316. * @param[in] pll Target PLL index
  317. *
  318. * @return true if target PLL is enabled
  319. */
  320. static inline bool pllctl_pll_is_enabled(PLLCTL_Type *ptr, uint8_t pll)
  321. {
  322. return (ptr->PLL[pll].STATUS & PLLCTL_PLL_STATUS_ENABLE_MASK);
  323. }
  324. /**
  325. * @brief Check if XTAL is stable
  326. *
  327. * @param[in] ptr PLLCTL base address
  328. *
  329. * @return true if XTAL is stable
  330. */
  331. static inline bool pllctl_xtal_is_stable(PLLCTL_Type *ptr)
  332. {
  333. return ptr->XTAL & PLLCTL_XTAL_RESPONSE_MASK;
  334. }
  335. /**
  336. * @brief Check if XTAL is enabled
  337. *
  338. * @param[in] ptr PLLCTL base address
  339. *
  340. * @return true if XTAL is enabled
  341. */
  342. static inline bool pllctl_xtal_is_enabled(PLLCTL_Type *ptr)
  343. {
  344. return ptr->XTAL & PLLCTL_XTAL_ENABLE_MASK;
  345. }
  346. /*
  347. * @brief set XTAL rampup time in cycles of IRC24M
  348. *
  349. * @param[in] ptr PLLCTL base address
  350. */
  351. static inline void pllctl_xtal_set_rampup_time(PLLCTL_Type *ptr, uint32_t cycles)
  352. {
  353. ptr->XTAL = (ptr->XTAL & ~PLLCTL_XTAL_RAMP_TIME_MASK) | PLLCTL_XTAL_RAMP_TIME_SET(cycles);
  354. }
  355. /**
  356. * @brief Set refdiv
  357. *
  358. * @param[in] ptr PLLCTL base address
  359. * @param[in] pll Target PLL index
  360. * @param[in] div Divider value (0x1-0x3F)
  361. *
  362. * @return status_success if everything is okay
  363. */
  364. hpm_stat_t pllctl_set_refdiv(PLLCTL_Type *ptr, uint8_t pll, uint8_t div);
  365. /**
  366. * @brief Initialize PLL working at integer mode with specific frequency
  367. *
  368. * @param[in] ptr PLLCTL base address
  369. * @param[in] pll Target PLL index
  370. * @param[in] freq_in_hz Target frequency, expected >= 375000000Hz
  371. *
  372. * @return status_success if everything is okay
  373. *
  374. * @note The actual frequency might be slightly different from freq_in_hz due to calculation.
  375. */
  376. hpm_stat_t pllctl_init_int_pll_with_freq(PLLCTL_Type *ptr, uint8_t pll,
  377. uint32_t freq_in_hz);
  378. /**
  379. * @brief Initialize PLL working at franction mode with specific frequency
  380. *
  381. * @param[in] ptr PLLCTL base address
  382. * @param[in] pll Target PLL index
  383. * @param[in] freq_in_hz Target frequency, expected >= 375000000Hz
  384. *
  385. * @return status_success if everything is okay
  386. * @note The actual frequency might be slightly different from freq_in_hz due to calculation.
  387. */
  388. hpm_stat_t pllctl_init_frac_pll_with_freq(PLLCTL_Type *ptr, uint8_t pll,
  389. uint32_t freq_in_hz);
  390. /**
  391. * @brief Get frequency of target PLL
  392. *
  393. * @param[in] ptr PLLCTL base address
  394. * @param[in] pll Target PLL index
  395. *
  396. * @return current frequency of target PLL in Hz
  397. */
  398. uint32_t pllctl_get_pll_freq_in_hz(PLLCTL_Type *ptr, uint8_t pll);
  399. #ifdef __cplusplus
  400. }
  401. #endif
  402. /**
  403. * @}
  404. */
  405. #endif /* HPM_PLLCTL_DRV_H */