
From: Takahiro Kuwano Takahiro.Kuwano@infineon.com
Synchronize set_4byte() with Linux v6.10 as much as possible.
Introduce {nor, params}->set_4byte_addr_mode(). The params->set_4byte_addr_mode is initialized with one of the manufacturer specific methods, copied to nor->set_4byte_addr_mode, then it is called from spi_nor_set_4byte_addr_mode() renamed from set_4byte().
There are still manufacturer checks here and there. And The set_4byte_addr_mode() method in both nor-> and params-> looks redundant. Those should be cleaned up separately in another patch set.
The following commits in Linux are related to this patch. 64c160f32235 ("mtd: spi-nor: Create a ->set_4byte() method") 81924dae5194 ("mtd: spi-nor: Emphasise which is the genericset_4byte_addr_mode() method") 076aa4eac8b3 ("mtd: spi-nor: core: Move generic method to core - micron_st_nor_set_4byte_addr_mode") 288df4378319 ("mtd: spi-nor: core: Update name and description of micron_st_nor_set_4byte_addr_mode") f1f1976224f3 ("mtd: spi-nor: core: Update name and description of spansion_set_4byte_addr_mode") b6094ac83dd4 ("mtd: spi-nor: core: Introduce spi_nor_set_4byte_addr_mode()")
Signed-off-by: Takahiro Kuwano Takahiro.Kuwano@infineon.com --- drivers/mtd/spi/spi-nor-core.c | 179 +++++++++++++++++++++++++-------- include/linux/mtd/spi-nor.h | 3 + 2 files changed, 138 insertions(+), 44 deletions(-)
diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c index 8d2afaa0e2..d523c045f4 100644 --- a/drivers/mtd/spi/spi-nor-core.c +++ b/drivers/mtd/spi/spi-nor-core.c @@ -680,54 +680,123 @@ static void spi_nor_set_4byte_opcodes(struct spi_nor *nor, } #endif /* !CONFIG_SPI_FLASH_BAR */
-/* Enable/disable 4-byte addressing mode. */ -static int set_4byte(struct spi_nor *nor, const struct flash_info *info, - int enable) +/** + * spi_nor_set_4byte_addr_mode_en4b_ex4b() - Enter/Exit 4-byte address mode + * using SPINOR_OP_EN4B/SPINOR_OP_EX4B. Typically used by + * Winbond and Macronix. Cypress also uses SPINOR_OP_EN4B + * to enter, but not SPINOR_OP_EX4B to exit. + * @nor: pointer to 'struct spi_nor'. + * @enable: true to enter the 4-byte address mode, false to exit the 4-byte + * address mode. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_set_4byte_addr_mode_en4b_ex4b(struct spi_nor *nor, + bool enable) { - int status; - bool need_wren = false; - u8 cmd; + u8 opcode; + int ret;
- switch (JEDEC_MFR(info)) { - case SNOR_MFR_ST: - case SNOR_MFR_MICRON: - /* Some Micron need WREN command; all will accept it */ - need_wren = true; - fallthrough; - case SNOR_MFR_ISSI: - case SNOR_MFR_MACRONIX: - case SNOR_MFR_WINBOND: - if (need_wren) - write_enable(nor); + if (enable) + opcode = SPINOR_OP_EN4B; + else if (JEDEC_MFR(nor->info) == SNOR_MFR_CYPRESS) + opcode = SPINOR_OP_EX4B_CYPRESS; + else + opcode = SPINOR_OP_EX4B;
- cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B; - status = nor->write_reg(nor, cmd, NULL, 0); - if (need_wren) - write_disable(nor); + ret = nor->write_reg(nor, opcode, NULL, 0); + if (ret) + return ret;
- if (!status && !enable && - JEDEC_MFR(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); - nor->cmd_buf[0] = 0; - nor->write_reg(nor, SPINOR_OP_WREAR, nor->cmd_buf, 1); - write_disable(nor); - } + /* + * 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. + */ + if (!enable && JEDEC_MFR(nor->info) == SNOR_MFR_WINBOND) { + ret = write_enable(nor); + if (ret) + return ret;
- return status; - case SNOR_MFR_CYPRESS: - cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B_CYPRESS; - return nor->write_reg(nor, cmd, NULL, 0); - default: - /* Spansion style */ - nor->cmd_buf[0] = enable << 7; - return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1); + nor->cmd_buf[0] = 0; + ret = nor->write_reg(nor, SPINOR_OP_WREAR, nor->cmd_buf, + 1); + if (ret) + return ret; + + ret = write_disable(nor); + if (ret) + return ret; + } + + return 0; +} + +/** + * spi_nor_set_4byte_addr_mode_wren_en4b_ex4b() - Set 4-byte address mode using + * SPINOR_OP_WREN followed by SPINOR_OP_EN4B or + * SPINOR_OP_EX4B. Typically used by ST and Micron flashes. + * @nor: pointer to 'struct spi_nor'. + * @enable: true to enter the 4-byte address mode, false to exit the 4-byte + * address mode. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_set_4byte_addr_mode_wren_en4b_ex4b(struct spi_nor *nor, + bool enable) +{ + int ret; + + ret = write_enable(nor); + if (ret) + return ret; + + ret = spi_nor_set_4byte_addr_mode_en4b_ex4b(nor, enable); + if (ret) + return ret; + + return write_disable(nor); +} + +/** + * spi_nor_set_4byte_addr_mode_brwr() - Set 4-byte address mode using + * SPINOR_OP_BRWR. Typically used by Spansion flashes. + * @nor: pointer to 'struct spi_nor'. + * @enable: true to enter the 4-byte address mode, false to exit the 4-byte + * address mode. + * + * 8-bit volatile bank register used to define A[30:A24] bits. MSB (bit[7]) is + * used to enable/disable 4-byte address mode. When MSB is set to ‘1’, 4-byte + * address mode is active and A[30:24] bits are don’t care. Write instruction is + * SPINOR_OP_BRWR(17h) with 1 byte of data. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_set_4byte_addr_mode_brwr(struct spi_nor *nor, bool enable) +{ + nor->cmd_buf[0] = enable ? BIT(7) : 0; + return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1); +} + +/* Enable/disable 4-byte addressing mode. */ +static int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable) +{ + int ret; + + ret = nor->set_4byte_addr_mode(nor, enable); + if (ret) + return ret; + + if (enable) { + nor->addr_width = 4; + nor->addr_mode_nbytes = 4; + } else { + nor->addr_width = 3; + nor->addr_mode_nbytes = 3; } + + return 0; }
#ifdef CONFIG_SPI_FLASH_SPANSION @@ -2884,6 +2953,25 @@ static int spi_nor_init_params(struct spi_nor *nor, } }
+ /* Select the procedure to enter/exit 4-byte address mode. */ + switch (JEDEC_MFR(info)) { + case SNOR_MFR_ST: + case SNOR_MFR_MICRON: + params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_wren_en4b_ex4b; + break; + + case SNOR_MFR_ISSI: + case SNOR_MFR_MACRONIX: + case SNOR_MFR_WINBOND: + case SNOR_MFR_CYPRESS: + params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_en4b_ex4b; + break; + + default: + params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_brwr; + break; + } + /* Override the parameters with data read from SFDP tables. */ nor->addr_width = 0; nor->mtd.erasesize = 0; @@ -3272,6 +3360,9 @@ static int spi_nor_default_setup(struct spi_nor *nor, else nor->quad_enable = NULL;
+ /* Enter/Exit 4-byte address mode */ + nor->set_4byte_addr_mode = params->set_4byte_addr_mode; + return 0; }
@@ -3504,7 +3595,7 @@ static int s25_s28_post_bfpt_fixup(struct spi_nor *nor, */ if (params->size > SZ_128M) { if (bfpt->dwords[BFPT_DWORD(16)] & BFPT_DWORD16_EX4B_PWRCYC) { - ret = set_4byte(nor, nor->info, 1); + ret = spi_nor_set_4byte_addr_mode(nor, true); if (ret) return ret; } @@ -3923,7 +4014,7 @@ static int spi_nor_init(struct spi_nor *nor) */ if (nor->flags & SNOR_F_BROKEN_RESET) debug("enabling reset hack; may not recover from unexpected reboots\n"); - set_4byte(nor, nor->info, 1); + return spi_nor_set_4byte_addr_mode(nor, true); }
return 0; diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 0d37a806c4..f5d26447c2 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -446,6 +446,7 @@ struct spi_nor_flash_parameter { struct spi_nor_pp_command page_programs[SNOR_CMD_PP_MAX];
int (*quad_enable)(struct spi_nor *nor); + int (*set_4byte_addr_mode)(struct spi_nor *nor, bool enable); };
/** @@ -528,6 +529,7 @@ struct spi_flash { * @flash_is_unlocked: [FLASH-SPECIFIC] check if a region of the SPI NOR is * completely unlocked * @quad_enable: [FLASH-SPECIFIC] enables SPI NOR quad mode + * @set_4byte_addr_mode:[FLASH-SPECIFIC] enter/exit 4-byte address mode * @octal_dtr_enable: [FLASH-SPECIFIC] enables SPI NOR octal DTR mode. * @ready: [FLASH-SPECIFIC] check if the flash is ready * @dirmap: pointers to struct spi_mem_dirmap_desc for reads/writes. @@ -579,6 +581,7 @@ struct spi_nor { int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len); int (*flash_is_unlocked)(struct spi_nor *nor, loff_t ofs, uint64_t len); int (*quad_enable)(struct spi_nor *nor); + int (*set_4byte_addr_mode)(struct spi_nor *nor, bool enable); int (*octal_dtr_enable)(struct spi_nor *nor); int (*ready)(struct spi_nor *nor);