[PATCH linux-next v6 3/8] mtd: spi-nor: update the SPI protocol when enabling manufacturer Quad mode

From: Cyrille Pitchen
Date: Wed Sep 09 2015 - 09:25:29 EST


Micron:
Once their Quad SPI protocol enabled, Micron spi-nor memories expect all
commands to use the SPI 4-4-4 protocol. Also when the Dual SPI protocol is
enabled, all commands must use the SPI 2-2-2 protocol.

Macronix:
When the QPI mode is enabled, all commands must use the SPI 4-4-4 protocol.

Spansion:
When Quad I/O operations are enabled, the Fast Read Quad Output (0x6b / 0x6c)
commands use the SPI 1-1-4 protocol, the Page Program Quad Output (0x32 /
0x34) command use the SPI 1-1-4 protocol whereas other commands use the
SPI 1-1-1 protocol.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@xxxxxxxxx>
---
drivers/mtd/spi-nor/spi-nor.c | 74 ++++++++++++++++++++++++++++++++++++++++---
include/linux/mtd/spi-nor.h | 1 +
2 files changed, 70 insertions(+), 5 deletions(-)

diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 80a0db078aaa..4b36aada3f4c 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -891,6 +891,12 @@ static int macronix_quad_enable(struct spi_nor *nor)
nor->cmd_buf[0] = val | SR_QUAD_EN_MX;
nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0);

+ /* Once QPI mode enabled, all commands use SPI 4-4-4 protocol. */
+ nor->erase_proto = SPI_PROTO_4_4_4;
+ nor->read_proto = SPI_PROTO_4_4_4;
+ nor->write_proto = SPI_PROTO_4_4_4;
+ nor->reg_proto = SPI_PROTO_4_4_4;
+
if (spi_nor_wait_till_ready(nor))
return 1;

@@ -938,10 +944,16 @@ static int spansion_quad_enable(struct spi_nor *nor)
return -EINVAL;
}

+ /* set read/write protocols */
+ nor->read_proto = SPI_PROTO_1_1_4;
+ nor->write_proto = SPI_PROTO_1_1_4;
+
return 0;
}

-static int micron_quad_enable(struct spi_nor *nor)
+static int micron_set_protocol(struct spi_nor *nor,
+ u8 mask, u8 value,
+ enum spi_protocol proto)
{
int ret;
u8 val;
@@ -954,14 +966,20 @@ static int micron_quad_enable(struct spi_nor *nor)

write_enable(nor);

- /* set EVCR, enable quad I/O */
- nor->cmd_buf[0] = val & ~EVCR_QUAD_EN_MICRON;
+ /* set EVCR protocol bits */
+ nor->cmd_buf[0] = (val & ~mask) | value;
ret = nor->write_reg(nor, SPINOR_OP_WD_EVCR, nor->cmd_buf, 1, 0);
if (ret < 0) {
dev_err(nor->dev, "error while writing EVCR register\n");
return ret;
}

+ /* switch protocol for ALL commands */
+ nor->erase_proto = proto;
+ nor->read_proto = proto;
+ nor->write_proto = proto;
+ nor->reg_proto = proto;
+
ret = spi_nor_wait_till_ready(nor);
if (ret)
return ret;
@@ -972,14 +990,23 @@ static int micron_quad_enable(struct spi_nor *nor)
dev_err(nor->dev, "error %d reading EVCR\n", ret);
return ret;
}
- if (val & EVCR_QUAD_EN_MICRON) {
- dev_err(nor->dev, "Micron EVCR Quad bit not clear\n");
+ if ((val & mask) != value) {
+ dev_err(nor->dev, "Micron EVCR protocol bits not valid\n");
return -EINVAL;
}

return 0;
}

+static inline int micron_quad_enable(struct spi_nor *nor)
+{
+ /* Clear Quad bit to enable quad mode */
+ return micron_set_protocol(nor,
+ EVCR_QUAD_EN_MICRON | EVCR_DUAL_EN_MICRON,
+ EVCR_DUAL_EN_MICRON,
+ SPI_PROTO_4_4_4);
+}
+
static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
{
int status;
@@ -1009,6 +1036,38 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
}
}

+static inline int micron_dual_enable(struct spi_nor *nor)
+{
+ /* Clear Dual bit but keep Quad bit set to enable dual mode */
+ return micron_set_protocol(nor,
+ EVCR_QUAD_EN_MICRON | EVCR_DUAL_EN_MICRON,
+ EVCR_QUAD_EN_MICRON,
+ SPI_PROTO_2_2_2);
+}
+
+static int set_dual_mode(struct spi_nor *nor, const struct flash_info *info)
+{
+ int status;
+
+ switch (JEDEC_MFR(info)) {
+ case CFI_MFR_ST:
+ status = micron_dual_enable(nor);
+ if (status) {
+ dev_err(nor->dev, "Micron dual-read not enabled\n");
+ return -EINVAL;
+ }
+ return status;
+ case CFI_MFR_MACRONIX:
+ case CFI_MFR_AMD:
+ nor->read_proto = SPI_PROTO_1_1_2;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static int spi_nor_check(struct spi_nor *nor)
{
if (!nor->dev || !nor->read || !nor->write ||
@@ -1160,6 +1219,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
}
nor->flash_read = SPI_NOR_QUAD;
} else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) {
+ ret = set_dual_mode(nor, info);
+ if (ret) {
+ dev_err(dev, "dual mode not supported\n");
+ return ret;
+ }
nor->flash_read = SPI_NOR_DUAL;
}

diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 08e405cbb6af..ce81b0e2cb37 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -74,6 +74,7 @@

/* Enhanced Volatile Configuration Register bits */
#define EVCR_QUAD_EN_MICRON 0x80 /* Micron Quad I/O */
+#define EVCR_DUAL_EN_MICRON 0x40 /* Micron Dual I/O */

/* Flag Status Register bits */
#define FSR_READY 0x80
--
1.8.2.2

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/