From 44a2f49b9bdc7e19413230d390ebf224054e6572 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 9 Jan 2026 18:18:25 +0100 Subject: [PATCH] mtd: spinand: winbond: W35N octal DTR support Extend the support for the W35N chip family by supporting the ODTR bus interface. The chip is capable to run in this mode, which brings a significant performance improvement. 1S-8S-8S: # flash_speed /dev/mtd0 -c1 -d eraseblock write speed is 7529 KiB/s eraseblock read speed is 15058 KiB/s 8D-8D-8D: # flash_speed /dev/mtd0 -c1 -d eraseblock write speed is 9481 KiB/s eraseblock read speed is 23272 KiB/s This is +55% read speed and +26% write speed with the same hardware. Tests have been conducted with a TI AM62A7 using the Cadence quad SPI controller. Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/winbond.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c index 90e4ece00cf5e..8430ae307be0f 100644 --- a/drivers/mtd/nand/spi/winbond.c +++ b/drivers/mtd/nand/spi/winbond.c @@ -36,6 +36,8 @@ */ static SPINAND_OP_VARIANTS(read_cache_octal_variants, + SPINAND_PAGE_READ_FROM_CACHE_8D_8D_8D_OP(0, 24, NULL, 0, 120 * HZ_PER_MHZ), + SPINAND_PAGE_READ_FROM_CACHE_8D_8D_8D_OP(0, 16, NULL, 0, 86 * HZ_PER_MHZ), SPINAND_PAGE_READ_FROM_CACHE_1S_1D_8D_OP(0, 3, NULL, 0, 120 * HZ_PER_MHZ), SPINAND_PAGE_READ_FROM_CACHE_1S_1D_8D_OP(0, 2, NULL, 0, 105 * HZ_PER_MHZ), SPINAND_PAGE_READ_FROM_CACHE_1S_8S_8S_OP(0, 20, NULL, 0, 0), @@ -48,11 +50,13 @@ static SPINAND_OP_VARIANTS(read_cache_octal_variants, SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); static SPINAND_OP_VARIANTS(write_cache_octal_variants, + SPINAND_PROG_LOAD_8D_8D_8D_OP(true, 0, NULL, 0), SPINAND_PROG_LOAD_1S_8S_8S_OP(true, 0, NULL, 0), SPINAND_PROG_LOAD_1S_1S_8S_OP(0, NULL, 0), SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0)); static SPINAND_OP_VARIANTS(update_cache_octal_variants, + SPINAND_PROG_LOAD_8D_8D_8D_OP(false, 0, NULL, 0), SPINAND_PROG_LOAD_1S_8S_8S_OP(false, 0, NULL, 0), SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0)); @@ -93,13 +97,22 @@ static SPINAND_OP_VARIANTS(update_cache_variants, SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_DATA_OUT(1, buf, 1)) +#define SPINAND_WINBOND_WRITE_VCR_8D_8D_8D(reg, buf) \ + SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x81, 8), \ + SPI_MEM_DTR_OP_ADDR(4, reg, 8), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_DTR_OP_DATA_OUT(2, buf, 8)) + static SPINAND_OP_VARIANTS(winbond_w35_ops, - SPINAND_WINBOND_WRITE_VCR_1S_1S_1S(0, NULL)); + SPINAND_WINBOND_WRITE_VCR_1S_1S_1S(0, NULL), + SPINAND_WINBOND_WRITE_VCR_8D_8D_8D(0, NULL)); static struct spi_mem_op spinand_fill_winbond_write_vcr_op(struct spinand_device *spinand, u8 reg, void *valptr) { - return (struct spi_mem_op)SPINAND_WINBOND_WRITE_VCR_1S_1S_1S(reg, valptr); + return (spinand->bus_iface == SSDR) ? + (struct spi_mem_op)SPINAND_WINBOND_WRITE_VCR_1S_1S_1S(reg, valptr) : + (struct spi_mem_op)SPINAND_WINBOND_WRITE_VCR_8D_8D_8D(reg, valptr); } #define SPINAND_WINBOND_SELECT_TARGET_1S_0_1S(buf) \ @@ -390,6 +403,9 @@ static int w35n0xjw_vcr_cfg(struct spinand_device *spinand, case SSDR: ref_op = spinand->ssdr_op_templates.read_cache; break; + case ODTR: + ref_op = spinand->odtr_op_templates.read_cache; + break; default: return -EOPNOTSUPP; }; -- 2.47.3