• Linus Walleij's avatar
    ARM: 8517/1: ICST: avoid arithmetic overflow in icst_hz() · 3f8a01e5
    Linus Walleij authored
    [ Upstream commit 5070fb14 ]
    
    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: stable@vger.kernel.org
    Cc: linux-clk@vger.kernel.org
    Cc: Pawel Moll <pawel.moll@arm.com>
    Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
    Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
    Signed-off-by: default avatarSasha Levin <sasha.levin@oracle.com>
    3f8a01e5
icst.c 2.44 KB