spi: spi-gpio: Don't set MOSI as an input if not 3WIRE mode
authorKris Bahnsen <kris@embeddedTS.com>
Wed, 7 Dec 2022 23:08:53 +0000 (15:08 -0800)
committerMark Brown <broonie@kernel.org>
Thu, 8 Dec 2022 11:46:41 +0000 (11:46 +0000)
The addition of 3WIRE support would affect MOSI direction even
when still in standard (4 wire) mode. This can lead to MOSI being
at an invalid logic level when a device driver sets an SPI
message with a NULL tx_buf.

spi.h states that if tx_buf is NULL then "zeros will be shifted
out ... " If MOSI is tristated then the data shifted out is subject
to pull resistors, keepers, or in the absence of those, noise.

This issue came to light when using spi-gpio connected to an
ADS7843 touchscreen controller. MOSI pulled high when clocking
MISO data in caused the SPI device to interpret this as a command
which would put the device in an unexpected and non-functional
state.

Fixes: 4b859db2c606 ("spi: spi-gpio: add SPI_3WIRE support")
Fixes: 5132b3d28371 ("spi: gpio: Support 3WIRE high-impedance turn-around")
Signed-off-by: Kris Bahnsen <kris@embeddedTS.com>
Link: https://lore.kernel.org/r/20221207230853.6174-1-kris@embeddedTS.com
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/spi/spi-gpio.c

index 4b12c49..9c8c794 100644 (file)
@@ -268,9 +268,19 @@ static int spi_gpio_set_direction(struct spi_device *spi, bool output)
        if (output)
                return gpiod_direction_output(spi_gpio->mosi, 1);
 
-       ret = gpiod_direction_input(spi_gpio->mosi);
-       if (ret)
-               return ret;
+       /*
+        * Only change MOSI to an input if using 3WIRE mode.
+        * Otherwise, MOSI could be left floating if there is
+        * no pull resistor connected to the I/O pin, or could
+        * be left logic high if there is a pull-up. Transmitting
+        * logic high when only clocking MISO data in can put some
+        * SPI devices in to a bad state.
+        */
+       if (spi->mode & SPI_3WIRE) {
+               ret = gpiod_direction_input(spi_gpio->mosi);
+               if (ret)
+                       return ret;
+       }
        /*
         * Send a turnaround high impedance cycle when switching
         * from output to input. Theoretically there should be