summaryrefslogtreecommitdiff
path: root/drivers/mtd
diff options
context:
space:
mode:
authorTakahiro Kuwano <Takahiro.Kuwano@infineon.com>2023-04-07 09:41:03 +0300
committerTudor Ambarus <tudor.ambarus@linaro.org>2023-04-08 09:30:17 +0300
commit91f3c430f622befb1b11f07df3bf7163f46adda6 (patch)
tree271f32374d36a1b069d0e5ed982194b1fc713334 /drivers/mtd
parentf24d423a5c4fbcd9789ad54454f7c805798a597a (diff)
downloadlinux-91f3c430f622befb1b11f07df3bf7163f46adda6.tar.xz
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.c109
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'.