384 lines
8.5 KiB
C
384 lines
8.5 KiB
C
/**
|
|
* @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);
|
|
}
|