Commit 093affb0 authored by Zdenko Pulitika's avatar Zdenko Pulitika Committed by Stephen Boyd

clk: pistachio: Fix 32bit integer overflows

This commit fixes 32bit integer overflows throughout the pll driver
(i.e. wherever the result of integer multiplication may exceed the
range of u32).

One of the functions affected by this problem is .recalc_rate. It
returns incorrect rate for some pll settings (not for all though)
which in turn results in the incorrect rate setup of pll's child
clocks.

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 c5a132a8
...@@ -88,12 +88,10 @@ static inline void pll_lock(struct pistachio_clk_pll *pll) ...@@ -88,12 +88,10 @@ static inline void pll_lock(struct pistachio_clk_pll *pll)
cpu_relax(); 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; dividend += divisor / 2;
do_div(dividend, divisor); return div64_u64(dividend, divisor);
return dividend;
} }
static inline struct pistachio_clk_pll *to_pistachio_pll(struct clk_hw *hw) static inline struct pistachio_clk_pll *to_pistachio_pll(struct clk_hw *hw)
...@@ -173,7 +171,7 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate, ...@@ -173,7 +171,7 @@ 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_clk_pll *pll = to_pistachio_pll(hw);
struct pistachio_pll_rate_table *params; struct pistachio_pll_rate_table *params;
int enabled = pll_gf40lp_frac_is_enabled(hw); int enabled = pll_gf40lp_frac_is_enabled(hw);
u32 val, vco, old_postdiv1, old_postdiv2; u64 val, vco, old_postdiv1, old_postdiv2;
const char *name = clk_hw_get_name(hw); const char *name = clk_hw_get_name(hw);
if (rate < MIN_OUTPUT_FRAC || rate > MAX_OUTPUT_FRAC) if (rate < MIN_OUTPUT_FRAC || rate > MAX_OUTPUT_FRAC)
...@@ -183,17 +181,17 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate, ...@@ -183,17 +181,17 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate,
if (!params || !params->refdiv) if (!params || !params->refdiv)
return -EINVAL; return -EINVAL;
vco = params->fref * params->fbdiv / params->refdiv; vco = div64_u64(params->fref * params->fbdiv, params->refdiv);
if (vco < MIN_VCO_FRAC_FRAC || vco > MAX_VCO_FRAC_FRAC) 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); MIN_VCO_FRAC_FRAC, MAX_VCO_FRAC_FRAC);
val = params->fref / params->refdiv; val = div64_u64(params->fref, params->refdiv);
if (val < MIN_PFD) 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); name, val, MIN_PFD);
if (val > vco / 16) 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); name, val, vco / 16);
val = pll_readl(pll, PLL_CTRL1); val = pll_readl(pll, PLL_CTRL1);
...@@ -237,8 +235,7 @@ static unsigned long pll_gf40lp_frac_recalc_rate(struct clk_hw *hw, ...@@ -237,8 +235,7 @@ static unsigned long pll_gf40lp_frac_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate) unsigned long parent_rate)
{ {
struct pistachio_clk_pll *pll = to_pistachio_pll(hw); struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
u32 val, prediv, fbdiv, frac, postdiv1, postdiv2; u64 val, prediv, fbdiv, frac, postdiv1, postdiv2, rate;
u64 rate = parent_rate;
val = pll_readl(pll, PLL_CTRL1); val = pll_readl(pll, PLL_CTRL1);
prediv = (val >> PLL_CTRL1_REFDIV_SHIFT) & PLL_CTRL1_REFDIV_MASK; prediv = (val >> PLL_CTRL1_REFDIV_SHIFT) & PLL_CTRL1_REFDIV_MASK;
...@@ -251,6 +248,7 @@ static unsigned long pll_gf40lp_frac_recalc_rate(struct clk_hw *hw, ...@@ -251,6 +248,7 @@ static unsigned long pll_gf40lp_frac_recalc_rate(struct clk_hw *hw,
PLL_FRAC_CTRL2_POSTDIV2_MASK; PLL_FRAC_CTRL2_POSTDIV2_MASK;
frac = (val >> PLL_FRAC_CTRL2_FRAC_SHIFT) & PLL_FRAC_CTRL2_FRAC_MASK; frac = (val >> PLL_FRAC_CTRL2_FRAC_SHIFT) & PLL_FRAC_CTRL2_FRAC_MASK;
rate = parent_rate;
rate *= (fbdiv << 24) + frac; rate *= (fbdiv << 24) + frac;
rate = do_div_round_closest(rate, (prediv * postdiv1 * postdiv2) << 24); rate = do_div_round_closest(rate, (prediv * postdiv1 * postdiv2) << 24);
...@@ -325,12 +323,12 @@ static int pll_gf40lp_laint_set_rate(struct clk_hw *hw, unsigned long rate, ...@@ -325,12 +323,12 @@ static int pll_gf40lp_laint_set_rate(struct clk_hw *hw, unsigned long rate,
if (!params || !params->refdiv) if (!params || !params->refdiv)
return -EINVAL; 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) if (vco < MIN_VCO_LA || vco > MAX_VCO_LA)
pr_warn("%s: VCO %u is out of range %lu..%lu\n", name, vco, pr_warn("%s: VCO %u is out of range %lu..%lu\n", name, vco,
MIN_VCO_LA, MAX_VCO_LA); MIN_VCO_LA, MAX_VCO_LA);
val = params->fref / params->refdiv; val = div_u64(params->fref, params->refdiv);
if (val < MIN_PFD) if (val < MIN_PFD)
pr_warn("%s: PFD %u is too low (min %lu)\n", pr_warn("%s: PFD %u is too low (min %lu)\n",
name, val, MIN_PFD); name, val, MIN_PFD);
......
...@@ -95,13 +95,13 @@ struct pistachio_fixed_factor { ...@@ -95,13 +95,13 @@ struct pistachio_fixed_factor {
} }
struct pistachio_pll_rate_table { struct pistachio_pll_rate_table {
unsigned long fref; unsigned long long fref;
unsigned long fout; unsigned long long fout;
unsigned int refdiv; unsigned long long refdiv;
unsigned int fbdiv; unsigned long long fbdiv;
unsigned int postdiv1; unsigned long long postdiv1;
unsigned int postdiv2; unsigned long long postdiv2;
unsigned int frac; unsigned long long frac;
}; };
enum pistachio_pll_type { enum pistachio_pll_type {
......
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