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 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html