diff options
| author | Boris Brezillon <boris.brezillon@free-electrons.com> | 2014-11-17 08:16:56 -0500 |
|---|---|---|
| committer | Michael Turquette <mturquette@linaro.org> | 2014-11-17 13:38:40 -0500 |
| commit | 69daf75aafc8410ef046c70be65c71f2dd4e08f9 (patch) | |
| tree | be9397ee4a44717bcd8648816ddeff31284ef83c | |
| parent | ff553ea1a3d5b903becda790eeb6bd3debf239f9 (diff) | |
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>
| -rw-r--r-- | drivers/clk/at91/clk-usb.c | 29 |
1 files changed, 15 insertions, 14 deletions
diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c index 5b3b63c07524..a23ac0c724f0 100644 --- a/drivers/clk/at91/clk-usb.c +++ b/drivers/clk/at91/clk-usb.c | |||
| @@ -52,29 +52,26 @@ static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk_hw *hw, | |||
| 52 | 52 | ||
| 53 | tmp = pmc_read(pmc, AT91_PMC_USB); | 53 | tmp = pmc_read(pmc, AT91_PMC_USB); |
| 54 | usbdiv = (tmp & AT91_PMC_OHCIUSBDIV) >> SAM9X5_USB_DIV_SHIFT; | 54 | usbdiv = (tmp & AT91_PMC_OHCIUSBDIV) >> SAM9X5_USB_DIV_SHIFT; |
| 55 | return parent_rate / (usbdiv + 1); | 55 | |
| 56 | return DIV_ROUND_CLOSEST(parent_rate, (usbdiv + 1)); | ||
| 56 | } | 57 | } |
| 57 | 58 | ||
| 58 | static long at91sam9x5_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate, | 59 | static long at91sam9x5_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate, |
| 59 | unsigned long *parent_rate) | 60 | unsigned long *parent_rate) |
| 60 | { | 61 | { |
| 61 | unsigned long div; | 62 | unsigned long div; |
| 62 | unsigned long bestrate; | 63 | |
| 63 | unsigned long tmp; | 64 | if (!rate) |
| 65 | return -EINVAL; | ||
| 64 | 66 | ||
| 65 | if (rate >= *parent_rate) | 67 | if (rate >= *parent_rate) |
| 66 | return *parent_rate; | 68 | return *parent_rate; |
| 67 | 69 | ||
| 68 | div = *parent_rate / rate; | 70 | div = DIV_ROUND_CLOSEST(*parent_rate, rate); |
| 69 | if (div >= SAM9X5_USB_MAX_DIV) | 71 | if (div > SAM9X5_USB_MAX_DIV + 1) |
| 70 | return *parent_rate / (SAM9X5_USB_MAX_DIV + 1); | 72 | div = SAM9X5_USB_MAX_DIV + 1; |
| 71 | |||
| 72 | bestrate = *parent_rate / div; | ||
| 73 | tmp = *parent_rate / (div + 1); | ||
| 74 | if (bestrate - rate > rate - tmp) | ||
| 75 | bestrate = tmp; | ||
| 76 | 73 | ||
| 77 | return bestrate; | 74 | return DIV_ROUND_CLOSEST(*parent_rate, div); |
| 78 | } | 75 | } |
| 79 | 76 | ||
| 80 | static int at91sam9x5_clk_usb_set_parent(struct clk_hw *hw, u8 index) | 77 | 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, | |||
| 106 | u32 tmp; | 103 | u32 tmp; |
| 107 | struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); | 104 | struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); |
| 108 | struct at91_pmc *pmc = usb->pmc; | 105 | struct at91_pmc *pmc = usb->pmc; |
| 109 | unsigned long div = parent_rate / rate; | 106 | unsigned long div; |
| 110 | 107 | ||
| 111 | if (parent_rate % rate || div < 1 || div >= SAM9X5_USB_MAX_DIV) | 108 | if (!rate) |
| 109 | return -EINVAL; | ||
| 110 | |||
| 111 | div = DIV_ROUND_CLOSEST(parent_rate, rate); | ||
| 112 | if (div > SAM9X5_USB_MAX_DIV + 1 || !div) | ||
| 112 | return -EINVAL; | 113 | return -EINVAL; |
| 113 | 114 | ||
| 114 | tmp = pmc_read(pmc, AT91_PMC_USB) & ~AT91_PMC_OHCIUSBDIV; | 115 | tmp = pmc_read(pmc, AT91_PMC_USB) & ~AT91_PMC_OHCIUSBDIV; |
