aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/spi/spi.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2021-02-22 09:19:08 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2021-02-22 09:19:08 -0800
commita2590d69893f232cbb79d149dbbb456a1febca22 (patch)
tree870326105cf0ff810fe9778764b276324fc6b1b9 /drivers/spi/spi.c
parentMerge tag 'regulator-v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator (diff)
parentMerge remote-tracking branch 'spi/for-5.12' into spi-next (diff)
downloadlinux-dev-a2590d69893f232cbb79d149dbbb456a1febca22.tar.xz
linux-dev-a2590d69893f232cbb79d149dbbb456a1febca22.zip
Merge tag 'spi-v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
Pull spi updates from Mark Brown: "The main focus of this release from a framework point of view has been spi-mem where we've acquired support for a few new hardware features which enable better performance on suitable hardware. Otherwise mostly thanks to Arnd's cleanup efforts on old platforms we've removed several obsolete drivers which just about balance out the newer drivers we've added this cycle. Summary: - Allow drivers to flag if they are unidirectional. - Support for DTR mode and hardware acceleration of dummy cycles in spi-mem. - Support for Allwinder H616, Intel Lightning Mountain, nVidia Tegra QuadSPI, Realtek RTL838x and RTL839x. - Removal of obsolete EFM32, Txx9 and SIRF Prima and Atlas drivers" * tag 'spi-v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (76 commits) spi: Skip zero-length transfers in spi_transfer_one_message() spi: dw: Avoid stack content exposure spi: cadence-quadspi: Use spi_mem_dtr_supports_op() spi: spi-mem: add spi_mem_dtr_supports_op() spi: atmel-quadspi: Disable the QSPI IP at suspend() spi: pxa2xx: Add IDs for the controllers found on Intel Lynxpoint spi: pxa2xx: Fix the controller numbering for Wildcat Point spi: Change provied to provided in the file spi.h spi: mediatek: add set_cs_timing support spi: support CS timing for HW & SW mode spi: add power control when set_cs_timing spi: stm32: make spurious and overrun interrupts visible spi: stm32h7: replace private SPI_1HZ_NS with NSEC_PER_SEC spi: stm32: defer probe for reset spi: stm32: driver uses reset controller only at init spi: stm32h7: ensure message are smaller than max size spi: stm32: use bitfield macros spi: stm32: do not mandate cs_gpio spi: stm32: properly handle 0 byte transfer spi: clps711xx: remove redundant white-space ...
Diffstat (limited to 'drivers/spi/spi.c')
-rw-r--r--drivers/spi/spi.c59
1 files changed, 48 insertions, 11 deletions
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 801d8b499788..b08efe88ccd6 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);
}
@@ -1267,7 +1269,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:
@@ -1945,6 +1947,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:
@@ -1966,6 +1971,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:
@@ -3333,12 +3341,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
@@ -3352,7 +3364,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.
*/
@@ -3449,11 +3462,31 @@ 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 &&
+ !(spi->cs_gpiod || gpio_is_valid(spi->cs_gpio))) {
+ 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) ||
@@ -3615,6 +3648,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)
@@ -3628,6 +3663,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)