spi: gpio: Support 3WIRE high-impedance turn-around
authorLinus Walleij <linus.walleij@linaro.org>
Thu, 1 Nov 2018 21:25:04 +0000 (22:25 +0100)
committerMark Brown <broonie@kernel.org>
Wed, 7 Nov 2018 16:15:49 +0000 (16:15 +0000)
Some devices such as the TPO TPG110 display panel require
a "high-impedance turn-around", in effect a clock cycle after
switching the line from output to input mode.

Support this in the GPIO driver to begin with. Other driver
may implement it if they can, it is unclear if this can
be achieved with anything else than GPIO bit-banging.

Cc: Andrzej Hajda <a.hajda@samsung.com>
Acked-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/spi/spi-gpio.c
include/linux/spi/spi.h

index 45973ee3ae119e59a3b6846889b0449a7ba3e0d6..a4aee26028cdf59c8bce2144e02b532e0d591cbd 100644 (file)
@@ -256,11 +256,29 @@ static int spi_gpio_setup(struct spi_device *spi)
 static int spi_gpio_set_direction(struct spi_device *spi, bool output)
 {
        struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
+       int ret;
 
        if (output)
                return gpiod_direction_output(spi_gpio->mosi, 1);
-       else
-               return gpiod_direction_input(spi_gpio->mosi);
+
+       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
+        * a clock delay here, but as has been noted above, the
+        * nsec delay function for bit-banged GPIO is simply
+        * {} because bit-banging just doesn't get fast enough
+        * anyway.
+        */
+       if (spi->mode & SPI_3WIRE_HIZ) {
+               gpiod_set_value_cansleep(spi_gpio->sck,
+                                        !(spi->mode & SPI_CPOL));
+               gpiod_set_value_cansleep(spi_gpio->sck,
+                                        !!(spi->mode & SPI_CPOL));
+       }
+       return 0;
 }
 
 static void spi_gpio_cleanup(struct spi_device *spi)
@@ -410,7 +428,7 @@ static int spi_gpio_probe(struct platform_device *pdev)
                return status;
 
        master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
-       master->mode_bits = SPI_3WIRE | SPI_CPHA | SPI_CPOL;
+       master->mode_bits = SPI_3WIRE | SPI_3WIRE_HIZ | SPI_CPHA | SPI_CPOL;
        master->flags = master_flags;
        master->bus_num = pdev->id;
        /* The master needs to think there is a chipselect even if not connected */
index 6be77fa5ab90cd38c5a86fe1f1c3e3546e3a016b..3ced58eebe1bc1e2cfc924f6083ff30f3334c460 100644 (file)
@@ -155,6 +155,7 @@ struct spi_device {
 #define        SPI_RX_DUAL     0x400                   /* receive with 2 wires */
 #define        SPI_RX_QUAD     0x800                   /* receive with 4 wires */
 #define SPI_CS_WORD    0x1000                  /* toggle cs after each word */
+#define        SPI_3WIRE_HIZ   0x2000                  /* high impedance turnaround */
        int                     irq;
        void                    *controller_state;
        void                    *controller_data;