Files
6CAR/drivers/source/clk/sdrv_pll_hw_access.c
2026-04-18 09:16:58 +08:00

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);
}