From 8b4195cd6dc3f1f0ab457d23d21e9f72fde0760a Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Wed, 23 Feb 2022 14:43:47 +0100 Subject: mtd: spi-nor: move all xilinx specifics into xilinx.c Mechanically move all the xilinx functions to its own module. Then register the new flash specific ready() function. Signed-off-by: Michael Walle Signed-off-by: Tudor Ambarus Reviewed-by: Pratyush Yadav Link: https://lore.kernel.org/r/20220223134358.1914798-22-michael@walle.cc --- include/linux/mtd/spi-nor.h | 9 --------- 1 file changed, 9 deletions(-) (limited to 'include/linux/mtd') diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index fc90fce26e33..b44b05a6f934 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -86,15 +86,6 @@ #define SPINOR_OP_BP 0x02 /* Byte program */ #define SPINOR_OP_AAI_WP 0xad /* Auto address increment word program */ -/* Used for S3AN flashes only */ -#define SPINOR_OP_XSE 0x50 /* Sector erase */ -#define SPINOR_OP_XPP 0x82 /* Page program */ -#define SPINOR_OP_XRDSR 0xd7 /* Read status register */ - -#define XSR_PAGESIZE BIT(0) /* Page size in Po2 or Linear */ -#define XSR_RDY BIT(7) /* Ready */ - - /* Used for Macronix and Winbond flashes. */ #define SPINOR_OP_EN4B 0xb7 /* Enter 4-byte mode */ #define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */ -- cgit v1.2.3-71-gd317 From c770abe52d81089a8b8ecd1fe42722e29bbab5f5 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Wed, 23 Feb 2022 14:43:50 +0100 Subject: 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 Signed-off-by: Tudor Ambarus Tested-by: Pratyush Yadav # on mt35xu512aba, s28hs512t Reviewed-by: Pratyush Yadav Link: https://lore.kernel.org/r/20220223134358.1914798-25-michael@walle.cc --- drivers/mtd/spi-nor/core.c | 123 +----------------------------------- drivers/mtd/spi-nor/micron-st.c | 134 ++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spi-nor.h | 8 --- 3 files changed, 135 insertions(+), 130 deletions(-) (limited to 'include/linux/mtd') 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 @@ -412,50 +412,6 @@ int spi_nor_read_sr(struct spi_nor *nor, u8 *sr) return ret; } -/** - * 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. @@ -664,75 +620,6 @@ int spi_nor_sr_ready(struct spi_nor *nor) return !(nor->bouncebuf[0] & SR_WIP); } -/** - * 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 = { diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index b44b05a6f934..4622251a79ff 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -47,8 +47,6 @@ #define SPINOR_OP_RDID 0x9f /* Read JEDEC ID */ #define SPINOR_OP_RDSFDP 0x5a /* Read SFDP */ #define SPINOR_OP_RDCR 0x35 /* Read configuration register */ -#define SPINOR_OP_RDFSR 0x70 /* Read flag status register */ -#define SPINOR_OP_CLFSR 0x50 /* Clear flag status register */ #define SPINOR_OP_RDEAR 0xc8 /* Read Extended Address Register */ #define SPINOR_OP_WREAR 0xc5 /* Write Extended Address Register */ #define SPINOR_OP_SRSTEN 0x66 /* Software Reset Enable */ @@ -126,12 +124,6 @@ /* Enhanced Volatile Configuration Register bits */ #define EVCR_QUAD_EN_MICRON BIT(7) /* Micron Quad I/O */ -/* 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 */ - /* Status Register 2 bits. */ #define SR2_QUAD_EN_BIT1 BIT(1) #define SR2_LB1 BIT(3) /* Security Register Lock Bit 1 */ -- cgit v1.2.3-71-gd317 From 837d5181beef068c16bb8424c2c1571a7d5d7966 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Wed, 23 Feb 2022 14:43:54 +0100 Subject: mtd: spi-nor: move all spansion specifics into spansion.c The clear status register flags is only available on spansion flashes. Move all the functions around that into the spanion module. Signed-off-by: Michael Walle Signed-off-by: Tudor Ambarus Tested-by: Pratyush Yadav # on mt35xu512aba, s28hs512t Reviewed-by: Pratyush Yadav Link: https://lore.kernel.org/r/20220223134358.1914798-29-michael@walle.cc --- drivers/mtd/spi-nor/core.c | 49 ----------------------------- drivers/mtd/spi-nor/spansion.c | 70 ++++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spi-nor.h | 1 - 3 files changed, 70 insertions(+), 50 deletions(-) (limited to 'include/linux/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index ac0faedebafe..e2b8b0a438ce 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -554,33 +554,6 @@ int spi_nor_write_ear(struct spi_nor *nor, u8 ear) return ret; } -/** - * spi_nor_clear_sr() - Clear the Status Register. - * @nor: pointer to 'struct spi_nor'. - */ -static void spi_nor_clear_sr(struct spi_nor *nor) -{ - int ret; - - if (nor->spimem) { - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 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_CLSR, - NULL, 0); - } - - if (ret) - dev_dbg(nor->dev, "error %d clearing SR\n", ret); -} - /** * spi_nor_sr_ready() - Query the Status Register to see if the flash is ready * for new commands. @@ -595,28 +568,6 @@ int spi_nor_sr_ready(struct spi_nor *nor) if (ret) return ret; - if (nor->flags & SNOR_F_USE_CLSR && - 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"); - - spi_nor_clear_sr(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] & SR_WIP); } diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index 1a0e7214d9e5..dbafe97c2636 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -8,6 +8,7 @@ #include "core.h" +#define SPINOR_OP_CLSR 0x30 /* Clear status register 1 */ #define SPINOR_OP_RD_ANY_REG 0x65 /* Read any register */ #define SPINOR_OP_WR_ANY_REG 0x71 /* Write any register */ #define SPINOR_REG_CYPRESS_CFR2V 0x00800003 @@ -294,6 +295,72 @@ static const struct flash_info spansion_nor_parts[] = { }, }; +/** + * spi_nor_clear_sr() - Clear the Status Register. + * @nor: pointer to 'struct spi_nor'. + */ +static void spi_nor_clear_sr(struct spi_nor *nor) +{ + int ret; + + if (nor->spimem) { + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 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_CLSR, + NULL, 0); + } + + if (ret) + dev_dbg(nor->dev, "error %d clearing SR\n", ret); +} + +/** + * spi_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'. + * + * Return: 1 if ready, 0 if not ready, -errno on errors. + */ +static int spi_nor_sr_ready_and_clear(struct spi_nor *nor) +{ + int ret; + + ret = spi_nor_read_sr(nor, nor->bouncebuf); + 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"); + + spi_nor_clear_sr(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] & SR_WIP); +} + static void spansion_nor_late_init(struct spi_nor *nor) { if (nor->params->size > SZ_16M) { @@ -302,6 +369,9 @@ static void spansion_nor_late_init(struct spi_nor *nor) nor->erase_opcode = SPINOR_OP_SE; nor->mtd.erasesize = nor->info->sector_size; } + + if (nor->flags & SNOR_F_USE_CLSR) + nor->params->ready = spi_nor_sr_ready_and_clear; } static const struct spi_nor_fixups spansion_nor_fixups = { diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 4622251a79ff..5e25a7b75ae2 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -90,7 +90,6 @@ /* Used for Spansion flashes only. */ #define SPINOR_OP_BRWR 0x17 /* Bank register write */ -#define SPINOR_OP_CLSR 0x30 /* Clear status register 1 */ /* Used for Micron flashes only. */ #define SPINOR_OP_RD_EVCR 0x65 /* Read EVCR register */ -- cgit v1.2.3-71-gd317