[RFC PATCH 4/4] spi: cadence-xspi: add ACMD support for SPI NOR
From: Fei Xie
Date: Mon Sep 21 2026 - 06:45:57 EST
From: "fei.xie" <fei.xie@xxxxxxxxxxxx>
Program the automatic command sequences from the SPI NOR operation
templates.
Use the PIO command path with MDMA data transfers for reads, page
programs and erases. Unsupported command layouts continue to use STIG
mode.
Signed-off-by: fei.xie <fei.xie@xxxxxxxxxxxx>
---
drivers/spi/spi-cadence-xspi.c | 440 ++++++++++++++++++++++++++++++++-
1 file changed, 433 insertions(+), 7 deletions(-)
diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c
index 114c1e0fd79fb..5900d4cbffb5c 100644
--- a/drivers/spi/spi-cadence-xspi.c
+++ b/drivers/spi/spi-cadence-xspi.c
@@ -12,11 +12,13 @@
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/mtd/spi-nor.h>
#include <linux/mtd/spinand.h>
#include <linux/overflow.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
+#include <linux/sizes.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>
#include <linux/bitfield.h>
@@ -73,6 +75,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_NOR_DMA_BUF_SIZE SZ_32K
#define CDNS_XSPI_NAND_OP_GET_FEATURE 0x0f
#define CDNS_XSPI_NAND_OP_WRITE_ENABLE 0x06
@@ -225,6 +228,7 @@
#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_P1_CMD_EXT_VAL GENMASK(15, 8)
#define CDNS_XSPI_READ_SEQ_CFG_2 0x0438
/* Write enable sequence config register */
@@ -232,12 +236,22 @@
#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_CMD_EXT_EN BIT(15)
+#define CDNS_XSPI_WE_SEQ_P1_CMD_EXT_VAL GENMASK(23, 16)
#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_CMD_IOS GENMASK(1, 0)
+#define CDNS_XSPI_STAT_SEQ_P1_CMD_EDGE BIT(4)
+#define CDNS_XSPI_STAT_SEQ_P1_CMD_EXT_EN BIT(5)
#define CDNS_XSPI_STAT_SEQ_P1_ADDR_CNT GENMASK(9, 8)
+#define CDNS_XSPI_STAT_SEQ_P1_ADDR_IOS GENMASK(11, 10)
+#define CDNS_XSPI_STAT_SEQ_P1_ADDR_EDGE BIT(12)
+#define CDNS_XSPI_STAT_SEQ_P1_DATA_IOS GENMASK(21, 20)
+#define CDNS_XSPI_STAT_SEQ_P1_DATA_EDGE BIT(22)
#define CDNS_XSPI_STAT_SEQ_CFG_1 0x0454
+#define CDNS_XSPI_STAT_SEQ_P1_DEV_RDY_DUMMY_CNT GENMASK(5, 0)
#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)
@@ -248,9 +262,11 @@
#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_P1_DEV_RDY_CMD_EXT_VAL GENMASK(7, 0)
#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_SIZE BIT(5)
#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)
@@ -288,6 +304,7 @@
#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_SEQ_SPI_NOR 0
#define CDNS_XSPI_XIP_MODE_CFG 0x0388
#define CDNS_XSPI_XIP_EN BIT(0)
@@ -476,7 +493,13 @@ struct cdns_xspi_acmd_info {
u64 row_addr;
u64 column_addr;
size_t data_nbytes;
+ u8 nor_status_buswidth;
+ u8 nor_write_enable_buswidth;
bool row_addr_valid;
+ bool nor_status_dtr;
+ bool nor_write_enable_dtr;
+ bool nor_status_initialized;
+ bool nor_write_enable_initialized;
bool initialized;
};
struct cdns_xspi_dev {
@@ -592,6 +615,31 @@ static void cdns_xspi_set_interrupts(struct cdns_xspi_dev *cdns_xspi,
writel(intr_enable, cdns_xspi->iobase + CDNS_XSPI_INTR_ENABLE_REG);
}
+static u8 cdns_xspi_get_opcode(const struct spi_mem_op *op)
+{
+ if (op->cmd.nbytes == 2)
+ return op->cmd.opcode >> 8;
+
+ return op->cmd.opcode;
+}
+
+static u8 cdns_xspi_get_opcode_extension(const struct spi_mem_op *op)
+{
+ if (op->cmd.nbytes == 2)
+ return op->cmd.opcode;
+
+ return 0;
+}
+
+static u32 cdns_xspi_get_dummy_cycles(const struct spi_mem_op *op)
+{
+ if (!op->dummy.nbytes || !op->dummy.buswidth)
+ return 0;
+
+ return op->dummy.nbytes * BITS_PER_BYTE /
+ (op->dummy.buswidth * (op->dummy.dtr + 1));
+}
+
static void cdns_xspi_nand_cfg_seq_init(struct cdns_xspi_dev *cdns_xspi,
struct spinand_device *spinand)
{
@@ -872,6 +920,268 @@ static int cdns_xspi_nand_init(struct cdns_xspi_dev *cdns_xspi,
return 0;
}
+static int cdns_xspi_nor_cfg_seq_init(struct cdns_xspi_dev *cdns_xspi,
+ struct spi_nor *nor)
+{
+ unsigned int page_size;
+ u32 seq_cfg;
+
+ if (!is_power_of_2(nor->mtd.writebufsize))
+ return -EOPNOTSUPP;
+
+ page_size = ilog2(nor->mtd.writebufsize);
+ if (!FIELD_FIT(CDNS_XSPI_SEQ_PAGE_SIZE_PGM, page_size))
+ return -EOPNOTSUPP;
+
+ seq_cfg = readl(cdns_xspi->iobase + CDNS_XSPI_GLOBAL_SEQ_CFG);
+ seq_cfg = u32_replace_bits(seq_cfg, CDNS_XSPI_SEQ_SPI_NOR,
+ CDNS_XSPI_SEQ_TYPE);
+ seq_cfg = u32_replace_bits(seq_cfg, page_size,
+ CDNS_XSPI_SEQ_PAGE_SIZE_PGM);
+ seq_cfg = u32_replace_bits(seq_cfg,
+ ilog2(CDNS_XSPI_NOR_DMA_BUF_SIZE),
+ CDNS_XSPI_SEQ_PAGE_SIZE_RD);
+ writel(seq_cfg, cdns_xspi->iobase + CDNS_XSPI_GLOBAL_SEQ_CFG);
+ writel(0, cdns_xspi->iobase + CDNS_XSPI_GLOBAL_SEQ_CFG_1);
+
+ return 0;
+}
+
+static int cdns_xspi_nor_read_seq_init(struct cdns_xspi_dev *cdns_xspi,
+ const struct spi_mem_op *op)
+{
+ u32 dummy_cycles = cdns_xspi_get_dummy_cycles(op);
+ u32 cfg0;
+ u32 cfg1;
+
+ if (!FIELD_FIT(CDNS_XSPI_READ_SEQ_P1_ADDR_CNT, op->addr.nbytes) ||
+ !FIELD_FIT(CDNS_XSPI_READ_SEQ_P1_DUMMY_CNT, dummy_cycles))
+ return -EOPNOTSUPP;
+
+ cfg0 = FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_CMD_VAL,
+ cdns_xspi_get_opcode(op)) |
+ 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);
+ cfg1 = FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_CMD_EXT_EN,
+ op->cmd.nbytes == 2) |
+ FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_CMD_EXT_VAL,
+ cdns_xspi_get_opcode_extension(op));
+
+ writel(cfg0, cdns_xspi->iobase + CDNS_XSPI_READ_SEQ_CFG_0);
+ writel(cfg1, cdns_xspi->iobase + CDNS_XSPI_READ_SEQ_CFG_1);
+ writel(0, cdns_xspi->iobase + CDNS_XSPI_READ_SEQ_CFG_2);
+
+ return 0;
+}
+
+static int cdns_xspi_nor_program_seq_init(struct cdns_xspi_dev *cdns_xspi,
+ const struct spi_mem_op *op)
+{
+ u32 dummy_cycles = cdns_xspi_get_dummy_cycles(op);
+ u32 cfg0;
+ u32 cfg1;
+
+ if (!FIELD_FIT(CDNS_XSPI_PROG_SEQ_P1_ADDR_CNT, op->addr.nbytes) ||
+ !FIELD_FIT(CDNS_XSPI_PROG_SEQ_P1_DUMMY_CNT, dummy_cycles))
+ return -EOPNOTSUPP;
+
+ cfg0 = FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_CMD_VAL,
+ cdns_xspi_get_opcode(op)) |
+ 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);
+ cfg1 = FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_CMD_EXT_EN,
+ op->cmd.nbytes == 2) |
+ FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_CMD_EXT_VAL,
+ cdns_xspi_get_opcode_extension(op));
+
+ writel(cfg0, cdns_xspi->iobase + CDNS_XSPI_PROG_SEQ_CFG_0);
+ writel(cfg1, cdns_xspi->iobase + CDNS_XSPI_PROG_SEQ_CFG_1);
+ writel(0, cdns_xspi->iobase + CDNS_XSPI_PROG_SEQ_CFG_2);
+
+ return 0;
+}
+
+static int cdns_xspi_nor_erase_seq_init(struct cdns_xspi_dev *cdns_xspi,
+ struct spi_nor *nor,
+ const struct spi_mem_op *op)
+{
+ unsigned int erase_size;
+ u32 cfg0;
+
+ if (!is_power_of_2(nor->mtd.erasesize))
+ return -EOPNOTSUPP;
+
+ erase_size = ilog2(nor->mtd.erasesize);
+ if (!FIELD_FIT(CDNS_XSPI_ERSS_SEQ_P1_ADDR_CNT, op->addr.nbytes) ||
+ !FIELD_FIT(CDNS_XSPI_ERSS_SEQ_P1_SECT_SIZE, erase_size))
+ return -EOPNOTSUPP;
+
+ cfg0 = FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_CMD_VAL,
+ cdns_xspi_get_opcode(op)) |
+ FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_CMD_IOS,
+ ilog2(op->cmd.buswidth)) |
+ FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_CMD_EDGE, op->cmd.dtr) |
+ FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_ADDR_CNT, op->addr.nbytes) |
+ FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_CMD_EXT_EN,
+ op->cmd.nbytes == 2) |
+ FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_CMD_EXT_VAL,
+ cdns_xspi_get_opcode_extension(op)) |
+ FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_ADDR_IOS,
+ ilog2(op->addr.buswidth)) |
+ FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_ADDR_EDGE, op->addr.dtr);
+
+ writel(cfg0, cdns_xspi->iobase + CDNS_XSPI_ERSS_SEQ_CFG_0);
+ writel(FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_SECT_SIZE, erase_size),
+ cdns_xspi->iobase + CDNS_XSPI_ERSS_SEQ_CFG_1);
+ writel(0, cdns_xspi->iobase + CDNS_XSPI_ERSS_SEQ_CFG_2);
+
+ return 0;
+}
+
+static int cdns_xspi_nor_status_seq_init(struct cdns_xspi_dev *cdns_xspi,
+ const struct spi_mem_op *op)
+{
+ u32 dummy_cycles = cdns_xspi_get_dummy_cycles(op);
+ u32 addr_count = op->addr.nbytes ? op->addr.nbytes - 1 : 0;
+ u32 cfg0;
+ u32 cfg1;
+ u32 cfg5;
+
+ if (op->data.dir != SPI_MEM_DATA_IN || !op->data.nbytes ||
+ !FIELD_FIT(CDNS_XSPI_STAT_SEQ_P1_ADDR_CNT, addr_count) ||
+ !FIELD_FIT(CDNS_XSPI_STAT_SEQ_P1_DEV_RDY_DUMMY_CNT,
+ dummy_cycles) || upper_32_bits(op->addr.val))
+ return -EOPNOTSUPP;
+
+ cfg0 = FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_CMD_IOS,
+ ilog2(op->cmd.buswidth)) |
+ FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_CMD_EDGE, op->cmd.dtr) |
+ FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_CMD_EXT_EN,
+ op->cmd.nbytes == 2) |
+ FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_ADDR_CNT, addr_count) |
+ FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_ADDR_IOS,
+ op->addr.buswidth ? ilog2(op->addr.buswidth) : 0) |
+ FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_ADDR_EDGE, op->addr.dtr) |
+ FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_DATA_IOS,
+ ilog2(op->data.buswidth)) |
+ FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_DATA_EDGE, op->data.dtr);
+ cfg1 = FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_DEV_RDY_DUMMY_CNT,
+ dummy_cycles) |
+ (op->addr.nbytes ? CDNS_XSPI_P1_DEV_RDY_ADDR_EN : 0);
+ cfg5 = FIELD_PREP(CDNS_XSPI_STAT_SEQ_DEV_RDY_IDX, __ffs(SR_WIP)) |
+ (op->data.dtr ? CDNS_XSPI_STAT_SEQ_DEV_RDY_SIZE : 0) |
+ CDNS_XSPI_STAT_SEQ_DEV_RDY_EN;
+
+ writel(cfg0, cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_0);
+ writel(cfg1, cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_1);
+ writel(FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_DEV_RDY_CMD_VAL,
+ cdns_xspi_get_opcode(op)),
+ cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_2);
+ writel(FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_DEV_RDY_CMD_EXT_VAL,
+ cdns_xspi_get_opcode_extension(op)),
+ cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_3);
+ writel(0, cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_4);
+ writel(cfg5, cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_5);
+ writel(lower_32_bits(op->addr.val),
+ cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_7);
+ writel(0, cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_8);
+ writel(0, cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_9);
+ writel(0, cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_10);
+
+ cdns_xspi->acmd_info.nor_status_initialized = true;
+ cdns_xspi->acmd_info.nor_status_buswidth = op->cmd.buswidth;
+ cdns_xspi->acmd_info.nor_status_dtr = op->cmd.dtr;
+
+ return 0;
+}
+
+static void
+cdns_xspi_nor_write_enable_seq_init(struct cdns_xspi_dev *cdns_xspi,
+ const struct spi_mem_op *op)
+{
+ u32 cfg;
+
+ cfg = FIELD_PREP(CDNS_XSPI_WE_SEQ_P1_CMD_VAL,
+ cdns_xspi_get_opcode(op)) |
+ FIELD_PREP(CDNS_XSPI_WE_SEQ_P1_CMD_IOS,
+ ilog2(op->cmd.buswidth)) |
+ FIELD_PREP(CDNS_XSPI_WE_SEQ_P1_CMD_EDGE, op->cmd.dtr) |
+ FIELD_PREP(CDNS_XSPI_WE_SEQ_P1_CMD_EXT_EN,
+ op->cmd.nbytes == 2) |
+ FIELD_PREP(CDNS_XSPI_WE_SEQ_P1_CMD_EXT_VAL,
+ cdns_xspi_get_opcode_extension(op)) |
+ CDNS_XSPI_WE_SEQ_P1_EN;
+ writel(cfg, cdns_xspi->iobase + CDNS_XSPI_WE_SEQ_CFG_0);
+ cdns_xspi->acmd_info.nor_write_enable_initialized = true;
+ cdns_xspi->acmd_info.nor_write_enable_buswidth = op->cmd.buswidth;
+ cdns_xspi->acmd_info.nor_write_enable_dtr = op->cmd.dtr;
+}
+
+static int cdns_xspi_nor_init(struct cdns_xspi_dev *cdns_xspi,
+ struct spi_nor *nor)
+{
+ struct spi_mem_op status_op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_IN(1, NULL, 1));
+ int ret;
+
+ ret = cdns_xspi_nor_cfg_seq_init(cdns_xspi, nor);
+ if (ret)
+ return ret;
+
+ cdns_xspi_acmd_disable_xip(cdns_xspi);
+ ret = cdns_xspi_acmd_alloc_dma_buf(cdns_xspi,
+ CDNS_XSPI_NOR_DMA_BUF_SIZE);
+ if (ret)
+ return ret;
+
+ if (!spi_nor_protocol_is_dtr(nor->reg_proto) &&
+ !cdns_xspi->acmd_info.nor_status_initialized) {
+ ret = cdns_xspi_nor_status_seq_init(cdns_xspi, &status_op);
+ if (ret)
+ return ret;
+ }
+
+ cdns_xspi->acmd_info.initialized = true;
+
+ return 0;
+}
+
+static bool
+cdns_xspi_nor_helper_sequences_ready(const struct cdns_xspi_dev *cdns_xspi,
+ const struct spi_nor *nor)
+{
+ u8 buswidth = spi_nor_get_protocol_inst_nbits(nor->reg_proto);
+ bool dtr = spi_nor_protocol_is_dtr(nor->reg_proto);
+
+ return cdns_xspi->acmd_info.nor_status_initialized &&
+ cdns_xspi->acmd_info.nor_status_buswidth == buswidth &&
+ cdns_xspi->acmd_info.nor_status_dtr == dtr &&
+ cdns_xspi->acmd_info.nor_write_enable_initialized &&
+ cdns_xspi->acmd_info.nor_write_enable_buswidth == buswidth &&
+ cdns_xspi->acmd_info.nor_write_enable_dtr == dtr;
+}
+
static int cdns_xspi_controller_init(struct cdns_xspi_dev *cdns_xspi)
{
u32 ctrl_ver;
@@ -1315,9 +1625,9 @@ static int cdns_xspi_nand_pio_read(struct cdns_xspi_dev *cdns_xspi,
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)
+static int cdns_xspi_send_nand_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;
@@ -1325,9 +1635,6 @@ static int cdns_xspi_send_pio_command(struct cdns_xspi_dev *cdns_xspi,
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 ||
@@ -1401,6 +1708,115 @@ static int cdns_xspi_send_pio_command(struct cdns_xspi_dev *cdns_xspi,
op->data.dir != SPI_MEM_NO_DATA);
}
+static int cdns_xspi_send_nor_pio_command(struct cdns_xspi_dev *cdns_xspi,
+ struct spi_mem *mem,
+ const struct spi_mem_op *op)
+{
+ struct spi_nor *nor = spi_mem_get_drvdata(mem);
+ u8 opcode = cdns_xspi_get_opcode(op);
+ int ret;
+
+ if (!nor)
+ goto use_stig;
+
+ if (opcode == SPINOR_OP_WREN) {
+ cdns_xspi_nor_write_enable_seq_init(cdns_xspi, op);
+ goto use_stig;
+ }
+
+ if (opcode == SPINOR_OP_RDSR && op->data.dir == SPI_MEM_DATA_IN) {
+ cdns_xspi->acmd_info.nor_status_initialized = false;
+ ret = cdns_xspi_nor_status_seq_init(cdns_xspi, op);
+ if (ret)
+ dev_dbg(cdns_xspi->dev,
+ "cannot use RDSR operation for ACMD status: %d\n",
+ ret);
+ goto use_stig;
+ }
+
+ if (!nor->mtd.size || !nor->mtd.writebufsize)
+ goto use_stig;
+
+ if (opcode != nor->read_opcode && opcode != nor->program_opcode &&
+ opcode != nor->erase_opcode)
+ goto use_stig;
+ if (upper_32_bits(op->addr.val))
+ goto use_stig;
+
+ if (!cdns_xspi->acmd_info.initialized) {
+ ret = cdns_xspi_nor_init(cdns_xspi, nor);
+ if (ret == -EOPNOTSUPP)
+ goto use_stig;
+ if (ret) {
+ dev_err(cdns_xspi->dev,
+ "failed to initialize NOR ACMD: %d\n", ret);
+ return ret;
+ }
+ }
+
+ if (opcode == nor->read_opcode && op->data.dir == SPI_MEM_DATA_IN) {
+ ret = cdns_xspi_nor_read_seq_init(cdns_xspi, op);
+ if (ret == -EOPNOTSUPP)
+ goto use_stig;
+ if (ret)
+ return ret;
+
+ return cdns_xspi_pio_mdma_read(cdns_xspi, op->addr.val,
+ op->data.buf.in,
+ op->data.nbytes);
+ }
+
+ if (opcode == nor->program_opcode &&
+ op->data.dir == SPI_MEM_DATA_OUT) {
+ if (!cdns_xspi_nor_helper_sequences_ready(cdns_xspi, nor))
+ goto use_stig;
+
+ ret = cdns_xspi_nor_program_seq_init(cdns_xspi, op);
+ if (ret == -EOPNOTSUPP)
+ goto use_stig;
+ if (ret)
+ return ret;
+
+ return cdns_xspi_pio_mdma_program(cdns_xspi, op->addr.val,
+ op->data.buf.out,
+ op->data.nbytes);
+ }
+
+ if (opcode == nor->erase_opcode && op->data.dir == SPI_MEM_NO_DATA) {
+ if (nor->mtd.numeraseregions ||
+ !cdns_xspi_nor_helper_sequences_ready(cdns_xspi, nor))
+ goto use_stig;
+
+ ret = cdns_xspi_nor_erase_seq_init(cdns_xspi, nor, op);
+ if (ret == -EOPNOTSUPP)
+ goto use_stig;
+ if (ret)
+ return ret;
+
+ return cdns_xspi_pio_erase(cdns_xspi, op->addr.val);
+ }
+
+use_stig:
+ return cdns_xspi_send_stig_command(cdns_xspi, op,
+ op->data.dir != SPI_MEM_NO_DATA);
+}
+
+static int cdns_xspi_send_pio_command(struct cdns_xspi_dev *cdns_xspi,
+ struct spi_mem *mem,
+ const struct spi_mem_op *op)
+{
+ switch (cdns_xspi->flash_type) {
+ case CDNS_XSPI_FLASH_TYPE_NAND:
+ return cdns_xspi_send_nand_pio_command(cdns_xspi, mem, op);
+
+ case CDNS_XSPI_FLASH_TYPE_NOR:
+ return cdns_xspi_send_nor_pio_command(cdns_xspi, mem, op);
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
static int cdns_xspi_mem_op(struct cdns_xspi_dev *cdns_xspi,
struct spi_mem *mem,
const struct spi_mem_op *op)
@@ -1484,8 +1900,18 @@ static int cdns_xspi_adjust_mem_op_size(struct spi_mem *mem, struct spi_mem_op *
{
struct cdns_xspi_dev *cdns_xspi =
spi_controller_get_devdata(mem->spi->controller);
+ size_t max_len = cdns_xspi->sdmasize;
+
+ if (cdns_xspi->work_mode == CDNS_XSPI_WORK_MODE_ACMD) {
+ if (cdns_xspi->acmd_info.initialized)
+ max_len = min_t(size_t, max_len,
+ cdns_xspi->dma_buf_len);
+ else if (cdns_xspi->flash_type == CDNS_XSPI_FLASH_TYPE_NOR)
+ max_len = min_t(size_t, max_len,
+ CDNS_XSPI_NOR_DMA_BUF_SIZE);
+ }
- op->data.nbytes = clamp_val(op->data.nbytes, 0, cdns_xspi->sdmasize);
+ op->data.nbytes = min_t(size_t, op->data.nbytes, max_len);
return 0;
}
--
2.34.1