diff options
author | Chris Packham <chris.packham@alliedtelesis.co.nz> | 2024-10-31 08:49:20 +1300 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2024-11-01 14:48:53 +0000 |
commit | 25d284715845a465a1a3693a09cf8b6ab8bd9caf (patch) | |
tree | a560a9bb1a13c71cc97781d3013b985aaf9505d7 | |
parent | f399051ec1ff02e74ae5c2517aed2cc486fd005b (diff) |
spi: spi-mem: rtl-snand: Correctly handle DMA transfers
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>
-rw-r--r-- | drivers/spi/spi-realtek-rtl-snand.c | 46 |
1 files changed, 30 insertions, 16 deletions
diff --git a/drivers/spi/spi-realtek-rtl-snand.c b/drivers/spi/spi-realtek-rtl-snand.c index 23c42c8469e4..cd0484041147 100644 --- a/drivers/spi/spi-realtek-rtl-snand.c +++ b/drivers/spi/spi-realtek-rtl-snand.c @@ -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); |