[PATCH v2 1/2] clk: meson: mpll: fix division by zero in rate_from_params
Neil Armstrong
narmstrong at baylibre.com
Fri Apr 7 11:39:18 EDT 2017
On 04/07/2017 05:34 PM, Jerome Brunet wrote:
> From: Martin Blumenstingl <martin.blumenstingl at googlemail.com>
>
> According to the public datasheet all register bits in HHI_MPLL_CNTL7,
> HHI_MPLL_CNTL8 and HHI_MPLL_CNTL9 default to zero. On all GX SoCs these
> seem to be initialized by the bootloader to some default value.
> However, on my Meson8 board they are not initialized, leading to a
> division by zero in rate_from_params as the math is:
> (parent_rate * SDM_DEN) / ((SDM_DEN * 0) + 0)
>
> According to the datasheet, the minimum n2 value is 4. The rate provided
> by the clock when n2 is less than this minimum is unpredictable. In such
> case, we report an error.
>
> Although the rate_from_params function was only introduced recently the
> original bug has been there for much longer. It was only exposed
> recently when the MPLL clocks were added to the Meson8b clock driver.
>
> Fixes: 1c50da4f27 ("clk: meson: add mpll support")
> Signed-off-by: Martin Blumenstingl <martin.blumenstingl at googlemail.com>
> Signed-off-by: Jerome Brunet <jbrunet at baylibre.com>
> ---
> drivers/clk/meson/clk-mpll.c | 26 +++++++++++++++-----------
> 1 file changed, 15 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/clk/meson/clk-mpll.c b/drivers/clk/meson/clk-mpll.c
> index 540dabe5adad..d9462b505dcc 100644
> --- a/drivers/clk/meson/clk-mpll.c
> +++ b/drivers/clk/meson/clk-mpll.c
> @@ -65,18 +65,21 @@
> #include "clkc.h"
>
> #define SDM_DEN 16384
> -#define SDM_MIN 1
> -#define SDM_MAX 16383
> #define N2_MIN 4
> #define N2_MAX 511
>
> #define to_meson_clk_mpll(_hw) container_of(_hw, struct meson_clk_mpll, hw)
>
> -static unsigned long rate_from_params(unsigned long parent_rate,
> +static long rate_from_params(unsigned long parent_rate,
> unsigned long sdm,
> unsigned long n2)
> {
> - return (parent_rate * SDM_DEN) / ((SDM_DEN * n2) + sdm);
> + unsigned long divisor = (SDM_DEN * n2) + sdm;
> +
> + if (n2 < N2_MIN)
> + return -EINVAL;
> +
> + return (parent_rate * SDM_DEN) / divisor;
> }
>
> static void params_from_rate(unsigned long requested_rate,
> @@ -89,17 +92,13 @@ static void params_from_rate(unsigned long requested_rate,
>
> if (div < N2_MIN) {
> *n2 = N2_MIN;
> - *sdm = SDM_MIN;
> + *sdm = 0;
> } else if (div > N2_MAX) {
> *n2 = N2_MAX;
> - *sdm = SDM_MAX;
> + *sdm = SDM_DEN - 1;
> } else {
> *n2 = div;
> *sdm = DIV_ROUND_UP(rem * SDM_DEN, requested_rate);
> - if (*sdm < SDM_MIN)
> - *sdm = SDM_MIN;
> - else if (*sdm > SDM_MAX)
> - *sdm = SDM_MAX;
> }
> }
>
> @@ -109,6 +108,7 @@ static unsigned long mpll_recalc_rate(struct clk_hw *hw,
> struct meson_clk_mpll *mpll = to_meson_clk_mpll(hw);
> struct parm *p;
> unsigned long reg, sdm, n2;
> + long rate;
>
> p = &mpll->sdm;
> reg = readl(mpll->base + p->reg_off);
> @@ -118,7 +118,11 @@ static unsigned long mpll_recalc_rate(struct clk_hw *hw,
> reg = readl(mpll->base + p->reg_off);
> n2 = PARM_GET(p->width, p->shift, reg);
>
> - return rate_from_params(parent_rate, sdm, n2);
> + rate = rate_from_params(parent_rate, sdm, n2);
> + if (rate < 0)
> + return 0;
> +
> + return rate;
> }
>
> static long mpll_round_rate(struct clk_hw *hw,
>
Reviewed-by: Neil Armstrong <narmstrong at baylibre.com>
More information about the linux-arm-kernel
mailing list