Thread (16 messages) 16 messages, 2 authors, 2016-02-12
STALE3762d
Revisions (3)
  1. v1 [diff vs current]
  2. v2 [diff vs current]
  3. v3 current

[PATCH v3 05/14] mtd: spi-nor: fix support of Winbond memories

From: Cyrille Pitchen <hidden>
Date: 2016-02-03 13:28:45
Also in: linux-arm-kernel, lkml
Subsystem: memory technology devices (mtd), spi nor subsystem, the rest · Maintainers: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra, Pratyush Yadav, Michael Walle, Linus Torvalds

This patch fixes the support of Winbond memories. Indeed, before
performing any Quad SPI command, the Quad Enable (QE) non-volatile bit
MUST be set in the Status Register 2.

According to the w25q16fw datasheet from Winbond:
"When QE=1, the /WP pin becomes IO2 and /HOLD pin becomes IO3."

Quad SPI instructions requires the bidirectional IO2 and IO3 pins.

Signed-off-by: Cyrille Pitchen <redacted>
---
 drivers/mtd/spi-nor/spi-nor.c | 100 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/mtd/spi-nor.h   |   6 +++
 2 files changed, 106 insertions(+)
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 2b2572e58a91..980da91e84a4 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -1147,6 +1147,40 @@ static int spansion_quad_enable(struct spi_nor *nor)
 	return 0;
 }
 
+static int winbond_quad_enable(struct spi_nor *nor)
+{
+	int ret;
+	u8 sr2;
+
+	ret = nor->read_reg(nor, SPINOR_OP_RDSR2_WINB, &sr2, 1);
+	if (ret < 0)
+		return ret;
+
+	if (likely(sr2 & SR2_QUAD_EN_WINB))
+		return 0;
+	dev_warn(nor->dev, "Winbond Quad mode disabled, enable it\n");
+
+	write_enable(nor);
+
+	sr2 |= SR2_QUAD_EN_WINB;
+	ret = nor->write_reg(nor, SPINOR_OP_WRSR2_WINB, &sr2, 1);
+	if (ret < 0)
+		return ret;
+
+	if (spi_nor_wait_till_ready(nor))
+		return -EIO;
+
+	ret = nor->read_reg(nor, SPINOR_OP_RDSR2_WINB, &sr2, 1);
+	if (ret < 0)
+		return ret;
+	if (!(sr2 & SR2_QUAD_EN_WINB)) {
+		dev_err(nor->dev, "Winbond Quad bit not set\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int macronix_set_quad_mode(struct spi_nor *nor)
 {
 	int status;
@@ -1207,6 +1241,63 @@ static int macronix_set_single_mode(struct spi_nor *nor)
 	return 0;
 }
 
+static int winbond_set_quad_mode(struct spi_nor *nor)
+{
+	int status;
+
+	/* Check whether the QPI mode is enabled. */
+	if (nor->read_proto == SNOR_PROTO_4_4_4) {
+		/* Since the QPI mode is enabled, the Quad Enabled (QE)
+		 * non-volatile bit is already set.
+		 * If the Fast Read 1-4-4 (0xeb) were used, we should
+		 * take care about the value M7-M0 written during
+		 * dummy/mode cycles to avoid entering the continuous
+		 * read mode by  mistake.
+		 * Also the Fast Read 1-1-4 (0x6b) op code is not
+		 * supported in QPI mode.
+		 * Hence the Fast Read 1-1-1 (0x0b) op code is chosen.
+		 */
+		nor->read_opcode = SPINOR_OP_READ_FAST;
+		return 0;
+	}
+
+	/*
+	 * The QPI mode is disabled but we still need to set the QE bit:
+	 * when QE=1, the /WP pin becomes IO2 and /HOLD pin becomes IO3.
+	 * If the Fast Read 1-4-4 (0xeb) were used, we should take care
+	 * about the value M7-M0 written during dummy/mode cycles to
+	 * avoid entering the continuous read mode by mistake.
+	 * Hence the Fast Read 1-1-4 (0x6b) op code is preferred.
+	 */
+	status = winbond_quad_enable(nor);
+	if (status) {
+		dev_err(nor->dev, "Winbond quad-read nor enabled\n");
+		return status;
+	}
+	nor->read_proto = SNOR_PROTO_1_1_4;
+	nor->read_opcode = SPINOR_OP_READ_1_1_4;
+	return 0;
+}
+
+/*
+ * For both Winbond Dual and Single modes, we don't care about the value of
+ * the Quad Enabled (QE) bit since the memory still replies to Dual or Single
+ * SPI commands.
+ */
+
+static int winbond_set_dual_mode(struct spi_nor *nor)
+{
+	nor->read_proto = SNOR_PROTO_1_1_2;
+	nor->read_opcode = SPINOR_OP_READ_1_1_2;
+	return 0;
+}
+
+static int winbond_set_single_mode(struct spi_nor *nor)
+{
+	nor->read_proto = SNOR_PROTO_1_1_1;
+	return 0;
+}
+
 static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
 {
 	int status;
@@ -1215,6 +1306,9 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
 	case SNOR_MFR_MACRONIX:
 		return macronix_set_quad_mode(nor);
 
+	case SNOR_MFR_WINBOND:
+		return winbond_set_quad_mode(nor);
+
 	case SNOR_MFR_MICRON:
 		/* Check whether Micron Quad mode is enabled. */
 		if (nor->read_proto != SNOR_PROTO_4_4_4)
@@ -1244,6 +1338,9 @@ static int set_dual_mode(struct spi_nor *nor, const struct flash_info *info)
 	case SNOR_MFR_MACRONIX:
 		return macronix_set_dual_mode(nor);
 
+	case SNOR_MFR_WINBOND:
+		return winbond_set_dual_mode(nor);
+
 	case SNOR_MFR_MICRON:
 		/* Check whether Micron Dual mode is enabled. */
 		if (nor->read_proto != SNOR_PROTO_2_2_2)
@@ -1265,6 +1362,9 @@ static int set_single_mode(struct spi_nor *nor, const struct flash_info *info)
 	case SNOR_MFR_MACRONIX:
 		return macronix_set_single_mode(nor);
 
+	case SNOR_MFR_WINBOND:
+		return winbond_set_single_mode(nor);
+
 	default:
 		nor->read_proto = SNOR_PROTO_1_1_1;
 		break;
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 89e3228ec1d0..46343f5a8162 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -75,6 +75,12 @@
 #define SPINOR_OP_EN4B		0xb7	/* Enter 4-byte mode */
 #define SPINOR_OP_EX4B		0xe9	/* Exit 4-byte mode */
 
+/* Used for Winbond flashes only. */
+#define SPINOR_OP_RDSR2_WINB	0x35	/* Read status register 2 */
+#define SPINOR_OP_WRSR2_WINB	0x31	/* Write status register 2 */
+
+#define SR2_QUAD_EN_WINB	BIT(1)	/* Quad Enable bit */
+
 /* Used for Spansion flashes only. */
 #define SPINOR_OP_BRWR		0x17	/* Bank register write */
 
-- 
1.8.2.2
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help