clk: clocking-wizard: Program PLL CP/RES and lock parameters on reconfig

When dynamically reconfiguring the PLL multiplier, the charge pump (CP),
loop filter resistance (RES), and lock timing parameters must be updated
to match the new multiplier value. Without this, the PLL may fail to lock
or exhibit jitter at certain multiply factors.

Add lookup tables for CP/RES and lock timing values indexed by multiplier
range, and program registers for chargepump and lock delay during dynamic
reconfiguration.

Signed-off-by: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
Signed-off-by: Brian Masney <bmasney@redhat.com>
This commit is contained in:
Shubhrajyoti Datta 2026-06-29 11:21:46 +05:30 committed by Brian Masney
parent 46e31d69a5
commit d85028afef

View File

@ -35,6 +35,10 @@
#define WZRD_DIVCLK 21
#define WZRD_CLKFBOUT_4 51
#define WZRD_CLKFBOUT_3 48
#define WZRD_CP 18
#define WZRD_LOCK 27
#define WZRD_LOCK_REF_DLY 28
#define WZRD_RES 30
#define WZRD_DUTY_CYCLE 2
#define WZRD_O_DIV 4
@ -49,6 +53,10 @@
#define WZRD_P5FEDGE_SHIFT 15
#define WZRD_CLKOUT0_PREDIV2 BIT(11)
#define WZRD_EDGE_SHIFT 8
#define WZRD_CP_MASK GENMASK(3, 0)
#define WZRD_RES_MASK GENMASK(4, 1)
#define WZRD_LOCK_FB_DLY_MASK GENMASK(14, 10)
#define WZRD_LOCK_REF_DLY_LOCK_REF_DLY_MASK GENMASK(14, 10)
#define WZRD_CLKFBOUT_MULT_SHIFT 8
#define WZRD_CLKFBOUT_MULT_MASK (0xff << WZRD_CLKFBOUT_MULT_SHIFT)
@ -440,6 +448,130 @@ static int clk_wzrd_reconfig(struct clk_wzrd_divider *divider, void __iomem *div
WZRD_USEC_POLL, WZRD_TIMEOUT_POLL);
}
struct wzrd_pll_filter {
u32 m_min;
u32 m_max;
u32 cp;
u32 res;
};
static const struct wzrd_pll_filter wzrd_cp_res_table[] = {
{ 4, 4, 5, 15 },
{ 5, 5, 6, 15 },
{ 6, 6, 7, 15 },
{ 7, 7, 13, 15 },
{ 8, 8, 14, 15 },
{ 9, 9, 15, 15 },
{ 10, 10, 14, 7 },
{ 11, 11, 15, 7 },
{ 12, 13, 15, 11 },
{ 14, 14, 15, 13 },
{ 15, 15, 15, 3 },
{ 16, 17, 14, 5 },
{ 18, 19, 15, 5 },
{ 20, 21, 15, 9 },
{ 22, 23, 14, 14 },
{ 24, 26, 15, 14 },
{ 27, 28, 14, 1 },
{ 29, 33, 15, 1 },
{ 34, 37, 14, 6 },
{ 38, 44, 15, 6 },
{ 45, 57, 15, 10 },
{ 58, 63, 13, 12 },
{ 64, 70, 14, 12 },
{ 71, 86, 15, 12 },
{ 87, 94, 14, 2 },
{ 95, 145, 15, 2 },
{ 146, 163, 12, 4 },
{ 164, 181, 13, 4 },
{ 182, 200, 14, 4 },
{ 201, 273, 15, 4 },
{ 274, 300, 13, 8 },
{ 301, 325, 14, 8 },
{ 326, 432, 15, 8 },
};
struct wzrd_lock_timing {
u32 m_min;
u32 m_max;
u32 ref_dly;
u32 fb_dly;
u32 lock_cnt;
};
static const struct wzrd_lock_timing wzrd_lock_table[] = {
{ 4, 4, 4, 4, 1000 },
{ 5, 5, 6, 6, 1000 },
{ 6, 8, 7, 7, 1000 },
{ 9, 12, 8, 8, 1000 },
{ 13, 13, 10, 10, 1000 },
{ 14, 16, 13, 13, 1000 },
{ 17, 17, 16, 16, 825 },
{ 18, 18, 16, 16, 750 },
{ 19, 20, 16, 16, 700 },
{ 21, 21, 16, 16, 650 },
{ 22, 23, 16, 16, 625 },
{ 24, 24, 16, 16, 575 },
{ 25, 25, 16, 16, 550 },
{ 26, 28, 16, 16, 525 },
{ 29, 30, 16, 16, 475 },
{ 31, 31, 16, 16, 450 },
{ 32, 33, 16, 16, 425 },
{ 34, 36, 16, 16, 400 },
{ 37, 37, 16, 16, 375 },
{ 38, 40, 16, 16, 350 },
{ 41, 43, 16, 16, 325 },
{ 44, 47, 16, 16, 300 },
{ 48, 51, 16, 16, 275 },
{ 52, 205, 16, 16, 250 },
{ 206, 432, 16, 16, 225 },
};
static void clk_wzrd_update_cp_res_lock(struct clk_wzrd_divider *divider, u32 m)
{
u32 lock_ref_dly = 16, lock_fb_dly = 16, lock_cnt = 250, cp = 15, res = 15;
void __iomem *base = divider->base;
u32 reg;
int i;
for (i = 0; i < ARRAY_SIZE(wzrd_cp_res_table); i++) {
if (m >= wzrd_cp_res_table[i].m_min &&
m <= wzrd_cp_res_table[i].m_max) {
cp = wzrd_cp_res_table[i].cp;
res = wzrd_cp_res_table[i].res;
break;
}
}
for (i = 0; i < ARRAY_SIZE(wzrd_lock_table); i++) {
if (m >= wzrd_lock_table[i].m_min &&
m <= wzrd_lock_table[i].m_max) {
lock_ref_dly = wzrd_lock_table[i].ref_dly;
lock_fb_dly = wzrd_lock_table[i].fb_dly;
lock_cnt = wzrd_lock_table[i].lock_cnt;
break;
}
}
reg = readl(base + WZRD_CLK_CFG_REG(1, WZRD_CP));
reg &= ~WZRD_CP_MASK;
reg |= FIELD_PREP(WZRD_CP_MASK, cp);
writel(reg, base + WZRD_CLK_CFG_REG(1, WZRD_CP));
reg = readl(base + WZRD_CLK_CFG_REG(1, WZRD_RES));
reg &= ~WZRD_RES_MASK;
reg |= FIELD_PREP(WZRD_RES_MASK, res);
writel(reg, base + WZRD_CLK_CFG_REG(1, WZRD_RES));
reg = lock_cnt | FIELD_PREP(WZRD_LOCK_FB_DLY_MASK, lock_fb_dly);
writel(reg, base + WZRD_CLK_CFG_REG(1, WZRD_LOCK));
reg = readl(base + WZRD_CLK_CFG_REG(1, WZRD_LOCK_REF_DLY));
reg &= ~WZRD_LOCK_REF_DLY_LOCK_REF_DLY_MASK;
reg |= FIELD_PREP(WZRD_LOCK_REF_DLY_LOCK_REF_DLY_MASK, lock_ref_dly);
writel(reg, base + WZRD_CLK_CFG_REG(1, WZRD_LOCK_REF_DLY));
}
static int clk_wzrd_dynamic_ver_all_nolock(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
@ -471,6 +603,8 @@ static int clk_wzrd_dynamic_ver_all_nolock(struct clk_hw *hw, unsigned long rate
writel(regval1, divider->base + WZRD_CLK_CFG_REG(1,
WZRD_CLKFBOUT_2));
clk_wzrd_update_cp_res_lock(divider, m);
value2 = divider->d;
edged = value2 % WZRD_DUTY_CYCLE;
regh = (value2 / WZRD_DUTY_CYCLE);