Commit 7937c6c5 authored by Zdenko Pulitika's avatar Zdenko Pulitika Committed by Stephen Boyd

clk: pistachio: Fix PLL rate calculation in integer mode

.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: 43049b0c("CLK: Pistachio: Add PLL driver")
Cc: <stable@vger.kernel.org> # 4.1
Reviewed-by: default avatarAndrew Bresticker <abrestic@chromium.org>
Signed-off-by: default avatarZdenko Pulitika <zdenko.pulitika@imgtec.com>
Signed-off-by: default avatarGovindraj Raja <govindraj.raja@imgtec.com>
Signed-off-by: default avatarStephen Boyd <sboyd@codeaurora.org>
parent e53f21c7
......@@ -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 *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)
......@@ -180,7 +209,11 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate,
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(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);
......@@ -247,8 +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;
/* 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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment