diff options
| author | Miquel Raynal <miquel.raynal@bootlin.com> | 2021-12-31 13:29:26 +0100 |
|---|---|---|
| committer | Miquel Raynal <miquel.raynal@bootlin.com> | 2021-12-31 13:29:36 +0100 |
| commit | bee387131abe02f43da0ba784446ed4c0dd06dbb (patch) | |
| tree | 88b8bb19be7cef55371adb9173d08dd098ad2934 | |
| parent | 2dc6de1cd303bf9298eee4b4ad4bbe911e8a8a1d (diff) | |
| parent | 5f340402bbfc1ee75e7b62b98f6ad85e14ce587c (diff) | |
| download | linux-bee387131abe02f43da0ba784446ed4c0dd06dbb.tar.gz linux-bee387131abe02f43da0ba784446ed4c0dd06dbb.tar.bz2 linux-bee387131abe02f43da0ba784446ed4c0dd06dbb.zip | |
Merge tag 'spi-nor/for-5.17' into mtd/next
SPI NOR core changes:
- Add Pratyush as SPI NOR co-maintainer.
- Flash parameters initialization was done in a spaghetti way. Clean
flash parameters initialization.
- Rework the flash_info flags and clarify where one should be used.
- Initialize all flash parameters based on JESD216 SFDP where possible.
Flash parameters and settings that are SFDP discoverable should not be
duplicated via flash_info flags at flash declaration.
- Remove debugfs entries that duplicate sysfs entries.
SPI NOR manufacturer drivers changes:
- Use late_init() hook in various drivers to make it clear that those
flash parameters are either not declared in the JESD216 SFDP standard,
or the SFDP tables which define those flash parameters are not defined
by the flash.
- Fix mtd size for s3an flashes.
- Write 2 bytes when disabling Octal DTR mode: 1 byte long transactions are
not allowed in 8D-8D-8D mode.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
| -rw-r--r-- | MAINTAINERS | 2 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/atmel.c | 79 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/catalyst.c | 15 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/core.c | 556 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/core.h | 217 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/eon.c | 33 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/esmt.c | 15 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/everspin.c | 12 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/fujitsu.c | 3 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/gigadevice.c | 59 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/intel.c | 12 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/issi.c | 62 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/macronix.c | 107 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/micron-st.c | 234 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/otp.c | 2 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/sfdp.c | 20 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/spansion.c | 184 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/sst.c | 96 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/swp.c | 2 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/winbond.c | 168 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/xilinx.c | 21 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/xmc.c | 10 | ||||
| -rw-r--r-- | include/linux/mtd/spi-nor.h | 2 |
23 files changed, 1081 insertions, 830 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 7a2345ce8521..352a781409eb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17930,8 +17930,8 @@ F: drivers/pinctrl/spear/ SPI NOR SUBSYSTEM M: Tudor Ambarus <tudor.ambarus@microchip.com> +M: Pratyush Yadav <p.yadav@ti.com> R: Michael Walle <michael@walle.cc> -R: Pratyush Yadav <p.yadav@ti.com> L: linux-mtd@lists.infradead.org S: Maintained W: http://www.linux-mtd.infradead.org/ diff --git a/drivers/mtd/spi-nor/atmel.c b/drivers/mtd/spi-nor/atmel.c index 1fea5cab492c..d6d889ce8876 100644 --- a/drivers/mtd/spi-nor/atmel.c +++ b/drivers/mtd/spi-nor/atmel.c @@ -48,13 +48,13 @@ static const struct spi_nor_locking_ops atmel_at25fs_locking_ops = { .is_locked = atmel_at25fs_is_locked, }; -static void atmel_at25fs_default_init(struct spi_nor *nor) +static void atmel_at25fs_late_init(struct spi_nor *nor) { nor->params->locking_ops = &atmel_at25fs_locking_ops; } static const struct spi_nor_fixups atmel_at25fs_fixups = { - .default_init = atmel_at25fs_default_init, + .late_init = atmel_at25fs_late_init, }; /** @@ -146,50 +146,59 @@ static const struct spi_nor_locking_ops atmel_global_protection_ops = { .is_locked = atmel_is_global_protected, }; -static void atmel_global_protection_default_init(struct spi_nor *nor) +static void atmel_global_protection_late_init(struct spi_nor *nor) { nor->params->locking_ops = &atmel_global_protection_ops; } static const struct spi_nor_fixups atmel_global_protection_fixups = { - .default_init = atmel_global_protection_default_init, + .late_init = atmel_global_protection_late_init, }; static const struct flash_info atmel_parts[] = { /* Atmel -- some are (confusingly) marketed as "DataFlash" */ - { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K | SPI_NOR_HAS_LOCK) + { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4) + FLAGS(SPI_NOR_HAS_LOCK) + NO_SFDP_FLAGS(SECT_4K) .fixups = &atmel_at25fs_fixups }, - { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_HAS_LOCK) + { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8) + FLAGS(SPI_NOR_HAS_LOCK) + NO_SFDP_FLAGS(SECT_4K) .fixups = &atmel_at25fs_fixups }, - - { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, - SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) - .fixups = &atmel_global_protection_fixups }, - { "at25df321", INFO(0x1f4700, 0, 64 * 1024, 64, - SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) - .fixups = &atmel_global_protection_fixups }, - { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64, - SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) - .fixups = &atmel_global_protection_fixups }, - { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, - SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) - .fixups = &atmel_global_protection_fixups }, - - { "at25sl321", INFO(0x1f4216, 0, 64 * 1024, 64, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - - { "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8, SECT_4K) }, - { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, - SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) - .fixups = &atmel_global_protection_fixups }, - { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, - SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) - .fixups = &atmel_global_protection_fixups }, - { "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, - SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) - .fixups = &atmel_global_protection_fixups }, - - { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) }, + { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8) + FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) + NO_SFDP_FLAGS(SECT_4K) + .fixups = &atmel_global_protection_fixups }, + { "at25df321", INFO(0x1f4700, 0, 64 * 1024, 64) + FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) + NO_SFDP_FLAGS(SECT_4K) + .fixups = &atmel_global_protection_fixups }, + { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64) + FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) + NO_SFDP_FLAGS(SECT_4K) + .fixups = &atmel_global_protection_fixups }, + { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128) + FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) + NO_SFDP_FLAGS(SECT_4K) + .fixups = &atmel_global_protection_fixups }, + { "at25sl321", INFO(0x1f4216, 0, 64 * 1024, 64) + NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8) + NO_SFDP_FLAGS(SECT_4K) }, + { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16) + FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) + NO_SFDP_FLAGS(SECT_4K) + .fixups = &atmel_global_protection_fixups }, + { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32) + FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) + NO_SFDP_FLAGS(SECT_4K) + .fixups = &atmel_global_protection_fixups }, + { "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64) + FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) + NO_SFDP_FLAGS(SECT_4K) + .fixups = &atmel_global_protection_fixups }, + { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16) + NO_SFDP_FLAGS(SECT_4K) }, }; const struct spi_nor_manufacturer spi_nor_atmel = { diff --git a/drivers/mtd/spi-nor/catalyst.c b/drivers/mtd/spi-nor/catalyst.c index 011b83e99e95..ae4d67e01bb3 100644 --- a/drivers/mtd/spi-nor/catalyst.c +++ b/drivers/mtd/spi-nor/catalyst.c @@ -10,16 +10,11 @@ static const struct flash_info catalyst_parts[] = { /* Catalyst / On Semiconductor -- non-JEDEC */ - { "cat25c11", CAT25_INFO(16, 8, 16, 1, - SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, - { "cat25c03", CAT25_INFO(32, 8, 16, 2, - SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, - { "cat25c09", CAT25_INFO(128, 8, 32, 2, - SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, - { "cat25c17", CAT25_INFO(256, 8, 32, 2, - SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, - { "cat25128", CAT25_INFO(2048, 8, 64, 2, - SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "cat25c11", CAT25_INFO(16, 8, 16, 1) }, + { "cat25c03", CAT25_INFO(32, 8, 16, 2) }, + { "cat25c09", CAT25_INFO(128, 8, 32, 2) }, + { "cat25c17", CAT25_INFO(256, 8, 32, 2) }, + { "cat25128", CAT25_INFO(2048, 8, 64, 2) }, }; const struct spi_nor_manufacturer spi_nor_catalyst = { diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index cc08bd707378..04ea180118e3 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -1952,6 +1952,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, struct spi_nor *nor = mtd_to_spi_nor(mtd); size_t page_offset, page_remain, i; ssize_t ret; + u32 page_size = nor->params->page_size; dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len); @@ -1968,16 +1969,15 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, * calculated with an AND operation. On the other cases we * need to do a modulus operation (more expensive). */ - if (is_power_of_2(nor->page_size)) { - page_offset = addr & (nor->page_size - 1); + if (is_power_of_2(page_size)) { + page_offset = addr & (page_size - 1); } else { uint64_t aux = addr; - page_offset = do_div(aux, nor->page_size); + page_offset = do_div(aux, page_size); } /* the size of data remaining on the first page */ - page_remain = min_t(size_t, - nor->page_size - page_offset, len - i); + page_remain = min_t(size_t, page_size - page_offset, len - i); addr = spi_nor_convert_addr(nor, addr); @@ -2115,7 +2115,7 @@ static int spi_nor_spimem_check_op(struct spi_nor *nor, */ op->addr.nbytes = 4; if (!spi_mem_supports_op(nor->spimem, op)) { - if (nor->mtd.size > SZ_16M) + if (nor->params->size > SZ_16M) return -EOPNOTSUPP; /* If flash size <= 16MB, 3 address bytes are sufficient */ @@ -2141,7 +2141,7 @@ static int spi_nor_spimem_check_readop(struct spi_nor *nor, struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(read->opcode, 0), SPI_MEM_OP_ADDR(3, 0, 0), SPI_MEM_OP_DUMMY(1, 0), - SPI_MEM_OP_DATA_IN(1, NULL, 0)); + SPI_MEM_OP_DATA_IN(2, NULL, 0)); spi_nor_spimem_setup_op(nor, &op, read->proto); @@ -2167,7 +2167,7 @@ static int spi_nor_spimem_check_pp(struct spi_nor *nor, struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(pp->opcode, 0), SPI_MEM_OP_ADDR(3, 0, 0), SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(1, NULL, 0)); + SPI_MEM_OP_DATA_OUT(2, NULL, 0)); spi_nor_spimem_setup_op(nor, &op, pp->proto); @@ -2484,13 +2484,61 @@ static int spi_nor_default_setup(struct spi_nor *nor, return 0; } +static int spi_nor_set_addr_width(struct spi_nor *nor) +{ + if (nor->addr_width) { + /* already configured from SFDP */ + } else if (nor->read_proto == SNOR_PROTO_8_8_8_DTR) { + /* + * In 8D-8D-8D mode, one byte takes half a cycle to transfer. So + * in this protocol an odd address width cannot be used because + * then the address phase would only span a cycle and a half. + * Half a cycle would be left over. We would then have to start + * the dummy phase in the middle of a cycle and so too the data + * phase, and we will end the transaction with half a cycle left + * over. + * + * Force all 8D-8D-8D flashes to use an address width of 4 to + * avoid this situation. + */ + nor->addr_width = 4; + } else if (nor->info->addr_width) { + nor->addr_width = nor->info->addr_width; + } else { + nor->addr_width = 3; + } + + if (nor->addr_width == 3 && nor->params->size > 0x1000000) { + /* enable 4-byte addressing if the device exceeds 16MiB */ + nor->addr_width = 4; + } + + if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) { + dev_dbg(nor->dev, "address width is too large: %u\n", + nor->addr_width); + return -EINVAL; + } + + /* Set 4byte opcodes when possible. */ + if (nor->addr_width == 4 && nor->flags & SNOR_F_4B_OPCODES && + !(nor->flags & SNOR_F_HAS_4BAIT)) + spi_nor_set_4byte_opcodes(nor); + + return 0; +} + static int spi_nor_setup(struct spi_nor *nor, const struct spi_nor_hwcaps *hwcaps) { - if (!nor->params->setup) - return 0; + int ret; - return nor->params->setup(nor, hwcaps); + if (nor->params->setup) { + ret = nor->params->setup(nor, hwcaps); + if (ret) + return ret; + } + + return spi_nor_set_addr_width(nor); } /** @@ -2509,107 +2557,50 @@ static void spi_nor_manufacturer_init_params(struct spi_nor *nor) } /** - * spi_nor_sfdp_init_params() - Initialize the flash's parameters and settings - * based on JESD216 SFDP standard. - * @nor: pointer to a 'struct spi_nor'. - * - * The method has a roll-back mechanism: in case the SFDP parsing fails, the - * legacy flash parameters and settings will be restored. - */ -static void spi_nor_sfdp_init_params(struct spi_nor *nor) -{ - struct spi_nor_flash_parameter sfdp_params; - - memcpy(&sfdp_params, nor->params, sizeof(sfdp_params)); - - if (spi_nor_parse_sfdp(nor)) { - memcpy(nor->params, &sfdp_params, sizeof(*nor->params)); - nor->addr_width = 0; - nor->flags &= ~SNOR_F_4B_OPCODES; - } -} - -/** - * spi_nor_info_init_params() - Initialize the flash's parameters and settings - * based on nor->info data. + * spi_nor_no_sfdp_init_params() - Initialize the flash's parameters and + * settings based on nor->info->sfdp_flags. This method should be called only by + * flashes that do not define SFDP tables. If the flash supports SFDP but the + * information is wrong and the settings from this function can not be retrieved + * by parsing SFDP, one should instead use the fixup hooks and update the wrong + * bits. * @nor: pointer to a 'struct spi_nor'. */ -static void spi_nor_info_init_params(struct spi_nor *nor) +static void spi_nor_no_sfdp_init_params(struct spi_nor *nor) { struct spi_nor_flash_parameter *params = nor->params; struct spi_nor_erase_map *map = ¶ms->erase_map; - const struct flash_info *info = nor->info; - struct device_node *np = spi_nor_get_flash_node(nor); + const u8 no_sfdp_flags = nor->info->no_sfdp_flags; u8 i, erase_mask; - /* Initialize default flash parameters and settings. */ - params->quad_enable = spi_nor_sr2_bit1_quad_enable; - params->set_4byte_addr_mode = spansion_set_4byte_addr_mode; - params->setup = spi_nor_default_setup; - params->otp.org = &info->otp_org; - - /* Default to 16-bit Write Status (01h) Command */ - nor->flags |= SNOR_F_HAS_16BIT_SR; - - /* Set SPI NOR sizes. */ - params->writesize = 1; - params->size = (u64)info->sector_size * info->n_sectors; - params->page_size = info->page_size; - - if (!(info->flags & SPI_NOR_NO_FR)) { - /* Default to Fast Read for DT and non-DT platform devices. */ - params->hwcaps.mask |= SNOR_HWCAPS_READ_FAST; - - /* Mask out Fast Read if not requested at DT instantiation. */ - if (np && !of_property_read_bool(np, "m25p,fast-read")) - params->hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST; - } - - /* (Fast) Read settings. */ - params->hwcaps.mask |= SNOR_HWCAPS_READ; - spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ], - 0, 0, SPINOR_OP_READ, - SNOR_PROTO_1_1_1); - - if (params->hwcaps.mask & SNOR_HWCAPS_READ_FAST) - spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_FAST], - 0, 8, SPINOR_OP_READ_FAST, - SNOR_PROTO_1_1_1); - - if (info->flags & SPI_NOR_DUAL_READ) { + if (no_sfdp_flags & SPI_NOR_DUAL_READ) { params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2; 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); } - if (info->flags & SPI_NOR_QUAD_READ) { + if (no_sfdp_flags & SPI_NOR_QUAD_READ) { params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4; 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); } - if (info->flags & SPI_NOR_OCTAL_READ) { + if (no_sfdp_flags & SPI_NOR_OCTAL_READ) { params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_8; spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_1_1_8], 0, 8, SPINOR_OP_READ_1_1_8, SNOR_PROTO_1_1_8); } - if (info->flags & SPI_NOR_OCTAL_DTR_READ) { + if (no_sfdp_flags & SPI_NOR_OCTAL_DTR_READ) { 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_READ_FAST, SNOR_PROTO_8_8_8_DTR); } - /* Page Program settings. */ - params->hwcaps.mask |= SNOR_HWCAPS_PP; - spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP], - SPINOR_OP_PP, SNOR_PROTO_1_1_1); - - if (info->flags & SPI_NOR_OCTAL_DTR_PP) { + if (no_sfdp_flags & SPI_NOR_OCTAL_DTR_PP) { params->hwcaps.mask |= SNOR_HWCAPS_PP_8_8_8_DTR; /* * Since xSPI Page Program opcode is backward compatible with @@ -2625,52 +2616,111 @@ static void spi_nor_info_init_params(struct spi_nor *nor) */ erase_mask = 0; i = 0; - if (info->flags & SECT_4K_PMC) { + if (no_sfdp_flags & SECT_4K_PMC) { erase_mask |= BIT(i); spi_nor_set_erase_type(&map->erase_type[i], 4096u, SPINOR_OP_BE_4K_PMC); i++; - } else if (info->flags & SECT_4K) { + } else if (no_sfdp_flags & SECT_4K) { erase_mask |= BIT(i); spi_nor_set_erase_type(&map->erase_type[i], 4096u, SPINOR_OP_BE_4K); i++; } erase_mask |= BIT(i); - spi_nor_set_erase_type(&map->erase_type[i], info->sector_size, + spi_nor_set_erase_type(&map->erase_type[i], nor->info->sector_size, SPINOR_OP_SE); spi_nor_init_uniform_erase_map(map, erase_mask, params->size); } /** - * spi_nor_post_sfdp_fixups() - Updates the flash's parameters and settings - * after SFDP has been parsed (is also called for SPI NORs that do not - * support RDSFDP). + * spi_nor_init_flags() - Initialize NOR flags for settings that are not defined + * in the JESD216 SFDP standard, thus can not be retrieved when parsing SFDP. * @nor: pointer to a 'struct spi_nor' - * - * Typically used to tweak various parameters that could not be extracted by - * other means (i.e. when information provided by the SFDP/flash_info tables - * are incomplete or wrong). */ -static void spi_nor_post_sfdp_fixups(struct spi_nor *nor) +static void spi_nor_init_flags(struct spi_nor *nor) { - if (nor->manufacturer && nor->manufacturer->fixups && - nor->manufacturer->fixups->post_sfdp) - nor->manufacturer->fixups->post_sfdp(nor); + struct device_node *np = spi_nor_get_flash_node(nor); + const u16 flags = nor->info->flags; + + if (of_property_read_bool(np, "broken-flash-reset")) + nor->flags |= SNOR_F_BROKEN_RESET; - if (nor->info->fixups && nor->info->fixups->post_sfdp) - nor->info->fixups->post_sfdp(nor); + if (flags & SPI_NOR_SWP_IS_VOLATILE) + nor->flags |= SNOR_F_SWP_IS_VOLATILE; + + if (flags & SPI_NOR_HAS_LOCK) + nor->flags |= SNOR_F_HAS_LOCK; + + if (flags & SPI_NOR_HAS_TB) { + nor->flags |= SNOR_F_HAS_SR_TB; + if (flags & SPI_NOR_TB_SR_BIT6) + nor->flags |= SNOR_F_HAS_SR_TB_BIT6; + } + + if (flags & SPI_NOR_4BIT_BP) { + nor->flags |= SNOR_F_HAS_4BIT_BP; + if (flags & SPI_NOR_BP3_SR_BIT6) + nor->flags |= SNOR_F_HAS_SR_BP3_BIT6; + } + + if (flags & NO_CHIP_ERASE) + nor->flags |= SNOR_F_NO_OP_CHIP_ERASE; + + if (flags & USE_CLSR) + nor->flags |= SNOR_F_USE_CLSR; + + if (flags & USE_FSR) + nor->flags |= SNOR_F_USE_FSR; + + /* + * Make sure the XSR_RDY flag is set before calling + * spi_nor_wait_till_ready(). Xilinx S3AN share MFR + * with Atmel SPI NOR. + */ + if (flags & SPI_NOR_XSR_RDY) + nor->flags |= SNOR_F_READY_XSR_RDY; +} + +/** + * spi_nor_init_fixup_flags() - Initialize NOR flags for settings that can not + * be discovered by SFDP for this particular flash because the SFDP table that + * indicates this support is not defined in the flash. In case the table for + * this support is defined but has wrong values, one should instead use a + * post_sfdp() hook to set the SNOR_F equivalent flag. + * @nor: pointer to a 'struct spi_nor' + */ +static void spi_nor_init_fixup_flags(struct spi_nor *nor) +{ + const u8 fixup_flags = nor->info->fixup_flags; + + if (fixup_flags & SPI_NOR_4B_OPCODES) + nor->flags |= SNOR_F_4B_OPCODES; + + if (fixup_flags & SPI_NOR_IO_MODE_EN_VOLATILE) + nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE; } /** * spi_nor_late_init_params() - Late initialization of default flash parameters. * @nor: pointer to a 'struct spi_nor' * - * Used to set default flash parameters and settings when the ->default_init() - * hook or the SFDP parser let voids. + * Used to initialize flash parameters that are not declared in the JESD216 + * SFDP standard, or where SFDP tables are not defined at all. + * Will replace the spi_nor_manufacturer_init_params() method. */ static void spi_nor_late_init_params(struct spi_nor *nor) { + if (nor->manufacturer && nor->manufacturer->fixups && + nor->manufacturer->fixups->late_init) + nor->manufacturer->fixups->late_init(nor); + + if (nor->info->fixups && nor->info->fixups->late_init) + nor->info->fixups->late_init(nor); + + spi_nor_init_flags(nor); + spi_nor_init_fixup_flags(nor); + /* * NOR protection support. When locking_ops are not provided, we pick * the default ones. @@ -2680,6 +2730,99 @@ static void spi_nor_late_init_params(struct spi_nor *nor) } /** + * spi_nor_sfdp_init_params_deprecated() - Deprecated way of initializing flash + * parameters and settings based on JESD216 SFDP standard. + * @nor: pointer to a 'struct spi_nor'. + * + * The method has a roll-back mechanism: in case the SFDP parsing fails, the + * legacy flash parameters and settings will be restored. + */ +static void spi_nor_sfdp_init_params_deprecated(struct spi_nor *nor) +{ + struct spi_nor_flash_parameter sfdp_params; + + memcpy(&sfdp_params, nor->params, sizeof(sfdp_params)); + + if (spi_nor_parse_sfdp(nor)) { + memcpy(nor->params, &sfdp_params, sizeof(*nor->params)); + nor->addr_width = 0; + nor->flags &= ~SNOR_F_4B_OPCODES; + } +} + +/** + * spi_nor_init_params_deprecated() - Deprecated way of initializing flash + * parameters and settings. + * @nor: pointer to a 'struct spi_nor'. + * + * The method assumes that flash doesn't support SFDP so it initializes flash + * parameters in spi_nor_no_sfdp_init_params() which later on can be overwritten + * when parsing SFDP, if supported. + */ +static void spi_nor_init_params_deprecated(struct spi_nor *nor) +{ + spi_nor_no_sfdp_init_params(nor); + + spi_nor_manufacturer_init_params(nor); + + if (nor->info->no_sfdp_flags & (SPI_NOR_DUAL_READ | + SPI_NOR_QUAD_READ | + SPI_NOR_OCTAL_READ | + SPI_NOR_OCTAL_DTR_READ)) + spi_nor_sfdp_init_params_deprecated(nor); +} + +/** + * spi_nor_init_default_params() - Default initialization of flash parameters + * and settings. Done for all flashes, regardless is they define SFDP tables + * or not. + * @nor: pointer to a 'struct spi_nor'. + */ +static void spi_nor_init_default_params(struct spi_nor *nor) +{ + struct spi_nor_flash_parameter *params = nor->params; + const struct flash_info *info = nor->info; + struct device_node *np = spi_nor_get_flash_node(nor); + + params->quad_enable = spi_nor_sr2_bit1_quad_enable; + params->set_4byte_addr_mode = spansion_set_4byte_addr_mode; + params->setup = spi_nor_default_setup; + params->otp.org = &info->otp_org; + + /* Default to 16-bit Write Status (01h) Command */ + nor->flags |= SNOR_F_HAS_16BIT_SR; + + /* Set SPI NOR sizes. */ + params->writesize = 1; + params->size = (u64)info->sector_size * info->n_sectors; + params->page_size = info->page_size; + + if (!(info->flags & SPI_NOR_NO_FR)) { + /* Default to Fast Read for DT and non-DT platform devices. */ + params->hwcaps.mask |= SNOR_HWCAPS_READ_FAST; + + /* Mask out Fast Read if not requested at DT instantiation. */ + if (np && !of_property_read_bool(np, "m25p,fast-read")) + params->hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST; + } + + /* (Fast) Read settings. */ + params->hwcaps.mask |= SNOR_HWCAPS_READ; + spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ], + 0, 0, SPINOR_OP_READ, + SNOR_PROTO_1_1_1); + + if (params->hwcaps.mask & SNOR_HWCAPS_READ_FAST) + spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_FAST], + 0, 8, SPINOR_OP_READ_FAST, + SNOR_PROTO_1_1_1); + /* Page Program settings. */ + params->hwcaps.mask |= SNOR_HWCAPS_PP; + spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP], + SPINOR_OP_PP, SNOR_PROTO_1_1_1); +} + +/** * spi_nor_init_params() - Initialize the flash's parameters and settings. * @nor: pointer to a 'struct spi_nor'. * @@ -2699,39 +2842,44 @@ static void spi_nor_late_init_params(struct spi_nor *nor) * which can be overwritten by: * 3/ SFDP flash parameters initialization. JESD216 SFDP is a standard and * should be more accurate that the above. - * spi_nor_sfdp_init_params() + * spi_nor_parse_sfdp() or spi_nor_no_sfdp_init_params() * * Please note that there is a ->post_bfpt() fixup hook that can overwrite * the flash parameters and settings immediately after parsing the Basic * Flash Parameter Table. + * spi_nor_post_sfdp_fixups() is called after the SFDP tables are parsed. + * It is used to tweak various flash parameters when information provided + * by the SFDP tables are wrong. * * which can be overwritten by: - * 4/ Post SFDP flash parameters initialization. Used to tweak various - * parameters that could not be extracted by other means (i.e. when - * information provided by the SFDP/flash_info tables are incomplete or - * wrong). - * spi_nor_post_sfdp_fixups() - * - * 5/ Late default flash parameters initialization, used when the - * ->default_init() hook or the SFDP parser do not set specific params. + * 4/ Late flash parameters initialization, used to initialize flash + * parameters that are not declared in the JESD216 SFDP standard, or where SFDP + * tables are not defined at all. * spi_nor_late_init_params() + * + * Return: 0 on success, -errno otherwise. */ static int spi_nor_init_params(struct spi_nor *nor) { + int ret; + nor->params = devm_kzalloc(nor->dev, sizeof(*nor->params), GFP_KERNEL); if (!nor->params) return -ENOMEM; - spi_nor_info_init_params(nor); - - spi_nor_manufacturer_init_params(nor); + spi_nor_init_default_params(nor); - if ((nor->info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_OCTAL_READ | SPI_NOR_OCTAL_DTR_READ)) && - !(nor->info->flags & SPI_NOR_SKIP_SFDP)) - spi_nor_sfdp_init_params(nor); - - spi_nor_post_sfdp_fixups(nor); + if (nor->info->parse_sfdp) { + ret = spi_nor_parse_sfdp(nor); + if (ret) { + dev_err(nor->dev, "BFPT parsing failed. Please consider using SPI_NOR_SKIP_SFDP when declaring the flash\n"); + return ret; + } + } else if (nor->info->no_sfdp_flags & SPI_NOR_SKIP_SFDP) { + spi_nor_no_sfdp_init_params(nor); + } else { + spi_nor_init_params_deprecated(nor); + } spi_nor_late_init_params(nor); @@ -2978,59 +3126,6 @@ static const struct flash_info *spi_nor_match_id(struct spi_nor *nor, return NULL; } -static int spi_nor_set_addr_width(struct spi_nor *nor) -{ - if (nor->addr_width) { - /* already configured from SFDP */ - } else if (nor->read_proto == SNOR_PROTO_8_8_8_DTR) { - /* - * In 8D-8D-8D mode, one byte takes half a cycle to transfer. So - * in this protocol an odd address width cannot be used because - * then the address phase would only span a cycle and a half. - * Half a cycle would be left over. We would then have to start - * the dummy phase in the middle of a cycle and so too the data - * phase, and we will end the transaction with half a cycle left - * over. - * - * Force all 8D-8D-8D flashes to use an address width of 4 to - * avoid this situation. - */ - nor->addr_width = 4; - } else if (nor->info->addr_width) { - nor->addr_width = nor->info->addr_width; - } else { - nor->addr_width = 3; - } - - if (nor->addr_width == 3 && nor->mtd.size > 0x1000000) { - /* enable 4-byte addressing if the device exceeds 16MiB */ - nor->addr_width = 4; - } - - if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) { - dev_dbg(nor->dev, "address width is too large: %u\n", - nor->addr_width); - return -EINVAL; - } - - /* Set 4byte opcodes when possible. */ - if (nor->addr_width == 4 && nor->flags & SNOR_F_4B_OPCODES && - !(nor->flags & SNOR_F_HAS_4BAIT)) - spi_nor_set_4byte_opcodes(nor); - - return 0; -} - -static void spi_nor_debugfs_init(struct spi_nor *nor, - const struct flash_info *info) -{ - struct mtd_info *mtd = &nor->mtd; - - mtd->dbg.partname = info->name; - mtd->dbg.partid = devm_kasprintf(nor->dev, GFP_KERNEL, "spi-nor:%*phN", - info->id_len, info->id); -} - static const struct flash_info *spi_nor_get_flash_info(struct spi_nor *nor, const char *name) { @@ -3071,13 +3166,41 @@ static const struct flash_info *spi_nor_get_flash_info(struct spi_nor *nor, return info; } +static void spi_nor_set_mtd_info(struct spi_nor *nor) +{ + struct mtd_info *mtd = &nor->mtd; + struct device *dev = nor->dev; + + spi_nor_set_mtd_locking_ops(nor); + spi_nor_set_mtd_otp_ops(nor); + + mtd->dev.parent = dev; + if (!mtd->name) + mtd->name = dev_name(dev); + mtd->type = MTD_NORFLASH; + mtd->flags = MTD_CAP_NORFLASH; + if (nor->info->flags & SPI_NOR_NO_ERASE) + mtd->flags |= MTD_NO_ERASE; + mtd->writesize = nor->params->writesize; + mtd->writebufsize = nor->params->page_size; + mtd->size = nor->params->size; + mtd->_erase = spi_nor_erase; + mtd->_read = spi_nor_read; + /* Might be already set by some SST flashes. */ + if (!mtd->_write) + mtd->_write = spi_nor_write; + mtd->_suspend = spi_nor_suspend; + mtd->_resume = spi_nor_resume; + mtd->_get_device = spi_nor_get_device; + mtd->_put_device = spi_nor_put_device; +} + int spi_nor_scan(struct spi_nor *nor, const char *name, const struct spi_nor_hwcaps *hwcaps) { const struct flash_info *info; struct device *dev = nor->dev; struct mtd_info *mtd = &nor->mtd; - struct device_node *np = spi_nor_get_flash_node(nor); int ret; int i; @@ -3094,7 +3217,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, * We need the bounce buffer early to read/write registers when going * |
