From: Boris Brezillon <boris.brezillon@xxxxxxxxxxx> The procedure used to enable 4 byte addressing mode depends on the NOR device, so let's provide a hook so that manufacturer specific handling can be implemented in a sane way. set_4byte methods can be amended when parsing BFPT. Signed-off-by: Boris Brezillon <boris.brezillon@xxxxxxxxxxx> Signed-off-by: Tudor Ambarus <tudor.ambarus@xxxxxxxxxxxxx> --- drivers/mtd/spi-nor/spi-nor.c | 119 ++++++++++++++++++++++-------------------- include/linux/mtd/spi-nor.h | 3 ++ 2 files changed, 64 insertions(+), 58 deletions(-) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index b2e72668e7ab..e35aae88d38b 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -758,6 +758,21 @@ static void spi_nor_set_4byte_opcodes(struct spi_nor *nor) } } +static int spi_nor_write_ear(struct spi_nor *nor, u8 ear) +{ + if (nor->spimem) { + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREAR, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(0, NULL, 1)); + + return spi_nor_data_op(nor, &op, &ear, 1); + } + + return nor->write_reg(nor, SPINOR_OP_WREAR, &ear, 1); +} + static int macronix_set_4byte(struct spi_nor *nor, bool enable) { if (nor->spimem) { @@ -777,6 +792,39 @@ static int macronix_set_4byte(struct spi_nor *nor, bool enable) NULL, 0); } +static int st_micron_set_4byte(struct spi_nor *nor, bool enable) +{ + int ret; + + write_enable(nor); + ret = macronix_set_4byte(nor, enable); + write_disable(nor); + + return ret; +} + +static int winbond_set_4byte(struct spi_nor *nor, bool enable) +{ + int ret; + + ret = macronix_set_4byte(nor, enable); + if (ret || enable) + return ret; + + /* + * On Winbond W25Q256FV, leaving 4byte mode causes the Extended Address + * Register to be set to 1, so all 3-byte-address reads come from the + * second 16M. + * We must clear the register to enable normal behavior. + */ + write_enable(nor); + nor->cmd_buf[0] = 0; + ret = spi_nor_write_ear(nor, nor->cmd_buf[0]); + write_disable(nor); + + return ret; +} + static int spansion_set_4byte(struct spi_nor *nor, bool enable) { u8 quad_en = enable << 7; @@ -794,62 +842,6 @@ static int spansion_set_4byte(struct spi_nor *nor, bool enable) return nor->write_reg(nor, SPINOR_OP_BRWR, &quad_en, 1); } -static int spi_nor_write_ear(struct spi_nor *nor, u8 ear) -{ - if (nor->spimem) { - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREAR, 1), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(0, NULL, 1)); - - return spi_nor_data_op(nor, &op, &ear, 1); - } - - return nor->write_reg(nor, SPINOR_OP_WREAR, &ear, 1); -} - -/* Enable/disable 4-byte addressing mode. */ -static int set_4byte(struct spi_nor *nor, bool enable) -{ - int status; - bool need_wren = false; - - switch (JEDEC_MFR(nor->info)) { - case SNOR_MFR_ST: - case SNOR_MFR_MICRON: - /* Some Micron need WREN command; all will accept it */ - need_wren = true; - /* fall through */ - case SNOR_MFR_MACRONIX: - case SNOR_MFR_WINBOND: - if (need_wren) - write_enable(nor); - - status = macronix_set_4byte(nor, enable); - if (need_wren) - write_disable(nor); - - if (!status && !enable && - JEDEC_MFR(nor->info) == SNOR_MFR_WINBOND) { - /* - * On Winbond W25Q256FV, leaving 4byte mode causes - * the Extended Address Register to be set to 1, so all - * 3-byte-address reads come from the second 16M. - * We must clear the register to enable normal behavior. - */ - write_enable(nor); - spi_nor_write_ear(nor, 0); - write_disable(nor); - } - - return status; - default: - /* Spansion style */ - return spansion_set_4byte(nor, enable); - } -} - static int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr) { if (nor->spimem) { @@ -4287,11 +4279,18 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor, static void macronix_set_default_init(struct spi_nor *nor) { nor->quad_enable = macronix_quad_enable; + nor->set_4byte = macronix_set_4byte; } static void st_micron_set_default_init(struct spi_nor *nor) { nor->quad_enable = NULL; + nor->set_4byte = st_micron_set_4byte; +} + +static void winbond_set_default_init(struct spi_nor *nor) +{ + nor->set_4byte = winbond_set_4byte; } static void spi_nor_mfr_init_params(struct spi_nor *nor, @@ -4307,6 +4306,9 @@ static void spi_nor_mfr_init_params(struct spi_nor *nor, st_micron_set_default_init(nor); break; + case SNOR_MFR_WINBOND: + winbond_set_default_init(nor); + break; default: break; } @@ -4685,7 +4687,7 @@ static int spi_nor_init(struct spi_nor *nor) */ WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET, "enabling reset hack; may not recover from unexpected reboots\n"); - set_4byte(nor, true); + nor->set_4byte(nor, true); } return 0; @@ -4709,7 +4711,7 @@ void spi_nor_restore(struct spi_nor *nor) /* restore the addressing mode */ if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES) && nor->flags & SNOR_F_BROKEN_RESET) - set_4byte(nor, false); + nor->set_4byte(nor, false); } EXPORT_SYMBOL_GPL(spi_nor_restore); @@ -4801,6 +4803,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, /* Kept only for backward compatibility purpose. */ nor->quad_enable = spansion_quad_enable; + nor->set_4byte = spansion_set_4byte; /* Init flash parameters based on flash_info struct and SFDP */ spi_nor_init_params(nor, ¶ms); diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 4521a38452d6..a434ab7a53e6 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -378,6 +378,8 @@ struct flash_info; * @flash_unlock: [FLASH-SPECIFIC] unlock a region of the SPI NOR * @flash_is_locked: [FLASH-SPECIFIC] check if a region of the SPI NOR is * @quad_enable: [FLASH-SPECIFIC] enables SPI NOR quad mode + * @set_4byte: [FLASH-SPECIFIC] puts the SPI NOR in 4 byte addressing + * mode * @clear_sr_bp: [FLASH-SPECIFIC] clears the Block Protection Bits from * the SPI NOR Status Register. * completely locked @@ -420,6 +422,7 @@ struct spi_nor { int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len); int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len); int (*quad_enable)(struct spi_nor *nor); + int (*set_4byte)(struct spi_nor *nor, bool enable); int (*clear_sr_bp)(struct spi_nor *nor); void *priv; -- 2.9.5 ______________________________________________________ Linux MTD discussion mailing list http://lists.infradead.org/mailman/listinfo/linux-mtd/