diff options
Diffstat (limited to 'drivers/mtd/spi-nor/micron-st.c')
-rw-r--r-- | drivers/mtd/spi-nor/micron-st.c | 259 |
1 files changed, 210 insertions, 49 deletions
diff --git a/drivers/mtd/spi-nor/micron-st.c b/drivers/mtd/spi-nor/micron-st.c index bb95b1aabf74..8a20475ce77a 100644 --- a/drivers/mtd/spi-nor/micron-st.c +++ b/drivers/mtd/spi-nor/micron-st.c @@ -8,6 +8,11 @@ #include "core.h" +/* flash_info mfr_flag. Used to read proprietary FSR register. */ +#define USE_FSR BIT(0) + +#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,7 +22,13 @@ #define SPINOR_MT_OCT_DTR 0xe7 /* Enable Octal DTR. */ #define SPINOR_MT_EXSPI 0xff /* Enable Extended SPI (default) */ -static int spi_nor_micron_octal_dtr_enable(struct spi_nor *nor, bool enable) +/* 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; u8 *buf = nor->bouncebuf; @@ -102,7 +113,7 @@ static int spi_nor_micron_octal_dtr_enable(struct spi_nor *nor, bool enable) static void mt35xu512aba_default_init(struct spi_nor *nor) { - nor->params->octal_dtr_enable = spi_nor_micron_octal_dtr_enable; + nor->params->octal_dtr_enable = micron_st_nor_octal_dtr_enable; } static void mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor) @@ -130,20 +141,22 @@ static const struct spi_nor_fixups mt35xu512aba_fixups = { .post_sfdp = mt35xu512aba_post_sfdp_fixup, }; -static const struct flash_info micron_parts[] = { +static const struct flash_info micron_nor_parts[] = { { "mt35xu512aba", INFO(0x2c5b1a, 0, 128 * 1024, 512) - FLAGS(USE_FSR) NO_SFDP_FLAGS(SECT_4K | SPI_NOR_OCTAL_READ | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_OCTAL_DTR_PP) FIXUP_FLAGS(SPI_NOR_4B_OPCODES | SPI_NOR_IO_MODE_EN_VOLATILE) - .fixups = &mt35xu512aba_fixups}, + MFR_FLAGS(USE_FSR) + .fixups = &mt35xu512aba_fixups + }, { "mt35xu02g", INFO(0x2c5b1c, 0, 128 * 1024, 2048) - FLAGS(USE_FSR) NO_SFDP_FLAGS(SECT_4K | SPI_NOR_OCTAL_READ) - FIXUP_FLAGS(SPI_NOR_4B_OPCODES) }, + FIXUP_FLAGS(SPI_NOR_4B_OPCODES) + MFR_FLAGS(USE_FSR) + }, }; -static const struct flash_info st_parts[] = { +static const struct flash_info st_nor_parts[] = { { "n25q016a", INFO(0x20bb15, 0, 64 * 1024, 32) NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) }, { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64) @@ -156,57 +169,79 @@ static const struct flash_info st_parts[] = { NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) }, { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256) FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP | - SPI_NOR_BP3_SR_BIT6 | USE_FSR) - NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) }, + SPI_NOR_BP3_SR_BIT6) + NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) + MFR_FLAGS(USE_FSR) + }, { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256) FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP | - SPI_NOR_BP3_SR_BIT6 | USE_FSR) - NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) }, + SPI_NOR_BP3_SR_BIT6) + NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) + MFR_FLAGS(USE_FSR) + }, { "mt25ql256a", INFO6(0x20ba19, 0x104400, 64 * 1024, 512) - FLAGS(USE_FSR) NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) - FIXUP_FLAGS(SPI_NOR_4B_OPCODES) }, + FIXUP_FLAGS(SPI_NOR_4B_OPCODES) + MFR_FLAGS(USE_FSR) + }, { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512) - FLAGS(USE_FSR) NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | - SPI_NOR_QUAD_READ) }, + SPI_NOR_QUAD_READ) + MFR_FLAGS(USE_FSR) + }, { "mt25qu256a", INFO6(0x20bb19, 0x104400, 64 * 1024, 512) - FLAGS(USE_FSR) NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) - FIXUP_FLAGS(SPI_NOR_4B_OPCODES) }, + FIXUP_FLAGS(SPI_NOR_4B_OPCODES) + MFR_FLAGS(USE_FSR) + }, { "n25q256ax1", INFO(0x20bb19, 0, 64 * 1024, 512) - FLAGS(USE_FSR) - NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) }, + NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) + MFR_FLAGS(USE_FSR) + }, { "mt25ql512a", INFO6(0x20ba20, 0x104400, 64 * 1024, 1024) - FLAGS(USE_FSR) NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) - FIXUP_FLAGS(SPI_NOR_4B_OPCODES) }, + FIXUP_FLAGS(SPI_NOR_4B_OPCODES) + MFR_FLAGS(USE_FSR) + }, { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024) FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP | - SPI_NOR_BP3_SR_BIT6 | USE_FSR) - NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) }, + SPI_NOR_BP3_SR_BIT6) + NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) + MFR_FLAGS(USE_FSR) + }, { "mt25qu512a", INFO6(0x20bb20, 0x104400, 64 * 1024, 1024) - FLAGS(USE_FSR) NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) - FIXUP_FLAGS(SPI_NOR_4B_OPCODES) }, + FIXUP_FLAGS(SPI_NOR_4B_OPCODES) + MFR_FLAGS(USE_FSR) + }, { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024) FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP | - SPI_NOR_BP3_SR_BIT6 | USE_FSR) - NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) }, + SPI_NOR_BP3_SR_BIT6) + NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) + MFR_FLAGS(USE_FSR) + }, { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048) FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP | - SPI_NOR_BP3_SR_BIT6 | NO_CHIP_ERASE | USE_FSR) - NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) }, + SPI_NOR_BP3_SR_BIT6 | NO_CHIP_ERASE) + NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) + MFR_FLAGS(USE_FSR) + }, { "n25q00a", INFO(0x20bb21, 0, 64 * 1024, 2048) - FLAGS(NO_CHIP_ERASE | USE_FSR) - NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) }, + FLAGS(NO_CHIP_ERASE) + NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) + MFR_FLAGS(USE_FSR) + }, { "mt25ql02g", INFO(0x20ba22, 0, 64 * 1024, 4096) - FLAGS(NO_CHIP_ERASE | USE_FSR) - NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) }, + FLAGS(NO_CHIP_ERASE) + NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) + MFR_FLAGS(USE_FSR) + }, { "mt25qu02g", INFO(0x20bb22, 0, 64 * 1024, 4096) - FLAGS(NO_CHIP_ERASE | USE_FSR) + FLAGS(NO_CHIP_ERASE) NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | - SPI_NOR_QUAD_READ) }, + SPI_NOR_QUAD_READ) + MFR_FLAGS(USE_FSR) + }, { "m25p05", INFO(0x202010, 0, 32 * 1024, 2) }, { "m25p10", INFO(0x202011, 0, 32 * 1024, 4) }, @@ -250,15 +285,15 @@ static const struct flash_info st_parts[] = { }; /** - * st_micron_set_4byte_addr_mode() - Set 4-byte address mode for ST and Micron - * flashes. + * micron_st_nor_set_4byte_addr_mode() - Set 4-byte address mode for 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 st_micron_set_4byte_addr_mode(struct spi_nor *nor, bool enable) +static int micron_st_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable) { int ret; @@ -273,28 +308,154 @@ static int st_micron_set_4byte_addr_mode(struct spi_nor *nor, bool enable) return spi_nor_write_disable(nor); } -static void micron_st_default_init(struct spi_nor *nor) +/** + * micron_st_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 micron_st_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; +} + +/** + * micron_st_nor_clear_fsr() - Clear the Flag Status Register. + * @nor: pointer to 'struct spi_nor'. + */ +static void micron_st_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); +} + +/** + * micron_st_nor_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 micron_st_nor_ready(struct spi_nor *nor) +{ + int sr_ready, ret; + + sr_ready = spi_nor_sr_ready(nor); + if (sr_ready < 0) + return sr_ready; + + ret = micron_st_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"); + + micron_st_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; nor->flags &= ~SNOR_F_HAS_16BIT_SR; nor->params->quad_enable = NULL; - nor->params->set_4byte_addr_mode = st_micron_set_4byte_addr_mode; + 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->info->mfr_flags & USE_FSR) + nor->params->ready = micron_st_nor_ready; } -static const struct spi_nor_fixups micron_st_fixups = { - .default_init = micron_st_default_init, +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 = { .name = "micron", - .parts = micron_parts, - .nparts = ARRAY_SIZE(micron_parts), - .fixups = µn_st_fixups, + .parts = micron_nor_parts, + .nparts = ARRAY_SIZE(micron_nor_parts), + .fixups = µn_st_nor_fixups, }; const struct spi_nor_manufacturer spi_nor_st = { .name = "st", - .parts = st_parts, - .nparts = ARRAY_SIZE(st_parts), - .fixups = µn_st_fixups, + .parts = st_nor_parts, + .nparts = ARRAY_SIZE(st_nor_parts), + .fixups = µn_st_nor_fixups, }; |