linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
From: u.kleine-koenig@pengutronix.de (Uwe Kleine-König)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 1/3] clk: socfpga: Fix integer overflow in clock calculation
Date: Thu, 27 Feb 2014 10:58:49 +0100	[thread overview]
Message-ID: <20140227095849.GL6865@pengutronix.de> (raw)
In-Reply-To: <1392844273-11918-1-git-send-email-dinguyen@altera.com>

On Wed, Feb 19, 2014 at 03:11:10PM -0600, dinguyen at altera.com wrote:
> From: Dinh Nguyen <dinguyen@altera.com>
> 
> Use 64-bit integer for calculating clock rate. Also use do_div for the
> 64-bit division.
Some details would be interesting, depending on that some clever math
would be a cheaper alternative.
 
> Signed-off-by: Graham Moore <grmoore@altera.com>
> Signed-off-by: Dinh Nguyen <dinguyen@altera.com>
> Cc: Mike Turquette <mturquette@linaro.org>
> Cc: Steffen Trumtrar <s.trumtrar@pengutronix.de>
> ---
>  drivers/clk/socfpga/clk-pll.c |    8 +++++---
>  1 file changed, 5 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/clk/socfpga/clk-pll.c b/drivers/clk/socfpga/clk-pll.c
> index 362004e..834b6e9 100644
> --- a/drivers/clk/socfpga/clk-pll.c
> +++ b/drivers/clk/socfpga/clk-pll.c
> @@ -44,7 +44,8 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk,
>  					 unsigned long parent_rate)
>  {
>  	struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
> -	unsigned long divf, divq, vco_freq, reg;
> +	unsigned long divf, divq, reg;
> +	unsigned long long vco_freq;
>  	unsigned long bypass;
>  
>  	reg = readl(socfpgaclk->hw.reg);
> @@ -54,8 +55,9 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk,
>  
>  	divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT;
>  	divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT;
> -	vco_freq = parent_rate * (divf + 1);
> -	return vco_freq / (1 + divq);
> +	vco_freq = (unsigned long long)parent_rate * (divf + 1);
> +	do_div(vco_freq, (1 + divq));
> +	return (unsigned long)vco_freq;

You want

	f = some value in [1; 8192]
	q = some value in [1; 64]

	return parent_rate * f / q;

(with mathematic rounding?). Depending on the values not only
parent_rate * f doesn't fit into a 32 bit value, but also
parent_rate * f / q. This isn't catched.

Something like that might work, too:

	i = parent_rate // q
	j = parent_rate % q

	return i * f + j * f / q

j * f cannot overflow because they are in the range [0; 63] and
[1; 8192] respectively. And if i * f overflows your vco_freq above is to
big to fit into an unsigned long, too.

I didn't check with paper and pencil or some testing if the results
match, but I think they do. I'm not sure how the two approaches compare
performance-wise, you use a 64bit division, I'm using two 32 bit
divisions. But I wouldn't be surprised if my approach was faster.
(Also note that gcc -O3 fails to use only two divisions for my algorithm
but uses three instead. There might be a kernel helper that is able to
calculate i and j above with a single division though.)

Also note that the cast in the return statement isn't needed. (It might
be good to have it though to signal that you might loose some bits for
the human reader though.)

(Reference: This is what I looked at:
	$ cat multdiv.c 
	unsigned multdiv_ukl(unsigned parent_rate, unsigned f, unsigned q)
	{
		unsigned i, j;

		i = parent_rate / q;
		j = parent_rate % q;

		return i * f + j * f / q;
	}

	unsigned multdiv_dinguyen(unsigned parent_rate, unsigned f, unsigned q)
	{
		return (unsigned long long)parent_rate * f / q;
	}
	$ arm-gcc -S -o - -O3 multdiv_ukl.c
	[...]
)

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-K?nig            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

      parent reply	other threads:[~2014-02-27  9:58 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-02-19 21:11 [PATCH 1/3] clk: socfpga: Fix integer overflow in clock calculation dinguyen at altera.com
2014-02-19 21:11 ` [PATCH 2/3] clk: socfpga: Support multiple parents for the pll clocks dinguyen at altera.com
2014-02-19 21:11 ` [PATCH 3/3] dts: socfpga: Update clock entry to support multiple parents dinguyen at altera.com
2014-02-26  8:37 ` [PATCH 1/3] clk: socfpga: Fix integer overflow in clock calculation Mike Turquette
2014-02-26 15:33   ` Dinh Nguyen
2014-02-26 20:24     ` Mike Turquette
2014-02-27  9:58 ` Uwe Kleine-König [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20140227095849.GL6865@pengutronix.de \
    --to=u.kleine-koenig@pengutronix.de \
    --cc=linux-arm-kernel@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).