diff options
| author | Tom Rini <[email protected]> | 2024-12-06 16:38:50 -0600 |
|---|---|---|
| committer | Tom Rini <[email protected]> | 2024-12-06 16:38:50 -0600 |
| commit | a03f0f9e6fe32b41ba17c5db5086422b88f31346 (patch) | |
| tree | ca22b33153b3b5b47622f13ed03d954268a109c5 /drivers | |
| parent | dca82739b9d767d4074e990b060f0da93ba36550 (diff) | |
| parent | 79d91e77f4c23c052f086e920311f5a3c703cfc0 (diff) | |
Merge patch series "PLL Sequencing update"
Manorit Chawdhry <[email protected]> says:
It has done a re-write of the full driver and the commits aren't split
to keep the bisectability intact.
Boot Logs: https://gist.github.com/manorit2001/1eaba109d722715a233244da693133d3
Link: https://lore.kernel.org/r/[email protected]
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/clk/ti/clk-k3-pll.c | 357 |
1 files changed, 276 insertions, 81 deletions
diff --git a/drivers/clk/ti/clk-k3-pll.c b/drivers/clk/ti/clk-k3-pll.c index b3a1b4cedb7..b775bd55faa 100644 --- a/drivers/clk/ti/clk-k3-pll.c +++ b/drivers/clk/ti/clk-k3-pll.c @@ -14,6 +14,7 @@ #include <linux/clk-provider.h> #include "k3-clk.h" #include <linux/rational.h> +#include <linux/delay.h> /* 16FFT register offsets */ #define PLL_16FFT_CFG 0x08 @@ -29,10 +30,12 @@ /* CAL STAT register bits */ #define PLL_16FFT_CAL_STAT_CAL_LOCK BIT(31) +#define PLL_16FFT_CAL_STAT_CAL_LOCK_TIMEOUT (4350U * 100U) /* CFG register bits */ #define PLL_16FFT_CFG_PLL_TYPE_SHIFT (0) #define PLL_16FFT_CFG_PLL_TYPE_MASK (0x3 << 0) +#define PLL_16FFT_CFG_PLL_TYPE_FRAC2 0 #define PLL_16FFT_CFG_PLL_TYPE_FRACF 1 /* CAL CTRL register bits */ @@ -41,14 +44,21 @@ #define PLL_16FFT_CAL_CTRL_CAL_BYP BIT(15) #define PLL_16FFT_CAL_CTRL_CAL_CNT_SHIFT 16 #define PLL_16FFT_CAL_CTRL_CAL_CNT_MASK (0x7 << 16) +#define PLL_16FFT_CAL_CTRL_CAL_IN_MASK (0xFFFU) /* CTRL register bits */ #define PLL_16FFT_CTRL_BYPASS_EN BIT(31) +#define PLL_16FFT_CTRL_BYP_ON_LOCKLOSS BIT(16) #define PLL_16FFT_CTRL_PLL_EN BIT(15) +#define PLL_16FFT_CTRL_INTL_BYP_EN BIT(8) +#define PLL_16FFT_CTRL_CLK_4PH_EN BIT(5) +#define PLL_16FFT_CTRL_CLK_POSTDIV_EN BIT(4) #define PLL_16FFT_CTRL_DSM_EN BIT(1) +#define PLL_16FFT_CTRL_DAC_EN BIT(0) /* STAT register bits */ #define PLL_16FFT_STAT_LOCK BIT(0) +#define PLL_16FFT_STAT_LOCK_TIMEOUT (150U * 100U) /* FREQ_CTRL0 bits */ #define PLL_16FFT_FREQ_CTRL0_FB_DIV_INT_MASK 0xfff @@ -62,7 +72,6 @@ /* FREQ_CTRL1 bits */ #define PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_BITS 24 #define PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_MASK 0xffffff -#define PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_SHIFT 0 /* KICK register magic values */ #define PLL_KICK0_VALUE 0x68ef3490 @@ -75,68 +84,199 @@ */ struct ti_pll_clk { struct clk clk; - void __iomem *reg; + void __iomem *base; }; #define to_clk_pll(_clk) container_of(_clk, struct ti_pll_clk, clk) -static int ti_pll_wait_for_lock(struct clk *clk) +static int ti_pll_clk_disable(struct clk *clk) { struct ti_pll_clk *pll = to_clk_pll(clk); + u32 ctrl; + + ctrl = readl(pll->base + PLL_16FFT_CTRL); + + if ((ctrl & PLL_16FFT_CTRL_PLL_EN)) { + ctrl &= ~PLL_16FFT_CTRL_PLL_EN; + writel(ctrl, pll->base + PLL_16FFT_CTRL); + + /* wait 1us */ + udelay(1); + } + + return 0; +} + +static int ti_pll_clk_enable(struct clk *clk) +{ + struct ti_pll_clk *pll = to_clk_pll(clk); + u32 ctrl; + + ctrl = readl(pll->base + PLL_16FFT_CTRL); + ctrl |= PLL_16FFT_CTRL_PLL_EN; + writel(ctrl, pll->base + PLL_16FFT_CTRL); + + /* Wait 1us */ + udelay(1); + + return 0; +} + +static bool clk_pll_16fft_check_lock(const struct ti_pll_clk *pll) +{ u32 stat; + + stat = readl(pll->base + PLL_16FFT_STAT); + return (stat & PLL_16FFT_STAT_LOCK); +} + +static bool clk_pll_16fft_check_cal_lock(const struct ti_pll_clk *pll) +{ + u32 stat; + + stat = readl(pll->base + PLL_16FFT_CAL_STAT); + return (stat & PLL_16FFT_CAL_STAT_CAL_LOCK); +} + +static void clk_pll_16fft_cal_int(const struct ti_pll_clk *pll) +{ + u32 cal; + + cal = readl(pll->base + PLL_16FFT_CAL_CTRL); + + /* Enable fast cal mode */ + cal |= PLL_16FFT_CAL_CTRL_FAST_CAL; + + /* Disable calibration bypass */ + cal &= ~PLL_16FFT_CAL_CTRL_CAL_BYP; + + /* Set CALCNT to 2 */ + cal &= ~PLL_16FFT_CAL_CTRL_CAL_CNT_MASK; + cal |= 2 << PLL_16FFT_CAL_CTRL_CAL_CNT_SHIFT; + + /* Set CAL_IN to 0 */ + cal &= ~PLL_16FFT_CAL_CTRL_CAL_IN_MASK; + + /* Note this register does not readback the written value. */ + writel(cal, pll->base + PLL_16FFT_CAL_CTRL); + + /* Wait 1us before enabling the CAL_EN field */ + udelay(1); + + cal = readl(pll->base + PLL_16FFT_CAL_CTRL); + + /* Enable calibration for FRACF */ + cal |= PLL_16FFT_CAL_CTRL_CAL_EN; + + /* Note this register does not readback the written value. */ + writel(cal, pll->base + PLL_16FFT_CAL_CTRL); +} + +static void clk_pll_16fft_disable_cal(const struct ti_pll_clk *pll) +{ + u32 cal, stat; + + cal = readl(pll->base + PLL_16FFT_CAL_CTRL); + cal &= ~PLL_16FFT_CAL_CTRL_CAL_EN; + /* Note this register does not readback the written value. */ + writel(cal, pll->base + PLL_16FFT_CAL_CTRL); + do { + stat = readl(pll->base + PLL_16FFT_CAL_STAT); + } while (stat & PLL_16FFT_CAL_STAT_CAL_LOCK); +} + +static int ti_pll_wait_for_lock(struct clk *clk) +{ + struct ti_pll_clk *pll = to_clk_pll(clk); u32 cfg; u32 cal; u32 freq_ctrl1; - int i; + unsigned int i; u32 pllfm; u32 pll_type; - int success; + u32 cal_en = 0; + bool success; - for (i = 0; i < 100000; i++) { - stat = readl(pll->reg + PLL_16FFT_STAT); - if (stat & PLL_16FFT_STAT_LOCK) { - success = 1; + /* + * Minimum VCO input freq is 5MHz, and the longest a lock should + * be consider to be timed out after 750 cycles. Be conservative + * and assume each loop takes 10 cycles and we run at a + * max of 1GHz. That gives 15000 loop cycles. We may end up waiting + * longer than necessary for timeout, but that should be ok. + */ + success = false; + for (i = 0; i < PLL_16FFT_STAT_LOCK_TIMEOUT; i++) { + if (clk_pll_16fft_check_lock(pll)) { + success = true; break; } } - /* Enable calibration if not in fractional mode of the FRACF PLL */ - freq_ctrl1 = readl(pll->reg + PLL_16FFT_FREQ_CTRL1); + /* Disable calibration in the fractional mode of the FRACF PLL based on data + * from silicon and simulation data. + */ + freq_ctrl1 = readl(pll->base + PLL_16FFT_FREQ_CTRL1); pllfm = freq_ctrl1 & PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_MASK; - pllfm >>= PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_SHIFT; - cfg = readl(pll->reg + PLL_16FFT_CFG); + + cfg = readl(pll->base + PLL_16FFT_CFG); pll_type = (cfg & PLL_16FFT_CFG_PLL_TYPE_MASK) >> PLL_16FFT_CFG_PLL_TYPE_SHIFT; - if (success && pll_type == PLL_16FFT_CFG_PLL_TYPE_FRACF && pllfm == 0) { - cal = readl(pll->reg + PLL_16FFT_CAL_CTRL); + if (success && pll_type == PLL_16FFT_CFG_PLL_TYPE_FRACF) { + cal = readl(pll->base + PLL_16FFT_CAL_CTRL); + cal_en = (cal & PLL_16FFT_CAL_CTRL_CAL_EN); + } - /* Enable calibration for FRACF */ - cal |= PLL_16FFT_CAL_CTRL_CAL_EN; + if (success && pll_type == PLL_16FFT_CFG_PLL_TYPE_FRACF && + pllfm == 0 && cal_en == 1) { + /* + * Wait for calibration lock. + * + * Lock should occur within: + * + * 170 * 2^(5+CALCNT) / PFD + * 21760 / PFD + * + * CALCNT = 2, PFD = 5-50MHz. This gives a range of 0.435mS to + * 4.35mS depending on PFD frequency. + * + * Be conservative and assume each loop takes 10 cycles and we run at a + * max of 1GHz. That gives 435000 loop cycles. We may end up waiting + * longer than necessary for timeout, but that should be ok. + * + * The recommend timeout for CALLOCK to go high is 4.35 ms + */ + success = false; + for (i = 0; i < PLL_16FFT_CAL_STAT_CAL_LOCK_TIMEOUT; i++) { + if (clk_pll_16fft_check_cal_lock(pll)) { + success = true; + break; + } + } - /* Enable fast cal mode */ - cal |= PLL_16FFT_CAL_CTRL_FAST_CAL; + /* In case of cal lock failure, operate without calibration */ + if (!success) { + debug("Failure for calibration, falling back without calibration\n"); - /* Disable calibration bypass */ - cal &= ~PLL_16FFT_CAL_CTRL_CAL_BYP; + /* Disable PLL */ + ti_pll_clk_disable(clk); - /* Set CALCNT to 2 */ - cal &= ~PLL_16FFT_CAL_CTRL_CAL_CNT_MASK; - cal |= 2 << PLL_16FFT_CAL_CTRL_CAL_CNT_SHIFT; + /* Disable Calibration */ + clk_pll_16fft_disable_cal(pll); - /* Note this register does not readback the written value. */ - writel(cal, pll->reg + PLL_16FFT_CAL_CTRL); + /* Enable PLL */ + ti_pll_clk_enable(clk); - success = 0; - for (i = 0; i < 100000; i++) { - stat = readl(pll->reg + PLL_16FFT_CAL_STAT); - if (stat & PLL_16FFT_CAL_STAT_CAL_LOCK) { - success = 1; - break; + /* Wait for PLL Lock */ + for (i = 0; i < PLL_16FFT_STAT_LOCK_TIMEOUT; i++) { + if (clk_pll_16fft_check_lock(pll)) { + success = true; + break; + } } } } - if (success == 0) { + if (!success) { printf("%s: pll (%s) failed to lock\n", __func__, clk->dev->name); return -EBUSY; @@ -156,14 +296,14 @@ static ulong ti_pll_clk_get_rate(struct clk *clk) u32 ctrl; /* Check if we are in bypass */ - ctrl = readl(pll->reg + PLL_16FFT_CTRL); + ctrl = readl(pll->base + PLL_16FFT_CTRL); if (ctrl & PLL_16FFT_CTRL_BYPASS_EN) return parent_freq; - pllm = readl(pll->reg + PLL_16FFT_FREQ_CTRL0); - pllfm = readl(pll->reg + PLL_16FFT_FREQ_CTRL1); + pllm = readl(pll->base + PLL_16FFT_FREQ_CTRL0); + pllfm = readl(pll->base + PLL_16FFT_FREQ_CTRL1); - plld = readl(pll->reg + PLL_16FFT_DIV_CTRL) & + plld = readl(pll->base + PLL_16FFT_DIV_CTRL) & PLL_16FFT_DIV_CTRL_REF_DIV_MASK; current_freq = parent_freq * pllm / plld; @@ -180,6 +320,30 @@ static ulong ti_pll_clk_get_rate(struct clk *clk) return current_freq; } +static bool ti_pll_clk_is_bypass(struct ti_pll_clk *pll) +{ + u32 ctrl; + bool ret; + + ctrl = readl(pll->base + PLL_16FFT_CTRL); + ret = (ctrl & PLL_16FFT_CTRL_BYPASS_EN) != 0; + + return ret; +} + +static void ti_pll_clk_bypass(struct ti_pll_clk *pll, bool bypass) +{ + u32 ctrl; + + ctrl = readl(pll->base + PLL_16FFT_CTRL); + if (bypass) + ctrl |= PLL_16FFT_CTRL_BYPASS_EN; + else + ctrl &= ~PLL_16FFT_CTRL_BYPASS_EN; + + writel(ctrl, pll->base + PLL_16FFT_CTRL); +} + static ulong ti_pll_clk_set_rate(struct clk *clk, ulong rate) { struct ti_pll_clk *pll = to_clk_pll(clk); @@ -187,9 +351,13 @@ static ulong ti_pll_clk_set_rate(struct clk *clk, ulong rate) u64 parent_freq = clk_get_parent_rate(clk); int ret; u32 ctrl; + u32 cfg; + u32 pll_type; unsigned long pllm; u32 pllfm = 0; unsigned long plld; + u32 freq_ctrl0; + u32 freq_ctrl1; u32 div_ctrl; u32 rem; int shift; @@ -212,16 +380,22 @@ static ulong ti_pll_clk_set_rate(struct clk *clk, ulong rate) break; } - /* Put PLL to bypass mode */ - ctrl = readl(pll->reg + PLL_16FFT_CTRL); - ctrl |= PLL_16FFT_CTRL_BYPASS_EN; - writel(ctrl, pll->reg + PLL_16FFT_CTRL); + if (!ti_pll_clk_is_bypass(pll)) { + /* Put the PLL into bypass */ + ti_pll_clk_bypass(pll, true); + } + + /* Disable the PLL */ + ti_pll_clk_disable(clk); if (rate == parent_freq) { debug("%s: put %s to bypass\n", __func__, clk->dev->name); return rate; } + cfg = readl(pll->base + PLL_16FFT_CFG); + pll_type = (cfg & PLL_16FFT_CFG_PLL_TYPE_MASK) >> PLL_16FFT_CFG_PLL_TYPE_SHIFT; + debug("%s: pre-frac-calc: rate=%u, parent_freq=%u, plld=%u, pllm=%u\n", __func__, (u32)rate, (u32)parent_freq, (u32)plld, (u32)pllm); @@ -237,31 +411,75 @@ static ulong ti_pll_clk_set_rate(struct clk *clk, ulong rate) plld = 1; } - if (pllfm) - ctrl |= PLL_16FFT_CTRL_DSM_EN; - else - ctrl &= ~PLL_16FFT_CTRL_DSM_EN; + /* Program the new rate */ + freq_ctrl0 = readl(pll->base + PLL_16FFT_FREQ_CTRL0); + freq_ctrl1 = readl(pll->base + PLL_16FFT_FREQ_CTRL1); + div_ctrl = readl(pll->base + PLL_16FFT_DIV_CTRL); + + freq_ctrl0 &= ~PLL_16FFT_FREQ_CTRL0_FB_DIV_INT_MASK; + freq_ctrl0 |= pllm; - writel(pllm, pll->reg + PLL_16FFT_FREQ_CTRL0); - writel(pllfm, pll->reg + PLL_16FFT_FREQ_CTRL1); + freq_ctrl1 &= ~PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_MASK; + freq_ctrl1 |= pllfm; /* * div_ctrl register contains other divider values, so rmw * only plld and leave existing values alone */ - div_ctrl = readl(pll->reg + PLL_16FFT_DIV_CTRL); div_ctrl &= ~PLL_16FFT_DIV_CTRL_REF_DIV_MASK; div_ctrl |= plld; - writel(div_ctrl, pll->reg + PLL_16FFT_DIV_CTRL); - ctrl &= ~PLL_16FFT_CTRL_BYPASS_EN; - ctrl |= PLL_16FFT_CTRL_PLL_EN; - writel(ctrl, pll->reg + PLL_16FFT_CTRL); + /* Make sure we have fractional support if required */ + ctrl = readl(pll->base + PLL_16FFT_CTRL); + + /* Don't use internal bypass,it is not glitch free. Always prefer glitchless bypass */ + ctrl &= ~(PLL_16FFT_CTRL_INTL_BYP_EN | PLL_16FFT_CTRL_CLK_4PH_EN); + + /* Always enable output if PLL, Always bypass if we lose lock */ + ctrl |= (PLL_16FFT_CTRL_CLK_POSTDIV_EN | PLL_16FFT_CTRL_BYP_ON_LOCKLOSS); + + /* Enable fractional support if required */ + if (pll_type == PLL_16FFT_CFG_PLL_TYPE_FRACF) { + if (pllfm != 0) + ctrl |= (PLL_16FFT_CTRL_DSM_EN | PLL_16FFT_CTRL_DAC_EN); + else + ctrl &= ~(PLL_16FFT_CTRL_DSM_EN | PLL_16FFT_CTRL_DAC_EN); + } + + /* Enable Fractional by default for PLL_16FFT_CFG_PLL_TYPE_FRAC2 */ + if (pll_type == PLL_16FFT_CFG_PLL_TYPE_FRAC2) + ctrl |= (PLL_16FFT_CTRL_DSM_EN | PLL_16FFT_CTRL_DAC_EN); + + writel(freq_ctrl0, pll->base + PLL_16FFT_FREQ_CTRL0); + writel(freq_ctrl1, pll->base + PLL_16FFT_FREQ_CTRL1); + writel(div_ctrl, pll->base + PLL_16FFT_DIV_CTRL); + writel(ctrl, pll->base + PLL_16FFT_CTRL); + + /* Configure PLL calibration*/ + if (pll_type == PLL_16FFT_CFG_PLL_TYPE_FRACF) { + if (pllfm != 0) { + /* Disable Calibration in Fractional mode */ + clk_pll_16fft_disable_cal(pll); + } else { + /* Enable Calibration in Integer mode */ + clk_pll_16fft_cal_int(pll); + } + } + + /* + * Wait at least 1 ref cycle before enabling PLL. + * Minimum VCO input frequency is 5MHz, therefore maximum + * wait time for 1 ref clock is 0.2us. + */ + udelay(1); + ti_pll_clk_enable(clk); ret = ti_pll_wait_for_lock(clk); if (ret) return ret; + ti_pll_clk_bypass(pll, false); + debug("%s: pllm=%u, plld=%u, pllfm=%u, parent_freq=%u\n", __func__, (u32)pllm, (u32)plld, (u32)pllfm, (u32)parent_freq); @@ -279,30 +497,7 @@ static ulong ti_pll_clk_set_rate(struct clk *clk, ulong rate) return current_freq; } -static int ti_pll_clk_enable(struct clk *clk) -{ - struct ti_pll_clk *pll = to_clk_pll(clk); - u32 ctrl; - - ctrl = readl(pll->reg + PLL_16FFT_CTRL); - ctrl &= ~PLL_16FFT_CTRL_BYPASS_EN; - ctrl |= PLL_16FFT_CTRL_PLL_EN; - writel(ctrl, pll->reg + PLL_16FFT_CTRL); - - return ti_pll_wait_for_lock(clk); -} - -static int ti_pll_clk_disable(struct clk *clk) -{ - struct ti_pll_clk *pll = to_clk_pll(clk); - u32 ctrl; - ctrl = readl(pll->reg + PLL_16FFT_CTRL); - ctrl |= PLL_16FFT_CTRL_BYPASS_EN; - writel(ctrl, pll->reg + PLL_16FFT_CTRL); - - return 0; -} static const struct clk_ops ti_pll_clk_ops = { .get_rate = ti_pll_clk_get_rate, @@ -323,7 +518,7 @@ struct clk *clk_register_ti_pll(const char *name, const char *parent_name, if (!pll) return ERR_PTR(-ENOMEM); - pll->reg = reg; + pll->base = reg; ret = clk_register(&pll->clk, "ti-pll-clk", name, parent_name); if (ret) { @@ -333,19 +528,19 @@ struct clk *clk_register_ti_pll(const char *name, const char *parent_name, } /* Unlock the PLL registers */ - writel(PLL_KICK0_VALUE, pll->reg + PLL_KICK0); - writel(PLL_KICK1_VALUE, pll->reg + PLL_KICK1); + writel(PLL_KICK0_VALUE, pll->base + PLL_KICK0); + writel(PLL_KICK1_VALUE, pll->base + PLL_KICK1); /* Enable all HSDIV outputs */ - cfg = readl(pll->reg + PLL_16FFT_CFG); + cfg = readl(pll->base + PLL_16FFT_CFG); for (i = 0; i < 16; i++) { hsdiv_presence_bit = BIT(16 + i); hsdiv_ctrl_offs = 0x80 + (i * 4); /* Enable HSDIV output if present */ if ((hsdiv_presence_bit & cfg) != 0UL) { - ctrl = readl(pll->reg + hsdiv_ctrl_offs); + ctrl = readl(pll->base + hsdiv_ctrl_offs); ctrl |= PLL_16FFT_HSDIV_CTRL_CLKOUT_EN; - writel(ctrl, pll->reg + hsdiv_ctrl_offs); + writel(ctrl, pll->base + hsdiv_ctrl_offs); } } |
