diff --git a/drivers/clk/xilinx/clk-xlnx-clock-wizard.c b/drivers/clk/xilinx/clk-xlnx-clock-wizard.c index 4a0136349f71..f5d14543167a 100644 --- a/drivers/clk/xilinx/clk-xlnx-clock-wizard.c +++ b/drivers/clk/xilinx/clk-xlnx-clock-wizard.c @@ -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);