mirror of https://gitee.com/openkylin/linux.git
clk: at91: usb: fix at91sam9x5 recalc, round and set rate
First check for rate == 0 in set_rate and round_rate to avoid div by zero. Then, in order to get the closest rate, round all divisions to the closest result instead of rounding them down. Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com> Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com> Signed-off-by: Michael Turquette <mturquette@linaro.org>
This commit is contained in:
parent
ff553ea1a3
commit
69daf75aaf
|
@ -52,29 +52,26 @@ static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk_hw *hw,
|
||||||
|
|
||||||
tmp = pmc_read(pmc, AT91_PMC_USB);
|
tmp = pmc_read(pmc, AT91_PMC_USB);
|
||||||
usbdiv = (tmp & AT91_PMC_OHCIUSBDIV) >> SAM9X5_USB_DIV_SHIFT;
|
usbdiv = (tmp & AT91_PMC_OHCIUSBDIV) >> SAM9X5_USB_DIV_SHIFT;
|
||||||
return parent_rate / (usbdiv + 1);
|
|
||||||
|
return DIV_ROUND_CLOSEST(parent_rate, (usbdiv + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
static long at91sam9x5_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
|
static long at91sam9x5_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||||
unsigned long *parent_rate)
|
unsigned long *parent_rate)
|
||||||
{
|
{
|
||||||
unsigned long div;
|
unsigned long div;
|
||||||
unsigned long bestrate;
|
|
||||||
unsigned long tmp;
|
if (!rate)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
if (rate >= *parent_rate)
|
if (rate >= *parent_rate)
|
||||||
return *parent_rate;
|
return *parent_rate;
|
||||||
|
|
||||||
div = *parent_rate / rate;
|
div = DIV_ROUND_CLOSEST(*parent_rate, rate);
|
||||||
if (div >= SAM9X5_USB_MAX_DIV)
|
if (div > SAM9X5_USB_MAX_DIV + 1)
|
||||||
return *parent_rate / (SAM9X5_USB_MAX_DIV + 1);
|
div = SAM9X5_USB_MAX_DIV + 1;
|
||||||
|
|
||||||
bestrate = *parent_rate / div;
|
return DIV_ROUND_CLOSEST(*parent_rate, div);
|
||||||
tmp = *parent_rate / (div + 1);
|
|
||||||
if (bestrate - rate > rate - tmp)
|
|
||||||
bestrate = tmp;
|
|
||||||
|
|
||||||
return bestrate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int at91sam9x5_clk_usb_set_parent(struct clk_hw *hw, u8 index)
|
static int at91sam9x5_clk_usb_set_parent(struct clk_hw *hw, u8 index)
|
||||||
|
@ -106,9 +103,13 @@ static int at91sam9x5_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||||
u32 tmp;
|
u32 tmp;
|
||||||
struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
|
struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
|
||||||
struct at91_pmc *pmc = usb->pmc;
|
struct at91_pmc *pmc = usb->pmc;
|
||||||
unsigned long div = parent_rate / rate;
|
unsigned long div;
|
||||||
|
|
||||||
if (parent_rate % rate || div < 1 || div >= SAM9X5_USB_MAX_DIV)
|
if (!rate)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
div = DIV_ROUND_CLOSEST(parent_rate, rate);
|
||||||
|
if (div > SAM9X5_USB_MAX_DIV + 1 || !div)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
tmp = pmc_read(pmc, AT91_PMC_USB) & ~AT91_PMC_OHCIUSBDIV;
|
tmp = pmc_read(pmc, AT91_PMC_USB) & ~AT91_PMC_OHCIUSBDIV;
|
||||||
|
|
Loading…
Reference in New Issue