]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
mtd: spinand: winbond: Enable high-speed modes on w25n0xjw
authorMiquel Raynal <miquel.raynal@bootlin.com>
Wed, 18 Jun 2025 12:14:23 +0000 (14:14 +0200)
committerMiquel Raynal <miquel.raynal@bootlin.com>
Wed, 30 Jul 2025 09:32:16 +0000 (11:32 +0200)
w25n0xjw chips have a high-speed capability hidden in a configuration
register. Once enabled, dual/quad SDR reads may be performed at a much
higher frequency.

Implement the new ->configure_chip() hook for this purpose and configure
the SR4 register accordingly.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
drivers/mtd/nand/spi/core.c
drivers/mtd/nand/spi/winbond.c
include/linux/mtd/spinand.h

index 9ed3cf2e5dba0deabd940aadb8a145bd22f96c07..93d22b412dd3f79b5492a3120ab350d6125fe6a6 100644 (file)
@@ -20,7 +20,7 @@
 #include <linux/spi/spi.h>
 #include <linux/spi/spi-mem.h>
 
-static int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val)
+int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val)
 {
        struct spi_mem_op op = SPINAND_GET_FEATURE_1S_1S_1S_OP(reg,
                                                      spinand->scratchbuf);
index 7a9e22e81dcf46ab26b03f7abf7112e0793f836c..18ae6f58a546e184a54ce0ea74b9b3fe03a10f72 100644 (file)
@@ -18,6 +18,9 @@
 
 #define W25N04KV_STATUS_ECC_5_8_BITFLIPS       (3 << 4)
 
+#define W25N0XJW_SR4                   0xD0
+#define W25N0XJW_SR4_HS                        BIT(2)
+
 /*
  * "X2" in the core is equivalent to "dual output" in the datasheets,
  * "X4" in the core is equivalent to "quad output" in the datasheets.
@@ -42,10 +45,12 @@ static SPINAND_OP_VARIANTS(update_cache_octal_variants,
 static SPINAND_OP_VARIANTS(read_cache_dual_quad_dtr_variants,
                SPINAND_PAGE_READ_FROM_CACHE_1S_4D_4D_OP(0, 8, NULL, 0, 80 * HZ_PER_MHZ),
                SPINAND_PAGE_READ_FROM_CACHE_1S_1D_4D_OP(0, 2, NULL, 0, 80 * HZ_PER_MHZ),
+               SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 4, NULL, 0, 0),
                SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 2, NULL, 0, 104 * HZ_PER_MHZ),
                SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0),
                SPINAND_PAGE_READ_FROM_CACHE_1S_2D_2D_OP(0, 4, NULL, 0, 80 * HZ_PER_MHZ),
                SPINAND_PAGE_READ_FROM_CACHE_1S_1D_2D_OP(0, 2, NULL, 0, 80 * HZ_PER_MHZ),
+               SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 2, NULL, 0, 0),
                SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 1, NULL, 0, 104 * HZ_PER_MHZ),
                SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0),
                SPINAND_PAGE_READ_FROM_CACHE_1S_1D_1D_OP(0, 2, NULL, 0, 80 * HZ_PER_MHZ),
@@ -230,6 +235,40 @@ static int w25n02kv_ecc_get_status(struct spinand_device *spinand,
        return -EINVAL;
 }
 
+static int w25n0xjw_hs_cfg(struct spinand_device *spinand)
+{
+       const struct spi_mem_op *op;
+       bool hs;
+       u8 sr4;
+       int ret;
+
+       op = spinand->op_templates.read_cache;
+       if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr)
+               hs = false;
+       else if (op->cmd.buswidth == 1 && op->addr.buswidth == 1 &&
+                op->dummy.buswidth == 1 && op->data.buswidth == 1)
+               hs = false;
+       else if (!op->max_freq)
+               hs = true;
+       else
+               hs = false;
+
+       ret = spinand_read_reg_op(spinand, W25N0XJW_SR4, &sr4);
+       if (ret)
+               return ret;
+
+       if (hs)
+               sr4 |= W25N0XJW_SR4_HS;
+       else
+               sr4 &= ~W25N0XJW_SR4_HS;
+
+       ret = spinand_write_reg_op(spinand, W25N0XJW_SR4, sr4);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
 static const struct spinand_info winbond_spinand_table[] = {
        /* 512M-bit densities */
        SPINAND_INFO("W25N512GW", /* 1.8V */
@@ -268,7 +307,8 @@ static const struct spinand_info winbond_spinand_table[] = {
                                              &write_cache_variants,
                                              &update_cache_variants),
                     0,
-                    SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)),
+                    SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
+                    SPINAND_CONFIGURE_CHIP(w25n0xjw_hs_cfg)),
        SPINAND_INFO("W25N01KV", /* 3.3V */
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xae, 0x21),
                     NAND_MEMORG(1, 2048, 96, 64, 1024, 20, 1, 1, 1),
@@ -324,7 +364,8 @@ static const struct spinand_info winbond_spinand_table[] = {
                                              &write_cache_variants,
                                              &update_cache_variants),
                     0,
-                    SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)),
+                    SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
+                    SPINAND_CONFIGURE_CHIP(w25n0xjw_hs_cfg)),
        SPINAND_INFO("W25N02KV", /* 3.3V */
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x22),
                     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
index 69674fd191d9d0753a867334dae2012d8260a02c..53c881e41fc7edac7a526fa7eb6134c4f520054d 100644 (file)
@@ -739,6 +739,7 @@ int spinand_match_and_init(struct spinand_device *spinand,
                           enum spinand_readid_method rdid_method);
 
 int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val);
+int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val);
 int spinand_write_reg_op(struct spinand_device *spinand, u8 reg, u8 val);
 int spinand_select_target(struct spinand_device *spinand, unsigned int target);