Skip to content

Commit 5070fb1

Browse files
Linus WalleijRussell King
authored andcommitted
ARM: 8517/1: ICST: avoid arithmetic overflow in icst_hz()
When trying to set the ICST 307 clock to 25174000 Hz I ran into this arithmetic error: the icst_hz_to_vco() correctly figure out DIVIDE=2, RDW=100 and VDW=99 yielding a frequency of 25174000 Hz out of the VCO. (I replicated the icst_hz() function in a spreadsheet to verify this.) However, when I called icst_hz() on these VCO settings it would instead return 4122709 Hz. This causes an error in the common clock driver for ICST as the common clock framework will call .round_rate() on the clock which will utilize icst_hz_to_vco() followed by icst_hz() suggesting the erroneous frequency, and then the clock gets set to this. The error did not manifest in the old clock framework since this high frequency was only used by the CLCD, which calls clk_set_rate() without first calling clk_round_rate() and since the old clock framework would not call clk_round_rate() before setting the frequency, the correct values propagated into the VCO. After some experimenting I figured out that it was due to a simple arithmetic overflow: the divisor for 24Mhz reference frequency as reference becomes 24000000*2*(99+8)=0x132212400 and the "1" in bit 32 overflows and is lost. But introducing an explicit 64-by-32 bit do_div() and casting the divisor into (u64) we get the right frequency back, and the right frequency gets set. Tested on the ARM Versatile. Cc: [email protected] Cc: [email protected] Cc: Pawel Moll <[email protected]> Signed-off-by: Linus Walleij <[email protected]> Signed-off-by: Russell King <[email protected]>
1 parent 03590cb commit 5070fb1

File tree

1 file changed

+6
-2
lines changed

1 file changed

+6
-2
lines changed

arch/arm/common/icst.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*/
1717
#include <linux/module.h>
1818
#include <linux/kernel.h>
19-
19+
#include <asm/div64.h>
2020
#include <asm/hardware/icst.h>
2121

2222
/*
@@ -29,7 +29,11 @@ EXPORT_SYMBOL(icst525_s2div);
2929

3030
unsigned long icst_hz(const struct icst_params *p, struct icst_vco vco)
3131
{
32-
return p->ref * 2 * (vco.v + 8) / ((vco.r + 2) * p->s2div[vco.s]);
32+
u64 dividend = p->ref * 2 * (u64)(vco.v + 8);
33+
u32 divisor = (vco.r + 2) * p->s2div[vco.s];
34+
35+
do_div(dividend, divisor);
36+
return (unsigned long)dividend;
3337
}
3438

3539
EXPORT_SYMBOL(icst_hz);

0 commit comments

Comments
 (0)