spi: spi-mem: rtl-snand: Correctly handle DMA transfers
authorChris Packham <chris.packham@alliedtelesis.co.nz>
Wed, 30 Oct 2024 19:49:20 +0000 (08:49 +1300)
committerMark Brown <broonie@kernel.org>
Fri, 1 Nov 2024 14:48:53 +0000 (14:48 +0000)
The RTL9300 has some limitations on the maximum DMA transfers possible.
For reads this is 2080 bytes (520*4) for writes this is 520 bytes. Deal
with this by splitting transfers into appropriately sized parts.

Fixes: 42d20a6a61b8 ("spi: spi-mem: Add Realtek SPI-NAND controller")
Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
Link: https://patch.msgid.link/20241030194920.3202282-1-chris.packham@alliedtelesis.co.nz
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/spi/spi-realtek-rtl-snand.c

index 23c42c8..cd04840 100644 (file)
@@ -231,19 +231,22 @@ out_deselect:
 
 static int rtl_snand_dma_xfer(struct rtl_snand *snand, int cs, const struct spi_mem_op *op)
 {
+       unsigned int pos, nbytes;
        int ret;
        dma_addr_t buf_dma;
        enum dma_data_direction dir;
-       u32 trig;
+       u32 trig, len, maxlen;
 
        ret = rtl_snand_xfer_head(snand, cs, op);
        if (ret)
                goto out_deselect;
 
        if (op->data.dir == SPI_MEM_DATA_IN) {
+               maxlen = 2080;
                dir = DMA_FROM_DEVICE;
                trig = 0;
        } else if (op->data.dir == SPI_MEM_DATA_OUT) {
+               maxlen = 520;
                dir = DMA_TO_DEVICE;
                trig = 1;
        } else {
@@ -264,26 +267,37 @@ static int rtl_snand_dma_xfer(struct rtl_snand *snand, int cs, const struct spi_
        if (ret)
                goto out_unmap;
 
-       reinit_completion(&snand->comp);
+       pos = 0;
+       len = op->data.nbytes;
 
-       ret = regmap_write(snand->regmap, SNAFDRSAR, buf_dma);
-       if (ret)
-               goto out_disable_int;
+       while (pos < len) {
+               nbytes = len - pos;
+               if (nbytes > maxlen)
+                       nbytes = maxlen;
 
-       ret = regmap_write(snand->regmap, SNAFDLR,
-                          CMR_WID(op->data.buswidth) | (op->data.nbytes & 0xffff));
-       if (ret)
-               goto out_disable_int;
+               reinit_completion(&snand->comp);
 
-       ret = regmap_write(snand->regmap, SNAFDTR, trig);
-       if (ret)
-               goto out_disable_int;
+               ret = regmap_write(snand->regmap, SNAFDRSAR, buf_dma + pos);
+               if (ret)
+                       goto out_disable_int;
 
-       if (!wait_for_completion_timeout(&snand->comp, usecs_to_jiffies(20000)))
-               ret = -ETIMEDOUT;
+               pos += nbytes;
 
-       if (ret)
-               goto out_disable_int;
+               ret = regmap_write(snand->regmap, SNAFDLR,
+                               CMR_WID(op->data.buswidth) | nbytes);
+               if (ret)
+                       goto out_disable_int;
+
+               ret = regmap_write(snand->regmap, SNAFDTR, trig);
+               if (ret)
+                       goto out_disable_int;
+
+               if (!wait_for_completion_timeout(&snand->comp, usecs_to_jiffies(20000)))
+                       ret = -ETIMEDOUT;
+
+               if (ret)
+                       goto out_disable_int;
+       }
 
 out_disable_int:
        regmap_update_bits(snand->regmap, SNAFCFR, SNAFCFR_DMA_IE, 0);