diff options
author | Jonathan Cameron <Jonathan.Cameron@huawei.com> | 2018-09-22 10:02:45 +0100 |
---|---|---|
committer | Jonathan Cameron <Jonathan.Cameron@huawei.com> | 2018-09-22 10:02:45 +0100 |
commit | 7a5f0b3a08e323d93d17b0eca48048c970c6eae2 (patch) | |
tree | f5a8ebf528bbf857f0224c19389e08a52cba1811 | |
parent | c8d5b99696398ea1039a6b83543ef572cdba911f (diff) | |
parent | cbaa62e0094a840fecc853910e0c0454529cec03 (diff) | |
download | linux-7a5f0b3a08e323d93d17b0eca48048c970c6eae2.tar.bz2 |
Merge tag 'spi-cs-word' into togreg
spi: Provide SPI_CS_WORD
This provides a SPI operation mode which changes chip select after every
word, used by some devices such as ADCs and DACs.
-rw-r--r-- | drivers/spi/spi.c | 31 | ||||
-rw-r--r-- | include/linux/spi/spi.h | 2 |
2 files changed, 31 insertions, 2 deletions
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index ec395a6baf9c..be73d236919f 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2774,8 +2774,10 @@ int spi_setup(struct spi_device *spi) return -EINVAL; /* help drivers fail *cleanly* when they need options * that aren't supported with their current controller + * SPI_CS_WORD has a fallback software implementation, + * so it is ignored here. */ - bad_bits = spi->mode & ~spi->controller->mode_bits; + bad_bits = spi->mode & ~(spi->controller->mode_bits | SPI_CS_WORD); ugly_bits = bad_bits & (SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD); if (ugly_bits) { @@ -2829,6 +2831,33 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message) if (list_empty(&message->transfers)) return -EINVAL; + /* If an SPI controller does not support toggling the CS line on each + * transfer (indicated by the SPI_CS_WORD flag), we can emulate it by + * splitting transfers into one-word transfers and ensuring that + * cs_change is set for each transfer. + */ + if ((spi->mode & SPI_CS_WORD) && !(ctlr->mode_bits & SPI_CS_WORD)) { + size_t maxsize; + int ret; + + maxsize = (spi->bits_per_word + 7) / 8; + + /* spi_split_transfers_maxsize() requires message->spi */ + message->spi = spi; + + ret = spi_split_transfers_maxsize(ctlr, message, maxsize, + GFP_KERNEL); + if (ret) + return ret; + + list_for_each_entry(xfer, &message->transfers, transfer_list) { + /* don't change cs_change on the last entry in the list */ + if (list_is_last(&xfer->transfer_list, &message->transfers)) + break; + xfer->cs_change = 1; + } + } + /* Half-duplex links include original MicroWire, and ones with * only one data pin like SPI_3WIRE (switches direction) or where * either MOSI or MISO is missing. They can also be caused by diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index a64235e05321..7cc1466111f5 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_CS_WORD 0x1000 /* toggle cs after each word */ int irq; void *controller_state; void *controller_data; @@ -177,7 +178,6 @@ struct spi_device { * the controller talks to each chip, like: * - memory packing (12 bit samples into low bits, others zeroed) * - priority - * - drop chipselect after each word * - chipselect delays * - ... */ |