From 96b232d03a0c4462eacf879ed80b0cfd235e65c4 Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Fri, 8 Jan 2016 17:02:17 +0100 Subject: [PATCH 20/33] mtd: spi-nor: fix support of Winbond memories 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 --- 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 1b1f5a6..aa7d26d 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1166,6 +1166,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; @@ -1226,6 +1260,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; @@ -1234,6 +1325,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) @@ -1263,6 +1357,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) @@ -1284,6 +1381,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 89e3228..46343f5 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 */ -- 2.8.1