spi: mvebu: fix baudrate calculation for armada variant
commit7243e0b207
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>
This commit is contained in:
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 a new issue