From be192209d5a33c912caa4a05d6f92b89328d8db8 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Mon, 6 Jul 2020 17:22:35 +0800 Subject: mtd: spi-nor: Add capability to disable flash quad mode Previous we didn't provide a way to disable the flash's quad mode. Which means we cannot do some cleanup works when to remove or poweroff the flash, like what set 4-byte address mode does in spi_nor_restore(). Add the capability to disable the flash quad mode, by introducing an enable flag in the flash parameters quad_enable() hooks and related functions. Signed-off-by: Yicong Yang Signed-off-by: Tudor Ambarus Reviewed-by: Pratyush Yadav Link: https://lore.kernel.org/r/1594027356-19088-2-git-send-email-yangyicong@hisilicon.com --- drivers/mtd/spi-nor/core.c | 55 ++++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 19 deletions(-) (limited to 'drivers/mtd/spi-nor/core.c') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 0369d98b2d12..ab8f55438e21 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -1907,15 +1907,16 @@ static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) } /** - * spi_nor_sr1_bit6_quad_enable() - Set the Quad Enable BIT(6) in the Status - * Register 1. + * spi_nor_sr1_bit6_quad_enable() - Set/Unset the Quad Enable BIT(6) in the + * Status Register 1. * @nor: pointer to a 'struct spi_nor' + * @enable: true to enable Quad mode, false to disable Quad mode. * * Bit 6 of the Status Register 1 is the QE bit for Macronix like QSPI memories. * * Return: 0 on success, -errno otherwise. */ -int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor) +int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor, bool enable) { int ret; @@ -1923,45 +1924,56 @@ int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor) if (ret) return ret; - if (nor->bouncebuf[0] & SR1_QUAD_EN_BIT6) + if ((enable && (nor->bouncebuf[0] & SR1_QUAD_EN_BIT6)) || + (!enable && !(nor->bouncebuf[0] & SR1_QUAD_EN_BIT6))) return 0; - nor->bouncebuf[0] |= SR1_QUAD_EN_BIT6; + if (enable) + nor->bouncebuf[0] |= SR1_QUAD_EN_BIT6; + else + nor->bouncebuf[0] &= ~SR1_QUAD_EN_BIT6; return spi_nor_write_sr1_and_check(nor, nor->bouncebuf[0]); } /** - * spi_nor_sr2_bit1_quad_enable() - set the Quad Enable BIT(1) in the Status - * Register 2. + * spi_nor_sr2_bit1_quad_enable() - set/unset the Quad Enable BIT(1) in the + * Status Register 2. * @nor: pointer to a 'struct spi_nor'. + * @enable: true to enable Quad mode, false to disable Quad mode. * * Bit 1 of the Status Register 2 is the QE bit for Spansion like QSPI memories. * * Return: 0 on success, -errno otherwise. */ -int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor) +int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor, bool enable) { int ret; if (nor->flags & SNOR_F_NO_READ_CR) - return spi_nor_write_16bit_cr_and_check(nor, SR2_QUAD_EN_BIT1); + return spi_nor_write_16bit_cr_and_check(nor, + enable ? SR2_QUAD_EN_BIT1 : 0); ret = spi_nor_read_cr(nor, nor->bouncebuf); if (ret) return ret; - if (nor->bouncebuf[0] & SR2_QUAD_EN_BIT1) + if ((enable && (nor->bouncebuf[0] & SR2_QUAD_EN_BIT1)) || + (!enable && !(nor->bouncebuf[0] & SR2_QUAD_EN_BIT1))) return 0; - nor->bouncebuf[0] |= SR2_QUAD_EN_BIT1; + if (enable) + nor->bouncebuf[0] |= SR2_QUAD_EN_BIT1; + else + nor->bouncebuf[0] &= ~SR2_QUAD_EN_BIT1; return spi_nor_write_16bit_cr_and_check(nor, nor->bouncebuf[0]); } /** - * spi_nor_sr2_bit7_quad_enable() - set QE bit in Status Register 2. + * spi_nor_sr2_bit7_quad_enable() - set/unset QE bit in Status Register 2. * @nor: pointer to a 'struct spi_nor' + * @enable: true to enable Quad mode, false to disable Quad mode. * * Set the Quad Enable (QE) bit in the Status Register 2. * @@ -1971,7 +1983,7 @@ int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor) * * Return: 0 on success, -errno otherwise. */ -int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor) +int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor, bool enable) { u8 *sr2 = nor->bouncebuf; int ret; @@ -1981,11 +1993,15 @@ int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor) ret = spi_nor_read_sr2(nor, sr2); if (ret) return ret; - if (*sr2 & SR2_QUAD_EN_BIT7) + if ((enable && (*sr2 & SR2_QUAD_EN_BIT7)) || + (!enable && !(*sr2 & SR2_QUAD_EN_BIT7))) return 0; /* Update the Quad Enable bit. */ - *sr2 |= SR2_QUAD_EN_BIT7; + if (enable) + *sr2 |= SR2_QUAD_EN_BIT7; + else + *sr2 &= ~SR2_QUAD_EN_BIT7; ret = spi_nor_write_sr2(nor, sr2); if (ret) @@ -2898,12 +2914,13 @@ static int spi_nor_init_params(struct spi_nor *nor) } /** - * spi_nor_quad_enable() - enable Quad I/O if needed. + * spi_nor_quad_enable() - enable/disable Quad I/O if needed. * @nor: pointer to a 'struct spi_nor' + * @enable: true to enable Quad mode. false to disable Quad mode. * * Return: 0 on success, -errno otherwise. */ -static int spi_nor_quad_enable(struct spi_nor *nor) +static int spi_nor_quad_enable(struct spi_nor *nor, bool enable) { if (!nor->params->quad_enable) return 0; @@ -2912,7 +2929,7 @@ static int spi_nor_quad_enable(struct spi_nor *nor) spi_nor_get_protocol_width(nor->write_proto) == 4)) return 0; - return nor->params->quad_enable(nor); + return nor->params->quad_enable(nor, enable); } /** @@ -2936,7 +2953,7 @@ static int spi_nor_init(struct spi_nor *nor) { int err; - err = spi_nor_quad_enable(nor); + err = spi_nor_quad_enable(nor, true); if (err) { dev_dbg(nor->dev, "quad mode not supported\n"); return err; -- cgit v1.2.3 From cc59e6bb6cd69d3347c06ccce088c5c6052e041e Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Mon, 6 Jul 2020 17:22:36 +0800 Subject: mtd: spi-nor: Disable the flash quad mode in spi_nor_restore() If the flash's quad mode is enabled, it'll remain in the quad mode when it's removed. If we drive the flash next time in Standard/Dual SPI mode, the QE bit is not cleared and the function of flash's WP# and RESET#/HOLD# have been switched to IO2 and IO3 and are not restored. Disable the Quad mode in spi_nor_restore(), then the flash's QE bit will be cleared when removed. This will make sure the flash always enter the Standard/Dual SPI mode when loaded. Signed-off-by: Yicong Yang Signed-off-by: Tudor Ambarus Reviewed-by: Pratyush Yadav Link: https://lore.kernel.org/r/1594027356-19088-3-git-send-email-yangyicong@hisilicon.com --- drivers/mtd/spi-nor/core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mtd/spi-nor/core.c') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index ab8f55438e21..65eff4ce6ab1 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -3000,6 +3000,8 @@ void spi_nor_restore(struct spi_nor *nor) if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES) && nor->flags & SNOR_F_BROKEN_RESET) nor->params->set_4byte_addr_mode(nor, false); + + spi_nor_quad_enable(nor, false); } EXPORT_SYMBOL_GPL(spi_nor_restore); -- cgit v1.2.3