diff options
Diffstat (limited to 'drivers/mtd/spi-nor/spansion.c')
-rw-r--r-- | drivers/mtd/spi-nor/spansion.c | 460 |
1 files changed, 396 insertions, 64 deletions
diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index 12a256c0ef4c..15f9a80c10b9 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -14,13 +14,26 @@ #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_CFR1V 0x00800002 +#define SPINOR_REG_CYPRESS_VREG 0x00800000 +#define SPINOR_REG_CYPRESS_STR1 0x0 +#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_CFR2V 0x00800003 +#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_11_24 0xb -#define SPINOR_REG_CYPRESS_CFR3V 0x00800004 +#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_CFR5V 0x00800006 +#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) @@ -29,6 +42,7 @@ SPINOR_REG_CYPRESS_CFR5_OPI) #define SPINOR_REG_CYPRESS_CFR5_OCT_DTR_DS SPINOR_REG_CYPRESS_CFR5_BIT6 #define SPINOR_OP_CYPRESS_RD_FAST 0xee +#define SPINOR_REG_CYPRESS_ARCFN 0x00000006 /* Cypress SPI NOR flash operations. */ #define CYPRESS_NOR_WR_ANY_REG_OP(naddr, addr, ndata, buf) \ @@ -37,10 +51,10 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_DATA_OUT(ndata, buf, 0)) -#define CYPRESS_NOR_RD_ANY_REG_OP(naddr, addr, buf) \ +#define CYPRESS_NOR_RD_ANY_REG_OP(naddr, addr, ndummy, buf) \ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 0), \ SPI_MEM_OP_ADDR(naddr, addr, 0), \ - SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DUMMY(ndummy, 0), \ SPI_MEM_OP_DATA_IN(1, buf, 0)) #define SPANSION_CLSR_OP \ @@ -49,6 +63,84 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DATA) +/** + * spansion_nor_clear_sr() - Clear the Status Register. + * @nor: pointer to 'struct spi_nor'. + */ +static void spansion_nor_clear_sr(struct spi_nor *nor) +{ + int ret; + + if (nor->spimem) { + struct spi_mem_op op = SPANSION_CLSR_OP; + + 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); +} + +static int cypress_nor_sr_ready_and_clear_reg(struct spi_nor *nor, u64 addr) +{ + struct spi_mem_op op = + CYPRESS_NOR_RD_ANY_REG_OP(nor->params->addr_mode_nbytes, addr, + 0, nor->bouncebuf); + int ret; + + ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); + 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"); + + spansion_nor_clear_sr(nor); + + ret = spi_nor_write_disable(nor); + if (ret) + return ret; + + return -EIO; + } + + return !(nor->bouncebuf[0] & SR_WIP); +} +/** + * cypress_nor_sr_ready_and_clear() - Query the Status Register of each die by + * using Read Any Register command to see if the whole 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 cypress_nor_sr_ready_and_clear(struct spi_nor *nor) +{ + struct spi_nor_flash_parameter *params = nor->params; + u64 addr; + int ret; + u8 i; + + for (i = 0; i < params->n_dice; i++) { + addr = params->vreg_offset[i] + SPINOR_REG_CYPRESS_STR1; + ret = cypress_nor_sr_ready_and_clear_reg(nor, addr); + if (ret < 0) + return ret; + else if (ret == 0) + return 0; + } + + return 1; +} + static int cypress_nor_octal_dtr_en(struct spi_nor *nor) { struct spi_mem_op op; @@ -125,21 +217,7 @@ static int cypress_nor_octal_dtr_dis(struct spi_nor *nor) return 0; } -/** - * cypress_nor_quad_enable_volatile() - enable Quad I/O mode in volatile - * register. - * @nor: pointer to a 'struct spi_nor' - * - * It is recommended to update volatile registers in the field application due - * to a risk of the non-volatile registers corruption by power interrupt. This - * function sets Quad Enable bit in CFR1 volatile. If users set the Quad Enable - * bit in the CFR1 non-volatile in advance (typically by a Flash programmer - * before mounting Flash on PCB), the Quad Enable bit in the CFR1 volatile is - * also set during Flash power-up. - * - * Return: 0 on success, -errno otherwise. - */ -static int cypress_nor_quad_enable_volatile(struct spi_nor *nor) +static int cypress_nor_quad_enable_volatile_reg(struct spi_nor *nor, u64 addr) { struct spi_mem_op op; u8 addr_mode_nbytes = nor->params->addr_mode_nbytes; @@ -147,8 +225,7 @@ static int cypress_nor_quad_enable_volatile(struct spi_nor *nor) int ret; op = (struct spi_mem_op) - CYPRESS_NOR_RD_ANY_REG_OP(addr_mode_nbytes, - SPINOR_REG_CYPRESS_CFR1V, + CYPRESS_NOR_RD_ANY_REG_OP(addr_mode_nbytes, addr, 0, nor->bouncebuf); ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); @@ -161,8 +238,7 @@ static int cypress_nor_quad_enable_volatile(struct spi_nor *nor) /* Update the Quad Enable bit. */ nor->bouncebuf[0] |= SPINOR_REG_CYPRESS_CFR1_QUAD_EN; op = (struct spi_mem_op) - CYPRESS_NOR_WR_ANY_REG_OP(addr_mode_nbytes, - SPINOR_REG_CYPRESS_CFR1V, 1, + CYPRESS_NOR_WR_ANY_REG_OP(addr_mode_nbytes, addr, 1, nor->bouncebuf); ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto); if (ret) @@ -172,8 +248,7 @@ static int cypress_nor_quad_enable_volatile(struct spi_nor *nor) /* Read back and check it. */ op = (struct spi_mem_op) - CYPRESS_NOR_RD_ANY_REG_OP(addr_mode_nbytes, - SPINOR_REG_CYPRESS_CFR1V, + CYPRESS_NOR_RD_ANY_REG_OP(addr_mode_nbytes, addr, 0, nor->bouncebuf); ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); if (ret) @@ -188,21 +263,156 @@ static int cypress_nor_quad_enable_volatile(struct spi_nor *nor) } /** - * cypress_nor_set_page_size() - Set page size which corresponds to the flash - * configuration. + * cypress_nor_quad_enable_volatile() - enable Quad I/O mode in volatile + * register. * @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. + * It is recommended to update volatile registers in the field application due + * to a risk of the non-volatile registers corruption by power interrupt. This + * function sets Quad Enable bit in CFR1 volatile. If users set the Quad Enable + * bit in the CFR1 non-volatile in advance (typically by a Flash programmer + * before mounting Flash on PCB), the Quad Enable bit in the CFR1 volatile is + * also set during Flash power-up. + * + * Return: 0 on success, -errno otherwise. + */ +static int cypress_nor_quad_enable_volatile(struct spi_nor *nor) +{ + struct spi_nor_flash_parameter *params = nor->params; + u64 addr; + 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); + if (ret) + return ret; + } + + return 0; +} + +/** + * cypress_nor_determine_addr_mode_by_sr1() - Determine current address mode + * (3 or 4-byte) by querying status + * register 1 (SR1). + * @nor: pointer to a 'struct spi_nor' + * @addr_mode: ponter to a buffer where we return the determined + * address mode. + * + * This function tries to determine current address mode by comparing SR1 value + * from RDSR1(no address), RDAR(3-byte address), and RDAR(4-byte address). + * + * Return: 0 on success, -errno otherwise. + */ +static int cypress_nor_determine_addr_mode_by_sr1(struct spi_nor *nor, + u8 *addr_mode) +{ + struct spi_mem_op op = + CYPRESS_NOR_RD_ANY_REG_OP(3, SPINOR_REG_CYPRESS_STR1V, 0, + nor->bouncebuf); + bool is3byte, is4byte; + int ret; + + ret = spi_nor_read_sr(nor, &nor->bouncebuf[1]); + if (ret) + return ret; + + ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); + if (ret) + return ret; + + is3byte = (nor->bouncebuf[0] == nor->bouncebuf[1]); + + op = (struct spi_mem_op) + CYPRESS_NOR_RD_ANY_REG_OP(4, SPINOR_REG_CYPRESS_STR1V, 0, + nor->bouncebuf); + ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); + if (ret) + return ret; + + is4byte = (nor->bouncebuf[0] == nor->bouncebuf[1]); + + if (is3byte == is4byte) + return -EIO; + if (is3byte) + *addr_mode = 3; + else + *addr_mode = 4; + + return 0; +} + +/** + * cypress_nor_set_addr_mode_nbytes() - Set the number of address bytes mode of + * current address mode. + * @nor: pointer to a 'struct spi_nor' + * + * Determine current address mode by reading SR1 with different methods, then + * query CFR2V[7] to confirm. If determination is failed, force enter to 4-byte + * address mode. * * Return: 0 on success, -errno otherwise. */ -static int cypress_nor_set_page_size(struct spi_nor *nor) +static int cypress_nor_set_addr_mode_nbytes(struct spi_nor *nor) +{ + struct spi_mem_op op; + u8 addr_mode; + int ret; + + /* + * Read SR1 by RDSR1 and RDAR(3- AND 4-byte addr). Use write enable + * that sets bit-1 in SR1. + */ + ret = spi_nor_write_enable(nor); + if (ret) + return ret; + ret = cypress_nor_determine_addr_mode_by_sr1(nor, &addr_mode); + if (ret) { + ret = spi_nor_set_4byte_addr_mode(nor, true); + if (ret) + return ret; + return spi_nor_write_disable(nor); + } + ret = spi_nor_write_disable(nor); + if (ret) + return ret; + + /* + * Query CFR2V and make sure no contradiction between determined address + * mode and CFR2V[7]. + */ + op = (struct spi_mem_op) + CYPRESS_NOR_RD_ANY_REG_OP(addr_mode, SPINOR_REG_CYPRESS_CFR2V, + 0, nor->bouncebuf); + ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); + if (ret) + return ret; + + if (nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR2_ADRBYT) { + if (addr_mode != 4) + return spi_nor_set_4byte_addr_mode(nor, true); + } else { + if (addr_mode != 3) + return spi_nor_set_4byte_addr_mode(nor, true); + } + + nor->params->addr_nbytes = addr_mode; + nor->params->addr_mode_nbytes = addr_mode; + + 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, + SPINOR_REG_CYPRESS_CFR3V, 0, nor->bouncebuf); int ret; @@ -218,18 +428,135 @@ static int cypress_nor_set_page_size(struct spi_nor *nor) return 0; } + +static int cypress_nor_get_page_size_mcp(struct spi_nor *nor) +{ + struct spi_mem_op op = + CYPRESS_NOR_RD_ANY_REG_OP(nor->params->addr_mode_nbytes, + 0, 0, nor->bouncebuf); + struct spi_nor_flash_parameter *params = nor->params; + int ret; + u8 i; + + /* + * Use the minimum common page size configuration. Programming 256-byte + * under 512-byte page size configuration is safe. + */ + params->page_size = 256; + for (i = 0; i < params->n_dice; i++) { + op.addr.val = params->vreg_offset[i] + SPINOR_REG_CYPRESS_CFR3; + + ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); + if (ret) + return ret; + + if (!(nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR3_PGSZ)) + return 0; + } + + params->page_size = 512; + + 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) +{ + /* + * Programming is supported only in 16-byte ECC data unit granularity. + * Byte-programming, bit-walking, or multiple program operations to the + * same ECC data unit without an erase are not allowed. + */ + nor->params->writesize = 16; + nor->flags |= SNOR_F_ECC; +} + +static int +s25fs256t_post_bfpt_fixup(struct spi_nor *nor, + const struct sfdp_parameter_header *bfpt_header, + const struct sfdp_bfpt *bfpt) +{ + struct spi_mem_op op; + int ret; + + ret = cypress_nor_set_addr_mode_nbytes(nor); + if (ret) + return ret; + + /* Read Architecture Configuration Register (ARCFN) */ + op = (struct spi_mem_op) + CYPRESS_NOR_RD_ANY_REG_OP(nor->params->addr_mode_nbytes, + SPINOR_REG_CYPRESS_ARCFN, 1, + nor->bouncebuf); + ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); + if (ret) + return ret; + + /* ARCFN value must be 0 if uniform sector is selected */ + if (nor->bouncebuf[0]) + return -ENODEV; + + return cypress_nor_get_page_size(nor); +} + +static int s25fs256t_post_sfdp_fixup(struct spi_nor *nor) +{ + struct spi_nor_flash_parameter *params = nor->params; + + /* 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; +} + +static void s25fs256t_late_init(struct spi_nor *nor) +{ + cypress_nor_ecc_init(nor); +} + +static struct spi_nor_fixups s25fs256t_fixups = { + .post_bfpt = s25fs256t_post_bfpt_fixup, + .post_sfdp = s25fs256t_post_sfdp_fixup, + .late_init = s25fs256t_late_init, +}; + static int s25hx_t_post_bfpt_fixup(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, const struct sfdp_bfpt *bfpt) { + int ret; + + ret = cypress_nor_set_addr_mode_nbytes(nor); + if (ret) + return ret; + /* Replace Quad Enable with volatile version */ nor->params->quad_enable = cypress_nor_quad_enable_volatile; - return cypress_nor_set_page_size(nor); + return 0; } -static void s25hx_t_post_sfdp_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; @@ -251,6 +578,12 @@ static void s25hx_t_post_sfdp_fixup(struct spi_nor *nor) break; } } + + /* 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) @@ -260,8 +593,11 @@ static void s25hx_t_late_init(struct spi_nor *nor) /* Fast Read 4B requires mode cycles */ params->reads[SNOR_CMD_READ_FAST].num_mode_clocks = 8; - /* The writesize should be ECC data unit size */ - params->writesize = 16; + cypress_nor_ecc_init(nor); + + /* Replace ready() with multi die version */ + if (params->n_dice) + params->ready = cypress_nor_sr_ready_and_clear; } static struct spi_nor_fixups s25hx_t_fixups = { @@ -286,7 +622,7 @@ static int cypress_nor_octal_dtr_enable(struct spi_nor *nor, bool enable) cypress_nor_octal_dtr_dis(nor); } -static void s28hx_t_post_sfdp_fixup(struct spi_nor *nor) +static int s28hx_t_post_sfdp_fixup(struct spi_nor *nor) { /* * On older versions of the flash the xSPI Profile 1.0 table has the @@ -312,19 +648,27 @@ static void s28hx_t_post_sfdp_fixup(struct spi_nor *nor) * actual value for that is 4. */ nor->params->rdsr_addr_nbytes = 4; + + return cypress_nor_get_page_size(nor); } static int s28hx_t_post_bfpt_fixup(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, const struct sfdp_bfpt *bfpt) { - return cypress_nor_set_page_size(nor); + int ret; + + ret = cypress_nor_set_addr_mode_nbytes(nor); + if (ret) + return ret; + + return 0; } static void s28hx_t_late_init(struct spi_nor *nor) { nor->params->octal_dtr_enable = cypress_nor_octal_dtr_enable; - nor->params->writesize = 16; + cypress_nor_ecc_init(nor); } static const struct spi_nor_fixups s28hx_t_fixups = { @@ -446,6 +790,9 @@ static const struct flash_info spansion_nor_parts[] = { { "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) @@ -454,6 +801,10 @@ static const struct flash_info spansion_nor_parts[] = { 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) @@ -462,6 +813,10 @@ static const struct flash_info spansion_nor_parts[] = { 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) @@ -483,29 +838,6 @@ static const struct flash_info spansion_nor_parts[] = { }; /** - * spansion_nor_clear_sr() - Clear the Status Register. - * @nor: pointer to 'struct spi_nor'. - */ -static void spansion_nor_clear_sr(struct spi_nor *nor) -{ - int ret; - - if (nor->spimem) { - struct spi_mem_op op = SPANSION_CLSR_OP; - - 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); -} - -/** * spansion_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'. |