[PATCH v3 11/14] mtd: spi-nor: configure the number of dummy clock cycles on Spansion memories

From: Cyrille Pitchen
Date: Wed Feb 03 2016 - 08:31:54 EST


On Spansion memories, the number of dummy clock cycles to be used during
Fast Read commands is configured through the 2bit latency code (LC). These
bits are non-volatile inside the Configuration Register.

To avoid breaking the configuration expected at reset by some bootloaders,
we'd rather read the latency code and set the nor->read_dummy value
accordingly than update those non-volatile bits.

Since the Quad Enable non-volatile bit can be read at the same time from
the Control Register, we now check its value to avoid some calls of the
spansion_quad_enable() function when they are not needed.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@xxxxxxxxx>
---
drivers/mtd/spi-nor/spi-nor.c | 159 ++++++++++++++++++++++++++++++++++++------
1 file changed, 137 insertions(+), 22 deletions(-)

diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index c560fbbb8479..356db141dcb2 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -1668,47 +1668,162 @@ static int micron_set_single_mode(struct spi_nor *nor)
return micron_set_dummy_cycles(nor, read_dummy);
}

-static int spansion_set_quad_mode(struct spi_nor *nor)
+static inline int spansion_get_config(struct spi_nor *nor,
+ bool *quad_enabled,
+ u8 *latency_code)
{
- int status;
+ int cr;

- status = spansion_quad_enable(nor);
- if (status) {
- dev_err(nor->dev, "Spansion quad-read not enabled\n");
+ cr = read_cr(nor);
+ if (cr < 0) {
+ dev_err(nor->dev,
+ "error while reading the configuration register\n");
+ return cr;
+ }
+
+ if (quad_enabled)
+ *quad_enabled = !!(cr & CR_QUAD_EN_SPAN);
+
+ if (latency_code)
+ *latency_code = (u8)((cr & GENMASK(7, 6)) >> 6);
+
+ return 0;
+}
+
+static int spansion_set_dummy_cycles(struct spi_nor *nor, u8 latency_code)
+{
+ /* SDR dummy cycles */
+ switch (nor->read_opcode) {
+ case SPINOR_OP_READ:
+ case SPINOR_OP_READ4:
+ nor->read_dummy = 0;
+ break;
+
+ case SPINOR_OP_READ_FAST:
+ case SPINOR_OP_READ_1_1_2:
+ case SPINOR_OP_READ_1_1_4:
+ case SPINOR_OP_READ4_FAST:
+ case SPINOR_OP_READ4_1_1_2:
+ case SPINOR_OP_READ4_1_1_4:
+ nor->read_dummy = (latency_code == 3) ? 0 : 8;
+ break;
+
+ case SPINOR_OP_READ_1_2_2:
+ case SPINOR_OP_READ4_1_2_2:
+ switch (latency_code) {
+ default:
+ case 0:
+ case 3:
+ nor->read_dummy = 4;
+ break;
+ case 1:
+ nor->read_dummy = 5;
+ break;
+ case 2:
+ nor->read_dummy = 6;
+ break;
+ }
+ break;
+
+
+ case SPINOR_OP_READ_1_4_4:
+ case SPINOR_OP_READ4_1_4_4:
+ switch (latency_code) {
+ default:
+ case 0:
+ case 1:
+ nor->read_dummy = 4;
+ break;
+ case 2:
+ nor->read_dummy = 5;
+ break;
+ case 3:
+ nor->read_dummy = 1;
+ break;
+ }
+
+ default:
return -EINVAL;
}
+
+ return 0;
+}
+
+static int spansion_set_quad_mode(struct spi_nor *nor)
+{
+ bool quad_enabled;
+ u8 latency_code;
+ int ret;
+
+ /*
+ * The QUAD bit of Configuration Register must be set (CR Bit1=1) for
+ * using any Quad SPI command.
+ */
+ ret = spansion_get_config(nor, &quad_enabled, &latency_code);
+ if (ret)
+ return ret;
+
+ /* The Quad mode should be enabled ... */
+ if (!quad_enabled) {
+ /* ... if not try to enable it. */
+ dev_warn(nor->dev, "Spansion Quad mode disabled, enable it\n");
+ ret = spansion_quad_enable(nor);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Don't use the Fast Read Quad I/O (0xeb / 0xec) commands as their
+ * number of dummy cycles can not be set to a multiple of 8: some SPI
+ * controllers, especially those relying on the m25p80 driver, expect
+ * the number of dummy cycles to be a multiple of 8.
+ * Also when using a Fast Read Quad I/O command, the memory checks the
+ * value of the first mode/dummy cycles to decice whether it enters or
+ * leaves the Countinuous Read mode. We should never enter the
+ * Countinuous Read mode as the spi-nor framework doesn't support it.
+ * For all these reason, we'd rather use the Fast Read Quad Output
+ * 1-1-4 (0x6b / 0x6c) commands instead.
+ */
nor->read_proto = SNOR_PROTO_1_1_4;
nor->read_opcode = SPINOR_OP_READ_1_1_4;
- nor->read_dummy = 8;
- return 0;
+ return spansion_set_dummy_cycles(nor, latency_code);
}

static int spansion_set_dual_mode(struct spi_nor *nor)
{
+ u8 latency_code;
+ int ret;
+
+ /* We don't care about the quad mode status */
+ ret = spansion_get_config(nor, NULL, &latency_code);
+ if (ret)
+ return ret;
+
+ /*
+ * Don't use the Fast Read Dual I/O (0xbb / 0xbc) commands as their
+ * number of dummy cycles can not bet set to a multiple of 8: some SPI
+ * controllers, especially those relying on the m25p80 driver, expect
+ * the number of dummy cycles to be a multiple of 8.
+ * For this reason, w'd rather use the Fast Read Dual Output 1-1-2
+ * (0x3b / 0x3c) commands instead.
+ */
nor->read_proto = SNOR_PROTO_1_1_2;
nor->read_opcode = SPINOR_OP_READ_1_1_2;
- nor->read_dummy = 8;
- return 0;
+ return spansion_set_dummy_cycles(nor, latency_code);
}

static int spansion_set_single_mode(struct spi_nor *nor)
{
- u8 read_dummy;
-
- switch (nor->read_opcode) {
- case SPINOR_OP_READ:
- case SPINOR_OP_READ4:
- read_dummy = 0;
- break;
+ u8 latency_code;
+ int ret;

- default:
- read_dummy = 8;
- break;
- }
+ /* We don't care about the quad mode status */
+ ret = spansion_get_config(nor, NULL, &latency_code);
+ if (ret)
+ return ret;

nor->read_proto = SNOR_PROTO_1_1_1;
- nor->read_dummy = read_dummy;
- return 0;
+ return spansion_set_dummy_cycles(nor, latency_code);
}

static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
--
1.8.2.2