[RFC,03/34] mtd: spi-nor: Create a ->set_4byte() method
diff mbox series

Message ID 20181207092637.18687-4-boris.brezillon@bootlin.com
State Under Review
Delegated to: Ambarus Tudor
Headers show
Series
  • mtd: spi-nor: Move manufacturer/SFDP code out of the core
Related show

Commit Message

Boris Brezillon Dec. 7, 2018, 9:26 a.m. UTC
The procedure used to enable 4 byte addressing mode depends on the NOR
device, so let's provide a hook so that manufacturer specific handling
can be implemented in a sane way.

This is also an opportunity to create a per-manufacturer post SFDP
fixups function where we'll put all the manufacturer specific tweaks.

Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
---
 drivers/mtd/spi-nor/spi-nor.c | 138 +++++++++++++++++++++++-----------
 include/linux/mtd/spi-nor.h   |   3 +
 2 files changed, 99 insertions(+), 42 deletions(-)

Patch
diff mbox series

diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 6b458ff4effa..bea267c446b5 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -470,46 +470,15 @@  static void spi_nor_set_4byte_opcodes(struct spi_nor *nor)
 /* Enable/disable 4-byte addressing mode. */
 static int set_4byte(struct spi_nor *nor, bool enable)
 {
-	int status;
-	bool need_wren = false;
-	u8 cmd;
+	if (nor->set_4byte)
+		return nor->set_4byte(nor, enable);
 
-	switch (JEDEC_MFR(nor->info)) {
-	case SNOR_MFR_ST:
-	case SNOR_MFR_MICRON:
-		/* Some Micron need WREN command; all will accept it */
-		need_wren = true;
-		/* fall through */
-	case SNOR_MFR_MACRONIX:
-	case SNOR_MFR_WINBOND:
-		if (need_wren)
-			write_enable(nor);
-
-		cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B;
-		status = nor->write_reg(nor, cmd, NULL, 0);
-		if (need_wren)
-			write_disable(nor);
-
-		if (!status && !enable &&
-		    JEDEC_MFR(nor->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);
-		}
-
-		return status;
-	default:
-		/* Spansion style */
-		nor->cmd_buf[0] = enable << 7;
-		return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1);
-	}
+	/*
+	 * Spansion style. Should work for all NORs that do not have their own
+	 * ->set_4byte() implementation.
+	 */
+	nor->cmd_buf[0] = enable << 7;
+	return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1);
 }
 
 static int s3an_sr_ready(struct spi_nor *nor)
@@ -3666,13 +3635,98 @@  void spi_nor_restore(struct spi_nor *nor)
 }
 EXPORT_SYMBOL_GPL(spi_nor_restore);
 
+static int macronix_set_4byte(struct spi_nor *nor, bool enable)
+{
+	return nor->write_reg(nor, enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B,
+			      NULL, 0);
+}
+
+static int st_micron_set_4byte(struct spi_nor *nor, bool enable)
+{
+	int ret;
+
+	write_enable(nor);
+	ret = macronix_set_4byte(nor, enable);
+	write_disable(nor);
+
+	return ret;
+}
+
+static int winbond_set_4byte(struct spi_nor *nor, bool enable)
+{
+	int ret;
+
+	ret = macronix_set_4byte(nor, enable);
+	if (ret || enable)
+		return ret;
+
+	/*
+	 * 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);
+
+	return ret;
+}
+
+static void st_micron_post_sfdp_fixups(struct spi_nor *nor)
+{
+	nor->set_4byte = st_micron_set_4byte;
+}
+
+static void macronix_post_sfdp_fixups(struct spi_nor *nor)
+{
+	nor->set_4byte = macronix_set_4byte;
+}
+
+static void winbond_post_sfdp_fixups(struct spi_nor *nor)
+{
+	nor->set_4byte = winbond_set_4byte;
+}
+
+static int
+spi_nor_manufacturer_post_sfdp_fixups(struct spi_nor *nor,
+				      struct spi_nor_flash_parameter *params)
+{
+	switch (JEDEC_MFR(nor->info)) {
+	case SNOR_MFR_ST:
+	case SNOR_MFR_MICRON:
+		st_micron_post_sfdp_fixups(nor);
+		break;
+
+	case SNOR_MFR_MACRONIX:
+		macronix_post_sfdp_fixups(nor);
+		break;
+
+	case SNOR_MFR_WINBOND:
+		winbond_post_sfdp_fixups(nor);
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
 static int spi_nor_post_sfdp_fixups(struct spi_nor *nor,
 				    struct spi_nor_flash_parameter *params)
 {
-	if (nor->info->fixups && nor->info->fixups->post_sfdp)
-		return nor->info->fixups->post_sfdp(nor, params);
+	int ret;
 
-	return 0;
+	ret = spi_nor_manufacturer_post_sfdp_fixups(nor, params);
+	if (ret)
+		return ret;
+
+	if (nor->info->fixups && nor->info->fixups->post_sfdp)
+		ret = nor->info->fixups->post_sfdp(nor, params);
+
+	return ret;
 }
 
 static const struct flash_info *spi_nor_match_id(const char *name)
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 5f177aa39f68..d28a9913b165 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -365,6 +365,8 @@  struct flash_info;
  * @flash_is_locked:	[FLASH-SPECIFIC] check if a region of the SPI NOR is
  * @quad_enable:	[FLASH-SPECIFIC] enables SPI NOR quad mode
  *			completely locked
+ * @set_4byte:		[FLASH-SPECIFIC] put the SPI NOR in 4 byte addressing
+ *			mode
  * @priv:		the private data
  */
 struct spi_nor {
@@ -401,6 +403,7 @@  struct spi_nor {
 	int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
 	int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len);
 	int (*quad_enable)(struct spi_nor *nor);
+	int (*set_4byte)(struct spi_nor *nor, bool enable);
 
 	void *priv;
 };