From: Marek Szyprowski <[email protected]> There are some boards that cut corners on the SPI standard and omit one of the two data wires (MOSI or MISO) when connecting simple chips which don't need them. This is distinct from SPI_3WIRE, which also has just one data line but uses that for bidirectional transfers.
One example of such board is a NCP ARM S3C6410 based machine. This patch adds support for such non-standard configuration in GPIO-based SPI controller. It has been tested in configuration without MISO pin. [ [email protected]: split SPI core bits, compile fix, use gpio_is_valid instead of designated GPIO literals, make sure half-duplex flag is set too; add comment about those HDX modes; error checks ] Reviewed-by: Kyungmin Park <[email protected]> Signed-off-by: Marek Szyprowski <[email protected]> Signed-off-by: David Brownell <[email protected]> --- drivers/spi/spi_gpio.c | 64 ++++++++++++++++++++++++++++++----------- include/linux/spi/spi_gpio.h | 10 +++--- 2 files changed, 54 insertions(+), 20 deletions(-) --- a/drivers/spi/spi_gpio.c +++ b/drivers/spi/spi_gpio.c @@ -109,12 +109,16 @@ static inline void setsck(const struct s static inline void setmosi(const struct spi_device *spi, int is_on) { - gpio_set_value(SPI_MOSI_GPIO, is_on); + if (gpio_is_valid(SPI_MOSI_GPIO)) + gpio_set_value(SPI_MOSI_GPIO, is_on); } static inline int getmiso(const struct spi_device *spi) { - return !!gpio_get_value(SPI_MISO_GPIO); + if (gpio_is_valid(SPI_MISO_GPIO)) + return !!gpio_get_value(SPI_MISO_GPIO); + else + return 0; } #undef pdata @@ -228,24 +232,44 @@ static int __init spi_gpio_alloc(unsigne value = gpio_direction_input(pin); else value = gpio_direction_output(pin, 0); + if (value) + gpio_free(pin); } return value; } static int __init -spi_gpio_request(struct spi_gpio_platform_data *pdata, const char *label) +spi_gpio_request(struct spi_gpio_platform_data *pdata, + const char *label, u16 *res_flags) { int value; /* NOTE: SPI_*_GPIO symbols may reference "pdata" */ - value = spi_gpio_alloc(SPI_MOSI_GPIO, label, false); - if (value) - goto done; + /* + * This supports some half-duplex signal-minimized flavors of + * SPI, omitting MOSI (for RX only) or MISO (for TX only). + * + * REVISIT it could also be made to support SPI_3WIRE, which + * also uses one data pin ... for both directions, switching + * from output to input as needed for each spi_transfer. + */ + if (gpio_is_valid(SPI_MOSI_GPIO)) { + value = spi_gpio_alloc(SPI_MOSI_GPIO, label, false); + if (value) + goto done; + } else { + if (!gpio_is_valid(SPI_MISO_GPIO)) + return -ENODEV; + *res_flags |= SPI_MASTER_HALF_DUPLEX | SPI_MASTER_NO_TX; + } - value = spi_gpio_alloc(SPI_MISO_GPIO, label, true); - if (value) - goto free_mosi; + if (gpio_is_valid(SPI_MISO_GPIO)) { + value = spi_gpio_alloc(SPI_MISO_GPIO, label, true); + if (value) + goto free_mosi; + } else + *res_flags |= SPI_MASTER_HALF_DUPLEX | SPI_MASTER_NO_RX; value = spi_gpio_alloc(SPI_SCK_GPIO, label, false); if (value) @@ -254,9 +278,11 @@ spi_gpio_request(struct spi_gpio_platfor goto done; free_miso: - gpio_free(SPI_MISO_GPIO); + if (gpio_is_valid(SPI_MISO_GPIO)) + gpio_free(SPI_MISO_GPIO); free_mosi: - gpio_free(SPI_MOSI_GPIO); + if (gpio_is_valid(SPI_MOSI_GPIO)) + gpio_free(SPI_MOSI_GPIO); done: return value; } @@ -267,6 +293,7 @@ static int __init spi_gpio_probe(struct struct spi_master *master; struct spi_gpio *spi_gpio; struct spi_gpio_platform_data *pdata; + u16 master_flags = 0; pdata = pdev->dev.platform_data; #ifdef GENERIC_BITBANG @@ -274,7 +301,7 @@ static int __init spi_gpio_probe(struct return -ENODEV; #endif - status = spi_gpio_request(pdata, dev_name(&pdev->dev)); + status = spi_gpio_request(pdata, dev_name(&pdev->dev), &master_flags); if (status < 0) return status; @@ -290,6 +317,7 @@ static int __init spi_gpio_probe(struct if (pdata) spi_gpio->pdata = *pdata; + master->flags = master_flags; master->bus_num = pdev->id; master->num_chipselect = SPI_N_CHIPSEL; master->setup = spi_gpio_setup; @@ -308,8 +336,10 @@ static int __init spi_gpio_probe(struct if (status < 0) { spi_master_put(spi_gpio->bitbang.master); gpio_free: - gpio_free(SPI_MISO_GPIO); - gpio_free(SPI_MOSI_GPIO); + if (gpio_is_valid(SPI_MISO_GPIO)) + gpio_free(SPI_MISO_GPIO); + if (gpio_is_valid(SPI_MOSI_GPIO)) + gpio_free(SPI_MOSI_GPIO); gpio_free(SPI_SCK_GPIO); spi_master_put(master); } @@ -332,8 +362,10 @@ static int __exit spi_gpio_remove(struct platform_set_drvdata(pdev, NULL); - gpio_free(SPI_MISO_GPIO); - gpio_free(SPI_MOSI_GPIO); + if (gpio_is_valid(SPI_MISO_GPIO)) + gpio_free(SPI_MISO_GPIO); + if (gpio_is_valid(SPI_MOSI_GPIO)) + gpio_free(SPI_MOSI_GPIO); gpio_free(SPI_SCK_GPIO); return status; --- a/include/linux/spi/spi_gpio.h +++ b/include/linux/spi/spi_gpio.h @@ -38,8 +38,10 @@ /** * struct spi_gpio_platform_data - parameter for bitbanged SPI master * @sck: number of the GPIO used for clock output - * @mosi: number of the GPIO used for Master Output, Slave In (MOSI) data - * @miso: number of the GPIO used for Master Input, Slave Output (MISO) data + * @mosi: Number of the GPIO used for Master Output, Slave In (MOSI) data. + * If master output is unavailable, an invalid GPIO number like -EINVAL. + * @miso: Number of the GPIO used for Master Input, Slave Output (MISO) data. + * If master input is unavailable, an invalid GPIO number like -EINVAL. * @num_chipselect: how many slaves to allow * * All GPIO signals used with the SPI bus managed through this driver @@ -57,8 +59,8 @@ */ struct spi_gpio_platform_data { unsigned sck; - unsigned mosi; - unsigned miso; + int mosi; + int miso; u16 num_chipselect; }; ------------------------------------------------------------------------------ _______________________________________________ spi-devel-general mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/spi-devel-general
