/** * @file sdrv_pll_hw_access.c * @brief taishan pll hw access source file. * * @copyright Copyright (c) 2021 Semidrive Semiconductor. * All rights reserved. */ #include "sdrv_pll_hw_access.h" #include "sdrv_ckgen_common.h" status_t sdrv_pll_vco_enable(uint32_t base, bool enable) { uint32_t reg = base + PLL_CTRL; uint32_t val; val = readl(reg); if (enable) { val |= (1u << PLL_CTRL_RESETN); val &= ~(1u << PLL_CTRL_PD); } else { val &= ~(1u << PLL_CTRL_RESETN); val |= (1u << PLL_CTRL_PD); } writel(val, reg); if (enable) { if (!sdrv_ckgen_wait(base + PLL_STATUS, PLL_STATUS_LOCK, 1u, PLL_WAIT_TIME)) { return SDRV_CKGEN_PLL_NOT_LOCK; } } return SDRV_STATUS_OK; } uint32_t sdrv_pll_get_vco_rate(uint32_t base, uint32_t src_rate) { uint32_t reg = base + PLL_CTRL; uint32_t f_vco; uint32_t val; bool is_int_mode; uint32_t post_div; uint32_t m_val; uint32_t n_val; uint32_t tmp_u32; uint64_t tmp_u64; val = readl(reg); if ((val >> PLL_CTRL_DSM_DISABLE) & 0x1u) { is_int_mode = true; } else { is_int_mode = false; } post_div = ((val >> PLL_CTRL_POSTDIV) & 0x1u) ? 2 : 1; val = readl(base + PLL_N_NUM); n_val = (val >> PLL_N_NUM_VAL_LSB) & PLL_N_NUM_VAL_MASK; tmp_u32 = src_rate / post_div; f_vco = tmp_u32 * n_val; if (!is_int_mode) { val = readl(base + PLL_M_NUM); m_val = (val >> PLL_M_NUM_VAL_LSB) & PLL_M_NUM_VAL_MASK; tmp_u64 = (uint64_t)tmp_u32 * (uint64_t)m_val; f_vco += (tmp_u64 >> 24u); } return f_vco; } status_t sdrv_pll_set_vco_rate(uint32_t base, uint32_t src_rate, uint32_t rate, sdrv_ckgen_pll_ssc_config_t *ssc_cfg) { uint32_t ctrl_val; uint32_t m_reg_val; uint32_t post_div; uint32_t m_val; uint32_t n_val; uint32_t frac_val; uint64_t tmp_u64; uint64_t vco_out; uint32_t p_list[3] = {2, 4, 2}; bool recfg = false; status_t ret = SDRV_CKGEN_PLL_RATE_WRONG; bool dsm_en = false; ctrl_val = readl(base + PLL_CTRL); if (!BIT(ctrl_val, PLL_CTRL_PD) && BIT(ctrl_val, PLL_CTRL_RESETN)) { RMWREG32(base + PLL_CTRL, PLL_CTRL_FORCE_BYPASS, 1, 1); recfg = true; } sdrv_pll_vco_enable(base, false); if (ssc_cfg) { sdrv_pll_set_dsm_ssc_dep(base, ssc_cfg->ampl); sdrv_pll_set_dsm_ssc_mode(base, ssc_cfg->mode); sdrv_pll_set_dsm_ssc_freq(base, ssc_cfg->freq); dsm_en = (ssc_cfg->mode != CKGEN_NO_SSC); } for (uint32_t i = 0; i < 3; i++) { post_div = p_list[i]; vco_out = (uint64_t)rate * post_div; if (vco_out > PLL_VCO_FMAX) { continue; } if ((i != 2) && (vco_out < PLL_VCO_BAND)) { continue; } vco_out /= 2; n_val = vco_out / src_rate; frac_val = vco_out % src_rate; /* integer mode */ if (frac_val == 0u) { m_val = 0; ret = 0; break; } /* fractional mode */ tmp_u64 = (uint64_t)frac_val << 24u; tmp_u64 = tmp_u64 / (uint64_t)src_rate; if (tmp_u64 > PLL_M_NUM_VAL_MASK) { continue; } m_val = (uint32_t)tmp_u64; ret = 0; break; } if (ret == 0) { RMWREG32(base + PLL_N_NUM, PLL_N_NUM_VAL_LSB, 7u, n_val); m_reg_val = readl(base + PLL_M_NUM); m_reg_val &= ~(PLL_M_NUM_VAL_MASK + (1 << PLL_M_NUM_SW_READY)); m_reg_val |= (m_val + (1 << PLL_M_NUM_SW_READY)); writel(m_reg_val, base + PLL_M_NUM); ctrl_val = readl(base + PLL_CTRL); if ((m_val != 0) || dsm_en) { ctrl_val &= ~(1u << PLL_CTRL_DSM_DISABLE); } else { ctrl_val |= (1u << PLL_CTRL_DSM_DISABLE); } ctrl_val &= ~(1u << PLL_CTRL_LOCK_MODE); if (post_div == 2) { ctrl_val &= ~(1u << PLL_CTRL_POSTDIV); } else { ctrl_val |= (1u << PLL_CTRL_POSTDIV); } ctrl_val &= ~(7u << PLL_CTRL_VCO_BAND_LSB); if ((vco_out * 2) >= PLL_VCO_BAND) { ctrl_val |= (7u << PLL_CTRL_VCO_BAND_LSB); } else { ctrl_val |= (4u << PLL_CTRL_VCO_BAND_LSB); } writel(ctrl_val, base + PLL_CTRL); ret = sdrv_pll_vco_enable(base, true); if (recfg) { RMWREG32(base + PLL_CTRL, PLL_CTRL_FORCE_BYPASS, 1, 0); } } return ret; } status_t sdrv_pll_lvds_clk0_enable(uint32_t base, bool enable) { uint32_t reg; uint32_t val; reg = base + PLL_LVDS_CTL; val = readl(reg); if (enable) { val |= (1u << PLL_LVDS_CTL_GATING_EN); } else { val &= ~(1u << PLL_LVDS_CTL_GATING_EN); } writel(val, reg); return SDRV_STATUS_OK; } status_t sdrv_pll_lvds_clk1_enable(uint32_t base, bool enable) { uint32_t reg; uint32_t val; reg = base + PLL_LVDS_CTL; val = readl(reg); if (enable) { val |= (1u << PLL_LVDS_CTL_DIV2_GATING_EN); } else { val &= ~(1u << PLL_LVDS_CTL_DIV2_GATING_EN); } writel(val, reg); return SDRV_STATUS_OK; } status_t sdrv_pll_lvds_clk2_enable(uint32_t base, bool enable) { uint32_t reg; uint32_t val; reg = base + PLL_LVDS_CTL; val = readl(reg); if (enable) { val |= (1u << PLL_LVDS_CTL_DIV7_GATING_EN); } else { val &= ~(1u << PLL_LVDS_CTL_DIV7_GATING_EN); } writel(val, reg); return SDRV_STATUS_OK; } status_t sdrv_pll_lvds_ckgen_enable(uint32_t base, bool enable) { uint32_t reg; uint32_t val; reg = base + PLL_LVDS_CTL; val = readl(reg); if (enable) { val |= (1u << PLL_LVDS_CTL_CKGEN_GATING_EN); } else { val &= ~(1u << PLL_LVDS_CTL_CKGEN_GATING_EN); } writel(val, reg); return SDRV_STATUS_OK; } status_t sdrv_pll_set_lvds_clk1_div(uint32_t base, uint8_t div) { uint32_t reg; int ret; reg = base + PLL_LVDS_CTL; RMWREG32(reg, PLL_LVDS_CTL_DIV2_NUM_LSB, 5u, div - 1); ret = sdrv_ckgen_wait(reg, PLL_LVDS_CTL_DIV2_CHG_BUSY, 0u, PLL_WAIT_TIME); if (ret) { return sdrv_pll_lvds_clk1_enable(base, true); } else { return SDRV_CKGEN_PLL_LVDS_DIV2_CHG_BUSY; } } status_t sdrv_pll_set_lvds_clk2_div(uint32_t base, uint8_t div) { uint32_t reg; int ret; reg = base + PLL_LVDS_CTL; RMWREG32(reg, PLL_LVDS_CTL_DIV7_NUM_LSB, 5u, div - 1); ret = sdrv_ckgen_wait(reg, PLL_LVDS_CTL_DIV7_CHG_BUSY, 0u, PLL_WAIT_TIME); if (ret) { return sdrv_pll_lvds_clk2_enable(base, true); } else { return SDRV_CKGEN_PLL_LVDS_DIV7_CHG_BUSY; } } status_t sdrv_pll_set_lvds_ckgen_div(uint32_t base, uint8_t div) { uint32_t reg; int ret; reg = base + PLL_LVDS_CTL; RMWREG32(reg, PLL_LVDS_CTL_CKGEN_DIV_NUM_LSB, 4u, div - 1); ret = sdrv_ckgen_wait(reg, PLL_LVDS_CTL_CKGEN_CHG_BUSY, 0u, PLL_WAIT_TIME); if (ret) { return sdrv_pll_lvds_ckgen_enable(base, true); } else { return SDRV_CKGEN_PLL_LVDS_CKGEN_CHG_BUSY; } } uint8_t sdrv_pll_get_lvds_clk1_div(uint32_t base) { uint32_t reg; uint8_t div; reg = base + PLL_LVDS_CTL; div = (readl(reg) >> PLL_LVDS_CTL_DIV2_NUM_LSB) & PLL_LVDS_CTL_DIV2_NUM_MASK; return div; } uint8_t sdrv_pll_get_lvds_clk2_div(uint32_t base) { uint32_t reg; uint8_t div; reg = base + PLL_LVDS_CTL; div = (readl(reg) >> PLL_LVDS_CTL_DIV7_NUM_LSB) & PLL_LVDS_CTL_DIV7_NUM_MASK; return div; } uint8_t sdrv_pll_get_lvds_ckgen_div(uint32_t base) { uint32_t reg; uint8_t div; reg = base + PLL_LVDS_CTL; div = (readl(reg) >> PLL_LVDS_CTL_CKGEN_DIV_NUM_LSB) & PLL_LVDS_CTL_CKGEN_DIV_NUM_MASK; return div; } bool sdrv_pll_get_lock_status(uint32_t base) { return BIT(readl(base + PLL_STATUS), PLL_STATUS_LOCK); } void sdrv_pll_set_dsm_ssc_dep(uint32_t base, uint32_t ssc_dep) { uint32_t reg; reg = base + PLL_DSM; RMWREG32(reg, PLL_DSM_SSC_DEP_LSB, 5u, ssc_dep & PLL_DSM_SSC_DEP_MASK); } void sdrv_pll_set_dsm_ssc_freq(uint32_t base, uint32_t ssc_freq) { uint32_t reg; reg = base + PLL_DSM; RMWREG32(reg, PLL_DSM_SSC_FREQ_LSB, 2u, ssc_freq & PLL_DSM_SSC_FREQ_MASK); } void sdrv_pll_set_dsm_ssc_mode(uint32_t base, uint32_t mode) { uint32_t reg; reg = base + PLL_DSM; RMWREG32(reg, PLL_DSM_SSC_MODE_LSB, 2u, mode & PLL_DSM_SSC_MODE_MASK); }