summaryrefslogtreecommitdiff
path: root/drivers/spi/spi-zynqmp-gqspi.c
diff options
context:
space:
mode:
authorQuanyang Wang <quanyang.wang@windriver.com>2021-04-08 12:02:22 +0800
committerMark Brown <broonie@kernel.org>2021-04-09 13:40:49 +0100
commit8ad07d79bd56a531990a1a3f3f1c0eb19d2de806 (patch)
tree57e0b680947e148b883c5c82f1a6cbc6f5034ee8 /drivers/spi/spi-zynqmp-gqspi.c
parenta0f65be6e880a14d3445b75e7dc03d7d015fc922 (diff)
spi: spi-zynqmp-gqspi: transmit dummy circles by using the controller's internal functionality
There is a data corruption issue that occurs in the reading operation (cmd:0x6c) when transmitting common data as dummy circles. The gqspi controller has the functionality to send dummy clock circles. When writing data with the fields [receive, transmit, data_xfer] = [0,0,1] to the Generic FIFO, and configuring the correct SPI mode, the controller will transmit dummy circles. So let's switch to hardware dummy cycles transfer to fix this issue. Fixes: 1c26372e5aa9 ("spi: spi-zynqmp-gqspi: Update driver to use spi-mem framework") Signed-off-by: Quanyang Wang <quanyang.wang@windriver.com> Reviewed-by: Amit Kumar Mahapatra <amit.kumar-mahapatra@xilinx.com> Link: https://lore.kernel.org/r/20210408040223.23134-4-quanyang.wang@windriver.com Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'drivers/spi/spi-zynqmp-gqspi.c')
-rw-r--r--drivers/spi/spi-zynqmp-gqspi.c40
1 files changed, 18 insertions, 22 deletions
diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c
index 3b39461d58b3..cf73a069b759 100644
--- a/drivers/spi/spi-zynqmp-gqspi.c
+++ b/drivers/spi/spi-zynqmp-gqspi.c
@@ -521,7 +521,7 @@ static void zynqmp_qspi_filltxfifo(struct zynqmp_qspi *xqspi, int size)
{
u32 count = 0, intermediate;
- while ((xqspi->bytes_to_transfer > 0) && (count < size)) {
+ while ((xqspi->bytes_to_transfer > 0) && (count < size) && (xqspi->txbuf)) {
memcpy(&intermediate, xqspi->txbuf, 4);
zynqmp_gqspi_write(xqspi, GQSPI_TXD_OFST, intermediate);
@@ -580,7 +580,7 @@ static void zynqmp_qspi_fillgenfifo(struct zynqmp_qspi *xqspi, u8 nbits,
genfifoentry |= GQSPI_GENFIFO_DATA_XFER;
genfifoentry |= GQSPI_GENFIFO_TX;
transfer_len = xqspi->bytes_to_transfer;
- } else {
+ } else if (xqspi->rxbuf) {
genfifoentry &= ~GQSPI_GENFIFO_TX;
genfifoentry |= GQSPI_GENFIFO_DATA_XFER;
genfifoentry |= GQSPI_GENFIFO_RX;
@@ -588,6 +588,11 @@ static void zynqmp_qspi_fillgenfifo(struct zynqmp_qspi *xqspi, u8 nbits,
transfer_len = xqspi->dma_rx_bytes;
else
transfer_len = xqspi->bytes_to_receive;
+ } else {
+ /* Sending dummy circles here */
+ genfifoentry &= ~(GQSPI_GENFIFO_TX | GQSPI_GENFIFO_RX);
+ genfifoentry |= GQSPI_GENFIFO_DATA_XFER;
+ transfer_len = xqspi->bytes_to_transfer;
}
genfifoentry |= zynqmp_qspi_selectspimode(xqspi, nbits);
xqspi->genfifoentry = genfifoentry;
@@ -1011,32 +1016,23 @@ static int zynqmp_qspi_exec_op(struct spi_mem *mem,
}
if (op->dummy.nbytes) {
- tmpbuf = kzalloc(op->dummy.nbytes, GFP_KERNEL | GFP_DMA);
- if (!tmpbuf)
- return -ENOMEM;
- memset(tmpbuf, 0xff, op->dummy.nbytes);
- reinit_completion(&xqspi->data_completion);
- xqspi->txbuf = tmpbuf;
+ xqspi->txbuf = NULL;
xqspi->rxbuf = NULL;
- xqspi->bytes_to_transfer = op->dummy.nbytes;
+ /*
+ * xqspi->bytes_to_transfer here represents the dummy circles
+ * which need to be sent.
+ */
+ xqspi->bytes_to_transfer = op->dummy.nbytes * 8 / op->dummy.buswidth;
xqspi->bytes_to_receive = 0;
- zynqmp_qspi_write_op(xqspi, op->dummy.buswidth,
+ /*
+ * Using op->data.buswidth instead of op->dummy.buswidth here because
+ * we need to use it to configure the correct SPI mode.
+ */
+ zynqmp_qspi_write_op(xqspi, op->data.buswidth,
genfifoentry);
zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST,
zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST) |
GQSPI_CFG_START_GEN_FIFO_MASK);
- zynqmp_gqspi_write(xqspi, GQSPI_IER_OFST,
- GQSPI_IER_TXEMPTY_MASK |
- GQSPI_IER_GENFIFOEMPTY_MASK |
- GQSPI_IER_TXNOT_FULL_MASK);
- if (!wait_for_completion_interruptible_timeout
- (&xqspi->data_completion, msecs_to_jiffies(1000))) {
- err = -ETIMEDOUT;
- kfree(tmpbuf);
- goto return_err;
- }
-
- kfree(tmpbuf);
}
if (op->data.nbytes) {