[PATCH 1/4] mtd: spi-nor: Rework set_4byte()

tkuw584924 at gmail.com tkuw584924 at gmail.com
Thu Aug 8 08:00:07 CEST 2024


From: Takahiro Kuwano <Takahiro.Kuwano at infineon.com>

Synchronize set_4byte() with Linux v6.10 as much as possible.

Introduce {nor, params}->set_4byte_addr_mode().
The params->set_4byte_addr_mode is initialized with one of the
manufacturer specific methods, copied to nor->set_4byte_addr_mode,
then it is called from spi_nor_set_4byte_addr_mode() renamed from
set_4byte().

There are still manufacturer checks here and there.
And The set_4byte_addr_mode() method in both nor-> and params-> looks
redundant. Those should be cleaned up separately in another patch set.

The following commits in Linux are related to this patch.
64c160f32235 ("mtd: spi-nor: Create a ->set_4byte() method")
81924dae5194 ("mtd: spi-nor: Emphasise which is the
               genericset_4byte_addr_mode() method")
076aa4eac8b3 ("mtd: spi-nor: core: Move generic method to core -
               micron_st_nor_set_4byte_addr_mode")
288df4378319 ("mtd: spi-nor: core: Update name and description of
               micron_st_nor_set_4byte_addr_mode")
f1f1976224f3 ("mtd: spi-nor: core: Update name and description of
               spansion_set_4byte_addr_mode")
b6094ac83dd4 ("mtd: spi-nor: core: Introduce
               spi_nor_set_4byte_addr_mode()")

Signed-off-by: Takahiro Kuwano <Takahiro.Kuwano at infineon.com>
---
 drivers/mtd/spi/spi-nor-core.c | 179 +++++++++++++++++++++++++--------
 include/linux/mtd/spi-nor.h    |   3 +
 2 files changed, 138 insertions(+), 44 deletions(-)

diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index 8d2afaa0e2..d523c045f4 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -680,54 +680,123 @@ static void spi_nor_set_4byte_opcodes(struct spi_nor *nor,
 }
 #endif /* !CONFIG_SPI_FLASH_BAR */
 
-/* Enable/disable 4-byte addressing mode. */
-static int set_4byte(struct spi_nor *nor, const struct flash_info *info,
-		     int enable)
+/**
+ * spi_nor_set_4byte_addr_mode_en4b_ex4b() - Enter/Exit 4-byte address mode
+ *			using SPINOR_OP_EN4B/SPINOR_OP_EX4B. Typically used by
+ *			Winbond and Macronix. Cypress also uses SPINOR_OP_EN4B
+ *			to enter, but not SPINOR_OP_EX4B to exit.
+ * @nor:	pointer to 'struct spi_nor'.
+ * @enable:	true to enter the 4-byte address mode, false to exit the 4-byte
+ *		address mode.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_set_4byte_addr_mode_en4b_ex4b(struct spi_nor *nor,
+						 bool enable)
 {
-	int status;
-	bool need_wren = false;
-	u8 cmd;
+	u8 opcode;
+	int ret;
 
-	switch (JEDEC_MFR(info)) {
-	case SNOR_MFR_ST:
-	case SNOR_MFR_MICRON:
-		/* Some Micron need WREN command; all will accept it */
-		need_wren = true;
-		fallthrough;
-	case SNOR_MFR_ISSI:
-	case SNOR_MFR_MACRONIX:
-	case SNOR_MFR_WINBOND:
-		if (need_wren)
-			write_enable(nor);
+	if (enable)
+		opcode = SPINOR_OP_EN4B;
+	else if (JEDEC_MFR(nor->info) == SNOR_MFR_CYPRESS)
+		opcode = SPINOR_OP_EX4B_CYPRESS;
+	else
+		opcode = SPINOR_OP_EX4B;
 
-		cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B;
-		status = nor->write_reg(nor, cmd, NULL, 0);
-		if (need_wren)
-			write_disable(nor);
+	ret = nor->write_reg(nor, opcode, NULL, 0);
+	if (ret)
+		return ret;
 
-		if (!status && !enable &&
-		    JEDEC_MFR(info) == SNOR_MFR_WINBOND) {
-			/*
-			 * On Winbond W25Q256FV, leaving 4byte mode causes
-			 * the Extended Address Register to be set to 1, so all
-			 * 3-byte-address reads come from the second 16M.
-			 * We must clear the register to enable normal behavior.
-			 */
-			write_enable(nor);
-			nor->cmd_buf[0] = 0;
-			nor->write_reg(nor, SPINOR_OP_WREAR, nor->cmd_buf, 1);
-			write_disable(nor);
-		}
+	/*
+	 * On Winbond W25Q256FV, leaving 4byte mode causes the Extended
+	 * Address Register to be set to 1, so all 3-byte-address reads
+	 * come from the second 16M. We must clear the register to
+	 * enable normal behavior.
+	 */
+	if (!enable && JEDEC_MFR(nor->info) == SNOR_MFR_WINBOND) {
+		ret = write_enable(nor);
+		if (ret)
+			return ret;
 
-		return status;
-	case SNOR_MFR_CYPRESS:
-		cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B_CYPRESS;
-		return nor->write_reg(nor, cmd, NULL, 0);
-	default:
-		/* Spansion style */
-		nor->cmd_buf[0] = enable << 7;
-		return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1);
+		nor->cmd_buf[0] = 0;
+		ret = nor->write_reg(nor, SPINOR_OP_WREAR, nor->cmd_buf,
+					1);
+		if (ret)
+			return ret;
+
+		ret = write_disable(nor);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * spi_nor_set_4byte_addr_mode_wren_en4b_ex4b() - Set 4-byte address mode using
+ *			SPINOR_OP_WREN followed by SPINOR_OP_EN4B or
+ *			SPINOR_OP_EX4B. Typically used by ST and Micron flashes.
+ * @nor:	pointer to 'struct spi_nor'.
+ * @enable:	true to enter the 4-byte address mode, false to exit the 4-byte
+ *		address mode.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_set_4byte_addr_mode_wren_en4b_ex4b(struct spi_nor *nor,
+						      bool enable)
+{
+	int ret;
+
+	ret = write_enable(nor);
+	if (ret)
+		return ret;
+
+	ret = spi_nor_set_4byte_addr_mode_en4b_ex4b(nor, enable);
+	if (ret)
+		return ret;
+
+	return write_disable(nor);
+}
+
+/**
+ * spi_nor_set_4byte_addr_mode_brwr() - Set 4-byte address mode using
+ *			SPINOR_OP_BRWR. Typically used by Spansion flashes.
+ * @nor:	pointer to 'struct spi_nor'.
+ * @enable:	true to enter the 4-byte address mode, false to exit the 4-byte
+ *		address mode.
+ *
+ * 8-bit volatile bank register used to define A[30:A24] bits. MSB (bit[7]) is
+ * used to enable/disable 4-byte address mode. When MSB is set to ‘1’, 4-byte
+ * address mode is active and A[30:24] bits are don’t care. Write instruction is
+ * SPINOR_OP_BRWR(17h) with 1 byte of data.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_set_4byte_addr_mode_brwr(struct spi_nor *nor, bool enable)
+{
+	nor->cmd_buf[0] = enable ? BIT(7) : 0;
+	return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1);
+}
+
+/* Enable/disable 4-byte addressing mode. */
+static int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
+{
+	int ret;
+
+	ret = nor->set_4byte_addr_mode(nor, enable);
+	if (ret)
+		return ret;
+
+	if (enable) {
+		nor->addr_width = 4;
+		nor->addr_mode_nbytes = 4;
+	} else {
+		nor->addr_width = 3;
+		nor->addr_mode_nbytes = 3;
 	}
+
+	return 0;
 }
 
 #ifdef CONFIG_SPI_FLASH_SPANSION
@@ -2884,6 +2953,25 @@ static int spi_nor_init_params(struct spi_nor *nor,
 		}
 	}
 
+	/* Select the procedure to enter/exit 4-byte address mode. */
+	switch (JEDEC_MFR(info)) {
+	case SNOR_MFR_ST:
+	case SNOR_MFR_MICRON:
+		params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_wren_en4b_ex4b;
+		break;
+
+	case SNOR_MFR_ISSI:
+	case SNOR_MFR_MACRONIX:
+	case SNOR_MFR_WINBOND:
+	case SNOR_MFR_CYPRESS:
+		params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_en4b_ex4b;
+		break;
+
+	default:
+		params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_brwr;
+		break;
+	}
+
 	/* Override the parameters with data read from SFDP tables. */
 	nor->addr_width = 0;
 	nor->mtd.erasesize = 0;
@@ -3272,6 +3360,9 @@ static int spi_nor_default_setup(struct spi_nor *nor,
 	else
 		nor->quad_enable = NULL;
 
+	/* Enter/Exit 4-byte address mode */
+	nor->set_4byte_addr_mode = params->set_4byte_addr_mode;
+
 	return 0;
 }
 
@@ -3504,7 +3595,7 @@ static int s25_s28_post_bfpt_fixup(struct spi_nor *nor,
 	 */
 	if (params->size > SZ_128M) {
 		if (bfpt->dwords[BFPT_DWORD(16)] & BFPT_DWORD16_EX4B_PWRCYC) {
-			ret = set_4byte(nor, nor->info, 1);
+			ret = spi_nor_set_4byte_addr_mode(nor, true);
 			if (ret)
 				return ret;
 		}
@@ -3923,7 +4014,7 @@ static int spi_nor_init(struct spi_nor *nor)
 		 */
 		if (nor->flags & SNOR_F_BROKEN_RESET)
 			debug("enabling reset hack; may not recover from unexpected reboots\n");
-		set_4byte(nor, nor->info, 1);
+		return spi_nor_set_4byte_addr_mode(nor, true);
 	}
 
 	return 0;
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 0d37a806c4..f5d26447c2 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -446,6 +446,7 @@ struct spi_nor_flash_parameter {
 	struct spi_nor_pp_command	page_programs[SNOR_CMD_PP_MAX];
 
 	int (*quad_enable)(struct spi_nor *nor);
+	int (*set_4byte_addr_mode)(struct spi_nor *nor, bool enable);
 };
 
 /**
@@ -528,6 +529,7 @@ struct spi_flash {
  * @flash_is_unlocked:	[FLASH-SPECIFIC] check if a region of the SPI NOR is
  *			completely unlocked
  * @quad_enable:	[FLASH-SPECIFIC] enables SPI NOR quad mode
+ * @set_4byte_addr_mode:[FLASH-SPECIFIC] enter/exit 4-byte address mode
  * @octal_dtr_enable:	[FLASH-SPECIFIC] enables SPI NOR octal DTR mode.
  * @ready:		[FLASH-SPECIFIC] check if the flash is ready
  * @dirmap:		pointers to struct spi_mem_dirmap_desc for reads/writes.
@@ -579,6 +581,7 @@ struct spi_nor {
 	int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
 	int (*flash_is_unlocked)(struct spi_nor *nor, loff_t ofs, uint64_t len);
 	int (*quad_enable)(struct spi_nor *nor);
+	int (*set_4byte_addr_mode)(struct spi_nor *nor, bool enable);
 	int (*octal_dtr_enable)(struct spi_nor *nor);
 	int (*ready)(struct spi_nor *nor);
 
-- 
2.34.1



More information about the U-Boot mailing list