From: Takahiro Kuwano <[email protected]> Move chip specific fixups from core to manufacturer drivers. As manufacturer drivers are used in tiny as well, all fixups migrated from core need to be excluded in CONFIG_SPI_FLASH_TINY.
Some helper functions used in fixups are exposed from core and some need to be guarded by CONFIG_SPI_FLASH_TINY to avoid name conflict with local functions in tiny. Signed-off-by: Takahiro Kuwano <[email protected]> --- drivers/mtd/spi/macronix.c | 89 ++++ drivers/mtd/spi/micron-st.c | 99 +++++ drivers/mtd/spi/sf_internal.h | 10 + drivers/mtd/spi/spansion.c | 627 +++++++++++++++++++++++++++ drivers/mtd/spi/spi-nor-core.c | 929 +---------------------------------------- drivers/mtd/spi/winbond.c | 104 +++++ 6 files changed, 938 insertions(+), 920 deletions(-) diff --git a/drivers/mtd/spi/macronix.c b/drivers/mtd/spi/macronix.c index 87843978cad..9ca55fc4f99 100644 --- a/drivers/mtd/spi/macronix.c +++ b/drivers/mtd/spi/macronix.c @@ -5,6 +5,7 @@ * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ */ +#include <dm/device_compat.h> #include <linux/mtd/spi-nor.h> #include "sf_internal.h" @@ -59,7 +60,95 @@ static const struct flash_info macronix_nor_parts[] = { { INFO("mx25uw6345g", 0xc28437, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) }, }; +#if !CONFIG_IS_ENABLED(SPI_FLASH_TINY) +/** + * spi_nor_macronix_octal_dtr_enable() - Enable octal DTR on Macronix flashes. + * @nor: pointer to a 'struct spi_nor' + * + * Set Macronix max dummy cycles 20 to allow the flash to run at fastest frequency. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_macronix_octal_dtr_enable(struct spi_nor *nor) +{ + struct spi_mem_op op; + int ret; + u8 buf; + + ret = write_enable(nor); + if (ret) + return ret; + + buf = SPINOR_REG_MXIC_DC_20; + op = (struct spi_mem_op) + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_CR2, 1), + SPI_MEM_OP_ADDR(4, SPINOR_REG_MXIC_CR2_DC, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(1, &buf, 1)); + + ret = spi_mem_exec_op(nor->spi, &op); + if (ret) + return ret; + + ret = spi_nor_wait_till_ready(nor); + if (ret) + return ret; + + nor->read_dummy = MXIC_MAX_DC; + ret = write_enable(nor); + if (ret) + return ret; + + buf = SPINOR_REG_MXIC_OPI_DTR_EN; + op = (struct spi_mem_op) + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_CR2, 1), + SPI_MEM_OP_ADDR(4, SPINOR_REG_MXIC_CR2_MODE, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(1, &buf, 1)); + + ret = spi_mem_exec_op(nor->spi, &op); + if (ret) { + dev_err(nor->dev, "Failed to enable octal DTR mode\n"); + return ret; + } + nor->reg_proto = SNOR_PROTO_8_8_8_DTR; + + return 0; +} + +static void macronix_octal_late_init(struct spi_nor *nor, + struct spi_nor_flash_parameter *params) +{ + nor->octal_dtr_enable = spi_nor_macronix_octal_dtr_enable; +} + +static void macronix_octal_post_sfdp_fixup(struct spi_nor *nor, + struct spi_nor_flash_parameter *params) +{ + /* + * Adding SNOR_HWCAPS_PP_8_8_8_DTR in hwcaps.mask when + * SPI_NOR_OCTAL_DTR_READ flag exists. + */ + if (params->hwcaps.mask & SNOR_HWCAPS_READ_8_8_8_DTR) + params->hwcaps.mask |= SNOR_HWCAPS_PP_8_8_8_DTR; +} + +static struct spi_nor_fixups macronix_octal_fixups = { + .post_sfdp = macronix_octal_post_sfdp_fixup, + .late_init = macronix_octal_late_init, +}; + +static void macronix_set_fixups(struct spi_nor *nor) +{ + if (nor->info->flags & SPI_NOR_OCTAL_DTR_READ) + nor->fixups = ¯onix_octal_fixups; +} +#endif + const struct spi_nor_manufacturer spi_nor_macronix = { .parts = macronix_nor_parts, .nparts = ARRAY_SIZE(macronix_nor_parts), +#if !CONFIG_IS_ENABLED(SPI_FLASH_TINY) + .set_fixups = macronix_set_fixups, +#endif }; diff --git a/drivers/mtd/spi/micron-st.c b/drivers/mtd/spi/micron-st.c index 2d47f1c60c0..9982e9d660a 100644 --- a/drivers/mtd/spi/micron-st.c +++ b/drivers/mtd/spi/micron-st.c @@ -5,6 +5,7 @@ * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ */ +#include <dm/device_compat.h> #include <linux/mtd/spi-nor.h> #include "sf_internal.h" @@ -59,9 +60,107 @@ static const struct flash_info st_nor_parts[] = { { INFO("m25px64", 0x207117, 0, 64 * 1024, 128, 0) }, }; +#if !CONFIG_IS_ENABLED(SPI_FLASH_TINY) && CONFIG_IS_ENABLED(SPI_FLASH_MT35XU) +static int spi_nor_micron_octal_dtr_enable(struct spi_nor *nor) +{ + struct spi_mem_op op; + u8 buf; + u8 addr_width = 3; + int ret; + + /* Set dummy cycles for Fast Read to the default of 20. */ + ret = write_enable(nor); + if (ret) + return ret; + + buf = 20; + op = (struct spi_mem_op) + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 1), + SPI_MEM_OP_ADDR(addr_width, SPINOR_REG_MT_CFR1V, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(1, &buf, 1)); + ret = spi_mem_exec_op(nor->spi, &op); + if (ret) + return ret; + + ret = spi_nor_wait_till_ready(nor); + if (ret) + return ret; + + nor->read_dummy = 20; + + ret = write_enable(nor); + if (ret) + return ret; + + buf = SPINOR_MT_OCT_DTR; + op = (struct spi_mem_op) + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 1), + SPI_MEM_OP_ADDR(addr_width, SPINOR_REG_MT_CFR0V, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(1, &buf, 1)); + ret = spi_mem_exec_op(nor->spi, &op); + if (ret) { + dev_err(nor->dev, "Failed to enable octal DTR mode\n"); + return ret; + } + + return 0; +} + +static void mt35xu512aba_late_init(struct spi_nor *nor, + struct spi_nor_flash_parameter *params) +{ + nor->octal_dtr_enable = spi_nor_micron_octal_dtr_enable; +} + +static void mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor, + struct spi_nor_flash_parameter *params) +{ + /* Set the Fast Read settings. */ + params->hwcaps.mask |= SNOR_HWCAPS_READ_8_8_8_DTR; + spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_8_8_8_DTR], + 0, 20, SPINOR_OP_MT_DTR_RD, + SNOR_PROTO_8_8_8_DTR); + + params->hwcaps.mask |= SNOR_HWCAPS_PP_8_8_8_DTR; + + nor->cmd_ext_type = SPI_NOR_EXT_REPEAT; + params->rdsr_dummy = 8; + params->rdsr_addr_nbytes = 0; + + /* + * SCCR Map 22nd DWORD does not indicate DTR Octal Mode Enable + * for MT35XU512ABA but is actually supported by device. + */ + nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE; + + /* + * The BFPT quad enable field is set to a reserved value so the quad + * enable function is ignored by spi_nor_parse_bfpt(). Make sure we + * disable it. + */ + params->quad_enable = NULL; +} + +static struct spi_nor_fixups mt35xu512aba_fixups = { + .post_sfdp = mt35xu512aba_post_sfdp_fixup, + .late_init = mt35xu512aba_late_init, +}; + +static void micron_set_fixups(struct spi_nor *nor) +{ + if (!strcmp(nor->info->name, "mt35xu512aba")) + nor->fixups = &mt35xu512aba_fixups; +} +#endif + const struct spi_nor_manufacturer spi_nor_micron = { .parts = micron_nor_parts, .nparts = ARRAY_SIZE(micron_nor_parts), +#if !CONFIG_IS_ENABLED(SPI_FLASH_TINY) && CONFIG_IS_ENABLED(SPI_FLASH_MT35XU) + .set_fixups = micron_set_fixups, +#endif }; const struct spi_nor_manufacturer spi_nor_st = { diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index 941729f5707..0ea19913701 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -202,6 +202,12 @@ static inline int spi_nor_parse_sfdp(struct spi_nor *nor, #endif #if !CONFIG_IS_ENABLED(SPI_FLASH_TINY) +int write_enable(struct spi_nor *nor); +int set_4byte(struct spi_nor *nor, const struct flash_info *info, int enable); +int spi_nor_sr_ready(struct spi_nor *nor); +int spi_nor_wait_till_ready(struct spi_nor *nor); +int spi_nor_read_write_reg(struct spi_nor *nor, struct spi_mem_op *op, + void *buf); void spi_nor_set_read_settings(struct spi_nor_read_command *read, u8 num_mode_clocks, u8 num_wait_states, @@ -211,6 +217,10 @@ int spansion_read_cr_quad_enable(struct spi_nor *nor); int macronix_quad_enable(struct spi_nor *nor); #endif +void spi_nor_set_pp_settings(struct spi_nor_pp_command *pp, u8 opcode, + enum spi_nor_protocol proto); +int spi_nor_default_setup(struct spi_nor *nor, const struct flash_info *info, + const struct spi_nor_flash_parameter *params); int spi_nor_hwcaps_read2cmd(u32 hwcaps); int spansion_no_read_cr_quad_enable(struct spi_nor *nor); diff --git a/drivers/mtd/spi/spansion.c b/drivers/mtd/spi/spansion.c index 405f5bf5e79..1dd38667062 100644 --- a/drivers/mtd/spi/spansion.c +++ b/drivers/mtd/spi/spansion.c @@ -5,6 +5,8 @@ * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ */ +#include <dm/device_compat.h> +#include <linux/sizes.h> #include <linux/mtd/spi-nor.h> #include "sf_internal.h" @@ -67,7 +69,632 @@ static const struct flash_info spansion_nor_parts[] = { #endif }; +#if !CONFIG_IS_ENABLED(SPI_FLASH_TINY) + +/* Use ID byte 4 to distinguish S25FS256T and S25Hx-T */ +#define S25FS256T_ID4 (0x08) + +/* Number of dummy cycle for Read Any Register (RDAR) op. */ +#define S25FS_S_RDAR_DUMMY 8 + +static int spansion_read_any_reg(struct spi_nor *nor, u32 addr, u8 dummy, + u8 *val) +{ + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 0), + SPI_MEM_OP_ADDR(nor->addr_mode_nbytes, addr, 0), + SPI_MEM_OP_DUMMY(dummy, 0), + SPI_MEM_OP_DATA_IN(1, NULL, 0)); + u8 buf[2]; + int ret; + + spi_nor_setup_op(nor, &op, nor->reg_proto); + + /* + * In Octal DTR mode, the number of address bytes is always 4 regardless + * of addressing mode setting. + */ + if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) + op.addr.nbytes = 4; + + /* + * We don't want to read only one byte in DTR mode. So, read 2 and then + * discard the second byte. + */ + if (spi_nor_protocol_is_dtr(nor->reg_proto)) + op.data.nbytes = 2; + + ret = spi_nor_read_write_reg(nor, &op, buf); + if (ret) + return ret; + + *val = buf[0]; + + return 0; +} + +static int spansion_write_any_reg(struct spi_nor *nor, u32 addr, u8 val) +{ + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1), + SPI_MEM_OP_ADDR(nor->addr_mode_nbytes, addr, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(1, NULL, 1)); + + return spi_nor_read_write_reg(nor, &op, &val); +} + +/** + * spansion_quad_enable_volatile() - enable Quad I/O mode in volatile register. + * @nor: pointer to a 'struct spi_nor' + * @addr_base: base address of register (can be >0 in multi-die parts) + * @dummy: number of dummy cycles for register read + * + * 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. + * + * Return: 0 on success, -errno otherwise. + */ +static int spansion_quad_enable_volatile(struct spi_nor *nor, u32 addr_base, + u8 dummy) +{ + u32 addr = addr_base + SPINOR_REG_CYPRESS_CFR1V; + + u8 cr; + int ret; + + /* Check current Quad Enable bit value. */ + ret = spansion_read_any_reg(nor, addr, dummy, &cr); + if (ret < 0) { + dev_dbg(nor->dev, + "error while reading configuration register\n"); + return -EINVAL; + } + + if (cr & CR_QUAD_EN_SPAN) + return 0; + + cr |= CR_QUAD_EN_SPAN; + + write_enable(nor); + + ret = spansion_write_any_reg(nor, addr, cr); + + if (ret < 0) { + dev_dbg(nor->dev, + "error while writing configuration register\n"); + return -EINVAL; + } + + /* Read back and check it. */ + ret = spansion_read_any_reg(nor, addr, dummy, &cr); + if (ret || !(cr & CR_QUAD_EN_SPAN)) { + dev_dbg(nor->dev, "Spansion Quad bit not set\n"); + return -EINVAL; + } + + return 0; +} + +/* + * Read status register 1 by using Read Any Register command to support multi + * die package parts. + */ +static int spansion_sr_ready(struct spi_nor *nor, u32 addr_base, u8 dummy) +{ + u32 reg_addr = addr_base + SPINOR_REG_CYPRESS_STR1V; + u8 sr; + int ret; + + ret = spansion_read_any_reg(nor, reg_addr, dummy, &sr); + if (ret < 0) + return ret; + + if (sr & (SR_E_ERR | SR_P_ERR)) { + if (sr & SR_E_ERR) + dev_dbg(nor->dev, "Erase Error occurred\n"); + else + dev_dbg(nor->dev, "Programming Error occurred\n"); + + nor->write_reg(nor, SPINOR_OP_CYPRESS_CLPEF, NULL, 0); + return -EIO; + } + + return !(sr & SR_WIP); +} + +/** + * spansion_erase_non_uniform() - erase non-uniform sectors for Spansion/Cypress + * chips + * @nor: pointer to a 'struct spi_nor' + * @addr: address of the sector to erase + * @opcode_4k: opcode for 4K sector erase + * @ovlsz_top: size of overlaid portion at the top address + * @ovlsz_btm: size of overlaid portion at the bottom address + * + * Erase an address range on the nor chip that can contain 4KB sectors overlaid + * on top and/or bottom. The appropriate erase opcode and size are chosen by + * address to erase and size of overlaid portion. + * + * Return: number of bytes erased on success, -errno otherwise. + */ +static int spansion_erase_non_uniform(struct spi_nor *nor, u32 addr, + u8 opcode_4k, u32 ovlsz_top, + u32 ovlsz_btm) +{ + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(nor->erase_opcode, 0), + SPI_MEM_OP_ADDR(nor->addr_width, addr, 0), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_NO_DATA); + struct mtd_info *mtd = &nor->mtd; + u32 erasesize; + int ret; + + /* 4KB sectors */ + if (op.addr.val < ovlsz_btm || + op.addr.val >= mtd->size - ovlsz_top) { + op.cmd.opcode = opcode_4k; + erasesize = SZ_4K; + + /* Non-overlaid portion in the normal sector at the bottom */ + } else if (op.addr.val == ovlsz_btm) { + op.cmd.opcode = nor->erase_opcode; + erasesize = mtd->erasesize - ovlsz_btm; + + /* Non-overlaid portion in the normal sector at the top */ + } else if (op.addr.val == mtd->size - mtd->erasesize) { + op.cmd.opcode = nor->erase_opcode; + erasesize = mtd->erasesize - ovlsz_top; + + /* Normal sectors */ + } else { + op.cmd.opcode = nor->erase_opcode; + erasesize = mtd->erasesize; + } + + spi_nor_setup_op(nor, &op, nor->write_proto); + + ret = spi_mem_exec_op(nor->spi, &op); + if (ret) + return ret; + + return erasesize; +} + +static int s25fs_s_quad_enable(struct spi_nor *nor) +{ + return spansion_quad_enable_volatile(nor, 0, S25FS_S_RDAR_DUMMY); +} + +static int s25fs_s_erase_non_uniform(struct spi_nor *nor, loff_t addr) +{ + u8 opcode = nor->addr_width == 4 ? SPINOR_OP_BE_4K_4B : SPINOR_OP_BE_4K; + + /* Support 8 x 4KB sectors at bottom */ + return spansion_erase_non_uniform(nor, addr, opcode, 0, SZ_32K); +} + +static int s25fs_s_setup(struct spi_nor *nor, const struct flash_info *info, + const struct spi_nor_flash_parameter *params) +{ + int ret; + u8 cfr3v; + + /* Bank Address Register is not supported */ + if (CONFIG_IS_ENABLED(SPI_FLASH_BAR)) + return -EOPNOTSUPP; + + /* + * Read CR3V to check if uniform sector is selected. If not, assign an + * erase hook that supports non-uniform erase. + */ + ret = spansion_read_any_reg(nor, SPINOR_REG_CYPRESS_CFR3V, + S25FS_S_RDAR_DUMMY, &cfr3v); + if (ret) + return ret; + if (!(cfr3v & SPINOR_REG_CYPRESS_CFR3_UNISECT)) + nor->erase = s25fs_s_erase_non_uniform; + + return spi_nor_default_setup(nor, info, params); +} + +static void s25fs_s_late_init(struct spi_nor *nor, + struct spi_nor_flash_parameter *params) +{ + nor->setup = s25fs_s_setup; +} + +static int s25fs_s_post_bfpt_fixup(struct spi_nor *nor, + const struct sfdp_parameter_header *header, + const struct sfdp_bfpt *bfpt, + struct spi_nor_flash_parameter *params) +{ + /* The erase size is set to 4K from BFPT, but it's wrong. Fix it. */ + nor->erase_opcode = SPINOR_OP_SE; + nor->mtd.erasesize = nor->info->sector_size; + + /* The S25FS-S chip family reports 512-byte pages in BFPT but + * in reality the write buffer still wraps at the safe default + * of 256 bytes. Overwrite the page size advertised by BFPT + * to get the writes working. + */ + params->page_size = 256; + + return 0; +} + +static void s25fs_s_post_sfdp_fixup(struct spi_nor *nor, + struct spi_nor_flash_parameter *params) +{ + /* + * The S25FS064S(8MB) supports 1-1-2 and 1-1-4 commands, but params for + * read ops in SFDP are wrong. The other density parts do not support + * 1-1-2 and 1-1-4 commands. + */ + if (params->size == SZ_8M) { + spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_1_1_2], + 0, 8, SPINOR_OP_READ_1_1_2, + SNOR_PROTO_1_1_2); + spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_1_1_4], + 0, 8, SPINOR_OP_READ_1_1_4, + SNOR_PROTO_1_1_4); + } else { + params->hwcaps.mask &= ~SNOR_HWCAPS_READ_1_1_2; + params->hwcaps.mask &= ~SNOR_HWCAPS_READ_1_1_4; + params->hwcaps.mask &= ~SNOR_HWCAPS_PP_1_1_4; + } + + /* Use volatile register to enable quad */ + params->quad_enable = s25fs_s_quad_enable; +} + +static struct spi_nor_fixups s25fs_s_fixups = { + .post_bfpt = s25fs_s_post_bfpt_fixup, + .post_sfdp = s25fs_s_post_sfdp_fixup, + .late_init = s25fs_s_late_init, +}; + +static int s25_s28_mdp_ready(struct spi_nor *nor) +{ + u32 addr; + int ret; + + for (addr = 0; addr < nor->mtd.size; addr += SZ_128M) { + ret = spansion_sr_ready(nor, addr, nor->rdsr_dummy); + if (ret < 0) + return ret; + else if (ret == 0) + return 0; + } + + return 1; +} + +static int s25_quad_enable(struct spi_nor *nor) +{ + u32 addr; + int ret; + + for (addr = 0; addr < nor->mtd.size; addr += SZ_128M) { + ret = spansion_quad_enable_volatile(nor, addr, 0); + if (ret) + return ret; + } + + return 0; +} + +static int s25_s28_erase_non_uniform(struct spi_nor *nor, loff_t addr) +{ + /* Support 32 x 4KB sectors at bottom */ + return spansion_erase_non_uniform(nor, addr, SPINOR_OP_BE_4K_4B, 0, + SZ_128K); +} + +static int s25_s28_setup(struct spi_nor *nor, const struct flash_info *info, + const struct spi_nor_flash_parameter *params) +{ + int ret; + u8 cr; + +#if CONFIG_IS_ENABLED(SPI_FLASH_BAR) + return -ENOTSUPP; /* Bank Address Register is not supported */ +#endif + /* + * S25FS256T has multiple sector architecture options, with selection of + * count and location of 128KB and 64KB sectors. This driver supports + * uniform 128KB only due to complexity of non-uniform layout. + */ + if (nor->info->id[4] == S25FS256T_ID4) { + ret = spansion_read_any_reg(nor, SPINOR_REG_CYPRESS_ARCFN, 8, + &cr); + if (ret) + return ret; + + if (cr) /* Option 0 (ARCFN[7:0] == 0x00) is uniform */ + return -EOPNOTSUPP; + + return spi_nor_default_setup(nor, info, params); + } + + /* + * Read CFR3V to check if uniform sector is selected. If not, assign an + * erase hook that supports non-uniform erase. + */ + ret = spansion_read_any_reg(nor, SPINOR_REG_CYPRESS_CFR3V, 0, &cr); + if (ret) + return ret; + if (!(cr & SPINOR_REG_CYPRESS_CFR3_UNISECT)) + nor->erase = s25_s28_erase_non_uniform; + + /* + * For the multi-die package parts, the ready() hook is needed to check + * all dies' status via read any register. + */ + if (nor->mtd.size > SZ_128M) + nor->ready = s25_s28_mdp_ready; + + return spi_nor_default_setup(nor, info, params); +} + +static void s25_late_init(struct spi_nor *nor, + struct spi_nor_flash_parameter *params) +{ + nor->setup = s25_s28_setup; + + /* + * 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. + */ + params->writesize = 16; +} + +static int s25_s28_post_bfpt_fixup(struct spi_nor *nor, + const struct sfdp_parameter_header *header, + const struct sfdp_bfpt *bfpt, + struct spi_nor_flash_parameter *params) +{ + int ret; + u32 addr; + u8 cfr3v; + + /* erase size in case it is set to 4K from BFPT */ + nor->erase_opcode = SPINOR_OP_SE_4B; + nor->mtd.erasesize = nor->info->sector_size; + + /* + * The default address mode in multi-die package parts (>1Gb) may be + * 3- or 4-byte, depending on model number. BootROM code in some SoCs + * use 3-byte mode for backward compatibility and should switch to + * 4-byte mode after BootROM phase. Since registers in the 2nd die are + * mapped within 32-bit address space, we need to make sure the flash is + * in 4-byte address mode. The default address mode can be distinguished + * by BFPT 16th DWORD. Power cycle exits 4-byte address mode if default + * is 3-byte address mode. + */ + if (params->size > SZ_128M) { + if (bfpt->dwords[BFPT_DWORD(16)] & BFPT_DWORD16_EX4B_PWRCYC) { + ret = set_4byte(nor, nor->info, 1); + if (ret) + return ret; + } + nor->addr_mode_nbytes = 4; + } + + /* The default address mode in S25FS256T is 4. */ + if (nor->info->id[4] == S25FS256T_ID4) + nor->addr_mode_nbytes = 4; + + /* + * The page_size is set to 512B from BFPT, but it actually depends on + * the configuration register. Look up the CFR3V and determine the + * page_size. For multi-die package parts, use 512B only when the all + * dies are configured to 512B buffer. + */ + for (addr = 0; addr < params->size; addr += SZ_128M) { + ret = spansion_read_any_reg(nor, + addr + SPINOR_REG_CYPRESS_CFR3V, 0, + &cfr3v); + if (ret) + return ret; + + if (!(cfr3v & SPINOR_REG_CYPRESS_CFR3_PGSZ)) { + params->page_size = 256; + return 0; + } + } + params->page_size = 512; + + return 0; +} + +static void s25_post_sfdp_fixup(struct spi_nor *nor, + struct spi_nor_flash_parameter *params) +{ + if (nor->info->id[4] == S25FS256T_ID4) { + /* PP_1_1_4 is supported */ + params->hwcaps.mask |= SNOR_HWCAPS_PP_1_1_4; + } else { + /* READ_FAST_4B (0Ch) requires mode cycles*/ + params->reads[SNOR_CMD_READ_FAST].num_mode_clocks = 8; + /* PP_1_1_4 is not supported */ + params->hwcaps.mask &= ~SNOR_HWCAPS_PP_1_1_4; + /* Use volatile register to enable quad */ + params->quad_enable = s25_quad_enable; + } +} + +static struct spi_nor_fixups s25_fixups = { + .post_bfpt = s25_s28_post_bfpt_fixup, + .post_sfdp = s25_post_sfdp_fixup, + .late_init = s25_late_init, +}; + +static int s25fl256l_setup(struct spi_nor *nor, const struct flash_info *info, + const struct spi_nor_flash_parameter *params) +{ + return -ENOTSUPP; /* Bank Address Register is not supported */ +} + +static void s25fl256l_late_init(struct spi_nor *nor, + struct spi_nor_flash_parameter *params) +{ + nor->setup = s25fl256l_setup; +} + +static struct spi_nor_fixups s25fl256l_fixups = { + .late_init = s25fl256l_late_init, +}; + +#ifdef CONFIG_SPI_FLASH_S28HX_T +/** + * spi_nor_cypress_octal_dtr_enable() - Enable octal DTR on Cypress flashes. + * @nor: pointer to a 'struct spi_nor' + * + * This also sets the memory access latency cycles to 24 to allow the flash to + * run at up to 200MHz. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_cypress_octal_dtr_enable(struct spi_nor *nor) +{ + u32 addr; + u8 buf; + int ret; + + ret = write_enable(nor); + if (ret) + return ret; + + /* Use 24 dummy cycles for memory array reads. */ + for (addr = 0; addr < nor->mtd.size; addr += SZ_128M) { + ret = spansion_read_any_reg(nor, + addr + SPINOR_REG_CYPRESS_CFR2V, 0, + &buf); + if (ret) + return ret; + + buf &= ~SPINOR_REG_CYPRESS_CFR2_MEMLAT_MASK; + buf |= SPINOR_REG_CYPRESS_CFR2_MEMLAT_11_24; + ret = spansion_write_any_reg(nor, + addr + SPINOR_REG_CYPRESS_CFR2V, + buf); + if (ret) { + dev_warn(nor->dev, "failed to set default memory latency value: %d\n", ret); + return ret; + } + } + nor->read_dummy = 24; + + ret = write_enable(nor); + if (ret) + return ret; + + /* Set the octal and DTR enable bits. */ + buf = SPINOR_REG_CYPRESS_CFR5_OCT_DTR_EN; + for (addr = 0; addr < nor->mtd.size; addr += SZ_128M) { + ret = spansion_write_any_reg(nor, + addr + SPINOR_REG_CYPRESS_CFR5V, + buf); + if (ret) { + dev_warn(nor->dev, "Failed to enable octal DTR mode\n"); + return ret; + } + } + + return 0; +} + +static void s28hx_t_late_init(struct spi_nor *nor, + struct spi_nor_flash_parameter *params) +{ + nor->octal_dtr_enable = spi_nor_cypress_octal_dtr_enable; + nor->setup = s25_s28_setup; + + /* + * 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. + */ + params->writesize = 16; +} + +static void s28hx_t_post_sfdp_fixup(struct spi_nor *nor, + struct spi_nor_flash_parameter *params) +{ + /* + * 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 (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; + + params->hwcaps.mask |= SNOR_HWCAPS_PP_8_8_8_DTR; + + /* This flash is also missing the 4-byte Page Program opcode bit. */ + 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(¶ms->page_programs[SNOR_CMD_PP_8_8_8_DTR], + SPINOR_OP_PP_4B, SNOR_PROTO_8_8_8_DTR); + + /* + * The xSPI Profile 1.0 table advertises the number of additional + * address bytes needed for Read Status Register command as 0 but the + * actual value for that is 4. + */ + params->rdsr_addr_nbytes = 4; +} + +static struct spi_nor_fixups s28hx_t_fixups = { + .post_sfdp = s28hx_t_post_sfdp_fixup, + .post_bfpt = s25_s28_post_bfpt_fixup, + .late_init = s28hx_t_late_init, +}; +#endif /* CONFIG_SPI_FLASH_S28HX_T */ + +static void spansion_set_fixups(struct spi_nor *nor) +{ + if (JEDEC_MFR(nor->info) == SNOR_MFR_CYPRESS) { + switch (nor->info->id[1]) { + case 0x2a: /* S25HL (QSPI, 3.3V) */ + case 0x2b: /* S25HS (QSPI, 1.8V) */ + nor->fixups = &s25_fixups; + break; + +#ifdef CONFIG_SPI_FLASH_S28HX_T + case 0x5a: /* S28HL (Octal, 3.3V) */ + case 0x5b: /* S28HS (Octal, 1.8V) */ + nor->fixups = &s28hx_t_fixups; + break; +#endif + + default: + break; + } + } + + if (CONFIG_IS_ENABLED(SPI_FLASH_BAR) && + !strcmp(nor->info->name, "s25fl256l")) + nor->fixups = &s25fl256l_fixups; + + /* For FS-S (family ID = 0x81) */ + if (JEDEC_MFR(nor->info) == SNOR_MFR_SPANSION && nor->info->id[5] == 0x81) + nor->fixups = &s25fs_s_fixups; +} +#endif /* !CONFIG_IS_ENABLED(SPI_FLASH_TINY) */ + const struct spi_nor_manufacturer spi_nor_spansion = { .parts = spansion_nor_parts, .nparts = ARRAY_SIZE(spansion_nor_parts), +#if !CONFIG_IS_ENABLED(SPI_FLASH_TINY) + .set_fixups = spansion_set_fixups, +#endif }; diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c index d107828092c..6e61b5653bd 100644 --- a/drivers/mtd/spi/spi-nor-core.c +++ b/drivers/mtd/spi/spi-nor-core.c @@ -123,8 +123,8 @@ void spi_nor_setup_op(const struct spi_nor *nor, } } -static int spi_nor_read_write_reg(struct spi_nor *nor, struct spi_mem_op - *op, void *buf) +int spi_nor_read_write_reg(struct spi_nor *nor, struct spi_mem_op *op, + void *buf) { if (op->data.dir == SPI_MEM_DATA_IN) op->data.buf.in = buf; @@ -165,55 +165,6 @@ static int spi_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) return spi_nor_read_write_reg(nor, &op, buf); } -#ifdef CONFIG_SPI_FLASH_SPANSION -static int spansion_read_any_reg(struct spi_nor *nor, u32 addr, u8 dummy, - u8 *val) -{ - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 0), - SPI_MEM_OP_ADDR(nor->addr_mode_nbytes, addr, 0), - SPI_MEM_OP_DUMMY(dummy, 0), - SPI_MEM_OP_DATA_IN(1, NULL, 0)); - u8 buf[2]; - int ret; - - spi_nor_setup_op(nor, &op, nor->reg_proto); - - /* - * In Octal DTR mode, the number of address bytes is always 4 regardless - * of addressing mode setting. - */ - if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) - op.addr.nbytes = 4; - - /* - * We don't want to read only one byte in DTR mode. So, read 2 and then - * discard the second byte. - */ - if (spi_nor_protocol_is_dtr(nor->reg_proto)) - op.data.nbytes = 2; - - ret = spi_nor_read_write_reg(nor, &op, buf); - if (ret) - return ret; - - *val = buf[0]; - - return 0; -} - -static int spansion_write_any_reg(struct spi_nor *nor, u32 addr, u8 val) -{ - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1), - SPI_MEM_OP_ADDR(nor->addr_mode_nbytes, addr, 1), - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(1, NULL, 1)); - - return spi_nor_read_write_reg(nor, &op, &val); -} -#endif - static ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len, u_char *buf) { @@ -472,7 +423,7 @@ static int write_sr3(struct spi_nor *nor, u8 val) * Set write enable latch with Write Enable command. * Returns negative if error occurred. */ -static int write_enable(struct spi_nor *nor) +int write_enable(struct spi_nor *nor) { return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0); } @@ -577,8 +528,7 @@ static void spi_nor_set_4byte_opcodes(struct spi_nor *nor, #endif /* !CONFIG_IS_ENABLED(SPI_FLASH_BAR) */ /* Enable/disable 4-byte addressing mode. */ -static int set_4byte(struct spi_nor *nor, const struct flash_info *info, - int enable) +int set_4byte(struct spi_nor *nor, const struct flash_info *info, int enable) { int status; bool need_wren = false; @@ -626,36 +576,7 @@ static int set_4byte(struct spi_nor *nor, const struct flash_info *info, } } -#ifdef CONFIG_SPI_FLASH_SPANSION -/* - * Read status register 1 by using Read Any Register command to support multi - * die package parts. - */ -static int spansion_sr_ready(struct spi_nor *nor, u32 addr_base, u8 dummy) -{ - u32 reg_addr = addr_base + SPINOR_REG_CYPRESS_STR1V; - u8 sr; - int ret; - - ret = spansion_read_any_reg(nor, reg_addr, dummy, &sr); - if (ret < 0) - return ret; - - if (sr & (SR_E_ERR | SR_P_ERR)) { - if (sr & SR_E_ERR) - dev_dbg(nor->dev, "Erase Error occurred\n"); - else - dev_dbg(nor->dev, "Programming Error occurred\n"); - - nor->write_reg(nor, SPINOR_OP_CYPRESS_CLPEF, NULL, 0); - return -EIO; - } - - return !(sr & SR_WIP); -} -#endif - -static int spi_nor_sr_ready(struct spi_nor *nor) +int spi_nor_sr_ready(struct spi_nor *nor) { int sr = read_sr(nor); @@ -745,7 +666,7 @@ static int spi_nor_wait_till_ready_with_timeout(struct spi_nor *nor, return -ETIMEDOUT; } -static int spi_nor_wait_till_ready(struct spi_nor *nor) +int spi_nor_wait_till_ready(struct spi_nor *nor) { return spi_nor_wait_till_ready_with_timeout(nor, DEFAULT_READY_WAIT_JIFFIES); @@ -1035,67 +956,6 @@ err: return ret; } -#ifdef CONFIG_SPI_FLASH_SPANSION -/** - * spansion_erase_non_uniform() - erase non-uniform sectors for Spansion/Cypress - * chips - * @nor: pointer to a 'struct spi_nor' - * @addr: address of the sector to erase - * @opcode_4k: opcode for 4K sector erase - * @ovlsz_top: size of overlaid portion at the top address - * @ovlsz_btm: size of overlaid portion at the bottom address - * - * Erase an address range on the nor chip that can contain 4KB sectors overlaid - * on top and/or bottom. The appropriate erase opcode and size are chosen by - * address to erase and size of overlaid portion. - * - * Return: number of bytes erased on success, -errno otherwise. - */ -static int spansion_erase_non_uniform(struct spi_nor *nor, u32 addr, - u8 opcode_4k, u32 ovlsz_top, - u32 ovlsz_btm) -{ - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(nor->erase_opcode, 0), - SPI_MEM_OP_ADDR(nor->addr_width, addr, 0), - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_NO_DATA); - struct mtd_info *mtd = &nor->mtd; - u32 erasesize; - int ret; - - /* 4KB sectors */ - if (op.addr.val < ovlsz_btm || - op.addr.val >= mtd->size - ovlsz_top) { - op.cmd.opcode = opcode_4k; - erasesize = SZ_4K; - - /* Non-overlaid portion in the normal sector at the bottom */ - } else if (op.addr.val == ovlsz_btm) { - op.cmd.opcode = nor->erase_opcode; - erasesize = mtd->erasesize - ovlsz_btm; - - /* Non-overlaid portion in the normal sector at the top */ - } else if (op.addr.val == mtd->size - mtd->erasesize) { - op.cmd.opcode = nor->erase_opcode; - erasesize = mtd->erasesize - ovlsz_top; - - /* Normal sectors */ - } else { - op.cmd.opcode = nor->erase_opcode; - erasesize = mtd->erasesize; - } - - spi_nor_setup_op(nor, &op, nor->write_proto); - - ret = spi_mem_exec_op(nor->spi, &op); - if (ret) - return ret; - - return erasesize; -} -#endif - #if defined(CONFIG_SPI_FLASH_LOCK) #if defined(CONFIG_SPI_FLASH_STMICRO) || defined(CONFIG_SPI_FLASH_SST) /* Write status register and ensure bits in mask match written values */ @@ -1980,61 +1840,6 @@ int macronix_quad_enable(struct spi_nor *nor) } #endif -#ifdef CONFIG_SPI_FLASH_SPANSION -/** - * spansion_quad_enable_volatile() - enable Quad I/O mode in volatile register. - * @nor: pointer to a 'struct spi_nor' - * @addr_base: base address of register (can be >0 in multi-die parts) - * @dummy: number of dummy cycles for register read - * - * 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. - * - * Return: 0 on success, -errno otherwise. - */ -static int spansion_quad_enable_volatile(struct spi_nor *nor, u32 addr_base, - u8 dummy) -{ - u32 addr = addr_base + SPINOR_REG_CYPRESS_CFR1V; - - u8 cr; - int ret; - - /* Check current Quad Enable bit value. */ - ret = spansion_read_any_reg(nor, addr, dummy, &cr); - if (ret < 0) { - dev_dbg(nor->dev, - "error while reading configuration register\n"); - return -EINVAL; - } - - if (cr & CR_QUAD_EN_SPAN) - return 0; - - cr |= CR_QUAD_EN_SPAN; - - write_enable(nor); - - ret = spansion_write_any_reg(nor, addr, cr); - - if (ret < 0) { - dev_dbg(nor->dev, - "error while writing configuration register\n"); - return -EINVAL; - } - - /* Read back and check it. */ - ret = spansion_read_any_reg(nor, addr, dummy, &cr); - if (ret || !(cr & CR_QUAD_EN_SPAN)) { - dev_dbg(nor->dev, "Spansion Quad bit not set\n"); - return -EINVAL; - } - - return 0; -} -#endif - #if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND) /* * Write status Register and configuration register with 2 bytes @@ -2165,7 +1970,7 @@ spi_nor_set_read_settings(struct spi_nor_read_command *read, read->proto = proto; } -static void +void spi_nor_set_pp_settings(struct spi_nor_pp_command *pp, u8 opcode, enum spi_nor_protocol proto) @@ -2755,9 +2560,8 @@ static int spi_nor_select_erase(struct spi_nor *nor, return 0; } -static int spi_nor_default_setup(struct spi_nor *nor, - const struct flash_info *info, - const struct spi_nor_flash_parameter *params) +int spi_nor_default_setup(struct spi_nor *nor, const struct flash_info *info, + const struct spi_nor_flash_parameter *params) { u32 shared_mask; bool enable_quad_io; @@ -2809,664 +2613,6 @@ static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info, return nor->setup(nor, info, params); } -#ifdef CONFIG_SPI_FLASH_SPANSION - -/* Use ID byte 4 to distinguish S25FS256T and S25Hx-T */ -#define S25FS256T_ID4 (0x08) - -/* Number of dummy cycle for Read Any Register (RDAR) op. */ -#define S25FS_S_RDAR_DUMMY 8 - -static int s25fs_s_quad_enable(struct spi_nor *nor) -{ - return spansion_quad_enable_volatile(nor, 0, S25FS_S_RDAR_DUMMY); -} - -static int s25fs_s_erase_non_uniform(struct spi_nor *nor, loff_t addr) -{ - u8 opcode = nor->addr_width == 4 ? SPINOR_OP_BE_4K_4B : SPINOR_OP_BE_4K; - - /* Support 8 x 4KB sectors at bottom */ - return spansion_erase_non_uniform(nor, addr, opcode, 0, SZ_32K); -} - -static int s25fs_s_setup(struct spi_nor *nor, const struct flash_info *info, - const struct spi_nor_flash_parameter *params) -{ - int ret; - u8 cfr3v; - - /* Bank Address Register is not supported */ - if (CONFIG_IS_ENABLED(SPI_FLASH_BAR)) - return -EOPNOTSUPP; - - /* - * Read CR3V to check if uniform sector is selected. If not, assign an - * erase hook that supports non-uniform erase. - */ - ret = spansion_read_any_reg(nor, SPINOR_REG_CYPRESS_CFR3V, - S25FS_S_RDAR_DUMMY, &cfr3v); - if (ret) - return ret; - if (!(cfr3v & SPINOR_REG_CYPRESS_CFR3_UNISECT)) - nor->erase = s25fs_s_erase_non_uniform; - - return spi_nor_default_setup(nor, info, params); -} - -static void s25fs_s_late_init(struct spi_nor *nor, - struct spi_nor_flash_parameter *params) -{ - nor->setup = s25fs_s_setup; -} - -static int s25fs_s_post_bfpt_fixup(struct spi_nor *nor, - const struct sfdp_parameter_header *header, - const struct sfdp_bfpt *bfpt, - struct spi_nor_flash_parameter *params) -{ - /* The erase size is set to 4K from BFPT, but it's wrong. Fix it. */ - nor->erase_opcode = SPINOR_OP_SE; - nor->mtd.erasesize = nor->info->sector_size; - - /* The S25FS-S chip family reports 512-byte pages in BFPT but - * in reality the write buffer still wraps at the safe default - * of 256 bytes. Overwrite the page size advertised by BFPT - * to get the writes working. - */ - params->page_size = 256; - - return 0; -} - -static void s25fs_s_post_sfdp_fixup(struct spi_nor *nor, - struct spi_nor_flash_parameter *params) -{ - /* - * The S25FS064S(8MB) supports 1-1-2 and 1-1-4 commands, but params for - * read ops in SFDP are wrong. The other density parts do not support - * 1-1-2 and 1-1-4 commands. - */ - if (params->size == SZ_8M) { - spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_1_1_2], - 0, 8, SPINOR_OP_READ_1_1_2, - SNOR_PROTO_1_1_2); - spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_1_1_4], - 0, 8, SPINOR_OP_READ_1_1_4, - SNOR_PROTO_1_1_4); - } else { - params->hwcaps.mask &= ~SNOR_HWCAPS_READ_1_1_2; - params->hwcaps.mask &= ~SNOR_HWCAPS_READ_1_1_4; - params->hwcaps.mask &= ~SNOR_HWCAPS_PP_1_1_4; - } - - /* Use volatile register to enable quad */ - params->quad_enable = s25fs_s_quad_enable; -} - -static struct spi_nor_fixups s25fs_s_fixups = { - .post_bfpt = s25fs_s_post_bfpt_fixup, - .post_sfdp = s25fs_s_post_sfdp_fixup, - .late_init = s25fs_s_late_init, -}; - -static int s25_s28_mdp_ready(struct spi_nor *nor) -{ - u32 addr; - int ret; - - for (addr = 0; addr < nor->mtd.size; addr += SZ_128M) { - ret = spansion_sr_ready(nor, addr, nor->rdsr_dummy); - if (ret < 0) - return ret; - else if (ret == 0) - return 0; - } - - return 1; -} - -static int s25_quad_enable(struct spi_nor *nor) -{ - u32 addr; - int ret; - - for (addr = 0; addr < nor->mtd.size; addr += SZ_128M) { - ret = spansion_quad_enable_volatile(nor, addr, 0); - if (ret) - return ret; - } - - return 0; -} - -static int s25_s28_erase_non_uniform(struct spi_nor *nor, loff_t addr) -{ - /* Support 32 x 4KB sectors at bottom */ - return spansion_erase_non_uniform(nor, addr, SPINOR_OP_BE_4K_4B, 0, - SZ_128K); -} - -static int s25_s28_setup(struct spi_nor *nor, const struct flash_info *info, - const struct spi_nor_flash_parameter *params) -{ - int ret; - u8 cr; - -#if CONFIG_IS_ENABLED(SPI_FLASH_BAR) - return -ENOTSUPP; /* Bank Address Register is not supported */ -#endif - /* - * S25FS256T has multiple sector architecture options, with selection of - * count and location of 128KB and 64KB sectors. This driver supports - * uniform 128KB only due to complexity of non-uniform layout. - */ - if (nor->info->id[4] == S25FS256T_ID4) { - ret = spansion_read_any_reg(nor, SPINOR_REG_CYPRESS_ARCFN, 8, - &cr); - if (ret) - return ret; - - if (cr) /* Option 0 (ARCFN[7:0] == 0x00) is uniform */ - return -EOPNOTSUPP; - - return spi_nor_default_setup(nor, info, params); - } - - /* - * Read CFR3V to check if uniform sector is selected. If not, assign an - * erase hook that supports non-uniform erase. - */ - ret = spansion_read_any_reg(nor, SPINOR_REG_CYPRESS_CFR3V, 0, &cr); - if (ret) - return ret; - if (!(cr & SPINOR_REG_CYPRESS_CFR3_UNISECT)) - nor->erase = s25_s28_erase_non_uniform; - - /* - * For the multi-die package parts, the ready() hook is needed to check - * all dies' status via read any register. - */ - if (nor->mtd.size > SZ_128M) - nor->ready = s25_s28_mdp_ready; - - return spi_nor_default_setup(nor, info, params); -} - -static void s25_late_init(struct spi_nor *nor, - struct spi_nor_flash_parameter *params) -{ - nor->setup = s25_s28_setup; - - /* - * 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. - */ - params->writesize = 16; -} - -static int s25_s28_post_bfpt_fixup(struct spi_nor *nor, - const struct sfdp_parameter_header *header, - const struct sfdp_bfpt *bfpt, - struct spi_nor_flash_parameter *params) -{ - int ret; - u32 addr; - u8 cfr3v; - - /* erase size in case it is set to 4K from BFPT */ - nor->erase_opcode = SPINOR_OP_SE_4B; - nor->mtd.erasesize = nor->info->sector_size; - - /* - * The default address mode in multi-die package parts (>1Gb) may be - * 3- or 4-byte, depending on model number. BootROM code in some SoCs - * use 3-byte mode for backward compatibility and should switch to - * 4-byte mode after BootROM phase. Since registers in the 2nd die are - * mapped within 32-bit address space, we need to make sure the flash is - * in 4-byte address mode. The default address mode can be distinguished - * by BFPT 16th DWORD. Power cycle exits 4-byte address mode if default - * is 3-byte address mode. - */ - if (params->size > SZ_128M) { - if (bfpt->dwords[BFPT_DWORD(16)] & BFPT_DWORD16_EX4B_PWRCYC) { - ret = set_4byte(nor, nor->info, 1); - if (ret) - return ret; - } - nor->addr_mode_nbytes = 4; - } - - /* The default address mode in S25FS256T is 4. */ - if (nor->info->id[4] == S25FS256T_ID4) - nor->addr_mode_nbytes = 4; - - /* - * The page_size is set to 512B from BFPT, but it actually depends on - * the configuration register. Look up the CFR3V and determine the - * page_size. For multi-die package parts, use 512B only when the all - * dies are configured to 512B buffer. - */ - for (addr = 0; addr < params->size; addr += SZ_128M) { - ret = spansion_read_any_reg(nor, - addr + SPINOR_REG_CYPRESS_CFR3V, 0, - &cfr3v); - if (ret) - return ret; - - if (!(cfr3v & SPINOR_REG_CYPRESS_CFR3_PGSZ)) { - params->page_size = 256; - return 0; - } - } - params->page_size = 512; - - return 0; -} - -static void s25_post_sfdp_fixup(struct spi_nor *nor, - struct spi_nor_flash_parameter *params) -{ - if (nor->info->id[4] == S25FS256T_ID4) { - /* PP_1_1_4 is supported */ - params->hwcaps.mask |= SNOR_HWCAPS_PP_1_1_4; - } else { - /* READ_FAST_4B (0Ch) requires mode cycles*/ - params->reads[SNOR_CMD_READ_FAST].num_mode_clocks = 8; - /* PP_1_1_4 is not supported */ - params->hwcaps.mask &= ~SNOR_HWCAPS_PP_1_1_4; - /* Use volatile register to enable quad */ - params->quad_enable = s25_quad_enable; - } -} - -static struct spi_nor_fixups s25_fixups = { - .post_bfpt = s25_s28_post_bfpt_fixup, - .post_sfdp = s25_post_sfdp_fixup, - .late_init = s25_late_init, -}; - -static int s25fl256l_setup(struct spi_nor *nor, const struct flash_info *info, - const struct spi_nor_flash_parameter *params) -{ - return -ENOTSUPP; /* Bank Address Register is not supported */ -} - -static void s25fl256l_late_init(struct spi_nor *nor, - struct spi_nor_flash_parameter *params) -{ - nor->setup = s25fl256l_setup; -} - -static struct spi_nor_fixups s25fl256l_fixups = { - .late_init = s25fl256l_late_init, -}; -#endif - -#ifdef CONFIG_SPI_FLASH_S28HX_T -/** - * spi_nor_cypress_octal_dtr_enable() - Enable octal DTR on Cypress flashes. - * @nor: pointer to a 'struct spi_nor' - * - * This also sets the memory access latency cycles to 24 to allow the flash to - * run at up to 200MHz. - * - * Return: 0 on success, -errno otherwise. - */ -static int spi_nor_cypress_octal_dtr_enable(struct spi_nor *nor) -{ - u32 addr; - u8 buf; - int ret; - - ret = write_enable(nor); - if (ret) - return ret; - - /* Use 24 dummy cycles for memory array reads. */ - for (addr = 0; addr < nor->mtd.size; addr += SZ_128M) { - ret = spansion_read_any_reg(nor, - addr + SPINOR_REG_CYPRESS_CFR2V, 0, - &buf); - if (ret) - return ret; - - buf &= ~SPINOR_REG_CYPRESS_CFR2_MEMLAT_MASK; - buf |= SPINOR_REG_CYPRESS_CFR2_MEMLAT_11_24; - ret = spansion_write_any_reg(nor, - addr + SPINOR_REG_CYPRESS_CFR2V, - buf); - if (ret) { - dev_warn(nor->dev, "failed to set default memory latency value: %d\n", ret); - return ret; - } - } - nor->read_dummy = 24; - - ret = write_enable(nor); - if (ret) - return ret; - - /* Set the octal and DTR enable bits. */ - buf = SPINOR_REG_CYPRESS_CFR5_OCT_DTR_EN; - for (addr = 0; addr < nor->mtd.size; addr += SZ_128M) { - ret = spansion_write_any_reg(nor, - addr + SPINOR_REG_CYPRESS_CFR5V, - buf); - if (ret) { - dev_warn(nor->dev, "Failed to enable octal DTR mode\n"); - return ret; - } - } - - return 0; -} - -static void s28hx_t_late_init(struct spi_nor *nor, - struct spi_nor_flash_parameter *params) -{ - nor->octal_dtr_enable = spi_nor_cypress_octal_dtr_enable; - nor->setup = s25_s28_setup; - - /* - * 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. - */ - params->writesize = 16; -} - -static void s28hx_t_post_sfdp_fixup(struct spi_nor *nor, - struct spi_nor_flash_parameter *params) -{ - /* - * 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 (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; - - params->hwcaps.mask |= SNOR_HWCAPS_PP_8_8_8_DTR; - - /* This flash is also missing the 4-byte Page Program opcode bit. */ - 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(¶ms->page_programs[SNOR_CMD_PP_8_8_8_DTR], - SPINOR_OP_PP_4B, SNOR_PROTO_8_8_8_DTR); - - /* - * The xSPI Profile 1.0 table advertises the number of additional - * address bytes needed for Read Status Register command as 0 but the - * actual value for that is 4. - */ - params->rdsr_addr_nbytes = 4; -} - -static struct spi_nor_fixups s28hx_t_fixups = { - .post_sfdp = s28hx_t_post_sfdp_fixup, - .post_bfpt = s25_s28_post_bfpt_fixup, - .late_init = s28hx_t_late_init, -}; -#endif /* CONFIG_SPI_FLASH_S28HX_T */ - -#ifdef CONFIG_SPI_FLASH_MT35XU -static int spi_nor_micron_octal_dtr_enable(struct spi_nor *nor) -{ - struct spi_mem_op op; - u8 buf; - u8 addr_width = 3; - int ret; - - /* Set dummy cycles for Fast Read to the default of 20. */ - ret = write_enable(nor); - if (ret) - return ret; - - buf = 20; - op = (struct spi_mem_op) - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 1), - SPI_MEM_OP_ADDR(addr_width, SPINOR_REG_MT_CFR1V, 1), - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(1, &buf, 1)); - ret = spi_mem_exec_op(nor->spi, &op); - if (ret) - return ret; - - ret = spi_nor_wait_till_ready(nor); - if (ret) - return ret; - - nor->read_dummy = 20; - - ret = write_enable(nor); - if (ret) - return ret; - - buf = SPINOR_MT_OCT_DTR; - op = (struct spi_mem_op) - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 1), - SPI_MEM_OP_ADDR(addr_width, SPINOR_REG_MT_CFR0V, 1), - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(1, &buf, 1)); - ret = spi_mem_exec_op(nor->spi, &op); - if (ret) { - dev_err(nor->dev, "Failed to enable octal DTR mode\n"); - return ret; - } - - return 0; -} - -static void mt35xu512aba_late_init(struct spi_nor *nor, - struct spi_nor_flash_parameter *params) -{ - nor->octal_dtr_enable = spi_nor_micron_octal_dtr_enable; -} - -static void mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor, - struct spi_nor_flash_parameter *params) -{ - /* Set the Fast Read settings. */ - params->hwcaps.mask |= SNOR_HWCAPS_READ_8_8_8_DTR; - spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_8_8_8_DTR], - 0, 20, SPINOR_OP_MT_DTR_RD, - SNOR_PROTO_8_8_8_DTR); - - params->hwcaps.mask |= SNOR_HWCAPS_PP_8_8_8_DTR; - - nor->cmd_ext_type = SPI_NOR_EXT_REPEAT; - params->rdsr_dummy = 8; - params->rdsr_addr_nbytes = 0; - - /* - * SCCR Map 22nd DWORD does not indicate DTR Octal Mode Enable - * for MT35XU512ABA but is actually supported by device. - */ - nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE; - - /* - * The BFPT quad enable field is set to a reserved value so the quad - * enable function is ignored by spi_nor_parse_bfpt(). Make sure we - * disable it. - */ - params->quad_enable = NULL; -} - -static struct spi_nor_fixups mt35xu512aba_fixups = { - .post_sfdp = mt35xu512aba_post_sfdp_fixup, - .late_init = mt35xu512aba_late_init, -}; -#endif /* CONFIG_SPI_FLASH_MT35XU */ - -#if CONFIG_IS_ENABLED(SPI_FLASH_MACRONIX) -/** - * spi_nor_macronix_octal_dtr_enable() - Enable octal DTR on Macronix flashes. - * @nor: pointer to a 'struct spi_nor' - * - * Set Macronix max dummy cycles 20 to allow the flash to run at fastest frequency. - * - * Return: 0 on success, -errno otherwise. - */ -static int spi_nor_macronix_octal_dtr_enable(struct spi_nor *nor) -{ - struct spi_mem_op op; - int ret; - u8 buf; - - ret = write_enable(nor); - if (ret) - return ret; - - buf = SPINOR_REG_MXIC_DC_20; - op = (struct spi_mem_op) - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_CR2, 1), - SPI_MEM_OP_ADDR(4, SPINOR_REG_MXIC_CR2_DC, 1), - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(1, &buf, 1)); - - ret = spi_mem_exec_op(nor->spi, &op); - if (ret) - return ret; - - ret = spi_nor_wait_till_ready(nor); - if (ret) - return ret; - - nor->read_dummy = MXIC_MAX_DC; - ret = write_enable(nor); - if (ret) - return ret; - - buf = SPINOR_REG_MXIC_OPI_DTR_EN; - op = (struct spi_mem_op) - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_CR2, 1), - SPI_MEM_OP_ADDR(4, SPINOR_REG_MXIC_CR2_MODE, 1), - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(1, &buf, 1)); - - ret = spi_mem_exec_op(nor->spi, &op); - if (ret) { - dev_err(nor->dev, "Failed to enable octal DTR mode\n"); - return ret; - } - nor->reg_proto = SNOR_PROTO_8_8_8_DTR; - - return 0; -} - -static void macronix_octal_late_init(struct spi_nor *nor, - struct spi_nor_flash_parameter *params) -{ - nor->octal_dtr_enable = spi_nor_macronix_octal_dtr_enable; -} - -static void macronix_octal_post_sfdp_fixup(struct spi_nor *nor, - struct spi_nor_flash_parameter *params) -{ - /* - * Adding SNOR_HWCAPS_PP_8_8_8_DTR in hwcaps.mask when - * SPI_NOR_OCTAL_DTR_READ flag exists. - */ - if (params->hwcaps.mask & SNOR_HWCAPS_READ_8_8_8_DTR) - params->hwcaps.mask |= SNOR_HWCAPS_PP_8_8_8_DTR; -} - -static struct spi_nor_fixups macronix_octal_fixups = { - .post_sfdp = macronix_octal_post_sfdp_fixup, - .late_init = macronix_octal_late_init, -}; -#endif /* CONFIG_SPI_FLASH_MACRONIX */ - -#if CONFIG_IS_ENABLED(SPI_FLASH_WINBOND) - -#define WINBOND_NOR_OP_SELDIE 0xc2 /* Select active die */ - -struct winbond_nor_priv { - unsigned int n_dice; -}; - -/** - * winbond_nor_select_die() - Set active die. - * @nor: pointer to 'struct spi_nor'. - * @die: die to set active. - * - * Certain Winbond chips feature more than a single die. This is mostly hidden - * to the user, except that some chips may experience time deviation when - * modifying the status bits between dies, which in some corner cases may - * produce problematic races. Being able to explicitly select a die to check its - * state in this case may be useful. - * - * Return: 0 on success, -errno otherwise. - */ -static int winbond_nor_select_die(struct spi_nor *nor, u8 die) -{ - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(WINBOND_NOR_OP_SELDIE, 1), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(1, &die, 0)); - - int ret; - - spi_nor_setup_op(nor, &op, SNOR_PROTO_1_1_1); - ret = spi_mem_exec_op(nor->spi, &op); - if (ret) - debug("Error %d selecting die %d\n", ret, die); - - return ret; -} - -static int winbond_nor_multi_die_ready(struct spi_nor *nor) -{ - struct winbond_nor_priv *winbond_priv = nor->priv; - unsigned int n_dice = winbond_priv ? winbond_priv->n_dice : 1; - int ret, i; - - for (i = 0; i < n_dice; i++) { - ret = winbond_nor_select_die(nor, i); - if (ret) - return ret; - - ret = spi_nor_sr_ready(nor); - if (ret <= 0) - return ret; - } - - return 1; -} - -static void winbond_nor_multi_die_post_sfdp_fixups(struct spi_nor *nor, - struct spi_nor_flash_parameter *param) -{ - struct winbond_nor_priv *winbond_priv; - - winbond_priv = kmalloc(sizeof(*winbond_priv), GFP_KERNEL); - if (!winbond_priv) - return; - - winbond_priv->n_dice = param->size / SZ_64M; - - /* - * SFDP supports dice numbers, but this information is only available in - * optional additional tables which are not provided by these chips. - * Dice number has an impact though, because these devices need extra - * care when reading the busy bit. - */ - nor->priv = winbond_priv; - nor->ready = winbond_nor_multi_die_ready; -} - -static struct spi_nor_fixups winbond_nor_multi_die_fixups = { - .post_sfdp = winbond_nor_multi_die_post_sfdp_fixups, -}; -#endif /* CONFIG_SPI_FLASH_WINBOND */ - /** spi_nor_octal_dtr_enable() - enable Octal DTR I/O if needed * @nor: pointer to a 'struct spi_nor' * @@ -3644,63 +2790,6 @@ void spi_nor_set_fixups(struct spi_nor *nor) { if (nor->manufacturer->set_fixups) nor->manufacturer->set_fixups(nor); - -#ifdef CONFIG_SPI_FLASH_SPANSION - if (JEDEC_MFR(nor->info) == SNOR_MFR_CYPRESS) { - switch (nor->info->id[1]) { - case 0x2a: /* S25HL (QSPI, 3.3V) */ - case 0x2b: /* S25HS (QSPI, 1.8V) */ - nor->fixups = &s25_fixups; - break; - -#ifdef CONFIG_SPI_FLASH_S28HX_T - case 0x5a: /* S28HL (Octal, 3.3V) */ - case 0x5b: /* S28HS (Octal, 1.8V) */ - nor->fixups = &s28hx_t_fixups; - break; -#endif - - default: - break; - } - } - - if (CONFIG_IS_ENABLED(SPI_FLASH_BAR) && - !strcmp(nor->info->name, "s25fl256l")) - nor->fixups = &s25fl256l_fixups; - - /* For FS-S (family ID = 0x81) */ - if (JEDEC_MFR(nor->info) == SNOR_MFR_SPANSION && nor->info->id[5] == 0x81) - nor->fixups = &s25fs_s_fixups; -#endif - -#ifdef CONFIG_SPI_FLASH_MT35XU - if (!strcmp(nor->info->name, "mt35xu512aba")) - nor->fixups = &mt35xu512aba_fixups; -#endif - -#if CONFIG_IS_ENABLED(SPI_FLASH_MACRONIX) - if (JEDEC_MFR(nor->info) == SNOR_MFR_MACRONIX && - nor->info->flags & SPI_NOR_OCTAL_DTR_READ) - nor->fixups = ¯onix_octal_fixups; -#endif /* SPI_FLASH_MACRONIX */ - -#if CONFIG_IS_ENABLED(SPI_FLASH_WINBOND) - if (JEDEC_MFR(nor->info) == SNOR_MFR_WINBOND) { - u8 multi_die_models[][2] = { - { 0x40, 0x21 }, /* W25Q01JV */ - { 0x70, 0x22 }, /* W25Q02JV */ - }; - int i; - - for (i = 0; i < sizeof(multi_die_models) / 2; i++) { - if (!memcmp(nor->info->id + 1, multi_die_models[i], 2)) { - nor->fixups = &winbond_nor_multi_die_fixups; - break; - } - } - } -#endif /* SPI_FLASH_WINBOND */ } int spi_nor_scan(struct spi_nor *nor) diff --git a/drivers/mtd/spi/winbond.c b/drivers/mtd/spi/winbond.c index 02c29ccf78c..d1030959b2c 100644 --- a/drivers/mtd/spi/winbond.c +++ b/drivers/mtd/spi/winbond.c @@ -5,6 +5,7 @@ * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ */ +#include <linux/sizes.h> #include <linux/mtd/spi-nor.h> #include "sf_internal.h" @@ -140,7 +141,110 @@ static const struct flash_info winbond_nor_parts[] = { { INFO("w77q51nw", 0xef8a1a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, }; +#if !CONFIG_IS_ENABLED(SPI_FLASH_TINY) + +#define WINBOND_NOR_OP_SELDIE 0xc2 /* Select active die */ + +struct winbond_nor_priv { + unsigned int n_dice; +}; + +/** + * winbond_nor_select_die() - Set active die. + * @nor: pointer to 'struct spi_nor'. + * @die: die to set active. + * + * Certain Winbond chips feature more than a single die. This is mostly hidden + * to the user, except that some chips may experience time deviation when + * modifying the status bits between dies, which in some corner cases may + * produce problematic races. Being able to explicitly select a die to check its + * state in this case may be useful. + * + * Return: 0 on success, -errno otherwise. + */ +static int winbond_nor_select_die(struct spi_nor *nor, u8 die) +{ + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(WINBOND_NOR_OP_SELDIE, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(1, &die, 0)); + + int ret; + + spi_nor_setup_op(nor, &op, SNOR_PROTO_1_1_1); + ret = spi_mem_exec_op(nor->spi, &op); + if (ret) + debug("Error %d selecting die %d\n", ret, die); + + return ret; +} + +static int winbond_nor_multi_die_ready(struct spi_nor *nor) +{ + struct winbond_nor_priv *winbond_priv = nor->priv; + unsigned int n_dice = winbond_priv ? winbond_priv->n_dice : 1; + int ret, i; + + for (i = 0; i < n_dice; i++) { + ret = winbond_nor_select_die(nor, i); + if (ret) + return ret; + + ret = spi_nor_sr_ready(nor); + if (ret <= 0) + return ret; + } + + return 1; +} + +static void winbond_nor_multi_die_post_sfdp_fixups(struct spi_nor *nor, + struct spi_nor_flash_parameter *param) +{ + struct winbond_nor_priv *winbond_priv; + + winbond_priv = kmalloc(sizeof(*winbond_priv), GFP_KERNEL); + if (!winbond_priv) + return; + + winbond_priv->n_dice = param->size / SZ_64M; + + /* + * SFDP supports dice numbers, but this information is only available in + * optional additional tables which are not provided by these chips. + * Dice number has an impact though, because these devices need extra + * care when reading the busy bit. + */ + nor->priv = winbond_priv; + nor->ready = winbond_nor_multi_die_ready; +} + +static struct spi_nor_fixups winbond_nor_multi_die_fixups = { + .post_sfdp = winbond_nor_multi_die_post_sfdp_fixups, +}; + +static void winbond_set_fixups(struct spi_nor *nor) +{ + u8 multi_die_models[][2] = { + { 0x40, 0x21 }, /* W25Q01JV */ + { 0x70, 0x22 }, /* W25Q02JV */ + }; + int i; + + for (i = 0; i < sizeof(multi_die_models) / 2; i++) { + if (!memcmp(nor->info->id + 1, multi_die_models[i], 2)) { + nor->fixups = &winbond_nor_multi_die_fixups; + break; + } + } +} +#endif /* !CONFIG_IS_ENABLED(SPI_FLASH_TINY) */ + const struct spi_nor_manufacturer spi_nor_winbond = { .parts = winbond_nor_parts, .nparts = ARRAY_SIZE(winbond_nor_parts), +#if !CONFIG_IS_ENABLED(SPI_FLASH_TINY) + .set_fixups = winbond_set_fixups, +#endif }; -- 2.43.0

