| From 7937c6c57e0da7bffa7b10bac23f230c77523e35 Mon Sep 17 00:00:00 2001 |
| From: Zdenko Pulitika <zdenko.pulitika@imgtec.com> |
| Date: Wed, 26 Aug 2015 17:11:39 +0100 |
| Subject: clk: pistachio: Fix PLL rate calculation in integer mode |
| |
| From: Zdenko Pulitika <zdenko.pulitika@imgtec.com> |
| |
| commit 7937c6c57e0da7bffa7b10bac23f230c77523e35 upstream. |
| |
| .recalc_rate callback for the fractional PLL doesn't take operating |
| mode into account when calculating PLL rate. This results in |
| the incorrect PLL rates when PLL is operating in integer mode. |
| |
| Operating mode of fractional PLL is based on the value of the |
| fractional divider. Currently it assumes that the PLL will always |
| be configured in fractional mode which may not be |
| the case. This may result in the wrong output frequency. |
| |
| Also vco was calculated based on the current operating mode which |
| makes no sense because .set_rate is setting operating mode. Instead, |
| vco should be calculated using PLL settings that are about to be set. |
| |
| Fixes: 43049b0c83f17("CLK: Pistachio: Add PLL driver") |
| Reviewed-by: Andrew Bresticker <abrestic@chromium.org> |
| Signed-off-by: Zdenko Pulitika <zdenko.pulitika@imgtec.com> |
| Signed-off-by: Govindraj Raja <govindraj.raja@imgtec.com> |
| Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/clk/pistachio/clk-pll.c | 48 ++++++++++++++++++++++++++++++++++++++-- |
| 1 file changed, 46 insertions(+), 2 deletions(-) |
| |
| --- 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; |
| @@ -99,6 +105,29 @@ static inline struct pistachio_clk_pll * |
| 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) |
| @@ -180,7 +209,11 @@ static int pll_gf40lp_frac_set_rate(stru |
| if (!params || !params->refdiv) |
| return -EINVAL; |
| |
| - vco = div64_u64(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 %llu is out of range %lu..%lu\n", name, vco, |
| MIN_VCO_FRAC_FRAC, MAX_VCO_FRAC_FRAC); |
| @@ -224,6 +257,12 @@ static int pll_gf40lp_frac_set_rate(stru |
| (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); |
| |
| @@ -247,8 +286,13 @@ static unsigned long pll_gf40lp_frac_rec |
| PLL_FRAC_CTRL2_POSTDIV2_MASK; |
| frac = (val >> PLL_FRAC_CTRL2_FRAC_SHIFT) & PLL_FRAC_CTRL2_FRAC_MASK; |
| |
| + /* get operating mode (int/frac) and calculate rate accordingly */ |
| rate = parent_rate; |
| - rate *= (fbdiv << 24) + frac; |
| + 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; |