[RFC PATCH 2/4] spi: cadence-xspi: add ACMD support for SPI NAND

Fei Xie fei.xie at horizon.auto
Mon Sep 21 02:36:59 PDT 2026


From: "fei.xie" <fei.xie at horizon.auto>

Add PIO master-DMA support for the controller auto-command mode.
Configure the SPI NAND read, program, erase, status and reset
sequences, retaining STIG for operations not consumed by ACMD.

Signed-off-by: fei.xie <fei.xie at horizon.auto>
---
 drivers/spi/spi-cadence-xspi.c | 782 +++++++++++++++++++++++++++++++++
 1 file changed, 782 insertions(+)

diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c
index 39c868a5b171c..0fedf6cd6a590 100644
--- a/drivers/spi/spi-cadence-xspi.c
+++ b/drivers/spi/spi-cadence-xspi.c
@@ -4,6 +4,7 @@
 
 #include <linux/completion.h>
 #include <linux/delay.h>
+#include <linux/dma-mapping.h>
 #include <linux/err.h>
 #include <linux/errno.h>
 #include <linux/interrupt.h>
@@ -11,6 +12,8 @@
 #include <linux/iopoll.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/mtd/spinand.h>
+#include <linux/overflow.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/property.h>
@@ -54,7 +57,33 @@
 #define CDNS_XSPI_CMD_REG_4			0x0010
 #define CDNS_XSPI_CMD_REG_5			0x0014
 
+/* Auto command fields in command register 0 */
+#define CDNS_XSPI_ACMD_MODE			GENMASK(31, 30)
+#define CDNS_XSPI_ACMD_TRD_NUM			GENMASK(26, 24)
+#define CDNS_XSPI_ACMD_BANK_NUM			GENMASK(22, 20)
+#define CDNS_XSPI_ACMD_DMA_SEL			BIT(19)
+#define CDNS_XSPI_ACMD_INT_EN			BIT(18)
+#define CDNS_XSPI_ACMD_CMD_TYPE			GENMASK(15, 0)
+
+#define CDNS_XSPI_ACMD_READ_OP			0x2200
+#define CDNS_XSPI_ACMD_PROG_OP			0x2100
+#define CDNS_XSPI_ACMD_ERASE_OP			0x1000
+#define CDNS_XSPI_ACMD_RESET_OP			0x1100
+#define CDNS_XSPI_ACMD_DATA_THREAD		7
+#define CDNS_XSPI_ACMD_ERASE_THREAD		5
+#define CDNS_XSPI_ACMD_TIMEOUT_MS		1000
+#define CDNS_XSPI_ACMD_MODE_PIO			1
+
+#define CDNS_XSPI_NAND_OP_GET_FEATURE		0x0f
+#define CDNS_XSPI_NAND_OP_WRITE_ENABLE		0x06
+#define CDNS_XSPI_NAND_OP_PROGRAM_EXECUTE	0x10
+#define CDNS_XSPI_NAND_OP_PAGE_READ		0x13
+#define CDNS_XSPI_NAND_OP_BLOCK_ERASE		0xd8
+#define CDNS_XSPI_NAND_OP_RESET			0xff
+#define CDNS_XSPI_NAND_STATUS_REG		0xc0
+
 /* Command status registers */
+#define CDNS_XSPI_CMD_STATUS_PTR_REG	0x0040
 #define CDNS_XSPI_CMD_STATUS_REG		0x0044
 
 /* Controller status register */
@@ -143,10 +172,126 @@
 #define CDNS_XSPI_CMD_STATUS_CRC_ERROR		BIT(2)
 #define CDNS_XSPI_CMD_STATUS_BUS_ERROR		BIT(1)
 #define CDNS_XSPI_CMD_STATUS_INV_SEQ_ERROR	BIT(0)
+/* Reset sequence config register */
+#define CDNS_XSPI_RST_SEQ_CFG_0			0x0400
+#define CDNS_XSPI_RST_SEQ_P1_CMD1_VAL		GENMASK(15, 8)
+
+/* Erase sequence config registers */
+#define CDNS_XSPI_ERSS_SEQ_CFG_0			0x0410
+#define CDNS_XSPI_ERSS_SEQ_P1_CMD_VAL		GENMASK(7, 0)
+#define CDNS_XSPI_ERSS_SEQ_P1_CMD_IOS		GENMASK(9, 8)
+#define CDNS_XSPI_ERSS_SEQ_P1_CMD_EDGE		BIT(11)
+#define CDNS_XSPI_ERSS_SEQ_P1_ADDR_CNT		GENMASK(14, 12)
+#define CDNS_XSPI_ERSS_SEQ_P1_CMD_EXT_EN	BIT(15)
+#define CDNS_XSPI_ERSS_SEQ_P1_CMD_EXT_VAL	GENMASK(23, 16)
+#define CDNS_XSPI_ERSS_SEQ_P1_ADDR_IOS		GENMASK(25, 24)
+#define CDNS_XSPI_ERSS_SEQ_P1_ADDR_EDGE		BIT(28)
+
+#define CDNS_XSPI_ERSS_SEQ_CFG_1			0x0414
+#define CDNS_XSPI_ERSS_SEQ_P1_SECT_SIZE		GENMASK(4, 0)
+
+#define CDNS_XSPI_ERSS_SEQ_CFG_2			0x0418
+
+/* Program sequence config registers */
+#define CDNS_XSPI_PROG_SEQ_CFG_0			0x0420
+#define CDNS_XSPI_PROG_SEQ_P1_CMD_VAL		GENMASK(7, 0)
+#define CDNS_XSPI_PROG_SEQ_P1_CMD_IOS		GENMASK(9, 8)
+#define CDNS_XSPI_PROG_SEQ_P1_CMD_EDGE		BIT(11)
+#define CDNS_XSPI_PROG_SEQ_P1_ADDR_CNT		GENMASK(14, 12)
+#define CDNS_XSPI_PROG_SEQ_P1_ADDR_IOS		GENMASK(17, 16)
+#define CDNS_XSPI_PROG_SEQ_P1_ADDR_EDGE		BIT(19)
+#define CDNS_XSPI_PROG_SEQ_P1_DATA_IOS		GENMASK(21, 20)
+#define CDNS_XSPI_PROG_SEQ_P1_DATA_EDGE		BIT(23)
+#define CDNS_XSPI_PROG_SEQ_P1_DUMMY_CNT		GENMASK(29, 24)
+
+#define CDNS_XSPI_PROG_SEQ_CFG_1			0x0424
+#define CDNS_XSPI_PROG_SEQ_P1_CMD_EXT_EN		BIT(0)
+#define CDNS_XSPI_PROG_SEQ_P1_CMD_EXT_VAL		GENMASK(15, 8)
+
+#define CDNS_XSPI_PROG_SEQ_CFG_2			0x0428
+
+/* Read sequence config registers */
+#define CDNS_XSPI_READ_SEQ_CFG_0			0x0430
+#define CDNS_XSPI_READ_SEQ_P1_CMD_VAL		GENMASK(7, 0)
+#define CDNS_XSPI_READ_SEQ_P1_CMD_IOS		GENMASK(9, 8)
+#define CDNS_XSPI_READ_SEQ_P1_CMD_EDGE		BIT(11)
+#define CDNS_XSPI_READ_SEQ_P1_ADDR_CNT		GENMASK(14, 12)
+#define CDNS_XSPI_READ_SEQ_P1_ADDR_IOS		GENMASK(17, 16)
+#define CDNS_XSPI_READ_SEQ_P1_ADDR_EDGE		BIT(19)
+#define CDNS_XSPI_READ_SEQ_P1_DATA_IOS		GENMASK(21, 20)
+#define CDNS_XSPI_READ_SEQ_P1_DATA_EDGE		BIT(23)
+#define CDNS_XSPI_READ_SEQ_P1_DUMMY_CNT		GENMASK(29, 24)
+
+#define CDNS_XSPI_READ_SEQ_CFG_1			0x0434
+#define CDNS_XSPI_READ_SEQ_P1_CMD_EXT_EN		BIT(0)
+#define CDNS_XSPI_READ_SEQ_P1_CACHE_RANDOM_READ_EN	BIT(4)
+#define CDNS_XSPI_READ_SEQ_CFG_2			0x0438
+
+/* Write enable sequence config register */
+#define CDNS_XSPI_WE_SEQ_CFG_0			0x0440
+#define CDNS_XSPI_WE_SEQ_P1_CMD_VAL		GENMASK(7, 0)
+#define CDNS_XSPI_WE_SEQ_P1_CMD_IOS		GENMASK(9, 8)
+#define CDNS_XSPI_WE_SEQ_P1_CMD_EDGE		BIT(11)
+#define CDNS_XSPI_WE_SEQ_P1_EN			BIT(24)
+
+/* Status sequence config registers */
+#define CDNS_XSPI_STAT_SEQ_CFG_0			0x0450
+#define CDNS_XSPI_STAT_SEQ_P1_ADDR_CNT			GENMASK(9, 8)
+#define CDNS_XSPI_STAT_SEQ_CFG_1			0x0454
+#define CDNS_XSPI_P1_DEV_RDY_ADDR_EN		BIT(6)
+#define CDNS_XSPI_P1_PROG_FAIL_ADDR_EN		BIT(22)
+#define CDNS_XSPI_P1_ERS_FAIL_ADDR_EN		BIT(30)
+
+#define CDNS_XSPI_STAT_SEQ_CFG_2			0x0458
+#define CDNS_XSPI_STAT_SEQ_P1_DEV_RDY_CMD_VAL	GENMASK(7, 0)
+#define CDNS_XSPI_STAT_SEQ_P1_ERS_FAIL_CMD_VAL	GENMASK(15, 8)
+#define CDNS_XSPI_STAT_SEQ_P1_PROG_FAIL_CMD_VAL	GENMASK(31, 24)
+
+#define CDNS_XSPI_STAT_SEQ_CFG_3			0x045c
+#define CDNS_XSPI_STAT_SEQ_CFG_4			0x0460
+#define CDNS_XSPI_STAT_SEQ_CFG_5			0x0464
+#define CDNS_XSPI_STAT_SEQ_DEV_RDY_IDX		GENMASK(3, 0)
+#define CDNS_XSPI_STAT_SEQ_DEV_RDY_EN		BIT(6)
+#define CDNS_XSPI_STAT_SEQ_ERS_FAIL_IDX		GENMASK(11, 8)
+#define CDNS_XSPI_STAT_SEQ_ERS_FAIL_VAL		BIT(12)
+#define CDNS_XSPI_STAT_SEQ_ERS_FAIL_EN		BIT(14)
+#define CDNS_XSPI_STAT_SEQ_PROG_FAIL_IDX		GENMASK(27, 24)
+#define CDNS_XSPI_STAT_SEQ_PROG_FAIL_VAL		BIT(28)
+#define CDNS_XSPI_STAT_SEQ_PROG_FAIL_EN			BIT(30)
+
+#define CDNS_XSPI_STAT_SEQ_CFG_7			0x046c
+#define CDNS_XSPI_STAT_SEQ_CFG_8			0x0470
+#define CDNS_XSPI_STAT_SEQ_CFG_9			0x0474
+
+#define CDNS_XSPI_STAT_SEQ_CFG_10		0x0478
+#define CDNS_XSPI_STAT_SEQ_ECC_FAIL_EN		BIT(31)
+#define CDNS_XSPI_STAT_SEQ_CRDY_VAL		BIT(27)
+#define CDNS_XSPI_STAT_SEQ_CRDY_IDX		GENMASK(26, 24)
+#define CDNS_XSPI_STAT_SEQ_ECC_CORR_VAL		GENMASK(23, 16)
+#define CDNS_XSPI_STAT_SEQ_ECC_FAIL_VAL		GENMASK(15, 8)
+#define CDNS_XSPI_STAT_SEQ_ECC_FAIL_MASK		GENMASK(7, 0)
 
 #define CDNS_XSPI_STIG_DONE_FLAG		BIT(0)
 #define CDNS_XSPI_TRD_STATUS			0x0104
 
+#define CDNS_XSPI_FLASH_TYPE_NOR		0
+#define CDNS_XSPI_FLASH_TYPE_NAND		1
+
+#define CDNS_XSPI_GLOBAL_SEQ_CFG			0x0390
+#define CDNS_XSPI_SEQ_TYPE			GENMASK(24, 23)
+#define CDNS_XSPI_SEQ_PAGE_SIZE_PGM		GENMASK(7, 4)
+#define CDNS_XSPI_SEQ_PAGE_SIZE_RD		GENMASK(3, 0)
+#define CDNS_XSPI_SEQ_SPI_NAND			3
+
+#define CDNS_XSPI_GLOBAL_SEQ_CFG_1		0x0394
+#define CDNS_XSPI_SEQ_PLANE_CNT			GENMASK(29, 28)
+#define CDNS_XSPI_SEQ_PAGE_PER_BLOCK		GENMASK(26, 24)
+#define CDNS_XSPI_SEQ_PAGE_CA_SIZE		BIT(16)
+#define CDNS_XSPI_SEQ_PAGE_SIZE_EXT		GENMASK(8, 0)
+
+#define CDNS_XSPI_XIP_MODE_CFG			0x0388
+#define CDNS_XSPI_XIP_EN			BIT(0)
+
 #define MODE_NO_OF_BYTES			GENMASK(25, 24)
 #define MODEBYTES_COUNT			1
 
@@ -305,6 +450,7 @@ enum cdns_xspi_stig_cmd_dir {
 
 struct cdns_xspi_driver_data {
 	bool mrvl_hw_overlay;
+	bool use_acmd;
 	u32 dll_phy_ctrl;
 	u32 ctb_rfile_phy_ctrl;
 	u32 rfile_phy_tsel;
@@ -313,12 +459,26 @@ struct cdns_xspi_driver_data {
 	u32 rfile_phy_gate_lpbk_ctrl;
 	u32 rfile_phy_dll_master_ctrl;
 	u32 rfile_phy_dll_slave_ctrl;
+	u8 flash_type;
 };
 
 static struct cdns_xspi_driver_data cdns_driver_data = {
 	.mrvl_hw_overlay = false,
 };
 
+static struct cdns_xspi_driver_data cdns_nand_driver_data = {
+	.mrvl_hw_overlay = false,
+	.use_acmd = true,
+	.flash_type = CDNS_XSPI_FLASH_TYPE_NAND,
+};
+
+struct cdns_xspi_acmd_info {
+	u64 row_addr;
+	u64 column_addr;
+	size_t data_nbytes;
+	bool row_addr_valid;
+	bool initialized;
+};
 struct cdns_xspi_dev {
 	struct platform_device *pdev;
 	struct spi_controller *host;
@@ -351,6 +511,13 @@ struct cdns_xspi_dev {
 
 	bool xfer_in_progress;
 	int current_xfer_qword;
+	u32 work_mode;
+	u8 flash_type;
+
+	struct cdns_xspi_acmd_info acmd_info;
+	void *dma_buf;
+	dma_addr_t dma_addr;
+	u32 dma_buf_len;
 };
 
 static int cdns_xspi_wait_for_controller_idle(struct cdns_xspi_dev *cdns_xspi)
@@ -425,6 +592,268 @@ static void cdns_xspi_set_interrupts(struct cdns_xspi_dev *cdns_xspi,
 	writel(intr_enable, cdns_xspi->iobase + CDNS_XSPI_INTR_ENABLE_REG);
 }
 
+static void cdns_xspi_nand_cfg_seq_init(struct cdns_xspi_dev *cdns_xspi,
+					struct spinand_device *spinand)
+{
+	u32 seq_cfg, seq_cfg1;
+
+	seq_cfg = readl(cdns_xspi->iobase + CDNS_XSPI_GLOBAL_SEQ_CFG);
+	seq_cfg1 = readl(cdns_xspi->iobase + CDNS_XSPI_GLOBAL_SEQ_CFG_1);
+	seq_cfg = u32_replace_bits(seq_cfg, CDNS_XSPI_SEQ_SPI_NAND,
+				   CDNS_XSPI_SEQ_TYPE);
+	seq_cfg = u32_replace_bits(seq_cfg,
+				   ilog2(spinand->base.memorg.pagesize),
+				   CDNS_XSPI_SEQ_PAGE_SIZE_PGM);
+	seq_cfg = u32_replace_bits(seq_cfg,
+				   ilog2(spinand->base.memorg.pagesize),
+				   CDNS_XSPI_SEQ_PAGE_SIZE_RD);
+	seq_cfg1 = u32_replace_bits(seq_cfg1,
+				    ilog2(spinand->base.memorg.planes_per_lun),
+				    CDNS_XSPI_SEQ_PLANE_CNT);
+	seq_cfg1 = u32_replace_bits(seq_cfg1,
+				    ilog2(spinand->base.memorg.pages_per_eraseblock),
+				    CDNS_XSPI_SEQ_PAGE_PER_BLOCK);
+	seq_cfg1 = u32_replace_bits(seq_cfg1,
+				    !!(spinand->base.memorg.pagesize & BIT(12)),
+				    CDNS_XSPI_SEQ_PAGE_CA_SIZE);
+	seq_cfg1 = u32_replace_bits(seq_cfg1, spinand->base.memorg.oobsize,
+				    CDNS_XSPI_SEQ_PAGE_SIZE_EXT);
+
+	writel(seq_cfg, cdns_xspi->iobase + CDNS_XSPI_GLOBAL_SEQ_CFG);
+	writel(seq_cfg1, cdns_xspi->iobase + CDNS_XSPI_GLOBAL_SEQ_CFG_1);
+}
+
+static void cdns_xspi_nand_read_seq_init(struct cdns_xspi_dev *cdns_xspi,
+					 struct spinand_device *spinand)
+{
+	const struct spi_mem_op *op = spinand->op_templates->read_cache;
+	u32 dummy_cycles = 0;
+	u32 read_seq_cfg0;
+	u32 read_seq_cfg1;
+
+	if (op->dummy.nbytes && op->dummy.buswidth)
+		dummy_cycles = op->dummy.nbytes * BITS_PER_BYTE /
+			       (op->dummy.buswidth * (op->dummy.dtr + 1));
+
+	read_seq_cfg0 =
+		FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_CMD_VAL, op->cmd.opcode) |
+		FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_CMD_IOS,
+			   ilog2(op->cmd.buswidth)) |
+		FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_CMD_EDGE, op->cmd.dtr) |
+		FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_ADDR_CNT,
+			   op->addr.nbytes) |
+		FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_ADDR_IOS,
+			   ilog2(op->addr.buswidth)) |
+		FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_ADDR_EDGE, op->addr.dtr) |
+		FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_DATA_IOS,
+			   ilog2(op->data.buswidth)) |
+		FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_DATA_EDGE, op->data.dtr) |
+		FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_DUMMY_CNT, dummy_cycles);
+	read_seq_cfg1 = FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_CMD_EXT_EN,
+				   op->cmd.nbytes > 1) |
+		CDNS_XSPI_READ_SEQ_P1_CACHE_RANDOM_READ_EN;
+
+	writel(read_seq_cfg0, cdns_xspi->iobase + CDNS_XSPI_READ_SEQ_CFG_0);
+	writel(read_seq_cfg1, cdns_xspi->iobase + CDNS_XSPI_READ_SEQ_CFG_1);
+	writel(0, cdns_xspi->iobase + CDNS_XSPI_READ_SEQ_CFG_2);
+
+	dev_dbg(cdns_xspi->dev,
+		"ACMD read: op=%02x %u-%u-%u%s addr=%u dummy=%u cfg=%08x/%08x\n",
+		op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth,
+		op->data.buswidth, op->data.dtr ? " DTR" : " SDR",
+		op->addr.nbytes, dummy_cycles, read_seq_cfg0, read_seq_cfg1);
+}
+
+static void cdns_xspi_nand_write_seq_init(struct cdns_xspi_dev *cdns_xspi,
+					  struct spinand_device *spinand)
+{
+	const struct spi_mem_op *op = spinand->op_templates->write_cache;
+	u32 dummy_cycles = 0;
+	u32 write_seq_cfg0;
+	u32 write_seq_cfg1;
+
+	if (op->dummy.nbytes && op->dummy.buswidth)
+		dummy_cycles = op->dummy.nbytes * BITS_PER_BYTE /
+			       (op->dummy.buswidth * (op->dummy.dtr + 1));
+
+	write_seq_cfg0 =
+		FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_CMD_VAL, op->cmd.opcode) |
+		FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_CMD_IOS,
+			   ilog2(op->cmd.buswidth)) |
+		FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_CMD_EDGE, op->cmd.dtr) |
+		FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_ADDR_CNT,
+			   op->addr.nbytes) |
+		FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_ADDR_IOS,
+			   ilog2(op->addr.buswidth)) |
+		FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_ADDR_EDGE, op->addr.dtr) |
+		FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_DATA_IOS,
+			   ilog2(op->data.buswidth)) |
+		FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_DATA_EDGE, op->data.dtr) |
+		FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_DUMMY_CNT, dummy_cycles);
+	write_seq_cfg1 = FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_CMD_EXT_EN,
+				    op->cmd.nbytes > 1);
+
+	writel(write_seq_cfg0, cdns_xspi->iobase + CDNS_XSPI_PROG_SEQ_CFG_0);
+	writel(write_seq_cfg1, cdns_xspi->iobase + CDNS_XSPI_PROG_SEQ_CFG_1);
+	writel(0, cdns_xspi->iobase + CDNS_XSPI_PROG_SEQ_CFG_2);
+
+	dev_dbg(cdns_xspi->dev,
+		"ACMD program: op=%02x %u-%u-%u%s addr=%u dummy=%u cfg=%08x/%08x\n",
+		op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth,
+		op->data.buswidth, op->data.dtr ? " DTR" : " SDR",
+		op->addr.nbytes, dummy_cycles, write_seq_cfg0, write_seq_cfg1);
+}
+
+static void cdns_xspi_nand_erase_seq_init(struct cdns_xspi_dev *cdns_xspi,
+					  struct spinand_device *spinand)
+{
+	u32 erase_seq_cfg0;
+	u32 erase_seq_cfg1;
+
+	/* SPI-NAND block erase is always D8h with a 3-byte row address. */
+	erase_seq_cfg0 =
+		FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_CMD_VAL, 0xd8) |
+		FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_CMD_IOS, 0) |
+		FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_CMD_EDGE, 0) |
+		FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_ADDR_CNT, 3) |
+		FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_CMD_EXT_EN, 0) |
+		FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_CMD_EXT_VAL, 0) |
+		FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_ADDR_IOS, 0) |
+		FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_ADDR_EDGE, 0);
+	erase_seq_cfg1 = FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_SECT_SIZE,
+				    ilog2(nanddev_eraseblock_size(&spinand->base)));
+
+	writel(erase_seq_cfg0,
+	       cdns_xspi->iobase + CDNS_XSPI_ERSS_SEQ_CFG_0);
+	writel(erase_seq_cfg1,
+	       cdns_xspi->iobase + CDNS_XSPI_ERSS_SEQ_CFG_1);
+	writel(0, cdns_xspi->iobase + CDNS_XSPI_ERSS_SEQ_CFG_2);
+
+	dev_dbg(cdns_xspi->dev,
+		"ACMD erase sequence: cfg0=%08x cfg1=%08x\n",
+		erase_seq_cfg0, erase_seq_cfg1);
+}
+
+static void cdns_xspi_nand_status_seq_init(struct cdns_xspi_dev *cdns_xspi)
+{
+	u32 stat_seq_cfg1;
+	u32 stat_seq_cfg2;
+	u32 stat_seq_cfg5;
+
+	writel(FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_ADDR_CNT, 0),
+	       cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_0);
+
+	stat_seq_cfg1 = CDNS_XSPI_P1_DEV_RDY_ADDR_EN |
+			CDNS_XSPI_P1_PROG_FAIL_ADDR_EN |
+			CDNS_XSPI_P1_ERS_FAIL_ADDR_EN;
+	writel(stat_seq_cfg1, cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_1);
+
+	stat_seq_cfg2 = FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_DEV_RDY_CMD_VAL,
+				   CDNS_XSPI_NAND_OP_GET_FEATURE) |
+		FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_PROG_FAIL_CMD_VAL,
+			   CDNS_XSPI_NAND_OP_GET_FEATURE) |
+		FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_ERS_FAIL_CMD_VAL,
+			   CDNS_XSPI_NAND_OP_GET_FEATURE);
+	writel(stat_seq_cfg2, cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_2);
+	writel(0, cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_3);
+	writel(0, cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_4);
+
+	stat_seq_cfg5 =
+		FIELD_PREP(CDNS_XSPI_STAT_SEQ_DEV_RDY_IDX,
+			   __ffs(STATUS_BUSY)) |
+		CDNS_XSPI_STAT_SEQ_DEV_RDY_EN |
+		FIELD_PREP(CDNS_XSPI_STAT_SEQ_ERS_FAIL_IDX,
+			   __ffs(STATUS_ERASE_FAILED)) |
+		CDNS_XSPI_STAT_SEQ_ERS_FAIL_VAL |
+		CDNS_XSPI_STAT_SEQ_ERS_FAIL_EN |
+		FIELD_PREP(CDNS_XSPI_STAT_SEQ_PROG_FAIL_IDX,
+			   __ffs(STATUS_PROG_FAILED)) |
+		CDNS_XSPI_STAT_SEQ_PROG_FAIL_VAL |
+		CDNS_XSPI_STAT_SEQ_PROG_FAIL_EN;
+	writel(stat_seq_cfg5, cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_5);
+	writel(CDNS_XSPI_NAND_STATUS_REG,
+	       cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_7);
+	writel(CDNS_XSPI_NAND_STATUS_REG,
+	       cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_8);
+	writel(CDNS_XSPI_NAND_STATUS_REG,
+	       cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_9);
+
+	writel(CDNS_XSPI_STAT_SEQ_ECC_FAIL_EN |
+	       FIELD_PREP(CDNS_XSPI_STAT_SEQ_CRDY_IDX, __ffs(STATUS_BUSY)) |
+	       FIELD_PREP(CDNS_XSPI_STAT_SEQ_CRDY_VAL, 0) |
+	       FIELD_PREP(CDNS_XSPI_STAT_SEQ_ECC_CORR_VAL,
+			  STATUS_ECC_HAS_BITFLIPS) |
+	       FIELD_PREP(CDNS_XSPI_STAT_SEQ_ECC_FAIL_VAL,
+			  STATUS_ECC_UNCOR_ERROR) |
+	       FIELD_PREP(CDNS_XSPI_STAT_SEQ_ECC_FAIL_MASK,
+			  STATUS_ECC_MASK),
+	       cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_10);
+}
+
+static void cdns_xspi_nand_write_enable_seq_init(struct cdns_xspi_dev *cdns_xspi)
+{
+	u32 cfg;
+
+	cfg = readl(cdns_xspi->iobase + CDNS_XSPI_WE_SEQ_CFG_0);
+	cfg = u32_replace_bits(cfg, 1, CDNS_XSPI_WE_SEQ_P1_EN);
+	cfg = u32_replace_bits(cfg, CDNS_XSPI_NAND_OP_WRITE_ENABLE,
+			       CDNS_XSPI_WE_SEQ_P1_CMD_VAL);
+	cfg = u32_replace_bits(cfg, 0, CDNS_XSPI_WE_SEQ_P1_CMD_IOS);
+	cfg = u32_replace_bits(cfg, 0, CDNS_XSPI_WE_SEQ_P1_CMD_EDGE);
+	writel(cfg, cdns_xspi->iobase + CDNS_XSPI_WE_SEQ_CFG_0);
+}
+
+static void cdns_xspi_set_mode_acmd(struct cdns_xspi_dev *cdns_xspi)
+{
+	u32 reg_val;
+
+	reg_val = readl(cdns_xspi->iobase + CDNS_XSPI_CTRL_CONFIG_REG);
+	reg_val = u32_replace_bits(reg_val, CDNS_XSPI_WORK_MODE_ACMD,
+				   CDNS_XSPI_CTRL_WORK_MODE);
+	writel(reg_val, cdns_xspi->iobase + CDNS_XSPI_CTRL_CONFIG_REG);
+}
+
+static void cdns_xspi_nand_reset_seq_init(struct cdns_xspi_dev *cdns_xspi)
+{
+	u32 cfg;
+
+	cfg = FIELD_PREP(CDNS_XSPI_RST_SEQ_P1_CMD1_VAL,
+			 CDNS_XSPI_NAND_OP_RESET);
+	writel(cfg, cdns_xspi->iobase + CDNS_XSPI_RST_SEQ_CFG_0);
+}
+
+static int cdns_xspi_nand_init(struct cdns_xspi_dev *cdns_xspi,
+			       struct spinand_device *spinand)
+{
+	u32 reg_val;
+
+	cdns_xspi_nand_cfg_seq_init(cdns_xspi, spinand);
+	cdns_xspi_nand_read_seq_init(cdns_xspi, spinand);
+	cdns_xspi_nand_reset_seq_init(cdns_xspi);
+	cdns_xspi_nand_write_seq_init(cdns_xspi, spinand);
+	cdns_xspi_nand_write_enable_seq_init(cdns_xspi);
+	cdns_xspi_nand_status_seq_init(cdns_xspi);
+	cdns_xspi_nand_erase_seq_init(cdns_xspi, spinand);
+
+	reg_val = readl(cdns_xspi->iobase + CDNS_XSPI_XIP_MODE_CFG);
+	if (reg_val & CDNS_XSPI_XIP_EN) {
+		reg_val &= ~CDNS_XSPI_XIP_EN;
+		writel(reg_val, cdns_xspi->iobase + CDNS_XSPI_XIP_MODE_CFG);
+	}
+	cdns_xspi->dma_buf_len = spinand->base.memorg.pagesize +
+				 spinand->base.memorg.oobsize;
+	cdns_xspi->dma_buf = dmam_alloc_coherent(cdns_xspi->dev,
+						 cdns_xspi->dma_buf_len,
+						 &cdns_xspi->dma_addr,
+						 GFP_KERNEL);
+	if (!cdns_xspi->dma_buf)
+		return -ENOMEM;
+
+	cdns_xspi->acmd_info.initialized = true;
+
+	return 0;
+}
+
 static int cdns_xspi_controller_init(struct cdns_xspi_dev *cdns_xspi)
 {
 	u32 ctrl_ver;
@@ -580,6 +1009,349 @@ static int cdns_xspi_send_stig_command(struct cdns_xspi_dev *cdns_xspi,
 	return 0;
 }
 
+static int cdns_xspi_acmd_get_thread_status(struct cdns_xspi_dev *cdns_xspi,
+					    u32 thread)
+{
+	writel(thread, cdns_xspi->iobase + CDNS_XSPI_CMD_STATUS_PTR_REG);
+
+	return cdns_xspi_check_command_status(cdns_xspi);
+}
+
+static int cdns_xspi_acmd_run(struct cdns_xspi_dev *cdns_xspi, u32 cmd_regs[6],
+			      u32 thread)
+{
+	unsigned long timeout;
+	int ret;
+
+	cdns_xspi_set_mode_acmd(cdns_xspi);
+	reinit_completion(&cdns_xspi->auto_cmd_complete);
+	cdns_xspi_set_interrupts(cdns_xspi, true);
+	cdns_xspi_trigger_command(cdns_xspi, cmd_regs);
+
+	timeout = msecs_to_jiffies(CDNS_XSPI_ACMD_TIMEOUT_MS);
+	if (!wait_for_completion_timeout(&cdns_xspi->auto_cmd_complete,
+					 timeout)) {
+		dev_err(cdns_xspi->dev, "ACMD command timed out\n");
+		ret = -ETIMEDOUT;
+	} else {
+		ret = cdns_xspi_acmd_get_thread_status(cdns_xspi, thread);
+	}
+
+	cdns_xspi_set_interrupts(cdns_xspi, false);
+
+	return ret;
+}
+
+static int cdns_xspi_nand_addr(struct spinand_device *spinand, u64 row,
+			       u64 column, u64 *xspi_addr)
+{
+	const struct nand_memory_organization *memorg = &spinand->base.memorg;
+	u64 column_limit, page_stride;
+
+	page_stride = roundup_pow_of_two(memorg->pagesize + memorg->oobsize);
+	if (check_mul_overflow(page_stride, memorg->planes_per_lun,
+			       &column_limit) ||
+	    column >= column_limit ||
+	    check_mul_overflow(row, page_stride, xspi_addr) ||
+	    check_add_overflow(*xspi_addr, column, xspi_addr))
+		return -ERANGE;
+
+	return 0;
+}
+
+static u32 cdns_xspi_acmd_cmd(struct cdns_xspi_dev *cdns_xspi, u32 type,
+			      u32 thread, bool use_dma)
+{
+	return FIELD_PREP(CDNS_XSPI_ACMD_MODE, CDNS_XSPI_ACMD_MODE_PIO) |
+	       FIELD_PREP(CDNS_XSPI_ACMD_TRD_NUM, thread) |
+	       FIELD_PREP(CDNS_XSPI_ACMD_BANK_NUM, cdns_xspi->cur_cs) |
+	       (use_dma ? CDNS_XSPI_ACMD_DMA_SEL : 0) |
+	       CDNS_XSPI_ACMD_INT_EN |
+	       FIELD_PREP(CDNS_XSPI_ACMD_CMD_TYPE, type);
+}
+
+static int cdns_xspi_pio_mdma_erase(struct cdns_xspi_dev *cdns_xspi,
+				    struct spinand_device *spinand,
+				    const struct spi_mem_op *op)
+{
+	u32 cmd_regs[6] = { 0 };
+	u64 xspi_addr;
+	int ret;
+
+	ret = cdns_xspi_wait_for_controller_idle(cdns_xspi);
+	if (ret) {
+		dev_err(cdns_xspi->dev,
+			"controller did not become idle before erase\n");
+		return ret;
+	}
+
+	ret = cdns_xspi_nand_addr(spinand, op->addr.val, 0, &xspi_addr);
+	if (ret)
+		return ret;
+
+	if (upper_32_bits(xspi_addr)) {
+		dev_err(cdns_xspi->dev,
+			"erase address 0x%llx exceeds the ACMD PIO range\n",
+			xspi_addr);
+		return -ERANGE;
+	}
+
+	cmd_regs[1] = lower_32_bits(xspi_addr);
+	cmd_regs[0] = cdns_xspi_acmd_cmd(cdns_xspi,
+					 CDNS_XSPI_ACMD_ERASE_OP,
+					 CDNS_XSPI_ACMD_ERASE_THREAD, false);
+
+	ret = cdns_xspi_acmd_run(cdns_xspi, cmd_regs,
+				 CDNS_XSPI_ACMD_ERASE_THREAD);
+	if (ret)
+		dev_err(cdns_xspi->dev, "ACMD erase failed: %d\n", ret);
+
+	return ret;
+}
+
+static int cdns_xspi_pio_reset(struct cdns_xspi_dev *cdns_xspi)
+{
+	u32 cmd_regs[6] = { 0 };
+	int ret;
+
+	ret = cdns_xspi_wait_for_controller_idle(cdns_xspi);
+	if (ret) {
+		dev_err(cdns_xspi->dev,
+			"controller did not become idle before reset\n");
+		return ret;
+	}
+	cmd_regs[0] = cdns_xspi_acmd_cmd(cdns_xspi,
+					 CDNS_XSPI_ACMD_RESET_OP,
+					 CDNS_XSPI_ACMD_DATA_THREAD, false);
+	ret = cdns_xspi_acmd_run(cdns_xspi, cmd_regs,
+				 CDNS_XSPI_ACMD_DATA_THREAD);
+	if (ret)
+		return ret;
+
+	cdns_xspi->acmd_info.row_addr_valid = false;
+	cdns_xspi->acmd_info.column_addr = 0;
+	cdns_xspi->acmd_info.data_nbytes = 0;
+	cdns_xspi->out_buffer = NULL;
+
+	return 0;
+}
+
+static int cdns_xspi_pio_mdma_program(struct cdns_xspi_dev *cdns_xspi,
+				      struct spinand_device *spinand,
+				      const struct spi_mem_op *op)
+{
+	u32 cmd_regs[6] = { 0 };
+	u64 xspi_addr;
+	int ret;
+
+	if (!cdns_xspi->out_buffer || !cdns_xspi->acmd_info.data_nbytes) {
+		dev_err(cdns_xspi->dev, "missing program-load data\n");
+		ret = -EINVAL;
+		goto out_clear_program_state;
+	}
+
+	if (cdns_xspi->acmd_info.data_nbytes > cdns_xspi->dma_buf_len) {
+		ret = -EMSGSIZE;
+		goto out_clear_program_state;
+	}
+
+	memcpy(cdns_xspi->dma_buf, cdns_xspi->out_buffer,
+	       cdns_xspi->acmd_info.data_nbytes);
+
+	ret = cdns_xspi_wait_for_controller_idle(cdns_xspi);
+	if (ret) {
+		dev_err(cdns_xspi->dev,
+			"controller did not become idle before program\n");
+		goto out_clear_program_state;
+	}
+
+	ret = cdns_xspi_nand_addr(spinand, op->addr.val,
+				  cdns_xspi->acmd_info.column_addr,
+				  &xspi_addr);
+	if (ret)
+		goto out_clear_program_state;
+
+	if (upper_32_bits(xspi_addr)) {
+		dev_err(cdns_xspi->dev,
+			"program address 0x%llx exceeds the ACMD PIO range\n",
+			xspi_addr);
+		ret = -ERANGE;
+		goto out_clear_program_state;
+	}
+
+	cmd_regs[1] = lower_32_bits(xspi_addr);
+	cmd_regs[2] = lower_32_bits(cdns_xspi->dma_addr);
+	cmd_regs[3] = upper_32_bits(cdns_xspi->dma_addr);
+	cmd_regs[4] = cdns_xspi->acmd_info.data_nbytes - 1;
+	cmd_regs[0] = cdns_xspi_acmd_cmd(cdns_xspi,
+					 CDNS_XSPI_ACMD_PROG_OP,
+					 CDNS_XSPI_ACMD_DATA_THREAD, true);
+
+	ret = cdns_xspi_acmd_run(cdns_xspi, cmd_regs,
+				 CDNS_XSPI_ACMD_DATA_THREAD);
+
+out_clear_program_state:
+	cdns_xspi->acmd_info.column_addr = 0;
+	cdns_xspi->acmd_info.data_nbytes = 0;
+	cdns_xspi->out_buffer = NULL;
+	return ret;
+}
+
+static int cdns_xspi_pio_mdma_read(struct cdns_xspi_dev *cdns_xspi,
+				   struct spinand_device *spinand,
+				   const struct spi_mem_op *op)
+{
+	u32 cmd_regs[6] = { 0 };
+	u64 xspi_addr;
+	int ret;
+
+	if (!cdns_xspi->acmd_info.row_addr_valid) {
+		dev_err(cdns_xspi->dev,
+			"read cache command without a pending page read\n");
+		return -EINVAL;
+	}
+
+	if (op->data.dir != SPI_MEM_DATA_IN || !op->data.nbytes ||
+	    !op->data.buf.in) {
+		ret = -EINVAL;
+		goto out_clear_read_state;
+	}
+
+	if (op->data.nbytes > cdns_xspi->dma_buf_len) {
+		ret = -EMSGSIZE;
+		goto out_clear_read_state;
+	}
+
+	ret = cdns_xspi_wait_for_controller_idle(cdns_xspi);
+	if (ret) {
+		dev_err(cdns_xspi->dev,
+			"controller did not become idle before read\n");
+		goto out_clear_read_state;
+	}
+
+	ret = cdns_xspi_nand_addr(spinand,
+				  cdns_xspi->acmd_info.row_addr,
+				  op->addr.val, &xspi_addr);
+	if (ret)
+		goto out_clear_read_state;
+
+	if (upper_32_bits(xspi_addr)) {
+		dev_err(cdns_xspi->dev,
+			"read address 0x%llx exceeds the ACMD PIO range\n",
+			xspi_addr);
+		ret = -ERANGE;
+		goto out_clear_read_state;
+	}
+
+	cmd_regs[1] = lower_32_bits(xspi_addr);
+	cmd_regs[2] = lower_32_bits(cdns_xspi->dma_addr);
+	cmd_regs[3] = upper_32_bits(cdns_xspi->dma_addr);
+	cmd_regs[4] = op->data.nbytes - 1;
+	cmd_regs[0] = cdns_xspi_acmd_cmd(cdns_xspi,
+					 CDNS_XSPI_ACMD_READ_OP,
+					 CDNS_XSPI_ACMD_DATA_THREAD, true);
+
+	ret = cdns_xspi_acmd_run(cdns_xspi, cmd_regs,
+				 CDNS_XSPI_ACMD_DATA_THREAD);
+	if (ret) {
+		dev_err(cdns_xspi->dev, "ACMD read failed: %d\n", ret);
+		goto out_clear_read_state;
+	}
+
+	memcpy(op->data.buf.in, cdns_xspi->dma_buf, op->data.nbytes);
+
+out_clear_read_state:
+	cdns_xspi->acmd_info.row_addr_valid = false;
+	cdns_xspi->acmd_info.row_addr = 0;
+	return ret;
+}
+
+static int cdns_xspi_send_pio_command(struct cdns_xspi_dev *cdns_xspi,
+				      struct spi_mem *mem,
+				      const struct spi_mem_op *op)
+{
+	struct spinand_device *spinand;
+	const struct spi_mem_op *read_cache;
+	const struct spi_mem_op *write_cache;
+	const struct spi_mem_op *update_cache;
+	int ret;
+
+	if (cdns_xspi->flash_type != CDNS_XSPI_FLASH_TYPE_NAND)
+		goto use_stig;
+
+	spinand = spi_mem_get_drvdata(mem);
+	if (!spinand || !spinand->op_templates ||
+	    !spinand->op_templates->read_cache ||
+	    !spinand->op_templates->write_cache ||
+	    !spinand->op_templates->update_cache)
+		goto use_stig;
+
+	if (!cdns_xspi->acmd_info.initialized) {
+		ret = cdns_xspi_nand_init(cdns_xspi, spinand);
+		if (ret) {
+			dev_err(cdns_xspi->dev,
+				"failed to initialize ACMD: %d\n", ret);
+			return ret;
+		}
+	}
+
+	read_cache = spinand->op_templates->read_cache;
+	write_cache = spinand->op_templates->write_cache;
+	update_cache = spinand->op_templates->update_cache;
+
+	if ((write_cache && op->cmd.opcode == write_cache->cmd.opcode) ||
+	    (update_cache && op->cmd.opcode == update_cache->cmd.opcode)) {
+		if (op->data.dir != SPI_MEM_DATA_OUT || !op->data.nbytes ||
+		    !op->data.buf.out)
+			return -EINVAL;
+
+		cdns_xspi_nand_write_enable_seq_init(cdns_xspi);
+		cdns_xspi->out_buffer = op->data.buf.out;
+		cdns_xspi->acmd_info.column_addr = op->addr.val;
+		cdns_xspi->acmd_info.data_nbytes = op->data.nbytes;
+		return 0;
+	}
+
+	switch (op->cmd.opcode) {
+	case CDNS_XSPI_NAND_OP_PAGE_READ:
+		cdns_xspi->acmd_info.row_addr = op->addr.val;
+		cdns_xspi->acmd_info.row_addr_valid = true;
+		return 0;
+
+	case CDNS_XSPI_NAND_OP_GET_FEATURE:
+		if (op->addr.val != CDNS_XSPI_NAND_STATUS_REG ||
+		    !cdns_xspi->acmd_info.row_addr_valid)
+			break;
+
+		if (op->data.dir != SPI_MEM_DATA_IN || !op->data.nbytes ||
+		    !op->data.buf.in)
+			return -EINVAL;
+
+		memset(op->data.buf.in, 0, op->data.nbytes);
+		return 0;
+
+	case CDNS_XSPI_NAND_OP_RESET:
+		return cdns_xspi_pio_reset(cdns_xspi);
+
+	case CDNS_XSPI_NAND_OP_BLOCK_ERASE:
+		return cdns_xspi_pio_mdma_erase(cdns_xspi, spinand, op);
+
+	case CDNS_XSPI_NAND_OP_PROGRAM_EXECUTE:
+		return cdns_xspi_pio_mdma_program(cdns_xspi, spinand, op);
+
+	default:
+		break;
+	}
+
+	if (read_cache && op->cmd.opcode == read_cache->cmd.opcode)
+		return cdns_xspi_pio_mdma_read(cdns_xspi, spinand, op);
+
+use_stig:
+	/* ACMD does not consume this operation; execute it through STIG. */
+	return cdns_xspi_send_stig_command(cdns_xspi, op,
+					   op->data.dir != SPI_MEM_NO_DATA);
+}
+
 static int cdns_xspi_mem_op(struct cdns_xspi_dev *cdns_xspi,
 			    struct spi_mem *mem,
 			    const struct spi_mem_op *op)
@@ -589,6 +1361,9 @@ static int cdns_xspi_mem_op(struct cdns_xspi_dev *cdns_xspi,
 	if (cdns_xspi->cur_cs != spi_get_chipselect(mem->spi, 0))
 		cdns_xspi->cur_cs = spi_get_chipselect(mem->spi, 0);
 
+	if (cdns_xspi->work_mode == CDNS_XSPI_WORK_MODE_ACMD)
+		return cdns_xspi_send_pio_command(cdns_xspi, mem, op);
+
 	return cdns_xspi_send_stig_command(cdns_xspi, op,
 					   (dir != SPI_MEM_NO_DATA));
 }
@@ -1217,6 +1992,9 @@ static int cdns_xspi_probe(struct platform_device *pdev)
 	cdns_xspi->host = host;
 	cdns_xspi->dev = &pdev->dev;
 	cdns_xspi->cur_cs = 0;
+	cdns_xspi->flash_type = cdns_xspi->driver_data->flash_type;
+	cdns_xspi->work_mode = cdns_xspi->driver_data->use_acmd ?
+		CDNS_XSPI_WORK_MODE_ACMD : CDNS_XSPI_WORK_MODE_STIG;
 
 	init_completion(&cdns_xspi->cmd_complete);
 	init_completion(&cdns_xspi->auto_cmd_complete);
@@ -1344,6 +2122,10 @@ static const struct of_device_id cdns_xspi_of_match[] = {
 		.data = &marvell_driver_data,
 	},
 #endif
+	{
+		.compatible = "cdns,xspi-nand",
+		.data = &cdns_nand_driver_data,
+	},
 	{ /* end of table */}
 };
 MODULE_DEVICE_TABLE(of, cdns_xspi_of_match);
-- 
2.34.1




More information about the linux-mtd mailing list