diff options
Diffstat (limited to 'drivers/mtd/spi-nor/spansion.c')
| -rw-r--r-- | drivers/mtd/spi-nor/spansion.c | 750 |
1 files changed, 510 insertions, 240 deletions
diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index 15f9a80c10b9..8498c7003d88 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -4,14 +4,21 @@ * Copyright (C) 2014, Freescale Semiconductor, Inc. */ +#include <linux/bitfield.h> +#include <linux/device.h> +#include <linux/errno.h> #include <linux/mtd/spi-nor.h> #include "core.h" /* flash_info mfr_flag. Used to clear sticky prorietary SR bits. */ #define USE_CLSR BIT(0) +#define USE_CLPEF BIT(1) #define SPINOR_OP_CLSR 0x30 /* Clear status register 1 */ +#define SPINOR_OP_CLPEF 0x82 /* Clear program/erase failure flags */ +#define SPINOR_OP_CYPRESS_EX4B 0xB8 /* Exit 4-byte address mode */ +#define SPINOR_OP_CYPRESS_DIE_ERASE 0x61 /* Chip (die) erase */ #define SPINOR_OP_RD_ANY_REG 0x65 /* Read any register */ #define SPINOR_OP_WR_ANY_REG 0x71 /* Write any register */ #define SPINOR_REG_CYPRESS_VREG 0x00800000 @@ -19,21 +26,16 @@ #define SPINOR_REG_CYPRESS_STR1V \ (SPINOR_REG_CYPRESS_VREG + SPINOR_REG_CYPRESS_STR1) #define SPINOR_REG_CYPRESS_CFR1 0x2 -#define SPINOR_REG_CYPRESS_CFR1V \ - (SPINOR_REG_CYPRESS_VREG + SPINOR_REG_CYPRESS_CFR1) #define SPINOR_REG_CYPRESS_CFR1_QUAD_EN BIT(1) /* Quad Enable */ #define SPINOR_REG_CYPRESS_CFR2 0x3 #define SPINOR_REG_CYPRESS_CFR2V \ (SPINOR_REG_CYPRESS_VREG + SPINOR_REG_CYPRESS_CFR2) +#define SPINOR_REG_CYPRESS_CFR2_MEMLAT_MASK GENMASK(3, 0) #define SPINOR_REG_CYPRESS_CFR2_MEMLAT_11_24 0xb #define SPINOR_REG_CYPRESS_CFR2_ADRBYT BIT(7) #define SPINOR_REG_CYPRESS_CFR3 0x4 -#define SPINOR_REG_CYPRESS_CFR3V \ - (SPINOR_REG_CYPRESS_VREG + SPINOR_REG_CYPRESS_CFR3) #define SPINOR_REG_CYPRESS_CFR3_PGSZ BIT(4) /* Page size. */ #define SPINOR_REG_CYPRESS_CFR5 0x6 -#define SPINOR_REG_CYPRESS_CFR5V \ - (SPINOR_REG_CYPRESS_VREG + SPINOR_REG_CYPRESS_CFR5) #define SPINOR_REG_CYPRESS_CFR5_BIT6 BIT(6) #define SPINOR_REG_CYPRESS_CFR5_DDR BIT(1) #define SPINOR_REG_CYPRESS_CFR5_OPI BIT(0) @@ -57,22 +59,39 @@ SPI_MEM_OP_DUMMY(ndummy, 0), \ SPI_MEM_OP_DATA_IN(1, buf, 0)) -#define SPANSION_CLSR_OP \ - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 0), \ +#define CYPRESS_NOR_EN4B_EX4B_OP(enable) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(enable ? SPINOR_OP_EN4B : \ + SPINOR_OP_CYPRESS_EX4B, 0), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPANSION_OP(opcode) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0), \ SPI_MEM_OP_NO_ADDR, \ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DATA) /** + * struct spansion_nor_params - Spansion private parameters. + * @clsr: Clear Status Register or Clear Program and Erase Failure Flag + * opcode. + */ +struct spansion_nor_params { + u8 clsr; +}; + +/** * spansion_nor_clear_sr() - Clear the Status Register. * @nor: pointer to 'struct spi_nor'. */ static void spansion_nor_clear_sr(struct spi_nor *nor) { + const struct spansion_nor_params *priv_params = nor->params->priv; int ret; if (nor->spimem) { - struct spi_mem_op op = SPANSION_CLSR_OP; + struct spi_mem_op op = SPANSION_OP(priv_params->clsr); spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); @@ -88,11 +107,18 @@ static void spansion_nor_clear_sr(struct spi_nor *nor) static int cypress_nor_sr_ready_and_clear_reg(struct spi_nor *nor, u64 addr) { + struct spi_nor_flash_parameter *params = nor->params; struct spi_mem_op op = - CYPRESS_NOR_RD_ANY_REG_OP(nor->params->addr_mode_nbytes, addr, + CYPRESS_NOR_RD_ANY_REG_OP(params->addr_mode_nbytes, addr, 0, nor->bouncebuf); int ret; + if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) { + op.addr.nbytes = nor->addr_nbytes; + op.dummy.nbytes = params->rdsr_dummy; + op.data.nbytes = 2; + } + ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); if (ret) return ret; @@ -141,18 +167,26 @@ static int cypress_nor_sr_ready_and_clear(struct spi_nor *nor) return 1; } -static int cypress_nor_octal_dtr_en(struct spi_nor *nor) +static int cypress_nor_set_memlat(struct spi_nor *nor, u64 addr) { struct spi_mem_op op; u8 *buf = nor->bouncebuf; int ret; u8 addr_mode_nbytes = nor->params->addr_mode_nbytes; + op = (struct spi_mem_op) + CYPRESS_NOR_RD_ANY_REG_OP(addr_mode_nbytes, addr, 0, buf); + + ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); + if (ret) + return ret; + /* Use 24 dummy cycles for memory array reads. */ - *buf = SPINOR_REG_CYPRESS_CFR2_MEMLAT_11_24; + *buf &= ~SPINOR_REG_CYPRESS_CFR2_MEMLAT_MASK; + *buf |= FIELD_PREP(SPINOR_REG_CYPRESS_CFR2_MEMLAT_MASK, + SPINOR_REG_CYPRESS_CFR2_MEMLAT_11_24); op = (struct spi_mem_op) - CYPRESS_NOR_WR_ANY_REG_OP(addr_mode_nbytes, - SPINOR_REG_CYPRESS_CFR2V, 1, buf); + CYPRESS_NOR_WR_ANY_REG_OP(addr_mode_nbytes, addr, 1, buf); ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto); if (ret) @@ -160,15 +194,41 @@ static int cypress_nor_octal_dtr_en(struct spi_nor *nor) nor->read_dummy = 24; + return 0; +} + +static int cypress_nor_set_octal_dtr_bits(struct spi_nor *nor, u64 addr) +{ + struct spi_mem_op op; + u8 *buf = nor->bouncebuf; + /* Set the octal and DTR enable bits. */ buf[0] = SPINOR_REG_CYPRESS_CFR5_OCT_DTR_EN; op = (struct spi_mem_op) - CYPRESS_NOR_WR_ANY_REG_OP(addr_mode_nbytes, - SPINOR_REG_CYPRESS_CFR5V, 1, buf); + CYPRESS_NOR_WR_ANY_REG_OP(nor->params->addr_mode_nbytes, + addr, 1, buf); - ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto); - if (ret) - return ret; + return spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto); +} + +static int cypress_nor_octal_dtr_en(struct spi_nor *nor) +{ + const struct spi_nor_flash_parameter *params = nor->params; + u8 *buf = nor->bouncebuf; + u64 addr; + int i, ret; + + for (i = 0; i < params->n_dice; i++) { + addr = params->vreg_offset[i] + SPINOR_REG_CYPRESS_CFR2; + ret = cypress_nor_set_memlat(nor, addr); + if (ret) + return ret; + + addr = params->vreg_offset[i] + SPINOR_REG_CYPRESS_CFR5; + ret = cypress_nor_set_octal_dtr_bits(nor, addr); + if (ret) + return ret; + } /* Read flash ID to make sure the switch was successful. */ ret = spi_nor_read_id(nor, nor->addr_nbytes, 3, buf, @@ -178,17 +238,16 @@ static int cypress_nor_octal_dtr_en(struct spi_nor *nor) return ret; } - if (memcmp(buf, nor->info->id, nor->info->id_len)) + if (memcmp(buf, nor->info->id->bytes, nor->info->id->len)) return -EINVAL; return 0; } -static int cypress_nor_octal_dtr_dis(struct spi_nor *nor) +static int cypress_nor_set_single_spi_bits(struct spi_nor *nor, u64 addr) { struct spi_mem_op op; u8 *buf = nor->bouncebuf; - int ret; /* * The register is 1-byte wide, but 1-byte transactions are not allowed @@ -198,11 +257,23 @@ static int cypress_nor_octal_dtr_dis(struct spi_nor *nor) buf[0] = SPINOR_REG_CYPRESS_CFR5_OCT_DTR_DS; buf[1] = 0; op = (struct spi_mem_op) - CYPRESS_NOR_WR_ANY_REG_OP(nor->addr_nbytes, - SPINOR_REG_CYPRESS_CFR5V, 2, buf); - ret = spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_8_8_8_DTR); - if (ret) - return ret; + CYPRESS_NOR_WR_ANY_REG_OP(nor->addr_nbytes, addr, 2, buf); + return spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_8_8_8_DTR); +} + +static int cypress_nor_octal_dtr_dis(struct spi_nor *nor) +{ + const struct spi_nor_flash_parameter *params = nor->params; + u8 *buf = nor->bouncebuf; + u64 addr; + int i, ret; + + for (i = 0; i < params->n_dice; i++) { + addr = params->vreg_offset[i] + SPINOR_REG_CYPRESS_CFR5; + ret = cypress_nor_set_single_spi_bits(nor, addr); + if (ret) + return ret; + } /* Read flash ID to make sure the switch was successful. */ ret = spi_nor_read_id(nor, 0, 0, buf, SNOR_PROTO_1_1_1); @@ -211,7 +282,7 @@ static int cypress_nor_octal_dtr_dis(struct spi_nor *nor) return ret; } - if (memcmp(buf, nor->info->id, nor->info->id_len)) + if (memcmp(buf, nor->info->id->bytes, nor->info->id->len)) return -EINVAL; return 0; @@ -283,10 +354,6 @@ static int cypress_nor_quad_enable_volatile(struct spi_nor *nor) u8 i; int ret; - if (!params->n_dice) - return cypress_nor_quad_enable_volatile_reg(nor, - SPINOR_REG_CYPRESS_CFR1V); - for (i = 0; i < params->n_dice; i++) { addr = params->vreg_offset[i] + SPINOR_REG_CYPRESS_CFR1; ret = cypress_nor_quad_enable_volatile_reg(nor, addr); @@ -297,6 +364,20 @@ static int cypress_nor_quad_enable_volatile(struct spi_nor *nor) return 0; } +static int cypress_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable) +{ + int ret; + struct spi_mem_op op = CYPRESS_NOR_EN4B_EX4B_OP(enable); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + + ret = spi_mem_exec_op(nor->spimem, &op); + if (ret) + dev_dbg(nor->dev, "error %d setting 4-byte mode\n", ret); + + return ret; +} + /** * cypress_nor_determine_addr_mode_by_sr1() - Determine current address mode * (3 or 4-byte) by querying status @@ -408,28 +489,17 @@ static int cypress_nor_set_addr_mode_nbytes(struct spi_nor *nor) return 0; } -static int cypress_nor_get_page_size_single_chip(struct spi_nor *nor) -{ - struct spi_mem_op op = - CYPRESS_NOR_RD_ANY_REG_OP(nor->params->addr_mode_nbytes, - SPINOR_REG_CYPRESS_CFR3V, 0, - nor->bouncebuf); - int ret; - - ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); - if (ret) - return ret; - - if (nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR3_PGSZ) - nor->params->page_size = 512; - else - nor->params->page_size = 256; - - return 0; -} - - -static int cypress_nor_get_page_size_mcp(struct spi_nor *nor) +/** + * cypress_nor_get_page_size() - Get flash page size configuration. + * @nor: pointer to a 'struct spi_nor' + * + * The BFPT table advertises a 512B or 256B page size depending on part but the + * page size is actually configurable (with the default being 256B). Read from + * CFR3V[4] and set the correct size. + * + * Return: 0 on success, -errno otherwise. + */ +static int cypress_nor_get_page_size(struct spi_nor *nor) { struct spi_mem_op op = CYPRESS_NOR_RD_ANY_REG_OP(nor->params->addr_mode_nbytes, @@ -459,23 +529,6 @@ static int cypress_nor_get_page_size_mcp(struct spi_nor *nor) return 0; } -/** - * cypress_nor_get_page_size() - Get flash page size configuration. - * @nor: pointer to a 'struct spi_nor' - * - * The BFPT table advertises a 512B or 256B page size depending on part but the - * page size is actually configurable (with the default being 256B). Read from - * CFR3V[4] and set the correct size. - * - * Return: 0 on success, -errno otherwise. - */ -static int cypress_nor_get_page_size(struct spi_nor *nor) -{ - if (nor->params->n_dice) - return cypress_nor_get_page_size_mcp(nor); - return cypress_nor_get_page_size_single_chip(nor); -} - static void cypress_nor_ecc_init(struct spi_nor *nor) { /* @@ -495,6 +548,9 @@ s25fs256t_post_bfpt_fixup(struct spi_nor *nor, struct spi_mem_op op; int ret; + /* Assign 4-byte address mode method that is not determined in BFPT */ + nor->params->set_4byte_addr_mode = cypress_nor_set_4byte_addr_mode; + ret = cypress_nor_set_addr_mode_nbytes(nor); if (ret) return ret; @@ -512,28 +568,42 @@ s25fs256t_post_bfpt_fixup(struct spi_nor *nor, if (nor->bouncebuf[0]) return -ENODEV; - return cypress_nor_get_page_size(nor); + return 0; } static int s25fs256t_post_sfdp_fixup(struct spi_nor *nor) { struct spi_nor_flash_parameter *params = nor->params; + /* + * S25FS256T does not define the SCCR map, but we would like to use the + * same code base for both single and multi chip package devices, thus + * set the vreg_offset and n_dice to be able to do so. + */ + params->vreg_offset = devm_kmalloc(nor->dev, sizeof(u32), GFP_KERNEL); + if (!params->vreg_offset) + return -ENOMEM; + + params->vreg_offset[0] = SPINOR_REG_CYPRESS_VREG; + params->n_dice = 1; + /* PP_1_1_4_4B is supported but missing in 4BAIT. */ params->hwcaps.mask |= SNOR_HWCAPS_PP_1_1_4; spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP_1_1_4], SPINOR_OP_PP_1_1_4_4B, SNOR_PROTO_1_1_4); - return 0; + return cypress_nor_get_page_size(nor); } -static void s25fs256t_late_init(struct spi_nor *nor) +static int s25fs256t_late_init(struct spi_nor *nor) { cypress_nor_ecc_init(nor); + + return 0; } -static struct spi_nor_fixups s25fs256t_fixups = { +static const struct spi_nor_fixups s25fs256t_fixups = { .post_bfpt = s25fs256t_post_bfpt_fixup, .post_sfdp = s25fs256t_post_sfdp_fixup, .late_init = s25fs256t_late_init, @@ -546,6 +616,9 @@ s25hx_t_post_bfpt_fixup(struct spi_nor *nor, { int ret; + /* Assign 4-byte address mode method that is not determined in BFPT */ + nor->params->set_4byte_addr_mode = cypress_nor_set_4byte_addr_mode; + ret = cypress_nor_set_addr_mode_nbytes(nor); if (ret) return ret; @@ -558,10 +631,20 @@ s25hx_t_post_bfpt_fixup(struct spi_nor *nor, static int s25hx_t_post_sfdp_fixup(struct spi_nor *nor) { - struct spi_nor_erase_type *erase_type = - nor->params->erase_map.erase_type; + struct spi_nor_flash_parameter *params = nor->params; + struct spi_nor_erase_type *erase_type = params->erase_map.erase_type; unsigned int i; + if (!params->n_dice || !params->vreg_offset) { + dev_err(nor->dev, "%s failed. The volatile register offset could not be retrieved from SFDP.\n", + __func__); + return -EOPNOTSUPP; + } + + /* The 2 Gb parts duplicate info and advertise 4 dice instead of 2. */ + if (params->size == SZ_256M) + params->n_dice = 2; + /* * In some parts, 3byte erase opcodes are advertised by 4BAIT. * Convert them to 4byte erase opcodes. @@ -579,35 +662,30 @@ static int s25hx_t_post_sfdp_fixup(struct spi_nor *nor) } } - /* The 2 Gb parts duplicate info and advertise 4 dice instead of 2. */ - if (nor->params->size == SZ_256M) - nor->params->n_dice = 2; - return cypress_nor_get_page_size(nor); } -static void s25hx_t_late_init(struct spi_nor *nor) +static int s25hx_t_late_init(struct spi_nor *nor) { struct spi_nor_flash_parameter *params = nor->params; /* Fast Read 4B requires mode cycles */ params->reads[SNOR_CMD_READ_FAST].num_mode_clocks = 8; - + params->ready = cypress_nor_sr_ready_and_clear; cypress_nor_ecc_init(nor); - /* Replace ready() with multi die version */ - if (params->n_dice) - params->ready = cypress_nor_sr_ready_and_clear; + params->die_erase_opcode = SPINOR_OP_CYPRESS_DIE_ERASE; + return 0; } -static struct spi_nor_fixups s25hx_t_fixups = { +static const struct spi_nor_fixups s25hx_t_fixups = { .post_bfpt = s25hx_t_post_bfpt_fixup, .post_sfdp = s25hx_t_post_sfdp_fixup, .late_init = s25hx_t_late_init, }; /** - * cypress_nor_octal_dtr_enable() - Enable octal DTR on Cypress flashes. + * cypress_nor_set_octal_dtr() - Enable or disable octal DTR on Cypress flashes. * @nor: pointer to a 'struct spi_nor' * @enable: whether to enable or disable Octal DTR * @@ -616,7 +694,7 @@ static struct spi_nor_fixups s25hx_t_fixups = { * * Return: 0 on success, -errno otherwise. */ -static int cypress_nor_octal_dtr_enable(struct spi_nor *nor, bool enable) +static int cypress_nor_set_octal_dtr(struct spi_nor *nor, bool enable) { return enable ? cypress_nor_octal_dtr_en(nor) : cypress_nor_octal_dtr_dis(nor); @@ -624,22 +702,34 @@ static int cypress_nor_octal_dtr_enable(struct spi_nor *nor, bool enable) static int s28hx_t_post_sfdp_fixup(struct spi_nor *nor) { + struct spi_nor_flash_parameter *params = nor->params; + + if (!params->n_dice || !params->vreg_offset) { + dev_err(nor->dev, "%s failed. The volatile register offset could not be retrieved from SFDP.\n", + __func__); + return -EOPNOTSUPP; + } + + /* The 2 Gb parts duplicate info and advertise 4 dice instead of 2. */ + if (params->size == SZ_256M) + params->n_dice = 2; + /* * On older versions of the flash the xSPI Profile 1.0 table has the * 8D-8D-8D Fast Read opcode as 0x00. But it actually should be 0xEE. */ - if (nor->params->reads[SNOR_CMD_READ_8_8_8_DTR].opcode == 0) - nor->params->reads[SNOR_CMD_READ_8_8_8_DTR].opcode = + if (params->reads[SNOR_CMD_READ_8_8_8_DTR].opcode == 0) + params->reads[SNOR_CMD_READ_8_8_8_DTR].opcode = SPINOR_OP_CYPRESS_RD_FAST; /* This flash is also missing the 4-byte Page Program opcode bit. */ - spi_nor_set_pp_settings(&nor->params->page_programs[SNOR_CMD_PP], + spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP], SPINOR_OP_PP_4B, SNOR_PROTO_1_1_1); /* * Since xSPI Page Program opcode is backward compatible with * Legacy SPI, use Legacy SPI opcode there as well. */ - spi_nor_set_pp_settings(&nor->params->page_programs[SNOR_CMD_PP_8_8_8_DTR], + spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP_8_8_8_DTR], SPINOR_OP_PP_4B, SNOR_PROTO_8_8_8_DTR); /* @@ -647,7 +737,7 @@ static int s28hx_t_post_sfdp_fixup(struct spi_nor *nor) * address bytes needed for Read Status Register command as 0 but the * actual value for that is 4. */ - nor->params->rdsr_addr_nbytes = 4; + params->rdsr_addr_nbytes = 4; return cypress_nor_get_page_size(nor); } @@ -656,19 +746,21 @@ static int s28hx_t_post_bfpt_fixup(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, const struct sfdp_bfpt *bfpt) { - int ret; + /* Assign 4-byte address mode method that is not determined in BFPT */ + nor->params->set_4byte_addr_mode = cypress_nor_set_4byte_addr_mode; - ret = cypress_nor_set_addr_mode_nbytes(nor); - if (ret) - return ret; - - return 0; + return cypress_nor_set_addr_mode_nbytes(nor); } -static void s28hx_t_late_init(struct spi_nor *nor) +static int s28hx_t_late_init(struct spi_nor *nor) { - nor->params->octal_dtr_enable = cypress_nor_octal_dtr_enable; + struct spi_nor_flash_parameter *params = nor->params; + + params->set_octal_dtr = cypress_nor_set_octal_dtr; + params->ready = cypress_nor_sr_ready_and_clear; cypress_nor_ecc_init(nor); + + return 0; } static const struct spi_nor_fixups s28hx_t_fixups = { @@ -693,148 +785,307 @@ 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[] = { - /* Spansion/Cypress -- single (large) sector size only, at least - * for the chips listed here (without boot sectors). - */ - { "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64) - NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128) - NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "s25fl128s0", INFO6(0x012018, 0x4d0080, 256 * 1024, 64) - NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) - MFR_FLAGS(USE_CLSR) - }, - { "s25fl128s1", INFO6(0x012018, 0x4d0180, 64 * 1024, 256) - NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) - MFR_FLAGS(USE_CLSR) - }, - { "s25fl256s0", INFO6(0x010219, 0x4d0080, 256 * 1024, 128) - NO_SFDP_FLAGS(SPI_NOR_SKIP_SFDP | SPI_NOR_DUAL_READ | - SPI_NOR_QUAD_READ) - MFR_FLAGS(USE_CLSR) - }, - { "s25fl256s1", INFO6(0x010219, 0x4d0180, 64 * 1024, 512) - NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) - MFR_FLAGS(USE_CLSR) - }, - { "s25fl512s", INFO6(0x010220, 0x4d0080, 256 * 1024, 256) - FLAGS(SPI_NOR_HAS_LOCK) - NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) - MFR_FLAGS(USE_CLSR) - }, - { "s25fs128s1", INFO6(0x012018, 0x4d0181, 64 * 1024, 256) - NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) - MFR_FLAGS(USE_CLSR) - .fixups = &s25fs_s_nor_fixups, }, - { "s25fs256s0", INFO6(0x010219, 0x4d0081, 256 * 1024, 128) - NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) - MFR_FLAGS(USE_CLSR) - }, - { "s25fs256s1", INFO6(0x010219, 0x4d0181, 64 * 1024, 512) - NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) - MFR_FLAGS(USE_CLSR) - }, - { "s25fs512s", INFO6(0x010220, 0x4d0081, 256 * 1024, 256) - NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) - MFR_FLAGS(USE_CLSR) - .fixups = &s25fs_s_nor_fixups, }, - { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64) }, - { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256) }, - { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64) - NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) - MFR_FLAGS(USE_CLSR) - }, - { "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256) - NO_SFDP_FLAGS(SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) - MFR_FLAGS(USE_CLSR) - }, - { "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8) }, - { "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16) }, - { "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32) }, - { "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64) }, - { "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128) }, - { "s25fl004k", INFO(0xef4013, 0, 64 * 1024, 8) - NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | - SPI_NOR_QUAD_READ) }, - { "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16) - NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | - SPI_NOR_QUAD_READ) }, - { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32) - NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | - SPI_NOR_QUAD_READ) }, - { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128) - NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | - SPI_NOR_QUAD_READ) }, - { "s25fl116k", INFO(0x014015, 0, 64 * 1024, 32) - NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | - SPI_NOR_QUAD_READ) }, - { "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64) - NO_SFDP_FLAGS(SECT_4K) }, - { "s25fl164k", INFO(0x014017, 0, 64 * 1024, 128) - NO_SFDP_FLAGS(SECT_4K) }, - { "s25fl204k", INFO(0x014013, 0, 64 * 1024, 8) - NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ) }, - { "s25fl208k", INFO(0x014014, 0, 64 * 1024, 16) - NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ) }, - { "s25fl064l", INFO(0x016017, 0, 64 * 1024, 128) - NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) - FIXUP_FLAGS(SPI_NOR_4B_OPCODES) }, - { "s25fl128l", INFO(0x016018, 0, 64 * 1024, 256) - NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) - FIXUP_FLAGS(SPI_NOR_4B_OPCODES) }, - { "s25fl256l", INFO(0x016019, 0, 64 * 1024, 512) - NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) - FIXUP_FLAGS(SPI_NOR_4B_OPCODES) }, - { "s25fs256t", INFO6(0x342b19, 0x0f0890, 0, 0) - PARSE_SFDP - .fixups = &s25fs256t_fixups }, - { "s25hl512t", INFO6(0x342a1a, 0x0f0390, 256 * 1024, 256) - PARSE_SFDP - MFR_FLAGS(USE_CLSR) - .fixups = &s25hx_t_fixups }, - { "s25hl01gt", INFO6(0x342a1b, 0x0f0390, 256 * 1024, 512) - PARSE_SFDP - MFR_FLAGS(USE_CLSR) - .fixups = &s25hx_t_fixups }, - { "s25hl02gt", INFO6(0x342a1c, 0x0f0090, 0, 0) - PARSE_SFDP - FLAGS(NO_CHIP_ERASE) - .fixups = &s25hx_t_fixups }, - { "s25hs512t", INFO6(0x342b1a, 0x0f0390, 256 * 1024, 256) - PARSE_SFDP - MFR_FLAGS(USE_CLSR) - .fixups = &s25hx_t_fixups }, - { "s25hs01gt", INFO6(0x342b1b, 0x0f0390, 256 * 1024, 512) - PARSE_SFDP - MFR_FLAGS(USE_CLSR) - .fixups = &s25hx_t_fixups }, - { "s25hs02gt", INFO6(0x342b1c, 0x0f0090, 0, 0) - PARSE_SFDP - FLAGS(NO_CHIP_ERASE) - .fixups = &s25hx_t_fixups }, - { "cy15x104q", INFO6(0x042cc2, 0x7f7f7f, 512 * 1024, 1) - FLAGS(SPI_NOR_NO_ERASE) }, - { "s28hl512t", INFO(0x345a1a, 0, 256 * 1024, 256) - PARSE_SFDP + { + .id = SNOR_ID(0x01, 0x02, 0x12), + .name = "s25sl004a", + .size = SZ_512K, + }, { + .id = SNOR_ID(0x01, 0x02, 0x13), + .name = "s25sl008a", + .size = SZ_1M, + }, { + .id = SNOR_ID(0x01, 0x02, 0x14), + .name = "s25sl016a", + .size = SZ_2M, + }, { + .id = SNOR_ID(0x01, 0x02, 0x15, 0x4d, 0x00), + .name = "s25sl032p", + .size = SZ_4M, + .no_sfdp_flags = SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + }, { + .id = SNOR_ID(0x01, 0x02, 0x15), + .name = "s25sl032a", + .size = SZ_4M, + }, { + .id = SNOR_ID(0x01, 0x02, 0x16, 0x4d, 0x00), + .name = "s25sl064p", + .size = SZ_8M, + .no_sfdp_flags = SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + }, { + .id = SNOR_ID(0x01, 0x02, 0x16), + .name = "s25sl064a", + .size = SZ_8M, + }, { + .id = SNOR_ID(0x01, 0x02, 0x19, 0x4d, 0x00, 0x80), + .name = "s25fl256s0", + .size = SZ_32M, + .sector_size = SZ_256K, + .no_sfdp_flags = SPI_NOR_SKIP_SFDP | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .mfr_flags = USE_CLSR, + }, { + .id = SNOR_ID(0x01, 0x02, 0x19, 0x4d, 0x00, 0x81), + .name = "s25fs256s0", + .size = SZ_32M, + .sector_size = SZ_256K, + .no_sfdp_flags = SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .mfr_flags = USE_CLSR, + }, { + .id = SNOR_ID(0x01, 0x02, 0x19, 0x4d, 0x01, 0x80), + .name = "s25fl256s1", + .size = SZ_32M, + .no_sfdp_flags = SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .mfr_flags = USE_CLSR, + }, { + .id = SNOR_ID(0x01, 0x02, 0x19, 0x4d, 0x01, 0x81), + .name = "s25fs256s1", + .size = SZ_32M, + .no_sfdp_flags = SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .mfr_flags = USE_CLSR, + }, { + .id = SNOR_ID(0x01, 0x02, 0x20, 0x4d, 0x00, 0x80), + .name = "s25fl512s", + .size = SZ_64M, + .sector_size = SZ_256K, + .flags = SPI_NOR_HAS_LOCK, + .no_sfdp_flags = SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .mfr_flags = USE_CLSR, + }, { + .id = SNOR_ID(0x01, 0x02, 0x20, 0x4d, 0x00, 0x81), + .name = "s25fs512s", + .size = SZ_64M, + .sector_size = SZ_256K, + .no_sfdp_flags = SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .mfr_flags = USE_CLSR, + .fixups = &s25fs_s_nor_fixups, + }, { + .id = SNOR_ID(0x01, 0x20, 0x18, 0x03, 0x00), + .name = "s25sl12800", + .size = SZ_16M, + .sector_size = SZ_256K, + }, { + .id = SNOR_ID(0x01, 0x20, 0x18, 0x03, 0x01), + .name = "s25sl12801", + .size = SZ_16M, + }, { + .id = SNOR_ID(0x01, 0x20, 0x18, 0x4d, 0x00, 0x80), + .name = "s25fl128s0", + .size = SZ_16M, + .sector_size = SZ_256K, + .no_sfdp_flags = SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .mfr_flags = USE_CLSR, + }, { + .id = SNOR_ID(0x01, 0x20, 0x18, 0x4d, 0x00), + .name = "s25fl129p0", + .size = SZ_16M, + .sector_size = SZ_256K, + .no_sfdp_flags = SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .mfr_flags = USE_CLSR, + }, { + .id = SNOR_ID(0x01, 0x20, 0x18, 0x4d, 0x01, 0x80), + .name = "s25fl128s1", + .size = SZ_16M, + .no_sfdp_flags = SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .mfr_flags = USE_CLSR, + }, { + .id = SNOR_ID(0x01, 0x20, 0x18, 0x4d, 0x01, 0x81), + .name = "s25fs128s1", + .size = SZ_16M, + .no_sfdp_flags = SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .mfr_flags = USE_CLSR, + .fixups = &s25fs_s_nor_fixups, + }, { + .id = SNOR_ID(0x01, 0x20, 0x18, 0x4d, 0x01), + .name = "s25fl129p1", + .size = SZ_16M, + .no_sfdp_flags = SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .mfr_flags = USE_CLSR, + }, { + .id = SNOR_ID(0x01, 0x40, 0x13), + .name = "s25fl204k", + .size = SZ_512K, + .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ, + }, { + .id = SNOR_ID(0x01, 0x40, 0x14), + .name = "s25fl208k", + .size = SZ_1M, + .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ, + }, { + .id = SNOR_ID(0x01, 0x40, 0x15), + .name = "s25fl116k", + .size = SZ_2M, + .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + }, { + .id = SNOR_ID(0x01, 0x40, 0x16), + .name = "s25fl132k", + .size = SZ_4M, + .no_sfdp_flags = SECT_4K, + }, { + .id = SNOR_ID(0x01, 0x40, 0x17), + .name = "s25fl164k", + .size = SZ_8M, + .no_sfdp_flags = SECT_4K, + }, { + .id = SNOR_ID(0x01, 0x60, 0x17), + .name = "s25fl064l", + .size = SZ_8M, + .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .fixup_flags = SPI_NOR_4B_OPCODES, + }, { + .id = SNOR_ID(0x01, 0x60, 0x18), + .name = "s25fl128l", + .size = SZ_16M, + .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .fixup_flags = SPI_NOR_4B_OPCODES, + }, { + .id = SNOR_ID(0x01, 0x60, 0x19), + .name = "s25fl256l", + .size = SZ_32M, + .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .fixup_flags = SPI_NOR_4B_OPCODES, + }, { + .id = SNOR_ID(0x04, 0x2c, 0xc2, 0x7f, 0x7f, 0x7f), + .name = "cy15x104q", + .size = SZ_512K, + .sector_size = SZ_512K, + .flags = SPI_NOR_NO_ERASE, + }, { + .id = SNOR_ID(0x34, 0x2a, 0x1a, 0x0f, 0x03, 0x90), + .name = "s25hl512t", + .mfr_flags = USE_CLPEF, + .fixups = &s25hx_t_fixups + }, { + .id = SNOR_ID(0x34, 0x2a, 0x1b, 0x0f, 0x03, 0x90), + .name = "s25hl01gt", + .mfr_flags = USE_CLPEF, + .fixups = &s25hx_t_fixups + }, { + .id = SNOR_ID(0x34, 0x2a, 0x1c, 0x0f, 0x00, 0x90), + .name = "s25hl02gt", + .mfr_flags = USE_CLPEF, + .fixups = &s25hx_t_fixups + }, { + .id = SNOR_ID(0x34, 0x2b, 0x19, 0x0f, 0x08, 0x90), + .name = "s25fs256t", + .mfr_flags = USE_CLPEF, + .fixups = &s25fs256t_fixups + }, { + .id = SNOR_ID(0x34, 0x2b, 0x1a, 0x0f, 0x03, 0x90), + .name = "s25hs512t", + .mfr_flags = USE_CLPEF, + .fixups = &s25hx_t_fixups + }, { + .id = SNOR_ID(0x34, 0x2b, 0x1b, 0x0f, 0x03, 0x90), + .name = "s25hs01gt", + .mfr_flags = USE_CLPEF, + .fixups = &s25hx_t_fixups + }, { + .id = SNOR_ID(0x34, 0x2b, 0x1c, 0x0f, 0x00, 0x90), + .name = "s25hs02gt", + .mfr_flags = USE_CLPEF, + .fixups = &s25hx_t_fixups + }, { + /* S28HL256T */ + .id = SNOR_ID(0x34, 0x5a, 0x19), + .mfr_flags = USE_CLPEF, + .fixups = &s28hx_t_fixups, + }, { + .id = SNOR_ID(0x34, 0x5a, 0x1a), + .name = "s28hl512t", + .mfr_flags = USE_CLPEF, + .fixups = &s28hx_t_fixups, + }, { + .id = SNOR_ID(0x34, 0x5a, 0x1b), + .name = "s28hl01gt", + .mfr_flags = USE_CLPEF, .fixups = &s28hx_t_fixups, - }, - { "s28hl01gt", INFO(0x345a1b, 0, 256 * 1024, 512) - PARSE_SFDP + }, { + /* S28HL02GT */ + .id = SNOR_ID(0x34, 0x5a, 0x1c), + .mfr_flags = USE_CLPEF, .fixups = &s28hx_t_fixups, - }, - { "s28hs512t", INFO(0x345b1a, 0, 256 * 1024, 256) - PARSE_SFDP + }, { + .id = SNOR_ID(0x34, 0x5b, 0x19), + .mfr_flags = USE_CLPEF, .fixups = &s28hx_t_fixups, - }, - { "s28hs01gt", INFO(0x345b1b, 0, 256 * 1024, 512) - PARSE_SFDP + }, { + .id = SNOR_ID(0x34, 0x5b, 0x1a), + .name = "s28hs512t", + .mfr_flags = USE_CLPEF, .fixups = &s28hx_t_fixups, - }, + }, { + .id = SNOR_ID(0x34, 0x5b, 0x1b), + .name = "s28hs01gt", + .mfr_flags = USE_CLPEF, + .fixups = &s28hx_t_fixups, + }, { + .id = SNOR_ID(0x34, 0x5b, 0x1c), + .name = "s28hs02gt", + .mfr_flags = USE_CLPEF, + .fixups = &s28hx_t_fixups, + }, { + .id = SNOR_ID(0xef, 0x40, 0x13), + .name = "s25fl004k", + .size = SZ_512K, + .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + }, { + .id = SNOR_ID(0xef, 0x40, 0x14), + .name = "s25fl008k", + .size = SZ_1M, + .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + }, { + .id = SNOR_ID(0xef, 0x40, 0x15), + .name = "s25fl016k", + .size = SZ_2M, + .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + }, { + .id = SNOR_ID(0xef, 0x40, 0x17), + .name = "s25fl064k", + .size = SZ_8M, + .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + } }; /** @@ -876,17 +1127,36 @@ static int spansion_nor_sr_ready_and_clear(struct spi_nor *nor) return !(nor->bouncebuf[0] & SR_WIP); } -static void spansion_nor_late_init(struct spi_nor *nor) +static int spansion_nor_late_init(struct spi_nor *nor) { - if (nor->params->size > SZ_16M) { + struct spi_nor_flash_parameter *params = nor->params; + struct spansion_nor_params *priv_params; + u8 mfr_flags = nor->info->mfr_flags; + + if (params->size > SZ_16M) { nor->flags |= SNOR_F_4B_OPCODES; /* No small sector erase for 4-byte command set */ nor->erase_opcode = SPINOR_OP_SE; - nor->mtd.erasesize = nor->info->sector_size; + nor->mtd.erasesize = nor->info->sector_size ?: + SPI_NOR_DEFAULT_SECTOR_SIZE; } - if (nor->info->mfr_flags & USE_CLSR) - nor->params->ready = spansion_nor_sr_ready_and_clear; + if (mfr_flags & (USE_CLSR | USE_CLPEF)) { + priv_params = devm_kmalloc(nor->dev, sizeof(*priv_params), + GFP_KERNEL); + if (!priv_params) + return -ENOMEM; + + if (mfr_flags & USE_CLSR) + priv_params->clsr = SPINOR_OP_CLSR; + else if (mfr_flags & USE_CLPEF) + priv_params->clsr = SPINOR_OP_CLPEF; + + params->priv = priv_params; + params->ready = spansion_nor_sr_ready_and_clear; + } + + return 0; } static const struct spi_nor_fixups spansion_nor_fixups = { |
