]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
spi: zynqmp_gqspi: Fix issue of reading more than 32bits length
authorAshok Reddy Soma <ashok.reddy.soma@amd.com>
Thu, 25 Aug 2022 12:59:04 +0000 (06:59 -0600)
committerMichal Simek <michal.simek@amd.com>
Tue, 13 Sep 2022 09:32:48 +0000 (11:32 +0200)
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@amd.com>
Link: https://lore.kernel.org/r/20220825125906.11581-5-ashok.reddy.soma@amd.com
Signed-off-by: Michal Simek <michal.simek@amd.com>
drivers/spi/zynqmp_gqspi.c

index 78a1b487311063e8a48f8aeda0aa3eeacaa43cfb..d5ccb48fde779fa5d9a5208e8cea133077455ca3 100644 (file)
@@ -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;
 }