diff options
Diffstat (limited to 'drivers/mtd/spi-nor')
| -rw-r--r-- | drivers/mtd/spi-nor/core.c | 10 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/core.h | 6 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/micron-st.c | 101 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/sfdp.c | 30 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/spansion.c | 38 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/winbond.c | 24 |
6 files changed, 163 insertions, 46 deletions
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 20ea80450f22..d3f8a78efd3b 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2459,6 +2459,16 @@ spi_nor_spimem_adjust_hwcaps(struct spi_nor *nor, u32 *hwcaps) ¶ms->page_programs[ppidx])) *hwcaps &= ~BIT(cap); } + + /* Some SPI controllers might not support CR read opcode. */ + if (!(nor->flags & SNOR_F_NO_READ_CR)) { + struct spi_mem_op op = SPI_NOR_RDCR_OP(nor->bouncebuf); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + + if (spi_nor_spimem_check_op(nor, &op)) + nor->flags |= SNOR_F_NO_READ_CR; + } } /** diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index ceff412f7d65..16b382d4f04f 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -409,6 +409,10 @@ struct spi_nor_flash_parameter { * flash parameters when information provided by the flash_info * table is incomplete or wrong. * @post_bfpt: called after the BFPT table has been parsed + * @smpt_read_dummy: called during SMPT table is being parsed. Used to fix the + * number of dummy cycles in read register ops. + * @smpt_map_id: called after map ID in SMPT table has been determined for the + * case the map ID is wrong and needs to be fixed. * @post_sfdp: called after SFDP has been parsed (is also called for SPI NORs * that do not support RDSFDP). Typically used to tweak various * parameters that could not be extracted by other means (i.e. @@ -426,6 +430,8 @@ struct spi_nor_fixups { int (*post_bfpt)(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, const struct sfdp_bfpt *bfpt); + void (*smpt_read_dummy)(const struct spi_nor *nor, u8 *read_dummy); + void (*smpt_map_id)(const struct spi_nor *nor, u8 *map_id); int (*post_sfdp)(struct spi_nor *nor); int (*late_init)(struct spi_nor *nor); }; diff --git a/drivers/mtd/spi-nor/micron-st.c b/drivers/mtd/spi-nor/micron-st.c index 187239ccd549..88033384a71e 100644 --- a/drivers/mtd/spi-nor/micron-st.c +++ b/drivers/mtd/spi-nor/micron-st.c @@ -127,9 +127,36 @@ static int micron_st_nor_set_octal_dtr(struct spi_nor *nor, bool enable) micron_st_nor_octal_dtr_dis(nor); } -static void mt35xu512aba_default_init(struct spi_nor *nor) +static int micron_st_nor_four_die_late_init(struct spi_nor *nor) { - nor->params->set_octal_dtr = micron_st_nor_set_octal_dtr; + struct spi_nor_flash_parameter *params = nor->params; + + params->die_erase_opcode = SPINOR_OP_MT_DIE_ERASE; + params->n_dice = 4; + + /* + * Unfortunately the die erase opcode does not have a 4-byte opcode + * correspondent for these flashes. The SFDP 4BAIT table fails to + * consider the die erase too. We're forced to enter in the 4 byte + * address mode in order to benefit of the die erase. + */ + return spi_nor_set_4byte_addr_mode(nor, true); +} + +static int micron_st_nor_two_die_late_init(struct spi_nor *nor) +{ + struct spi_nor_flash_parameter *params = nor->params; + + params->die_erase_opcode = SPINOR_OP_MT_DIE_ERASE; + params->n_dice = 2; + + /* + * Unfortunately the die erase opcode does not have a 4-byte opcode + * correspondent for these flashes. The SFDP 4BAIT table fails to + * consider the die erase too. We're forced to enter in the 4 byte + * address mode in order to benefit of the die erase. + */ + return spi_nor_set_4byte_addr_mode(nor, true); } static int mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor) @@ -155,22 +182,38 @@ static int mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor) } static const struct spi_nor_fixups mt35xu512aba_fixups = { - .default_init = mt35xu512aba_default_init, .post_sfdp = mt35xu512aba_post_sfdp_fixup, }; +static const struct spi_nor_fixups mt35xu01gbba_fixups = { + .post_sfdp = mt35xu512aba_post_sfdp_fixup, + .late_init = micron_st_nor_two_die_late_init, +}; + static const struct flash_info micron_nor_parts[] = { { + /* MT35XU512ABA */ .id = SNOR_ID(0x2c, 0x5b, 0x1a), - .name = "mt35xu512aba", - .sector_size = SZ_128K, - .size = SZ_64M, - .no_sfdp_flags = SECT_4K | SPI_NOR_OCTAL_READ | - SPI_NOR_OCTAL_DTR_READ | SPI_NOR_OCTAL_DTR_PP, .mfr_flags = USE_FSR, - .fixup_flags = SPI_NOR_4B_OPCODES | SPI_NOR_IO_MODE_EN_VOLATILE, + .fixup_flags = SPI_NOR_IO_MODE_EN_VOLATILE, .fixups = &mt35xu512aba_fixups, }, { + /* MT35XU01GBBA */ + .id = SNOR_ID(0x2c, 0x5b, 0x1b), + .mfr_flags = USE_FSR, + .fixup_flags = SPI_NOR_IO_MODE_EN_VOLATILE, + .fixups = &mt35xu01gbba_fixups, + }, { + /* + * The MT35XU02GCBA flash device does not support chip erase, + * according to its datasheet. It supports die erase, which + * means the current driver implementation will likely need to + * be converted to use die erase. Furthermore, similar to the + * MT35XU01GBBA, the SPI_NOR_IO_MODE_EN_VOLATILE flag probably + * needs to be enabled. + * + * TODO: Fix these and test on real hardware. + */ .id = SNOR_ID(0x2c, 0x5b, 0x1c), .name = "mt35xu02g", .sector_size = SZ_128K, @@ -193,48 +236,16 @@ static const struct spi_nor_fixups mt25qu512a_fixups = { .post_bfpt = mt25qu512a_post_bfpt_fixup, }; -static int st_nor_four_die_late_init(struct spi_nor *nor) -{ - struct spi_nor_flash_parameter *params = nor->params; - - params->die_erase_opcode = SPINOR_OP_MT_DIE_ERASE; - params->n_dice = 4; - - /* - * Unfortunately the die erase opcode does not have a 4-byte opcode - * correspondent for these flashes. The SFDP 4BAIT table fails to - * consider the die erase too. We're forced to enter in the 4 byte - * address mode in order to benefit of the die erase. - */ - return spi_nor_set_4byte_addr_mode(nor, true); -} - -static int st_nor_two_die_late_init(struct spi_nor *nor) -{ - struct spi_nor_flash_parameter *params = nor->params; - - params->die_erase_opcode = SPINOR_OP_MT_DIE_ERASE; - params->n_dice = 2; - - /* - * Unfortunately the die erase opcode does not have a 4-byte opcode - * correspondent for these flashes. The SFDP 4BAIT table fails to - * consider the die erase too. We're forced to enter in the 4 byte - * address mode in order to benefit of the die erase. - */ - return spi_nor_set_4byte_addr_mode(nor, true); -} - static const struct spi_nor_fixups n25q00_fixups = { - .late_init = st_nor_four_die_late_init, + .late_init = micron_st_nor_four_die_late_init, }; static const struct spi_nor_fixups mt25q01_fixups = { - .late_init = st_nor_two_die_late_init, + .late_init = micron_st_nor_two_die_late_init, }; static const struct spi_nor_fixups mt25q02_fixups = { - .late_init = st_nor_four_die_late_init, + .late_init = micron_st_nor_four_die_late_init, }; static const struct flash_info st_nor_parts[] = { @@ -635,6 +646,8 @@ static int micron_st_nor_late_init(struct spi_nor *nor) if (!params->set_4byte_addr_mode) params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_wren_en4b_ex4b; + params->set_octal_dtr = micron_st_nor_set_octal_dtr; + return 0; } diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index 21727f9a4ac6..a8324c2da0ac 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -699,6 +699,17 @@ static u8 spi_nor_smpt_addr_nbytes(const struct spi_nor *nor, const u32 settings } } +static void spi_nor_smpt_read_dummy_fixups(const struct spi_nor *nor, + u8 *read_dummy) +{ + if (nor->manufacturer && nor->manufacturer->fixups && + nor->manufacturer->fixups->smpt_read_dummy) + nor->manufacturer->fixups->smpt_read_dummy(nor, read_dummy); + + if (nor->info->fixups && nor->info->fixups->smpt_read_dummy) + nor->info->fixups->smpt_read_dummy(nor, read_dummy); +} + /** * spi_nor_smpt_read_dummy() - return the configuration detection command read * latency, in clock cycles. @@ -711,11 +722,24 @@ static u8 spi_nor_smpt_read_dummy(const struct spi_nor *nor, const u32 settings) { u8 read_dummy = SMPT_CMD_READ_DUMMY(settings); - if (read_dummy == SMPT_CMD_READ_DUMMY_IS_VARIABLE) - return nor->read_dummy; + if (read_dummy == SMPT_CMD_READ_DUMMY_IS_VARIABLE) { + read_dummy = nor->read_dummy; + spi_nor_smpt_read_dummy_fixups(nor, &read_dummy); + } + return read_dummy; } +static void spi_nor_smpt_map_id_fixups(const struct spi_nor *nor, u8 *map_id) +{ + if (nor->manufacturer && nor->manufacturer->fixups && + nor->manufacturer->fixups->smpt_map_id) + nor->manufacturer->fixups->smpt_map_id(nor, map_id); + + if (nor->info->fixups && nor->info->fixups->smpt_map_id) + nor->info->fixups->smpt_map_id(nor, map_id); +} + /** * spi_nor_get_map_in_use() - get the configuration map in use * @nor: pointer to a 'struct spi_nor' @@ -769,6 +793,8 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt, map_id = map_id << 1 | !!(*buf & read_data_mask); } + spi_nor_smpt_map_id_fixups(nor, &map_id); + /* * If command descriptors are provided, they always precede map * descriptors in the table. There is no need to start the iteration diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index a0296c871634..8498c7003d88 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -785,8 +785,46 @@ s25fs_s_nor_post_bfpt_fixups(struct spi_nor *nor, return 0; } +static void s25fs_s_nor_smpt_read_dummy(const struct spi_nor *nor, + u8 *read_dummy) +{ + /* + * The configuration detection dwords in S25FS-S SMPT has 65h as + * command instruction and 'variable' as configuration detection command + * latency. Set 8 dummy cycles as it is factory default for 65h (read + * any register) op. + */ + *read_dummy = 8; +} + +static void s25fs_s_nor_smpt_map_id_dummy(const struct spi_nor *nor, u8 *map_id) +{ + /* + * The S25FS512S chip supports: + * - Hybrid sector option which has physical set of eight 4-KB sectors + * and one 224-KB sector at the top or bottom of address space with + * all remaining sectors of 256-KB + * - Uniform sector option which has uniform 256-KB sectors + * + * On the other hand, the datasheet rev.O Table 71 on page 153 JEDEC + * Sector Map Parameter Dword-6 Config. Detect-3 does use CR3NV[1] to + * discern 64-KB(CR3NV[1]=0) and 256-KB(CR3NV[1]=1) uniform sectors + * device configuration. And in section 7.5.5.1 Configuration Register 3 + * Non-volatile (CR3NV) page 61, the CR3NV[1] is RFU Reserved for Future + * Use and set to 0, which means 64-KB uniform. Since the device does + * not support 64-KB uniform sectors in any configuration, parsing SMPT + * table cannot find a valid sector map entry and fails. Fix this up by + * setting SMPT by overwriting the CR3NV[1] value to 1, as the table + * expects. + */ + if (nor->params->size == SZ_64M) + *map_id |= BIT(0); +} + static const struct spi_nor_fixups s25fs_s_nor_fixups = { .post_bfpt = s25fs_s_nor_post_bfpt_fixups, + .smpt_read_dummy = s25fs_s_nor_smpt_read_dummy, + .smpt_map_id = s25fs_s_nor_smpt_map_id_dummy, }; static const struct flash_info spansion_nor_parts[] = { diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c index 63a93c9eb917..fb855fe44733 100644 --- a/drivers/mtd/spi-nor/winbond.c +++ b/drivers/mtd/spi-nor/winbond.c @@ -343,6 +343,30 @@ static const struct flash_info winbond_nor_parts[] = { .id = SNOR_ID(0xef, 0x80, 0x20), .name = "w25q512nwm", .otp = SNOR_OTP(256, 3, 0x1000, 0x1000), + }, { + /* W25Q01NWxxIQ */ + .id = SNOR_ID(0xef, 0x60, 0x21), + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP, + }, { + /* W25Q01NWxxIM */ + .id = SNOR_ID(0xef, 0x80, 0x21), + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP, + }, { + /* W25Q02NWxxIM */ + .id = SNOR_ID(0xef, 0x80, 0x22), + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP, + }, { + /* W25H512NWxxAM */ + .id = SNOR_ID(0xef, 0xa0, 0x20), + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP, + }, { + /* W25H01NWxxAM */ + .id = SNOR_ID(0xef, 0xa0, 0x21), + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP, + }, { + /* W25H02NWxxAM */ + .id = SNOR_ID(0xef, 0xa0, 0x22), + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP, }, }; |
