* [PATCH 0/4] spi: a3700: Add improvements and full-duplex transfers
@ 2018-01-17 16:15 Maxime Chevallier
2018-01-17 16:15 ` [PATCH 1/4] spi: a3700: Clear DATA_OUT when performing a read Maxime Chevallier
` (3 more replies)
0 siblings, 4 replies; 9+ messages in thread
From: Maxime Chevallier @ 2018-01-17 16:15 UTC (permalink / raw)
To: linux-arm-kernel
This patchset adds some improvements on the existing Armada 3700 SPI
driver, the biggest being adding support for full-duplex transfers.
The first patch fixes an undocumented (to my knowledge, I might have
missed something) behaviour where some of the content of the last
write transfer is shifted out during a read half-duplex operation.
The second one sets the correct limits on the SPI max and min
frequencies, based on the source clock rate.
The last 2 patches enable full-duplex transfers. On this controller,
full-duplex transfers cannot use the hardware FIFOs and have to be
performed either 1 or 4 bytes at a time. Because this is more
CPU intensive, and a bit slower than FIFO mode, we only use the
full-duplex way of transferring data when necessary.
Maxime Chevallier (4):
spi: a3700: Clear DATA_OUT when performing a read
spi: a3700: Set frequency limits at startup
spi: a3700: Allow to enable or disable FIFO mode
spi: a3700: Add full-duplex support
drivers/spi/spi-armada-3700.c | 106 ++++++++++++++++++++++++++++++++++++------
1 file changed, 91 insertions(+), 15 deletions(-)
--
2.1.4
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH 1/4] spi: a3700: Clear DATA_OUT when performing a read 2018-01-17 16:15 [PATCH 0/4] spi: a3700: Add improvements and full-duplex transfers Maxime Chevallier @ 2018-01-17 16:15 ` Maxime Chevallier 2018-01-18 11:24 ` Applied "spi: a3700: Clear DATA_OUT when performing a read" to the spi tree Mark Brown 2018-01-17 16:15 ` [PATCH 2/4] spi: a3700: Set frequency limits at startup Maxime Chevallier ` (2 subsequent siblings) 3 siblings, 1 reply; 9+ messages in thread From: Maxime Chevallier @ 2018-01-17 16:15 UTC (permalink / raw) To: linux-arm-kernel When performing a read using FIFO mode, the spi controller shifts out the last 2 bytes that were written in a previous transfer on MOSI. This undocumented behaviour can cause devices to misinterpret the transfer, so we explicitly clear the WFIFO before each read. This behaviour was noticed on EspressoBin. Signed-off-by: Maxime Chevallier <maxime.chevallier@smile.fr> --- drivers/spi/spi-armada-3700.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/spi/spi-armada-3700.c b/drivers/spi/spi-armada-3700.c index d653453..c11ea6c 100644 --- a/drivers/spi/spi-armada-3700.c +++ b/drivers/spi/spi-armada-3700.c @@ -615,6 +615,11 @@ static int a3700_spi_transfer_one(struct spi_master *master, a3700_spi_header_set(a3700_spi); if (xfer->rx_buf) { + /* Clear WFIFO, since it's last 2 bytes are shifted out during + * a read operation + */ + spireg_write(a3700_spi, A3700_SPI_DATA_OUT_REG, 0); + /* Set read data length */ spireg_write(a3700_spi, A3700_SPI_IF_DIN_CNT_REG, a3700_spi->buf_len); -- 2.1.4 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Applied "spi: a3700: Clear DATA_OUT when performing a read" to the spi tree 2018-01-17 16:15 ` [PATCH 1/4] spi: a3700: Clear DATA_OUT when performing a read Maxime Chevallier @ 2018-01-18 11:24 ` Mark Brown 0 siblings, 0 replies; 9+ messages in thread From: Mark Brown @ 2018-01-18 11:24 UTC (permalink / raw) To: linux-arm-kernel The patch spi: a3700: Clear DATA_OUT when performing a read has been applied to the spi tree at https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted. You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed. If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced. Please add any relevant lists and maintainers to the CCs when replying to this mail. Thanks, Mark >From 44a5f423e70374e5b42cecd85e78f2d79334e0f2 Mon Sep 17 00:00:00 2001 From: Maxime Chevallier <maxime.chevallier@smile.fr> Date: Wed, 17 Jan 2018 17:15:25 +0100 Subject: [PATCH] spi: a3700: Clear DATA_OUT when performing a read When performing a read using FIFO mode, the spi controller shifts out the last 2 bytes that were written in a previous transfer on MOSI. This undocumented behaviour can cause devices to misinterpret the transfer, so we explicitly clear the WFIFO before each read. This behaviour was noticed on EspressoBin. Signed-off-by: Maxime Chevallier <maxime.chevallier@smile.fr> Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-armada-3700.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/spi/spi-armada-3700.c b/drivers/spi/spi-armada-3700.c index 77fe55ce790c..4857b0119556 100644 --- a/drivers/spi/spi-armada-3700.c +++ b/drivers/spi/spi-armada-3700.c @@ -607,6 +607,11 @@ static int a3700_spi_transfer_one(struct spi_master *master, a3700_spi_header_set(a3700_spi); if (xfer->rx_buf) { + /* Clear WFIFO, since it's last 2 bytes are shifted out during + * a read operation + */ + spireg_write(a3700_spi, A3700_SPI_DATA_OUT_REG, 0); + /* Set read data length */ spireg_write(a3700_spi, A3700_SPI_IF_DIN_CNT_REG, a3700_spi->buf_len); -- 2.15.1 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 2/4] spi: a3700: Set frequency limits at startup 2018-01-17 16:15 [PATCH 0/4] spi: a3700: Add improvements and full-duplex transfers Maxime Chevallier 2018-01-17 16:15 ` [PATCH 1/4] spi: a3700: Clear DATA_OUT when performing a read Maxime Chevallier @ 2018-01-17 16:15 ` Maxime Chevallier 2018-01-18 11:23 ` Applied "spi: a3700: Set frequency limits at startup" to the spi tree Mark Brown 2018-01-17 16:15 ` [PATCH 3/4] spi: a3700: Allow to enable or disable FIFO mode Maxime Chevallier 2018-01-17 16:15 ` [PATCH 4/4] spi: a3700: Add full-duplex support Maxime Chevallier 3 siblings, 1 reply; 9+ messages in thread From: Maxime Chevallier @ 2018-01-17 16:15 UTC (permalink / raw) To: linux-arm-kernel Armada 3700 SPI controller has an internal clock divider which can divide the parent clock frequency by up to 30. This patch sets the limits in the spi_controller fields so that we can detect when a non-supported frequency is requested by a device for a transfer. Signed-off-by: Maxime Chevallier <maxime.chevallier@smile.fr> --- drivers/spi/spi-armada-3700.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/spi/spi-armada-3700.c b/drivers/spi/spi-armada-3700.c index c11ea6c..8048468 100644 --- a/drivers/spi/spi-armada-3700.c +++ b/drivers/spi/spi-armada-3700.c @@ -27,6 +27,8 @@ #define DRIVER_NAME "armada_3700_spi" +#define A3700_SPI_MAX_SPEED_HZ 100000000 +#define A3700_SPI_MAX_PRESCALE 30 #define A3700_SPI_TIMEOUT 10 /* SPI Register Offest */ @@ -823,6 +825,11 @@ static int a3700_spi_probe(struct platform_device *pdev) goto error; } + master->max_speed_hz = min_t(unsigned long, A3700_SPI_MAX_SPEED_HZ, + clk_get_rate(spi->clk)); + master->min_speed_hz = DIV_ROUND_UP(clk_get_rate(spi->clk), + A3700_SPI_MAX_PRESCALE); + ret = a3700_spi_init(spi); if (ret) goto error_clk; -- 2.1.4 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Applied "spi: a3700: Set frequency limits at startup" to the spi tree 2018-01-17 16:15 ` [PATCH 2/4] spi: a3700: Set frequency limits at startup Maxime Chevallier @ 2018-01-18 11:23 ` Mark Brown 0 siblings, 0 replies; 9+ messages in thread From: Mark Brown @ 2018-01-18 11:23 UTC (permalink / raw) To: linux-arm-kernel The patch spi: a3700: Set frequency limits at startup has been applied to the spi tree at https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted. You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed. If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced. Please add any relevant lists and maintainers to the CCs when replying to this mail. Thanks, Mark >From abf3a49e50967bd67cff67f289690f76436f461f Mon Sep 17 00:00:00 2001 From: Maxime Chevallier <maxime.chevallier@smile.fr> Date: Wed, 17 Jan 2018 17:15:26 +0100 Subject: [PATCH] spi: a3700: Set frequency limits at startup Armada 3700 SPI controller has an internal clock divider which can divide the parent clock frequency by up to 30. This patch sets the limits in the spi_controller fields so that we can detect when a non-supported frequency is requested by a device for a transfer. Signed-off-by: Maxime Chevallier <maxime.chevallier@smile.fr> Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-armada-3700.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/spi/spi-armada-3700.c b/drivers/spi/spi-armada-3700.c index 4857b0119556..07f227e3c834 100644 --- a/drivers/spi/spi-armada-3700.c +++ b/drivers/spi/spi-armada-3700.c @@ -27,6 +27,8 @@ #define DRIVER_NAME "armada_3700_spi" +#define A3700_SPI_MAX_SPEED_HZ 100000000 +#define A3700_SPI_MAX_PRESCALE 30 #define A3700_SPI_TIMEOUT 10 /* SPI Register Offest */ @@ -815,6 +817,11 @@ static int a3700_spi_probe(struct platform_device *pdev) goto error; } + master->max_speed_hz = min_t(unsigned long, A3700_SPI_MAX_SPEED_HZ, + clk_get_rate(spi->clk)); + master->min_speed_hz = DIV_ROUND_UP(clk_get_rate(spi->clk), + A3700_SPI_MAX_PRESCALE); + ret = a3700_spi_init(spi); if (ret) goto error_clk; -- 2.15.1 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 3/4] spi: a3700: Allow to enable or disable FIFO mode 2018-01-17 16:15 [PATCH 0/4] spi: a3700: Add improvements and full-duplex transfers Maxime Chevallier 2018-01-17 16:15 ` [PATCH 1/4] spi: a3700: Clear DATA_OUT when performing a read Maxime Chevallier 2018-01-17 16:15 ` [PATCH 2/4] spi: a3700: Set frequency limits at startup Maxime Chevallier @ 2018-01-17 16:15 ` Maxime Chevallier 2018-01-18 11:23 ` Applied "spi: a3700: Allow to enable or disable FIFO mode" to the spi tree Mark Brown 2018-01-17 16:15 ` [PATCH 4/4] spi: a3700: Add full-duplex support Maxime Chevallier 3 siblings, 1 reply; 9+ messages in thread From: Maxime Chevallier @ 2018-01-17 16:15 UTC (permalink / raw) To: linux-arm-kernel The armada 3700 SPI controller allows to make transfers without using the 32 bytes RFIFO and WFIFO. This commit enable switching between FIFO and non-FIFO mode, which is necessary to implement full-duplex transfers. Signed-off-by: Maxime Chevallier <maxime.chevallier@smile.fr> --- drivers/spi/spi-armada-3700.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-armada-3700.c b/drivers/spi/spi-armada-3700.c index 8048468..b387309 100644 --- a/drivers/spi/spi-armada-3700.c +++ b/drivers/spi/spi-armada-3700.c @@ -186,12 +186,15 @@ static int a3700_spi_pin_mode_set(struct a3700_spi *a3700_spi, return 0; } -static void a3700_spi_fifo_mode_set(struct a3700_spi *a3700_spi) +static void a3700_spi_fifo_mode_set(struct a3700_spi *a3700_spi, bool enable) { u32 val; val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG); - val |= A3700_SPI_FIFO_MODE; + if (enable) + val |= A3700_SPI_FIFO_MODE; + else + val &= ~A3700_SPI_FIFO_MODE; spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val); } @@ -299,7 +302,7 @@ static int a3700_spi_init(struct a3700_spi *a3700_spi) a3700_spi_deactivate_cs(a3700_spi, i); /* Enable FIFO mode */ - a3700_spi_fifo_mode_set(a3700_spi); + a3700_spi_fifo_mode_set(a3700_spi, true); /* Set SPI mode */ a3700_spi_mode_set(a3700_spi, master->mode_bits); -- 2.1.4 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Applied "spi: a3700: Allow to enable or disable FIFO mode" to the spi tree 2018-01-17 16:15 ` [PATCH 3/4] spi: a3700: Allow to enable or disable FIFO mode Maxime Chevallier @ 2018-01-18 11:23 ` Mark Brown 0 siblings, 0 replies; 9+ messages in thread From: Mark Brown @ 2018-01-18 11:23 UTC (permalink / raw) To: linux-arm-kernel The patch spi: a3700: Allow to enable or disable FIFO mode has been applied to the spi tree at https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted. You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed. If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced. Please add any relevant lists and maintainers to the CCs when replying to this mail. Thanks, Mark >From a456c9320d997fdb26bfab496dd9e37f155cc788 Mon Sep 17 00:00:00 2001 From: Maxime Chevallier <maxime.chevallier@smile.fr> Date: Wed, 17 Jan 2018 17:15:27 +0100 Subject: [PATCH] spi: a3700: Allow to enable or disable FIFO mode The armada 3700 SPI controller allows to make transfers without using the 32 bytes RFIFO and WFIFO. This commit enable switching between FIFO and non-FIFO mode, which is necessary to implement full-duplex transfers. Signed-off-by: Maxime Chevallier <maxime.chevallier@smile.fr> Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-armada-3700.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-armada-3700.c b/drivers/spi/spi-armada-3700.c index 07f227e3c834..97938c6d6267 100644 --- a/drivers/spi/spi-armada-3700.c +++ b/drivers/spi/spi-armada-3700.c @@ -185,12 +185,15 @@ static int a3700_spi_pin_mode_set(struct a3700_spi *a3700_spi, return 0; } -static void a3700_spi_fifo_mode_set(struct a3700_spi *a3700_spi) +static void a3700_spi_fifo_mode_set(struct a3700_spi *a3700_spi, bool enable) { u32 val; val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG); - val |= A3700_SPI_FIFO_MODE; + if (enable) + val |= A3700_SPI_FIFO_MODE; + else + val &= ~A3700_SPI_FIFO_MODE; spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val); } @@ -291,7 +294,7 @@ static int a3700_spi_init(struct a3700_spi *a3700_spi) a3700_spi_deactivate_cs(a3700_spi, i); /* Enable FIFO mode */ - a3700_spi_fifo_mode_set(a3700_spi); + a3700_spi_fifo_mode_set(a3700_spi, true); /* Set SPI mode */ a3700_spi_mode_set(a3700_spi, master->mode_bits); -- 2.15.1 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 4/4] spi: a3700: Add full-duplex support 2018-01-17 16:15 [PATCH 0/4] spi: a3700: Add improvements and full-duplex transfers Maxime Chevallier ` (2 preceding siblings ...) 2018-01-17 16:15 ` [PATCH 3/4] spi: a3700: Allow to enable or disable FIFO mode Maxime Chevallier @ 2018-01-17 16:15 ` Maxime Chevallier 2018-01-18 11:23 ` Applied "spi: a3700: Add full-duplex support" to the spi tree Mark Brown 3 siblings, 1 reply; 9+ messages in thread From: Maxime Chevallier @ 2018-01-17 16:15 UTC (permalink / raw) To: linux-arm-kernel The armada 3700 SPI controller has support for full-duplex transfers, but it can only be done without using the hardware FIFOs. A full duplex transfer is done by shifting 4 bytes at a time, or even one byte at a time for transfers less than 4 bytes long. While this method is perfectly suitable for small transfers, it is still slower than using the FIFOs. This commit implement full-duplex support, making sure that half-duplex transfers are still done using the FIFOs with the existing method. Some setup functions were moved around to make sure the controller is properly configured before beginning each transfer. This was tested on EspressoBin with a logical analyser, and a simple setup where MISO is connected on MOSI. Transfers were made from userspace using spidev and spi-pipe from the spi-tools project Signed-off-by: Maxime Chevallier <maxime.chevallier@smile.fr> --- drivers/spi/spi-armada-3700.c | 85 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 12 deletions(-) diff --git a/drivers/spi/spi-armada-3700.c b/drivers/spi/spi-armada-3700.c index b387309..a8576c8 100644 --- a/drivers/spi/spi-armada-3700.c +++ b/drivers/spi/spi-armada-3700.c @@ -421,15 +421,20 @@ static void a3700_spi_transfer_setup(struct spi_device *spi, struct spi_transfer *xfer) { struct a3700_spi *a3700_spi; - unsigned int byte_len; a3700_spi = spi_master_get_devdata(spi->master); a3700_spi_clock_set(a3700_spi, xfer->speed_hz); - byte_len = xfer->bits_per_word >> 3; + /* Use 4 bytes long transfers. Each transfer method has its way to deal + * with the remaining bytes for non 4-bytes aligned transfers. + */ + a3700_spi_bytelen_set(a3700_spi, 4); - a3700_spi_fifo_thres_set(a3700_spi, byte_len); + /* Initialize the working buffers */ + a3700_spi->tx_buf = xfer->tx_buf; + a3700_spi->rx_buf = xfer->rx_buf; + a3700_spi->buf_len = xfer->len; } static void a3700_spi_set_cs(struct spi_device *spi, bool enable) @@ -584,27 +589,26 @@ static int a3700_spi_prepare_message(struct spi_master *master, if (ret) return ret; - a3700_spi_bytelen_set(a3700_spi, 4); - a3700_spi_mode_set(a3700_spi, spi->mode); return 0; } -static int a3700_spi_transfer_one(struct spi_master *master, +static int a3700_spi_transfer_one_fifo(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer) { struct a3700_spi *a3700_spi = spi_master_get_devdata(master); int ret = 0, timeout = A3700_SPI_TIMEOUT; - unsigned int nbits = 0; + unsigned int nbits = 0, byte_len; u32 val; - a3700_spi_transfer_setup(spi, xfer); + /* Make sure we use FIFO mode */ + a3700_spi_fifo_mode_set(a3700_spi, true); - a3700_spi->tx_buf = xfer->tx_buf; - a3700_spi->rx_buf = xfer->rx_buf; - a3700_spi->buf_len = xfer->len; + /* Configure FIFO thresholds */ + byte_len = xfer->bits_per_word >> 3; + a3700_spi_fifo_thres_set(a3700_spi, byte_len); if (xfer->tx_buf) nbits = xfer->tx_nbits; @@ -739,6 +743,64 @@ static int a3700_spi_transfer_one(struct spi_master *master, return ret; } +static int a3700_spi_transfer_one_full_duplex(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct a3700_spi *a3700_spi = spi_master_get_devdata(master); + u32 val_in, val_out; + + /* Disable FIFO mode */ + a3700_spi_fifo_mode_set(a3700_spi, false); + + while (a3700_spi->buf_len) { + + /* When we have less than 4 bytes to transfer, switch to 1 byte + * mode. This is reset after each transfer + */ + if (a3700_spi->buf_len < 4) + a3700_spi_bytelen_set(a3700_spi, 1); + + if (a3700_spi->byte_len == 1) + val_out = *a3700_spi->tx_buf; + else + val_out = cpu_to_le32(*(u32 *)a3700_spi->tx_buf); + + spireg_write(a3700_spi, A3700_SPI_DATA_OUT_REG, val_out); + + /* Wait for all the data to be shifted in / out */ + while (!(spireg_read(a3700_spi, A3700_SPI_IF_CTRL_REG) & + A3700_SPI_XFER_DONE)) + cpu_relax(); + + val_in = le32_to_cpu(spireg_read(a3700_spi, + A3700_SPI_DATA_IN_REG)); + + memcpy(a3700_spi->rx_buf, &val_in, a3700_spi->byte_len); + + a3700_spi->buf_len -= a3700_spi->byte_len; + a3700_spi->tx_buf += a3700_spi->byte_len; + a3700_spi->rx_buf += a3700_spi->byte_len; + + } + + spi_finalize_current_transfer(master); + + return 0; +} + +static int a3700_spi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + a3700_spi_transfer_setup(spi, xfer); + + if (xfer->tx_buf && xfer->rx_buf) + return a3700_spi_transfer_one_full_duplex(master, spi, xfer); + + return a3700_spi_transfer_one_fifo(master, spi, xfer); +} + static int a3700_spi_unprepare_message(struct spi_master *master, struct spi_message *message) { @@ -788,7 +850,6 @@ static int a3700_spi_probe(struct platform_device *pdev) master->transfer_one = a3700_spi_transfer_one; master->unprepare_message = a3700_spi_unprepare_message; master->set_cs = a3700_spi_set_cs; - master->flags = SPI_MASTER_HALF_DUPLEX; master->mode_bits |= (SPI_RX_DUAL | SPI_TX_DUAL | SPI_RX_QUAD | SPI_TX_QUAD); -- 2.1.4 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Applied "spi: a3700: Add full-duplex support" to the spi tree 2018-01-17 16:15 ` [PATCH 4/4] spi: a3700: Add full-duplex support Maxime Chevallier @ 2018-01-18 11:23 ` Mark Brown 0 siblings, 0 replies; 9+ messages in thread From: Mark Brown @ 2018-01-18 11:23 UTC (permalink / raw) To: linux-arm-kernel The patch spi: a3700: Add full-duplex support has been applied to the spi tree at https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted. You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed. If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced. Please add any relevant lists and maintainers to the CCs when replying to this mail. Thanks, Mark >From f68a7dcb91b7957c5bb1c3e347775332af719519 Mon Sep 17 00:00:00 2001 From: Maxime Chevallier <maxime.chevallier@smile.fr> Date: Wed, 17 Jan 2018 17:15:28 +0100 Subject: [PATCH] spi: a3700: Add full-duplex support The armada 3700 SPI controller has support for full-duplex transfers, but it can only be done without using the hardware FIFOs. A full duplex transfer is done by shifting 4 bytes at a time, or even one byte at a time for transfers less than 4 bytes long. While this method is perfectly suitable for small transfers, it is still slower than using the FIFOs. This commit implement full-duplex support, making sure that half-duplex transfers are still done using the FIFOs with the existing method. Some setup functions were moved around to make sure the controller is properly configured before beginning each transfer. This was tested on EspressoBin with a logical analyser, and a simple setup where MISO is connected on MOSI. Transfers were made from userspace using spidev and spi-pipe from the spi-tools project Signed-off-by: Maxime Chevallier <maxime.chevallier@smile.fr> Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-armada-3700.c | 85 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 12 deletions(-) diff --git a/drivers/spi/spi-armada-3700.c b/drivers/spi/spi-armada-3700.c index 97938c6d6267..fdc35dabcda2 100644 --- a/drivers/spi/spi-armada-3700.c +++ b/drivers/spi/spi-armada-3700.c @@ -413,15 +413,20 @@ static void a3700_spi_transfer_setup(struct spi_device *spi, struct spi_transfer *xfer) { struct a3700_spi *a3700_spi; - unsigned int byte_len; a3700_spi = spi_master_get_devdata(spi->master); a3700_spi_clock_set(a3700_spi, xfer->speed_hz); - byte_len = xfer->bits_per_word >> 3; + /* Use 4 bytes long transfers. Each transfer method has its way to deal + * with the remaining bytes for non 4-bytes aligned transfers. + */ + a3700_spi_bytelen_set(a3700_spi, 4); - a3700_spi_fifo_thres_set(a3700_spi, byte_len); + /* Initialize the working buffers */ + a3700_spi->tx_buf = xfer->tx_buf; + a3700_spi->rx_buf = xfer->rx_buf; + a3700_spi->buf_len = xfer->len; } static void a3700_spi_set_cs(struct spi_device *spi, bool enable) @@ -576,27 +581,26 @@ static int a3700_spi_prepare_message(struct spi_master *master, if (ret) return ret; - a3700_spi_bytelen_set(a3700_spi, 4); - a3700_spi_mode_set(a3700_spi, spi->mode); return 0; } -static int a3700_spi_transfer_one(struct spi_master *master, +static int a3700_spi_transfer_one_fifo(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer) { struct a3700_spi *a3700_spi = spi_master_get_devdata(master); int ret = 0, timeout = A3700_SPI_TIMEOUT; - unsigned int nbits = 0; + unsigned int nbits = 0, byte_len; u32 val; - a3700_spi_transfer_setup(spi, xfer); + /* Make sure we use FIFO mode */ + a3700_spi_fifo_mode_set(a3700_spi, true); - a3700_spi->tx_buf = xfer->tx_buf; - a3700_spi->rx_buf = xfer->rx_buf; - a3700_spi->buf_len = xfer->len; + /* Configure FIFO thresholds */ + byte_len = xfer->bits_per_word >> 3; + a3700_spi_fifo_thres_set(a3700_spi, byte_len); if (xfer->tx_buf) nbits = xfer->tx_nbits; @@ -731,6 +735,64 @@ static int a3700_spi_transfer_one(struct spi_master *master, return ret; } +static int a3700_spi_transfer_one_full_duplex(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct a3700_spi *a3700_spi = spi_master_get_devdata(master); + u32 val_in, val_out; + + /* Disable FIFO mode */ + a3700_spi_fifo_mode_set(a3700_spi, false); + + while (a3700_spi->buf_len) { + + /* When we have less than 4 bytes to transfer, switch to 1 byte + * mode. This is reset after each transfer + */ + if (a3700_spi->buf_len < 4) + a3700_spi_bytelen_set(a3700_spi, 1); + + if (a3700_spi->byte_len == 1) + val_out = *a3700_spi->tx_buf; + else + val_out = cpu_to_le32(*(u32 *)a3700_spi->tx_buf); + + spireg_write(a3700_spi, A3700_SPI_DATA_OUT_REG, val_out); + + /* Wait for all the data to be shifted in / out */ + while (!(spireg_read(a3700_spi, A3700_SPI_IF_CTRL_REG) & + A3700_SPI_XFER_DONE)) + cpu_relax(); + + val_in = le32_to_cpu(spireg_read(a3700_spi, + A3700_SPI_DATA_IN_REG)); + + memcpy(a3700_spi->rx_buf, &val_in, a3700_spi->byte_len); + + a3700_spi->buf_len -= a3700_spi->byte_len; + a3700_spi->tx_buf += a3700_spi->byte_len; + a3700_spi->rx_buf += a3700_spi->byte_len; + + } + + spi_finalize_current_transfer(master); + + return 0; +} + +static int a3700_spi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + a3700_spi_transfer_setup(spi, xfer); + + if (xfer->tx_buf && xfer->rx_buf) + return a3700_spi_transfer_one_full_duplex(master, spi, xfer); + + return a3700_spi_transfer_one_fifo(master, spi, xfer); +} + static int a3700_spi_unprepare_message(struct spi_master *master, struct spi_message *message) { @@ -780,7 +842,6 @@ static int a3700_spi_probe(struct platform_device *pdev) master->transfer_one = a3700_spi_transfer_one; master->unprepare_message = a3700_spi_unprepare_message; master->set_cs = a3700_spi_set_cs; - master->flags = SPI_MASTER_HALF_DUPLEX; master->mode_bits |= (SPI_RX_DUAL | SPI_TX_DUAL | SPI_RX_QUAD | SPI_TX_QUAD); -- 2.15.1 ^ permalink raw reply related [flat|nested] 9+ messages in thread
end of thread, other threads:[~2018-01-18 11:24 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2018-01-17 16:15 [PATCH 0/4] spi: a3700: Add improvements and full-duplex transfers Maxime Chevallier 2018-01-17 16:15 ` [PATCH 1/4] spi: a3700: Clear DATA_OUT when performing a read Maxime Chevallier 2018-01-18 11:24 ` Applied "spi: a3700: Clear DATA_OUT when performing a read" to the spi tree Mark Brown 2018-01-17 16:15 ` [PATCH 2/4] spi: a3700: Set frequency limits at startup Maxime Chevallier 2018-01-18 11:23 ` Applied "spi: a3700: Set frequency limits at startup" to the spi tree Mark Brown 2018-01-17 16:15 ` [PATCH 3/4] spi: a3700: Allow to enable or disable FIFO mode Maxime Chevallier 2018-01-18 11:23 ` Applied "spi: a3700: Allow to enable or disable FIFO mode" to the spi tree Mark Brown 2018-01-17 16:15 ` [PATCH 4/4] spi: a3700: Add full-duplex support Maxime Chevallier 2018-01-18 11:23 ` Applied "spi: a3700: Add full-duplex support" to the spi tree Mark Brown
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox