diff options
author | Michael Walle <michael@walle.cc> | 2022-02-23 14:43:50 +0100 |
---|---|---|
committer | Tudor Ambarus <tudor.ambarus@microchip.com> | 2022-02-25 18:12:19 +0200 |
commit | c770abe52d81089a8b8ecd1fe42722e29bbab5f5 (patch) | |
tree | fc7ad385fdbbed5e65f1cd23ade350fb7027556f /drivers/mtd/spi-nor | |
parent | 56b852e8635ba83ecd47ad5f6ad269e8ab782ec8 (diff) | |
download | linux-c770abe52d81089a8b8ecd1fe42722e29bbab5f5.tar.gz |
mtd: spi-nor: move all micron-st specifics into micron-st.c
The flag status register is only available on micron flashes. Move all
the functions around that into the micron module.
This is almost a mechanical move except for the spi_nor_fsr_ready()
which now also checks the normal status register. Previously, this was
done in spi_nor_ready().
Signed-off-by: Michael Walle <michael@walle.cc>
Signed-off-by: Tudor Ambarus <tudor.ambarus@microchip.com>
Tested-by: Pratyush Yadav <p.yadav@ti.com> # on mt35xu512aba, s28hs512t
Reviewed-by: Pratyush Yadav <p.yadav@ti.com>
Link: https://lore.kernel.org/r/20220223134358.1914798-25-michael@walle.cc
Diffstat (limited to 'drivers/mtd/spi-nor')
-rw-r--r-- | drivers/mtd/spi-nor/core.c | 123 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/micron-st.c | 134 |
2 files changed, 135 insertions, 122 deletions
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index ae1560250c48..5b56d718692b 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -413,50 +413,6 @@ int spi_nor_read_sr(struct spi_nor *nor, u8 *sr) } /** - * spi_nor_read_fsr() - Read the Flag Status Register. - * @nor: pointer to 'struct spi_nor' - * @fsr: pointer to a DMA-able buffer where the value of the - * Flag Status Register will be written. Should be at least 2 - * bytes. - * - * Return: 0 on success, -errno otherwise. - */ -static int spi_nor_read_fsr(struct spi_nor *nor, u8 *fsr) -{ - int ret; - - if (nor->spimem) { - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 0), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(1, fsr, 0)); - - if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) { - op.addr.nbytes = nor->params->rdsr_addr_nbytes; - op.dummy.nbytes = nor->params->rdsr_dummy; - /* - * We don't want to read only one byte in DTR mode. So, - * read 2 and then discard the second byte. - */ - op.data.nbytes = 2; - } - - spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); - - ret = spi_mem_exec_op(nor->spimem, &op); - } else { - ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_RDFSR, fsr, - 1); - } - - if (ret) - dev_dbg(nor->dev, "error %d reading FSR\n", ret); - - return ret; -} - -/** * spi_nor_read_cr() - Read the Configuration Register using the * SPINOR_OP_RDCR (35h) command. * @nor: pointer to 'struct spi_nor' @@ -665,75 +621,6 @@ int spi_nor_sr_ready(struct spi_nor *nor) } /** - * spi_nor_clear_fsr() - Clear the Flag Status Register. - * @nor: pointer to 'struct spi_nor'. - */ -static void spi_nor_clear_fsr(struct spi_nor *nor) -{ - int ret; - - if (nor->spimem) { - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLFSR, 0), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_NO_DATA); - - 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_CLFSR, - NULL, 0); - } - - if (ret) - dev_dbg(nor->dev, "error %d clearing FSR\n", ret); -} - -/** - * spi_nor_fsr_ready() - Query the Flag Status Register to see if the flash is - * ready for new commands. - * @nor: pointer to 'struct spi_nor'. - * - * Return: 1 if ready, 0 if not ready, -errno on errors. - */ -static int spi_nor_fsr_ready(struct spi_nor *nor) -{ - int ret = spi_nor_read_fsr(nor, nor->bouncebuf); - - if (ret) - return ret; - - if (nor->bouncebuf[0] & (FSR_E_ERR | FSR_P_ERR)) { - if (nor->bouncebuf[0] & FSR_E_ERR) - dev_err(nor->dev, "Erase operation failed.\n"); - else - dev_err(nor->dev, "Program operation failed.\n"); - - if (nor->bouncebuf[0] & FSR_PT_ERR) - dev_err(nor->dev, - "Attempted to modify a protected sector.\n"); - - spi_nor_clear_fsr(nor); - - /* - * WEL bit remains set to one when an erase or page program - * error occurs. Issue a Write Disable command to protect - * against inadvertent writes that can possibly corrupt the - * contents of the memory. - */ - ret = spi_nor_write_disable(nor); - if (ret) - return ret; - - return -EIO; - } - - return !!(nor->bouncebuf[0] & FSR_READY); -} - -/** * spi_nor_ready() - Query the flash to see if it is ready for new commands. * @nor: pointer to 'struct spi_nor'. * @@ -741,19 +628,11 @@ static int spi_nor_fsr_ready(struct spi_nor *nor) */ static int spi_nor_ready(struct spi_nor *nor) { - int sr, fsr; - /* Flashes might override the standard routine. */ if (nor->params->ready) return nor->params->ready(nor); - sr = spi_nor_sr_ready(nor); - if (sr < 0) - return sr; - fsr = nor->flags & SNOR_F_USE_FSR ? spi_nor_fsr_ready(nor) : 1; - if (fsr < 0) - return fsr; - return sr && fsr; + return spi_nor_sr_ready(nor); } /** diff --git a/drivers/mtd/spi-nor/micron-st.c b/drivers/mtd/spi-nor/micron-st.c index 7a68f2ad3ea1..e580830ed70f 100644 --- a/drivers/mtd/spi-nor/micron-st.c +++ b/drivers/mtd/spi-nor/micron-st.c @@ -8,6 +8,8 @@ #include "core.h" +#define SPINOR_OP_RDFSR 0x70 /* Read flag status register */ +#define SPINOR_OP_CLFSR 0x50 /* Clear flag status register */ #define SPINOR_OP_MT_DTR_RD 0xfd /* Fast Read opcode in DTR mode */ #define SPINOR_OP_MT_RD_ANY_REG 0x85 /* Read volatile register */ #define SPINOR_OP_MT_WR_ANY_REG 0x81 /* Write volatile register */ @@ -17,6 +19,12 @@ #define SPINOR_MT_OCT_DTR 0xe7 /* Enable Octal DTR. */ #define SPINOR_MT_EXSPI 0xff /* Enable Extended SPI (default) */ +/* Flag Status Register bits */ +#define FSR_READY BIT(7) /* Device status, 0 = Busy, 1 = Ready */ +#define FSR_E_ERR BIT(5) /* Erase operation status */ +#define FSR_P_ERR BIT(4) /* Program operation status */ +#define FSR_PT_ERR BIT(1) /* Protection error bit */ + static int micron_st_nor_octal_dtr_enable(struct spi_nor *nor, bool enable) { struct spi_mem_op op; @@ -273,6 +281,125 @@ static int micron_st_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable) return spi_nor_write_disable(nor); } +/** + * spi_nor_read_fsr() - Read the Flag Status Register. + * @nor: pointer to 'struct spi_nor' + * @fsr: pointer to a DMA-able buffer where the value of the + * Flag Status Register will be written. Should be at least 2 + * bytes. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_read_fsr(struct spi_nor *nor, u8 *fsr) +{ + int ret; + + if (nor->spimem) { + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 0), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_IN(1, fsr, 0)); + + if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) { + op.addr.nbytes = nor->params->rdsr_addr_nbytes; + op.dummy.nbytes = nor->params->rdsr_dummy; + /* + * We don't want to read only one byte in DTR mode. So, + * read 2 and then discard the second byte. + */ + op.data.nbytes = 2; + } + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + + ret = spi_mem_exec_op(nor->spimem, &op); + } else { + ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_RDFSR, fsr, + 1); + } + + if (ret) + dev_dbg(nor->dev, "error %d reading FSR\n", ret); + + return ret; +} + +/** + * spi_nor_clear_fsr() - Clear the Flag Status Register. + * @nor: pointer to 'struct spi_nor'. + */ +static void spi_nor_clear_fsr(struct spi_nor *nor) +{ + int ret; + + if (nor->spimem) { + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLFSR, 0), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_NO_DATA); + + 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_CLFSR, + NULL, 0); + } + + if (ret) + dev_dbg(nor->dev, "error %d clearing FSR\n", ret); +} + +/** + * spi_nor_fsr_ready() - Query the Status Register as well as the Flag Status + * Register to see if the flash is ready for new commands. If there are any + * errors in the FSR clear them. + * @nor: pointer to 'struct spi_nor'. + * + * Return: 1 if ready, 0 if not ready, -errno on errors. + */ +static int spi_nor_fsr_ready(struct spi_nor *nor) +{ + int sr_ready, ret; + + sr_ready = spi_nor_sr_ready(nor); + if (sr_ready < 0) + return sr_ready; + + ret = spi_nor_read_fsr(nor, nor->bouncebuf); + if (ret) + return ret; + + if (nor->bouncebuf[0] & (FSR_E_ERR | FSR_P_ERR)) { + if (nor->bouncebuf[0] & FSR_E_ERR) + dev_err(nor->dev, "Erase operation failed.\n"); + else + dev_err(nor->dev, "Program operation failed.\n"); + + if (nor->bouncebuf[0] & FSR_PT_ERR) + dev_err(nor->dev, + "Attempted to modify a protected sector.\n"); + + spi_nor_clear_fsr(nor); + + /* + * WEL bit remains set to one when an erase or page program + * error occurs. Issue a Write Disable command to protect + * against inadvertent writes that can possibly corrupt the + * contents of the memory. + */ + ret = spi_nor_write_disable(nor); + if (ret) + return ret; + + return -EIO; + } + + return sr_ready && !!(nor->bouncebuf[0] & FSR_READY); +} + static void micron_st_nor_default_init(struct spi_nor *nor) { nor->flags |= SNOR_F_HAS_LOCK; @@ -281,8 +408,15 @@ static void micron_st_nor_default_init(struct spi_nor *nor) nor->params->set_4byte_addr_mode = micron_st_nor_set_4byte_addr_mode; } +static void micron_st_nor_late_init(struct spi_nor *nor) +{ + if (nor->flags & SNOR_F_USE_FSR) + nor->params->ready = spi_nor_fsr_ready; +} + static const struct spi_nor_fixups micron_st_nor_fixups = { .default_init = micron_st_nor_default_init, + .late_init = micron_st_nor_late_init, }; const struct spi_nor_manufacturer spi_nor_micron = { |