From d962608ce2188a1d46ec9d356d6fad5cd6fc0341 Mon Sep 17 00:00:00 2001 From: Dragos Bogdan Date: Mon, 21 Dec 2020 17:29:35 +0200 Subject: spi: Add SPI_NO_TX/RX support Transmit/receive only is a valid SPI mode. For example, the MOSI/TX line might be missing from an ADC while for a DAC the MISO/RX line may be optional. This patch adds these two new modes: SPI_NO_TX and SPI_NO_RX. This way, the drivers will be able to identify if any of these two lines is missing and to adjust the transfers accordingly. Signed-off-by: Dragos Bogdan Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20201221152936.53873-2-alexandru.ardelean@analog.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'drivers/spi/spi.c') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 51d7c004fbab..ca75f4036eda 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1941,6 +1941,9 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, /* Device DUAL/QUAD mode */ if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) { switch (value) { + case 0: + spi->mode |= SPI_NO_TX; + break; case 1: break; case 2: @@ -1962,6 +1965,9 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, if (!of_property_read_u32(nc, "spi-rx-bus-width", &value)) { switch (value) { + case 0: + spi->mode |= SPI_NO_RX; + break; case 1: break; case 2: @@ -3329,12 +3335,16 @@ int spi_setup(struct spi_device *spi) unsigned bad_bits, ugly_bits; int status; - /* check mode to prevent that DUAL and QUAD set at the same time + /* + * check mode to prevent that any two of DUAL, QUAD and NO_MOSI/MISO + * are set at the same time */ - if (((spi->mode & SPI_TX_DUAL) && (spi->mode & SPI_TX_QUAD)) || - ((spi->mode & SPI_RX_DUAL) && (spi->mode & SPI_RX_QUAD))) { + if ((hweight_long(spi->mode & + (SPI_TX_DUAL | SPI_TX_QUAD | SPI_NO_TX)) > 1) || + (hweight_long(spi->mode & + (SPI_RX_DUAL | SPI_RX_QUAD | SPI_NO_RX)) > 1)) { dev_err(&spi->dev, - "setup: can not select dual and quad at the same time\n"); + "setup: can not select any two of dual, quad and no-rx/tx at the same time\n"); return -EINVAL; } /* if it is SPI_3WIRE mode, DUAL and QUAD should be forbidden @@ -3348,7 +3358,8 @@ int spi_setup(struct spi_device *spi) * SPI_CS_WORD has a fallback software implementation, * so it is ignored here. */ - bad_bits = spi->mode & ~(spi->controller->mode_bits | SPI_CS_WORD); + bad_bits = spi->mode & ~(spi->controller->mode_bits | SPI_CS_WORD | + SPI_NO_TX | SPI_NO_RX); /* nothing prevents from working with active-high CS in case if it * is driven by GPIO. */ @@ -3610,6 +3621,8 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message) * 2. check tx/rx_nbits match the mode in spi_device */ if (xfer->tx_buf) { + if (spi->mode & SPI_NO_TX) + return -EINVAL; if (xfer->tx_nbits != SPI_NBITS_SINGLE && xfer->tx_nbits != SPI_NBITS_DUAL && xfer->tx_nbits != SPI_NBITS_QUAD) @@ -3623,6 +3636,8 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message) } /* check transfer rx_nbits */ if (xfer->rx_buf) { + if (spi->mode & SPI_NO_RX) + return -EINVAL; if (xfer->rx_nbits != SPI_NBITS_SINGLE && xfer->rx_nbits != SPI_NBITS_DUAL && xfer->rx_nbits != SPI_NBITS_QUAD) -- cgit From 4cea6b8cc34ee61358d681bd2009b8bac1736ffe Mon Sep 17 00:00:00 2001 From: "leilk.liu" Date: Sun, 7 Feb 2021 11:09:51 +0800 Subject: spi: add power control when set_cs_timing As to set_cs_timing takes effect immediately, power spi is needed when call spi_set_cs_timing. Signed-off-by: leilk.liu Link: https://lore.kernel.org/r/20210207030953.9297-2-leilk.liu@mediatek.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) (limited to 'drivers/spi/spi.c') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index a17efa8c3047..21ea3e8a00e2 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -3460,11 +3460,30 @@ EXPORT_SYMBOL_GPL(spi_setup); int spi_set_cs_timing(struct spi_device *spi, struct spi_delay *setup, struct spi_delay *hold, struct spi_delay *inactive) { + struct device *parent = spi->controller->dev.parent; size_t len; + int status; + + if (spi->controller->set_cs_timing) { + if (spi->controller->auto_runtime_pm) { + status = pm_runtime_get_sync(parent); + if (status < 0) { + pm_runtime_put_noidle(parent); + dev_err(&spi->controller->dev, "Failed to power device: %d\n", + status); + return status; + } - if (spi->controller->set_cs_timing) - return spi->controller->set_cs_timing(spi, setup, hold, - inactive); + status = spi->controller->set_cs_timing(spi, setup, + hold, inactive); + pm_runtime_mark_last_busy(parent); + pm_runtime_put_autosuspend(parent); + return status; + } else { + return spi->controller->set_cs_timing(spi, setup, hold, + inactive); + } + } if ((setup && setup->unit == SPI_DELAY_UNIT_SCK) || (hold && hold->unit == SPI_DELAY_UNIT_SCK) || -- cgit From 0486d9f91d373e7f47276f30898ee0cb12656a70 Mon Sep 17 00:00:00 2001 From: "leilk.liu" Date: Sun, 7 Feb 2021 11:09:52 +0800 Subject: spi: support CS timing for HW & SW mode this patch supports the controller's HW CS and SW CS via use cs_gpio. Signed-off-by: leilk.liu Link: https://lore.kernel.org/r/20210207030953.9297-3-leilk.liu@mediatek.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/spi/spi.c') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 21ea3e8a00e2..a247fcac0dc7 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -810,7 +810,8 @@ static void spi_set_cs(struct spi_device *spi, bool enable) spi->controller->last_cs_enable = enable; spi->controller->last_cs_mode_high = spi->mode & SPI_CS_HIGH; - if (!spi->controller->set_cs_timing) { + if (spi->cs_gpiod || gpio_is_valid(spi->cs_gpio) || + !spi->controller->set_cs_timing) { if (enable1) spi_delay_exec(&spi->controller->cs_setup, NULL); else @@ -841,7 +842,8 @@ static void spi_set_cs(struct spi_device *spi, bool enable) spi->controller->set_cs(spi, !enable); } - if (!spi->controller->set_cs_timing) { + if (spi->cs_gpiod || gpio_is_valid(spi->cs_gpio) || + !spi->controller->set_cs_timing) { if (!enable1) spi_delay_exec(&spi->controller->cs_inactive, NULL); } @@ -3464,7 +3466,8 @@ int spi_set_cs_timing(struct spi_device *spi, struct spi_delay *setup, size_t len; int status; - if (spi->controller->set_cs_timing) { + if (spi->controller->set_cs_timing && + !(spi->cs_gpiod || gpio_is_valid(spi->cs_gpio))) { if (spi->controller->auto_runtime_pm) { status = pm_runtime_get_sync(parent); if (status < 0) { -- cgit From b306320322c9cfaa465bc2c7367acf6072b1ac0e Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Thu, 11 Feb 2021 19:08:20 +0100 Subject: spi: Skip zero-length transfers in spi_transfer_one_message() With the introduction of 26751de25d25 ("spi: bcm2835: Micro-optimise FIFO loops") it has become apparent that some users might initiate zero-length SPI transfers. A fact the micro-optimization omitted, and which turned out to cause crashes[1]. Instead of changing the micro-optimization itself, use a bigger hammer and skip zero-length transfers altogether for drivers using the default transfer_one_message() implementation. Reported-by: Phil Elwell Fixes: 26751de25d25 ("spi: bcm2835: Micro-optimise FIFO loops") Signed-off-by: Nicolas Saenz Julienne [1] https://github.com/raspberrypi/linux/issues/4100 Link: https://lore.kernel.org/r/20210211180820.25757-1-nsaenzjulienne@suse.de Signed-off-by: Mark Brown --- drivers/spi/spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi/spi.c') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 720ab34784c1..ccca3a7409fa 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1267,7 +1267,7 @@ static int spi_transfer_one_message(struct spi_controller *ctlr, ptp_read_system_prets(xfer->ptp_sts); } - if (xfer->tx_buf || xfer->rx_buf) { + if ((xfer->tx_buf || xfer->rx_buf) && xfer->len) { reinit_completion(&ctlr->xfer_completion); fallback_pio: -- cgit