lkml.org 
[lkml]   [2020]   [Jul]   [20]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.7 097/244] spi: spi-sun6i: sun6i_spi_transfer_one(): fix setting of clock rate
    Date
    From: Marc Kleine-Budde <mkl@pengutronix.de>

    [ Upstream commit ed7815db70d17b1741883f2da8e1d80bc2efe517 ]

    A SPI transfer defines the _maximum_ speed of the SPI transfer. However the
    driver doesn't take into account that the clock divider is always rounded down
    (due to integer arithmetics). This results in a too high clock rate for the SPI
    transfer.

    E.g.: with a mclk_rate of 24 MHz and a SPI transfer speed of 10 MHz, the
    original code calculates a reg of "0", which results in a effective divider of
    "2" and a 12 MHz clock for the SPI transfer.

    This patch fixes the issue by using DIV_ROUND_UP() instead of a plain
    integer division.

    While there simplify the divider calculation for the CDR1 case, use
    order_base_2() instead of two ilog2() calculations.

    Fixes: 3558fe900e8a ("spi: sunxi: Add Allwinner A31 SPI controller driver")
    Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
    Acked-by: Maxime Ripard <mripard@kernel.org>
    Link: https://lore.kernel.org/r/20200706143443.9855-2-mkl@pengutronix.de
    Signed-off-by: Mark Brown <broonie@kernel.org>
    Signed-off-by: Sasha Levin <sashal@kernel.org>
    ---
    drivers/spi/spi-sun6i.c | 14 ++++++--------
    1 file changed, 6 insertions(+), 8 deletions(-)

    diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c
    index ec7967be9e2f5..956df79035d56 100644
    --- a/drivers/spi/spi-sun6i.c
    +++ b/drivers/spi/spi-sun6i.c
    @@ -198,7 +198,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
    struct spi_transfer *tfr)
    {
    struct sun6i_spi *sspi = spi_master_get_devdata(master);
    - unsigned int mclk_rate, div, timeout;
    + unsigned int mclk_rate, div, div_cdr1, div_cdr2, timeout;
    unsigned int start, end, tx_time;
    unsigned int trig_level;
    unsigned int tx_len = 0;
    @@ -287,14 +287,12 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
    * First try CDR2, and if we can't reach the expected
    * frequency, fall back to CDR1.
    */
    - div = mclk_rate / (2 * tfr->speed_hz);
    - if (div <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) {
    - if (div > 0)
    - div--;
    -
    - reg = SUN6I_CLK_CTL_CDR2(div) | SUN6I_CLK_CTL_DRS;
    + div_cdr1 = DIV_ROUND_UP(mclk_rate, tfr->speed_hz);
    + div_cdr2 = DIV_ROUND_UP(div_cdr1, 2);
    + if (div_cdr2 <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) {
    + reg = SUN6I_CLK_CTL_CDR2(div_cdr2 - 1) | SUN6I_CLK_CTL_DRS;
    } else {
    - div = ilog2(mclk_rate) - ilog2(tfr->speed_hz);
    + div = min(SUN6I_CLK_CTL_CDR1_MASK, order_base_2(div_cdr1));
    reg = SUN6I_CLK_CTL_CDR1(div);
    }

    --
    2.25.1


    \
     
     \ /
      Last update: 2020-07-20 18:22    [W:2.511 / U:0.252 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site