spi: mvebu: fix baudrate calculation for armada variant
commitsteinar/wifi_calib_4_9_kernel7243e0b207
upstream. The calculation of SPR and SPPR doesn't round correctly at several places which might result in baud rates that are too big. For example with tclk_hz = 250000001 and target rate 25000000 it determined a divider of 10 which is wrong. Instead of fixing all the corner cases replace the calculation by an algorithm without a loop which should even be quicker to execute apart from being correct. Fixes:df59fa7f4b
("spi: orion: support armada extended baud rates") Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Signed-off-by: Mark Brown <broonie@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
parent
05b7bdf1c3
commit
f52e670a5b
|
@ -138,37 +138,62 @@ static int orion_spi_baudrate_set(struct spi_device *spi, unsigned int speed)
|
|||
tclk_hz = clk_get_rate(orion_spi->clk);
|
||||
|
||||
if (devdata->typ == ARMADA_SPI) {
|
||||
unsigned int clk, spr, sppr, sppr2, err;
|
||||
unsigned int best_spr, best_sppr, best_err;
|
||||
/*
|
||||
* Given the core_clk (tclk_hz) and the target rate (speed) we
|
||||
* determine the best values for SPR (in [0 .. 15]) and SPPR (in
|
||||
* [0..7]) such that
|
||||
*
|
||||
* core_clk / (SPR * 2 ** SPPR)
|
||||
*
|
||||
* is as big as possible but not bigger than speed.
|
||||
*/
|
||||
|
||||
best_err = speed;
|
||||
best_spr = 0;
|
||||
best_sppr = 0;
|
||||
/* best integer divider: */
|
||||
unsigned divider = DIV_ROUND_UP(tclk_hz, speed);
|
||||
unsigned spr, sppr;
|
||||
|
||||
/* Iterate over the valid range looking for best fit */
|
||||
for (sppr = 0; sppr < 8; sppr++) {
|
||||
sppr2 = 0x1 << sppr;
|
||||
if (divider < 16) {
|
||||
/* This is the easy case, divider is less than 16 */
|
||||
spr = divider;
|
||||
sppr = 0;
|
||||
|
||||
spr = tclk_hz / sppr2;
|
||||
spr = DIV_ROUND_UP(spr, speed);
|
||||
if ((spr == 0) || (spr > 15))
|
||||
continue;
|
||||
} else {
|
||||
unsigned two_pow_sppr;
|
||||
/*
|
||||
* Find the highest bit set in divider. This and the
|
||||
* three next bits define SPR (apart from rounding).
|
||||
* SPPR is then the number of zero bits that must be
|
||||
* appended:
|
||||
*/
|
||||
sppr = fls(divider) - 4;
|
||||
|
||||
clk = tclk_hz / (spr * sppr2);
|
||||
err = speed - clk;
|
||||
/*
|
||||
* As SPR only has 4 bits, we have to round divider up
|
||||
* to the next multiple of 2 ** sppr.
|
||||
*/
|
||||
two_pow_sppr = 1 << sppr;
|
||||
divider = (divider + two_pow_sppr - 1) & -two_pow_sppr;
|
||||
|
||||
if (err < best_err) {
|
||||
best_spr = spr;
|
||||
best_sppr = sppr;
|
||||
best_err = err;
|
||||
}
|
||||
/*
|
||||
* recalculate sppr as rounding up divider might have
|
||||
* increased it enough to change the position of the
|
||||
* highest set bit. In this case the bit that now
|
||||
* doesn't make it into SPR is 0, so there is no need to
|
||||
* round again.
|
||||
*/
|
||||
sppr = fls(divider) - 4;
|
||||
spr = divider >> sppr;
|
||||
|
||||
/*
|
||||
* Now do range checking. SPR is constructed to have a
|
||||
* width of 4 bits, so this is fine for sure. So we
|
||||
* still need to check for sppr to fit into 3 bits:
|
||||
*/
|
||||
if (sppr > 7)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((best_sppr == 0) && (best_spr == 0))
|
||||
return -EINVAL;
|
||||
|
||||
prescale = ((best_sppr & 0x6) << 5) |
|
||||
((best_sppr & 0x1) << 4) | best_spr;
|
||||
prescale = ((sppr & 0x6) << 5) | ((sppr & 0x1) << 4) | spr;
|
||||
} else {
|
||||
/*
|
||||
* the supported rates are: 4,6,8...30
|
||||
|
|
Loading…
Reference in New Issue