diff mbox series

[2/2] spi: gpio: Support 3WIRE high-impedance turn-around

Message ID 20180925143220.19007-2-linus.walleij@linaro.org
State New
Headers show
Series [1/2] spi: Add a DT binding for high-impedance turn-around | expand

Commit Message

Linus Walleij Sept. 25, 2018, 2:32 p.m. UTC
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>

---
ChangeLog v1->v2:
- Change some spelling to use dashes, high-impedance
  turn-around.
- Collect Lorenzo's ACK.
---
 drivers/spi/spi-gpio.c  | 24 +++++++++++++++++++++---
 drivers/spi/spi.c       |  2 ++
 include/linux/spi/spi.h |  1 +
 3 files changed, 24 insertions(+), 3 deletions(-)

-- 
2.17.1
diff mbox series

Patch

diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c
index 088772ebef9b..899527a060f2 100644
--- a/drivers/spi/spi-gpio.c
+++ b/drivers/spi/spi-gpio.c
@@ -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 */
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index ec395a6baf9c..a816fa05fa02 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1559,6 +1559,8 @@  static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
 		spi->mode |= SPI_CS_HIGH;
 	if (of_property_read_bool(nc, "spi-3wire"))
 		spi->mode |= SPI_3WIRE;
+	if (of_property_read_bool(nc, "spi-3wire-high-impedance-turn-around"))
+		spi->mode |= SPI_3WIRE_HIZ;
 	if (of_property_read_bool(nc, "spi-lsb-first"))
 		spi->mode |= SPI_LSB_FIRST;
 
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index a64235e05321..b58aaf4a4e4b 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -163,6 +163,7 @@  struct spi_device {
 #define	SPI_TX_QUAD	0x200			/* transmit with 4 wires */
 #define	SPI_RX_DUAL	0x400			/* receive with 2 wires */
 #define	SPI_RX_QUAD	0x800			/* receive with 4 wires */
+#define	SPI_3WIRE_HIZ	0x1000			/* high impedance turnaround */
 	int			irq;
 	void			*controller_state;
 	void			*controller_data;