[PATCH 4/6] spi: zynqmp_gqspi: Fix issue of reading more than 32bits length

Ashok Reddy Soma ashok.reddy.soma at amd.com
Thu Aug 25 14:59:04 CEST 2022


As the flash sizes are increasing day by day, QSPI can have devices of
size > 512MB. In qspi driver we are trying to read all the data at once
using DMA.

The DMA descriptor destination size is only 29bits long.

QSPIDMA_DST_SIZE 0xFF0F0804

BITS:  1:0      Reserved to keep word alignment
BITS: 28:2      Number of 4-byte words the DMA will transfer
BITS: 31:29     Reserved: Returns 0 when read, writes ignored

So we can only transfer data of 0x1FFFFFF0(512MB minus 4bytes) bytes.
Anything above will overflow this register and will ignore higher bits
above 29 bits.

Change the DMA functionality if the requested size is greater than or
equal to 512MB to read 256MB chunks.

Signed-off-by: Ashok Reddy Soma <ashok.reddy.soma at amd.com>
---

 drivers/spi/zynqmp_gqspi.c | 62 ++++++++++++++++++++++++--------------
 1 file changed, 39 insertions(+), 23 deletions(-)

diff --git a/drivers/spi/zynqmp_gqspi.c b/drivers/spi/zynqmp_gqspi.c
index 78a1b48731..d5ccb48fde 100644
--- a/drivers/spi/zynqmp_gqspi.c
+++ b/drivers/spi/zynqmp_gqspi.c
@@ -22,6 +22,7 @@
 #include <dm/device_compat.h>
 #include <linux/bitops.h>
 #include <linux/err.h>
+#include <linux/sizes.h>
 #include <zynqmp_firmware.h>
 
 #define GQSPI_GFIFO_STRT_MODE_MASK	BIT(29)
@@ -666,38 +667,53 @@ static int zynqmp_qspi_start_dma(struct zynqmp_qspi_priv *priv,
 	u32 addr;
 	u32 size;
 	u32 actuallen = priv->len;
+	u32 totallen = priv->len;
 	int ret = 0;
 	struct zynqmp_qspi_dma_regs *dma_regs = priv->dma_regs;
 
-	writel((unsigned long)buf, &dma_regs->dmadst);
-	writel(roundup(priv->len, GQSPI_DMA_ALIGN), &dma_regs->dmasize);
-	writel(GQSPI_DMA_DST_I_STS_MASK, &dma_regs->dmaier);
-	addr = (unsigned long)buf;
-	size = roundup(priv->len, GQSPI_DMA_ALIGN);
-	flush_dcache_range(addr, addr + size);
+	while (totallen) {
+		if (totallen >= SZ_512M)
+			priv->len = SZ_256M;
+		else
+			priv->len = totallen;
 
-	while (priv->len) {
-		zynqmp_qspi_calc_exp(priv, &gen_fifo_cmd);
-		zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd);
+		totallen -= priv->len; /* Save remaining bytes length to read */
+		actuallen = priv->len; /* Actual number of bytes reading */
 
-		debug("GFIFO_CMD_RX:0x%x\n", gen_fifo_cmd);
-	}
+		writel((unsigned long)buf, &dma_regs->dmadst);
+		writel(roundup(priv->len, GQSPI_DMA_ALIGN), &dma_regs->dmasize);
+		writel(GQSPI_DMA_DST_I_STS_MASK, &dma_regs->dmaier);
+		addr = (unsigned long)buf;
+		size = roundup(priv->len, GQSPI_DMA_ALIGN);
+		flush_dcache_range(addr, addr + size);
 
-	ret = wait_for_bit_le32(&dma_regs->dmaisr, GQSPI_DMA_DST_I_STS_DONE,
-				1, GQSPI_TIMEOUT, 1);
-	if (ret) {
-		printf("DMA Timeout:0x%x\n", readl(&dma_regs->dmaisr));
-		return -ETIMEDOUT;
-	}
+		while (priv->len) {
+			zynqmp_qspi_calc_exp(priv, &gen_fifo_cmd);
+			zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd);
+
+			debug("GFIFO_CMD_RX:0x%x\n", gen_fifo_cmd);
+		}
+
+		ret = wait_for_bit_le32(&dma_regs->dmaisr,
+					GQSPI_DMA_DST_I_STS_DONE, 1,
+					GQSPI_TIMEOUT, 1);
+		if (ret) {
+			printf("DMA Timeout:0x%x\n", readl(&dma_regs->dmaisr));
+			return -ETIMEDOUT;
+		}
+
+		writel(GQSPI_DMA_DST_I_STS_DONE, &dma_regs->dmaisr);
 
-	writel(GQSPI_DMA_DST_I_STS_DONE, &dma_regs->dmaisr);
+		debug("buf:0x%lx, rxbuf:0x%lx, *buf:0x%x len: 0x%x\n",
+		      (unsigned long)buf, (unsigned long)priv->rx_buf, *buf,
+		      actuallen);
 
-	debug("buf:0x%lx, rxbuf:0x%lx, *buf:0x%x len: 0x%x\n",
-	      (unsigned long)buf, (unsigned long)priv->rx_buf, *buf,
-	      actuallen);
+		if (buf != priv->rx_buf)
+			memcpy(priv->rx_buf, buf, actuallen);
 
-	if (buf != priv->rx_buf)
-		memcpy(priv->rx_buf, buf, actuallen);
+		buf = (u32 *)((u8 *)buf + actuallen);
+		priv->rx_buf = (u8 *)priv->rx_buf + actuallen;
+	}
 
 	return 0;
 }
-- 
2.17.1



More information about the U-Boot mailing list