Commit a44648b057f5331fe6c0e863dc693ed335490e7a

Authored by Anton Vorontsov
Committed by Linus Torvalds
1 parent e24a4d1ee3

spi_mpc83xx: fix prescale modulus calculation

Long ago I've noticed (but didn't pay much attention) that
spi_mpc83xx using PM calculations that differs from what
specs describe. I.e.

u8 pm = mpc83xx_spi->spibrg / (spi->max_speed_hz * 4);

While specs says: "The SPI baud rate generator clock source (either
system clock or system clock divided by 16, depending on DIV16 bit) is
divided by 4 * ([PM] + 1), a range from 4 to 64.".

Thus " - 1" is missing in the spi_mpc83xx's formula.

Why nobody noticed that bug? Probably because sysclk usually less then
user expects, e.g. you expect 200 MHz, but real clock is 198 MHz,
and integer rounding helps when this formula is used.

Suppose it's SPI in QE, SYSCLK at 198 MHz, thus SPIBRG at 99MHz, 25 MHz
requested.

PM = (99MHz / ( 25 MHz * 4 )), PM == 0, output SPICLK will be 24.75 MHz

At lower frequencies this bug is more noticeable, though.

And this bug shows itself in all its beauty if SYSCLK is equal or a bit
more than you expect (200 MHz SYSCLK, 100 MHz SPIBRG):
PM = (100MHz / ( 25 MHz * 4 )), PM == 1, output SPICLK will be 12.625 MHz!

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 1 changed file with 6 additions and 2 deletions Side-by-side Diff

drivers/spi/spi_mpc83xx.c
... ... @@ -148,6 +148,8 @@
148 148 if (value == BITBANG_CS_ACTIVE) {
149 149 u32 regval = mpc83xx_spi_read_reg(&mpc83xx_spi->base->mode);
150 150 u32 len = spi->bits_per_word;
  151 + u8 pm;
  152 +
151 153 if (len == 32)
152 154 len = 0;
153 155 else
... ... @@ -170,7 +172,7 @@
170 172 regval |= SPMODE_LEN(len);
171 173  
172 174 if ((mpc83xx_spi->spibrg / spi->max_speed_hz) >= 64) {
173   - u8 pm = mpc83xx_spi->spibrg / (spi->max_speed_hz * 64);
  175 + pm = mpc83xx_spi->spibrg / (spi->max_speed_hz * 64) - 1;
174 176 if (pm > 0x0f) {
175 177 dev_err(&spi->dev, "Requested speed is too "
176 178 "low: %d Hz. Will use %d Hz instead.\n",
... ... @@ -180,7 +182,9 @@
180 182 }
181 183 regval |= SPMODE_PM(pm) | SPMODE_DIV16;
182 184 } else {
183   - u8 pm = mpc83xx_spi->spibrg / (spi->max_speed_hz * 4);
  185 + pm = mpc83xx_spi->spibrg / (spi->max_speed_hz * 4);
  186 + if (pm)
  187 + pm--;
184 188 regval |= SPMODE_PM(pm);
185 189 }
186 190