Files
luban-lite/bsp/artinchip/hal/cmu/aic_hal_pll_clk.c
2024-12-02 11:10:03 +08:00

349 lines
10 KiB
C

/*
* Copyright (c) 2022-2024, ArtInChip Technology Co., Ltd
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <aic_core.h>
#include "aic_hal_clk.h"
#define to_clk_pll(_hw) container_of(_hw, struct aic_clk_pll_cfg, comm)
/* ALL chips:
* Other vco of clock not change
* The vco of pll_fra2 range from (768M-1560M) to (360M~1584M)
*/
static const struct aic_pll_vco vco_arr[] = {
{360000000, 1584000000, "pll_fra2"},
{768000000, 1560000000, "other"},
};
static int clk_pll_wait_lock(void)
{
aic_udelay(200);
return 0;
}
static void clk_vco_select(struct aic_clk_pll_cfg *pll,
unsigned long *min, unsigned long *max)
{
const struct aic_pll_vco *vco;
for (int i = 0; i < (ARRAY_SIZE(vco_arr) - 1); i++) {
vco = &vco_arr[i];
if (pll->id == hal_clk_get_id(vco->name)) {
*min = vco->vco_min;
*max = vco->vco_max;
return;
}
}
*max = vco_arr[ARRAY_SIZE(vco_arr) - 1].vco_max;
*min = vco_arr[ARRAY_SIZE(vco_arr) - 1].vco_min;
}
static inline void clk_pll_bypass(struct aic_clk_pll_cfg *pll, unsigned int bypass)
{
u32 val;
val = readl(cmu_reg(pll->offset_gen));
val &= ~(1 << PLL_OUT_MUX);
val |= (!bypass << PLL_OUT_MUX);
writel(val, cmu_reg(pll->offset_gen));
}
static int clk_pll_enable(struct aic_clk_comm_cfg *comm_cfg)
{
struct aic_clk_pll_cfg *pll = to_clk_pll(comm_cfg);
u32 val;
if (pll->flag & CLK_NO_CHANGE) {
return 0;
}
val = readl(cmu_reg(pll->offset_gen));
val |= (1 << PLL_OUT_SYS | 1 << PLL_EN_BIT);
writel(val, cmu_reg(pll->offset_gen));
return clk_pll_wait_lock();
}
static void clk_pll_disable(struct aic_clk_comm_cfg *comm_cfg)
{
struct aic_clk_pll_cfg *pll = to_clk_pll(comm_cfg);
u32 val;
if (!(pll->flag & CLK_NO_CHANGE)) {
val = readl(cmu_reg(pll->offset_gen));
val &= ~(1 << PLL_OUT_SYS | 1 << PLL_EN_BIT);
writel(val, cmu_reg(pll->offset_gen));
}
}
static int clk_pll_is_enable(struct aic_clk_comm_cfg *comm_cfg)
{
struct aic_clk_pll_cfg *pll = to_clk_pll(comm_cfg);
u32 reg_val = readl(cmu_reg(pll->offset_gen));
if ((reg_val & (1 << PLL_EN_BIT)) && (reg_val & (1 << PLL_OUT_SYS)))
return 1;
return 0;
}
static void clk_pll_enable_sdm(struct aic_clk_pll_cfg *pll, unsigned long parent_rate, u32 factor_n)
{
u32 reg_val, ppm_max, sdm_amp;
u64 sdm_step;
ppm_max = 1000000 / (factor_n + 1);
/* 1% spread */
if (ppm_max < PLL_SDM_SPREAD_PPM)
sdm_amp = 0;
else
sdm_amp = PLL_SDM_AMP_MAX -
PLL_SDM_SPREAD_PPM * PLL_SDM_AMP_MAX / ppm_max;
/* SDM uses triangular wave, 33KHz by default */
sdm_step = (u64)(PLL_SDM_AMP_MAX - sdm_amp) * 2 * PLL_SDM_SPREAD_FREQ;
do_div(sdm_step, parent_rate);
if (sdm_step > 511)
sdm_step = 511;
reg_val = (1UL << PLL_SDM_EN_BIT) |
(2 << PLL_SDM_MODE_BIT) |
(sdm_step << PLL_SDM_STEP_BIT) |
(3 << PLL_SDM_FREQ_BIT) |
(sdm_amp << PLL_SDM_AMP_BIT);
writel(reg_val, cmu_reg(pll->offset_sdm));
}
static unsigned long clk_pll_recalc_rate(struct aic_clk_comm_cfg *comm_cfg,
unsigned long parent_rate)
{
struct aic_clk_pll_cfg *pll = to_clk_pll(comm_cfg);
u32 factor_n, factor_m, factor_p, fra_in;
u64 rate, rate_int, rate_fra;
u8 fra_en = 0;
u32 reg_val, sdm_en, sdm_bot;
/* PLL output mux is CLK_24M */
if (!((readl(cmu_reg(pll->offset_gen)) >> PLL_OUT_MUX) & 0x1))
return CLOCK_24M;
factor_n = (readl(cmu_reg(pll->offset_gen)) >> PLL_FACTORN_BIT) &
PLL_FACTORN_MASK;
factor_m = (readl(cmu_reg(pll->offset_gen)) >> PLL_FACTORM_BIT) &
PLL_FACTORM_MASK;
factor_p = (readl(cmu_reg(pll->offset_gen)) >> PLL_FACTORP_BIT) &
PLL_FACTORP_MASK;
if (pll->type == AIC_PLL_FRA)
fra_en = (readl(cmu_reg(pll->offset_fra)) >> PLL_FRAC_EN_BIT) & 0x1;
if (pll->type == AIC_PLL_INT || !fra_en) {
rate = parent_rate / (factor_p + 1) * (factor_n + 1) / (factor_m + 1);
} else {
fra_in = readl(cmu_reg(pll->offset_fra)) & PLL_FRAC_DIV_MASK;
rate_int =
parent_rate / (factor_p + 1) * (factor_n + 1) / (factor_m + 1);
rate_fra = (u64)parent_rate / (factor_p + 1) * fra_in;
do_div(rate_fra, PLL_FRAC_DIV_MASK * (factor_m + 1));
rate = rate_int + rate_fra;
}
if (pll->type == AIC_PLL_SDM) {
reg_val = readl(cmu_reg(pll->offset_sdm));
sdm_en = reg_val >> 31;
sdm_bot = reg_val & 0xFFFF;
/*
* When the PLL is fractional PLL, SDM_EN will also be set to 1,
* so SDM_BOT is needed to help determine whether the spreading function
* has been enabled.
*/
if (!sdm_en || !sdm_bot)
clk_pll_enable_sdm(pll, parent_rate, factor_n);
}
#ifdef FPGA_BOARD_ARTINCHIP
rate = fpga_board_rate[pll->id];
#endif
return rate;
}
static long clk_pll_round_rate(struct aic_clk_comm_cfg *comm_cfg,
unsigned long rate, unsigned long *prate)
{
struct aic_clk_pll_cfg *pll = to_clk_pll(comm_cfg);
u32 factor_n, factor_m, factor_p;
long rrate, vco_rate;
unsigned long pll_vco_min, pll_vco_max;
unsigned long parent_rate = *prate;
if (pll->type == AIC_PLL_FRA)
return rate;
clk_vco_select(pll, &pll_vco_min, &pll_vco_max);
/* The frequency constraint of PLL_VCO is between 768M and 1560M
* But the PLL_VCO of pll_fra2 is between 360M and 1584M
*/
if (rate < pll_vco_min)
factor_m = DIV_ROUND_UP(pll_vco_min, rate) - 1;
else
factor_m = 0;
if (factor_m > PLL_FACTORM_MASK)
factor_m = PLL_FACTORM_MASK;
vco_rate = (factor_m + 1) * rate;
if (vco_rate > pll_vco_max)
vco_rate = pll_vco_max;
factor_p = (vco_rate % parent_rate) ? 1 : 0;
if (!factor_p)
return rate;
else if (!(vco_rate % (parent_rate / (factor_p + 1))))
return rate;
factor_n = vco_rate / parent_rate * (factor_p + 1) - 1;
rrate = parent_rate / (factor_p + 1) * (factor_n + 1) / (factor_m + 1);
#ifdef FPGA_BOARD_ARTINCHIP
rrate = fpga_board_rate[pll->id];
#endif
return rrate;
}
static int clk_pll_set_rate(struct aic_clk_comm_cfg *comm_cfg,
unsigned long rate, unsigned long parent_rate)
{
u32 factor_n, factor_m, factor_p, reg_val;
u64 val, fra_in = 0;
u8 fra_en, factor_m_en;
u32 sdm_en;
unsigned long vco_rate, pll_vco_min, pll_vco_max;
struct aic_clk_pll_cfg *pll = to_clk_pll(comm_cfg);
if (pll->flag & CLK_NO_CHANGE)
return 0;
clk_vco_select(pll, &pll_vco_min, &pll_vco_max);
if (rate == CLOCK_24M) {
val = readl(cmu_reg(pll->offset_gen));
val &= ~(1 << PLL_OUT_MUX);
writel(val, cmu_reg(pll->offset_gen));
return 0;
}
/* Switch the output of PLL to 24MHz */
clk_pll_bypass(pll, 1);
/* Calculate PLL parameters.
* The frequency constraint of PLL_VCO is between 768M and 1560M
* But the PLL_VCO of pll_fra2 is between 360M and 1584M
*/
if (rate < pll_vco_min)
factor_m = DIV_ROUND_UP(pll_vco_min, rate) - 1;
else
factor_m = 0;
/* Avoid factor_m == 2 */
if (factor_m == 2)
factor_m = 3;
if (factor_m > PLL_FACTORM_MASK)
factor_m = PLL_FACTORM_MASK;
if (factor_m)
factor_m_en = 1;
else
factor_m_en = 0;
vco_rate = (factor_m + 1) * rate;
if (vco_rate > pll_vco_max)
vco_rate = pll_vco_max;
factor_p = (vco_rate % parent_rate) ? 1 : 0;
factor_n = vco_rate * (factor_p + 1) / parent_rate - 1;
reg_val = readl(cmu_reg(pll->offset_gen));
reg_val &= ~0xFFFF;
reg_val |= (factor_m_en << PLL_FACTORM_EN_BIT) |
(factor_n << PLL_FACTORN_BIT) |
(factor_m << PLL_FACTORM_BIT) |
(factor_p << PLL_FACTORP_BIT);
/* If SDM enable, set PLL_ICP = 0 */
if (pll->type == AIC_PLL_SDM)
reg_val &= ~(0x1F << 24);
writel(reg_val, cmu_reg(pll->offset_gen));
if (pll->type == AIC_PLL_FRA) {
val = rate % (parent_rate * (factor_n + 1) /
(factor_m + 1) / (factor_p + 1));
fra_en = val ? 1 : 0;
if (fra_en) {
fra_in = val * (factor_p + 1) *
(factor_m + 1) * PLL_FRAC_DIV_MASK;
do_div(fra_in, parent_rate);
}
/* Configure fractional division */
writel(fra_en << PLL_FRAC_EN_BIT | fra_in, cmu_reg(pll->offset_fra));
/* when using decimal divsion, do not configure spreading parameters */
sdm_en = (1UL << PLL_SDM_EN_BIT) | (2UL << PLL_SDM_MODE_BIT);
writel(sdm_en, cmu_reg(pll->offset_sdm));
} else if (pll->type == AIC_PLL_SDM) {
clk_pll_enable_sdm(pll, parent_rate, factor_n);
}
reg_val = readl(cmu_reg(pll->offset_gen));
reg_val |= (1 << PLL_OUT_SYS | 1 << PLL_EN_BIT);
writel(reg_val, cmu_reg(pll->offset_gen));
if (!clk_pll_wait_lock()) {
clk_pll_bypass(pll, 0);
} else {
//pr_err("%d not lock\n", pll->id);
return -EAGAIN;
}
return 0;
}
static unsigned int clk_pll_get_parent(struct aic_clk_comm_cfg *comm_cfg)
{
struct aic_clk_pll_cfg *pll = to_clk_pll(comm_cfg);
return pll->parent_id;
}
const struct aic_clk_ops aic_clk_pll_ops = {
.enable = clk_pll_enable,
.disable = clk_pll_disable,
.is_enabled = clk_pll_is_enable,
.recalc_rate = clk_pll_recalc_rate,
.round_rate = clk_pll_round_rate,
.set_rate = clk_pll_set_rate,
.get_parent = clk_pll_get_parent,
};
void hal_clk_pll_lowpower(void)
{
#if defined(AIC_CMU_DRV_V10)
#if 0
*(volatile uint32_t *)(CMU_BASE+PLL_IN_REG) &= ~(0x7U << 29);
#endif
#elif defined(AIC_CMU_DRV_V11)
int xtal_en = readl(SID_BASE + 0x1C) & (0x1 << 1);
if (xtal_en)
*(volatile uint32_t *)(CMU_BASE+PLL_IN_REG) &= ~((0x7U << 29) | (0x1U << 1));
else
*(volatile uint32_t *)(CMU_BASE+PLL_IN_REG) &= ~((0x7U << 29) | (0x1U << 28));
#elif defined(AIC_CMU_DRV_V12)
*(volatile uint32_t *)(CMU_BASE+PLL_IN_REG) &= ~((0x7U << 29) | (0x1U << 1));
#endif
}