diff options
author | Pratyush Yadav <p.yadav@ti.com> | 2024-03-09 01:33:52 +0530 |
---|---|---|
committer | Praneeth Bajjuri <praneeth@ti.com> | 2024-03-11 13:51:58 -0500 |
commit | e29174df9a8042a26e8898bc46d12961174e9229 (patch) | |
tree | 123715f76d8759e8e2f93e6891d7ff2a2acef36f | |
parent | 2d7226230b41a3f722a2b7951d220a906257a320 (diff) |
spi: cadence-quadspi: do not use memcpy_fromio() on 8D-8D-8D ops
In 8D-8D-8D mode an odd number of bytes cannot be read from the flash
since they would result in half a cycle being left over. memcpy_fromio()
makes no guarantees of access width size. On arm64 it is a mix of 1-byte
and 8-bytes accesses. On arm it is just 1-byte accesses.
memcpy_fromio() cannot be trusted when 8D-8D-8D reads are involved.
Instead, explicitly perfrom memcpy from the IO space for 8D-8D-8D ops by
making sure no odd-length accesses are performed. Since this controller
can be used on both arm and arm64 platforms, only 4-byte reads are used
to make sure the same code works on both platforms.
Signed-off-by: Pratyush Yadav <p.yadav@ti.com>
Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
Signed-off-by: Apurva Nandan <a-nandan@ti.com>
-rw-r--r-- | drivers/spi/spi-cadence-quadspi.c | 42 |
1 files changed, 41 insertions, 1 deletions
diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index b81a60581889..7b89bcf20960 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -2011,6 +2011,46 @@ err_unmap: return ret; } +static void cqspi_memcpy_fromio(const struct spi_mem_op *op, void *to, + const void __iomem *from, size_t count) +{ + if (op->data.buswidth == 8 && op->data.dtr) { + /* + * 8D-8D-8D ops with odd length should be rejected by + * supports_op() so no need to worry about that. + */ + while (count && !IS_ALIGNED((unsigned long)from, 4)) { + *(u16 *)to = __raw_readw(from); + from += 2; + to += 2; + count -= 2; + } + + /* + * The controller can work with both 32-bit and 64-bit + * platforms. 32-bit platforms won't have a readq. So use a + * readl instead. + */ + while (count >= 4) { + *(u32 *)to = __raw_readl(from); + from += 4; + to += 4; + count -= 4; + } + + while (count) { + *(u16 *)to = __raw_readw(from); + from += 2; + to += 2; + count -= 2; + } + + return; + } + + memcpy_fromio(to, from, count); +} + static int cqspi_direct_read_execute(struct cqspi_flash_pdata *f_pdata, const struct spi_mem_op *op) { @@ -2023,7 +2063,7 @@ static int cqspi_direct_read_execute(struct cqspi_flash_pdata *f_pdata, int ret; if (!cqspi->rx_chan || !virt_addr_valid(buf)) { - memcpy_fromio(buf, cqspi->ahb_base + from, len); + cqspi_memcpy_fromio(op, buf, cqspi->ahb_base + from, len); return 0; } |