[PATCH] Add JEDEC ID table entries for additional ISSI SPI-NOR devices.
Jeffrey Yu
jeyu at issi.com
Mon May 18 20:22:14 PDT 2026
Add JEDEC ID table entries for additional ISSI SPI-NOR
devices.
These parts previously not yet supported.
With these entries, Linux software can match the device by JEDEC ID
and use the existing ISSI SPI-NOR device handling.
Newly added devices include:
- IS25WX256 (JEDEC 0x9d5b19)
https://www.issi.com/WW/pdf/25LX-WX256-128.pdf
- IS25LX256 (JEDEC 0x9d5a19)
https://www.issi.com/WW/pdf/25LX-WX256-128.pdf
- IS25WX512M (JEDEC 0x9d5b1a)
https://www.issi.com/WW/pdf/25LX-WX512M.pdf
- IS25LX512M (JEDEC 0x9d5a1a)
https://www.issi.com/WW/pdf/25LX-WX512M.pdf
- IS25WX01G (JEDEC 0x9d5b1b)
https://www.issi.com/WW/pdf/25LX-WX01G.pdf
- IS25LX01G (JEDEC 0x9d5a1b)
https://www.issi.com/WW/pdf/25LX-WX01G.pdf
Signed-off-by: Jeffrey Y <jeyu at issi.com>
---
drivers/mtd/spi-nor/issi.c | 322 +++++++++++++++++++++++++++++++++++++
1 file changed, 322 insertions(+)
create mode 100644 drivers/mtd/spi-nor/issi.c
diff --git a/drivers/mtd/spi-nor/issi.c b/drivers/mtd/spi-nor/issi.c
new file mode 100644
index 000000000000..13c06d207864
--- /dev/null
+++ b/drivers/mtd/spi-nor/issi.c
@@ -0,0 +1,322 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2005, Intec Automation Inc.
+ * Copyright (C) 2014, Freescale Semiconductor, Inc.
+ */
+
+#include <linux/mtd/spi-nor.h>
+
+#include "core.h"
+
+#define SPINOR_OP_IS_DTR_RD 0xfd /* Fast Read opcode in DTR mode */
+#define SPINOR_OP_IS_RD_ANY_REG 0x85 /* Read volatile register */
+#define SPINOR_OP_IS_WR_ANY_REG 0x81 /* Write volatile register */
+#define SPINOR_REG_IS_CFR0V 0x00 /* For setting octal DTR mode */
+#define SPINOR_REG_IS_CFR1V 0x01 /* For setting dummy cycles */
+#define SPINOR_IS_OCT_DTR 0xe7 /* Enable Octal DTR. */
+#define SPINOR_IS_EXSPI 0xff /* Enable Extended SPI (default) */
+
+static int spi_nor_issi_phy_enable(struct spi_nor *nor)
+{
+ struct spi_mem_op op;
+ u8 *buf = nor->bouncebuf;
+ int ret;
+
+ ret = spi_nor_write_enable(nor);
+ if (ret)
+ goto ret;
+
+ buf[0] = SPINOR_IS_EXSPI;
+
+ op = (struct spi_mem_op)
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_IS_WR_ANY_REG, 1),
+ SPI_MEM_OP_ADDR(4, SPINOR_REG_IS_CFR0V, 1),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(1, buf, 1));
+
+ spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_1_1_1);
+
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ if (ret)
+ goto ret;
+
+ nor->spimem->spi->controller->flags |= SPI_CONTROLLER_SDR_PHY;
+ /* Read flash ID to make sure the switch was successful. */
+ op = (struct spi_mem_op)
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_DUMMY(0, 1),
+ SPI_MEM_OP_DATA_IN(nor->info->id->len, buf, 1));
+
+ spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_1_1_1);
+
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ if (ret)
+ goto ret;
+
+ if (memcmp(buf, nor->info->id->bytes, nor->info->id->len))
+ goto ret;
+
+ return 0;
+ret:
+ nor->spimem->spi->controller->flags &= ~SPI_CONTROLLER_SDR_PHY;
+ return 0;
+}
+static int spi_nor_issi_octal_dtr_enable(struct spi_nor *nor, bool enable)
+{
+ struct spi_mem_op op;
+ u8 *buf = nor->bouncebuf;
+ int ret;
+
+ if (enable) {
+ /* Use 20 dummy cycles for memory array reads. */
+ ret = spi_nor_write_enable(nor);
+ if (ret)
+ return ret;
+
+ *buf = 20;
+ op = (struct spi_mem_op)
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_IS_WR_ANY_REG, 1),
+ SPI_MEM_OP_ADDR(3, SPINOR_REG_IS_CFR1V, 1),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(1, buf, 1));
+
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ if (ret)
+ return ret;
+
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ return ret;
+ }
+
+ ret = spi_nor_write_enable(nor);
+ if (ret)
+ return ret;
+
+ if (enable)
+ *buf = SPINOR_IS_OCT_DTR;
+ else
+ *buf = SPINOR_IS_EXSPI;
+
+ op = (struct spi_mem_op)
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_IS_WR_ANY_REG, 1),
+ SPI_MEM_OP_ADDR(enable ? 3 : 4,
+ SPINOR_REG_IS_CFR0V, 1),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(1, buf, 1));
+
+ if (!enable)
+ spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR);
+
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ if (ret)
+ return ret;
+
+ if ((nor->flags & SNOR_F_HAS_STACKED) && nor->spimem->spi->cs_index_mask == 1)
+ return 0;
+
+ /* Read flash ID to make sure the switch was successful. */
+ op = (struct spi_mem_op)
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_DUMMY(enable ? 8 : 0, 1),
+ SPI_MEM_OP_DATA_IN(round_up(nor->info->id->len, 2),
+ buf, 1));
+
+ if (enable)
+ spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR);
+
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ if (ret)
+ return ret;
+
+ if (memcmp(buf, nor->info->id->bytes, nor->info->id->len))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int is25wx256_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
+{
+ int ret;
+
+ ret = spi_nor_write_enable(nor);
+ if (ret)
+ return ret;
+
+ ret = spi_nor_set_4byte_addr_mode(nor, enable);
+ if (ret)
+ return ret;
+
+ return spi_nor_write_disable(nor);
+}
+
+static void is25wx256_default_init(struct spi_nor *nor)
+{
+ struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
+
+ params->set_octal_dtr = spi_nor_issi_octal_dtr_enable;
+ params->set_4byte_addr_mode = is25wx256_set_4byte_addr_mode;
+ params->phy_enable = spi_nor_issi_phy_enable;
+}
+
+static int is25wx256_post_sfdp_fixup(struct spi_nor *nor)
+{
+ struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
+
+ /* Set the Fast Read settings. */
+ params->hwcaps.mask |= SNOR_HWCAPS_READ_8_8_8_DTR;
+ spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_8_8_8_DTR],
+ 0, 20, SPINOR_OP_IS_DTR_RD,
+ SNOR_PROTO_8_8_8_DTR);
+
+ nor->cmd_ext_type = SPI_NOR_EXT_REPEAT;
+ params->rdsr_dummy = 8;
+ params->rdsr_addr_nbytes = 0;
+
+ /*
+ * The BFPT quad enable field is set to a reserved value so the quad
+ * enable function is ignored by spi_nor_parse_bfpt(). Make sure we
+ * disable it.
+ */
+ params->quad_enable = NULL;
+
+ return 0;
+}
+
+static struct spi_nor_fixups is25wx256_fixups = {
+ .default_init = is25wx256_default_init,
+ .post_sfdp = is25wx256_post_sfdp_fixup,
+};
+
+static int
+is25lp256_post_bfpt_fixups(struct spi_nor *nor,
+ const struct sfdp_parameter_header *bfpt_header,
+ const struct sfdp_bfpt *bfpt)
+{
+ struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
+
+ /*
+ * IS25LP256 supports 4B opcodes, but the BFPT advertises
+ * BFPT_DWORD1_ADDRESS_BYTES_3_ONLY.
+ * Overwrite the number of address bytes advertised by the BFPT.
+ */
+ if ((bfpt->dwords[SFDP_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) ==
+ BFPT_DWORD1_ADDRESS_BYTES_3_ONLY)
+ params->addr_nbytes = 4;
+
+ return 0;
+}
+
+static const struct spi_nor_fixups is25lp256_fixups = {
+ .post_bfpt = is25lp256_post_bfpt_fixups,
+};
+
+static int pm25lv_nor_late_init(struct spi_nor *nor)
+{
+ struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
+ struct spi_nor_erase_map *map = ¶ms->erase_map;
+ int i;
+
+ /* The PM25LV series has a different 4k sector erase opcode */
+ for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++)
+ if (map->erase_type[i].size == 4096)
+ map->erase_type[i].opcode = SPINOR_OP_BE_4K_PMC;
+
+ return 0;
+}
+
+static const struct spi_nor_fixups pm25lv_nor_fixups = {
+ .late_init = pm25lv_nor_late_init,
+};
+
+static const struct flash_info issi_nor_parts[] = {
+ /* ISSI */
+ {
+ .id = SNOR_ID(0x9d, 0x5b, 0x19),
+ .name = "is25wx256",
+ .size = SZ_256M,
+ .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP |
+ SPI_NOR_BP3_SR_BIT6,
+ .no_sfdp_flags = SECT_4K | SPI_NOR_OCTAL_READ |
+ SPI_NOR_OCTAL_DTR_READ | SPI_NOR_OCTAL_DTR_PP,
+ .fixup_flags = SPI_NOR_4B_OPCODES | SPI_NOR_IO_MODE_EN_VOLATILE,
+ .mfr_flags = USE_FSR,
+ .fixups = &is25wx256_fixups,
+ }, {
+ .id = SNOR_ID(0x9d, 0x5a, 0x19),
+ .name = "is25lx256",
+ .size = SZ_256M,
+ .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP |
+ SPI_NOR_BP3_SR_BIT6,
+ .no_sfdp_flags = SECT_4K | SPI_NOR_OCTAL_READ |
+ SPI_NOR_OCTAL_DTR_READ | SPI_NOR_OCTAL_DTR_PP,
+ .fixup_flags = SPI_NOR_4B_OPCODES | SPI_NOR_IO_MODE_EN_VOLATILE,
+ .mfr_flags = USE_FSR,
+ .fixups = &is25wx256_fixups,
+ }, {
+ .id = SNOR_ID(0x9d, 0x5b, 0x1a),
+ .name = "is25wx512m",
+ .size = SZ_512M,
+ .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP |
+ SPI_NOR_BP3_SR_BIT6,
+ .no_sfdp_flags = SECT_4K | SPI_NOR_OCTAL_READ |
+ SPI_NOR_OCTAL_DTR_READ | SPI_NOR_OCTAL_DTR_PP,
+ .fixup_flags = SPI_NOR_4B_OPCODES | SPI_NOR_IO_MODE_EN_VOLATILE,
+ .mfr_flags = USE_FSR,
+ .fixups = &is25wx256_fixups,
+ }, {
+ .id = SNOR_ID(0x9d, 0x5a, 0x1a),
+ .name = "is25lx512m",
+ .size = SZ_512M,
+ .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP |
+ SPI_NOR_BP3_SR_BIT6,
+ .no_sfdp_flags = SECT_4K | SPI_NOR_OCTAL_READ |
+ SPI_NOR_OCTAL_DTR_READ | SPI_NOR_OCTAL_DTR_PP,
+ .fixup_flags = SPI_NOR_4B_OPCODES | SPI_NOR_IO_MODE_EN_VOLATILE,
+ .mfr_flags = USE_FSR,
+ .fixups = &is25wx256_fixups,
+ }, {
+ .id = SNOR_ID(0x9d, 0x5b, 0x1b),
+ .name = "is25wx01g",
+ .size = SZ_1G,
+ .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP |
+ SPI_NOR_BP3_SR_BIT6,
+ .no_sfdp_flags = SECT_4K | SPI_NOR_OCTAL_READ |
+ SPI_NOR_OCTAL_DTR_READ | SPI_NOR_OCTAL_DTR_PP,
+ .fixup_flags = SPI_NOR_4B_OPCODES | SPI_NOR_IO_MODE_EN_VOLATILE,
+ .mfr_flags = USE_FSR,
+ .fixups = &is25wx256_fixups,
+ }, {
+ .id = SNOR_ID(0x9d, 0x5a, 0x1b),
+ .name = "is25lx01g",
+ .size = SZ_1G,
+ .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP |
+ SPI_NOR_BP3_SR_BIT6,
+ .no_sfdp_flags = SECT_4K | SPI_NOR_OCTAL_READ |
+ SPI_NOR_OCTAL_DTR_READ | SPI_NOR_OCTAL_DTR_PP,
+ .fixup_flags = SPI_NOR_4B_OPCODES | SPI_NOR_IO_MODE_EN_VOLATILE,
+ .mfr_flags = USE_FSR,
+ .fixups = &is25wx256_fixups,
+ }
+};
+
+static void issi_nor_default_init(struct spi_nor *nor)
+{
+ struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
+
+ nor->flags &= ~SNOR_F_HAS_16BIT_SR;
+ params->quad_enable = spi_nor_sr1_bit6_quad_enable;
+}
+
+static const struct spi_nor_fixups issi_fixups = {
+ .default_init = issi_nor_default_init,
+};
+
+const struct spi_nor_manufacturer spi_nor_issi = {
+ .name = "issi",
+ .parts = issi_nor_parts,
+ .nparts = ARRAY_SIZE(issi_nor_parts),
+ .fixups = &issi_fixups,
+};
--
2.43.0
More information about the linux-mtd
mailing list