diff options
author | Takahiro Kuwano <Takahiro.Kuwano@infineon.com> | 2023-04-07 08:41:03 +0200 |
---|---|---|
committer | Tudor Ambarus <tudor.ambarus@linaro.org> | 2023-04-08 08:30:17 +0200 |
commit | 91f3c430f622befb1b11f07df3bf7163f46adda6 (patch) | |
tree | 271f32374d36a1b069d0e5ed982194b1fc713334 /drivers/mtd | |
parent | mtd: spi-nor: spansion: Rework cypress_nor_quad_enable_volatile() for multi-c... (diff) | |
download | linux-91f3c430f622befb1b11f07df3bf7163f46adda6.tar.xz linux-91f3c430f622befb1b11f07df3bf7163f46adda6.zip |
mtd: spi-nor: spansion: Add a new ->ready() hook for multi-chip device
For multi-chip devices, we need to make sure the all dice in the device
are ready. The cypress_nor_sr_ready_and_clear() reads SR in each die and
returns true only when all dice are ready. This function also takes care
for program or erase error handling by reusing spansion_nor_clear_sr().
To do that, spansion_nor_clear_sr() is moved to top.
Signed-off-by: Takahiro Kuwano <Takahiro.Kuwano@infineon.com>
Link: https://lore.kernel.org/r/3e4a64613ee733e002279349c75083433be45bf5.1680849425.git.Takahiro.Kuwano@infineon.com
Signed-off-by: Tudor Ambarus <tudor.ambarus@linaro.org>
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/spi-nor/spansion.c | 109 |
1 files changed, 85 insertions, 24 deletions
diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index 91bc0aace516..deb93ab6fe98 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -63,6 +63,84 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DATA) +/** + * spansion_nor_clear_sr() - Clear the Status Register. + * @nor: pointer to 'struct spi_nor'. + */ +static void spansion_nor_clear_sr(struct spi_nor *nor) +{ + int ret; + + if (nor->spimem) { + struct spi_mem_op op = SPANSION_CLSR_OP; + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + + ret = spi_mem_exec_op(nor->spimem, &op); + } else { + ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_CLSR, + NULL, 0); + } + + if (ret) + dev_dbg(nor->dev, "error %d clearing SR\n", ret); +} + +static int cypress_nor_sr_ready_and_clear_reg(struct spi_nor *nor, u64 addr) +{ + struct spi_mem_op op = + CYPRESS_NOR_RD_ANY_REG_OP(nor->params->addr_mode_nbytes, addr, + 0, nor->bouncebuf); + int ret; + + ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); + if (ret) + return ret; + + if (nor->bouncebuf[0] & (SR_E_ERR | SR_P_ERR)) { + if (nor->bouncebuf[0] & SR_E_ERR) + dev_err(nor->dev, "Erase Error occurred\n"); + else + dev_err(nor->dev, "Programming Error occurred\n"); + + spansion_nor_clear_sr(nor); + + ret = spi_nor_write_disable(nor); + if (ret) + return ret; + + return -EIO; + } + + return !(nor->bouncebuf[0] & SR_WIP); +} +/** + * cypress_nor_sr_ready_and_clear() - Query the Status Register of each die by + * using Read Any Register command to see if the whole flash is ready for new + * commands and clear it if there are any errors. + * @nor: pointer to 'struct spi_nor'. + * + * Return: 1 if ready, 0 if not ready, -errno on errors. + */ +static int cypress_nor_sr_ready_and_clear(struct spi_nor *nor) +{ + struct spi_nor_flash_parameter *params = nor->params; + u64 addr; + int ret; + u8 i; + + for (i = 0; i < params->n_dice; i++) { + addr = params->vreg_offset[i] + SPINOR_REG_CYPRESS_STR1; + ret = cypress_nor_sr_ready_and_clear_reg(nor, addr); + if (ret < 0) + return ret; + else if (ret == 0) + return 0; + } + + return 1; +} + static int cypress_nor_octal_dtr_en(struct spi_nor *nor) { struct spi_mem_op op; @@ -506,10 +584,16 @@ static int s25hx_t_post_sfdp_fixup(struct spi_nor *nor) static void s25hx_t_late_init(struct spi_nor *nor) { + struct spi_nor_flash_parameter *params = nor->params; + /* Fast Read 4B requires mode cycles */ - nor->params->reads[SNOR_CMD_READ_FAST].num_mode_clocks = 8; + params->reads[SNOR_CMD_READ_FAST].num_mode_clocks = 8; cypress_nor_ecc_init(nor); + + /* Replace ready() with multi die version */ + if (params->n_dice) + params->ready = cypress_nor_sr_ready_and_clear; } static struct spi_nor_fixups s25hx_t_fixups = { @@ -742,29 +826,6 @@ static const struct flash_info spansion_nor_parts[] = { }; /** - * spansion_nor_clear_sr() - Clear the Status Register. - * @nor: pointer to 'struct spi_nor'. - */ -static void spansion_nor_clear_sr(struct spi_nor *nor) -{ - int ret; - - if (nor->spimem) { - struct spi_mem_op op = SPANSION_CLSR_OP; - - spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); - - ret = spi_mem_exec_op(nor->spimem, &op); - } else { - ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_CLSR, - NULL, 0); - } - - if (ret) - dev_dbg(nor->dev, "error %d clearing SR\n", ret); -} - -/** * spansion_nor_sr_ready_and_clear() - Query the Status Register to see if the * flash is ready for new commands and clear it if there are any errors. * @nor: pointer to 'struct spi_nor'. |