Commit b90f3ecc authored by Marcin Ziemianowicz's avatar Marcin Ziemianowicz Committed by Greg Kroah-Hartman

clk: at91: PLL recalc_rate() now using cached MUL and DIV values

commit a982e45d upstream.

When a USB device is connected to the USB host port on the SAM9N12 then
you get "-62" error which seems to indicate USB replies from the device
are timing out. Based on a logic sniffer, I saw the USB bus was running
at half speed.

The PLL code uses cached MUL and DIV values which get set in set_rate()
and applied in prepare(), but the recalc_rate() function instead
queries the hardware instead of using these cached values. Therefore,
if recalc_rate() is called between a set_rate() and prepare(), the
wrong frequency is calculated and later the USB clock divider for the
SAM9N12 SOC will be configured for an incorrect clock.

In my case, the PLL hardware was set to 96 Mhz before the OHCI
driver loads, and therefore the usb clock divider was being set
to /2 even though the OHCI driver set the PLL to 48 Mhz.

As an alternative explanation, I noticed this was fixed in the past by
87e2ed33 ("clk: at91: fix recalc_rate implementation of PLL
driver") but the bug was later re-introduced by 1bdf0232 ("clk:
at91: make use of syscon/regmap internally").

Fixes: 1bdf0232 ("clk: at91: make use of syscon/regmap internally)
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarMarcin Ziemianowicz <marcin@ziemianowicz.com>
Acked-by: default avatarBoris Brezillon <boris.brezillon@bootlin.com>
Signed-off-by: default avatarStephen Boyd <sboyd@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent a98f1946
...@@ -132,19 +132,8 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, ...@@ -132,19 +132,8 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate) unsigned long parent_rate)
{ {
struct clk_pll *pll = to_clk_pll(hw); struct clk_pll *pll = to_clk_pll(hw);
unsigned int pllr;
u16 mul;
u8 div;
regmap_read(pll->regmap, PLL_REG(pll->id), &pllr);
div = PLL_DIV(pllr);
mul = PLL_MUL(pllr, pll->layout);
if (!div || !mul)
return 0;
return (parent_rate / div) * (mul + 1); return (parent_rate / pll->div) * (pll->mul + 1);
} }
static long clk_pll_get_best_div_mul(struct clk_pll *pll, unsigned long rate, static long clk_pll_get_best_div_mul(struct clk_pll *pll, unsigned long 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