spi: spi-gpio: add SPI_3WIRE support
authorLorenzo Bianconi <lorenzo.bianconi@redhat.com>
Sat, 28 Jul 2018 08:19:14 +0000 (10:19 +0200)
committerMark Brown <broonie@kernel.org>
Wed, 1 Aug 2018 13:50:28 +0000 (14:50 +0100)
Add SPI_3WIRE support to spi-gpio controller introducing
set_line_direction function pointer in spi_bitbang data structure.
Spi-gpio controller has been tested using hts221 temp/rh iio sensor
running in 3wire mode and lsm6dsm running in 4wire mode

Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/spi/spi-bitbang.c
drivers/spi/spi-gpio.c
include/linux/spi/spi_bitbang.h

index 76f1b53..f291760 100644 (file)
@@ -243,7 +243,23 @@ static int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t)
 {
        struct spi_bitbang_cs   *cs = spi->controller_state;
        unsigned                nsecs = cs->nsecs;
+       struct spi_bitbang      *bitbang;
+
+       bitbang = spi_master_get_devdata(spi->master);
+       if (bitbang->set_line_direction) {
+               int err;
 
+               err = bitbang->set_line_direction(spi, !!(t->tx_buf));
+               if (err < 0)
+                       return err;
+       }
+
+       if (spi->mode & SPI_3WIRE) {
+               unsigned flags;
+
+               flags = t->tx_buf ? SPI_MASTER_NO_RX : SPI_MASTER_NO_TX;
+               return cs->txrx_bufs(spi, cs->txrx_word, nsecs, t, flags);
+       }
        return cs->txrx_bufs(spi, cs->txrx_word, nsecs, t, 0);
 }
 
index be68298..0626e6e 100644 (file)
@@ -121,7 +121,10 @@ static inline int getmiso(const struct spi_device *spi)
 {
        struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
 
-       return !!gpiod_get_value_cansleep(spi_gpio->miso);
+       if (spi->mode & SPI_3WIRE)
+               return !!gpiod_get_value_cansleep(spi_gpio->mosi);
+       else
+               return !!gpiod_get_value_cansleep(spi_gpio->miso);
 }
 
 /*
@@ -250,6 +253,16 @@ static int spi_gpio_setup(struct spi_device *spi)
        return status;
 }
 
+static int spi_gpio_set_direction(struct spi_device *spi, bool output)
+{
+       struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
+
+       if (output)
+               return gpiod_direction_output(spi_gpio->mosi, 1);
+       else
+               return gpiod_direction_input(spi_gpio->mosi);
+}
+
 static void spi_gpio_cleanup(struct spi_device *spi)
 {
        spi_bitbang_cleanup(spi);
@@ -395,6 +408,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->flags = master_flags;
        master->bus_num = pdev->id;
        /* The master needs to think there is a chipselect even if not connected */
@@ -407,6 +421,7 @@ static int spi_gpio_probe(struct platform_device *pdev)
 
        spi_gpio->bitbang.master = master;
        spi_gpio->bitbang.chipselect = spi_gpio_chipselect;
+       spi_gpio->bitbang.set_line_direction = spi_gpio_set_direction;
 
        if ((master_flags & (SPI_MASTER_NO_TX | SPI_MASTER_NO_RX)) == 0) {
                spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_word_mode0;
index 5847f00..b7e021b 100644 (file)
@@ -31,6 +31,7 @@ struct spi_bitbang {
        u32     (*txrx_word[4])(struct spi_device *spi,
                        unsigned nsecs,
                        u32 word, u8 bits, unsigned flags);
+       int     (*set_line_direction)(struct spi_device *spi, bool output);
 };
 
 /* you can call these default bitbang->master methods from your custom