Commit 656378a5 authored by Thor Thayer's avatar Thor Thayer Committed by Ben Hutchings

spi: dw: Fix dynamic speed change.

commit 0a8727e6 upstream.

An IOCTL call that calls spi_setup() and then dw_spi_setup() will
overwrite the persisted last transfer speed. On each transfer, the
SPI speed is compared to the last transfer speed to determine if the
clock divider registers need to be updated (did the speed change?).
This bug was observed with the spidev driver using spi-config to
update the max transfer speed.

This fix: Don't overwrite the persisted last transaction clock speed
when updating the SPI parameters in dw_spi_setup(). On the next
transaction, the new speed won't match the persisted last speed
and the hardware registers will be updated.
On initialization, the persisted last transaction clock
speed will be 0 but will be updated after the first SPI
transaction.

Move zeroed clock divider check into clock change test because
chip->clk_div is zero on startup and would cause a divide-by-zero
error. The calculation was wrong as well (can't support odd #).
Reported-by: default avatarVlastimil Setka <setka@vsis.cz>
Signed-off-by: default avatarVlastimil Setka <setka@vsis.cz>
Signed-off-by: default avatarThor Thayer <tthayer@opensource.altera.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
parent 2e0f55ea
...@@ -400,9 +400,6 @@ static void pump_transfers(unsigned long data) ...@@ -400,9 +400,6 @@ static void pump_transfers(unsigned long data)
chip = dws->cur_chip; chip = dws->cur_chip;
spi = message->spi; spi = message->spi;
if (unlikely(!chip->clk_div))
chip->clk_div = dws->max_freq / chip->speed_hz;
if (message->state == ERROR_STATE) { if (message->state == ERROR_STATE) {
message->status = -EIO; message->status = -EIO;
goto early_exit; goto early_exit;
...@@ -444,7 +441,7 @@ static void pump_transfers(unsigned long data) ...@@ -444,7 +441,7 @@ static void pump_transfers(unsigned long data)
if (transfer->speed_hz) { if (transfer->speed_hz) {
speed = chip->speed_hz; speed = chip->speed_hz;
if (transfer->speed_hz != speed) { if ((transfer->speed_hz != speed) || (!chip->clk_div)) {
speed = transfer->speed_hz; speed = transfer->speed_hz;
if (speed > dws->max_freq) { if (speed > dws->max_freq) {
printk(KERN_ERR "MRST SPI0: unsupported" printk(KERN_ERR "MRST SPI0: unsupported"
...@@ -683,7 +680,6 @@ static int dw_spi_setup(struct spi_device *spi) ...@@ -683,7 +680,6 @@ static int dw_spi_setup(struct spi_device *spi)
dev_err(&spi->dev, "No max speed HZ parameter\n"); dev_err(&spi->dev, "No max speed HZ parameter\n");
return -EINVAL; return -EINVAL;
} }
chip->speed_hz = spi->max_speed_hz;
chip->tmode = 0; /* Tx & Rx */ chip->tmode = 0; /* Tx & Rx */
/* Default SPI mode is SCPOL = 0, SCPH = 0 */ /* Default SPI mode is SCPOL = 0, SCPH = 0 */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment