123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447 |
- /*
- * Copyright (c) 2021 HPMicro
- *
- * SPDX-License-Identifier: BSD-3-Clause
- *
- */
- #ifndef HPM_PLLCTL_DRV_H
- #define HPM_PLLCTL_DRV_H
- #include "hpm_common.h"
- #include "hpm_soc_feature.h"
- #include "hpm_pllctl_regs.h"
- /**
- *
- * @brief PLLCTL driver APIs
- * @defgroup pllctl_interface PLLCTL driver APIs
- * @{
- */
- #define PLLCTL_PLL_VCO_FREQ_MIN (375000000U)
- #define PLLCTL_PLL_VCO_FREQ_MAX (2200000000U)
- /*
- * @brief PLL parts with lock
- */
- #define PLLCTL_PLL_LOCK_SS_RESET PLLCTL_PLL_LOCK_LOCK_SS_RSTPTR_MASK
- #define PLLCTL_PLL_LOCK_REFDIV PLLCTL_PLL_LOCK_LOCK_REFDIV_MASK
- #define PLLCTL_PLL_LOCK_POSTDIV1 PLLCTL_PLL_LOCK_LOCK_POSTDIV1_MASK
- #define PLLCTL_PLL_LOCK_SS_SPREAD PLLCTL_PLL_LOCK_LOCK_SS_SPREAD_MASK
- #define PLLCTL_PLL_LOCK_SS_DIVVAL PLLCTL_PLL_LOCK_LOCK_SS_DIVVAL_MASK
- #define PLLCTL_PLL_LOCK_ALL (PLLCTL_PLL_LOCK_LOCK_SS_RSTPTR_MASK \
- | PLLCTL_PLL_LOCK_LOCK_REFDIV_MASK \
- | PLLCTL_PLL_LOCK_LOCK_POSTDIV1_MASK \
- | PLLCTL_PLL_LOCK_LOCK_SS_SPREAD_MASK \
- | PLLCTL_PLL_LOCK_LOCK_SS_DIVVAL_MASK \
- | PLLCTL_PLL_LOCK_LOCK_SS_DIVVAL_MASK)
- /*
- * @brief PLLCTL specific status
- */
- enum {
- status_pllctl_not_enabled = MAKE_STATUS(status_group_pllctl, 1),
- status_pllctl_out_of_range = MAKE_STATUS(status_group_pllctl, 2),
- };
- #ifdef __cplusplus
- extern "C" {
- #endif
- /**
- * @brief Unlock pll
- *
- * @param[in] ptr PLLCTL base address
- * @param[in] pll Target PLL index
- * @param[in] lock_mask Mask of PLL parts to be unlocked
- */
- static inline void pllctl_pll_unlock(PLLCTL_Type *ptr, uint8_t pll, uint32_t lock_mask)
- {
- ptr->PLL[pll].LOCK &= ~lock_mask;
- }
- /**
- * @brief Lock pll
- *
- * @param[in] ptr PLLCTL base address
- * @param[in] pll Target PLL index
- * @param[in] lock_mask Mask of PLL parts to be locked
- */
- static inline void pllctl_pll_lock(PLLCTL_Type *ptr, uint8_t pll, uint32_t lock_mask)
- {
- ptr->PLL[pll].LOCK = lock_mask;
- }
- /**
- * @brief Disable spread spectrum
- *
- * @param[in] ptr PLLCTL base address
- * @param[in] pll Target PLL index
- *
- * @return status_success if everything is okay
- */
- static inline hpm_stat_t pllctl_pll_ss_disable(PLLCTL_Type *ptr, uint8_t pll)
- {
- if (pll > (PLLCTL_SOC_PLL_MAX_COUNT - 1)) {
- return status_invalid_argument;
- }
- ptr->PLL[pll].CFG0 |= (PLLCTL_PLL_CFG0_SS_RSTPTR_MASK
- | PLLCTL_PLL_CFG0_SS_RESET_MASK);
- ptr->PLL[pll].CFG0 |= PLLCTL_PLL_CFG0_SS_DISABLE_SSCG_MASK;
- return status_success;
- }
- /**
- * @brief Power down target PLL
- *
- * @param[in] ptr PLLCTL base address
- * @param[in] pll Target PLL index
- *
- * @return status_success if everything is okay
- */
- static inline hpm_stat_t pllctl_pll_powerdown(PLLCTL_Type *ptr, uint8_t pll)
- {
- if (pll > (PLLCTL_SOC_PLL_MAX_COUNT - 1)) {
- return status_invalid_argument;
- }
- ptr->PLL[pll].CFG1 = (ptr->PLL[pll].CFG1 &
- ~(PLLCTL_PLL_CFG1_PLLCTRL_HW_EN_MASK | PLLCTL_PLL_CFG1_CLKEN_SW_MASK))
- | PLLCTL_PLL_CFG1_PLLPD_SW_MASK;
- return status_success;
- }
- /**
- * @brief Power on target PLL
- *
- * @param[in] ptr PLLCTL base address
- * @param[in] pll Target PLL index
- *
- * @return status_success if everything is okay
- */
- static inline hpm_stat_t pllctl_pll_poweron(PLLCTL_Type *ptr, uint8_t pll)
- {
- uint32_t cfg;
- if (pll > (PLLCTL_SOC_PLL_MAX_COUNT - 1)) {
- return status_invalid_argument;
- }
- cfg = ptr->PLL[pll].CFG1;
- if (!(cfg & PLLCTL_PLL_CFG1_PLLPD_SW_MASK)) {
- return status_success;
- }
- if (cfg & PLLCTL_PLL_CFG1_PLLCTRL_HW_EN_MASK) {
- ptr->PLL[pll].CFG1 &= ~PLLCTL_PLL_CFG1_PLLCTRL_HW_EN_MASK;
- }
- ptr->PLL[pll].CFG1 &= ~PLLCTL_PLL_CFG1_PLLPD_SW_MASK;
- /*
- * put back to hardware mode
- */
- ptr->PLL[pll].CFG1 |= PLLCTL_PLL_CFG1_PLLCTRL_HW_EN_MASK;
- return status_success;
- }
- /**
- * @brief Enable spread spectrum mode
- *
- * @param[in] ptr PLLCTL base address
- * @param[in] pll Target PLL index
- * @param[in] spread Spread spectrum depth (1-31, from 0.1% to 3.1%)
- * @param[in] div Spread spectrum divider (1-63, divide by 1 to 63)
- * @param[in] down_spread Set true if need down-spread, otherwise center-spread
- *
- * @return status_success if everything is okay
- */
- static inline hpm_stat_t pllctl_pll_ss_enable(PLLCTL_Type *ptr, uint8_t pll,
- uint8_t spread, uint8_t div,
- bool down_spread)
- {
- if ((pll > (PLLCTL_SOC_PLL_MAX_COUNT - 1))
- || (spread > (PLLCTL_PLL_CFG0_SS_SPREAD_MASK >> PLLCTL_PLL_CFG0_SS_SPREAD_SHIFT))
- || (div > (PLLCTL_PLL_CFG0_SS_DIVVAL_MASK >> PLLCTL_PLL_CFG0_SS_DIVVAL_SHIFT))) {
- return status_invalid_argument;
- }
- if (!(ptr->PLL[pll].CFG1 & PLLCTL_PLL_CFG1_PLLPD_SW_MASK)) {
- pllctl_pll_powerdown(ptr, pll);
- }
- ptr->PLL[pll].CFG0 &= ~(PLLCTL_PLL_CFG0_SS_RSTPTR_MASK
- | PLLCTL_PLL_CFG0_SS_RESET_MASK);
- ptr->PLL[pll].CFG0 &= ~PLLCTL_PLL_CFG0_SS_DISABLE_SSCG_MASK;
- ptr->PLL[pll].CFG0 = (ptr->PLL[pll].CFG0
- & ~(PLLCTL_PLL_CFG0_SS_SPREAD_MASK | PLLCTL_PLL_CFG0_SS_DIVVAL_MASK))
- | PLLCTL_PLL_CFG0_SS_SPREAD_SET(spread)
- | PLLCTL_PLL_CFG0_SS_DIVVAL_SET(div)
- | PLLCTL_PLL_CFG0_SS_DOWNSPREAD_SET(down_spread);
- pllctl_pll_poweron(ptr, pll);
- return status_success;
- }
- /**
- * @brief Set postdiv1 for PLL
- *
- * @param[in] ptr PLLCTL base address
- * @param[in] pll Target PLL index
- * @param[in] div Postdiv1 value (0x1~0x7)
- *
- * @return status_success if everything is okay
- */
- static inline hpm_stat_t pllctl_set_postdiv1(PLLCTL_Type *ptr, uint8_t pll, uint8_t div)
- {
- if ((pll > (PLLCTL_SOC_PLL_MAX_COUNT - 1))
- || (!div)
- || ((div) > (PLLCTL_PLL_CFG0_POSTDIV1_MASK >> PLLCTL_PLL_CFG0_POSTDIV1_SHIFT))) {
- return status_invalid_argument;
- }
- ptr->PLL[pll].CFG0 = ((ptr->PLL[pll].CFG0 & ~(PLLCTL_PLL_CFG0_POSTDIV1_MASK))) | PLLCTL_PLL_CFG0_POSTDIV1_SET(div);
- return status_success;
- }
- /**
- * @brief Set fbdiv for PLL integer mode
- *
- * Fout = Fref/refdiv * fbdiv / postdiv1
- *
- * @param[in] ptr PLLCTL base address
- * @param[in] pll Target PLL index
- * @param[in] fbdiv Fbdiv value (0x1~0x1000)
- * @note fbdiv value can not set too large, if Fref/refdiv * fbdiv > 2GHz, it might cause irrecoverable damage to that PLL
- *
- * @return status_success if everything is okay
- */
- static inline hpm_stat_t pllctl_set_fbdiv_int(PLLCTL_Type *ptr, uint8_t pll, uint16_t fbdiv)
- {
- if ((pll > (PLLCTL_SOC_PLL_MAX_COUNT - 1))
- || ((fbdiv - 1) > (PLLCTL_PLL_CFG2_FBDIV_INT_MASK >> PLLCTL_PLL_CFG2_FBDIV_INT_SHIFT))) {
- return status_invalid_argument;
- }
- ptr->PLL[pll].CFG2 = ((ptr->PLL[pll].CFG2 & ~(PLLCTL_PLL_CFG2_FBDIV_INT_MASK))) | PLLCTL_PLL_CFG2_FBDIV_INT_SET(fbdiv - 1);
- return status_success;
- }
- /**
- * @brief Set fbdiv for PLL fraction mode
- *
- * Fout = Fref/refdive * (fbdiv + frac/2^24)/postdiv1
- *
- * @param[in] ptr PLLCTL base address
- * @param[in] pll Target PLL index
- * @param[in] fbdiv Fbdiv value (0x1~0x1000)
- * @note fbdiv value can not set too large, if Fref/refdiv * fbdiv > 2GHz, it might cause irrecoverable damage to that PLL
- *
- * @return status_success if everything is okay
- */
- static inline hpm_stat_t pllctl_set_fbdiv_frac(PLLCTL_Type *ptr, uint8_t pll, uint16_t fbdiv)
- {
- if ((pll > (PLLCTL_SOC_PLL_MAX_COUNT - 1))
- || ((fbdiv - 1) > (PLLCTL_PLL_FREQ_FBDIV_FRAC_MASK >> PLLCTL_PLL_FREQ_FBDIV_FRAC_SHIFT))) {
- return status_invalid_argument;
- }
- ptr->PLL[pll].FREQ = (ptr->PLL[pll].FREQ & ~(PLLCTL_PLL_FREQ_FBDIV_FRAC_MASK))
- | PLLCTL_PLL_FREQ_FBDIV_FRAC_SET(fbdiv - 1);
- return status_success;
- }
- /**
- * @brief Set fraction for PLL fraction mode
- *
- * @param[in] ptr PLLCTL base address
- * @param[in] pll Target PLL index
- * @param[in] frac 24-bit fixed float point value
- *
- * @return
- */
- static inline hpm_stat_t pllctl_set_frac(PLLCTL_Type *ptr, uint8_t pll, uint32_t frac)
- {
- if ((pll > (PLLCTL_SOC_PLL_MAX_COUNT - 1))
- || (frac > (PLLCTL_PLL_FREQ_FRAC_MASK >> PLLCTL_PLL_FREQ_FRAC_SHIFT))) {
- return status_invalid_argument;
- }
- ptr->PLL[pll].FREQ = (ptr->PLL[pll].FREQ & ~(PLLCTL_PLL_FREQ_FRAC_MASK))
- | PLLCTL_PLL_FREQ_FRAC_SET(frac);
- return status_success;
- }
- /**
- * @brief Get PLL divx value
- *
- * @param[in] ptr PLLCTL base address
- * @param[in] pll Target PLL index
- * @param[in] div_index Target DIV to query
- *
- * @return Divider value of target DIV
- */
- static inline hpm_stat_t pllctl_get_div(PLLCTL_Type *ptr, uint8_t pll, uint8_t div_index)
- {
- if ((pll > (PLLCTL_SOC_PLL_MAX_COUNT - 1))
- || !(PLLCTL_SOC_PLL_HAS_DIV0(pll))) {
- return status_invalid_argument;
- }
- if (div_index) {
- return PLLCTL_PLL_DIV0_DIV_GET(ptr->PLL[pll].DIV1) + 1;
- } else {
- return PLLCTL_PLL_DIV0_DIV_GET(ptr->PLL[pll].DIV0) + 1;
- }
- }
- /**
- * @brief Set divider
- *
- * @param[in] ptr PLLCTL base address
- * @param[in] pll Target PLL index
- * @param[in] div_index DIV index
- * @param[in] div Divider value (starting from 1)
- *
- * @return status_success if everything is okay
- */
- static inline hpm_stat_t pllctl_set_div(PLLCTL_Type *ptr, uint8_t pll, uint8_t div_index, uint16_t div)
- {
- if ((pll > (PLLCTL_SOC_PLL_MAX_COUNT - 1))
- || !(PLLCTL_SOC_PLL_HAS_DIV0(pll))
- || ((div - 1) > (PLLCTL_PLL_DIV0_DIV_MASK >> PLLCTL_PLL_DIV0_DIV_SHIFT))) {
- return status_invalid_argument;
- }
- if (div_index) {
- ptr->PLL[pll].DIV1 = (ptr->PLL[pll].DIV1 & ~(PLLCTL_PLL_DIV1_DIV_MASK))
- | PLLCTL_PLL_DIV1_DIV_SET(div - 1);
- } else {
- ptr->PLL[pll].DIV0 = (ptr->PLL[pll].DIV0 & ~(PLLCTL_PLL_DIV0_DIV_MASK))
- | PLLCTL_PLL_DIV0_DIV_SET(div - 1);
- }
- return status_success;
- }
- /**
- * @brief Check if specific PLL DIV is stable
- *
- * @param[in] ptr PLLCTL base address
- * @param[in] pll Target PLL index
- * @param[in] div_index Target DIV to check
- *
- * @return true if target PLL DIV is stable
- */
- static inline bool pllctl_div_is_stable(PLLCTL_Type *ptr, uint8_t pll, uint8_t div_index)
- {
- if ((pll > (PLLCTL_SOC_PLL_MAX_COUNT - 1)) || !(PLLCTL_SOC_PLL_HAS_DIV0(pll))) {
- return false;
- }
- if (div_index) {
- return ptr->PLL[pll].DIV1 & PLLCTL_PLL_DIV0_RESPONSE_MASK;
- } else {
- return ptr->PLL[pll].DIV0 & PLLCTL_PLL_DIV0_RESPONSE_MASK;
- }
- }
- /**
- * @brief Check if target PLL is enabled
- *
- * @param[in] ptr PLLCTL base address
- * @param[in] pll Target PLL index
- *
- * @return true if target PLL is enabled
- */
- static inline bool pllctl_pll_is_enabled(PLLCTL_Type *ptr, uint8_t pll)
- {
- return (ptr->PLL[pll].STATUS & PLLCTL_PLL_STATUS_ENABLE_MASK);
- }
- /**
- * @brief Check if XTAL is stable
- *
- * @param[in] ptr PLLCTL base address
- *
- * @return true if XTAL is stable
- */
- static inline bool pllctl_xtal_is_stable(PLLCTL_Type *ptr)
- {
- return ptr->XTAL & PLLCTL_XTAL_RESPONSE_MASK;
- }
- /**
- * @brief Check if XTAL is enabled
- *
- * @param[in] ptr PLLCTL base address
- *
- * @return true if XTAL is enabled
- */
- static inline bool pllctl_xtal_is_enabled(PLLCTL_Type *ptr)
- {
- return ptr->XTAL & PLLCTL_XTAL_ENABLE_MASK;
- }
- /*
- * @brief set XTAL rampup time in cycles of IRC24M
- *
- * @param[in] ptr PLLCTL base address
- */
- static inline void pllctl_xtal_set_rampup_time(PLLCTL_Type *ptr, uint32_t cycles)
- {
- ptr->XTAL = (ptr->XTAL & ~PLLCTL_XTAL_RAMP_TIME_MASK) | PLLCTL_XTAL_RAMP_TIME_SET(cycles);
- }
- /**
- * @brief Set refdiv
- *
- * @param[in] ptr PLLCTL base address
- * @param[in] pll Target PLL index
- * @param[in] div Divider value (0x1-0x3F)
- *
- * @return status_success if everything is okay
- */
- hpm_stat_t pllctl_set_refdiv(PLLCTL_Type *ptr, uint8_t pll, uint8_t div);
- /**
- * @brief Initialize PLL working at integer mode with specific frequency
- *
- * @param[in] ptr PLLCTL base address
- * @param[in] pll Target PLL index
- * @param[in] freq_in_hz Target frequency, expected >= 375000000Hz
- *
- * @return status_success if everything is okay
- *
- * @note The actual frequency might be slightly different from freq_in_hz due to calculation.
- */
- hpm_stat_t pllctl_init_int_pll_with_freq(PLLCTL_Type *ptr, uint8_t pll,
- uint32_t freq_in_hz);
- /**
- * @brief Initialize PLL working at franction mode with specific frequency
- *
- * @param[in] ptr PLLCTL base address
- * @param[in] pll Target PLL index
- * @param[in] freq_in_hz Target frequency, expected >= 375000000Hz
- *
- * @return status_success if everything is okay
- * @note The actual frequency might be slightly different from freq_in_hz due to calculation.
- */
- hpm_stat_t pllctl_init_frac_pll_with_freq(PLLCTL_Type *ptr, uint8_t pll,
- uint32_t freq_in_hz);
- /**
- * @brief Get frequency of target PLL
- *
- * @param[in] ptr PLLCTL base address
- * @param[in] pll Target PLL index
- *
- * @return current frequency of target PLL in Hz
- */
- uint32_t pllctl_get_pll_freq_in_hz(PLLCTL_Type *ptr, uint8_t pll);
- #ifdef __cplusplus
- }
- #endif
- /**
- * @}
- */
- #endif /* HPM_PLLCTL_DRV_H */
|