From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932790AbcBCN2p (ORCPT ); Wed, 3 Feb 2016 08:28:45 -0500 Received: from eusmtp01.atmel.com ([212.144.249.243]:48461 "EHLO eusmtp01.atmel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755751AbcBCN2n (ORCPT ); Wed, 3 Feb 2016 08:28:43 -0500 From: Cyrille Pitchen To: , CC: , , , , , , , , , , , , , Cyrille Pitchen Subject: [PATCH v3 05/14] mtd: spi-nor: fix support of Winbond memories Date: Wed, 3 Feb 2016 14:26:50 +0100 Message-ID: X-Mailer: git-send-email 1.8.2.2 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 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 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