Skip to content

Commit ed7815d

Browse files
marckleinebuddebroonie
authored andcommitted
spi: spi-sun6i: sun6i_spi_transfer_one(): fix setting of clock rate
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: 3558fe9 ("spi: sunxi: Add Allwinner A31 SPI controller driver") Signed-off-by: Marc Kleine-Budde <[email protected]> Acked-by: Maxime Ripard <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Mark Brown <[email protected]>
1 parent 44b37eb commit ed7815d

File tree

1 file changed

+6
-8
lines changed

1 file changed

+6
-8
lines changed

drivers/spi/spi-sun6i.c

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
198198
struct spi_transfer *tfr)
199199
{
200200
struct sun6i_spi *sspi = spi_master_get_devdata(master);
201-
unsigned int mclk_rate, div, timeout;
201+
unsigned int mclk_rate, div, div_cdr1, div_cdr2, timeout;
202202
unsigned int start, end, tx_time;
203203
unsigned int trig_level;
204204
unsigned int tx_len = 0;
@@ -287,14 +287,12 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
287287
* First try CDR2, and if we can't reach the expected
288288
* frequency, fall back to CDR1.
289289
*/
290-
div = mclk_rate / (2 * tfr->speed_hz);
291-
if (div <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) {
292-
if (div > 0)
293-
div--;
294-
295-
reg = SUN6I_CLK_CTL_CDR2(div) | SUN6I_CLK_CTL_DRS;
290+
div_cdr1 = DIV_ROUND_UP(mclk_rate, tfr->speed_hz);
291+
div_cdr2 = DIV_ROUND_UP(div_cdr1, 2);
292+
if (div_cdr2 <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) {
293+
reg = SUN6I_CLK_CTL_CDR2(div_cdr2 - 1) | SUN6I_CLK_CTL_DRS;
296294
} else {
297-
div = ilog2(mclk_rate) - ilog2(tfr->speed_hz);
295+
div = min(SUN6I_CLK_CTL_CDR1_MASK, order_base_2(div_cdr1));
298296
reg = SUN6I_CLK_CTL_CDR1(div);
299297
}
300298

0 commit comments

Comments
 (0)