diff options
Diffstat (limited to 'drivers/clk/pistachio/clk-pll.c')
-rw-r--r-- | drivers/clk/pistachio/clk-pll.c | 81 |
1 files changed, 61 insertions, 20 deletions
diff --git a/drivers/clk/pistachio/clk-pll.c b/drivers/clk/pistachio/clk-pll.c index e17dada0dd21..7e8daab9025b 100644 --- a/drivers/clk/pistachio/clk-pll.c +++ b/drivers/clk/pistachio/clk-pll.c @@ -65,6 +65,12 @@ #define MIN_OUTPUT_FRAC 12000000UL #define MAX_OUTPUT_FRAC 1600000000UL +/* Fractional PLL operating modes */ +enum pll_mode { + PLL_MODE_FRAC, + PLL_MODE_INT, +}; + struct pistachio_clk_pll { struct clk_hw hw; void __iomem *base; @@ -88,12 +94,10 @@ static inline void pll_lock(struct pistachio_clk_pll *pll) cpu_relax(); } -static inline u32 do_div_round_closest(u64 dividend, u32 divisor) +static inline u64 do_div_round_closest(u64 dividend, u64 divisor) { dividend += divisor / 2; - do_div(dividend, divisor); - - return dividend; + return div64_u64(dividend, divisor); } static inline struct pistachio_clk_pll *to_pistachio_pll(struct clk_hw *hw) @@ -101,6 +105,29 @@ static inline struct pistachio_clk_pll *to_pistachio_pll(struct clk_hw *hw) return container_of(hw, struct pistachio_clk_pll, hw); } +static inline enum pll_mode pll_frac_get_mode(struct clk_hw *hw) +{ + struct pistachio_clk_pll *pll = to_pistachio_pll(hw); + u32 val; + + val = pll_readl(pll, PLL_CTRL3) & PLL_FRAC_CTRL3_DSMPD; + return val ? PLL_MODE_INT : PLL_MODE_FRAC; +} + +static inline void pll_frac_set_mode(struct clk_hw *hw, enum pll_mode mode) +{ + struct pistachio_clk_pll *pll = to_pistachio_pll(hw); + u32 val; + + val = pll_readl(pll, PLL_CTRL3); + if (mode == PLL_MODE_INT) + val |= PLL_FRAC_CTRL3_DSMPD | PLL_FRAC_CTRL3_DACPD; + else + val &= ~(PLL_FRAC_CTRL3_DSMPD | PLL_FRAC_CTRL3_DACPD); + + pll_writel(pll, val, PLL_CTRL3); +} + static struct pistachio_pll_rate_table * pll_get_params(struct pistachio_clk_pll *pll, unsigned long fref, unsigned long fout) @@ -136,8 +163,7 @@ static int pll_gf40lp_frac_enable(struct clk_hw *hw) u32 val; val = pll_readl(pll, PLL_CTRL3); - val &= ~(PLL_FRAC_CTRL3_PD | PLL_FRAC_CTRL3_DACPD | - PLL_FRAC_CTRL3_DSMPD | PLL_FRAC_CTRL3_FOUTPOSTDIVPD | + val &= ~(PLL_FRAC_CTRL3_PD | PLL_FRAC_CTRL3_FOUTPOSTDIVPD | PLL_FRAC_CTRL3_FOUT4PHASEPD | PLL_FRAC_CTRL3_FOUTVCOPD); pll_writel(pll, val, PLL_CTRL3); @@ -173,8 +199,8 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate, struct pistachio_clk_pll *pll = to_pistachio_pll(hw); struct pistachio_pll_rate_table *params; int enabled = pll_gf40lp_frac_is_enabled(hw); - u32 val, vco, old_postdiv1, old_postdiv2; - const char *name = __clk_get_name(hw->clk); + u64 val, vco, old_postdiv1, old_postdiv2; + const char *name = clk_hw_get_name(hw); if (rate < MIN_OUTPUT_FRAC || rate > MAX_OUTPUT_FRAC) return -EINVAL; @@ -183,17 +209,21 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate, if (!params || !params->refdiv) return -EINVAL; - vco = params->fref * params->fbdiv / params->refdiv; + /* calculate vco */ + vco = params->fref; + vco *= (params->fbdiv << 24) + params->frac; + vco = div64_u64(vco, params->refdiv << 24); + if (vco < MIN_VCO_FRAC_FRAC || vco > MAX_VCO_FRAC_FRAC) - pr_warn("%s: VCO %u is out of range %lu..%lu\n", name, vco, + pr_warn("%s: VCO %llu is out of range %lu..%lu\n", name, vco, MIN_VCO_FRAC_FRAC, MAX_VCO_FRAC_FRAC); - val = params->fref / params->refdiv; + val = div64_u64(params->fref, params->refdiv); if (val < MIN_PFD) - pr_warn("%s: PFD %u is too low (min %lu)\n", + pr_warn("%s: PFD %llu is too low (min %lu)\n", name, val, MIN_PFD); if (val > vco / 16) - pr_warn("%s: PFD %u is too high (max %u)\n", + pr_warn("%s: PFD %llu is too high (max %llu)\n", name, val, vco / 16); val = pll_readl(pll, PLL_CTRL1); @@ -227,6 +257,12 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate, (params->postdiv2 << PLL_FRAC_CTRL2_POSTDIV2_SHIFT); pll_writel(pll, val, PLL_CTRL2); + /* set operating mode */ + if (params->frac) + pll_frac_set_mode(hw, PLL_MODE_FRAC); + else + pll_frac_set_mode(hw, PLL_MODE_INT); + if (enabled) pll_lock(pll); @@ -237,8 +273,7 @@ static unsigned long pll_gf40lp_frac_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct pistachio_clk_pll *pll = to_pistachio_pll(hw); - u32 val, prediv, fbdiv, frac, postdiv1, postdiv2; - u64 rate = parent_rate; + u64 val, prediv, fbdiv, frac, postdiv1, postdiv2, rate; val = pll_readl(pll, PLL_CTRL1); prediv = (val >> PLL_CTRL1_REFDIV_SHIFT) & PLL_CTRL1_REFDIV_MASK; @@ -251,7 +286,13 @@ static unsigned long pll_gf40lp_frac_recalc_rate(struct clk_hw *hw, PLL_FRAC_CTRL2_POSTDIV2_MASK; frac = (val >> PLL_FRAC_CTRL2_FRAC_SHIFT) & PLL_FRAC_CTRL2_FRAC_MASK; - rate *= (fbdiv << 24) + frac; + /* get operating mode (int/frac) and calculate rate accordingly */ + rate = parent_rate; + if (pll_frac_get_mode(hw) == PLL_MODE_FRAC) + rate *= (fbdiv << 24) + frac; + else + rate *= (fbdiv << 24); + rate = do_div_round_closest(rate, (prediv * postdiv1 * postdiv2) << 24); return rate; @@ -279,7 +320,7 @@ static int pll_gf40lp_laint_enable(struct clk_hw *hw) u32 val; val = pll_readl(pll, PLL_CTRL1); - val &= ~(PLL_INT_CTRL1_PD | PLL_INT_CTRL1_DSMPD | + val &= ~(PLL_INT_CTRL1_PD | PLL_INT_CTRL1_FOUTPOSTDIVPD | PLL_INT_CTRL1_FOUTVCOPD); pll_writel(pll, val, PLL_CTRL1); @@ -316,7 +357,7 @@ static int pll_gf40lp_laint_set_rate(struct clk_hw *hw, unsigned long rate, struct pistachio_pll_rate_table *params; int enabled = pll_gf40lp_laint_is_enabled(hw); u32 val, vco, old_postdiv1, old_postdiv2; - const char *name = __clk_get_name(hw->clk); + const char *name = clk_hw_get_name(hw); if (rate < MIN_OUTPUT_LA || rate > MAX_OUTPUT_LA) return -EINVAL; @@ -325,12 +366,12 @@ static int pll_gf40lp_laint_set_rate(struct clk_hw *hw, unsigned long rate, if (!params || !params->refdiv) return -EINVAL; - vco = params->fref * params->fbdiv / params->refdiv; + vco = div_u64(params->fref * params->fbdiv, params->refdiv); if (vco < MIN_VCO_LA || vco > MAX_VCO_LA) pr_warn("%s: VCO %u is out of range %lu..%lu\n", name, vco, MIN_VCO_LA, MAX_VCO_LA); - val = params->fref / params->refdiv; + val = div_u64(params->fref, params->refdiv); if (val < MIN_PFD) pr_warn("%s: PFD %u is too low (min %lu)\n", name, val, MIN_PFD); |