spi: cadence-xspi: Support 32bit and 64bit slave dma interface

The cdns xspi controller slave dma interface may support wider data
width. Wider I/O width can benefit performance. We can know the width
by checking the CTRL_FEATURES_REG's DMA_DATA_WIDTH bit, 0 means 32bit
1 means 64bit.

A simple test with QSPI nor flash on one arm64 platform:

Use 8bit slave dma data width (now):
 # dd if=/dev/mtdblock0 of=/dev/null bs=8192 count=1000
 1000+0 records in
 1000+0 records out
 8192000 bytes (7.8MB) copied, 1.368735 seconds, 5.7MB/s

Use 32bit slave dma data width:
 # dd if=/dev/mtdblock0 of=/dev/null bs=8192 count=1000
 1000+0 records in
 1000+0 records out
 8192000 bytes (7.8MB) copied, 1.088787 seconds, 7.2MB/s

Improved by 26.3%!

Use 64bit slave dma data width:
 # dd if=/dev/mtdblock0 of=/dev/null bs=8192 count=1000
 1000+0 records in
 1000+0 records out
 8192000 bytes (7.8MB) copied, 0.831104 seconds, 9.4MB/s

Improved by 64.9%!

Signed-off-by: Jisheng Zhang <jszhang@kernel.org>
Link: https://patch.msgid.link/20260602235825.28614-1-jszhang@kernel.org
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Jisheng Zhang
2026-06-03 07:58:25 +08:00
committed by Mark Brown
parent f469138a77
commit 4954d4eca4

View File

@@ -373,6 +373,8 @@ struct cdns_xspi_dev {
void *in_buffer;
const void *out_buffer;
/* Slave DMA data width in bytes (4 or 8). */
u8 dma_data_width;
u8 hw_num_banks;
@@ -576,11 +578,56 @@ static int cdns_xspi_controller_init(struct cdns_xspi_dev *cdns_xspi)
ctrl_features = readl(cdns_xspi->iobase + CDNS_XSPI_CTRL_FEATURES_REG);
cdns_xspi->hw_num_banks = FIELD_GET(CDNS_XSPI_NUM_BANKS, ctrl_features);
cdns_xspi->dma_data_width = (ctrl_features & CDNS_XSPI_DMA_DATA_WIDTH) ? 8 : 4;
cdns_xspi->set_interrupts_handler(cdns_xspi, false);
return 0;
}
static inline void cdns_xspi_sdma_read(struct cdns_xspi_dev *cdns_xspi, size_t len)
{
void __iomem *src = cdns_xspi->sdmabase;
void *buf = cdns_xspi->in_buffer;
size_t offset = 0;
if (cdns_xspi->dma_data_width == 4) {
if (IS_ALIGNED((uintptr_t)src, 4) && IS_ALIGNED((uintptr_t)buf, 4)) {
ioread32_rep(src, buf, len >> 2);
offset = len & ~0x3;
len -= offset;
}
} else {
if (IS_ALIGNED((uintptr_t)src, 8) && IS_ALIGNED((uintptr_t)buf, 8)) {
readsq(src, buf, len >> 3);
offset = len & ~0x7;
len -= offset;
}
}
ioread8_rep(src, (u8 *)buf + offset, len);
}
static inline void cdns_xspi_sdma_write(struct cdns_xspi_dev *cdns_xspi, size_t len)
{
void __iomem *dst = cdns_xspi->sdmabase;
const void *buf = cdns_xspi->out_buffer;
size_t offset = 0;
if (cdns_xspi->dma_data_width == 4) {
if (IS_ALIGNED((uintptr_t)dst, 4) && IS_ALIGNED((uintptr_t)buf, 4)) {
iowrite32_rep(dst, buf, len >> 2);
offset = len & ~0x3;
len -= offset;
}
} else {
if (IS_ALIGNED((uintptr_t)dst, 8) && IS_ALIGNED((uintptr_t)buf, 8)) {
writesq(dst, buf, len >> 3);
offset = len & ~0x7;
len -= offset;
}
}
iowrite8_rep(dst, (const u8 *)buf + offset, len);
}
static void cdns_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi)
{
u32 sdma_size, sdma_trd_info;
@@ -592,13 +639,11 @@ static void cdns_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi)
switch (sdma_dir) {
case CDNS_XSPI_SDMA_DIR_READ:
ioread8_rep(cdns_xspi->sdmabase,
cdns_xspi->in_buffer, sdma_size);
cdns_xspi_sdma_read(cdns_xspi, sdma_size);
break;
case CDNS_XSPI_SDMA_DIR_WRITE:
iowrite8_rep(cdns_xspi->sdmabase,
cdns_xspi->out_buffer, sdma_size);
cdns_xspi_sdma_write(cdns_xspi, sdma_size);
break;
}
}