diff mbox series

[02/11] mtd: spi-nor: Octal DTR support for IS25*x

Message ID 20240411223709.573-3-greg.malysa@timesys.com
State Deferred
Delegated to: Tom Rini
Headers show
Series cadence-qspi: Add DTR support including PHY mode calibration | expand

Commit Message

Greg Malysa April 11, 2024, 10:36 p.m. UTC
From: Ian Roberts <ian.roberts@timesys.com>

ISSI IS25*x series SPIflash chips are capable of Octal IO and DDR.
Add spi-nor support to enable and operate in these modes.

Co-developed-by: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
Signed-off-by: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
Signed-off-by: Greg Malysa <greg.malysa@timesys.com>
Signed-off-by: Ian Roberts <ian.roberts@timesys.com>
---

 drivers/mtd/spi/spi-nor-core.c | 78 ++++++++++++++++++++++++++++++++++
 drivers/mtd/spi/spi-nor-ids.c  |  6 ++-
 include/linux/mtd/spi-nor.h    |  6 +++
 3 files changed, 89 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index f164c3cf73..ce86e53860 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -3968,6 +3968,76 @@  static struct spi_nor_fixups macronix_octal_fixups = {
 };
 #endif /* CONFIG_SPI_FLASH_MACRONIX */
 
+#ifdef CONFIG_SPI_FLASH_ISSI
+/**
+ * spi_nor_issi_octal_dtr_enable() - Enable octal DTR on ISSI flashes.
+ * @nor:	pointer to a 'struct spi_nor'
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_issi_octal_dtr_enable(struct spi_nor *nor)
+{
+	struct spi_mem_op op;
+	int ret;
+	u8 regval;
+
+	nor->read_dummy = ISSI_MAX_DC;
+
+	ret = write_enable(nor);
+	if (ret)
+		return ret;
+
+	regval = SPINOR_REG_ISSI_VCR_ODDR_EN;
+	op = (struct spi_mem_op)
+		SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_ISSI_WR_VCR, 1),
+			   SPI_MEM_OP_ADDR(3, SPINOR_REG_ISSI_VCR_IOMODE, 1),
+			   SPI_MEM_OP_NO_DUMMY,
+			   SPI_MEM_OP_DATA_OUT(1, &regval, 1));
+
+	ret = spi_mem_exec_op(nor->spi, &op);
+	if (ret) {
+		dev_err(nor->dev, "Failed to enable octal DTR mode\n");
+		return ret;
+	}
+
+	nor->reg_proto = SNOR_PROTO_8_8_8_DTR;
+
+	return 0;
+}
+
+static void issi_octal_default_init(struct spi_nor *nor)
+{
+	nor->octal_dtr_enable = spi_nor_issi_octal_dtr_enable;
+}
+
+static void issi_octal_post_sfdp_fixup(struct spi_nor *nor,
+				       struct spi_nor_flash_parameter *params)
+{
+	/*
+	 * Adding SNOR_HWCAPS_PP_8_8_8_DTR in hwcaps.mask when
+	 * SPI_NOR_OCTAL_DTR_READ flag exists.
+	 */
+	if (params->hwcaps.mask & SNOR_HWCAPS_READ_8_8_8_DTR)
+		params->hwcaps.mask |= SNOR_HWCAPS_PP_8_8_8_DTR;
+	nor->cmd_ext_type = SPI_NOR_EXT_INVERT;
+
+	spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_8_8_8_DTR],
+				  0, 16, 0x0c, SNOR_PROTO_8_8_8_DTR);
+	spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP_8_8_8_DTR],
+				0x12, SNOR_PROTO_8_8_8_DTR);
+
+	params->rdsr_dummy = 8;
+
+	nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE;
+	nor->flags |= SNOR_F_SOFT_RESET;
+}
+
+static struct spi_nor_fixups issi_octal_dtr_fixups = {
+	.default_init = issi_octal_default_init,
+	.post_sfdp = issi_octal_post_sfdp_fixup,
+};
+#endif /* CONFIG_SPI_FLASH_ISSI */
+
 /** spi_nor_octal_dtr_enable() - enable Octal DTR I/O if needed
  * @nor:                 pointer to a 'struct spi_nor'
  *
@@ -4161,6 +4231,14 @@  void spi_nor_set_fixups(struct spi_nor *nor)
 		nor->fixups = &mt35xu512aba_fixups;
 #endif
 
+#if CONFIG_IS_ENABLED(SPI_FLASH_ISSI)
+	if (JEDEC_MFR(nor->info) == SNOR_MFR_ISSI) {
+		if ((nor->info->id[1] == 0x5a || nor->info->id[1] == 0x5b) &&
+		    (nor->info->id[2] == 0x19 || nor->info->id[2] == 0x18))
+			nor->fixups = &issi_octal_dtr_fixups;
+	}
+#endif
+
 #if CONFIG_IS_ENABLED(SPI_FLASH_MACRONIX)
 	nor->fixups = &macronix_octal_fixups;
 #endif /* SPI_FLASH_MACRONIX */
diff --git a/drivers/mtd/spi/spi-nor-ids.c b/drivers/mtd/spi/spi-nor-ids.c
index 4e83b8c94c..e3e37cd79b 100644
--- a/drivers/mtd/spi/spi-nor-ids.c
+++ b/drivers/mtd/spi/spi-nor-ids.c
@@ -238,8 +238,12 @@  const struct flash_info spi_nor_ids[] = {
 			SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
 	{ INFO("is25wp01g",  0x9d701b, 0, 64 * 1024, 2048,
 			SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+	{ INFO("is25lx256", 0x9d5a19, 0, 128 * 1024, 256,
+			SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ
+			| SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
 	{ INFO("is25wx256",  0x9d5b19, 0, 128 * 1024, 256,
-			SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) },
+			SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ
+			| SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
 	{ INFO("is25lx512",  0x9d5a1a, 0, 64 * 1024, 1024,
 			SECT_4K | USE_FSR | SPI_NOR_4B_OPCODES | SPI_NOR_HAS_TB) },
 #endif
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 2eddb52392..bd364e74a7 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -131,6 +131,12 @@ 
 #define SPINOR_REG_MXIC_DC_20		0x0		/* Setting dummy cycles to 20 */
 #define MXIC_MAX_DC			20		/* Maximum value of dummy cycles */
 
+/* Used for ISSI flashes. */
+#define SPINOR_OP_ISSI_WR_VCR		0x81
+#define SPINOR_REG_ISSI_VCR_IOMODE	0x0
+#define SPINOR_REG_ISSI_VCR_ODDR_EN	0xe7
+#define ISSI_MAX_DC			16
+
 /* Used for Spansion flashes only. */
 #define SPINOR_OP_BRWR		0x17	/* Bank register write */
 #define SPINOR_OP_BRRD		0x16	/* Bank register read */