]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
spi: cadence-xspi: Support 32bit and 64bit slave dma interface
authorJisheng Zhang <jszhang@kernel.org>
Tue, 2 Jun 2026 23:58:25 +0000 (07:58 +0800)
committerMark Brown <broonie@kernel.org>
Thu, 4 Jun 2026 10:27:44 +0000 (11:27 +0100)
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>
drivers/spi/spi-cadence-xspi.c

index c45b29c043bf30b2303272eb62e760b611d06b16..e2bfb0c78b82c77c94b755a0cab006723954e8f8 100644 (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;
        }
 }