diff options
author | Richard Weinberger <richard@nod.at> | 2021-02-10 10:21:34 +0100 |
---|---|---|
committer | Richard Weinberger <richard@nod.at> | 2021-02-10 10:21:34 +0100 |
commit | b491f90ddff013e0be058297a8f704c59932c1fd (patch) | |
tree | 9164f81732681844c6a338a7b33971a17e910d5d /drivers/mtd | |
parent | Merge tag 'nand/for-5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/mt... (diff) | |
parent | mtd: spi-nor: sst: Add support for Global Unlock on sst26vf (diff) | |
download | linux-b491f90ddff013e0be058297a8f704c59932c1fd.tar.xz linux-b491f90ddff013e0be058297a8f704c59932c1fd.zip |
Merge tag 'spi-nor/for-5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux into mtd/next
SPI NOR core changes:
- Add non-uniform erase fixes.
- Add Global Block Unlock command. It is defined by few flash
vendors, and it is used for now just by sst.
SPI NOR controller drivers changes:
- intel-spi: Add support for Intel Alder Lake-P SPI serial flash.
- hisi-sfc: Put child node np on error path.
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/spi-nor/controllers/hisi-sfc.c | 4 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/controllers/intel-spi-pci.c | 1 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/core.c | 49 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/core.h | 2 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/sfdp.c | 5 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/sst.c | 52 |
6 files changed, 102 insertions, 11 deletions
diff --git a/drivers/mtd/spi-nor/controllers/hisi-sfc.c b/drivers/mtd/spi-nor/controllers/hisi-sfc.c index 7c26f8f565cb..47fbf1d1e557 100644 --- a/drivers/mtd/spi-nor/controllers/hisi-sfc.c +++ b/drivers/mtd/spi-nor/controllers/hisi-sfc.c @@ -399,8 +399,10 @@ static int hisi_spi_nor_register_all(struct hifmc_host *host) for_each_available_child_of_node(dev->of_node, np) { ret = hisi_spi_nor_register(np, host); - if (ret) + if (ret) { + of_node_put(np); goto fail; + } if (host->num_chip == HIFMC_MAX_CHIP_NUM) { dev_warn(dev, "Flash device number exceeds the maximum chipselect number\n"); diff --git a/drivers/mtd/spi-nor/controllers/intel-spi-pci.c b/drivers/mtd/spi-nor/controllers/intel-spi-pci.c index 555fe55d14ae..825610a2e9dc 100644 --- a/drivers/mtd/spi-nor/controllers/intel-spi-pci.c +++ b/drivers/mtd/spi-nor/controllers/intel-spi-pci.c @@ -73,6 +73,7 @@ static const struct pci_device_id intel_spi_pci_ids[] = { { PCI_VDEVICE(INTEL, 0x43a4), (unsigned long)&cnl_info }, { PCI_VDEVICE(INTEL, 0x4b24), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0x4da4), (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x51a4), (unsigned long)&cnl_info }, { PCI_VDEVICE(INTEL, 0x7aa4), (unsigned long)&cnl_info }, { PCI_VDEVICE(INTEL, 0xa0a4), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0xa1a4), (unsigned long)&bxt_info }, diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 20df44b753da..0522304f52fa 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -465,7 +465,7 @@ static int spi_nor_read_fsr(struct spi_nor *nor, u8 *fsr) * * Return: 0 on success, -errno otherwise. */ -static int spi_nor_read_cr(struct spi_nor *nor, u8 *cr) +int spi_nor_read_cr(struct spi_nor *nor, u8 *cr) { int ret; @@ -854,6 +854,43 @@ int spi_nor_wait_till_ready(struct spi_nor *nor) } /** + * spi_nor_global_block_unlock() - Unlock Global Block Protection. + * @nor: pointer to 'struct spi_nor'. + * + * Return: 0 on success, -errno otherwise. + */ +int spi_nor_global_block_unlock(struct spi_nor *nor) +{ + int ret; + + ret = spi_nor_write_enable(nor); + if (ret) + return ret; + + if (nor->spimem) { + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_GBULK, 0), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_NO_DATA); + + 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_GBULK, + NULL, 0); + } + + if (ret) { + dev_dbg(nor->dev, "error %d on Global Block Unlock\n", ret); + return ret; + } + + return spi_nor_wait_till_ready(nor); +} + +/** * spi_nor_write_sr() - Write the Status Register. * @nor: pointer to 'struct spi_nor'. * @sr: pointer to DMA-able buffer to write to the Status Register. @@ -1364,14 +1401,15 @@ spi_nor_find_best_erase_type(const struct spi_nor_erase_map *map, erase = &map->erase_type[i]; + /* Alignment is not mandatory for overlaid regions */ + if (region->offset & SNOR_OVERLAID_REGION && + region->size <= len) + return erase; + /* Don't erase more than what the user has asked for. */ if (erase->size > len) continue; - /* Alignment is not mandatory for overlaid regions */ - if (region->offset & SNOR_OVERLAID_REGION) - return erase; - spi_nor_div_by_erase_size(erase, addr, &rem); if (rem) continue; @@ -1515,6 +1553,7 @@ static int spi_nor_init_erase_cmd_list(struct spi_nor *nor, goto destroy_erase_cmd_list; if (prev_erase != erase || + erase->size != cmd->size || region->offset & SNOR_OVERLAID_REGION) { cmd = spi_nor_init_erase_cmd(region, erase); if (IS_ERR(cmd)) { diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index d631ee299de3..4a3f7f150b5d 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -434,12 +434,14 @@ int spi_nor_write_disable(struct spi_nor *nor); int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable); int spi_nor_write_ear(struct spi_nor *nor, u8 ear); int spi_nor_wait_till_ready(struct spi_nor *nor); +int spi_nor_global_block_unlock(struct spi_nor *nor); int spi_nor_lock_and_prep(struct spi_nor *nor); void spi_nor_unlock_and_unprep(struct spi_nor *nor); int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor); int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor); int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor); int spi_nor_read_sr(struct spi_nor *nor, u8 *sr); +int spi_nor_read_cr(struct spi_nor *nor, u8 *cr); int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len); int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 sr1); diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index 6ee7719e5903..25142ec4737b 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -788,7 +788,7 @@ spi_nor_region_check_overlay(struct spi_nor_erase_region *region, int i; for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) { - if (!(erase_type & BIT(i))) + if (!(erase[i].size && erase_type & BIT(erase[i].idx))) continue; if (region->size & erase[i].size_mask) { spi_nor_region_mark_overlay(region); @@ -858,6 +858,7 @@ spi_nor_init_non_uniform_erase_map(struct spi_nor *nor, offset = (region[i].offset & ~SNOR_ERASE_FLAGS_MASK) + region[i].size; } + spi_nor_region_mark_end(®ion[i - 1]); save_uniform_erase_type = map->uniform_erase_type; map->uniform_erase_type = spi_nor_sort_erase_mask(map, @@ -881,8 +882,6 @@ spi_nor_init_non_uniform_erase_map(struct spi_nor *nor, if (!(regions_erase_type & BIT(erase[i].idx))) spi_nor_set_erase_type(&erase[i], 0, 0xFF); - spi_nor_region_mark_end(®ion[i - 1]); - return 0; } diff --git a/drivers/mtd/spi-nor/sst.c b/drivers/mtd/spi-nor/sst.c index 00e48da0744a..980f4c09c91d 100644 --- a/drivers/mtd/spi-nor/sst.c +++ b/drivers/mtd/spi-nor/sst.c @@ -8,6 +8,53 @@ #include "core.h" +#define SST26VF_CR_BPNV BIT(3) + +static int sst26vf_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) +{ + return -EOPNOTSUPP; +} + +static int sst26vf_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) +{ + int ret; + + /* We only support unlocking the entire flash array. */ + if (ofs != 0 || len != nor->params->size) + return -EINVAL; + + ret = spi_nor_read_cr(nor, nor->bouncebuf); + if (ret) + return ret; + + if (!(nor->bouncebuf[0] & SST26VF_CR_BPNV)) { + dev_dbg(nor->dev, "Any block has been permanently locked\n"); + return -EINVAL; + } + + return spi_nor_global_block_unlock(nor); +} + +static int sst26vf_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) +{ + return -EOPNOTSUPP; +} + +static const struct spi_nor_locking_ops sst26vf_locking_ops = { + .lock = sst26vf_lock, + .unlock = sst26vf_unlock, + .is_locked = sst26vf_is_locked, +}; + +static void sst26vf_default_init(struct spi_nor *nor) +{ + nor->params->locking_ops = &sst26vf_locking_ops; +} + +static const struct spi_nor_fixups sst26vf_fixups = { + .default_init = sst26vf_default_init, +}; + static const struct flash_info sst_parts[] = { /* SST -- large erase sizes are "overlays", "sectors" are 4K */ { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, @@ -39,8 +86,9 @@ static const struct flash_info sst_parts[] = { { "sst26vf016b", INFO(0xbf2641, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_DUAL_READ) }, { "sst26vf064b", INFO(0xbf2643, 0, 64 * 1024, 128, - SECT_4K | SPI_NOR_DUAL_READ | - SPI_NOR_QUAD_READ) }, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) + .fixups = &sst26vf_fixups }, }; static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, |