diff options
Diffstat (limited to 'drivers/mtd')
73 files changed, 2150 insertions, 1389 deletions
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 6049ba2d6bcb..e5805eebeed6 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -1716,12 +1716,24 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le } +/* + * Keep noinline: inlined, the map_word temporaries put do_write_buffer() over + * the frame-size limit with MTD_MAP_BANK_WIDTH_32 and KASAN_STACK. + */ +static noinline void __xipram cfi_write_cmd(struct map_info *map, + unsigned long cmd, unsigned long adr) +{ + struct cfi_private *cfi = map->fldrv_priv; + + map_write(map, CMD(cmd), adr); +} + static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, unsigned long adr, const struct kvec **pvec, unsigned long *pvec_seek, int len) { struct cfi_private *cfi = map->fldrv_priv; - map_word status, write_cmd, datum; + map_word status, datum; unsigned long cmd_adr; int ret, wbufsize, word_gap, words; const struct kvec *vec; @@ -1740,9 +1752,6 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, if (is_LH28F640BF(cfi)) cmd_adr = adr; - /* Let's determine this according to the interleave only once */ - write_cmd = (cfi->cfiq->P_ID != P_ID_INTEL_PERFORMANCE) ? CMD(0xe8) : CMD(0xe9); - mutex_lock(&chip->mutex); ret = get_chip(map, chip, cmd_adr, FL_WRITING); if (ret) { @@ -1759,7 +1768,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, So we must check here and reset those bits if they're set. Otherwise we're just pissing in the wind */ if (chip->state != FL_STATUS) { - map_write(map, CMD(0x70), cmd_adr); + cfi_write_cmd(map, 0x70, cmd_adr); chip->state = FL_STATUS; } status = map_read(map, cmd_adr); @@ -1767,21 +1776,22 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, xip_enable(map, chip, cmd_adr); printk(KERN_WARNING "SR.4 or SR.5 bits set in buffer write (status %lx). Clearing.\n", status.x[0]); xip_disable(map, chip, cmd_adr); - map_write(map, CMD(0x50), cmd_adr); - map_write(map, CMD(0x70), cmd_adr); + cfi_write_cmd(map, 0x50, cmd_adr); + cfi_write_cmd(map, 0x70, cmd_adr); } chip->state = FL_WRITING_TO_BUFFER; - map_write(map, write_cmd, cmd_adr); + cfi_write_cmd(map, (cfi->cfiq->P_ID != P_ID_INTEL_PERFORMANCE) ? 0xe8 : 0xe9, + cmd_adr); ret = WAIT_TIMEOUT(map, chip, cmd_adr, 0, 0); if (ret) { /* Argh. Not ready for write to buffer */ map_word Xstatus = map_read(map, cmd_adr); - map_write(map, CMD(0x70), cmd_adr); + cfi_write_cmd(map, 0x70, cmd_adr); chip->state = FL_STATUS; status = map_read(map, cmd_adr); - map_write(map, CMD(0x50), cmd_adr); - map_write(map, CMD(0x70), cmd_adr); + cfi_write_cmd(map, 0x50, cmd_adr); + cfi_write_cmd(map, 0x70, cmd_adr); xip_enable(map, chip, cmd_adr); printk(KERN_ERR "%s: Chip not ready for buffer write. Xstatus = %lx, status = %lx\n", map->name, Xstatus.x[0], status.x[0]); @@ -1800,7 +1810,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, } /* Write length of data to come */ - map_write(map, CMD(words), cmd_adr ); + cfi_write_cmd(map, words, cmd_adr); /* Write data */ vec = *pvec; @@ -1837,7 +1847,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, *pvec_seek = vec_seek; /* GO GO GO */ - map_write(map, CMD(0xd0), cmd_adr); + cfi_write_cmd(map, 0xd0, cmd_adr); chip->state = FL_WRITING; ret = INVAL_CACHE_AND_WAIT(map, chip, cmd_adr, @@ -1845,7 +1855,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, chip->buffer_write_time, chip->buffer_write_time_max); if (ret) { - map_write(map, CMD(0x70), cmd_adr); + cfi_write_cmd(map, 0x70, cmd_adr); chip->state = FL_STATUS; xip_enable(map, chip, cmd_adr); printk(KERN_ERR "%s: buffer write error (status timeout)\n", map->name); @@ -1858,8 +1868,8 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, unsigned long chipstatus = MERGESTATUS(status); /* reset status */ - map_write(map, CMD(0x50), cmd_adr); - map_write(map, CMD(0x70), cmd_adr); + cfi_write_cmd(map, 0x50, cmd_adr); + cfi_write_cmd(map, 0x70, cmd_adr); xip_enable(map, chip, cmd_adr); if (chipstatus & 0x02) { @@ -2510,10 +2520,7 @@ static void cfi_intelext_save_locks(struct mtd_info *mtd) status = cfi_varsize_frob(mtd, do_getlockstatus_oneblock, adr, len, NULL); - if (status) - set_bit(block, region->lockmap); - else - clear_bit(block, region->lockmap); + assign_bit(block, region->lockmap, status); } } } diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 517db2f2707f..100cab95a17b 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -283,6 +283,21 @@ static void fixup_use_write_buffers(struct mtd_info *mtd) pr_debug("Using buffer write method\n"); mtd->_write = cfi_amdstd_write_buffers; } + + /* + * The word count of the Write to Buffer command is a single bus + * word per device, so an x8 device can be told to program at most + * 256 bytes however large a buffer it advertises - including when + * several of them are interleaved on a wider bus, where CMD() + * replicates the count into each device's lane and it is truncated + * there. + */ + if (cfi->device_type == CFI_DEVICETYPE_X8 && + cfi->cfiq->MaxBufWriteSize > 8) { + cfi->cfiq->MaxBufWriteSize = 8; + mtd->writebufsize = cfi_interleave(cfi) << + cfi->cfiq->MaxBufWriteSize; + } } #endif /* !FORCE_WORD_WRITE */ diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index 03e80b2c4f5a..349fa07be314 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -293,7 +293,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size, } size = bdev_nr_bytes(bdev); - if ((long)size % erase_size) { + if (!erase_size || (long)size % erase_size) { pr_err("erasesize must be a divisor of device size\n"); goto err_free_block2mtd; } diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 603fd0efc2ea..3d89f810e2db 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -1884,7 +1884,7 @@ static void doc_release_device(struct mtd_info *mtd) /** * docg3_resume - Awakens docg3 floor - * @pdev: platfrom device + * @pdev: platform device * * Returns 0 (always successful) */ @@ -1963,7 +1963,7 @@ static int docg3_suspend(struct platform_device *pdev, pm_message_t state) * @pdev: platform device * * Probes for a G3 chip at the specified IO space in the platform data - * ressources. The floor 0 must be available. + * resources. The floor 0 must be available. * * Returns 0 on success, -ENOMEM, -ENXIO on error */ @@ -1971,25 +1971,15 @@ static int __init docg3_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct mtd_info *mtd; - struct resource *ress; void __iomem *base; int ret, floor; struct docg3_cascade *cascade; - ret = -ENXIO; - ress = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!ress) { - dev_err(dev, "No I/O memory resource defined\n"); - return ret; - } + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); ret = -ENOMEM; - base = devm_ioremap(dev, ress->start, DOC_IOSPACE_SIZE); - if (!base) { - dev_err(dev, "devm_ioremap dev failed\n"); - return ret; - } - cascade = devm_kcalloc(dev, DOC_MAX_NBFLOORS, sizeof(*cascade), GFP_KERNEL); if (!cascade) diff --git a/drivers/mtd/devices/mtd_intel_dg.c b/drivers/mtd/devices/mtd_intel_dg.c index a566e86eb5e3..55a945c1a67a 100644 --- a/drivers/mtd/devices/mtd_intel_dg.c +++ b/drivers/mtd/devices/mtd_intel_dg.c @@ -387,7 +387,7 @@ idg_erase(struct intel_dg_nvm *nvm, u8 region, loff_t from, u64 len, u64 *fail_a void __iomem *base2 = nvm->base2; void __iomem *base = nvm->base; const u32 block = 0x10; - u32 iter = 0; + u32 iter; u32 reg; u64 i; @@ -396,6 +396,7 @@ idg_erase(struct intel_dg_nvm *nvm, u8 region, loff_t from, u64 len, u64 *fail_a iowrite32(region << 24 | block, base + NVM_ERASE_REG); if (nvm->non_posted_erase) { /* Wait for Erase Done */ + iter = 0; reg = ioread32(base2 + NVM_DEBUG_REG); while (!(reg & NVM_NON_POSTED_ERASE_DONE) && ++iter < NVM_NON_POSTED_ERASE_DONE_ITER) { diff --git a/drivers/mtd/devices/powernv_flash.c b/drivers/mtd/devices/powernv_flash.c index a12427d9e20f..fe1d6684d0bd 100644 --- a/drivers/mtd/devices/powernv_flash.c +++ b/drivers/mtd/devices/powernv_flash.c @@ -263,7 +263,7 @@ static int powernv_flash_probe(struct platform_device *pdev) } /** - * op_release - Release the driver + * powernv_flash_release - Release the driver * @pdev: the platform device * * Returns 0 diff --git a/drivers/mtd/hyperbus/hbmc-am654.c b/drivers/mtd/hyperbus/hbmc-am654.c index 9d31464046b2..8a14e908d0b5 100644 --- a/drivers/mtd/hyperbus/hbmc-am654.c +++ b/drivers/mtd/hyperbus/hbmc-am654.c @@ -78,14 +78,15 @@ static int am654_hbmc_dma_read(struct am654_hbmc_device_priv *priv, void *to, struct dma_chan *rx_chan = priv->rx_chan; struct dma_async_tx_descriptor *tx; dma_addr_t dma_dst, dma_src; + struct device *rx_dev = dmaengine_get_dma_device(rx_chan); dma_cookie_t cookie; int ret; if (!priv->rx_chan || !virt_addr_valid(to) || object_is_on_stack(to)) return -EINVAL; - dma_dst = dma_map_single(rx_chan->device->dev, to, len, DMA_FROM_DEVICE); - if (dma_mapping_error(rx_chan->device->dev, dma_dst)) { + dma_dst = dma_map_single(rx_dev, to, len, DMA_FROM_DEVICE); + if (dma_mapping_error(rx_dev, dma_dst)) { dev_dbg(priv->ctlr->dev, "DMA mapping failed\n"); return -EIO; } @@ -117,7 +118,7 @@ static int am654_hbmc_dma_read(struct am654_hbmc_device_priv *priv, void *to, } unmap_dma: - dma_unmap_single(rx_chan->device->dev, dma_dst, len, DMA_FROM_DEVICE); + dma_unmap_single(rx_dev, dma_dst, len, DMA_FROM_DEVICE); return ret; } diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 1bb3dba2631d..649fd145e15d 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -161,6 +161,23 @@ config MTD_AMD76XROM BE VERY CAREFUL. +config MTD_INT0800 + tristate "Read-only BIOS/firmware flash via ACPI INT0800" + depends on X86 && ACPI + select MTD_ROM + help + Support for reading the system firmware (BIOS/EFI) flash chip + through the memory window described by the ACPI INT0800 + "82802 firmware hub" device, present on most x86 machines. + + The flash is exposed read-only via the ROM chip driver, e.g. + for firmware analysis or extracting option ROMs (such as + video BIOS images embedded in EFI firmware volumes). There is + no write or erase support. + + To compile this driver as a module, choose M here: the + module will be called int0800. + config MTD_ICHXROM tristate "BIOS flash chip on Intel Controller Hub 2/3/4/5" depends on X86 && MTD_JEDECPROBE diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 01745eca1f73..2f1a7375e13c 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_MTD_L440GX) += l440gx.o obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o obj-$(CONFIG_MTD_ESB2ROM) += esb2rom.o obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o +obj-$(CONFIG_MTD_INT0800) += int0800.o obj-$(CONFIG_MTD_CK804XROM) += ck804xrom.o obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c index 457e6ab7f3fb..0c3e0eb08b8d 100644 --- a/drivers/mtd/maps/amd76xrom.c +++ b/drivers/mtd/maps/amd76xrom.c @@ -152,6 +152,8 @@ static int amd76xrom_init_one(struct pci_dev *pdev, printk(KERN_ERR MOD_NAME " %s(): Unable to register resource %pR - kernel bug?\n", __func__, &window->rsrc); + pci_dev_put(window->pdev); + window->pdev = NULL; return -EBUSY; } diff --git a/drivers/mtd/maps/int0800.c b/drivers/mtd/maps/int0800.c new file mode 100644 index 000000000000..c5f108aa3479 --- /dev/null +++ b/drivers/mtd/maps/int0800.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Read-only MTD access to the system firmware flash behind the ACPI + * INT0800 "Intel 82802 firmware hub" device. + * + * On x86 systems the boot flash is decoded into the physical address + * space below 4 GB, so a plain ioremap() is sufficient to read it - + * no SPI or LPC controller access is required. The declared _CRS + * window may be larger than the real flash (the whole top-16MiB + * decode range is commonly claimed); undecoded holes read as 0xff. + * + * The device is exposed read-only via the ROM chip driver; there is + * deliberately no write or erase support. + */ + +#include <linux/module.h> +#include <linux/acpi.h> +#include <linux/io.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +/* top-of-4GB firmware decode, used when _CRS reports no window */ +#define INT0800_DEFAULT_PHYS 0xffe00000UL +#define INT0800_DEFAULT_SIZE SZ_2M + +struct int0800 { + struct map_info map; + struct mtd_info *mtd; +}; + +static int int0800_probe(struct platform_device *pdev) +{ + struct resource *res; + struct int0800 *fw; + resource_size_t end; + int ret; + + fw = devm_kzalloc(&pdev->dev, sizeof(*fw), GFP_KERNEL); + if (!fw) + return -ENOMEM; + + fw->map.name = dev_name(&pdev->dev); + fw->map.bankwidth = 1; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) { + fw->map.phys = res->start; + fw->map.size = resource_size(res); + } else { + fw->map.phys = INT0800_DEFAULT_PHYS; + fw->map.size = INT0800_DEFAULT_SIZE; + } + + /* + * Plain ioremap on purpose: the window is already claimed by the + * ACPI/pnp resource reservation, so devm_ioremap_resource() would + * fail with -EBUSY. + */ + fw->map.virt = devm_ioremap(&pdev->dev, fw->map.phys, fw->map.size); + if (!fw->map.virt) + return -ENOMEM; + + simple_map_init(&fw->map); + fw->mtd = do_map_probe("map_rom", &fw->map); + if (!fw->mtd) + return -ENODEV; + fw->mtd->dev.parent = &pdev->dev; + platform_set_drvdata(pdev, fw); + + end = fw->map.phys + fw->map.size - 1; + dev_info(&pdev->dev, "mapped firmware window %pa-%pa\n", + &fw->map.phys, &end); + + ret = mtd_device_register(fw->mtd, NULL, 0); + if (ret) + map_destroy(fw->mtd); + return ret; +} + +static void int0800_remove(struct platform_device *pdev) +{ + struct int0800 *fw = platform_get_drvdata(pdev); + + mtd_device_unregister(fw->mtd); + map_destroy(fw->mtd); +} + +static const struct acpi_device_id int0800_ids[] = { + { "INT0800", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, int0800_ids); + +static struct platform_driver int0800_driver = { + .probe = int0800_probe, + .remove = int0800_remove, + .driver = { + .name = "int0800", + .acpi_match_table = int0800_ids, + /* + * remove() frees the mtd_info and unmaps the window even + * while /dev/mtdX fds are still open; the only safe + * teardown path is module unload, which open fds block + * via mtd->owner. + */ + .suppress_bind_attrs = true, + }, +}; +module_platform_driver(int0800_driver); + +MODULE_AUTHOR("Stephen Bancroft <stevereaver@gmail.com>"); +MODULE_DESCRIPTION("Read-only MTD map over the INT0800 firmware flash window"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/maps/l440gx.c b/drivers/mtd/maps/l440gx.c index 832b880d1aaf..1df5ea5fafca 100644 --- a/drivers/mtd/maps/l440gx.c +++ b/drivers/mtd/maps/l440gx.c @@ -110,8 +110,6 @@ static int __init init_l440gx(void) /* Allocate the resource region */ if (pci_assign_resource(pm_dev, PIIXE_IOBASE_RESOURCE) != 0) { - pci_dev_put(dev); - pci_dev_put(pm_dev); printk(KERN_WARNING "Could not allocate pm iobase resource\n"); iounmap(l440gx_map.virt); return -ENXIO; diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c index 34041cc8f173..f13a1f520d04 100644 --- a/drivers/mtd/maps/pci.c +++ b/drivers/mtd/maps/pci.c @@ -250,7 +250,7 @@ static int mtd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) struct mtd_info *mtd = NULL; int err; - err = pci_enable_device(dev); + err = pcim_enable_device(dev); if (err) goto out; diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 4d2e7b7774e9..42f250344f0c 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -18,6 +18,7 @@ #include <linux/spinlock.h> #include <linux/hdreg.h> #include <linux/mutex.h> +#include <linux/sched/mm.h> #include <linux/uaccess.h> #include "mtdcore.h" @@ -167,6 +168,7 @@ static blk_status_t mtd_queue_rq(struct blk_mq_hw_ctx *hctx, const struct blk_mq_queue_data *bd) { struct mtd_blktrans_dev *dev; + unsigned int noio_flags; dev = hctx->queue->queuedata; if (!dev) { @@ -174,10 +176,13 @@ static blk_status_t mtd_queue_rq(struct blk_mq_hw_ctx *hctx, return BLK_STS_IOERR; } + /* Reclaim must not recurse into I/O while processing requests. */ + noio_flags = memalloc_noio_save(); spin_lock_irq(&dev->queue_lock); list_add_tail(&bd->rq->queuelist, &dev->rq_list); mtd_blktrans_work(dev); spin_unlock_irq(&dev->queue_lock); + memalloc_noio_restore(noio_flags); return BLK_STS_OK; } diff --git a/drivers/mtd/mtd_virt_concat.c b/drivers/mtd/mtd_virt_concat.c index 25cf33fe1ec1..40b300021dda 100644 --- a/drivers/mtd/mtd_virt_concat.c +++ b/drivers/mtd/mtd_virt_concat.c @@ -130,6 +130,7 @@ int mtd_virt_concat_destroy(struct mtd_info *mtd) mtd_concat_destroy(&concat->mtd); } + list_del(&item->head); for (idx = 0; idx < item->count; idx++) of_node_put(item->nodes[idx]); @@ -201,6 +202,7 @@ void mtd_virt_concat_destroy_items(void) int i; list_for_each_entry_safe(item, temp, &concat_node_list, head) { + list_del(&item->head); for (i = 0; i < item->count; i++) of_node_put(item->nodes[i]); diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index d54d309c30a0..354cc71cd722 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -31,7 +31,7 @@ * Given a pointer to the MTD object in the mtd_concat structure, * we can retrieve the pointer to that structure with this macro. */ -#define CONCAT(x) ((struct mtd_concat *)(x)) +#define CONCAT(x) container_of_const(x, struct mtd_concat, mtd) /* * MTD methods which look up the relevant subdevice, translate the diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 16629382a787..b170c3144ca8 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -563,7 +563,7 @@ int mtd_pairing_info_to_wunit(struct mtd_info *mtd, return -EINVAL; if (master->pairing && master->pairing->get_wunit) - return mtd->pairing->get_wunit(master, info); + return master->pairing->get_wunit(master, info); return info->pair; } @@ -1083,6 +1083,7 @@ static int mtd_otp_nvmem_add(struct mtd_info *mtd) err: nvmem_unregister(mtd->otp_user_nvmem); + mtd->otp_user_nvmem = NULL; /* Don't report error if OTP is not supported. */ if (err == -EOPNOTSUPP) return 0; @@ -1332,7 +1333,7 @@ int __get_mtd_device(struct mtd_info *mtd) int err; if (master->_get_device) { - err = master->_get_device(mtd); + err = master->_get_device(master); if (err) return err; } diff --git a/drivers/mtd/mtdsuper.c b/drivers/mtd/mtdsuper.c index c709ff7aa6b5..02eede3fe582 100644 --- a/drivers/mtd/mtdsuper.c +++ b/drivers/mtd/mtdsuper.c @@ -31,8 +31,10 @@ static int mtd_get_sb(struct fs_context *fc, int ret; sb = sget_dev(fc, MKDEV(MTD_BLOCK_MAJOR, mtd->index)); - if (IS_ERR(sb)) + if (IS_ERR(sb)) { + put_mtd_device(mtd); return PTR_ERR(sb); + } if (sb->s_root) { /* new mountpoint for an already mounted superblock */ diff --git a/drivers/mtd/nand/ecc.c b/drivers/mtd/nand/ecc.c index 6ccdff3fc913..1d809cbd47e6 100644 --- a/drivers/mtd/nand/ecc.c +++ b/drivers/mtd/nand/ecc.c @@ -159,6 +159,13 @@ int nand_ecc_finish_io_req(struct nand_device *nand, } EXPORT_SYMBOL(nand_ecc_finish_io_req); +bool nand_ecc_is_pipelined(const struct nand_device *nand) +{ + return nand->ecc.engine && + nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED; +} +EXPORT_SYMBOL(nand_ecc_is_pipelined); + /* Define default OOB placement schemes for large and small page devices */ static int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section, struct mtd_oob_region *oobregion) diff --git a/drivers/mtd/nand/onenand/onenand_base.c b/drivers/mtd/nand/onenand/onenand_base.c index d08aeac86f9f..bca5a3bebd41 100644 --- a/drivers/mtd/nand/onenand/onenand_base.c +++ b/drivers/mtd/nand/onenand/onenand_base.c @@ -3279,7 +3279,7 @@ static void onenand_check_features(struct mtd_info *mtd) * * Now it has as following * KFM4G16Q4M has NOP 4 with version ID 0x0131 - * KFM4G16Q5M has NOP 1 with versoin ID 0x013e + * KFM4G16Q5M has NOP 1 with version ID 0x013e */ if ((this->version_id & 0xf) == 0xe) this->options |= ONENAND_HAS_NOP_1; diff --git a/drivers/mtd/nand/onenand/onenand_omap2.c b/drivers/mtd/nand/onenand/onenand_omap2.c index 0793251ada3b..644498187d8f 100644 --- a/drivers/mtd/nand/onenand/onenand_omap2.c +++ b/drivers/mtd/nand/onenand/onenand_omap2.c @@ -340,9 +340,9 @@ static inline int omap2_onenand_dma_transfer(struct omap2_onenand *c, tx->callback = omap2_onenand_dma_complete_func; tx->callback_param = &c->dma_done; - cookie = tx->tx_submit(tx); + cookie = dmaengine_submit(tx); if (dma_submit_error(cookie)) { - dev_err(&c->pdev->dev, "Failed to do DMA tx_submit\n"); + dev_err(&c->pdev->dev, "Failed to do dmaengine_submit\n"); return -EIO; } diff --git a/drivers/mtd/nand/qpic_common.c b/drivers/mtd/nand/qpic_common.c index 4f3e4dd766da..91ae970734f8 100644 --- a/drivers/mtd/nand/qpic_common.c +++ b/drivers/mtd/nand/qpic_common.c @@ -775,5 +775,136 @@ unalloc: } EXPORT_SYMBOL(qcom_nandc_alloc); +/* reads back FLASH_STATUS register set by the controller */ +int qcom_nandc_check_flash_errors(struct qcom_nand_controller *nandc, + int cw_cnt) +{ + int i; + + qcom_nandc_dev_to_mem(nandc, true); + + for (i = 0; i < cw_cnt; i++) { + u32 flash = le32_to_cpu(nandc->reg_read_buf[i]); + + if (flash & (FS_OP_ERR | FS_MPU_ERR)) + return -EIO; + } + + return 0; +} +EXPORT_SYMBOL(qcom_nandc_check_flash_errors); + +/* + * Helper to prepare DMA descriptors for configuring registers + * before reading a NAND page. + */ +void qcom_nandc_config_page_read(struct qcom_nand_controller *nandc) +{ + qcom_write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0); + qcom_write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); + if (!nandc->props->qpic_version2) + qcom_write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, 0); + qcom_write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_clr, + NAND_ERASED_CW_DETECT_CFG, 1, 0); + qcom_write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_set, + NAND_ERASED_CW_DETECT_CFG, 1, NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); +} +EXPORT_SYMBOL(qcom_nandc_config_page_read); + +/* + * Helper to prepare DMA descriptors used to configure registers needed for + * before writing a NAND page. + */ +void qcom_nandc_config_page_write(struct qcom_nand_controller *nandc) +{ + qcom_write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0); + qcom_write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); + if (!nandc->props->qpic_version2) + qcom_write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, + NAND_BAM_NEXT_SGL); +} +EXPORT_SYMBOL(qcom_nandc_config_page_write); + +/* + * Helper to prepare DMA descriptors for configuring registers + * before writing each codeword in NAND page. + */ +void qcom_nandc_config_cw_write(struct qcom_nand_controller *nandc) +{ + qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); + qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + + qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + + qcom_write_reg_dma(nandc, &nandc->regs->clrflashstatus, NAND_FLASH_STATUS, 1, 0); + qcom_write_reg_dma(nandc, &nandc->regs->clrreadstatus, NAND_READ_STATUS, 1, + NAND_BAM_NEXT_SGL); +} +EXPORT_SYMBOL(qcom_nandc_config_cw_write); + +/** + * qcom_nandc_set_read_loc_first() - to set read location first register + * @nandc: NAND controller's private data + * @reg_base: location register base + * @cw_offset: code word offset + * @read_size: code word read length + * @is_last_read_loc: is this the last read location + * + * This function will set location register value + */ +void qcom_nandc_set_read_loc_first(struct qcom_nand_controller *nandc, + int reg_base, u32 cw_offset, + u32 read_size, u32 is_last_read_loc) +{ + __le32 locreg_val; + u32 val = FIELD_PREP(READ_LOCATION_OFFSET_MASK, cw_offset) | + FIELD_PREP(READ_LOCATION_SIZE_MASK, read_size) | + FIELD_PREP(READ_LOCATION_LAST_MASK, is_last_read_loc); + + locreg_val = cpu_to_le32(val); + + if (reg_base == NAND_READ_LOCATION_0) + nandc->regs->read_location0 = locreg_val; + else if (reg_base == NAND_READ_LOCATION_1) + nandc->regs->read_location1 = locreg_val; + else if (reg_base == NAND_READ_LOCATION_2) + nandc->regs->read_location2 = locreg_val; + else if (reg_base == NAND_READ_LOCATION_3) + nandc->regs->read_location3 = locreg_val; +} +EXPORT_SYMBOL(qcom_nandc_set_read_loc_first); + +/** + * qcom_nandc_set_read_loc_last - to set read location last register + * @nandc: NAND controller's private data + * @reg_base: location register base + * @cw_offset: code word offset + * @read_size: code word read length + * @is_last_read_loc: is this the last read location + * + * This function will set location last register value + */ +void qcom_nandc_set_read_loc_last(struct qcom_nand_controller *nandc, + int reg_base, u32 cw_offset, + u32 read_size, u32 is_last_read_loc) +{ + __le32 locreg_val; + u32 val = FIELD_PREP(READ_LOCATION_OFFSET_MASK, cw_offset) | + FIELD_PREP(READ_LOCATION_SIZE_MASK, read_size) | + FIELD_PREP(READ_LOCATION_LAST_MASK, is_last_read_loc); + + locreg_val = cpu_to_le32(val); + + if (reg_base == NAND_READ_LOCATION_LAST_CW_0) + nandc->regs->read_location_last0 = locreg_val; + else if (reg_base == NAND_READ_LOCATION_LAST_CW_1) + nandc->regs->read_location_last1 = locreg_val; + else if (reg_base == NAND_READ_LOCATION_LAST_CW_2) + nandc->regs->read_location_last2 = locreg_val; + else if (reg_base == NAND_READ_LOCATION_LAST_CW_3) + nandc->regs->read_location_last3 = locreg_val; +} +EXPORT_SYMBOL(qcom_nandc_set_read_loc_last); + MODULE_DESCRIPTION("QPIC controller common api"); MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c index 8a19408abb63..0bdbcbb50d94 100644 --- a/drivers/mtd/nand/raw/atmel/nand-controller.c +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -2085,7 +2085,7 @@ static int atmel_nand_controller_init(struct atmel_nand_controller *nc, if (!np) { dev_err(dev, "Missing or invalid atmel,smc property\n"); ret = -EINVAL; - goto out_release_dma; + goto out_put_mck; } nc->smc = syscon_node_to_regmap(np); @@ -2093,11 +2093,13 @@ static int atmel_nand_controller_init(struct atmel_nand_controller *nc, if (IS_ERR(nc->smc)) { ret = PTR_ERR(nc->smc); dev_err(dev, "Could not get SMC regmap (err = %d)\n", ret); - goto out_release_dma; + goto out_put_mck; } return 0; +out_put_mck: + clk_put(nc->mck); out_release_dma: if (nc->dmac) dma_release_channel(nc->dmac); diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index 5b9dadd5405e..4a1f5e736a1c 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -3272,6 +3272,8 @@ static int brcmnand_edu_setup(struct platform_device *pdev) ctrl->edu_irq = platform_get_irq_optional(pdev, 1); if (ctrl->edu_irq < 0) { + if (ctrl->edu_irq != -ENXIO) + return ctrl->edu_irq; dev_warn(dev, "FLASH EDU enabled, using ctlrdy irq\n"); } else { diff --git a/drivers/mtd/nand/raw/cadence-nand-controller.c b/drivers/mtd/nand/raw/cadence-nand-controller.c index d53b35a8b3cb..4f5e6d2b128c 100644 --- a/drivers/mtd/nand/raw/cadence-nand-controller.c +++ b/drivers/mtd/nand/raw/cadence-nand-controller.c @@ -3143,6 +3143,9 @@ static int cadence_nand_init(struct cdns_nand_ctrl *cdns_ctrl) goto free_buf_desc; } + spin_lock_init(&cdns_ctrl->irq_lock); + init_completion(&cdns_ctrl->complete); + if (devm_request_irq(cdns_ctrl->dev, cdns_ctrl->irq, cadence_nand_isr, IRQF_SHARED, "cadence-nand-controller", cdns_ctrl)) { @@ -3151,9 +3154,6 @@ static int cadence_nand_init(struct cdns_nand_ctrl *cdns_ctrl) goto free_buf; } - spin_lock_init(&cdns_ctrl->irq_lock); - init_completion(&cdns_ctrl->complete); - ret = cadence_nand_hw_init(cdns_ctrl); if (ret) goto disable_irq; @@ -3229,7 +3229,7 @@ static void cadence_nand_remove(struct cdns_nand_ctrl *cdns_ctrl) { cadence_nand_chips_cleanup(cdns_ctrl); if (cdns_ctrl->dmac) - dma_unmap_resource(cdns_ctrl->dmac->device->dev, + dma_unmap_resource(dmaengine_get_dma_device(cdns_ctrl->dmac), cdns_ctrl->io.iova_dma, cdns_ctrl->io.size, DMA_BIDIRECTIONAL, 0); cadence_nand_irq_cleanup(cdns_ctrl->irq, cdns_ctrl); diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c index a88ac2cfaccd..cc75b6a67e07 100644 --- a/drivers/mtd/nand/raw/fsl_ifc_nand.c +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -12,7 +12,6 @@ #include <linux/platform_device.h> #include <linux/types.h> #include <linux/kernel.h> -#include <linux/of_address.h> #include <linux/slab.h> #include <linux/mtd/mtd.h> #include <linux/mtd/rawnand.h> @@ -50,7 +49,6 @@ struct fsl_ifc_nand_ctrl { unsigned int index; /* Pointer to next byte to 'read' */ unsigned int oob; /* Non zero if operating on OOB data */ unsigned int eccread; /* Non zero for a full-page ECC read */ - unsigned int counter; /* counter for the initializations */ unsigned int max_bitflips; /* Saved during READ0 cmd */ }; @@ -965,13 +963,6 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv) { - struct mtd_info *mtd = nand_to_mtd(&priv->chip); - - kfree(mtd->name); - - if (priv->vbase) - iounmap(priv->vbase); - ifc_nand_ctrl->chips[priv->bank] = NULL; return 0; @@ -996,12 +987,12 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) { struct fsl_ifc_runtime __iomem *ifc; struct fsl_ifc_mtd *priv; - struct resource res; + struct resource *res; + void __iomem *vbase; static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", "ofpart", NULL }; int ret; int bank; - struct device_node *node = dev->dev.of_node; struct mtd_info *mtd; if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->rregs) @@ -1009,15 +1000,13 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) ifc = fsl_ifc_ctrl_dev->rregs; /* get, allocate and map the memory resource */ - ret = of_address_to_resource(node, 0, &res); - if (ret) { - dev_err(&dev->dev, "%s: failed to get resource\n", __func__); - return ret; - } + vbase = devm_platform_get_and_ioremap_resource(dev, 0, &res); + if (IS_ERR(vbase)) + return PTR_ERR(vbase); /* find which chip select it is connected to */ for (bank = 0; bank < fsl_ifc_ctrl_dev->banks; bank++) { - if (match_bank(fsl_ifc_ctrl_dev->gregs, bank, res.start)) + if (match_bank(fsl_ifc_ctrl_dev->gregs, bank, res->start)) break; } @@ -1033,15 +1022,13 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) mutex_lock(&fsl_ifc_nand_mutex); if (!fsl_ifc_ctrl_dev->nand) { - ifc_nand_ctrl = kzalloc_obj(*ifc_nand_ctrl); + ifc_nand_ctrl = devm_kzalloc(fsl_ifc_ctrl_dev->dev, sizeof(*ifc_nand_ctrl), + GFP_KERNEL); if (!ifc_nand_ctrl) { mutex_unlock(&fsl_ifc_nand_mutex); return -ENOMEM; } - ifc_nand_ctrl->read_bytes = 0; - ifc_nand_ctrl->index = 0; - ifc_nand_ctrl->addr = NULL; fsl_ifc_ctrl_dev->nand = ifc_nand_ctrl; nand_controller_init(&ifc_nand_ctrl->controller); @@ -1054,13 +1041,7 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) priv->bank = bank; priv->ctrl = fsl_ifc_ctrl_dev; priv->dev = &dev->dev; - - priv->vbase = ioremap(res.start, resource_size(&res)); - if (!priv->vbase) { - dev_err(priv->dev, "%s: failed to map chip region\n", __func__); - ret = -ENOMEM; - goto err; - } + priv->vbase = vbase; dev_set_drvdata(priv->dev, priv); @@ -1076,7 +1057,7 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) &ifc->ifc_nand.nand_evter_intr_en); mtd = nand_to_mtd(&priv->chip); - mtd->name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start); + mtd->name = devm_kasprintf(priv->dev, GFP_KERNEL, "%llx.flash", (u64)res->start); if (!mtd->name) { ret = -ENOMEM; goto err; @@ -1098,7 +1079,7 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) goto cleanup_nand; dev_info(priv->dev, "IFC NAND device at 0x%llx, bank %d\n", - (unsigned long long)res.start, priv->bank); + (unsigned long long)res->start, priv->bank); return 0; @@ -1121,14 +1102,6 @@ static void fsl_ifc_nand_remove(struct platform_device *dev) nand_cleanup(chip); fsl_ifc_chip_remove(priv); - - mutex_lock(&fsl_ifc_nand_mutex); - ifc_nand_ctrl->counter--; - if (!ifc_nand_ctrl->counter) { - fsl_ifc_ctrl_dev->nand = NULL; - kfree(ifc_nand_ctrl); - } - mutex_unlock(&fsl_ifc_nand_mutex); } static const struct of_device_id fsl_ifc_nand_match[] = { diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index b13b2b0c3f30..837c23c63aeb 100644 --- a/drivers/mtd/nand/raw/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -461,7 +461,7 @@ static int fsmc_correct_ecc1(struct nand_chip *chip, chip->ecc.size, sm_order); } -/* Count the number of 0's in buff upto a max of max_bits */ +/* Count the number of 0's in buff up to a max of max_bits */ static int count_written_bits(u8 *buff, int size, int max_bits) { int k, written_bits = 0; @@ -524,7 +524,7 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len, tx->callback = dma_complete; tx->callback_param = host; - cookie = tx->tx_submit(tx); + cookie = dmaengine_submit(tx); ret = dma_submit_error(cookie); if (ret) { diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 527165ccc839..c61f3983a713 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -2271,7 +2271,7 @@ static int nand_boot_init(struct gpmi_nand_data *this) { nand_boot_set_geometry(this); - /* This is ROM arch-specific initilization before the BBT scanning. */ + /* This is ROM arch-specific initialization before the BBT scanning. */ if (GPMI_IS_MX23(this)) return mx23_boot_init(this); return 0; diff --git a/drivers/mtd/nand/raw/intel-nand-controller.c b/drivers/mtd/nand/raw/intel-nand-controller.c index 01cefdaf115d..94d2161bf5a1 100644 --- a/drivers/mtd/nand/raw/intel-nand-controller.c +++ b/drivers/mtd/nand/raw/intel-nand-controller.c @@ -290,6 +290,7 @@ static int ebu_dma_start(struct ebu_nand_controller *ebu_host, u32 dir, struct dma_async_tx_descriptor *tx; struct completion *dma_completion; dma_async_tx_callback callback; + struct device *dma_dev; struct dma_chan *chan; dma_cookie_t cookie; unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; @@ -307,8 +308,9 @@ static int ebu_dma_start(struct ebu_nand_controller *ebu_host, u32 dir, callback = ebu_dma_tx_callback; } - buf_dma = dma_map_single(chan->device->dev, (void *)buf, len, dir); - if (dma_mapping_error(chan->device->dev, buf_dma)) { + dma_dev = dmaengine_get_dma_device(chan); + buf_dma = dma_map_single(dma_dev, (void *)buf, len, dir); + if (dma_mapping_error(dma_dev, buf_dma)) { dev_err(ebu_host->dev, "Failed to map DMA buffer\n"); ret = -EIO; goto err_unmap; @@ -322,7 +324,7 @@ static int ebu_dma_start(struct ebu_nand_controller *ebu_host, u32 dir, tx->callback = callback; tx->callback_param = ebu_host; - cookie = tx->tx_submit(tx); + cookie = dmaengine_submit(tx); ret = dma_submit_error(cookie); if (ret) { diff --git a/drivers/mtd/nand/raw/internals.h b/drivers/mtd/nand/raw/internals.h index b7162ced9efa..b8b29341b88c 100644 --- a/drivers/mtd/nand/raw/internals.h +++ b/drivers/mtd/nand/raw/internals.h @@ -19,6 +19,7 @@ #define NAND_MFR_AMD 0x01 #define NAND_MFR_ATO 0x9b #define NAND_MFR_EON 0x92 +/* ESMT and GigaDevice share the same manufacturer ID. */ #define NAND_MFR_ESMT 0xc8 #define NAND_MFR_FUJITSU 0x04 #define NAND_MFR_HYNIX 0xad diff --git a/drivers/mtd/nand/raw/loongson-nand-controller.c b/drivers/mtd/nand/raw/loongson-nand-controller.c index 8490412d5be1..935f66602de9 100644 --- a/drivers/mtd/nand/raw/loongson-nand-controller.c +++ b/drivers/mtd/nand/raw/loongson-nand-controller.c @@ -391,7 +391,7 @@ static void loongson_nand_dma_callback(void *data) { struct loongson_nand_host *host = (struct loongson_nand_host *)data; struct dma_chan *chan = host->dma_chan; - struct device *dev = chan->device->dev; + struct device *dev = dmaengine_get_dma_device(chan); enum dma_status status; status = dmaengine_tx_status(chan, host->dma_cookie, NULL); @@ -407,7 +407,7 @@ static int loongson_nand_dma_transfer(struct loongson_nand_host *host, struct lo { struct nand_chip *chip = &host->chip; struct dma_chan *chan = host->dma_chan; - struct device *dev = chan->device->dev; + struct device *dev = dmaengine_get_dma_device(chan); struct dma_async_tx_descriptor *desc; enum dma_data_direction data_dir = op->is_write ? DMA_TO_DEVICE : DMA_FROM_DEVICE; enum dma_transfer_direction xfer_dir = op->is_write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c index 8f6a89d9ba83..d03486dc4115 100644 --- a/drivers/mtd/nand/raw/lpc32xx_mlc.c +++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c @@ -26,7 +26,6 @@ #include <linux/interrupt.h> #include <linux/of.h> #include <linux/gpio/consumer.h> -#include <linux/mtd/lpc32xx_mlc.h> #include <linux/io.h> #include <linux/mm.h> #include <linux/dma-mapping.h> @@ -174,7 +173,6 @@ static struct nand_bbt_descr lpc32xx_nand_bbt_mirror = { struct lpc32xx_nand_host { struct platform_device *pdev; struct nand_chip nand_chip; - struct lpc32xx_mlc_platform_data *pdata; struct clk *clk; struct gpio_desc *wp_gpio; void __iomem *io_base; @@ -394,6 +392,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len, { struct nand_chip *chip = mtd_to_nand(mtd); struct lpc32xx_nand_host *host = nand_get_controller_data(chip); + struct device *dma_dev = dmaengine_get_dma_device(host->dma_chan); struct dma_async_tx_descriptor *desc; int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; unsigned long time_left; @@ -401,8 +400,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len, sg_init_one(&host->sgl, mem, len); - res = dma_map_sg(host->dma_chan->device->dev, &host->sgl, 1, - DMA_BIDIRECTIONAL); + res = dma_map_sg(dma_dev, &host->sgl, 1, DMA_BIDIRECTIONAL); if (res != 1) { dev_err(mtd->dev.parent, "Failed to map sg list\n"); return -ENXIO; @@ -430,12 +428,10 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len, goto out1; } - dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, - DMA_BIDIRECTIONAL); + dma_unmap_sg(dma_dev, &host->sgl, 1, DMA_BIDIRECTIONAL); return 0; out1: - dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, - DMA_BIDIRECTIONAL); + dma_unmap_sg(dma_dev, &host->sgl, 1, DMA_BIDIRECTIONAL); return res; } @@ -580,24 +576,11 @@ static void lpc32xx_ecc_enable(struct nand_chip *chip, int mode) static int lpc32xx_dma_setup(struct lpc32xx_nand_host *host) { struct mtd_info *mtd = nand_to_mtd(&host->nand_chip); - dma_cap_mask_t mask; host->dma_chan = dma_request_chan(mtd->dev.parent, "rx-tx"); if (IS_ERR(host->dma_chan)) { - /* fallback to request using platform data */ - if (!host->pdata || !host->pdata->dma_filter) { - dev_err(mtd->dev.parent, "no DMA platform data\n"); - return -ENOENT; - } - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - host->dma_chan = dma_request_channel(mask, host->pdata->dma_filter, "nand-mlc"); - - if (!host->dma_chan) { - dev_err(mtd->dev.parent, "Failed to request DMA channel\n"); - return -EBUSY; - } + dev_err(mtd->dev.parent, "Failed to request DMA channel\n"); + return PTR_ERR(host->dma_chan); } /* @@ -736,8 +719,6 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) gpiod_set_consumer_name(host->wp_gpio, "NAND WP"); - host->pdata = dev_get_platdata(&pdev->dev); - /* link the private data structures */ nand_set_controller_data(nand_chip, host); nand_set_flash_node(nand_chip, pdev->dev.of_node); diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c index 10c8080207f4..638267d44360 100644 --- a/drivers/mtd/nand/raw/lpc32xx_slc.c +++ b/drivers/mtd/nand/raw/lpc32xx_slc.c @@ -25,7 +25,6 @@ #include <linux/dmaengine.h> #include <linux/gpio/consumer.h> #include <linux/of.h> -#include <linux/mtd/lpc32xx_slc.h> #define LPC32XX_MODNAME "lpc32xx-nand" @@ -213,7 +212,6 @@ struct lpc32xx_nand_cfg_slc { struct lpc32xx_nand_host { struct nand_chip nand_chip; - struct lpc32xx_slc_platform_data *pdata; struct clk *clk; struct gpio_desc *wp_gpio; void __iomem *io_base; @@ -427,6 +425,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma, { struct nand_chip *chip = mtd_to_nand(mtd); struct lpc32xx_nand_host *host = nand_get_controller_data(chip); + struct device *dma_dev = dmaengine_get_dma_device(host->dma_chan); struct dma_async_tx_descriptor *desc; int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; int res; @@ -448,8 +447,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma, sg_init_one(&host->sgl, mem, len); - res = dma_map_sg(host->dma_chan->device->dev, &host->sgl, 1, - DMA_BIDIRECTIONAL); + res = dma_map_sg(dma_dev, &host->sgl, 1, DMA_BIDIRECTIONAL); if (res != 1) { dev_err(mtd->dev.parent, "Failed to map sg list\n"); return -ENXIO; @@ -477,13 +475,11 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma, res = 0; } - dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, - DMA_BIDIRECTIONAL); + dma_unmap_sg(dma_dev, &host->sgl, 1, DMA_BIDIRECTIONAL); return res; out1: - dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, - DMA_BIDIRECTIONAL); + dma_unmap_sg(dma_dev, &host->sgl, 1, DMA_BIDIRECTIONAL); return -ENXIO; } @@ -727,24 +723,11 @@ static int lpc32xx_nand_write_page_raw_syndrome(struct nand_chip *chip, static int lpc32xx_nand_dma_setup(struct lpc32xx_nand_host *host) { struct mtd_info *mtd = nand_to_mtd(&host->nand_chip); - dma_cap_mask_t mask; host->dma_chan = dma_request_chan(mtd->dev.parent, "rx-tx"); if (IS_ERR(host->dma_chan)) { - /* fallback to request using platform data */ - if (!host->pdata || !host->pdata->dma_filter) { - dev_err(mtd->dev.parent, "no DMA platform data\n"); - return -ENOENT; - } - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - host->dma_chan = dma_request_channel(mask, host->pdata->dma_filter, "nand-slc"); - - if (!host->dma_chan) { - dev_err(mtd->dev.parent, "Failed to request DMA channel\n"); - return -EBUSY; - } + dev_err(mtd->dev.parent, "Failed to request DMA channel\n"); + return PTR_ERR(host->dma_chan); } return 0; @@ -873,8 +856,6 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) gpiod_set_consumer_name(host->wp_gpio, "NAND WP"); - host->pdata = dev_get_platdata(&pdev->dev); - chip = &host->nand_chip; mtd = nand_to_mtd(chip); nand_set_controller_data(chip, host); diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index 38b7eb5b992c..e5fa5540e80a 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -868,6 +868,7 @@ static int marvell_nfc_xfer_data_dma(struct marvell_nfc *nfc, unsigned int len) { unsigned int dma_len = min_t(int, ALIGN(len, 32), MAX_CHUNK_SIZE); + struct device *dma_dev = dmaengine_get_dma_device(nfc->dma_chan); struct dma_async_tx_descriptor *tx; struct scatterlist sg; dma_cookie_t cookie; @@ -876,7 +877,7 @@ static int marvell_nfc_xfer_data_dma(struct marvell_nfc *nfc, marvell_nfc_enable_dma(nfc); /* Prepare the DMA transfer */ sg_init_one(&sg, nfc->dma_buf, dma_len); - ret = dma_map_sg(nfc->dma_chan->device->dev, &sg, 1, direction); + ret = dma_map_sg(dma_dev, &sg, 1, direction); if (!ret) { dev_err(nfc->dev, "Could not map DMA S/G list\n"); return -ENXIO; @@ -888,7 +889,7 @@ static int marvell_nfc_xfer_data_dma(struct marvell_nfc *nfc, DMA_PREP_INTERRUPT); if (!tx) { dev_err(nfc->dev, "Could not prepare DMA S/G list\n"); - dma_unmap_sg(nfc->dma_chan->device->dev, &sg, 1, direction); + dma_unmap_sg(dma_dev, &sg, 1, direction); return -ENXIO; } @@ -900,7 +901,7 @@ static int marvell_nfc_xfer_data_dma(struct marvell_nfc *nfc, dma_async_issue_pending(nfc->dma_chan); ret = marvell_nfc_wait_cmdd(nfc->selected_chip); - dma_unmap_sg(nfc->dma_chan->device->dev, &sg, 1, direction); + dma_unmap_sg(dma_dev, &sg, 1, direction); marvell_nfc_disable_dma(nfc); if (ret) { dev_err(nfc->dev, "Timeout waiting for DMA (status: %d)\n", diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c index 21c7e1102746..c439b7814ebb 100644 --- a/drivers/mtd/nand/raw/mtk_nand.c +++ b/drivers/mtd/nand/raw/mtk_nand.c @@ -42,7 +42,7 @@ #define CON_BRD BIT(8) /* burst read */ #define CON_BWR BIT(9) /* burst write */ #define CON_SEC_SHIFT (12) -/* Timming control register */ +/* Timing control register */ #define NFI_ACCCON (0x0C) #define NFI_INTR_EN (0x10) #define INTR_AHB_DONE_EN BIT(6) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index a5b278ab9384..a225b10f8145 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -6205,7 +6205,7 @@ static int nand_scan_tail(struct nand_chip *chip) /* * FIXME: some NAND manufacturer drivers expect the first die to be * selected when manufacturer->init() is called. They should be fixed - * to explictly select the relevant die when interacting with the NAND + * to explicitly select the relevant die when interacting with the NAND * chip. */ nand_select_target(chip, 0); diff --git a/drivers/mtd/nand/raw/nand_esmt.c b/drivers/mtd/nand/raw/nand_esmt.c index 4412c407aef3..117b110fc54a 100644 --- a/drivers/mtd/nand/raw/nand_esmt.c +++ b/drivers/mtd/nand/raw/nand_esmt.c @@ -3,6 +3,9 @@ * Copyright (C) 2018 Toradex AG * * Author: Marcel Ziswiler <marcel.ziswiler@toradex.com> + * + * ESMT and GigaDevice share the same JEDEC manufacturer ID (0xc8), so this + * file handles chips from both vendors. */ #include <linux/mtd/rawnand.h> @@ -38,6 +41,35 @@ static void esmt_nand_decode_id(struct nand_chip *chip) nanddev_set_ecc_requirements(base, &requirements); } +/* + * GigaDevice GD9FU2G8F3A does not implement the timing mode feature: + * GET_FEATURES does not report the mode that was written, and + * SET_FEATURES is ignored, even though the parameter page advertises + * support. The vendor confirmed the chip works at every advertised + * timing mode without being told, so unflag the feature and configure + * the host side only. + */ +static void esmt_nand_fix_broken_get_timings(struct nand_chip *chip) +{ + int i; + static const char * const broken_get_timings[] = { + "GD9FU2G8F3A", + }; + + if (!chip->parameters.supports_set_get_features) + return; + + i = match_string(broken_get_timings, ARRAY_SIZE(broken_get_timings), + chip->parameters.model); + if (i < 0) + return; + + bitmap_clear(chip->parameters.get_feature_list, + ONFI_FEATURE_ADDR_TIMING_MODE, 1); + bitmap_clear(chip->parameters.set_feature_list, + ONFI_FEATURE_ADDR_TIMING_MODE, 1); +} + static int esmt_nand_init(struct nand_chip *chip) { if (nand_is_slc(chip)) @@ -50,6 +82,8 @@ static int esmt_nand_init(struct nand_chip *chip) chip->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE | NAND_BBM_LASTPAGE; + esmt_nand_fix_broken_get_timings(chip); + return 0; } diff --git a/drivers/mtd/nand/raw/nand_ids.c b/drivers/mtd/nand/raw/nand_ids.c index 08707ce9d7c0..9b2dd246398f 100644 --- a/drivers/mtd/nand/raw/nand_ids.c +++ b/drivers/mtd/nand/raw/nand_ids.c @@ -185,7 +185,7 @@ static const struct nand_manufacturer_desc nand_manufacturer_descs[] = { {NAND_MFR_AMD, "AMD/Spansion", &amd_nand_manuf_ops}, {NAND_MFR_ATO, "ATO"}, {NAND_MFR_EON, "Eon"}, - {NAND_MFR_ESMT, "ESMT", &esmt_nand_manuf_ops}, + {NAND_MFR_ESMT, "ESMT/GigaDevice", &esmt_nand_manuf_ops}, {NAND_MFR_FUJITSU, "Fujitsu"}, {NAND_MFR_HYNIX, "Hynix", &hynix_nand_manuf_ops}, {NAND_MFR_INTEL, "Intel"}, diff --git a/drivers/mtd/nand/raw/nand_legacy.c b/drivers/mtd/nand/raw/nand_legacy.c index 97700f80d5b8..94d5c7723c5a 100644 --- a/drivers/mtd/nand/raw/nand_legacy.c +++ b/drivers/mtd/nand/raw/nand_legacy.c @@ -319,7 +319,7 @@ static void nand_command(struct nand_chip *chip, unsigned int command, NAND_CTRL_CLE | NAND_CTRL_CHANGE); chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); - /* EZ-NAND can take upto 250ms as per ONFi v4.0 */ + /* EZ-NAND can take up to 250ms as per ONFi v4.0 */ nand_wait_status_ready(chip, 250); return; @@ -459,7 +459,7 @@ static void nand_command_lp(struct nand_chip *chip, unsigned int command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); - /* EZ-NAND can take upto 250ms as per ONFi v4.0 */ + /* EZ-NAND can take up to 250ms as per ONFi v4.0 */ nand_wait_status_ready(chip, 250); return; diff --git a/drivers/mtd/nand/raw/ndfc.c b/drivers/mtd/nand/raw/ndfc.c index a48274297d3b..d7b119028a71 100644 --- a/drivers/mtd/nand/raw/ndfc.c +++ b/drivers/mtd/nand/raw/ndfc.c @@ -162,8 +162,8 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc, return -ENODEV; nand_set_flash_node(chip, flash_np); - mtd->name = kasprintf(GFP_KERNEL, "%s.%pOFn", dev_name(&ndfc->ofdev->dev), - flash_np); + mtd->name = devm_kasprintf(&ndfc->ofdev->dev, GFP_KERNEL, "%s.%pOFn", + dev_name(&ndfc->ofdev->dev), flash_np); if (!mtd->name) { ret = -ENOMEM; goto err; @@ -177,18 +177,21 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc, err: of_node_put(flash_np); - if (ret) - kfree(mtd->name); return ret; } static int ndfc_probe(struct platform_device *ofdev) { struct ndfc_controller *ndfc; + void __iomem *ndfcbase; const __be32 *reg; u32 ccr; u32 cs; - int err, len = 0; + int len = 0; + + ndfcbase = devm_platform_ioremap_resource(ofdev, 0); + if (IS_ERR(ndfcbase)) + return PTR_ERR(ndfcbase); /* Read the reg property to get the chip select */ reg = of_get_property(ofdev->dev.of_node, "reg", &len); @@ -210,11 +213,7 @@ static int ndfc_probe(struct platform_device *ofdev) ndfc->ofdev = ofdev; dev_set_drvdata(&ofdev->dev, ndfc); - ndfc->ndfcbase = of_iomap(ofdev->dev.of_node, 0); - if (!ndfc->ndfcbase) { - dev_err(&ofdev->dev, "failed to get memory\n"); - return -EIO; - } + ndfc->ndfcbase = ndfcbase; ccr = NDFC_CCR_BS(ndfc->chip_select); @@ -232,13 +231,7 @@ static int ndfc_probe(struct platform_device *ofdev) iowrite32be(be32_to_cpup(reg), ndfc->ndfcbase + offset); } - err = ndfc_chip_init(ndfc, ofdev->dev.of_node); - if (err) { - iounmap(ndfc->ndfcbase); - return err; - } - - return 0; + return ndfc_chip_init(ndfc, ofdev->dev.of_node); } static void ndfc_remove(struct platform_device *ofdev) @@ -251,7 +244,6 @@ static void ndfc_remove(struct platform_device *ofdev) ret = mtd_device_unregister(mtd); WARN_ON(ret); nand_cleanup(chip); - kfree(mtd->name); } static const struct of_device_id ndfc_match[] = { diff --git a/drivers/mtd/nand/raw/nuvoton-ma35d1-nand-controller.c b/drivers/mtd/nand/raw/nuvoton-ma35d1-nand-controller.c index 1a285cd8fad6..f659d3a6b9ef 100644 --- a/drivers/mtd/nand/raw/nuvoton-ma35d1-nand-controller.c +++ b/drivers/mtd/nand/raw/nuvoton-ma35d1-nand-controller.c @@ -993,7 +993,6 @@ static int ma35_nand_probe(struct platform_device *pdev) ret = ma35_nand_chips_init(&pdev->dev, nand); if (ret) { dev_err(&pdev->dev, "failed to init NAND chips\n"); - clk_disable(nand->clk); return ret; } diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c index 552f36da79fc..4fa89aee934e 100644 --- a/drivers/mtd/nand/raw/omap2.c +++ b/drivers/mtd/nand/raw/omap2.c @@ -380,8 +380,8 @@ static inline int omap_nand_dma_transfer(struct nand_chip *chip, { struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); struct dma_async_tx_descriptor *tx; - enum dma_data_direction dir = is_write ? DMA_TO_DEVICE : - DMA_FROM_DEVICE; + enum dma_data_direction dir = is_write ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + struct device *dma_dev = dmaengine_get_dma_device(info->dma); struct scatterlist sg; unsigned long tim, limit; unsigned n; @@ -392,7 +392,7 @@ static inline int omap_nand_dma_transfer(struct nand_chip *chip, goto out_copy; sg_init_one(&sg, addr, len); - n = dma_map_sg(info->dma->device->dev, &sg, 1, dir); + n = dma_map_sg(dma_dev, &sg, 1, dir); if (n == 0) { dev_err(&info->pdev->dev, "Couldn't DMA map a %d byte buffer\n", len); @@ -434,11 +434,11 @@ static inline int omap_nand_dma_transfer(struct nand_chip *chip, /* disable and stop the PFPW engine */ omap_prefetch_reset(info->gpmc_cs, info); - dma_unmap_sg(info->dma->device->dev, &sg, 1, dir); + dma_unmap_sg(dma_dev, &sg, 1, dir); return 0; out_copy_unmap: - dma_unmap_sg(info->dma->device->dev, &sg, 1, dir); + dma_unmap_sg(dma_dev, &sg, 1, dir); out_copy: is_write == 0 ? omap_nand_data_in(chip, (void *)addr, len, false) : omap_nand_data_out(chip, addr, len, false); @@ -666,9 +666,7 @@ static void gen_true_ecc(u8 *ecc_buf) * If there is no error, %0 is returned. If there is an error but it * was corrected, %1 is returned. Otherwise, %-1 is returned. */ -static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */ - u8 *ecc_data2, /* read from register */ - u8 *page_data) +static int omap_compare_ecc(u8 *ecc_data1, u8 *ecc_data2, u8 *page_data) { uint i; u8 tmp0_bit[8], tmp1_bit[8], tmp2_bit[8]; diff --git a/drivers/mtd/nand/raw/pl35x-nand-controller.c b/drivers/mtd/nand/raw/pl35x-nand-controller.c index bd89aaadd1b2..26b61d856143 100644 --- a/drivers/mtd/nand/raw/pl35x-nand-controller.c +++ b/drivers/mtd/nand/raw/pl35x-nand-controller.c @@ -807,6 +807,7 @@ static int pl35x_nfc_setup_interface(struct nand_chip *chip, int cs, * macro to convert from one to the other. */ period_ns = NSEC_PER_SEC / clk_get_rate(mclk); + clk_put(mclk); /* * PL35X SMC needs one extra read cycle in SDR Mode 5. This is not diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index d7642db2e2df..2d98f79b492d 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -149,70 +149,6 @@ static bool qcom_nandc_is_last_cw(struct nand_ecc_ctrl *ecc, int cw) return cw == (ecc->steps - 1); } -/** - * nandc_set_read_loc_first() - to set read location first register - * @chip: NAND Private Flash Chip Data - * @reg_base: location register base - * @cw_offset: code word offset - * @read_size: code word read length - * @is_last_read_loc: is this the last read location - * - * This function will set location register value - */ -static void nandc_set_read_loc_first(struct nand_chip *chip, - int reg_base, u32 cw_offset, - u32 read_size, u32 is_last_read_loc) -{ - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - __le32 locreg_val; - u32 val = FIELD_PREP(READ_LOCATION_OFFSET_MASK, cw_offset) | - FIELD_PREP(READ_LOCATION_SIZE_MASK, read_size) | - FIELD_PREP(READ_LOCATION_LAST_MASK, is_last_read_loc); - - locreg_val = cpu_to_le32(val); - - if (reg_base == NAND_READ_LOCATION_0) - nandc->regs->read_location0 = locreg_val; - else if (reg_base == NAND_READ_LOCATION_1) - nandc->regs->read_location1 = locreg_val; - else if (reg_base == NAND_READ_LOCATION_2) - nandc->regs->read_location2 = locreg_val; - else if (reg_base == NAND_READ_LOCATION_3) - nandc->regs->read_location3 = locreg_val; -} - -/** - * nandc_set_read_loc_last - to set read location last register - * @chip: NAND Private Flash Chip Data - * @reg_base: location register base - * @cw_offset: code word offset - * @read_size: code word read length - * @is_last_read_loc: is this the last read location - * - * This function will set location last register value - */ -static void nandc_set_read_loc_last(struct nand_chip *chip, - int reg_base, u32 cw_offset, - u32 read_size, u32 is_last_read_loc) -{ - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - __le32 locreg_val; - u32 val = FIELD_PREP(READ_LOCATION_OFFSET_MASK, cw_offset) | - FIELD_PREP(READ_LOCATION_SIZE_MASK, read_size) | - FIELD_PREP(READ_LOCATION_LAST_MASK, is_last_read_loc); - - locreg_val = cpu_to_le32(val); - - if (reg_base == NAND_READ_LOCATION_LAST_CW_0) - nandc->regs->read_location_last0 = locreg_val; - else if (reg_base == NAND_READ_LOCATION_LAST_CW_1) - nandc->regs->read_location_last1 = locreg_val; - else if (reg_base == NAND_READ_LOCATION_LAST_CW_2) - nandc->regs->read_location_last2 = locreg_val; - else if (reg_base == NAND_READ_LOCATION_LAST_CW_3) - nandc->regs->read_location_last3 = locreg_val; -} - /* helper to configure location register values */ static void nandc_set_read_loc(struct nand_chip *chip, int cw, int reg, u32 cw_offset, u32 read_size, u32 is_last_read_loc) @@ -227,11 +163,13 @@ static void nandc_set_read_loc(struct nand_chip *chip, int cw, int reg, reg_base += reg * 4; if (nandc->props->qpic_version2 && qcom_nandc_is_last_cw(ecc, cw)) - return nandc_set_read_loc_last(chip, reg_base, cw_offset, - read_size, is_last_read_loc); + return qcom_nandc_set_read_loc_last(nandc, reg_base, cw_offset, + read_size, + is_last_read_loc); else - return nandc_set_read_loc_first(chip, reg_base, cw_offset, - read_size, is_last_read_loc); + return qcom_nandc_set_read_loc_first(nandc, reg_base, cw_offset, + read_size, + is_last_read_loc); } /* helper to configure address register values */ @@ -303,24 +241,6 @@ static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read, i /* * Helper to prepare DMA descriptors for configuring registers - * before reading a NAND page. - */ -static void config_nand_page_read(struct nand_chip *chip) -{ - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - - qcom_write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0); - qcom_write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); - if (!nandc->props->qpic_version2) - qcom_write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, 0); - qcom_write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_clr, - NAND_ERASED_CW_DETECT_CFG, 1, 0); - qcom_write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_set, - NAND_ERASED_CW_DETECT_CFG, 1, NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); -} - -/* - * Helper to prepare DMA descriptors for configuring registers * before reading each codeword in NAND page. */ static void @@ -328,14 +248,16 @@ config_nand_cw_read(struct nand_chip *chip, bool use_ecc, int cw) { struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); struct nand_ecc_ctrl *ecc = &chip->ecc; - __le32 *reg = &nandc->regs->read_location0; + int reg_base = NAND_READ_LOCATION_0; - if (nandc->props->qpic_version2 && qcom_nandc_is_last_cw(ecc, cw)) + if (nandc->props->qpic_version2 && qcom_nandc_is_last_cw(ecc, cw)) { reg = &nandc->regs->read_location_last0; + reg_base = NAND_READ_LOCATION_LAST_CW_0; + } if (nandc->props->supports_bam) - qcom_write_reg_dma(nandc, reg, NAND_READ_LOCATION_0, 4, NAND_BAM_NEXT_SGL); + qcom_write_reg_dma(nandc, reg, reg_base, 4, NAND_BAM_NEXT_SGL); qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); @@ -357,41 +279,10 @@ static void config_nand_single_cw_page_read(struct nand_chip *chip, bool use_ecc, int cw) { - config_nand_page_read(chip); - config_nand_cw_read(chip, use_ecc, cw); -} - -/* - * Helper to prepare DMA descriptors used to configure registers needed for - * before writing a NAND page. - */ -static void config_nand_page_write(struct nand_chip *chip) -{ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - qcom_write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0); - qcom_write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); - if (!nandc->props->qpic_version2) - qcom_write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, - NAND_BAM_NEXT_SGL); -} - -/* - * Helper to prepare DMA descriptors for configuring registers - * before writing each codeword in NAND page. - */ -static void config_nand_cw_write(struct nand_chip *chip) -{ - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - - qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); - qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); - - qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); - - qcom_write_reg_dma(nandc, &nandc->regs->clrflashstatus, NAND_FLASH_STATUS, 1, 0); - qcom_write_reg_dma(nandc, &nandc->regs->clrreadstatus, NAND_READ_STATUS, 1, - NAND_BAM_NEXT_SGL); + qcom_nandc_config_page_read(nandc); + config_nand_cw_read(chip, use_ecc, cw); } /* @@ -442,31 +333,6 @@ static bool erased_chunk_check_and_fixup(u8 *data_buf, int data_len) return true; } -struct read_stats { - __le32 flash; - __le32 buffer; - __le32 erased_cw; -}; - -/* reads back FLASH_STATUS register set by the controller */ -static int check_flash_errors(struct qcom_nand_host *host, int cw_cnt) -{ - struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - int i; - - qcom_nandc_dev_to_mem(nandc, true); - - for (i = 0; i < cw_cnt; i++) { - u32 flash = le32_to_cpu(nandc->reg_read_buf[i]); - - if (flash & (FS_OP_ERR | FS_MPU_ERR)) - return -EIO; - } - - return 0; -} - /* performs raw read for one codeword */ static int qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip, @@ -491,7 +357,7 @@ qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip, qcom_clear_bam_transaction(nandc); set_address(host, host->cw_size * cw, page); update_rw_regs(host, 1, true, raw_cw); - config_nand_page_read(chip); + qcom_nandc_config_page_read(nandc); data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); oob_size1 = host->bbm_size; @@ -538,7 +404,7 @@ qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip, return ret; } - return check_flash_errors(host, 1); + return qcom_nandc_check_flash_errors(nandc, 1); } /* @@ -624,12 +490,12 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, struct mtd_info *mtd = nand_to_mtd(chip); struct nand_ecc_ctrl *ecc = &chip->ecc; unsigned int max_bitflips = 0, uncorrectable_cws = 0; - struct read_stats *buf; + struct qcom_nandc_read_stats *buf; bool flash_op_err = false, erased; int i; u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf; - buf = (struct read_stats *)nandc->reg_read_buf; + buf = (struct qcom_nandc_read_stats *)nandc->reg_read_buf; qcom_nandc_dev_to_mem(nandc, true); for (i = 0; i < ecc->steps; i++, buf++) { @@ -728,7 +594,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf; int i, ret; - config_nand_page_read(chip); + qcom_nandc_config_page_read(nandc); /* queue cmd descs for each codeword */ for (i = 0; i < ecc->steps; i++) { @@ -990,7 +856,7 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const u8 *buf, host->use_ecc = true; update_rw_regs(host, ecc->steps, false, 0); - config_nand_page_write(chip); + qcom_nandc_config_page_write(nandc); for (i = 0; i < ecc->steps; i++) { int data_size, oob_size; @@ -1021,7 +887,7 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const u8 *buf, oob_buf, oob_size, 0); } - config_nand_cw_write(chip); + qcom_nandc_config_cw_write(nandc); data_buf += data_size; oob_buf += oob_size; @@ -1060,7 +926,7 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip, host->use_ecc = false; update_rw_regs(host, ecc->steps, false, 0); - config_nand_page_write(chip); + qcom_nandc_config_page_write(nandc); for (i = 0; i < ecc->steps; i++) { int data_size1, data_size2, oob_size1, oob_size2; @@ -1097,7 +963,7 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip, qcom_write_data_dma(nandc, reg_off, oob_buf, oob_size2, 0); oob_buf += oob_size2; - config_nand_cw_write(chip); + qcom_nandc_config_cw_write(nandc); } ret = qcom_submit_descs(nandc); @@ -1144,10 +1010,10 @@ static int qcom_nandc_write_oob(struct nand_chip *chip, int page) set_address(host, host->cw_size * (ecc->steps - 1), page); update_rw_regs(host, 1, false, 0); - config_nand_page_write(chip); + qcom_nandc_config_page_write(nandc); qcom_write_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, data_size + oob_size, 0); - config_nand_cw_write(chip); + qcom_nandc_config_cw_write(nandc); ret = qcom_submit_descs(nandc); if (ret) { @@ -1181,7 +1047,7 @@ static int qcom_nandc_block_bad(struct nand_chip *chip, loff_t ofs) if (ret) goto err; - if (check_flash_errors(host, 1)) { + if (qcom_nandc_check_flash_errors(nandc, 1)) { dev_warn(nandc->dev, "error when trying to read BBM\n"); goto err; } @@ -1220,10 +1086,10 @@ static int qcom_nandc_block_markbad(struct nand_chip *chip, loff_t ofs) set_address(host, host->cw_size * (ecc->steps - 1), page); update_rw_regs(host, 1, false, ecc->steps - 1); - config_nand_page_write(chip); + qcom_nandc_config_page_write(nandc); qcom_write_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, host->cw_size, 0); - config_nand_cw_write(chip); + qcom_nandc_config_cw_write(nandc); ret = qcom_submit_descs(nandc); if (ret) { @@ -1921,9 +1787,9 @@ static int qcom_param_page_type_exec(struct nand_chip *chip, const struct nand_ len = nand_subop_get_data_len(subop, op_id); if (nandc->props->qpic_version2) - nandc_set_read_loc_last(chip, reg_base, 0, len, 1); + qcom_nandc_set_read_loc_last(nandc, reg_base, 0, len, 1); else - nandc_set_read_loc_first(chip, reg_base, 0, len, 1); + qcom_nandc_set_read_loc_first(nandc, reg_base, 0, len, 1); if (!nandc->props->has_onfi_read_op) { qcom_write_reg_dma(nandc, &nandc->regs->vld, NAND_DEV_CMD_VLD, 1, 0); diff --git a/drivers/mtd/nand/raw/sh_flctl.c b/drivers/mtd/nand/raw/sh_flctl.c index 97f733e481ff..0497801d7059 100644 --- a/drivers/mtd/nand/raw/sh_flctl.c +++ b/drivers/mtd/nand/raw/sh_flctl.c @@ -378,6 +378,7 @@ static int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf, int len, enum dma_data_direction dir) { struct dma_async_tx_descriptor *desc = NULL; + struct device *dma_dev; struct dma_chan *chan; enum dma_transfer_direction tr_dir; dma_addr_t dma_addr; @@ -394,9 +395,10 @@ static int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf, tr_dir = DMA_MEM_TO_DEV; } - dma_addr = dma_map_single(chan->device->dev, buf, len, dir); + dma_dev = dmaengine_get_dma_device(chan); + dma_addr = dma_map_single(dma_dev, buf, len, dir); - if (!dma_mapping_error(chan->device->dev, dma_addr)) + if (!dma_mapping_error(dma_dev, dma_addr)) desc = dmaengine_prep_slave_single(chan, dma_addr, len, tr_dir, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); @@ -440,7 +442,7 @@ out: reg &= ~DREQ0EN; writel(reg, FLINTDMACR(flctl)); - dma_unmap_single(chan->device->dev, dma_addr, len, dir); + dma_unmap_single(dma_dev, dma_addr, len, dir); /* ret == 0 is success */ return ret; diff --git a/drivers/mtd/nand/raw/stm32_fmc2_nand.c b/drivers/mtd/nand/raw/stm32_fmc2_nand.c index c08d6b176372..9f42f8ab3212 100644 --- a/drivers/mtd/nand/raw/stm32_fmc2_nand.c +++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c @@ -2076,7 +2076,7 @@ static void stm32_fmc2_nfc_remove(struct platform_device *pdev) stm32_fmc2_nfc_wp_enable(nand); } -static int __maybe_unused stm32_fmc2_nfc_suspend(struct device *dev) +static int stm32_fmc2_nfc_suspend(struct device *dev) { struct stm32_fmc2_nfc *nfc = dev_get_drvdata(dev); struct stm32_fmc2_nand *nand = &nfc->nand; @@ -2090,7 +2090,7 @@ static int __maybe_unused stm32_fmc2_nfc_suspend(struct device *dev) return 0; } -static int __maybe_unused stm32_fmc2_nfc_resume(struct device *dev) +static int stm32_fmc2_nfc_resume(struct device *dev) { struct stm32_fmc2_nfc *nfc = dev_get_drvdata(dev); struct stm32_fmc2_nand *nand = &nfc->nand; @@ -2118,7 +2118,7 @@ static int __maybe_unused stm32_fmc2_nfc_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(stm32_fmc2_nfc_pm_ops, stm32_fmc2_nfc_suspend, +static DEFINE_SIMPLE_DEV_PM_OPS(stm32_fmc2_nfc_pm_ops, stm32_fmc2_nfc_suspend, stm32_fmc2_nfc_resume); static const struct stm32_fmc2_nfc_data stm32_fmc2_nfc_mp1_data = { @@ -2153,7 +2153,7 @@ static struct platform_driver stm32_fmc2_nfc_driver = { .driver = { .name = "stm32_fmc2_nfc", .of_match_table = stm32_fmc2_nfc_match, - .pm = &stm32_fmc2_nfc_pm_ops, + .pm = pm_sleep_ptr(&stm32_fmc2_nfc_pm_ops), }, }; module_platform_driver(stm32_fmc2_nfc_driver); diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 45ccbce91551..f41feebe4257 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -222,6 +222,7 @@ * USER_DATA_LEN registers. */ #define USER_DATA_SZ 4 +#define SUNXI_NFC_MAX_USER_DATA_SZ 32 /** * struct sunxi_nand_chip_sel - stores information related to NAND Chip Select @@ -1003,11 +1004,10 @@ static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand, struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, step); - u8 *user_data = NULL; + u8 user_data[SUNXI_NFC_MAX_USER_DATA_SZ] = {}; /* Randomize the Bad Block Marker. */ if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) { - user_data = kmalloc(user_data_sz, GFP_KERNEL); memcpy(user_data, oob, user_data_sz); sunxi_nfc_randomize_bbm(nand, page, user_data); oob = user_data; @@ -1040,7 +1040,6 @@ static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand, } } - kfree(user_data); } static void sunxi_nfc_hw_ecc_update_stats(struct nand_chip *nand, @@ -1991,7 +1990,7 @@ static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section, struct nand_chip *nand = mtd_to_nand(mtd); struct nand_ecc_ctrl *ecc = &nand->ecc; struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); - unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, section); + unsigned int user_data_sz; /* * The controller does not provide access to OOB bytes @@ -2000,6 +1999,8 @@ static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section, if (section >= ecc->steps) return -ERANGE; + user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, section); + /* * The first 2 bytes are used for BB markers, hence we * only have user_data_sz - 2 bytes available in the first user data @@ -2007,12 +2008,12 @@ static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section, */ if (section == 0) { oobregion->offset = 2; - oobregion->length = user_data_sz - 2; + oobregion->length = user_data_sz > 2 ? user_data_sz - 2 : 0; return 0; } - oobregion->offset = sunxi_get_ecc_offset(sunxi_nand, ecc, section); + oobregion->offset = sunxi_get_oob_offset(sunxi_nand, ecc, section); oobregion->length = user_data_sz; return 0; @@ -2041,6 +2042,9 @@ static int sunxi_nfc_maximize_user_data(struct nand_chip *nand, uint32_t oobsize int remaining_bytes = oobsize - (ecc_bytes * nsectors); int i, step; + if (nsectors <= 0) + return -EINVAL; + sunxi_nand->user_data_bytes = devm_kzalloc(nfc->dev, nsectors, GFP_KERNEL); if (!sunxi_nand->user_data_bytes) @@ -2056,6 +2060,8 @@ static int sunxi_nfc_maximize_user_data(struct nand_chip *nand, uint32_t oobsize if (sunxi_nand->user_data_bytes[step] == 0) break; } + if (sunxi_nand->user_data_bytes[0] < USER_DATA_SZ) + return -EINVAL; return 0; } @@ -2072,11 +2078,15 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, int total_user_data_sz = 0; int nsectors; int ecc_mode; + int ret; int i; if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) { int bytes = mtd->oobsize; + if (mtd->writesize < 1024) + return -EINVAL; + ecc->size = 1024; nsectors = mtd->writesize / ecc->size; @@ -2102,10 +2112,10 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, bytes -= total_user_data_sz; } else { /* - * remove at least the BBM size before computing the - * max ECC + * User-data lengths are encoded in four-byte units. Reserve + * the first word because it contains the two BBM bytes. */ - bytes -= 2; + bytes -= USER_DATA_SZ; } /* @@ -2169,9 +2179,12 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, * The rationale for variable data length is to prioritize maximum ECC * strength, and then use the remaining space for user data. */ - if (nfc->caps->reg_user_data_len) - sunxi_nfc_maximize_user_data(nand, mtd->oobsize, ecc->bytes, - nsectors); + if (nfc->caps->reg_user_data_len) { + ret = sunxi_nfc_maximize_user_data(nand, mtd->oobsize, + ecc->bytes, nsectors); + if (ret) + return ret; + } if (total_user_data_sz == 0) for (i = 0; i < nsectors; i++) diff --git a/drivers/mtd/nand/raw/vf610_nfc.c b/drivers/mtd/nand/raw/vf610_nfc.c index 9940681810cf..1b9b370adfab 100644 --- a/drivers/mtd/nand/raw/vf610_nfc.c +++ b/drivers/mtd/nand/raw/vf610_nfc.c @@ -505,6 +505,11 @@ static int vf610_nfc_exec_op(struct nand_chip *chip, check_only); } +static unsigned int vf610_nfc_spare_size(struct mtd_info *mtd) +{ + return min_t(unsigned int, mtd->oobsize, 64); +} + static inline int vf610_nfc_correct_data(struct nand_chip *chip, uint8_t *dat, uint8_t *oob, int page) { @@ -514,6 +519,7 @@ static inline int vf610_nfc_correct_data(struct nand_chip *chip, uint8_t *dat, u8 ecc_status; u8 ecc_count; int flips_threshold = nfc->chip.ecc.strength / 2; + int ret; ecc_status = vf610_nfc_read(nfc, ecc_status_off) & 0xff; ecc_count = ecc_status & ECC_STATUS_ERR_COUNT; @@ -521,16 +527,22 @@ static inline int vf610_nfc_correct_data(struct nand_chip *chip, uint8_t *dat, if (!(ecc_status & ECC_STATUS_MASK)) return ecc_count; + /* The failed decode leaves a bogus correction in SRAM; re-read without ECC */ nfc->data_access = true; - nand_read_oob_op(&nfc->chip, page, 0, oob, mtd->oobsize); + ret = nand_read_page_op(&nfc->chip, page, 0, dat, mtd->writesize); + if (!ret) + ret = nand_read_oob_op(&nfc->chip, page, 0, oob, + vf610_nfc_spare_size(mtd)); nfc->data_access = false; + if (ret) + return ret; /* - * On an erased page, bit count (including OOB) should be zero or - * at least less then half of the ECC strength. + * Run the erased-page check with the driver's historic threshold + * of half the ECC strength. */ - return nand_check_erased_ecc_chunk(dat, nfc->chip.ecc.size, oob, - mtd->oobsize, NULL, 0, + return nand_check_erased_ecc_chunk(dat, mtd->writesize, oob, + vf610_nfc_spare_size(mtd), NULL, 0, flips_threshold); } @@ -551,7 +563,7 @@ static int vf610_nfc_read_page(struct nand_chip *chip, uint8_t *buf, { struct vf610_nfc *nfc = chip_to_nfc(chip); struct mtd_info *mtd = nand_to_mtd(chip); - int trfr_sz = mtd->writesize + mtd->oobsize; + int trfr_sz = mtd->writesize + vf610_nfc_spare_size(mtd); u32 row = 0, cmd1 = 0, cmd2 = 0, code = 0; int stat; @@ -577,11 +589,16 @@ static int vf610_nfc_read_page(struct nand_chip *chip, uint8_t *buf, */ vf610_nfc_rd_from_sram(buf, nfc->regs + NFC_MAIN_AREA(0), mtd->writesize, false); - if (oob_required) + if (oob_required) { + unsigned int spare = vf610_nfc_spare_size(mtd); + vf610_nfc_rd_from_sram(chip->oob_poi, nfc->regs + NFC_MAIN_AREA(0) + mtd->writesize, - mtd->oobsize, false); + spare, false); + /* Not transferred, and never written: reads back erased */ + memset(chip->oob_poi + spare, 0xff, mtd->oobsize - spare); + } stat = vf610_nfc_correct_data(chip, buf, chip->oob_poi, page); @@ -599,7 +616,7 @@ static int vf610_nfc_write_page(struct nand_chip *chip, const uint8_t *buf, { struct vf610_nfc *nfc = chip_to_nfc(chip); struct mtd_info *mtd = nand_to_mtd(chip); - int trfr_sz = mtd->writesize + mtd->oobsize; + int trfr_sz = mtd->writesize + vf610_nfc_spare_size(mtd); u32 row = 0, cmd1 = 0, cmd2 = 0, code = 0; u8 status; int ret; @@ -740,6 +757,49 @@ static void vf610_nfc_init_controller(struct vf610_nfc *nfc) } } +/* + * With 64 byte OOB chips the core's large page layout matches what + * U-Boot uses, and on chips with more, U-Boot and older kernels clamped + * mtd->oobsize to 64. Modifying the OOB size is no longer possible, the + * actual chip geometry must be respected, so to avoid breaking those + * existing setups use our own layout: the core's large page one, + * computed over the first 64 spare bytes only. + */ +static int vf610_nfc_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_device *nand = mtd_to_nanddev(mtd); + unsigned int total_ecc_bytes = nand->ecc.ctx.total; + + if (section || !total_ecc_bytes) + return -ERANGE; + + oobregion->length = total_ecc_bytes; + oobregion->offset = vf610_nfc_spare_size(mtd) - oobregion->length; + + return 0; +} + +static int vf610_nfc_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_device *nand = mtd_to_nanddev(mtd); + unsigned int total_ecc_bytes = nand->ecc.ctx.total; + + if (section) + return -ERANGE; + + oobregion->length = vf610_nfc_spare_size(mtd) - total_ecc_bytes - 2; + oobregion->offset = 2; + + return 0; +} + +static const struct mtd_ooblayout_ops vf610_nfc_ooblayout_ops = { + .ecc = vf610_nfc_ooblayout_ecc, + .free = vf610_nfc_ooblayout_free, +}; + static int vf610_nfc_attach_chip(struct nand_chip *chip) { struct mtd_info *mtd = nand_to_mtd(chip); @@ -770,12 +830,7 @@ static int vf610_nfc_attach_chip(struct nand_chip *chip) return -ENXIO; } - /* Only 64 byte ECC layouts known */ - if (mtd->oobsize > 64) - mtd->oobsize = 64; - - /* Use default large page ECC layout defined in NAND core */ - mtd_set_ooblayout(mtd, nand_get_large_page_ooblayout()); + mtd_set_ooblayout(mtd, &vf610_nfc_ooblayout_ops); if (chip->ecc.strength == 32) { nfc->ecc_mode = ECC_60_BYTE; chip->ecc.bytes = 60; diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile index b5ccb44860df..f6b2a094fafc 100644 --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 spinand-objs := core.o otp.o spinand-objs += alliancememory.o ato.o dosilicon.o esmt.o fmsh.o foresee.o gigadevice.o heyangtek.o -spinand-objs += macronix.o micron.o paragon.o skyhigh.o toshiba.o winbond.o xtx.o +spinand-objs += issi.o macronix.o micron.o paragon.o skyhigh.o toshiba.o winbond.o xtx.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 8bf9301f25e7..133776ba53da 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -281,8 +281,26 @@ static int spinand_init_cfg_cache(struct spinand_device *spinand) static int spinand_init_quad_enable(struct spinand_device *spinand, bool enable) { - return spinand_upd_cfg(spinand, CFG_QUAD_ENABLE, - enable ? CFG_QUAD_ENABLE : 0); + struct nand_device *nand = spinand_to_nand(spinand); + unsigned int target; + int ret; + + /* + * QE is a per-die setting on some devices. Program each target + * individually when enabling or disabling quad I/O mode. + */ + for (target = 0; target < nand->memorg.ntargets; target++) { + ret = spinand_select_target(spinand, target); + if (ret) + return ret; + + ret = spinand_upd_cfg(spinand, CFG_QUAD_ENABLE, + enable ? CFG_QUAD_ENABLE : 0); + if (ret) + return ret; + } + + return 0; } static int spinand_ecc_enable(struct spinand_device *spinand, @@ -508,8 +526,7 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand, else rdesc->info.op_tmpl = &rdesc->info.primary_op_tmpl; - if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED && - req->mode != MTD_OPS_RAW) + if (nand_ecc_is_pipelined(nand) && req->mode != MTD_OPS_RAW) rdesc->info.op_tmpl->data.ecc = true; else rdesc->info.op_tmpl->data.ecc = false; @@ -603,8 +620,7 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand, wdesc = spinand->dirmaps[req->pos.plane].wdesc; - if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED && - req->mode != MTD_OPS_RAW) + if (nand_ecc_is_pipelined(nand) && req->mode != MTD_OPS_RAW) wdesc->info.op_tmpl->data.ecc = true; else wdesc->info.op_tmpl->data.ecc = false; @@ -1261,7 +1277,7 @@ static int spinand_create_dirmap(struct spinand_device *spinand, struct spi_mem_dirmap_desc *desc; bool enable_ecc = false, secondary_op = false; - if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED) + if (nand_ecc_is_pipelined(nand)) enable_ecc = true; if (spinand->cont_read_possible && spinand->op_templates->cont_read_cache) @@ -1360,6 +1376,7 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = { &foresee_spinand_manufacturer, &gigadevice_spinand_manufacturer, &heyangtek_spinand_manufacturer, + &issi_spinand_manufacturer, ¯onix_spinand_manufacturer, µn_spinand_manufacturer, ¶gon_spinand_manufacturer, @@ -1797,11 +1814,11 @@ try_ssdr: spinand->ssdr_op_templates.write_cache->data.buswidth == 4 || spinand->ssdr_op_templates.update_cache->data.buswidth == 4) quad_enable = true; - } - ret = spinand_init_quad_enable(spinand, quad_enable); - if (ret) - return ret; + ret = spinand_init_quad_enable(spinand, quad_enable); + if (ret) + return ret; + } if (spinand->configure_chip) { ret = spinand->configure_chip(spinand, SSDR); @@ -1809,7 +1826,7 @@ try_ssdr: return ret; } - return ret; + return 0; } static int spinand_init_flash(struct spinand_device *spinand) @@ -1980,12 +1997,17 @@ static int spinand_init(struct spinand_device *spinand) goto err_cleanup_ecc_engine; } - if (nand->ecc.engine) { - ret = mtd_ooblayout_count_freebytes(mtd); - if (ret < 0) - goto err_cleanup_ecc_engine; + if (!nand->ecc.engine) { + if (spinand->eccinfo.ooblayout) + mtd_set_ooblayout(mtd, spinand->eccinfo.ooblayout); + else + mtd_set_ooblayout(mtd, &spinand_noecc_ooblayout); } + ret = mtd_ooblayout_count_freebytes(mtd); + if (ret < 0) + goto err_cleanup_ecc_engine; + mtd->oobavail = ret; /* Propagate ECC information to mtd_info */ diff --git a/drivers/mtd/nand/spi/heyangtek.c b/drivers/mtd/nand/spi/heyangtek.c index 7fc50fd3de08..76d97ae9702c 100644 --- a/drivers/mtd/nand/spi/heyangtek.c +++ b/drivers/mtd/nand/spi/heyangtek.c @@ -35,8 +35,8 @@ static SPINAND_OP_VARIANTS(update_cache_variants, /* * HYF1GQ4UDACAE is a GD5F1GQ4-compatible die, so the OOB layout is taken * from gd5fxgq4xa: the on-die ECC parity occupies bytes 8..15 of each - * 16-byte section, the bad block marker sits in byte 0 and the remaining - * bytes are exposed as free. + * 16-byte section, and the remaining bytes are exposed as free, except the + * two bytes that spinand_isbad() reads as the bad block marker. */ static int hyf1gq4_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *region) @@ -60,9 +60,9 @@ static int hyf1gq4_ooblayout_free(struct mtd_info *mtd, int section, region->offset = 16 * section; region->length = 8; } else { - /* section 0 has one byte reserved for the bad block marker */ - region->offset = 1; - region->length = 7; + /* section 0 has two bytes reserved for the bad block marker */ + region->offset = 2; + region->length = 6; } return 0; diff --git a/drivers/mtd/nand/spi/issi.c b/drivers/mtd/nand/spi/issi.c new file mode 100644 index 000000000000..122b3a6e2f62 --- /dev/null +++ b/drivers/mtd/nand/spi/issi.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2026 ISSI + * + * Authors: + * Bill Lee <blee@issi.com> + * Jeff Kim <jekim@issi.com> + * Co-Author: + * Han Xu <han.xu@nxp.com> + */ + +#include <linux/bitfield.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/mtd/spinand.h> + +#define SPINAND_MFR_ISSI 0x9d + +#define ISSI_STATUS_ECC_MASK GENMASK(6, 4) +#define ISSI_STATUS_ECC_NO_BITFLIPS 0 +#define ISSI_STATUS_ECC_1TO3_BITFLIPS 1 +#define ISSI_STATUS_ECC_UNCOR_ERROR 2 +#define ISSI_STATUS_ECC_4TO6_BITFLIPS 3 +#define ISSI_STATUS_ECC_7TO8_BITFLIPS 5 + +/* + * As per datasheet, die selection is done by the 7th bit of Drive + * Strength Register (Address 0xD0). + */ +#define ISSI_DIE_SELECT_REG 0xD0 +#define ISSI_SELECT_DIE_MASK BIT(7) +#define ISSI_SELECT_DIE(x) ((x) << 7) + +static SPINAND_OP_VARIANTS( + quadio_read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); + +static SPINAND_OP_VARIANTS(x4_write_cache_variants, + SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(x4_update_cache_variants, + SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0)); + +static int issi_8_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section) + return -ERANGE; + + region->offset = mtd->oobsize / 2; + region->length = mtd->oobsize / 2; + + return 0; +} + +static int issi_8_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section) + return -ERANGE; + + /* Reserve 2 bytes for the BBM. */ + region->offset = 2; + region->length = (mtd->oobsize / 2) - 2; + + return 0; +} + +static const struct mtd_ooblayout_ops issi_8_ooblayout = { + .ecc = issi_8_ooblayout_ecc, + .free = issi_8_ooblayout_free, +}; + +static int issi_select_target(struct spinand_device *spinand, + unsigned int target) +{ + int ret; + u8 regval; + + if (target > 1) + return -EINVAL; + + ret = spinand_read_reg_op(spinand, ISSI_DIE_SELECT_REG, ®val); + if (ret) + return ret; + + regval &= ~ISSI_SELECT_DIE_MASK; + regval |= ISSI_SELECT_DIE(target); + + return spinand_write_reg_op(spinand, ISSI_DIE_SELECT_REG, regval); +} + +static int issi_8_ecc_get_status(struct spinand_device *spinand, u8 status) +{ + switch (FIELD_GET(ISSI_STATUS_ECC_MASK, status)) { + case ISSI_STATUS_ECC_NO_BITFLIPS: + return 0; + + case ISSI_STATUS_ECC_1TO3_BITFLIPS: + return 3; + + case ISSI_STATUS_ECC_4TO6_BITFLIPS: + return 6; + + case ISSI_STATUS_ECC_7TO8_BITFLIPS: + return 8; + + case ISSI_STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + default: + break; + } + + return -EINVAL; +} + +static const struct spinand_info issi_spinand_table[] = { + /* IS37/38SMW01G8B 1Gb 1.8V */ + SPINAND_INFO("IS37/38SMW01G8B", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x15), + NAND_MEMORG(1, 2048, 128, 64, 1024, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&issi_8_ooblayout, issi_8_ecc_get_status), + SPINAND_SELECT_TARGET(issi_select_target)), + + /* IS37/38SMW02G8B 2Gb 1.8V */ + SPINAND_INFO("IS37/38SMW02G8B", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x25), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&issi_8_ooblayout, issi_8_ecc_get_status), + SPINAND_SELECT_TARGET(issi_select_target)), + + /* IS37/38SML04G8B 4Gb 3.3V */ + SPINAND_INFO("IS37/38SML04G8", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x34), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 2), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&issi_8_ooblayout, issi_8_ecc_get_status), + SPINAND_SELECT_TARGET(issi_select_target)), + + /* IS37/38SMW04G8B 4Gb 1.8V */ + SPINAND_INFO("IS37/38SMW04G8B", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 2), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&issi_8_ooblayout, issi_8_ecc_get_status), + SPINAND_SELECT_TARGET(issi_select_target)), +}; + +static int issi_spinand_init(struct spinand_device *spinand) +{ + return 0; +} + +static const struct spinand_manufacturer_ops issi_spinand_manuf_ops = { + .init = issi_spinand_init, +}; + +const struct spinand_manufacturer issi_spinand_manufacturer = { + .id = SPINAND_MFR_ISSI, + .name = "ISSI", + .chips = issi_spinand_table, + .nchips = ARRAY_SIZE(issi_spinand_table), + .ops = &issi_spinand_manuf_ops, +}; diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c index 9b78c1e6cbc9..810975808c97 100644 --- a/drivers/mtd/nand/spi/winbond.c +++ b/drivers/mtd/nand/spi/winbond.c @@ -211,6 +211,13 @@ static SPINAND_OP_VARIANTS(read_cache_variants, SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); +static SPINAND_OP_VARIANTS(cont_read_cache_variants, + WINBOND_CONT_READ_FROM_CACHE_1S_4S_4S_OP(6, NULL, 0, 0), + WINBOND_CONT_READ_FROM_CACHE_1S_1S_4S_OP(4, NULL, 0, 0), + WINBOND_CONT_READ_FROM_CACHE_1S_2S_2S_OP(4, NULL, 0, 0), + WINBOND_CONT_READ_FROM_CACHE_1S_1S_2S_OP(4, NULL, 0, 0), + WINBOND_CONT_READ_FROM_CACHE_FAST_1S_1S_1S_OP(4, NULL, 0, 0)); + static SPINAND_OP_VARIANTS(write_cache_variants, SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0), SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0)); @@ -314,10 +321,18 @@ static int w25n01kv_ooblayout_ecc(struct mtd_info *mtd, int section, static int w25n02kv_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *region) { - if (section > 3) + int num_sections = 4; + int offset = 64; + + if (mtd->oobsize == 256) { + num_sections = 8; + offset = 128; + } + + if (section >= num_sections) return -ERANGE; - region->offset = 64 + (16 * section); + region->offset = offset + (16 * section); region->length = 13; return 0; @@ -326,9 +341,21 @@ static int w25n02kv_ooblayout_ecc(struct mtd_info *mtd, int section, static int w25n02kv_ooblayout_free(struct mtd_info *mtd, int section, struct mtd_oob_region *region) { - if (section > 3) + int num_sections = 4; + + if (mtd->oobsize == 256) + num_sections = 8; + + if (section >= num_sections) return -ERANGE; + /* + * For at least W25N04KW and W25N04LW, this region is actually + * split in two: + * ECC-protected "User Data I" at + 4 offset, length 12 + * unprotected "User Data II" at + 2 offset, length 2 + * This returns the full region for backwards compatibility. + */ region->offset = (16 * section) + 2; region->length = 14; @@ -345,6 +372,39 @@ static const struct mtd_ooblayout_ops w25n02kv_ooblayout = { .free = w25n02kv_ooblayout_free, }; +static int w25n08lw_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + /* + * With ecc enabled the parity bits are not accessible. So we can + * only see page + 128. Without ecc the full page + 256 is accessible. + * To make it simple just return the area as not accessible. + */ + return -ERANGE; +} + +static int w25n08lw_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 7) + return -ERANGE; + + /* + * Only expose the ECC protected "User Data I" bytes. The "User Data II" + * bytes at offset + 2 are not covered by the on-die ECC engine, and the + * first two bytes of the section hold the BBM. + */ + region->offset = (16 * section) + 4; + region->length = 12; + + return 0; +} + +static const struct mtd_ooblayout_ops w25n08lw_ooblayout = { + .ecc = w25n08lw_ooblayout_ecc, + .free = w25n08lw_ooblayout_free, +}; + static int w25n01jw_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *region) { @@ -768,6 +828,15 @@ static const struct spinand_info winbond_spinand_table[] = { &update_cache_variants), 0, SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)), + SPINAND_INFO("W25N04LW", /* 1.8V */ + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb2, 0x23), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)), SPINAND_INFO("W35N04JW", /* 1.8V */ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdf, 0x23), NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 4, 1), @@ -781,6 +850,18 @@ static const struct spinand_info winbond_spinand_table[] = { SPINAND_ECCINFO(&w35n01jw_ooblayout, w25w35nxxjw_ecc_get_status), SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg), SPINAND_CONT_READ(w35n0xjw_set_cont_read)), + /* 8G-bit densities */ + SPINAND_INFO("W25N08LW", /* 2x4G-bit 1.8V */ + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb3, 0x24), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 2, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_variants, + &write_cache_variants, + &update_cache_variants, + &cont_read_cache_variants), + 0, + SPINAND_ECCINFO(&w25n08lw_ooblayout, w25n02kv_ecc_get_status), + SPINAND_CONT_READ(w25n0xjw_set_cont_read)), }; static int winbond_spinand_init(struct spinand_device *spinand) diff --git a/drivers/mtd/parsers/qcomsmempart.c b/drivers/mtd/parsers/qcomsmempart.c index d4fdc46a0073..1591aea48b28 100644 --- a/drivers/mtd/parsers/qcomsmempart.c +++ b/drivers/mtd/parsers/qcomsmempart.c @@ -143,9 +143,9 @@ static int parse_qcomsmem_part(struct mtd_info *mtd, *c = tolower(*c); parts[j].name = name; - parts[j].offset = le32_to_cpu(pentry->offset) * mtd->erasesize; + parts[j].offset = (u64)le32_to_cpu(pentry->offset) * mtd->erasesize; parts[j].mask_flags = pentry->attr; - parts[j].size = le32_to_cpu(pentry->length) * mtd->erasesize; + parts[j].size = (u64)le32_to_cpu(pentry->length) * mtd->erasesize; pr_debug("%d: %s offs=0x%08x size=0x%08x attr:0x%08x\n", i, pentry->name, le32_to_cpu(pentry->offset), le32_to_cpu(pentry->length), pentry->attr); diff --git a/drivers/mtd/spi-nor/atmel.c b/drivers/mtd/spi-nor/atmel.c index 82c592f0a1e1..bccc702a2c4a 100644 --- a/drivers/mtd/spi-nor/atmel.c +++ b/drivers/mtd/spi-nor/atmel.c @@ -23,18 +23,28 @@ static int at25fs_nor_lock(struct spi_nor *nor, loff_t ofs, u64 len) static int at25fs_nor_unlock(struct spi_nor *nor, loff_t ofs, u64 len) { + /* Write 0x00 to the status register to disable write protection */ + u8 sr = 0; int ret; /* We only support unlocking the whole flash array */ if (ofs || len != nor->params->size) return -EINVAL; - /* Write 0x00 to the status register to disable write protection */ - ret = spi_nor_write_sr_and_check(nor, 0); + ret = spi_nor_write_sr1(nor, &sr); + if (ret) + return ret; + + ret = spi_nor_read_sr1(nor, &sr); if (ret) + return ret; + + if (sr) { dev_dbg(nor->dev, "unable to clear BP bits, WP# asserted?\n"); + return -EIO; + } - return ret; + return 0; } static int at25fs_nor_is_locked(struct spi_nor *nor, loff_t ofs, u64 len) @@ -50,6 +60,7 @@ static const struct spi_nor_locking_ops at25fs_nor_locking_ops = { static int at25fs_nor_late_init(struct spi_nor *nor) { + nor->params->opcodes.write_sr1 = SPINOR_OP_WRSR; nor->params->locking_ops = &at25fs_nor_locking_ops; return 0; @@ -69,6 +80,7 @@ static const struct spi_nor_fixups at25fs_nor_fixups = { * Return: 0 on success, -error otherwise. */ static int atmel_nor_set_global_protection(struct spi_nor *nor, loff_t ofs, + u64 len, bool is_protect) { int ret; @@ -78,19 +90,24 @@ static int atmel_nor_set_global_protection(struct spi_nor *nor, loff_t ofs, if (ofs || len != nor->params->size) return -EINVAL; - ret = spi_nor_read_sr(nor, nor->bouncebuf); + ret = spi_nor_read_sr1(nor, &sr); if (ret) return ret; - sr = nor->bouncebuf[0]; - /* SRWD bit needs to be cleared, otherwise the protection doesn't change */ if (sr & SR_SRWD) { sr &= ~SR_SRWD; - ret = spi_nor_write_sr_and_check(nor, sr); - if (ret) { - dev_dbg(nor->dev, "unable to clear SRWD bit, WP# asserted?\n"); + ret = spi_nor_write_sr1(nor, &sr); + if (ret) return ret; + + ret = spi_nor_read_sr1(nor, &sr); + if (ret) + return ret; + + if (sr & SR_SRWD) { + dev_dbg(nor->dev, "unable to clear SRWD bit, WP# asserted?\n"); + return -EIO; } } @@ -108,14 +125,7 @@ static int atmel_nor_set_global_protection(struct spi_nor *nor, loff_t ofs, sr &= ~ATMEL_SR_GLOBAL_PROTECT_MASK; } - nor->bouncebuf[0] = sr; - - /* - * We cannot use the spi_nor_write_sr_and_check() because this command - * isn't really setting any bits, instead it is an pseudo command for - * "Global Unprotect" or "Global Protect" - */ - return spi_nor_write_sr(nor, nor->bouncebuf, 1); + return spi_nor_write_sr1(nor, &sr); } static int atmel_nor_global_protect(struct spi_nor *nor, loff_t ofs, u64 len) @@ -131,16 +141,17 @@ static int atmel_nor_global_unprotect(struct spi_nor *nor, loff_t ofs, u64 len) static int atmel_nor_is_global_protected(struct spi_nor *nor, loff_t ofs, u64 len) { + u8 sr; int ret; if (ofs >= nor->params->size || (ofs + len) > nor->params->size) return -EINVAL; - ret = spi_nor_read_sr(nor, nor->bouncebuf); + ret = spi_nor_read_sr1(nor, &sr); if (ret) return ret; - return ((nor->bouncebuf[0] & ATMEL_SR_GLOBAL_PROTECT_MASK) == ATMEL_SR_GLOBAL_PROTECT_MASK); + return ((sr & ATMEL_SR_GLOBAL_PROTECT_MASK) == ATMEL_SR_GLOBAL_PROTECT_MASK); } static const struct spi_nor_locking_ops atmel_nor_global_protection_ops = { @@ -151,6 +162,7 @@ static const struct spi_nor_locking_ops atmel_nor_global_protection_ops = { static int atmel_nor_global_protection_late_init(struct spi_nor *nor) { + nor->params->opcodes.write_sr1 = SPINOR_OP_WRSR; nor->params->locking_ops = &atmel_nor_global_protection_ops; return 0; @@ -182,47 +194,40 @@ static const struct flash_info atmel_nor_parts[] = { .size = SZ_512K, .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, .no_sfdp_flags = SECT_4K, - .fixups = &atmel_nor_global_protection_fixups, }, { .id = SNOR_ID(0x1f, 0x45, 0x01), .name = "at26df081a", .size = SZ_1M, .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, .no_sfdp_flags = SECT_4K, - .fixups = &atmel_nor_global_protection_fixups }, { .id = SNOR_ID(0x1f, 0x46, 0x01), .name = "at26df161a", .size = SZ_2M, .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, .no_sfdp_flags = SECT_4K, - .fixups = &atmel_nor_global_protection_fixups }, { .id = SNOR_ID(0x1f, 0x47, 0x00), .name = "at25df321", .size = SZ_4M, .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, .no_sfdp_flags = SECT_4K, - .fixups = &atmel_nor_global_protection_fixups }, { .id = SNOR_ID(0x1f, 0x47, 0x01), .name = "at25df321a", .size = SZ_4M, .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, .no_sfdp_flags = SECT_4K, - .fixups = &atmel_nor_global_protection_fixups }, { .id = SNOR_ID(0x1f, 0x47, 0x08), .name = "at25ff321a", .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, - .fixups = &atmel_nor_global_protection_fixups }, { .id = SNOR_ID(0x1f, 0x48, 0x00), .name = "at25df641", .size = SZ_8M, .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, .no_sfdp_flags = SECT_4K, - .fixups = &atmel_nor_global_protection_fixups }, { .id = SNOR_ID(0x1f, 0x66, 0x01), .name = "at25fs010", @@ -230,14 +235,12 @@ static const struct flash_info atmel_nor_parts[] = { .size = SZ_128K, .flags = SPI_NOR_HAS_LOCK, .no_sfdp_flags = SECT_4K, - .fixups = &at25fs_nor_fixups }, { .id = SNOR_ID(0x1f, 0x66, 0x04), .name = "at25fs040", .size = SZ_512K, .flags = SPI_NOR_HAS_LOCK, .no_sfdp_flags = SECT_4K, - .fixups = &at25fs_nor_fixups }, { .id = SNOR_ID(0x1f, 0x87, 0x01), .size = SZ_4M, @@ -245,8 +248,22 @@ static const struct flash_info atmel_nor_parts[] = { }, }; +static const struct spi_nor_fixup atmel_fixups[] = { + { .id = SNOR_ID(0x1f, 0x44, 0x01), .fixups = &atmel_nor_global_protection_fixups }, + { .id = SNOR_ID(0x1f, 0x45, 0x01), .fixups = &atmel_nor_global_protection_fixups }, + { .id = SNOR_ID(0x1f, 0x46, 0x01), .fixups = &atmel_nor_global_protection_fixups }, + { .id = SNOR_ID(0x1f, 0x47, 0x00), .fixups = &atmel_nor_global_protection_fixups }, + { .id = SNOR_ID(0x1f, 0x47, 0x01), .fixups = &atmel_nor_global_protection_fixups }, + { .id = SNOR_ID(0x1f, 0x47, 0x08), .fixups = &atmel_nor_global_protection_fixups }, + { .id = SNOR_ID(0x1f, 0x48, 0x00), .fixups = &atmel_nor_global_protection_fixups }, + { .id = SNOR_ID(0x1f, 0x66, 0x01), .fixups = &at25fs_nor_fixups }, + { .id = SNOR_ID(0x1f, 0x66, 0x04), .fixups = &at25fs_nor_fixups }, +}; + const struct spi_nor_manufacturer spi_nor_atmel = { .name = "atmel", .parts = atmel_nor_parts, .nparts = ARRAY_SIZE(atmel_nor_parts), + .fixups = atmel_fixups, + .nfixups = ARRAY_SIZE(atmel_fixups), }; diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index ccf4396cdcd0..90524c58602e 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -59,7 +59,7 @@ static u8 spi_nor_get_cmd_ext(const struct spi_nor *nor, const struct spi_mem_op *op) { - switch (nor->cmd_ext_type) { + switch (nor->params->cmd_ext_type) { case SPI_NOR_EXT_INVERT: return ~op->cmd.opcode; @@ -83,6 +83,7 @@ void spi_nor_spimem_setup_op(const struct spi_nor *nor, struct spi_mem_op *op, const enum spi_nor_protocol proto) { + struct spi_nor_flash_parameter *params = nor->params; u8 ext; op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(proto); @@ -116,7 +117,7 @@ void spi_nor_spimem_setup_op(const struct spi_nor *nor, op->cmd.nbytes = 2; } - if (proto == SNOR_PROTO_8_8_8_DTR && nor->flags & SNOR_F_SWAP16) + if (proto == SNOR_PROTO_8_8_8_DTR && params->flags & SNOR_F_SWAP16) op->data.swap16 = true; } @@ -444,75 +445,6 @@ int spi_nor_read_id(struct spi_nor *nor, u8 naddr, u8 ndummy, u8 *id, } /** - * spi_nor_read_sr() - Read the Status Register. - * @nor: pointer to 'struct spi_nor'. - * @sr: pointer to a DMA-able buffer where the value of the - * Status Register will be written. Should be at least 2 bytes. - * - * Return: 0 on success, -errno otherwise. - */ -int spi_nor_read_sr(struct spi_nor *nor, u8 *sr) -{ - int ret; - - if (nor->spimem) { - struct spi_mem_op op = SPI_NOR_RDSR_OP(sr); - - if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) { - op.addr.nbytes = nor->params->rdsr_addr_nbytes; - op.dummy.nbytes = nor->params->rdsr_dummy; - /* - * We don't want to read only one byte in DTR mode. So, - * read 2 and then discard the second byte. - */ - op.data.nbytes = 2; - } - - spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); - - ret = spi_mem_exec_op(nor->spimem, &op); - } else { - ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_RDSR, sr, - 1); - } - - if (ret) - dev_dbg(nor->dev, "error %d reading SR\n", ret); - - return ret; -} - -/** - * spi_nor_read_cr() - Read the Configuration Register using the - * SPINOR_OP_RDCR (35h) command. - * @nor: pointer to 'struct spi_nor' - * @cr: pointer to a DMA-able buffer where the value of the - * Configuration Register will be written. - * - * Return: 0 on success, -errno otherwise. - */ -int spi_nor_read_cr(struct spi_nor *nor, u8 *cr) -{ - int ret; - - if (nor->spimem) { - struct spi_mem_op op = SPI_NOR_RDCR_OP(cr); - - spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); - - ret = spi_mem_exec_op(nor->spimem, &op); - } else { - ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_RDCR, cr, - 1); - } - - if (ret) - dev_dbg(nor->dev, "error %d reading CR\n", ret); - - return ret; -} - -/** * spi_nor_set_4byte_addr_mode_en4b_ex4b() - Enter/Exit 4-byte address mode * using SPINOR_OP_EN4B/SPINOR_OP_EX4B. Typically used by * Winbond and Macronix. @@ -617,12 +549,13 @@ int spi_nor_set_4byte_addr_mode_brwr(struct spi_nor *nor, bool enable) int spi_nor_sr_ready(struct spi_nor *nor) { int ret; + u8 sr; - ret = spi_nor_read_sr(nor, nor->bouncebuf); + ret = spi_nor_read_sr1(nor, &sr); if (ret) return ret; - return !(nor->bouncebuf[0] & SR_WIP); + return !(sr & SR_WIP); } /** @@ -633,7 +566,7 @@ int spi_nor_sr_ready(struct spi_nor *nor) */ static bool spi_nor_use_parallel_locking(struct spi_nor *nor) { - return nor->flags & SNOR_F_RWW; + return nor->params->flags & SNOR_F_RWW; } /* Locking helpers for status read operations */ @@ -784,343 +717,294 @@ int spi_nor_global_block_unlock(struct spi_nor *nor) } /** - * spi_nor_write_sr() - Write the Status Register. + * spi_nor_read_sr_ll() - Low-level Status Registers read. * @nor: pointer to 'struct spi_nor'. - * @sr: pointer to DMA-able buffer to write to the Status Register. - * @len: number of bytes to write to the Status Register. + * @opcode: opcode for the status register read operation. + * @sr: pointer to buffer for storing the content of the status registers. + * @len: number of status registers to read (1 or 2). * * Return: 0 on success, -errno otherwise. */ -int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len) +int spi_nor_read_sr_ll(struct spi_nor *nor, u8 opcode, u8 *sr, + unsigned int len) { - int ret; + int ret, i; - ret = spi_nor_write_enable(nor); - if (ret) - return ret; + if (len > 2) + return -EINVAL; + + for (i = 0; i < len; i++) + nor->bouncebuf[i] = 0; if (nor->spimem) { - struct spi_mem_op op = SPI_NOR_WRSR_OP(sr, len); + struct spi_mem_op op = SPI_NOR_RDSR_OP(opcode, nor->bouncebuf, len); + + if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) { + op.addr.nbytes = nor->params->rdsr_addr_nbytes; + op.dummy.nbytes = nor->params->rdsr_dummy; + /* + * We don't want to read only one byte in DTR mode. So, + * read 2 and then discard the second byte. + */ + op.data.nbytes = 2; + } 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_WRSR, sr, - len); + ret = spi_nor_controller_ops_read_reg(nor, opcode, nor->bouncebuf, len); } - if (ret) { - dev_dbg(nor->dev, "error %d writing SR\n", ret); - return ret; - } - - return spi_nor_wait_till_ready(nor); -} - -/** - * spi_nor_write_sr1_and_check() - Write one byte to the Status Register 1 and - * ensure that the byte written match the received value. - * @nor: pointer to a 'struct spi_nor'. - * @sr1: byte value to be written to the Status Register. - * - * Return: 0 on success, -errno otherwise. - */ -static int spi_nor_write_sr1_and_check(struct spi_nor *nor, u8 sr1) -{ - int ret; + memcpy(sr, nor->bouncebuf, len); - nor->bouncebuf[0] = sr1; - - ret = spi_nor_write_sr(nor, nor->bouncebuf, 1); - if (ret) - return ret; - - ret = spi_nor_read_sr(nor, nor->bouncebuf); if (ret) - return ret; + dev_dbg(nor->dev, "Error %d reading Status Registers\n", ret); - if (nor->bouncebuf[0] != sr1) { - dev_dbg(nor->dev, "SR1: read back test failed\n"); - return -EIO; - } - - return 0; + return ret; } /** - * spi_nor_write_16bit_sr_and_check() - Write the Status Register 1 and the - * Status Register 2 in one shot. Ensure that the byte written in the Status - * Register 1 match the received value, and that the 16-bit Write did not - * affect what was already in the Status Register 2. - * @nor: pointer to a 'struct spi_nor'. - * @sr1: byte value to be written to the Status Register 1. + * spi_nor_write_sr_ll() - Low-level Status Registers write. + * @nor: pointer to 'struct spi_nor'. + * @opcode: opcode for the status register write operation. + * @sr: pointer to status registers buffer to write. + * @len: number of status registers to write. * * Return: 0 on success, -errno otherwise. */ -static int spi_nor_write_16bit_sr_and_check(struct spi_nor *nor, u8 sr1) +static int spi_nor_write_sr_ll(struct spi_nor *nor, u8 opcode, const u8 *sr, + unsigned int len) { - int ret; - u8 *sr_cr = nor->bouncebuf; - u8 cr_written; - - /* Make sure we don't overwrite the contents of Status Register 2. */ - if (!(nor->flags & SNOR_F_NO_READ_CR)) { - ret = spi_nor_read_cr(nor, &sr_cr[1]); - if (ret) - return ret; - } else if ((spi_nor_get_protocol_width(nor->read_proto) == 4 || - spi_nor_get_protocol_width(nor->write_proto) == 4) && - nor->params->quad_enable) { - /* - * If the Status Register 2 Read command (35h) is not - * supported, we should at least be sure we don't - * change the value of the SR2 Quad Enable bit. - * - * When the Quad Enable method is set and the buswidth is 4, we - * can safely assume that the value of the QE bit is one, as a - * consequence of the nor->params->quad_enable() call. - * - * According to the JESD216 revB standard, BFPT DWORDS[15], - * bits 22:20, the 16-bit Write Status (01h) command is - * available just for the cases in which the QE bit is - * described in SR2 at BIT(1). - */ - sr_cr[1] = SR2_QUAD_EN_BIT1; - } else { - sr_cr[1] = 0; - } + int ret, i; - sr_cr[0] = sr1; + if (len > 2) + return -EINVAL; - ret = spi_nor_write_sr(nor, sr_cr, 2); + ret = spi_nor_write_enable(nor); if (ret) return ret; - ret = spi_nor_read_sr(nor, sr_cr); - if (ret) - return ret; + for (i = 0; i < len; i++) + nor->bouncebuf[i] = sr[i]; - if (sr1 != sr_cr[0]) { - dev_dbg(nor->dev, "SR: Read back test failed\n"); - return -EIO; - } + if (nor->spimem) { + struct spi_mem_op op = SPI_NOR_WRSR_OP(opcode, + nor->bouncebuf, len); - if (nor->flags & SNOR_F_NO_READ_CR) - return 0; + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); - cr_written = sr_cr[1]; + ret = spi_mem_exec_op(nor->spimem, &op); + } else { + ret = spi_nor_controller_ops_write_reg(nor, opcode, + nor->bouncebuf, len); + } - ret = spi_nor_read_cr(nor, &sr_cr[1]); - if (ret) + if (ret) { + dev_dbg(nor->dev, "Error %d writing Status Registers\n", ret); return ret; - - if (cr_written != sr_cr[1]) { - dev_dbg(nor->dev, "CR: read back test failed\n"); - return -EIO; } - return 0; + return spi_nor_wait_till_ready(nor); } /** - * spi_nor_write_16bit_cr_and_check() - Write the Status Register 1 and the - * Configuration Register in one shot. Ensure that the byte written in the - * Configuration Register match the received value, and that the 16-bit Write - * did not affect what was already in the Status Register 1. - * @nor: pointer to a 'struct spi_nor'. - * @cr: byte value to be written to the Configuration Register. + * spi_nor_read_sr1() - Read SR1 only + * Useful for: + * - The core to offer a generic "read SR1 and SR2" capability + * - Manufacturer drivers (since they know the chip SR layout) + * - Polling the BUSY bit in the core * - * Return: 0 on success, -errno otherwise. + * @nor: the spi_nor structure + * @sr1: pointer to a valid SR1 buffer + * + * Return 0 or errno. */ -int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr) +int spi_nor_read_sr1(struct spi_nor *nor, u8 *sr1) { - int ret; - u8 *sr_cr = nor->bouncebuf; - u8 sr_written; - - /* Keep the current value of the Status Register 1. */ - ret = spi_nor_read_sr(nor, sr_cr); - if (ret) - return ret; - - sr_cr[1] = cr; - - ret = spi_nor_write_sr(nor, sr_cr, 2); - if (ret) - return ret; - - sr_written = sr_cr[0]; - - ret = spi_nor_read_sr(nor, sr_cr); - if (ret) - return ret; - - if (sr_written != sr_cr[0]) { - dev_dbg(nor->dev, "SR: Read back test failed\n"); - return -EIO; - } - - if (nor->flags & SNOR_F_NO_READ_CR) - return 0; + return spi_nor_read_sr_ll(nor, nor->params->opcodes.read_sr1, sr1, 1); +} - ret = spi_nor_read_cr(nor, &sr_cr[1]); - if (ret) - return ret; +/** + * spi_nor_read_sr2() - Read SR2 only + * Useful for: + * - The core to offer a generic "read SR1 and SR2" capability + * - Manufacturer drivers (since they know the chip SR layout) + * - SR2 based OTP configuration + * + * @nor: the spi_nor structure + * @sr2: pointer to a valid SR2 buffer + * + * Return 0 or errno. + */ +int spi_nor_read_sr2(struct spi_nor *nor, u8 *sr2) +{ + struct spi_nor_flash_parameter *params = nor->params; - if (cr != sr_cr[1]) { - dev_dbg(nor->dev, "CR: read back test failed\n"); - return -EIO; - } + if (!params->opcodes.read_sr2) + return -EINVAL; - return 0; + return spi_nor_read_sr_ll(nor, params->opcodes.read_sr2, sr2, 1); } /** - * spi_nor_write_16bit_sr_cr_and_check() - Write the Status Register 1 and the - * Configuration Register in one shot. Ensure that the bytes written in both - * registers match the received value. - * @nor: pointer to a 'struct spi_nor'. - * @regs: two-byte array with values to be written to the status and - * configuration registers. + * spi_nor_read_sr1_and_sr2() - Read SR1 then SR2 + * General purpose helper, always safe to call. Will expectedly ignore + * SR2 on certain chips. * - * Return: 0 on success, -errno otherwise. + * @nor: the spi_nor structure + * @sr: pointer to a valid 2-byte array + * + * Return 0 or errno. */ -static int spi_nor_write_16bit_sr_cr_and_check(struct spi_nor *nor, const u8 *regs) +int spi_nor_read_sr1_and_sr2(struct spi_nor *nor, u8 *sr) { - u8 written_regs[2]; int ret; - written_regs[0] = regs[0]; - written_regs[1] = regs[1]; - nor->bouncebuf[0] = regs[0]; - nor->bouncebuf[1] = regs[1]; - - ret = spi_nor_write_sr(nor, nor->bouncebuf, 2); + ret = spi_nor_read_sr1(nor, &sr[0]); if (ret) return ret; - ret = spi_nor_read_sr(nor, &nor->bouncebuf[0]); - if (ret) - return ret; - - if (written_regs[0] != nor->bouncebuf[0]) { - dev_dbg(nor->dev, "SR: Read back test failed\n"); - return -EIO; - } - - if (nor->flags & SNOR_F_NO_READ_CR) - return 0; + if (nor->params->opcodes.read_sr2) + ret = spi_nor_read_sr2(nor, &sr[1]); + else + sr[1] = 0; - ret = spi_nor_read_cr(nor, &nor->bouncebuf[1]); - if (ret) - return ret; + return ret; +} - if (written_regs[1] != nor->bouncebuf[1]) { - dev_dbg(nor->dev, "CR: read back test failed\n"); +/** + * spi_nor_write_sr1() - Write SR1 only + * Useful for: + * - Manufacturer drivers (since they know the chip SR layout) + * + * @nor: the spi_nor structure + * @sr1: pointer to a valid SR1 buffer + * + * Return 0 or errno. + */ +int spi_nor_write_sr1(struct spi_nor *nor, const u8 *sr1) +{ + if (WARN_ONCE(!nor->params->opcodes.write_sr1, + "Restricted helper use, write SR1 not supported")) return -EIO; - } - return 0; + return spi_nor_write_sr_ll(nor, nor->params->opcodes.write_sr1, sr1, 1); } /** - * spi_nor_write_sr_and_check() - Write the Status Register 1 and ensure that - * the byte written match the received value without affecting other bits in the - * Status Register 1 and 2. - * @nor: pointer to a 'struct spi_nor'. - * @sr1: byte value to be written to the Status Register. + * spi_nor_write_sr2() - Write SR2 only + * Useful for: + * - Manufacturer drivers (since they know the chip SR layout) + * - SR2 based OTP configuration * - * Return: 0 on success, -errno otherwise. + * @nor: the spi_nor structure + * @sr2: pointer to a valid SR2 buffer + * + * Return 0 or errno. */ -int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 sr1) +int spi_nor_write_sr2(struct spi_nor *nor, const u8 *sr2) { - if (nor->flags & SNOR_F_HAS_16BIT_SR) - return spi_nor_write_16bit_sr_and_check(nor, sr1); + if (WARN_ONCE(!nor->params->opcodes.write_sr2, + "Restricted helper use, write SR2 not supported")) + return -EIO; - return spi_nor_write_sr1_and_check(nor, sr1); + return spi_nor_write_sr_ll(nor, nor->params->opcodes.write_sr2, sr2, 1); } /** - * spi_nor_write_sr_cr_and_check() - Write the Status Register 1 and ensure that - * the byte written match the received value. Same for the Control Register if - * available. - * @nor: pointer to a 'struct spi_nor'. - * @regs: byte array to be written to the registers. + * spi_nor_write_sr1_and_sr2() - Write SR1 then SR2 + * General purpose helper, always safe to call. Will expectedly ignore + * SR2 on certain chips. * - * Return: 0 on success, -errno otherwise. + * @nor: the spi_nor structure + * @sr: pointer to a valid 2-byte array + * + * Return 0 or errno. */ -int spi_nor_write_sr_cr_and_check(struct spi_nor *nor, const u8 *regs) +static int spi_nor_write_sr1_and_sr2(struct spi_nor *nor, const u8 *sr) { - if (nor->flags & SNOR_F_HAS_16BIT_SR) - return spi_nor_write_16bit_sr_cr_and_check(nor, regs); + struct spi_nor_flash_parameter *params = nor->params; + int ret; + + if (params->opcodes.write_sr1_and_sr2) + return spi_nor_write_sr_ll(nor, + params->opcodes.write_sr1_and_sr2, + sr, 2); + + ret = spi_nor_write_sr1(nor, &sr[0]); + if (ret) + return ret; + + if (params->opcodes.write_sr2) + return spi_nor_write_sr2(nor, &sr[1]); - return spi_nor_write_sr1_and_check(nor, regs[0]); + return 0; } /** - * spi_nor_write_sr2() - Write the Status Register 2 using the - * SPINOR_OP_WRSR2 (3eh) command. - * @nor: pointer to 'struct spi_nor'. - * @sr2: pointer to DMA-able buffer to write to the Status Register 2. + * spi_nor_write_sr1_and_sr2_and_check() - Write SR1 then SR2, then read + * them back and verifies + * General purpose helper, always safe to call. Will expectedly ignore + * SR2 on certain chips. * - * Return: 0 on success, -errno otherwise. + * @nor: the spi_nor structure + * @sr: pointer to a valid 2-byte array + * + * Return 0 or errno. */ -static int spi_nor_write_sr2(struct spi_nor *nor, const u8 *sr2) +int spi_nor_write_sr1_and_sr2_and_check(struct spi_nor *nor, const u8 *sr) { + u8 tmp[2]; int ret; - ret = spi_nor_write_enable(nor); + ret = spi_nor_write_sr1_and_sr2(nor, sr); if (ret) return ret; - if (nor->spimem) { - struct spi_mem_op op = SPI_NOR_WRSR2_OP(sr2); - - spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + ret = spi_nor_read_sr1_and_sr2(nor, tmp); + if (ret) + return ret; - ret = spi_mem_exec_op(nor->spimem, &op); - } else { - ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_WRSR2, - sr2, 1); - } + if (sr[0] != tmp[0]) + return -EIO; - if (ret) { - dev_dbg(nor->dev, "error %d writing SR2\n", ret); - return ret; - } + if (nor->params->opcodes.read_sr2 && sr[1] != tmp[1]) + return -EIO; - return spi_nor_wait_till_ready(nor); + return 0; } /** - * spi_nor_read_sr2() - Read the Status Register 2 using the - * SPINOR_OP_RDSR2 (3fh) command. - * @nor: pointer to 'struct spi_nor'. - * @sr2: pointer to DMA-able buffer where the value of the - * Status Register 2 will be written. + * spi_nor_generic_quad_enable() - Read the status registers, + * apply the QE bit mask, + * write the status registers, + * read them back and check the content. + * + * @nor: pointer to 'struct spi_nor' * * Return: 0 on success, -errno otherwise. */ -static int spi_nor_read_sr2(struct spi_nor *nor, u8 *sr2) +static int spi_nor_generic_quad_enable(struct spi_nor *nor) { + u8 *qe_mask = nor->params->qe_mask; + u8 sr[2] = {}; int ret; - if (nor->spimem) { - struct spi_mem_op op = SPI_NOR_RDSR2_OP(sr2); + if (!qe_mask[0] && !qe_mask[1]) + return 0; - spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + ret = spi_nor_read_sr1_and_sr2(nor, sr); + if (ret) + return ret; - ret = spi_mem_exec_op(nor->spimem, &op); - } else { - ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_RDSR2, sr2, - 1); - } + if (sr[0] & qe_mask[0] || sr[1] & qe_mask[1]) + return 0; - if (ret) - dev_dbg(nor->dev, "error %d reading SR2\n", ret); + sr[0] |= qe_mask[0]; + sr[1] |= qe_mask[1]; - return ret; + return spi_nor_write_sr1_and_sr2_and_check(nor, sr); } /** @@ -1140,7 +1024,7 @@ static int spi_nor_erase_die(struct spi_nor *nor, loff_t addr, size_t die_size) if (nor->spimem) { struct spi_mem_op op = - SPI_NOR_DIE_ERASE_OP(nor->params->die_erase_opcode, + SPI_NOR_DIE_ERASE_OP(nor->params->opcodes.die_erase, nor->addr_nbytes, addr, multi_die); spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); @@ -1310,7 +1194,7 @@ static bool spi_nor_rww_start_exclusive(struct spi_nor *nor) { struct spi_nor_rww *rww = &nor->rww; - mutex_lock(&nor->lock); + guard(mutex)(&nor->lock); if (rww->ongoing_io || rww->ongoing_rd || rww->ongoing_pe) return false; @@ -1346,6 +1230,9 @@ int spi_nor_prep_and_lock(struct spi_nor *nor) ret = wait_event_killable(nor->rww.wait, spi_nor_rww_start_exclusive(nor)); + if (ret) + spi_nor_unprep(nor); + return ret; } @@ -1417,6 +1304,9 @@ static int spi_nor_prep_and_lock_pe(struct spi_nor *nor, loff_t start, size_t le ret = wait_event_killable(nor->rww.wait, spi_nor_rww_start_pe(nor, start, len)); + if (ret) + spi_nor_unprep(nor); + return ret; } @@ -1490,6 +1380,9 @@ static int spi_nor_prep_and_lock_rd(struct spi_nor *nor, loff_t start, size_t le ret = wait_event_killable(nor->rww.wait, spi_nor_rww_start_rd(nor, start, len)); + if (ret) + spi_nor_unprep(nor); + return ret; } @@ -1852,8 +1745,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) return ret; /* chip (die) erase? */ - if ((len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) || - multi_die_erase) { + if (len == mtd->size || multi_die_erase) { ret = spi_nor_erase_dice(nor, addr, len, die_size); if (ret) goto erase_err; @@ -1904,106 +1796,6 @@ erase_err: return ret; } -/** - * spi_nor_sr1_bit6_quad_enable() - Set the Quad Enable BIT(6) in the Status - * Register 1. - * @nor: pointer to a 'struct spi_nor' - * - * Bit 6 of the Status Register 1 is the QE bit for Macronix like QSPI memories. - * - * Return: 0 on success, -errno otherwise. - */ -int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor) -{ - int ret; - - ret = spi_nor_read_sr(nor, nor->bouncebuf); - if (ret) - return ret; - - if (nor->bouncebuf[0] & SR1_QUAD_EN_BIT6) - return 0; - - nor->bouncebuf[0] |= SR1_QUAD_EN_BIT6; - - return spi_nor_write_sr1_and_check(nor, nor->bouncebuf[0]); -} - -/** - * spi_nor_sr2_bit1_quad_enable() - set the Quad Enable BIT(1) in the Status - * Register 2. - * @nor: pointer to a 'struct spi_nor'. - * - * Bit 1 of the Status Register 2 is the QE bit for Spansion like QSPI memories. - * - * Return: 0 on success, -errno otherwise. - */ -int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor) -{ - int ret; - - if (nor->flags & SNOR_F_NO_READ_CR) - return spi_nor_write_16bit_cr_and_check(nor, SR2_QUAD_EN_BIT1); - - ret = spi_nor_read_cr(nor, nor->bouncebuf); - if (ret) - return ret; - - if (nor->bouncebuf[0] & SR2_QUAD_EN_BIT1) - return 0; - - nor->bouncebuf[0] |= SR2_QUAD_EN_BIT1; - - return spi_nor_write_16bit_cr_and_check(nor, nor->bouncebuf[0]); -} - -/** - * spi_nor_sr2_bit7_quad_enable() - set QE bit in Status Register 2. - * @nor: pointer to a 'struct spi_nor' - * - * Set the Quad Enable (QE) bit in the Status Register 2. - * - * This is one of the procedures to set the QE bit described in the SFDP - * (JESD216 rev B) specification but no manufacturer using this procedure has - * been identified yet, hence the name of the function. - * - * Return: 0 on success, -errno otherwise. - */ -int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor) -{ - u8 *sr2 = nor->bouncebuf; - int ret; - u8 sr2_written; - - /* Check current Quad Enable bit value. */ - ret = spi_nor_read_sr2(nor, sr2); - if (ret) - return ret; - if (*sr2 & SR2_QUAD_EN_BIT7) - return 0; - - /* Update the Quad Enable bit. */ - *sr2 |= SR2_QUAD_EN_BIT7; - - ret = spi_nor_write_sr2(nor, sr2); - if (ret) - return ret; - - sr2_written = *sr2; - - /* Read back and check it. */ - ret = spi_nor_read_sr2(nor, sr2); - if (ret) - return ret; - - if (*sr2 != sr2_written) { - dev_dbg(nor->dev, "SR2: Read back test failed\n"); - return -EIO; - } - - return 0; -} - static const struct spi_nor_manufacturer *manufacturers[] = { &spi_nor_atmel, &spi_nor_eon, @@ -2494,6 +2286,7 @@ spi_nor_spimem_adjust_hwcaps(struct spi_nor *nor, u32 *hwcaps) { struct spi_nor_flash_parameter *params = nor->params; unsigned int cap; + u8 opcode; /* X-X-X modes are not supported yet, mask them all. */ *hwcaps &= ~SNOR_HWCAPS_X_X_X; @@ -2502,7 +2295,7 @@ spi_nor_spimem_adjust_hwcaps(struct spi_nor *nor, u32 *hwcaps) * If the reset line is broken, we do not want to enter a stateful * mode. */ - if (nor->flags & SNOR_F_BROKEN_RESET) + if (params->flags & SNOR_F_BROKEN_RESET) *hwcaps &= ~(SNOR_HWCAPS_X_X_X | SNOR_HWCAPS_X_X_X_DTR); for (cap = 0; cap < sizeof(*hwcaps) * BITS_PER_BYTE; cap++) { @@ -2525,14 +2318,15 @@ spi_nor_spimem_adjust_hwcaps(struct spi_nor *nor, u32 *hwcaps) *hwcaps &= ~BIT(cap); } - /* Some SPI controllers might not support CR read opcode. */ - if (!(nor->flags & SNOR_F_NO_READ_CR)) { - struct spi_mem_op op = SPI_NOR_RDCR_OP(nor->bouncebuf); + /* Some SPI controllers might not support reading SR2 */ + opcode = nor->params->opcodes.read_sr2; + if (opcode) { + struct spi_mem_op op = SPI_NOR_RDSR_OP(opcode, nor->bouncebuf, 1); spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); if (!spi_mem_supports_op(nor->spimem, &op)) - nor->flags |= SNOR_F_NO_READ_CR; + nor->params->opcodes.read_sr2 = 0; } } @@ -2578,26 +2372,6 @@ void spi_nor_init_uniform_erase_map(struct spi_nor_erase_map *map, map->n_regions = 1; } -int spi_nor_post_bfpt_fixups(struct spi_nor *nor, - const struct sfdp_parameter_header *bfpt_header, - const struct sfdp_bfpt *bfpt) -{ - int ret; - - if (nor->manufacturer && nor->manufacturer->fixups && - nor->manufacturer->fixups->post_bfpt) { - ret = nor->manufacturer->fixups->post_bfpt(nor, bfpt_header, - bfpt); - if (ret) - return ret; - } - - if (nor->info->fixups && nor->info->fixups->post_bfpt) - return nor->info->fixups->post_bfpt(nor, bfpt_header, bfpt); - - return 0; -} - static int spi_nor_select_read(struct spi_nor *nor, u32 shared_hwcaps) { @@ -2749,6 +2523,8 @@ static int spi_nor_select_erase(struct spi_nor *nor) static int spi_nor_set_addr_nbytes(struct spi_nor *nor) { + struct spi_nor_flash_parameter *params = nor->params; + if (nor->params->addr_nbytes) { nor->addr_nbytes = nor->params->addr_nbytes; } else if (nor->read_proto == SNOR_PROTO_8_8_8_DTR) { @@ -2783,8 +2559,8 @@ static int spi_nor_set_addr_nbytes(struct spi_nor *nor) } /* Set 4byte opcodes when possible. */ - if (nor->addr_nbytes == 4 && nor->flags & SNOR_F_4B_OPCODES && - !(nor->flags & SNOR_F_HAS_4BAIT)) + if (nor->addr_nbytes == 4 && params->flags & SNOR_F_4B_OPCODES && + !(params->flags & SNOR_F_HAS_4BAIT)) spi_nor_set_4byte_opcodes(nor); return 0; @@ -2851,6 +2627,26 @@ static int spi_nor_setup(struct spi_nor *nor, return spi_nor_set_addr_nbytes(nor); } +bool spi_nor_fixup_match(const struct spi_nor *nor, + const struct spi_nor_fixup *fixup) +{ + const struct spi_nor_id *id = nor->info ? nor->info->id : NULL; + + /* Filter by ID first, if available */ + if (fixup->id) { + if (!id || fixup->id->len > id->len || + memcmp(id->bytes, fixup->id->bytes, fixup->id->len)) + return false; + } + + /* Further filter with the match callback, if provided */ + if (fixup->match) + return fixup->match(nor); + + /* Either there was an ID and it matched, or it is a catch-all entry */ + return true; +} + /** * spi_nor_manufacturer_init_params() - Initialize the flash's parameters and * settings based on MFR register and ->default_init() hook. @@ -2858,21 +2654,27 @@ static int spi_nor_setup(struct spi_nor *nor, */ static void spi_nor_manufacturer_init_params(struct spi_nor *nor) { - if (nor->manufacturer && nor->manufacturer->fixups && - nor->manufacturer->fixups->default_init) - nor->manufacturer->fixups->default_init(nor); + const struct spi_nor_fixup *fixups; + unsigned int i; + + if (!nor->manufacturer || !nor->manufacturer->fixups) + return; + + fixups = nor->manufacturer->fixups; - if (nor->info->fixups && nor->info->fixups->default_init) - nor->info->fixups->default_init(nor); + for (i = 0; i < nor->manufacturer->nfixups; i++) { + if (fixups[i].fixups && fixups[i].fixups->default_init && + spi_nor_fixup_match(nor, &fixups[i])) + fixups[i].fixups->default_init(nor); + } } /** * 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. + * settings based on nor->info->sfdp_flags. + * 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_no_sfdp_init_params(struct spi_nor *nor) @@ -2947,39 +2749,40 @@ static void spi_nor_no_sfdp_init_params(struct spi_nor *nor) */ static void spi_nor_init_flags(struct spi_nor *nor) { + struct spi_nor_flash_parameter *params = nor->params; 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; + params->flags |= SNOR_F_BROKEN_RESET; if (of_property_read_bool(np, "no-wp")) - nor->flags |= SNOR_F_NO_WP; + params->flags |= SNOR_F_NO_WP; if (flags & SPI_NOR_SWP_IS_VOLATILE) - nor->flags |= SNOR_F_SWP_IS_VOLATILE; + params->flags |= SNOR_F_SWP_IS_VOLATILE; if (flags & SPI_NOR_HAS_LOCK) - nor->flags |= SNOR_F_HAS_LOCK; + params->flags |= SNOR_F_HAS_LOCK; if (flags & SPI_NOR_HAS_TB) { - nor->flags |= SNOR_F_HAS_SR_TB; + params->flags |= SNOR_F_HAS_SR_TB; if (flags & SPI_NOR_TB_SR_BIT6) - nor->flags |= SNOR_F_HAS_SR_TB_BIT6; + params->flags |= SNOR_F_HAS_SR_TB_BIT6; } if (flags & SPI_NOR_4BIT_BP) { - nor->flags |= SNOR_F_HAS_4BIT_BP; + params->flags |= SNOR_F_HAS_4BIT_BP; if (flags & SPI_NOR_BP3_SR_BIT6) - nor->flags |= SNOR_F_HAS_SR_BP3_BIT6; + params->flags |= SNOR_F_HAS_SR_BP3_BIT6; } if (flags & SPI_NOR_HAS_CMP) - nor->flags |= SNOR_F_HAS_SR2_CMP_BIT6; + params->flags |= SNOR_F_HAS_SR2_CMP_BIT6; if (flags & SPI_NOR_RWW && nor->params->n_banks > 1 && !nor->controller_ops) - nor->flags |= SNOR_F_RWW; + params->flags |= SNOR_F_RWW; } /** @@ -2992,13 +2795,25 @@ static void spi_nor_init_flags(struct spi_nor *nor) */ static void spi_nor_init_fixup_flags(struct spi_nor *nor) { - const u8 fixup_flags = nor->info->fixup_flags; + struct spi_nor_flash_parameter *params = nor->params; + const struct spi_nor_fixup *fixups; + unsigned int i; - if (fixup_flags & SPI_NOR_4B_OPCODES) - nor->flags |= SNOR_F_4B_OPCODES; + if (!nor->manufacturer || !nor->manufacturer->fixups) + return; - if (fixup_flags & SPI_NOR_IO_MODE_EN_VOLATILE) - nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE; + fixups = nor->manufacturer->fixups; + + for (i = 0; i < nor->manufacturer->nfixups; i++) { + if (!fixups[i].fixup_flags || + !spi_nor_fixup_match(nor, &fixups[i])) + continue; + + if (fixups[i].fixup_flags & SPI_NOR_4B_OPCODES) + params->flags |= SNOR_F_4B_OPCODES; + if (fixups[i].fixup_flags & SPI_NOR_IO_MODE_EN_VOLATILE) + params->flags |= SNOR_F_IO_MODE_EN_VOLATILE; + } } /** @@ -3012,26 +2827,28 @@ static void spi_nor_init_fixup_flags(struct spi_nor *nor) static int spi_nor_late_init_params(struct spi_nor *nor) { struct spi_nor_flash_parameter *params = nor->params; + const struct spi_nor_fixup *fixups; + unsigned int i; int ret; - if (nor->manufacturer && nor->manufacturer->fixups && - nor->manufacturer->fixups->late_init) { - ret = nor->manufacturer->fixups->late_init(nor); - if (ret) - return ret; - } - - /* Needed by some flashes late_init hooks. */ + /* Needed by some late_init hooks */ spi_nor_init_flags(nor); - if (nor->info->fixups && nor->info->fixups->late_init) { - ret = nor->info->fixups->late_init(nor); - if (ret) - return ret; + if (nor->manufacturer && nor->manufacturer->fixups) { + fixups = nor->manufacturer->fixups; + + for (i = 0; i < nor->manufacturer->nfixups; i++) { + if (fixups[i].fixups && fixups[i].fixups->late_init && + spi_nor_fixup_match(nor, &fixups[i])) { + ret = fixups[i].fixups->late_init(nor); + if (ret) + return ret; + } + } } - if (!nor->params->die_erase_opcode) - nor->params->die_erase_opcode = SPINOR_OP_CHIP_ERASE; + if (!nor->params->opcodes.die_erase) + nor->params->opcodes.die_erase = SPINOR_OP_CHIP_ERASE; /* Default method kept for backward compatibility. */ if (!params->set_4byte_addr_mode) @@ -3043,7 +2860,7 @@ static int spi_nor_late_init_params(struct spi_nor *nor) * NOR protection support. When locking_ops are not provided, we pick * the default ones. */ - if (nor->flags & SNOR_F_HAS_LOCK && !nor->params->locking_ops) + if (params->flags & SNOR_F_HAS_LOCK && !nor->params->locking_ops) spi_nor_init_default_locking_ops(nor); if (params->n_banks > 1) @@ -3053,48 +2870,6 @@ static int 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->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. @@ -3106,11 +2881,14 @@ static void spi_nor_init_default_params(struct spi_nor *nor) 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->otp.org = info->otp; + /* Default to 16-bit Read/Write Status commands */ + params->opcodes.read_sr1 = SPINOR_OP_RDSR; + params->opcodes.read_sr2 = SPINOR_OP_RDCR; + params->opcodes.write_sr1_and_sr2 = SPINOR_OP_WRSR; + params->quad_enable = spi_nor_generic_quad_enable; + params->qe_mask[1] = BIT(1); - /* Default to 16-bit Write Status (01h) Command */ - nor->flags |= SNOR_F_HAS_16BIT_SR; + params->otp.org = info->otp; /* Set SPI NOR sizes. */ params->writesize = 1; @@ -3154,7 +2932,8 @@ static void spi_nor_init_default_params(struct spi_nor *nor) * * 1/ Default flash parameters initialization. The initializations are done * based on nor->info data: - * spi_nor_info_init_params() + * spi_nor_init_default_params() + * spi_nor_no_sfdp_init_params() * * which can be overwritten by: * 2/ Manufacturer flash parameters initialization. The initializations are @@ -3165,7 +2944,7 @@ static void spi_nor_init_default_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_parse_sfdp() or spi_nor_no_sfdp_init_params() + * spi_nor_parse_sfdp() * * Please note that there is a ->post_bfpt() fixup hook that can overwrite * the flash parameters and settings immediately after parsing the Basic @@ -3191,17 +2970,14 @@ static int spi_nor_init_params(struct spi_nor *nor) return -ENOMEM; spi_nor_init_default_params(nor); + spi_nor_no_sfdp_init_params(nor); + spi_nor_manufacturer_init_params(nor); - if (spi_nor_needs_sfdp(nor)) { - 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); + ret = spi_nor_parse_sfdp(nor); + if (ret && spi_nor_needs_sfdp(nor)) { + dev_err(nor->dev, + "SFDP parsing failed. You need to manually declare the flash parameters.\n"); + return ret; } ret = spi_nor_late_init_params(nor); @@ -3231,7 +3007,7 @@ static int spi_nor_set_octal_dtr(struct spi_nor *nor, bool enable) nor->write_proto == SNOR_PROTO_8_8_8_DTR)) return 0; - if (!(nor->flags & SNOR_F_IO_MODE_EN_VOLATILE)) + if (!(nor->params->flags & SNOR_F_IO_MODE_EN_VOLATILE)) return 0; ret = nor->params->set_octal_dtr(nor, enable); @@ -3284,7 +3060,7 @@ int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable) * reboots (e.g., crashes). Warn the user (or hopefully, system * designer) that this is bad. */ - WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET, + WARN_ONCE(nor->params->flags & SNOR_F_BROKEN_RESET, "enabling reset hack; may not recover from unexpected reboots\n"); } @@ -3305,6 +3081,7 @@ int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable) static int spi_nor_init(struct spi_nor *nor) { + struct spi_nor_flash_parameter *params = nor->params; int err; err = spi_nor_set_octal_dtr(nor, true); @@ -3332,13 +3109,13 @@ static int spi_nor_init(struct spi_nor *nor) spi_nor_cache_sr_lock_bits(nor, NULL); if (IS_ENABLED(CONFIG_MTD_SPI_NOR_SWP_DISABLE) || (IS_ENABLED(CONFIG_MTD_SPI_NOR_SWP_DISABLE_ON_VOLATILE) && - nor->flags & SNOR_F_SWP_IS_VOLATILE)) { + params->flags & SNOR_F_SWP_IS_VOLATILE)) { spi_nor_try_unlock_all(nor); } if (nor->addr_nbytes == 4 && nor->read_proto != SNOR_PROTO_8_8_8_DTR && - !(nor->flags & SNOR_F_4B_OPCODES)) + !(params->flags & SNOR_F_4B_OPCODES)) return spi_nor_set_4byte_addr_mode(nor, true); return 0; @@ -3453,11 +3230,12 @@ static void spi_nor_put_device(struct mtd_info *mtd) static void spi_nor_restore(struct spi_nor *nor) { + struct spi_nor_flash_parameter *params = nor->params; int ret; /* restore the addressing mode */ - if (nor->addr_nbytes == 4 && !(nor->flags & SNOR_F_4B_OPCODES) && - nor->flags & SNOR_F_BROKEN_RESET) { + if (nor->addr_nbytes == 4 && !(params->flags & SNOR_F_4B_OPCODES) && + params->flags & SNOR_F_BROKEN_RESET) { ret = spi_nor_set_4byte_addr_mode(nor, false); if (ret) /* @@ -3468,7 +3246,7 @@ static void spi_nor_restore(struct spi_nor *nor) dev_err(nor->dev, "Failed to exit 4-byte address mode, err = %d\n", ret); } - if (nor->flags & SNOR_F_SOFT_RESET) + if (params->flags & SNOR_F_SOFT_RESET) spi_nor_soft_reset(nor); } @@ -3585,7 +3363,7 @@ static int spi_nor_set_mtd_info(struct spi_nor *nor) mtd->type = MTD_NORFLASH; mtd->flags = MTD_CAP_NORFLASH; /* Unset BIT_WRITEABLE to enable JFFS2 write buffer for ECC'd NOR */ - if (nor->flags & SNOR_F_ECC) + if (nor->params->flags & SNOR_F_ECC) mtd->flags &= ~MTD_BIT_WRITEABLE; if (nor->info->flags & SPI_NOR_NO_ERASE) mtd->flags |= MTD_NO_ERASE; @@ -3669,6 +3447,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, return PTR_ERR(info); nor->info = info; + nor->partname = info->name; mutex_init(&nor->lock); diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index ba2d1a862c9d..670182b3c2ad 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -37,36 +37,18 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DATA) -#define SPI_NOR_RDSR_OP(buf) \ - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 0), \ +#define SPI_NOR_RDSR_OP(opcode, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0), \ SPI_MEM_OP_NO_ADDR, \ SPI_MEM_OP_NO_DUMMY, \ - SPI_MEM_OP_DATA_IN(1, buf, 0)) + SPI_MEM_OP_DATA_IN(len, buf, 0)) -#define SPI_NOR_WRSR_OP(buf, len) \ - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 0), \ +#define SPI_NOR_WRSR_OP(opcode, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0), \ SPI_MEM_OP_NO_ADDR, \ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_DATA_OUT(len, buf, 0)) -#define SPI_NOR_RDSR2_OP(buf) \ - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR2, 0), \ - SPI_MEM_OP_NO_ADDR, \ - SPI_MEM_OP_NO_DUMMY, \ - SPI_MEM_OP_DATA_OUT(1, buf, 0)) - -#define SPI_NOR_WRSR2_OP(buf) \ - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR2, 0), \ - SPI_MEM_OP_NO_ADDR, \ - SPI_MEM_OP_NO_DUMMY, \ - SPI_MEM_OP_DATA_OUT(1, buf, 0)) - -#define SPI_NOR_RDCR_OP(buf) \ - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDCR, 0), \ - SPI_MEM_OP_NO_ADDR, \ - SPI_MEM_OP_NO_DUMMY, \ - SPI_MEM_OP_DATA_IN(1, buf, 0)) - #define SPI_NOR_EN4B_EX4B_OP(enable) \ SPI_MEM_OP(SPI_MEM_OP_CMD(enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B, 0), \ SPI_MEM_OP_NO_ADDR, \ @@ -121,16 +103,16 @@ SPI_MEM_OP_NO_ADDR, \ SPI_MEM_OP_NO_DATA) -/* Keep these in sync with the list in debugfs.c */ +/* + * This could contain holes, if adding a new flag use the first free spot. + * Keep the flags in sync with the list in debugfs.c + */ enum spi_nor_option_flags { SNOR_F_HAS_SR_TB = BIT(0), - SNOR_F_NO_OP_CHIP_ERASE = BIT(1), SNOR_F_BROKEN_RESET = BIT(2), SNOR_F_4B_OPCODES = BIT(3), SNOR_F_HAS_4BAIT = BIT(4), SNOR_F_HAS_LOCK = BIT(5), - SNOR_F_HAS_16BIT_SR = BIT(6), - SNOR_F_NO_READ_CR = BIT(7), SNOR_F_HAS_SR_TB_BIT6 = BIT(8), SNOR_F_HAS_4BIT_BP = BIT(9), SNOR_F_HAS_SR_BP3_BIT6 = BIT(10), @@ -341,6 +323,26 @@ struct spi_nor_otp { }; /** + * struct spi_nor_opcodes - SPI NOR flash specific opcodes. + * List of variable opcodes used by the chip. + * + * @die_erase: opcode for erasing a die, defaults to SPINOR_OP_CHIP_ERASE + * @read_sr1: opcode for reading SR1 alone + * @read_sr2: opcode for reading SR2 alone + * @write_sr1: opcode for writing SR1 alone + * @write_sr2: opcode for writing SR2 alone + * @write_sr1_and_sr2: opcode for writing SR1 then SR2 in one operation + */ +struct spi_nor_opcodes { + u8 die_erase; + u8 read_sr1; + u8 read_sr2; + u8 write_sr1; + u8 write_sr2; + u8 write_sr1_and_sr2; +}; + +/** * struct spi_nor_flash_parameter - SPI NOR flash parameters and settings. * Includes legacy flash parameters and settings that can be overwritten * by the spi_nor_fixups hooks, or dynamically when parsing the JESD216 @@ -348,6 +350,7 @@ struct spi_nor_otp { * * @bank_size: the flash memory bank density in bytes. * @size: the total flash memory density in bytes. + * @flags: flag options for the current SPI NOR (SNOR_F_*) * @writesize Minimal writable flash unit size. Defaults to 1. Set to * ECC unit size for ECC-ed flashes. * @page_size: the page size of the SPI NOR flash memory. @@ -362,7 +365,6 @@ struct spi_nor_otp { * command in octal DTR mode. * @n_banks: number of banks. * @n_dice: number of dice in the flash memory. - * @die_erase_opcode: die erase opcode. Defaults to SPINOR_OP_CHIP_ERASE. * @vreg_offset: volatile register offset for each die. * @hwcaps: describes the read and page program hardware * capabilities. @@ -370,11 +372,13 @@ struct spi_nor_otp { * in the array, the higher priority. * @page_programs: page program capabilities ordered by priority: the * higher index in the array, the higher priority. + * @cmd_ext_type: the command opcode extension type for DTR mode. * @erase_map: the erase map parsed from the SFDP Sector Map Parameter * Table. * @otp: SPI NOR OTP info. * @set_octal_dtr: enables or disables SPI NOR octal DTR mode. * @quad_enable: enables SPI NOR quad mode. + * @qe_mask: two bytes mask used to set/clear the QE bit * @set_4byte_addr_mode: puts the SPI NOR in 4 byte addressing mode. * @ready: (optional) flashes might use a different mechanism * than reading the status register to indicate they @@ -385,6 +389,7 @@ struct spi_nor_otp { struct spi_nor_flash_parameter { u64 bank_size; u64 size; + u32 flags; u32 writesize; u32 page_size; u8 addr_nbytes; @@ -393,18 +398,20 @@ struct spi_nor_flash_parameter { u8 rdsr_addr_nbytes; u8 n_banks; u8 n_dice; - u8 die_erase_opcode; u32 *vreg_offset; struct spi_nor_hwcaps hwcaps; struct spi_nor_read_command reads[SNOR_CMD_READ_MAX]; struct spi_nor_pp_command page_programs[SNOR_CMD_PP_MAX]; + enum spi_nor_cmd_ext cmd_ext_type; + struct spi_nor_opcodes opcodes; struct spi_nor_erase_map erase_map; struct spi_nor_otp otp; int (*set_octal_dtr)(struct spi_nor *nor, bool enable); int (*quad_enable)(struct spi_nor *nor); + u8 qe_mask[2]; int (*set_4byte_addr_mode)(struct spi_nor *nor, bool enable); int (*ready)(struct spi_nor *nor); @@ -446,6 +453,33 @@ struct spi_nor_fixups { }; /** + * struct spi_nor_fixup - SPI NOR fixup registration. + * @id: (optional) flash ID this fixup applies to, may only match the + * ID prefix, eg. just the first few bytes to match a whole family + * @match: (optional) custom match function (can be used together with @id) + * @fixup_flags: flags that indicate support that can be discovered via SFDP + * ideally, but can not be discovered for this particular flash + * because the SFDP table that indicates this support is not + * defined by 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. + * + * SPI_NOR_4B_OPCODES: use dedicated 4byte address op codes to support + * memory size above 128Mib. + * SPI_NOR_IO_MODE_EN_VOLATILE: flash enables the best available I/O mode + * via a volatile bit. + * @fixups: the fixup hooks to apply when this entry matches + */ +struct spi_nor_fixup { + const struct spi_nor_id *id; + bool (*match)(const struct spi_nor *nor); + u8 fixup_flags; +#define SPI_NOR_4B_OPCODES BIT(0) +#define SPI_NOR_IO_MODE_EN_VOLATILE BIT(1) + const struct spi_nor_fixups *fixups; +}; + +/** * struct spi_nor_id - SPI NOR flash ID. * * @bytes: the bytes returned by the flash when issuing command 9F. Typically, @@ -496,9 +530,7 @@ struct spi_nor_id { * be used with SPI_NOR_HAS_LOCK. * * @no_sfdp_flags: flags that indicate support that can be discovered via SFDP. - * Used when SFDP tables are not defined in the flash. These - * flags are used together with the SPI_NOR_SKIP_SFDP flag. - * SPI_NOR_SKIP_SFDP: skip parsing of SFDP tables. + * Used when SFDP tables are not defined in the flash. * SECT_4K: SPINOR_OP_BE_4K works uniformly. * SPI_NOR_DUAL_READ: flash supports Dual Read. * SPI_NOR_QUAD_READ: flash supports Quad Read. @@ -506,22 +538,10 @@ struct spi_nor_id { * SPI_NOR_OCTAL_DTR_READ: flash supports octal DTR Read. * SPI_NOR_OCTAL_DTR_PP: flash supports Octal DTR Page Program. * - * @fixup_flags: flags that indicate support that can be discovered via SFDP - * ideally, but can not be discovered for this particular flash - * because the SFDP table that indicates this support is not - * defined by 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. - * - * SPI_NOR_4B_OPCODES: use dedicated 4byte address op codes to support - * memory size above 128Mib. - * SPI_NOR_IO_MODE_EN_VOLATILE: flash enables the best available I/O mode - * via a volatile bit. * @mfr_flags: manufacturer private flags. Used in the manufacturer fixup * hooks to differentiate support between flashes of the same * manufacturer. * @otp_org: flash's OTP organization. - * @fixups: part specific fixup hooks. */ struct flash_info { char *name; @@ -545,7 +565,6 @@ struct flash_info { #define SPI_NOR_HAS_CMP BIT(10) u8 no_sfdp_flags; -#define SPI_NOR_SKIP_SFDP BIT(0) #define SECT_4K BIT(1) #define SPI_NOR_DUAL_READ BIT(3) #define SPI_NOR_QUAD_READ BIT(4) @@ -553,14 +572,9 @@ struct flash_info { #define SPI_NOR_OCTAL_DTR_READ BIT(6) #define SPI_NOR_OCTAL_DTR_PP BIT(7) - u8 fixup_flags; -#define SPI_NOR_4B_OPCODES BIT(0) -#define SPI_NOR_IO_MODE_EN_VOLATILE BIT(1) - u8 mfr_flags; const struct spi_nor_otp_organization *otp; - const struct spi_nor_fixups *fixups; }; #define SNOR_ID(...) \ @@ -582,13 +596,16 @@ struct flash_info { * @name: manufacturer name * @parts: array of parts supported by this manufacturer * @nparts: number of entries in the parts array - * @fixups: hooks called at various points in time during spi_nor_scan() + * @fixups: list of fixups, each matched by ID (or a custom match function), + * applied to any part of this manufacturer. + * @nfixups: number of entries in the fixups array */ struct spi_nor_manufacturer { const char *name; const struct flash_info *parts; unsigned int nparts; - const struct spi_nor_fixups *fixups; + const struct spi_nor_fixup *fixups; + unsigned int nfixups; }; /** @@ -619,6 +636,8 @@ extern const struct spi_nor_manufacturer spi_nor_xmc; extern const struct attribute_group *spi_nor_sysfs_groups[]; +bool spi_nor_fixup_match(const struct spi_nor *nor, + const struct spi_nor_fixup *fixup); void spi_nor_spimem_setup_op(const struct spi_nor *nor, struct spi_mem_op *op, const enum spi_nor_protocol proto); @@ -633,18 +652,17 @@ int spi_nor_wait_till_ready(struct spi_nor *nor); int spi_nor_global_block_unlock(struct spi_nor *nor); int spi_nor_prep_and_lock(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_id(struct spi_nor *nor, u8 naddr, u8 ndummy, u8 *id, enum spi_nor_protocol reg_proto); -int spi_nor_read_sr(struct spi_nor *nor, u8 *sr); int spi_nor_sr_ready(struct spi_nor *nor); -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); -int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr); -int spi_nor_write_sr_cr_and_check(struct spi_nor *nor, const u8 *regs); +int spi_nor_read_sr_ll(struct spi_nor *nor, u8 opcode, u8 *sr, + unsigned int len); +int spi_nor_read_sr1(struct spi_nor *nor, u8 *sr1); +int spi_nor_read_sr2(struct spi_nor *nor, u8 *sr2); +int spi_nor_read_sr1_and_sr2(struct spi_nor *nor, u8 *sr); +int spi_nor_write_sr1(struct spi_nor *nor, const u8 *sr1); +int spi_nor_write_sr2(struct spi_nor *nor, const u8 *sr2); +int spi_nor_write_sr1_and_sr2_and_check(struct spi_nor *nor, const u8 *sr); ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len, u8 *buf); @@ -680,10 +698,6 @@ void spi_nor_mask_erase_type(struct spi_nor_erase_type *erase); void spi_nor_init_uniform_erase_map(struct spi_nor_erase_map *map, u8 erase_mask, u64 flash_size); -int spi_nor_post_bfpt_fixups(struct spi_nor *nor, - const struct sfdp_parameter_header *bfpt_header, - const struct sfdp_bfpt *bfpt); - void spi_nor_init_default_locking_ops(struct spi_nor *nor); bool spi_nor_has_default_locking_ops(struct spi_nor *nor); void spi_nor_try_unlock_all(struct spi_nor *nor); diff --git a/drivers/mtd/spi-nor/debugfs.c b/drivers/mtd/spi-nor/debugfs.c index 288e2866daed..3f8d586a7622 100644 --- a/drivers/mtd/spi-nor/debugfs.c +++ b/drivers/mtd/spi-nor/debugfs.c @@ -14,13 +14,10 @@ #define SNOR_F_NAME(name) [ilog2(SNOR_F_##name)] = #name static const char *const snor_f_names[] = { SNOR_F_NAME(HAS_SR_TB), - SNOR_F_NAME(NO_OP_CHIP_ERASE), SNOR_F_NAME(BROKEN_RESET), SNOR_F_NAME(4B_OPCODES), SNOR_F_NAME(HAS_4BAIT), SNOR_F_NAME(HAS_LOCK), - SNOR_F_NAME(HAS_16BIT_SR), - SNOR_F_NAME(NO_READ_CR), SNOR_F_NAME(HAS_SR_TB_BIT6), SNOR_F_NAME(HAS_4BIT_BP), SNOR_F_NAME(HAS_SR_BP3_BIT6), @@ -84,13 +81,12 @@ static int spi_nor_params_show(struct seq_file *s, void *data) struct spi_nor_flash_parameter *params = nor->params; struct spi_nor_erase_map *erase_map = ¶ms->erase_map; struct spi_nor_erase_region *region = erase_map->regions; - const struct flash_info *info = nor->info; char buf[16], *str; loff_t lock_start; u64 lock_length; unsigned int i; - seq_printf(s, "name\t\t%s\n", info->name); + seq_printf(s, "name\t\t%s\n", nor->partname); seq_printf(s, "id\t\t%*ph\n", SPI_NOR_MAX_ID_LEN, nor->id); string_get_size(params->size, 1, STRING_UNITS_2, buf, sizeof(buf)); seq_printf(s, "size\t\t%s\n", buf); @@ -99,7 +95,7 @@ static int spi_nor_params_show(struct seq_file *s, void *data) seq_printf(s, "address nbytes\t%u\n", nor->addr_nbytes); seq_puts(s, "flags\t\t"); - spi_nor_print_flags(s, nor->flags, snor_f_names, + spi_nor_print_flags(s, params->flags, snor_f_names, ARRAY_SIZE(snor_f_names)); seq_puts(s, "\n"); @@ -108,8 +104,23 @@ static int spi_nor_params_show(struct seq_file *s, void *data) seq_printf(s, " dummy cycles\t%u\n", nor->read_dummy); seq_printf(s, " erase\t\t0x%02x\n", nor->erase_opcode); seq_printf(s, " program\t0x%02x\n", nor->program_opcode); - - switch (nor->cmd_ext_type) { + seq_printf(s, " SR1 read\t0x%02x\n", params->opcodes.read_sr1); + if (params->opcodes.read_sr2) + seq_printf(s, " SR2 read\t0x%02x\n", params->opcodes.read_sr2); + if (params->opcodes.write_sr1) + seq_printf(s, " SR1 write\t0x%02x\n", params->opcodes.write_sr1); + if (params->opcodes.write_sr2) + seq_printf(s, " SR2 write\t0x%02x\n", params->opcodes.write_sr2); + if (params->opcodes.write_sr1_and_sr2) + seq_printf(s, " SR1+SR2 write\t0x%02x\n", + params->opcodes.write_sr1_and_sr2); + + if (params->qe_mask[0] || params->qe_mask[1]) + seq_printf(s, " QE\t\tSR%d bit %d\n", + params->qe_mask[0] ? 1 : 2, + ffs(params->qe_mask[0] | params->qe_mask[1]) - 1); + + switch (params->cmd_ext_type) { case SPI_NOR_EXT_NONE: str = "none"; break; @@ -144,10 +155,8 @@ static int spi_nor_params_show(struct seq_file *s, void *data) } } - if (!(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) { - string_get_size(params->size, 1, STRING_UNITS_2, buf, sizeof(buf)); - seq_printf(s, " %02x (%s)\n", params->die_erase_opcode, buf); - } + string_get_size(params->size, 1, STRING_UNITS_2, buf, sizeof(buf)); + seq_printf(s, " %02x (%s)\n", params->opcodes.die_erase, buf); seq_puts(s, "\nsector map\n"); seq_puts(s, " region (in hex) | erase mask | overlaid\n"); diff --git a/drivers/mtd/spi-nor/everspin.c b/drivers/mtd/spi-nor/everspin.c index add37104d673..864dffba0e9c 100644 --- a/drivers/mtd/spi-nor/everspin.c +++ b/drivers/mtd/spi-nor/everspin.c @@ -44,9 +44,14 @@ static const struct spi_nor_fixups everspin_nor_fixups = { .default_init = everspin_nor_default_init, }; +static const struct spi_nor_fixup everspin_fixups[] = { + { .fixups = &everspin_nor_fixups }, +}; + const struct spi_nor_manufacturer spi_nor_everspin = { .name = "everspin", .parts = everspin_nor_parts, .nparts = ARRAY_SIZE(everspin_nor_parts), - .fixups = &everspin_nor_fixups, + .fixups = everspin_fixups, + .nfixups = ARRAY_SIZE(everspin_fixups), }; diff --git a/drivers/mtd/spi-nor/gigadevice.c b/drivers/mtd/spi-nor/gigadevice.c index ef1edd0add70..6a2e6ebda148 100644 --- a/drivers/mtd/spi-nor/gigadevice.c +++ b/drivers/mtd/spi-nor/gigadevice.c @@ -23,8 +23,10 @@ gd25q256_post_bfpt(struct spi_nor *nor, * GD25Q256E | SFDP_JESD216_MAJOR | SFDP_JESD216B_MINOR */ if (bfpt_header->major == SFDP_JESD216_MAJOR && - bfpt_header->minor == SFDP_JESD216_MINOR) - nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable; + bfpt_header->minor == SFDP_JESD216_MINOR) { + nor->params->qe_mask[0] = BIT(6); + nor->params->qe_mask[1] = 0; + } return 0; } @@ -62,8 +64,6 @@ static const struct flash_info gigadevice_nor_parts[] = { .id = SNOR_ID(0xc8, 0x40, 0x19), .name = "gd25q256", .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6, - .fixups = &gd25q256_fixups, - .fixup_flags = SPI_NOR_4B_OPCODES, }, { .id = SNOR_ID(0xc8, 0x60, 0x16), .name = "gd25lq32", @@ -85,8 +85,15 @@ static const struct flash_info gigadevice_nor_parts[] = { }, }; +static const struct spi_nor_fixup gigadevice_fixups[] = { + { .id = SNOR_ID(0xc8, 0x40, 0x19), .fixups = &gd25q256_fixups, + .fixup_flags = SPI_NOR_4B_OPCODES }, +}; + const struct spi_nor_manufacturer spi_nor_gigadevice = { .name = "gigadevice", .parts = gigadevice_nor_parts, .nparts = ARRAY_SIZE(gigadevice_nor_parts), + .fixups = gigadevice_fixups, + .nfixups = ARRAY_SIZE(gigadevice_fixups), }; diff --git a/drivers/mtd/spi-nor/issi.c b/drivers/mtd/spi-nor/issi.c index 18d9a00aa22e..2f057d731df2 100644 --- a/drivers/mtd/spi-nor/issi.c +++ b/drivers/mtd/spi-nor/issi.c @@ -52,13 +52,11 @@ static const struct flash_info issi_nor_parts[] = { .sector_size = SZ_32K, .size = SZ_64K, .no_sfdp_flags = SECT_4K, - .fixups = &pm25lv_nor_fixups }, { .name = "pm25lv010", .sector_size = SZ_32K, .size = SZ_128K, .no_sfdp_flags = SECT_4K, - .fixups = &pm25lv_nor_fixups }, { .id = SNOR_ID(0x7f, 0x9d, 0x20), .name = "is25cd512", @@ -103,8 +101,6 @@ static const struct flash_info issi_nor_parts[] = { }, { .id = SNOR_ID(0x9d, 0x60, 0x19), .name = "is25lp256", - .fixups = &is25lp256_fixups, - .fixup_flags = SPI_NOR_4B_OPCODES, }, { .id = SNOR_ID(0x9d, 0x70, 0x16), .name = "is25wp032", @@ -124,23 +120,40 @@ static const struct flash_info issi_nor_parts[] = { .id = SNOR_ID(0x9d, 0x70, 0x19), .name = "is25wp256", .flags = SPI_NOR_QUAD_PP, - .fixups = &is25lp256_fixups, - .fixup_flags = SPI_NOR_4B_OPCODES, } }; static void issi_nor_default_init(struct spi_nor *nor) { - nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable; + nor->params->qe_mask[0] = BIT(6); + nor->params->qe_mask[1] = 0; } static const struct spi_nor_fixups issi_fixups = { .default_init = issi_nor_default_init, }; +/* PM25LV parts have no JEDEC ID and are likely matched by name */ +static bool issi_pm25lv_match(const struct spi_nor *nor) +{ + const char *name = nor->info ? nor->info->name : NULL; + + return name && strstarts(name, "pm25lv"); +} + +static const struct spi_nor_fixup issi_fixup_list[] = { + { .fixups = &issi_fixups }, + { .match = issi_pm25lv_match, .fixups = &pm25lv_nor_fixups }, + { .id = SNOR_ID(0x9d, 0x60, 0x19), .fixups = &is25lp256_fixups, + .fixup_flags = SPI_NOR_4B_OPCODES }, + { .id = SNOR_ID(0x9d, 0x70, 0x19), .fixups = &is25lp256_fixups, + .fixup_flags = SPI_NOR_4B_OPCODES }, +}; + const struct spi_nor_manufacturer spi_nor_issi = { .name = "issi", .parts = issi_nor_parts, .nparts = ARRAY_SIZE(issi_nor_parts), - .fixups = &issi_fixups, + .fixups = issi_fixup_list, + .nfixups = ARRAY_SIZE(issi_fixup_list), }; diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c index e97f5cbd9aad..b3fdb4b2fa1c 100644 --- a/drivers/mtd/spi-nor/macronix.c +++ b/drivers/mtd/spi-nor/macronix.c @@ -40,7 +40,7 @@ mx25l25635_post_bfpt_fixups(struct spi_nor *nor, * its BFPT table. */ if (bfpt->dwords[SFDP_DWORD(5)] & BFPT_DWORD5_FAST_READ_4_4_4) - nor->flags |= SNOR_F_4B_OPCODES; + nor->params->flags |= SNOR_F_4B_OPCODES; return 0; } @@ -65,11 +65,12 @@ mx25l3255e_late_init_fixups(struct spi_nor *nor) /* * SFDP of MX25L3255E is JESD216, which does not include the Quad - * Enable bit Requirement in BFPT. As a result, during BFPT parsing, - * the quad_enable method is not set to spi_nor_sr1_bit6_quad_enable. - * Therefore, it is necessary to correct this setting by late_init. + * Enable bit Requirement in BFPT. As a result, during BFPT parsing + * the quad_enable mask is reset. Therefore, it is necessary to + * correct this setting by late_init. */ - params->quad_enable = spi_nor_sr1_bit6_quad_enable; + params->qe_mask[0] = BIT(6); + params->qe_mask[1] = 0; /* * In addition, MX25L3255E also supports 1-4-4 page program in 3-byte @@ -83,6 +84,25 @@ mx25l3255e_late_init_fixups(struct spi_nor *nor) return 0; } +static int +mx25l12805d_4pp3b_post_sfdp_fixups(struct spi_nor *nor) +{ + struct spi_nor_flash_parameter *params = nor->params; + + /* + * JEDEC ID 0xc22018 is shared by MX25L12805D (no SFDP, no 4PP) and + * MX25L12833F/MX25L12845G (support SFDP and 4PP in 3-byte mode). + * The legacy 05D lacks SFDP and will not execute this hook. For + * the newer flashes, 3-byte 1-4-4 PP is not defined in SFDP, so + * we safely enable it here. + */ + params->hwcaps.mask |= SNOR_HWCAPS_PP_1_4_4; + spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP_1_4_4], + SPINOR_OP_PP_1_4_4, SNOR_PROTO_1_4_4); + + return 0; +} + static const struct spi_nor_fixups mx25l25635_fixups = { .post_bfpt = mx25l25635_post_bfpt_fixups, .post_sfdp = macronix_qpp4b_post_sfdp_fixups, @@ -96,6 +116,10 @@ static const struct spi_nor_fixups mx25l3255e_fixups = { .late_init = mx25l3255e_late_init_fixups, }; +static const struct spi_nor_fixups mx25l12805d_4pp3b_fixups = { + .post_sfdp = mx25l12805d_4pp3b_post_sfdp_fixups, +}; + static const struct flash_info macronix_nor_parts[] = { { .id = SNOR_ID(0xc2, 0x20, 0x10), @@ -130,26 +154,23 @@ static const struct flash_info macronix_nor_parts[] = { .size = SZ_8M, .no_sfdp_flags = SECT_4K, }, { - /* MX25L12805D */ + /* MX25L12805D, MX25L12833F, MX25L12845G */ .id = SNOR_ID(0xc2, 0x20, 0x18), + .size = SZ_16M, .flags = SPI_NOR_HAS_LOCK | SPI_NOR_4BIT_BP, + .no_sfdp_flags = SECT_4K, }, { /* MX25L25635E, MX25L25645G */ .id = SNOR_ID(0xc2, 0x20, 0x19), - .fixups = &mx25l25635_fixups }, { - /* MX66L51235F */ + /* MX25L51245G, MX25L51273G, MX66L51235F */ .id = SNOR_ID(0xc2, 0x20, 0x1a), - .fixup_flags = SPI_NOR_4B_OPCODES, - .fixups = ¯onix_qpp4b_fixups, }, { /* MX66L1G45G */ .id = SNOR_ID(0xc2, 0x20, 0x1b), - .fixups = ¯onix_qpp4b_fixups, }, { /* MX66L2G45G */ .id = SNOR_ID(0xc2, 0x20, 0x1c), - .fixups = ¯onix_qpp4b_fixups, }, { .id = SNOR_ID(0xc2, 0x23, 0x14), .name = "mx25v8035f", @@ -186,17 +207,14 @@ static const struct flash_info macronix_nor_parts[] = { .size = SZ_16M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, }, { - /* MX25U51245G */ + /* MX25U51245G, MX25U51293G */ .id = SNOR_ID(0xc2, 0x25, 0x3a), - .fixups = ¯onix_qpp4b_fixups, }, { - /* MX66U1G45G */ + /* MX66U1G45G, MX66U1G93G */ .id = SNOR_ID(0xc2, 0x25, 0x3b), - .fixups = ¯onix_qpp4b_fixups, }, { /* MX66U2G45G */ .id = SNOR_ID(0xc2, 0x25, 0x3c), - .fixups = ¯onix_qpp4b_fixups, }, { .id = SNOR_ID(0xc2, 0x26, 0x18), .name = "mx25l12855e", @@ -228,7 +246,6 @@ static const struct flash_info macronix_nor_parts[] = { }, { /* MX25L3255E */ .id = SNOR_ID(0xc2, 0x9e, 0x16), - .fixups = &mx25l3255e_fixups, }, /* * This spares us of adding new flash entries for flashes that can be @@ -314,7 +331,8 @@ static int macronix_nor_set_octal_dtr(struct spi_nor *nor, bool enable) static void macronix_nor_default_init(struct spi_nor *nor) { - nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable; + nor->params->qe_mask[0] = BIT(6); + nor->params->qe_mask[1] = 0; } static int macronix_nor_late_init(struct spi_nor *nor) @@ -331,9 +349,24 @@ static const struct spi_nor_fixups macronix_nor_fixups = { .late_init = macronix_nor_late_init, }; +static const struct spi_nor_fixup macronix_fixups[] = { + { .fixups = ¯onix_nor_fixups }, + { .id = SNOR_ID(0xc2, 0x20, 0x18), .fixups = &mx25l12805d_4pp3b_fixups }, + { .id = SNOR_ID(0xc2, 0x20, 0x19), .fixups = &mx25l25635_fixups }, + { .id = SNOR_ID(0xc2, 0x20, 0x1a), .fixups = ¯onix_qpp4b_fixups, + .fixup_flags = SPI_NOR_4B_OPCODES }, + { .id = SNOR_ID(0xc2, 0x20, 0x1b), .fixups = ¯onix_qpp4b_fixups }, + { .id = SNOR_ID(0xc2, 0x20, 0x1c), .fixups = ¯onix_qpp4b_fixups }, + { .id = SNOR_ID(0xc2, 0x25, 0x3a), .fixups = ¯onix_qpp4b_fixups }, + { .id = SNOR_ID(0xc2, 0x25, 0x3b), .fixups = ¯onix_qpp4b_fixups }, + { .id = SNOR_ID(0xc2, 0x25, 0x3c), .fixups = ¯onix_qpp4b_fixups }, + { .id = SNOR_ID(0xc2, 0x9e, 0x16), .fixups = &mx25l3255e_fixups }, +}; + const struct spi_nor_manufacturer spi_nor_macronix = { .name = "macronix", .parts = macronix_nor_parts, .nparts = ARRAY_SIZE(macronix_nor_parts), - .fixups = ¯onix_nor_fixups, + .fixups = macronix_fixups, + .nfixups = ARRAY_SIZE(macronix_fixups), }; diff --git a/drivers/mtd/spi-nor/micron-st.c b/drivers/mtd/spi-nor/micron-st.c index c75b0a1cd567..f97f2dead8c2 100644 --- a/drivers/mtd/spi-nor/micron-st.c +++ b/drivers/mtd/spi-nor/micron-st.c @@ -131,7 +131,7 @@ static int micron_st_nor_four_die_late_init(struct spi_nor *nor) { struct spi_nor_flash_parameter *params = nor->params; - params->die_erase_opcode = SPINOR_OP_MT_DIE_ERASE; + params->opcodes.die_erase = SPINOR_OP_MT_DIE_ERASE; params->n_dice = 4; /* @@ -147,7 +147,7 @@ static int micron_st_nor_two_die_late_init(struct spi_nor *nor) { struct spi_nor_flash_parameter *params = nor->params; - params->die_erase_opcode = SPINOR_OP_MT_DIE_ERASE; + params->opcodes.die_erase = SPINOR_OP_MT_DIE_ERASE; params->n_dice = 2; /* @@ -177,7 +177,7 @@ static int mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor) spi_nor_set_pp_settings(&nor->params->page_programs[SNOR_CMD_PP_8_8_8_DTR], SPINOR_OP_PP_4B, SNOR_PROTO_8_8_8_DTR); - nor->cmd_ext_type = SPI_NOR_EXT_REPEAT; + nor->params->cmd_ext_type = SPI_NOR_EXT_REPEAT; nor->params->rdsr_dummy = 8; nor->params->rdsr_addr_nbytes = 0; @@ -205,14 +205,10 @@ static const struct flash_info micron_nor_parts[] = { /* MT35XU512ABA */ .id = SNOR_ID(0x2c, 0x5b, 0x1a), .mfr_flags = USE_FSR, - .fixup_flags = SPI_NOR_IO_MODE_EN_VOLATILE, - .fixups = &mt35xu512aba_fixups, }, { /* MT35XU01GBBA */ .id = SNOR_ID(0x2c, 0x5b, 0x1b), .mfr_flags = USE_FSR, - .fixup_flags = SPI_NOR_IO_MODE_EN_VOLATILE, - .fixups = &mt35_two_die_fixups, }, { .id = SNOR_ID(0x2c, 0x5b, 0x1c), .name = "mt35xu02g", @@ -220,8 +216,6 @@ static const struct flash_info micron_nor_parts[] = { .size = SZ_256M, .no_sfdp_flags = SECT_4K | SPI_NOR_OCTAL_READ, .mfr_flags = USE_FSR, - .fixup_flags = SPI_NOR_4B_OPCODES | SPI_NOR_IO_MODE_EN_VOLATILE, - .fixups = &mt35_two_die_fixups, }, }; @@ -229,10 +223,21 @@ static int mt25qu512a_post_bfpt_fixup(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, const struct sfdp_bfpt *bfpt) { - nor->flags &= ~SNOR_F_HAS_16BIT_SR; + nor->params->opcodes.write_sr1 = 0; return 0; } +/* + * n25q00a parts share the first same 3 ID bytes with mt25qu01g. + * In order to not mix the fixups, further filter out using the part name. + */ +static bool n25q00a_match(const struct spi_nor *nor) +{ + const char *name = nor->info ? nor->info->name : NULL; + + return name && !strncmp(name, "n25q00a", 7); +} + static const struct spi_nor_fixups mt25qu512a_fixups = { .post_bfpt = mt25qu512a_post_bfpt_fixup, }; @@ -395,7 +400,6 @@ static const struct flash_info st_nor_parts[] = { .name = "mt25ql256a", .size = SZ_32M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, - .fixup_flags = SPI_NOR_4B_OPCODES, .mfr_flags = USE_FSR, }, { .id = SNOR_ID(0x20, 0xba, 0x19), @@ -408,7 +412,6 @@ static const struct flash_info st_nor_parts[] = { .name = "mt25ql512a", .size = SZ_64M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, - .fixup_flags = SPI_NOR_4B_OPCODES, .mfr_flags = USE_FSR, }, { .id = SNOR_ID(0x20, 0xba, 0x20), @@ -426,14 +429,12 @@ static const struct flash_info st_nor_parts[] = { SPI_NOR_BP3_SR_BIT6, .no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ, .mfr_flags = USE_FSR, - .fixups = &n25q00_fixups, }, { .id = SNOR_ID(0x20, 0xba, 0x22), .name = "mt25ql02g", .size = SZ_256M, .no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ, .mfr_flags = USE_FSR, - .fixups = &mt25q02_fixups, }, { .id = SNOR_ID(0x20, 0xbb, 0x15), .name = "n25q016a", @@ -466,7 +467,6 @@ static const struct flash_info st_nor_parts[] = { .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP | SPI_NOR_BP3_SR_BIT6, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, - .fixup_flags = SPI_NOR_4B_OPCODES, .mfr_flags = USE_FSR, }, { .id = SNOR_ID(0x20, 0xbb, 0x19), @@ -480,7 +480,6 @@ static const struct flash_info st_nor_parts[] = { .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP | SPI_NOR_BP3_SR_BIT6, .mfr_flags = USE_FSR, - .fixups = &mt25qu512a_fixups, }, { .id = SNOR_ID(0x20, 0xbb, 0x20), .name = "n25q512a", @@ -493,21 +492,18 @@ static const struct flash_info st_nor_parts[] = { .id = SNOR_ID(0x20, 0xbb, 0x21, 0x10, 0x44, 0x00), .name = "mt25qu01g", .mfr_flags = USE_FSR, - .fixups = &mt25q01_fixups, }, { .id = SNOR_ID(0x20, 0xbb, 0x21), .name = "n25q00a", .size = SZ_128M, .no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ, .mfr_flags = USE_FSR, - .fixups = &n25q00_fixups, }, { .id = SNOR_ID(0x20, 0xbb, 0x22), .name = "mt25qu02g", .size = SZ_256M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, .mfr_flags = USE_FSR, - .fixups = &mt25q02_fixups, } }; @@ -632,8 +628,8 @@ static int micron_st_nor_ready(struct spi_nor *nor) static void micron_st_nor_default_init(struct spi_nor *nor) { - nor->flags |= SNOR_F_HAS_LOCK; - nor->flags &= ~SNOR_F_HAS_16BIT_SR; + nor->params->flags |= SNOR_F_HAS_LOCK; + nor->params->opcodes.write_sr1 = 0; nor->params->quad_enable = NULL; } @@ -657,16 +653,44 @@ static const struct spi_nor_fixups micron_st_nor_fixups = { .late_init = micron_st_nor_late_init, }; +static const struct spi_nor_fixup micron_fixups[] = { + { .fixups = µn_st_nor_fixups }, + { .id = SNOR_ID(0x2c, 0x5b, 0x1a), .fixups = &mt35xu512aba_fixups, + .fixup_flags = SPI_NOR_IO_MODE_EN_VOLATILE }, + { .id = SNOR_ID(0x2c, 0x5b, 0x1b), .fixups = &mt35_two_die_fixups, + .fixup_flags = SPI_NOR_IO_MODE_EN_VOLATILE }, + { .id = SNOR_ID(0x2c, 0x5b, 0x1c), .fixups = &mt35_two_die_fixups, + .fixup_flags = SPI_NOR_4B_OPCODES | SPI_NOR_IO_MODE_EN_VOLATILE }, +}; + +static const struct spi_nor_fixup st_fixups[] = { + { .fixups = µn_st_nor_fixups }, + { .id = SNOR_ID(0x20, 0xba, 0x19, 0x10, 0x44, 0x00), + .fixup_flags = SPI_NOR_4B_OPCODES }, + { .id = SNOR_ID(0x20, 0xba, 0x20, 0x10, 0x44, 0x00), + .fixup_flags = SPI_NOR_4B_OPCODES }, + { .id = SNOR_ID(0x20, 0xbb, 0x19, 0x10, 0x44, 0x00), + .fixup_flags = SPI_NOR_4B_OPCODES }, + { .id = SNOR_ID(0x20, 0xba, 0x21), .fixups = &n25q00_fixups }, + { .id = SNOR_ID(0x20, 0xba, 0x22), .fixups = &mt25q02_fixups }, + { .id = SNOR_ID(0x20, 0xbb, 0x20, 0x10, 0x44, 0x00), .fixups = &mt25qu512a_fixups }, + { .id = SNOR_ID(0x20, 0xbb, 0x21, 0x10, 0x44, 0x00), .fixups = &mt25q01_fixups }, + { .id = SNOR_ID(0x20, 0xbb, 0x21), .match = n25q00a_match, .fixups = &n25q00_fixups }, + { .id = SNOR_ID(0x20, 0xbb, 0x22), .fixups = &mt25q02_fixups }, +}; + const struct spi_nor_manufacturer spi_nor_micron = { .name = "micron", .parts = micron_nor_parts, .nparts = ARRAY_SIZE(micron_nor_parts), - .fixups = µn_st_nor_fixups, + .fixups = micron_fixups, + .nfixups = ARRAY_SIZE(micron_fixups), }; const struct spi_nor_manufacturer spi_nor_st = { .name = "st", .parts = st_nor_parts, .nparts = ARRAY_SIZE(st_nor_parts), - .fixups = µn_st_nor_fixups, + .fixups = st_fixups, + .nfixups = ARRAY_SIZE(st_fixups), }; diff --git a/drivers/mtd/spi-nor/otp.c b/drivers/mtd/spi-nor/otp.c index 7d0b145d78d8..bb9a801b3ceb 100644 --- a/drivers/mtd/spi-nor/otp.c +++ b/drivers/mtd/spi-nor/otp.c @@ -25,7 +25,7 @@ * Read a security register by using the SPINOR_OP_RSECR commands. * * In Winbond/GigaDevice datasheets the term "security register" stands for - * an one-time-programmable memory area, consisting of multiple bytes (usually + * a one-time-programmable memory area, consisting of multiple bytes (usually * 256). Thus one "security register" maps to one OTP region. * * This method is used on GigaDevice and Winbond flashes. @@ -175,24 +175,24 @@ static int spi_nor_otp_lock_bit_cr(unsigned int region) */ int spi_nor_otp_lock_sr2(struct spi_nor *nor, unsigned int region) { - u8 *cr = nor->bouncebuf; int ret, lock_bit; + u8 sr[2]; lock_bit = spi_nor_otp_lock_bit_cr(region); if (lock_bit < 0) return lock_bit; - ret = spi_nor_read_cr(nor, cr); + ret = spi_nor_read_sr1_and_sr2(nor, sr); if (ret) return ret; /* no need to write the register if region is already locked */ - if (cr[0] & lock_bit) + if (sr[1] & lock_bit) return 0; - cr[0] |= lock_bit; + sr[1] |= lock_bit; - return spi_nor_write_16bit_cr_and_check(nor, cr[0]); + return spi_nor_write_sr1_and_sr2_and_check(nor, sr); } /** @@ -207,18 +207,18 @@ int spi_nor_otp_lock_sr2(struct spi_nor *nor, unsigned int region) */ int spi_nor_otp_is_locked_sr2(struct spi_nor *nor, unsigned int region) { - u8 *cr = nor->bouncebuf; int ret, lock_bit; + u8 sr2; lock_bit = spi_nor_otp_lock_bit_cr(region); if (lock_bit < 0) return lock_bit; - ret = spi_nor_read_cr(nor, cr); + ret = spi_nor_read_sr2(nor, &sr2); if (ret) return ret; - return cr[0] & lock_bit; + return sr2 & lock_bit; } static loff_t spi_nor_otp_region_start(const struct spi_nor *nor, unsigned int region) diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index 4600983cb579..4a3f82e97cef 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -34,17 +34,6 @@ #define SFDP_SIGNATURE 0x50444653U -struct sfdp_header { - u32 signature; /* Ox50444653U <=> "SFDP" */ - u8 minor; - u8 major; - u8 nph; /* 0-base number of parameter headers */ - u8 unused; - - /* Basic Flash Parameter Table. */ - struct sfdp_parameter_header bfpt_header; -}; - /* Fast Read settings. */ struct sfdp_bfpt_read { /* The Fast Read x-y-z hardware capability in params->hwcaps.mask. */ @@ -126,6 +115,12 @@ struct sfdp_bfpt_erase { #define SFDP_4BAIT_DWORD_MAX 2 +/* + * Limit the total size of SFDP to a reasonable value to avoid allocating too + * much memory just of because the flash returned some insane values. + */ +#define SFDP_MAX_SIZE SZ_16K + struct sfdp_4bait { /* The hardware capability. */ u32 hwcaps; @@ -402,6 +397,45 @@ static void spi_nor_regions_sort_erase_types(struct spi_nor_erase_map *map) } /** + * spi_nor_post_bfpt_fixups() - Updates the flash's parameters and settings + * after BFPT has been parsed. Called only for flashes that define JESD216 SFDP + * tables. + * @nor: pointer to a 'struct spi_nor' + * @bfpt_header: pointer to the 'struct sfdp_parameter_header' describing + * the Basic Flash Parameter Table length and version + * @bfpt: pointer to the 'struct sfdp_bfpt' to be fixed up + * + * Used to tweak various flash parameters when information provided by the SFDP + * tables are wrong. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_post_bfpt_fixups(struct spi_nor *nor, + const struct sfdp_parameter_header *bfpt_header, + const struct sfdp_bfpt *bfpt) +{ + const struct spi_nor_fixup *fixups; + int ret; + unsigned int i; + + if (!nor->manufacturer || !nor->manufacturer->fixups) + return 0; + + fixups = nor->manufacturer->fixups; + + for (i = 0; i < nor->manufacturer->nfixups; i++) { + if (fixups[i].fixups && fixups[i].fixups->post_bfpt && + spi_nor_fixup_match(nor, &fixups[i])) { + ret = fixups[i].fixups->post_bfpt(nor, bfpt_header, bfpt); + if (ret) + return ret; + } + } + + return 0; +} + +/** * spi_nor_parse_bfpt() - read and parse the Basic Flash Parameter Table. * @nor: pointer to a 'struct spi_nor' * @bfpt_header: pointer to the 'struct sfdp_parameter_header' describing @@ -453,7 +487,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, bfpt_header->length * sizeof(u32)); addr = SFDP_PARAM_HEADER_PTP(bfpt_header); memset(&bfpt, 0, sizeof(bfpt)); - err = spi_nor_read_sfdp_dma_unsafe(nor, addr, len, &bfpt); + err = spi_nor_read_sfdp_dma_unsafe(nor, addr, len, &bfpt); if (err < 0) return err; @@ -561,34 +595,50 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, val >>= BFPT_DWORD11_PAGE_SIZE_SHIFT; params->page_size = 1U << val; + /* + * The standard declares various read and write status methods, some of + * them will be overloaded based on the QER field. + */ + params->opcodes.read_sr1 = SPINOR_OP_RDSR; + params->opcodes.read_sr2 = SPINOR_OP_RDCR; + params->opcodes.write_sr1 = SPINOR_OP_WRSR; + params->opcodes.write_sr1_and_sr2 = SPINOR_OP_WRSR; + + params->qe_mask[0] = 0; + params->qe_mask[1] = 0; + /* Quad Enable Requirements. */ switch (bfpt.dwords[SFDP_DWORD(15)] & BFPT_DWORD15_QER_MASK) { case BFPT_DWORD15_QER_NONE: - params->quad_enable = NULL; break; - case BFPT_DWORD15_QER_SR2_BIT1_BUGGY: + case BFPT_DWORD15_QER_SR2_BIT1_NO_1B_WR: /* * Writing only one byte to the Status Register has the * side-effect of clearing Status Register 2. */ + fallthrough; case BFPT_DWORD15_QER_SR2_BIT1_NO_RD: /* * Read Configuration Register (35h) instruction is not - * supported. + * supported. 16-bit writes expected. */ - nor->flags |= SNOR_F_HAS_16BIT_SR | SNOR_F_NO_READ_CR; - params->quad_enable = spi_nor_sr2_bit1_quad_enable; + params->opcodes.read_sr2 = 0; + params->opcodes.write_sr1 = 0; + params->qe_mask[1] = BIT(1); break; case BFPT_DWORD15_QER_SR1_BIT6: - nor->flags &= ~SNOR_F_HAS_16BIT_SR; - params->quad_enable = spi_nor_sr1_bit6_quad_enable; + params->opcodes.read_sr2 = 0; + params->opcodes.write_sr1_and_sr2 = 0; + params->qe_mask[0] = BIT(6); break; case BFPT_DWORD15_QER_SR2_BIT7: - nor->flags &= ~SNOR_F_HAS_16BIT_SR; - params->quad_enable = spi_nor_sr2_bit7_quad_enable; + params->opcodes.read_sr2 = SPINOR_OP_RDSR2; + params->opcodes.write_sr1_and_sr2 = 0; + params->opcodes.write_sr2 = SPINOR_OP_WRSR2; + params->qe_mask[1] = BIT(7); break; case BFPT_DWORD15_QER_SR2_BIT1: @@ -598,9 +648,14 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, * Register 2, so let's be cautious and keep the default * assumption of a 16-bit Write Status (01h) command. */ - nor->flags |= SNOR_F_HAS_16BIT_SR; + params->opcodes.write_sr1 = 0; + params->qe_mask[1] = BIT(1); + break; - params->quad_enable = spi_nor_sr2_bit1_quad_enable; + case BFPT_DWORD15_QER_SR2_BIT1_1B: + params->qe_mask[1] = BIT(1); + params->opcodes.write_sr1_and_sr2 = 0; + params->opcodes.write_sr2 = SPINOR_OP_WRSR2_ALT; break; default: @@ -608,6 +663,11 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, break; } + /* opcodes sanity check */ + WARN_ON(!params->opcodes.read_sr1 || + (!params->opcodes.write_sr1 && !params->opcodes.write_sr1_and_sr2) || + (!params->opcodes.read_sr2 && params->opcodes.write_sr2)); + dword = bfpt.dwords[SFDP_DWORD(16)] & BFPT_DWORD16_4B_ADDR_MODE_MASK; if (SFDP_MASK_CHECK(dword, BFPT_DWORD16_4B_ADDR_MODE_BRWR)) params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_brwr; @@ -620,7 +680,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, /* Soft Reset support. */ if (bfpt.dwords[SFDP_DWORD(16)] & BFPT_DWORD16_SWRST_EN_RST) - nor->flags |= SNOR_F_SOFT_RESET; + params->flags |= SNOR_F_SOFT_RESET; /* Stop here if not JESD216 rev C or later. */ if (bfpt_header->length == BFPT_DWORD_MAX_JESD216B) @@ -655,11 +715,11 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, /* 8D-8D-8D command extension. */ switch (bfpt.dwords[SFDP_DWORD(18)] & BFPT_DWORD18_CMD_EXT_MASK) { case BFPT_DWORD18_CMD_EXT_REP: - nor->cmd_ext_type = SPI_NOR_EXT_REPEAT; + params->cmd_ext_type = SPI_NOR_EXT_REPEAT; break; case BFPT_DWORD18_CMD_EXT_INV: - nor->cmd_ext_type = SPI_NOR_EXT_INVERT; + params->cmd_ext_type = SPI_NOR_EXT_INVERT; break; case BFPT_DWORD18_CMD_EXT_RES: @@ -673,7 +733,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, /* Byte order in 8D-8D-8D mode */ if (bfpt.dwords[SFDP_DWORD(18)] & BFPT_DWORD18_BYTE_ORDER_SWAPPED) - nor->flags |= SNOR_F_SWAP16; + params->flags |= SNOR_F_SWAP16; return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt); } @@ -702,12 +762,19 @@ static u8 spi_nor_smpt_addr_nbytes(const struct spi_nor *nor, const u32 settings static void spi_nor_smpt_read_dummy_fixups(const struct spi_nor *nor, u8 *read_dummy) { - if (nor->manufacturer && nor->manufacturer->fixups && - nor->manufacturer->fixups->smpt_read_dummy) - nor->manufacturer->fixups->smpt_read_dummy(nor, read_dummy); + const struct spi_nor_fixup *fixups; + unsigned int i; - if (nor->info->fixups && nor->info->fixups->smpt_read_dummy) - nor->info->fixups->smpt_read_dummy(nor, read_dummy); + if (!nor->manufacturer || !nor->manufacturer->fixups) + return; + + fixups = nor->manufacturer->fixups; + + for (i = 0; i < nor->manufacturer->nfixups; i++) { + if (fixups[i].fixups && fixups[i].fixups->smpt_read_dummy && + spi_nor_fixup_match(nor, &fixups[i])) + fixups[i].fixups->smpt_read_dummy(nor, read_dummy); + } } /** @@ -732,12 +799,19 @@ static u8 spi_nor_smpt_read_dummy(const struct spi_nor *nor, const u32 settings) static void spi_nor_smpt_map_id_fixups(const struct spi_nor *nor, u8 *map_id) { - if (nor->manufacturer && nor->manufacturer->fixups && - nor->manufacturer->fixups->smpt_map_id) - nor->manufacturer->fixups->smpt_map_id(nor, map_id); + const struct spi_nor_fixup *fixups; + unsigned int i; + + if (!nor->manufacturer || !nor->manufacturer->fixups) + return; - if (nor->info->fixups && nor->info->fixups->smpt_map_id) - nor->info->fixups->smpt_map_id(nor, map_id); + fixups = nor->manufacturer->fixups; + + for (i = 0; i < nor->manufacturer->nfixups; i++) { + if (fixups[i].fixups && fixups[i].fixups->smpt_map_id && + spi_nor_fixup_match(nor, &fixups[i])) + fixups[i].fixups->smpt_map_id(nor, map_id); + } } /** @@ -1017,6 +1091,8 @@ static int spi_nor_parse_4bait(struct spi_nor *nor, { SNOR_HWCAPS_PP, BIT(6) }, { SNOR_HWCAPS_PP_1_1_4, BIT(7) }, { SNOR_HWCAPS_PP_1_4_4, BIT(8) }, + { SNOR_HWCAPS_PP_1_1_8, BIT(23) }, + { SNOR_HWCAPS_PP_1_8_8, BIT(24) }, }; static const struct sfdp_4bait erases[SNOR_ERASE_TYPE_MAX] = { { 0u /* not used */, BIT(9) }, @@ -1143,6 +1219,14 @@ static int spi_nor_parse_4bait(struct spi_nor *nor, spi_nor_set_pp_settings(¶ms_pp[SNOR_CMD_PP_1_4_4], SPINOR_OP_PP_1_4_4_4B, SNOR_PROTO_1_4_4); + if (pp_hwcaps & SNOR_HWCAPS_PP_1_1_8) + spi_nor_set_pp_settings(¶ms_pp[SNOR_CMD_PP_1_1_8], + SPINOR_OP_PP_1_1_8_4B, + SNOR_PROTO_1_1_8); + if (pp_hwcaps & SNOR_HWCAPS_PP_1_8_8) + spi_nor_set_pp_settings(¶ms_pp[SNOR_CMD_PP_1_8_8], + SPINOR_OP_PP_1_8_8_4B, + SNOR_PROTO_1_8_8); for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) { if (erase_mask & BIT(i)) @@ -1160,7 +1244,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor, * SFDP compliant memories. */ params->addr_nbytes = 4; - nor->flags |= SNOR_F_4B_OPCODES | SNOR_F_HAS_4BAIT; + params->flags |= SNOR_F_4B_OPCODES | SNOR_F_HAS_4BAIT; /* fall through */ out: @@ -1175,6 +1259,7 @@ out: #define PROFILE1_DWORD5_DUMMY_166MHZ GENMASK(31, 27) #define PROFILE1_DWORD5_DUMMY_133MHZ GENMASK(21, 17) #define PROFILE1_DWORD5_DUMMY_100MHZ GENMASK(11, 7) +#define SFDP_PROFILE1_DWORD_MIN 5 /** * spi_nor_parse_profile1() - parse the xSPI Profile 1.0 table @@ -1192,6 +1277,9 @@ static int spi_nor_parse_profile1(struct spi_nor *nor, int ret; u8 dummy, opcode; + if (profile1_header->length < SFDP_PROFILE1_DWORD_MIN) + return -EINVAL; + len = profile1_header->length * sizeof(*dwords); dwords = kmalloc(len, GFP_KERNEL); if (!dwords) @@ -1262,6 +1350,7 @@ out: } #define SCCR_DWORD22_OCTAL_DTR_EN_VOLATILE BIT(31) +#define SFDP_SCCR_DWORD_MIN 22 /** * spi_nor_parse_sccr() - Parse the Status, Control and Configuration Register @@ -1280,6 +1369,9 @@ static int spi_nor_parse_sccr(struct spi_nor *nor, size_t len; int ret; + if (sccr_header->length < SFDP_SCCR_DWORD_MIN) + return -EINVAL; + len = sccr_header->length * sizeof(*dwords); dwords = kmalloc(len, GFP_KERNEL); if (!dwords) @@ -1306,7 +1398,7 @@ static int spi_nor_parse_sccr(struct spi_nor *nor, if (FIELD_GET(SCCR_DWORD22_OCTAL_DTR_EN_VOLATILE, dwords[SFDP_DWORD(22)])) - nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE; + params->flags |= SNOR_F_IO_MODE_EN_VOLATILE; out: kfree(dwords); @@ -1381,17 +1473,23 @@ out: */ static int spi_nor_post_sfdp_fixups(struct spi_nor *nor) { + const struct spi_nor_fixup *fixups; + unsigned int i; int ret; - if (nor->manufacturer && nor->manufacturer->fixups && - nor->manufacturer->fixups->post_sfdp) { - ret = nor->manufacturer->fixups->post_sfdp(nor); - if (ret) - return ret; - } + if (!nor->manufacturer || !nor->manufacturer->fixups) + return 0; - if (nor->info->fixups && nor->info->fixups->post_sfdp) - return nor->info->fixups->post_sfdp(nor); + fixups = nor->manufacturer->fixups; + + for (i = 0; i < nor->manufacturer->nfixups; i++) { + if (fixups[i].fixups && fixups[i].fixups->post_sfdp && + spi_nor_fixup_match(nor, &fixups[i])) { + ret = fixups[i].fixups->post_sfdp(nor); + if (ret) + return ret; + } + } return 0; } @@ -1433,12 +1531,16 @@ int spi_nor_check_sfdp_signature(struct spi_nor *nor) * runtime the main parameters needed to perform basic SPI flash operations such * as Fast Read, Page Program or Sector Erase commands. * + * Because the parsing is optional, all the settings have to be reverted. IOW, + * nothing of struct spi_nor shall be changed. + * * Return: 0 on success, -errno otherwise. */ int spi_nor_parse_sfdp(struct spi_nor *nor) { const struct sfdp_parameter_header *param_header, *bfpt_header; struct sfdp_parameter_header *param_headers = NULL; + struct spi_nor_flash_parameter *save_params; struct sfdp_header header; struct device *dev = nor->dev; struct sfdp *sfdp; @@ -1446,15 +1548,26 @@ int spi_nor_parse_sfdp(struct spi_nor *nor) size_t psize; int i, err; + /* + * Get a backup of all the parameter to roll back to in case of an + * error. + */ + save_params = kmalloc_objs(*save_params, 2); + if (!save_params) + return -ENOMEM; + save_params[0] = *nor->params; + /* Get the SFDP header. */ err = spi_nor_read_sfdp_dma_unsafe(nor, 0, sizeof(header), &header); if (err < 0) - return err; + goto free_save_params; /* Check the SFDP header version. */ if (le32_to_cpu(header.signature) != SFDP_SIGNATURE || - header.major != SFDP_JESD216_MAJOR) - return -EINVAL; + header.major != SFDP_JESD216_MAJOR) { + err = -EINVAL; + goto free_save_params; + } /* * Verify that the first and only mandatory parameter header is a @@ -1462,8 +1575,10 @@ int spi_nor_parse_sfdp(struct spi_nor *nor) */ bfpt_header = &header.bfpt_header; if (SFDP_PARAM_HEADER_ID(bfpt_header) != SFDP_BFPT_ID || - bfpt_header->major != SFDP_JESD216_MAJOR) - return -EINVAL; + bfpt_header->major != SFDP_JESD216_MAJOR) { + err = -EINVAL; + goto free_save_params; + } sfdp_size = SFDP_PARAM_HEADER_PTP(bfpt_header) + SFDP_PARAM_HEADER_PARAM_LEN(bfpt_header); @@ -1483,14 +1598,16 @@ int spi_nor_parse_sfdp(struct spi_nor *nor) psize = header.nph * sizeof(*param_headers); param_headers = kmalloc(psize, GFP_KERNEL); - if (!param_headers) - return -ENOMEM; + if (!param_headers) { + err = -ENOMEM; + goto free_save_params; + } err = spi_nor_read_sfdp(nor, sizeof(header), psize, param_headers); if (err < 0) { dev_dbg(dev, "failed to read SFDP parameter headers\n"); - goto exit; + goto free_param_headers; } } @@ -1505,20 +1622,16 @@ int spi_nor_parse_sfdp(struct spi_nor *nor) SFDP_PARAM_HEADER_PARAM_LEN(param_header)); } - /* - * Limit the total size to a reasonable value to avoid allocating too - * much memory just of because the flash returned some insane values. - */ - if (sfdp_size > PAGE_SIZE) { + if (sfdp_size > SFDP_MAX_SIZE) { dev_dbg(dev, "SFDP data (%zu) too big, truncating\n", sfdp_size); - sfdp_size = PAGE_SIZE; + sfdp_size = SFDP_MAX_SIZE; } sfdp = devm_kzalloc(dev, sizeof(*sfdp), GFP_KERNEL); if (!sfdp) { err = -ENOMEM; - goto exit; + goto free_param_headers; } /* @@ -1532,16 +1645,13 @@ int spi_nor_parse_sfdp(struct spi_nor *nor) sizeof(*sfdp->dwords), GFP_KERNEL); if (!sfdp->dwords) { err = -ENOMEM; - devm_kfree(dev, sfdp); - goto exit; + goto free_sfdp; } err = spi_nor_read_sfdp(nor, 0, sfdp_size, sfdp->dwords); if (err < 0) { dev_dbg(dev, "failed to read SFDP data\n"); - devm_kfree(dev, sfdp->dwords); - devm_kfree(dev, sfdp); - goto exit; + goto free_dwords; } nor->sfdp = sfdp; @@ -1563,10 +1673,11 @@ int spi_nor_parse_sfdp(struct spi_nor *nor) err = spi_nor_parse_bfpt(nor, bfpt_header); if (err) - goto exit; + goto clear_sfdp_ptr; /* Parse optional parameter tables. */ for (i = 0; i < header.nph; i++) { + save_params[1] = *nor->params; param_header = ¶m_headers[i]; switch (SFDP_PARAM_HEADER_ID(param_header)) { @@ -1600,15 +1711,46 @@ int spi_nor_parse_sfdp(struct spi_nor *nor) /* * Let's not drop all information we extracted so far * if optional table parsers fail. In case of failing, - * each optional parser is responsible to roll back to - * the previously known spi_nor data. + * roll back to the previously known + * spi_nor_flash_parameter data. */ err = 0; + *nor->params = save_params[1]; } } err = spi_nor_post_sfdp_fixups(nor); -exit: + if (err) + goto clear_sfdp_ptr; + + kfree(param_headers); + kfree(save_params); + + return 0; + +clear_sfdp_ptr: + nor->sfdp = NULL; +free_dwords: + devm_kfree(dev, sfdp->dwords); +free_sfdp: + devm_kfree(dev, sfdp); +free_param_headers: kfree(param_headers); + if (err) + *nor->params = save_params[0]; +free_save_params: + kfree(save_params); + return err; } + +/** + * spi_nor_sfdp_get_header() - retrieves the SFDP header + * @nor: pointer to a 'struct spi_nor' with a valid SFDP field + * + * Return: the cached SFDP header. + */ +struct sfdp_header *spi_nor_sfdp_get_header(const struct spi_nor *nor) +{ + return (struct sfdp_header *)nor->sfdp->dwords; +} diff --git a/drivers/mtd/spi-nor/sfdp.h b/drivers/mtd/spi-nor/sfdp.h index f74a0eb339ea..1e49ed548a7e 100644 --- a/drivers/mtd/spi-nor/sfdp.h +++ b/drivers/mtd/spi-nor/sfdp.h @@ -12,6 +12,7 @@ #define SFDP_JESD216_MINOR 0 #define SFDP_JESD216A_MINOR 5 #define SFDP_JESD216B_MINOR 6 +#define SFDP_JESD216F_MINOR 10 /* SFDP DWORDS are indexed from 1 but C arrays are indexed from 0. */ #define SFDP_DWORD(i) ((i) - 1) @@ -81,14 +82,21 @@ struct sfdp_bfpt { * instruction 35h. QE is set via Write Status instruction 01h with * two data bytes where bit 1 of the second byte is one. * [...] + * (from JESD216 rev F) + * - 110b: QE is bit 1 of status register 2. Status register 1 is read using + * Read Status instruction 05h. Status register2 is read using + * instruction 35h, and status register 3 is read using instruction 15h. + * QE is set via Write Status Register instruction 31h with one data + * byte. */ #define BFPT_DWORD15_QER_MASK GENMASK(22, 20) #define BFPT_DWORD15_QER_NONE (0x0UL << 20) /* Micron */ -#define BFPT_DWORD15_QER_SR2_BIT1_BUGGY (0x1UL << 20) +#define BFPT_DWORD15_QER_SR2_BIT1_NO_1B_WR (0x1UL << 20) #define BFPT_DWORD15_QER_SR1_BIT6 (0x2UL << 20) /* Macronix */ #define BFPT_DWORD15_QER_SR2_BIT7 (0x3UL << 20) #define BFPT_DWORD15_QER_SR2_BIT1_NO_RD (0x4UL << 20) #define BFPT_DWORD15_QER_SR2_BIT1 (0x5UL << 20) /* Spansion */ +#define BFPT_DWORD15_QER_SR2_BIT1_1B (0x6UL << 20) /* Winbond */ #define BFPT_DWORD16_EN4B_MASK GENMASK(31, 24) #define BFPT_DWORD16_EN4B_ALWAYS_4B BIT(30) @@ -141,4 +149,17 @@ struct sfdp_parameter_header { u8 id_msb; }; +struct sfdp_header { + u32 signature; /* Ox50444653U <=> "SFDP" */ + u8 minor; + u8 major; + u8 nph; /* 0-base number of parameter headers */ + u8 unused; + + /* Basic Flash Parameter Table. */ + struct sfdp_parameter_header bfpt_header; +}; + +struct sfdp_header *spi_nor_sfdp_get_header(const struct spi_nor *nor); + #endif /* __LINUX_MTD_SFDP_H */ diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index 65227d989de1..bc50881d5648 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -399,8 +399,9 @@ static int cypress_nor_determine_addr_mode_by_sr1(struct spi_nor *nor, nor->bouncebuf); bool is3byte, is4byte; int ret; + u8 sr; - ret = spi_nor_read_sr(nor, &nor->bouncebuf[1]); + ret = spi_nor_read_sr1(nor, &sr); if (ret) return ret; @@ -408,7 +409,7 @@ static int cypress_nor_determine_addr_mode_by_sr1(struct spi_nor *nor, if (ret) return ret; - is3byte = (nor->bouncebuf[0] == nor->bouncebuf[1]); + is3byte = (nor->bouncebuf[0] == sr); op = (struct spi_mem_op) CYPRESS_NOR_RD_ANY_REG_OP(4, SPINOR_REG_CYPRESS_STR1V, 0, @@ -417,7 +418,7 @@ static int cypress_nor_determine_addr_mode_by_sr1(struct spi_nor *nor, if (ret) return ret; - is4byte = (nor->bouncebuf[0] == nor->bouncebuf[1]); + is4byte = (nor->bouncebuf[0] == sr); if (is3byte == is4byte) return -EIO; @@ -537,7 +538,7 @@ static void cypress_nor_ecc_init(struct spi_nor *nor) * same ECC data unit without an erase are not allowed. */ nor->params->writesize = 16; - nor->flags |= SNOR_F_ECC; + nor->params->flags |= SNOR_F_ECC; } static int @@ -545,7 +546,6 @@ s25fs256t_post_bfpt_fixup(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, const struct sfdp_bfpt *bfpt) { - struct spi_mem_op op; int ret; /* Assign 4-byte address mode method that is not determined in BFPT */ @@ -555,19 +555,6 @@ s25fs256t_post_bfpt_fixup(struct spi_nor *nor, if (ret) return ret; - /* Read Architecture Configuration Register (ARCFN) */ - op = (struct spi_mem_op) - CYPRESS_NOR_RD_ANY_REG_OP(nor->params->addr_mode_nbytes, - SPINOR_REG_CYPRESS_ARCFN, 1, - nor->bouncebuf); - ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); - if (ret) - return ret; - - /* ARCFN value must be 0 if uniform sector is selected */ - if (nor->bouncebuf[0]) - return -ENODEV; - return 0; } @@ -598,6 +585,22 @@ static int s25fs256t_post_sfdp_fixup(struct spi_nor *nor) static int s25fs256t_late_init(struct spi_nor *nor) { + struct spi_mem_op op; + int ret; + + /* Read Architecture Configuration Register (ARCFN) */ + op = (struct spi_mem_op) + CYPRESS_NOR_RD_ANY_REG_OP(nor->params->addr_mode_nbytes, + SPINOR_REG_CYPRESS_ARCFN, 1, + nor->bouncebuf); + ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); + if (ret) + return ret; + + /* ARCFN value must be 0 if uniform sector is selected */ + if (nor->bouncebuf[0]) + return -ENODEV; + cypress_nor_ecc_init(nor); return 0; @@ -675,7 +678,7 @@ static int s25hx_t_late_init(struct spi_nor *nor) cypress_nor_ecc_init(nor); if (params->n_dice > 1) - params->die_erase_opcode = SPINOR_OP_CYPRESS_DIE_ERASE; + params->opcodes.die_erase = SPINOR_OP_CYPRESS_DIE_ERASE; return 0; } @@ -763,7 +766,7 @@ static int s28hx_t_late_init(struct spi_nor *nor) cypress_nor_ecc_init(nor); if (params->n_dice > 1) - params->die_erase_opcode = SPINOR_OP_CYPRESS_DIE_ERASE; + params->opcodes.die_erase = SPINOR_OP_CYPRESS_DIE_ERASE; return 0; } @@ -868,13 +871,6 @@ static const struct flash_info spansion_nor_parts[] = { .name = "s25fl256s0", .size = SZ_32M, .sector_size = SZ_256K, - .no_sfdp_flags = SPI_NOR_SKIP_SFDP | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, - .mfr_flags = USE_CLSR, - }, { - .id = SNOR_ID(0x01, 0x02, 0x19, 0x4d, 0x00, 0x81), - .name = "s25fs256s0", - .size = SZ_32M, - .sector_size = SZ_256K, .no_sfdp_flags = SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, .mfr_flags = USE_CLSR, }, { @@ -904,7 +900,6 @@ static const struct flash_info spansion_nor_parts[] = { .sector_size = SZ_256K, .no_sfdp_flags = SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, .mfr_flags = USE_CLSR, - .fixups = &s25fs_s_nor_fixups, }, { .id = SNOR_ID(0x01, 0x20, 0x18, 0x03, 0x00), .name = "s25sl12800", @@ -940,7 +935,6 @@ static const struct flash_info spansion_nor_parts[] = { .size = SZ_16M, .no_sfdp_flags = SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, .mfr_flags = USE_CLSR, - .fixups = &s25fs_s_nor_fixups, }, { .id = SNOR_ID(0x01, 0x20, 0x18, 0x4d, 0x01), .name = "s25fl129p1", @@ -977,19 +971,16 @@ static const struct flash_info spansion_nor_parts[] = { .name = "s25fl064l", .size = SZ_8M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, - .fixup_flags = SPI_NOR_4B_OPCODES, }, { .id = SNOR_ID(0x01, 0x60, 0x18), .name = "s25fl128l", .size = SZ_16M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, - .fixup_flags = SPI_NOR_4B_OPCODES, }, { .id = SNOR_ID(0x01, 0x60, 0x19), .name = "s25fl256l", .size = SZ_32M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, - .fixup_flags = SPI_NOR_4B_OPCODES, }, { .id = SNOR_ID(0x04, 0x2c, 0xc2, 0x7f, 0x7f, 0x7f), .name = "cy15x104q", @@ -1000,76 +991,61 @@ static const struct flash_info spansion_nor_parts[] = { .id = SNOR_ID(0x34, 0x2a, 0x1a, 0x0f, 0x03, 0x90), .name = "s25hl512t", .mfr_flags = USE_CLPEF, - .fixups = &s25hx_t_fixups }, { .id = SNOR_ID(0x34, 0x2a, 0x1b, 0x0f, 0x03, 0x90), .name = "s25hl01gt", .mfr_flags = USE_CLPEF, - .fixups = &s25hx_t_fixups }, { .id = SNOR_ID(0x34, 0x2a, 0x1c, 0x0f, 0x00, 0x90), .name = "s25hl02gt", .mfr_flags = USE_CLPEF, - .fixups = &s25hx_t_fixups }, { .id = SNOR_ID(0x34, 0x2b, 0x19, 0x0f, 0x08, 0x90), .name = "s25fs256t", .mfr_flags = USE_CLPEF, - .fixups = &s25fs256t_fixups }, { .id = SNOR_ID(0x34, 0x2b, 0x1a, 0x0f, 0x03, 0x90), .name = "s25hs512t", .mfr_flags = USE_CLPEF, - .fixups = &s25hx_t_fixups }, { .id = SNOR_ID(0x34, 0x2b, 0x1b, 0x0f, 0x03, 0x90), .name = "s25hs01gt", .mfr_flags = USE_CLPEF, - .fixups = &s25hx_t_fixups }, { .id = SNOR_ID(0x34, 0x2b, 0x1c, 0x0f, 0x00, 0x90), .name = "s25hs02gt", .mfr_flags = USE_CLPEF, - .fixups = &s25hx_t_fixups }, { /* S28HL256T */ .id = SNOR_ID(0x34, 0x5a, 0x19), .mfr_flags = USE_CLPEF, - .fixups = &s28hx_t_fixups, }, { .id = SNOR_ID(0x34, 0x5a, 0x1a), .name = "s28hl512t", .mfr_flags = USE_CLPEF, - .fixups = &s28hx_t_fixups, }, { .id = SNOR_ID(0x34, 0x5a, 0x1b), .name = "s28hl01gt", .mfr_flags = USE_CLPEF, - .fixups = &s28hx_t_fixups, }, { /* S28HL02GT */ .id = SNOR_ID(0x34, 0x5a, 0x1c), .mfr_flags = USE_CLPEF, - .fixups = &s28hx_t_fixups, }, { .id = SNOR_ID(0x34, 0x5b, 0x19), .mfr_flags = USE_CLPEF, - .fixups = &s28hx_t_fixups, }, { .id = SNOR_ID(0x34, 0x5b, 0x1a), .name = "s28hs512t", .mfr_flags = USE_CLPEF, - .fixups = &s28hx_t_fixups, }, { .id = SNOR_ID(0x34, 0x5b, 0x1b), .name = "s28hs01gt", .mfr_flags = USE_CLPEF, - .fixups = &s28hx_t_fixups, }, { .id = SNOR_ID(0x34, 0x5b, 0x1c), .name = "s28hs02gt", .mfr_flags = USE_CLPEF, - .fixups = &s28hx_t_fixups, }, { .id = SNOR_ID(0xef, 0x40, 0x13), .name = "s25fl004k", @@ -1103,13 +1079,14 @@ static const struct flash_info spansion_nor_parts[] = { static int spansion_nor_sr_ready_and_clear(struct spi_nor *nor) { int ret; + u8 sr; - ret = spi_nor_read_sr(nor, nor->bouncebuf); + ret = spi_nor_read_sr1(nor, &sr); if (ret) return ret; - if (nor->bouncebuf[0] & (SR_E_ERR | SR_P_ERR)) { - if (nor->bouncebuf[0] & SR_E_ERR) + if (sr & (SR_E_ERR | SR_P_ERR)) { + if (sr & SR_E_ERR) dev_err(nor->dev, "Erase Error occurred\n"); else dev_err(nor->dev, "Programming Error occurred\n"); @@ -1129,7 +1106,7 @@ static int spansion_nor_sr_ready_and_clear(struct spi_nor *nor) return -EIO; } - return !(nor->bouncebuf[0] & SR_WIP); + return !(sr & SR_WIP); } static int spansion_nor_late_init(struct spi_nor *nor) @@ -1139,7 +1116,7 @@ static int spansion_nor_late_init(struct spi_nor *nor) u8 mfr_flags = nor->info->mfr_flags; if (params->size > SZ_16M) { - nor->flags |= SNOR_F_4B_OPCODES; + params->flags |= SNOR_F_4B_OPCODES; /* No small sector erase for 4-byte command set */ nor->erase_opcode = SPINOR_OP_SE; nor->mtd.erasesize = nor->info->sector_size ?: @@ -1168,9 +1145,35 @@ static const struct spi_nor_fixups spansion_nor_fixups = { .late_init = spansion_nor_late_init, }; +static const struct spi_nor_fixup spansion_fixups[] = { + { .fixups = &spansion_nor_fixups }, + { .id = SNOR_ID(0x01, 0x02, 0x20, 0x4d, 0x00, 0x81), .fixups = &s25fs_s_nor_fixups }, + { .id = SNOR_ID(0x01, 0x02, 0x19, 0x4d, 0x01, 0x81), .fixups = &s25fs_s_nor_fixups }, + { .id = SNOR_ID(0x01, 0x20, 0x18, 0x4d, 0x01, 0x81), .fixups = &s25fs_s_nor_fixups }, + { .id = SNOR_ID(0x01, 0x60, 0x17), .fixup_flags = SPI_NOR_4B_OPCODES }, + { .id = SNOR_ID(0x01, 0x60, 0x18), .fixup_flags = SPI_NOR_4B_OPCODES }, + { .id = SNOR_ID(0x01, 0x60, 0x19), .fixup_flags = SPI_NOR_4B_OPCODES }, + { .id = SNOR_ID(0x34, 0x2a, 0x1a, 0x0f, 0x03, 0x90), .fixups = &s25hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x2a, 0x1b, 0x0f, 0x03, 0x90), .fixups = &s25hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x2a, 0x1c, 0x0f, 0x00, 0x90), .fixups = &s25hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x2b, 0x19, 0x0f, 0x08, 0x90), .fixups = &s25fs256t_fixups }, + { .id = SNOR_ID(0x34, 0x2b, 0x1a, 0x0f, 0x03, 0x90), .fixups = &s25hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x2b, 0x1b, 0x0f, 0x03, 0x90), .fixups = &s25hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x2b, 0x1c, 0x0f, 0x00, 0x90), .fixups = &s25hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x5a, 0x19), .fixups = &s28hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x5a, 0x1a), .fixups = &s28hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x5a, 0x1b), .fixups = &s28hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x5a, 0x1c), .fixups = &s28hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x5b, 0x19), .fixups = &s28hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x5b, 0x1a), .fixups = &s28hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x5b, 0x1b), .fixups = &s28hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x5b, 0x1c), .fixups = &s28hx_t_fixups }, +}; + const struct spi_nor_manufacturer spi_nor_spansion = { .name = "spansion", .parts = spansion_nor_parts, .nparts = ARRAY_SIZE(spansion_nor_parts), - .fixups = &spansion_nor_fixups, + .fixups = spansion_fixups, + .nfixups = ARRAY_SIZE(spansion_fixups), }; diff --git a/drivers/mtd/spi-nor/sst.c b/drivers/mtd/spi-nor/sst.c index db02c14ba16f..c460803b278b 100644 --- a/drivers/mtd/spi-nor/sst.c +++ b/drivers/mtd/spi-nor/sst.c @@ -21,16 +21,17 @@ static int sst26vf_nor_lock(struct spi_nor *nor, loff_t ofs, u64 len) static int sst26vf_nor_unlock(struct spi_nor *nor, loff_t ofs, u64 len) { int ret; + u8 cr; /* 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); + ret = spi_nor_read_sr2(nor, &cr); if (ret) return ret; - if (!(nor->bouncebuf[0] & SST26VF_CR_BPNV)) { + if (!(cr & SST26VF_CR_BPNV)) { dev_dbg(nor->dev, "Any block has been permanently locked\n"); return -EINVAL; } @@ -151,14 +152,12 @@ static const struct flash_info sst_nor_parts[] = { .id = SNOR_ID(0xbf, 0x26, 0x42), .name = "sst26vf032b", .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, - .fixups = &sst26vf_nor_fixups, }, { .id = SNOR_ID(0xbf, 0x26, 0x43), .name = "sst26vf064b", .size = SZ_8M, .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, - .fixups = &sst26vf_nor_fixups, }, { .id = SNOR_ID(0xbf, 0x26, 0x51), .name = "sst26wf016b", @@ -277,9 +276,16 @@ static const struct spi_nor_fixups sst_nor_fixups = { .late_init = sst_nor_late_init, }; +static const struct spi_nor_fixup sst_fixups[] = { + { .fixups = &sst_nor_fixups }, + { .id = SNOR_ID(0xbf, 0x26, 0x42), .fixups = &sst26vf_nor_fixups }, + { .id = SNOR_ID(0xbf, 0x26, 0x43), .fixups = &sst26vf_nor_fixups }, +}; + const struct spi_nor_manufacturer spi_nor_sst = { .name = "sst", .parts = sst_nor_parts, .nparts = ARRAY_SIZE(sst_nor_parts), - .fixups = &sst_nor_fixups, + .fixups = sst_fixups, + .nfixups = ARRAY_SIZE(sst_fixups), }; diff --git a/drivers/mtd/spi-nor/swp.c b/drivers/mtd/spi-nor/swp.c index 235070b215d1..7e667e4ca84d 100644 --- a/drivers/mtd/spi-nor/swp.c +++ b/drivers/mtd/spi-nor/swp.c @@ -13,12 +13,13 @@ static u8 spi_nor_get_sr_bp_mask(struct spi_nor *nor) { + struct spi_nor_flash_parameter *params = nor->params; u8 mask = SR_BP2 | SR_BP1 | SR_BP0; - if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6) + if (params->flags & SNOR_F_HAS_SR_BP3_BIT6) return mask | SR_BP3_BIT6; - if (nor->flags & SNOR_F_HAS_4BIT_BP) + if (params->flags & SNOR_F_HAS_4BIT_BP) return mask | SR_BP3; return mask; @@ -26,9 +27,11 @@ static u8 spi_nor_get_sr_bp_mask(struct spi_nor *nor) static u8 spi_nor_get_sr_tb_mask(struct spi_nor *nor) { - if (nor->flags & SNOR_F_HAS_SR_TB_BIT6) + struct spi_nor_flash_parameter *params = nor->params; + + if (params->flags & SNOR_F_HAS_SR_TB_BIT6) return SR_TB_BIT6; - else if (nor->flags & SNOR_F_HAS_SR_TB) + else if (params->flags & SNOR_F_HAS_SR_TB) return SR_TB_BIT5; else return 0; @@ -36,8 +39,10 @@ static u8 spi_nor_get_sr_tb_mask(struct spi_nor *nor) static u8 spi_nor_get_sr_cmp_mask(struct spi_nor *nor) { - if (!(nor->flags & SNOR_F_NO_READ_CR) && - nor->flags & SNOR_F_HAS_SR2_CMP_BIT6) + struct spi_nor_flash_parameter *params = nor->params; + + if (params->opcodes.read_sr2 && + params->flags & SNOR_F_HAS_SR2_CMP_BIT6) return SR2_CMP_BIT6; else return 0; @@ -67,15 +72,16 @@ u64 spi_nor_get_min_prot_length_sr(struct spi_nor *nor) void spi_nor_get_locked_range_sr(struct spi_nor *nor, const u8 *sr, loff_t *ofs, u64 *len) { + struct spi_nor_flash_parameter *params = nor->params; u64 min_prot_len; u8 bp_mask = spi_nor_get_sr_bp_mask(nor); u8 tb_mask = spi_nor_get_sr_tb_mask(nor); u8 cmp_mask = spi_nor_get_sr_cmp_mask(nor); u8 bp, val = sr[0] & bp_mask; - bool tb = (nor->flags & SNOR_F_HAS_SR_TB) ? sr[0] & tb_mask : 0; + bool tb = (params->flags & SNOR_F_HAS_SR_TB) ? sr[0] & tb_mask : 0; bool cmp = sr[1] & cmp_mask; - if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3_BIT6) + if (params->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3_BIT6) val = (val & ~SR_BP3_BIT6) | SR_BP3; bp = val >> SR_BP_SHIFT; @@ -153,10 +159,11 @@ static bool spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, u64 len, static int spi_nor_sr_set_bp_mask(struct spi_nor *nor, u8 *sr, u8 pow) { + struct spi_nor_flash_parameter *params = nor->params; u8 mask = spi_nor_get_sr_bp_mask(nor); u8 val = pow << SR_BP_SHIFT; - if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3) + if (params->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3) val = (val & ~SR_BP3) | SR_BP3_BIT6; if (val & ~mask) @@ -195,6 +202,45 @@ static int spi_nor_build_sr(struct spi_nor *nor, const u8 *old_sr, u8 *new_sr, } /* + * Make sure we do our best to guess SR2. This has historically only be needed + * for swp.c, so let's keep this extra carefulness in this file. + */ +static int spi_nor_read_sr2_careful(struct spi_nor *nor, u8 *sr2) +{ + struct spi_nor_flash_parameter *params = nor->params; + int ret; + + if (params->opcodes.read_sr2) { + ret = spi_nor_read_sr_ll(nor, params->opcodes.read_sr2, sr2, 1); + if (ret) + return ret; + } else if ((spi_nor_get_protocol_width(nor->read_proto) == 4 || + spi_nor_get_protocol_width(nor->write_proto) == 4) && + nor->params->quad_enable) { + /* + * Make sure the QE bit is persistently kept. qe_mask[1] will be + * 0 if the QE bit is in SR1. + */ + *sr2 = params->qe_mask[1]; + } else { + return 0; + } + + return 0; +} + +static int spi_nor_read_sr1_and_sr2_careful(struct spi_nor *nor, u8 *sr) +{ + int ret; + + ret = spi_nor_read_sr1(nor, &sr[0]); + if (ret) + return ret; + + return spi_nor_read_sr2_careful(nor, &sr[1]); +} + +/* * Keep a local cache containing all lock-related bits for debugfs use only. * This way, debugfs never needs to access the flash directly. */ @@ -207,17 +253,9 @@ void spi_nor_cache_sr_lock_bits(struct spi_nor *nor, u8 *sr) if (!sr) { - if (spi_nor_read_sr(nor, nor->bouncebuf)) + if (spi_nor_read_sr1_and_sr2_careful(nor, sr_cr)) return; - sr_cr[0] = nor->bouncebuf[0]; - - if (!(nor->flags & SNOR_F_NO_READ_CR)) { - if (spi_nor_read_cr(nor, nor->bouncebuf)) - return; - } - - sr_cr[1] = nor->bouncebuf[0]; sr = sr_cr; } @@ -261,32 +299,23 @@ void spi_nor_cache_sr_lock_bits(struct spi_nor *nor, u8 *sr) */ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) { + struct spi_nor_flash_parameter *params = nor->params; u64 min_prot_len = spi_nor_get_min_prot_length_sr(nor); u8 status_old[2] = {}, status_new[2] = {}, status_new_cmp[2] = {}; u8 *best_status_new = status_new; loff_t ofs_old, ofs_new, ofs_new_cmp; u64 len_old, len_new, len_new_cmp; loff_t lock_len; - bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB, + bool can_be_top = true, can_be_bottom = params->flags & SNOR_F_HAS_SR_TB, can_be_cmp = spi_nor_get_sr_cmp_mask(nor); bool use_top; int ret; u8 pow; - ret = spi_nor_read_sr(nor, nor->bouncebuf); + ret = spi_nor_read_sr1_and_sr2_careful(nor, status_old); if (ret) return ret; - status_old[0] = nor->bouncebuf[0]; - - if (!(nor->flags & SNOR_F_NO_READ_CR)) { - ret = spi_nor_read_cr(nor, nor->bouncebuf); - if (ret) - return ret; - - status_old[1] = nor->bouncebuf[0]; - } - /* If nothing in our range is unlocked, we don't need to do anything */ if (spi_nor_is_locked_sr(nor, ofs, len, status_old)) return 0; @@ -313,7 +342,7 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) lock_len = ofs + len; if (lock_len == nor->params->size) - pow = (nor->flags & SNOR_F_HAS_4BIT_BP) ? GENMASK(3, 0) : GENMASK(2, 0); + pow = (params->flags & SNOR_F_HAS_4BIT_BP) ? GENMASK(3, 0) : GENMASK(2, 0); else pow = ilog2(lock_len) - ilog2(min_prot_len) + 1; @@ -359,7 +388,7 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) * wrongly tied to GND (that includes internal pull-downs). * WP# pin hard strapped to GND can be a valid use case. */ - if (!(nor->flags & SNOR_F_NO_WP)) + if (!(params->flags & SNOR_F_NO_WP)) best_status_new[0] |= SR_SRWD; spi_nor_get_locked_range_sr(nor, status_old, &ofs_old, &len_old); @@ -378,10 +407,7 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) (ofs_old < ofs_new || (ofs_new + len_new) < (ofs_old + len_old))) return -EINVAL; - if (nor->flags & SNOR_F_NO_READ_CR) - ret = spi_nor_write_sr_and_check(nor, best_status_new[0]); - else - ret = spi_nor_write_sr_cr_and_check(nor, best_status_new); + ret = spi_nor_write_sr1_and_sr2_and_check(nor, best_status_new); if (ret) return ret; @@ -397,32 +423,23 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) */ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) { + struct spi_nor_flash_parameter *params = nor->params; u64 min_prot_len = spi_nor_get_min_prot_length_sr(nor); u8 status_old[2] = {}, status_new[2] = {}, status_new_cmp[2] = {}; u8 *best_status_new = status_new; loff_t ofs_old, ofs_new, ofs_new_cmp; u64 len_old, len_new, len_new_cmp; loff_t lock_len; - bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB, + bool can_be_top = true, can_be_bottom = params->flags & SNOR_F_HAS_SR_TB, can_be_cmp = spi_nor_get_sr_cmp_mask(nor); bool use_top; int ret; u8 pow; - ret = spi_nor_read_sr(nor, nor->bouncebuf); + ret = spi_nor_read_sr1_and_sr2_careful(nor, status_old); if (ret) return ret; - status_old[0] = nor->bouncebuf[0]; - - if (!(nor->flags & SNOR_F_NO_READ_CR)) { - ret = spi_nor_read_cr(nor, nor->bouncebuf); - if (ret) - return ret; - - status_old[1] = nor->bouncebuf[0]; - } - /* If nothing in our range is locked, we don't need to do anything */ if (spi_nor_is_unlocked_sr(nor, ofs, len, status_old)) return 0; @@ -512,10 +529,7 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) (ofs_new < ofs_old || (ofs_old + len_old) < (ofs_new + len_new))) return -EINVAL; - if (nor->flags & SNOR_F_NO_READ_CR) - ret = spi_nor_write_sr_and_check(nor, best_status_new[0]); - else - ret = spi_nor_write_sr_cr_and_check(nor, best_status_new); + ret = spi_nor_write_sr1_and_sr2_and_check(nor, best_status_new); if (ret) return ret; @@ -536,20 +550,10 @@ static int spi_nor_sr_is_locked(struct spi_nor *nor, loff_t ofs, u64 len) u8 sr_cr[2] = {}; int ret; - ret = spi_nor_read_sr(nor, nor->bouncebuf); + ret = spi_nor_read_sr1_and_sr2_careful(nor, sr_cr); if (ret) return ret; - sr_cr[0] = nor->bouncebuf[0]; - - if (!(nor->flags & SNOR_F_NO_READ_CR)) { - ret = spi_nor_read_cr(nor, nor->bouncebuf); - if (ret) - return ret; - - sr_cr[1] = nor->bouncebuf[0]; - } - return spi_nor_is_locked_sr(nor, ofs, len, sr_cr); } @@ -628,9 +632,10 @@ static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, u64 len) */ void spi_nor_try_unlock_all(struct spi_nor *nor) { + struct spi_nor_flash_parameter *params = nor->params; int ret; - if (!(nor->flags & SNOR_F_HAS_LOCK)) + if (!(params->flags & SNOR_F_HAS_LOCK)) return; dev_dbg(nor->dev, "Unprotecting entire flash array\n"); diff --git a/drivers/mtd/spi-nor/sysfs.c b/drivers/mtd/spi-nor/sysfs.c index 643513ee891b..a2dabe4c6422 100644 --- a/drivers/mtd/spi-nor/sysfs.c +++ b/drivers/mtd/spi-nor/sysfs.c @@ -25,7 +25,7 @@ static ssize_t partname_show(struct device *dev, struct spi_mem *spimem = spi_get_drvdata(spi); struct spi_nor *nor = spi_mem_get_drvdata(spimem); - return sysfs_emit(buf, "%s\n", nor->info->name); + return sysfs_emit(buf, "%s\n", nor->partname); } static DEVICE_ATTR_RO(partname); @@ -78,7 +78,7 @@ static umode_t spi_nor_sysfs_is_visible(struct kobject *kobj, if (attr == &dev_attr_manufacturer.attr && !nor->manufacturer) return 0; - if (attr == &dev_attr_partname.attr && !nor->info->name) + if (attr == &dev_attr_partname.attr && !nor->partname) return 0; if (attr == &dev_attr_jedec_id.attr && !nor->info->id && !nor->id) return 0; diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c index 8ebdbcec0b3f..51f6bcd3f8e5 100644 --- a/drivers/mtd/spi-nor/winbond.c +++ b/drivers/mtd/spi-nor/winbond.c @@ -24,20 +24,94 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_DATA_OUT(1, buf, 0)) -static int -w25q128_post_bfpt_fixups(struct spi_nor *nor, - const struct sfdp_parameter_header *bfpt_header, - const struct sfdp_bfpt *bfpt) +static bool is_w25qxxrv(const struct spi_nor *nor) +{ + struct sfdp_header *sfdp_h = spi_nor_sfdp_get_header(nor); + + /* + * W25QxxRV chips re-use the same ID as the W25QxxJV family. + * + * Chips are very similar, W25QxxRV brings mostly performance and power + * consumption improvements. The RV family does not require the multi + * die fixup. + * + * They can be distinguished based on their SFDP minor revision: + * W25QxxJV: JESD216A, minor revision == 05h + * W25Q512/01/02JV: JESD216B, minor revision == 06h + * W25QxxRV: JESD216F, minor revision >= 0Ah + */ + return sfdp_h->minor >= SFDP_JESD216F_MINOR; +} + +static bool is_zd25q128c(const struct spi_nor *nor, + const struct sfdp_parameter_header *bfpt_header) { /* * Zetta ZD25Q128C is a clone of the Winbond device. But the encoded * size is really wrong. It seems that they confused Mbit with MiB. * Thus the flash is discovered as a 2MiB device. */ - if (bfpt_header->major == SFDP_JESD216_MAJOR && - bfpt_header->minor == SFDP_JESD216_MINOR && - nor->params->size == SZ_2M && - nor->params->erase_map.regions[0].size == SZ_2M) { + return bfpt_header->major == SFDP_JESD216_MAJOR && + bfpt_header->minor == SFDP_JESD216_MINOR && + nor->params->size == SZ_2M && + nor->params->erase_map.regions[0].size == SZ_2M; +} + +/* + * Since SFDP is populated after ->default_init(), the match functions using + * nor->sfdp as discriminant cannot be used for this specific early fixup. + */ +static bool winbond_jv_match(const struct spi_nor *nor) +{ + return !nor->sfdp || !is_w25qxxrv(nor); +} + +static bool winbond_rv_match(const struct spi_nor *nor) +{ + return nor->sfdp && is_w25qxxrv(nor); +} + +static bool is_w25qxxpw(const struct spi_nor *nor) +{ + struct sfdp_header *sfdp_h = spi_nor_sfdp_get_header(nor); + + /* + * W25QxxPW chips re-use the same ID as the W25QxxJW/NW family. + * + * Chips are very similar, W25QxxPW brings mostly performance and power + * consumption improvements. One key difference in behaviour is the + * automatic 16-byte based error correction. + * + * They can be distinguished based on their SFDP minor revision: + * W25QxxJW: JESD216B, minor revision == 06h + * W25Q51/01/02NW: JESD216B, minor revision == 06h + * W25QxxPW: JESD216F, minor revision >= 0Ah + */ + return sfdp_h->minor >= SFDP_JESD216F_MINOR; +} + +static bool winbond_pw_match(const struct spi_nor *nor) +{ + return nor->sfdp && is_w25qxxpw(nor); +} + +static bool winbond_pw_with_ecc_match(const struct spi_nor *nor) +{ + const struct spi_nor_id *id = nor->info->id; + + if (!winbond_pw_match(nor)) + return false; + + /* W25QxxPW densities <= 32 Mbit (id[2] <= 0x16) do not have built-in ECC support */ + return id->len == 3 && id->bytes[2] >= 0x17; +} + +static int +w25q128_post_bfpt_fixups(struct spi_nor *nor, + const struct sfdp_parameter_header *bfpt_header, + const struct sfdp_bfpt *bfpt) +{ + if (is_zd25q128c(nor, bfpt_header) || winbond_rv_match(nor)) { nor->params->size = SZ_16M; nor->params->erase_map.regions[0].size = SZ_16M; } @@ -64,7 +138,7 @@ w25q256_post_bfpt_fixups(struct spi_nor *nor, */ if (bfpt_header->major == SFDP_JESD216_MAJOR && bfpt_header->minor == SFDP_JESD216A_MINOR) - nor->flags |= SNOR_F_4B_OPCODES; + nor->params->flags |= SNOR_F_4B_OPCODES; return 0; } @@ -73,26 +147,6 @@ static const struct spi_nor_fixups w25q256_fixups = { .post_bfpt = w25q256_post_bfpt_fixups, }; -static int -winbond_rdcr_post_bfpt_fixup(struct spi_nor *nor, - const struct sfdp_parameter_header *bfpt_header, - const struct sfdp_bfpt *bfpt) -{ - /* - * W25H02NW, unlike its W25H512NW nor W25H01NW cousins, improperly sets - * the QE BFPT configuration bits, indicating a non readable CR. This is - * both incorrect and impractical, as the chip features a CMP bit for its - * locking scheme that lays in the Control Register, and needs to be read. - */ - nor->flags &= ~SNOR_F_NO_READ_CR; - - return 0; -} - -static const struct spi_nor_fixups winbond_rdcr_fixup = { - .post_bfpt = winbond_rdcr_post_bfpt_fixup, -}; - /** * winbond_nor_select_die() - Set active die. * @nor: pointer to 'struct spi_nor'. @@ -166,6 +220,38 @@ static const struct spi_nor_fixups winbond_nor_multi_die_fixups = { .post_sfdp = winbond_nor_multi_die_post_sfdp_fixups, }; +static int winbond_nor_partname_post_sfdp_fixups(struct spi_nor *nor) +{ + /* + * W25QxxRV parts re-use the JEDEC IDs of the JV family. Their name + * being a legacy field, it is kept for the already established JV parts + * but must not be exposed by the newer RV ones. + */ + nor->partname = NULL; + + return 0; +} + +static const struct spi_nor_fixups winbond_nor_partname_fixups = { + .post_sfdp = winbond_nor_partname_post_sfdp_fixups, +}; + +static int winbond_nor_ecc_configuration_post_sfdp_fixups(struct spi_nor *nor) +{ + /* + * PW chips feature automatic error correction. Non 16-byte aligned + * writes work, but disable error correction on the region until next erase. + */ + nor->params->writesize = 16; + nor->params->flags |= SNOR_F_ECC; + + return 0; +} + +static const struct spi_nor_fixups winbond_nor_ecc_configuration_fixups = { + .post_sfdp = winbond_nor_ecc_configuration_post_sfdp_fixups, +}; + static const struct flash_info winbond_nor_parts[] = { { .id = SNOR_ID(0xef, 0x30, 0x10), @@ -218,38 +304,53 @@ static const struct flash_info winbond_nor_parts[] = { .size = SZ_1M, .no_sfdp_flags = SECT_4K, }, { + /* W25Q32JV-Q/N, W25Q32RV-Q/N */ .id = SNOR_ID(0xef, 0x40, 0x16), .name = "w25q32", .size = SZ_4M, .no_sfdp_flags = SECT_4K, + .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP, }, { + /* W25Q64JV-Q/N, W25Q64RV-Q/N */ .id = SNOR_ID(0xef, 0x40, 0x17), .name = "w25q64", .size = SZ_8M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP, }, { + /* W25Q128JV-Q/N, W25Q12RV-Q/N */ .id = SNOR_ID(0xef, 0x40, 0x18), /* Flavors w/ and w/o SFDP. */ .name = "w25q128", .size = SZ_16M, - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, - .fixups = &w25q128_fixups, + .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP, }, { + /* W25Q256JV-Q/N, W25Q25RV-Q/N */ .id = SNOR_ID(0xef, 0x40, 0x19), .name = "w25q256", .size = SZ_32M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, - .fixups = &w25q256_fixups, + .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | + SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, }, { + /* W25Q512JV-Q/N, W25Q51RV-Q/N */ .id = SNOR_ID(0xef, 0x40, 0x20), .name = "w25q512jvq", .size = SZ_64M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | + SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, }, { - /* W25Q01JV */ + /* W25Q01JV-Q/N, W25Q01RV-Q/N */ .id = SNOR_ID(0xef, 0x40, 0x21), - .fixups = &winbond_nor_multi_die_fixups, + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | + SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, + }, { + /* W25Q02RV-Q/N */ + .id = SNOR_ID(0xef, 0x40, 0x22), + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | + SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, }, { .id = SNOR_ID(0xef, 0x50, 0x12), .name = "w25q20bw", @@ -272,133 +373,166 @@ static const struct flash_info winbond_nor_parts[] = { .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, }, { + /* W25Q32DW-P/G, W25Q32FW-G/Q, W25Q32JW-Q/N, W25Q33PW-Q/N */ .id = SNOR_ID(0xef, 0x60, 0x16), .name = "w25q32dw", .size = SZ_4M, - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, + .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, .otp = SNOR_OTP(256, 3, 0x1000, 0x1000), }, { + /* W25Q64DW-P/G, W25Q64FW-G/Q, W25Q64JW-Q/N, W25Q64PW-Q/N */ .id = SNOR_ID(0xef, 0x60, 0x17), .name = "w25q64dw", .size = SZ_8M, - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, + .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, }, { + /* W25Q128FW-G/Q, W25Q128JW-Q/N, W25Q12PW-Q/N */ .id = SNOR_ID(0xef, 0x60, 0x18), .name = "w25q128fw", .size = SZ_16M, - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, + .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, }, { + /* W25Q256JW-Q/N, W25Q25PW-Q/N */ .id = SNOR_ID(0xef, 0x60, 0x19), .name = "w25q256jw", .size = SZ_32M, - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP, + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | + SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, }, { + /* W25Q512NW-Q/N */ .id = SNOR_ID(0xef, 0x60, 0x20), .name = "w25q512nwq", .otp = SNOR_OTP(256, 3, 0x1000, 0x1000), }, { + /* W25Q01NW-Q/N */ + .id = SNOR_ID(0xef, 0x60, 0x21), + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | + SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, + }, { + /* W25Q16JV-M */ .id = SNOR_ID(0xef, 0x70, 0x15), .name = "w25q16jv-im/jm", .size = SZ_2M, - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP, }, { + /* W25Q32JV-M, W25Q32RV-M */ .id = SNOR_ID(0xef, 0x70, 0x16), .name = "w25q32jv", .size = SZ_4M, - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP, }, { + /* W25Q64JV-M, W25Q64RV-M */ .id = SNOR_ID(0xef, 0x70, 0x17), .name = "w25q64jvm", .size = SZ_8M, - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, .no_sfdp_flags = SECT_4K, + .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP, }, { + /* W25Q128JV-M, W25Q12RV-M */ .id = SNOR_ID(0xef, 0x70, 0x18), .name = "w25q128jv", .size = SZ_16M, - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP, }, { + /* W25Q256JV-M, W25Q25RV-M */ .id = SNOR_ID(0xef, 0x70, 0x19), .name = "w25q256jvm", + .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | + SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, }, { - /* W25Q02JV */ + /* W25Q512JV-M, W25Q51RV-M */ + .id = SNOR_ID(0xef, 0x70, 0x20), + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | + SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, + }, { + /* W25Q01JV-M, W25Q01RV-M */ + .id = SNOR_ID(0xef, 0x70, 0x21), + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | + SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, + }, { + /* W25Q02JV-M, W25Q02RV-M */ .id = SNOR_ID(0xef, 0x70, 0x22), - .fixups = &winbond_nor_multi_die_fixups, + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | + SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, }, { .id = SNOR_ID(0xef, 0x71, 0x19), .name = "w25m512jv", .size = SZ_64M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, }, { + /* W25Q32JW-M, W25Q33PW-M */ .id = SNOR_ID(0xef, 0x80, 0x16), .name = "w25q32jwm", .size = SZ_4M, - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, + .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, .otp = SNOR_OTP(256, 3, 0x1000, 0x1000), }, { + /* W25Q64JW-M, W25Q64PW-M */ .id = SNOR_ID(0xef, 0x80, 0x17), .name = "w25q64jwm", .size = SZ_8M, - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, + .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, }, { + /* W25Q128JW-M, W25Q12PW-M */ .id = SNOR_ID(0xef, 0x80, 0x18), .name = "w25q128jwm", .size = SZ_16M, - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, + .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, }, { + /* W25Q256JW-M, W25Q25PW-M */ .id = SNOR_ID(0xef, 0x80, 0x19), .name = "w25q256jwm", .size = SZ_32M, - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP, + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | + SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, }, { + /* W25Q512NW-M */ .id = SNOR_ID(0xef, 0x80, 0x20), .name = "w25q512nwm", .otp = SNOR_OTP(256, 3, 0x1000, 0x1000), }, { - /* W25Q01NWxxIQ */ - .id = SNOR_ID(0xef, 0x60, 0x21), - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | - SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, - .fixups = &winbond_rdcr_fixup, - }, { - /* W25Q01NWxxIM */ + /* W25Q01NW-M */ .id = SNOR_ID(0xef, 0x80, 0x21), .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, }, { - /* W25Q02NWxxIM */ + /* W25Q02NW-M */ .id = SNOR_ID(0xef, 0x80, 0x22), .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, - .fixups = &winbond_rdcr_fixup, }, { - /* W25H512NWxxAM */ + /* W25H512NW-M */ .id = SNOR_ID(0xef, 0xa0, 0x20), .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, }, { - /* W25H01NWxxAM */ + /* W25H01NW-M */ .id = SNOR_ID(0xef, 0xa0, 0x21), .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, }, { - /* W25H02NWxxAM */ + /* W25H02NW-M */ .id = SNOR_ID(0xef, 0xa0, 0x22), .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, - .fixups = &winbond_rdcr_fixup, - }, + }, { + /* + * Catch all entry to make sure all chips solely relying + * on SFDP still go through the manufacturer hooks. + */ + .id = SNOR_ID(0xef), + } }; /** @@ -490,6 +624,38 @@ static int winbond_nor_late_init(struct spi_nor *nor) */ params->set_4byte_addr_mode = winbond_nor_set_4byte_addr_mode; + /* + * All W25Q/W25H chips do set the BFPT_DWORD15_QER_SR2_BIT1_NO_RD bit in + * their SFDP tables. The historical spi-nor assumption in this case has + * been to declare CR reads as unsupported, whereas the Jedec + * specification doesn't clearly state that. In practice, all these + * chips do support reading back the CR, which is needed for SWP support, + * so make sure that capability remains enabled. + * In practice, only exclude the old W25X family (JEDEC ID: EF 30 xx) + * which actually does not support this feature. + */ + if (nor->id[1] > 0x30) + params->opcodes.read_sr2 = SPINOR_OP_RDCR; + + /* + * Winbond has reused many IDs, up to four times at this + * stage. In general, most of the chips with SFDP support are + * correctly described by the ID table, but the non-SFDP chips, + * however, are known to not feature as many capabilities. Make + * sure we filter out those capabilities to keep backward + * compatibility with these devices manufactured until ~2016. + */ + if (!nor->sfdp) { + struct spi_nor_flash_parameter *p = nor->params; + + /* SPI_NOR_QUAD_PP was unsupported */ + p->hwcaps.mask &= ~SNOR_HWCAPS_PP_1_1_4; + spi_nor_set_pp_settings(&p->page_programs[SNOR_CMD_PP_1_1_4], 0, 0); + + /* SPI_NOR_HAS_CMP was unsupported */ + nor->params->flags &= ~SNOR_F_HAS_SR2_CMP_BIT6; + } + return 0; } @@ -497,9 +663,37 @@ static const struct spi_nor_fixups winbond_nor_fixups = { .late_init = winbond_nor_late_init, }; +static const struct spi_nor_fixup winbond_fixups[] = { + { .fixups = &winbond_nor_fixups }, + { .id = SNOR_ID(0xef, 0x40, 0x18), .fixups = &w25q128_fixups }, + { .id = SNOR_ID(0xef, 0x40, 0x19), .fixups = &w25q256_fixups }, + { .id = SNOR_ID(0xef, 0x40), .match = winbond_rv_match, + .fixups = &winbond_nor_partname_fixups }, + { .id = SNOR_ID(0xef, 0x40, 0x21), .match = winbond_jv_match, + .fixups = &winbond_nor_multi_die_fixups }, + { .id = SNOR_ID(0xef, 0x40, 0x22), .match = winbond_jv_match, + .fixups = &winbond_nor_multi_die_fixups }, + { .id = SNOR_ID(0xef, 0x60), .match = winbond_pw_with_ecc_match, + .fixups = &winbond_nor_ecc_configuration_fixups }, + { .id = SNOR_ID(0xef, 0x60), .match = winbond_pw_match, + .fixups = &winbond_nor_partname_fixups }, + { .id = SNOR_ID(0xef, 0x70), .match = winbond_rv_match, + .fixups = &winbond_nor_partname_fixups }, + { .id = SNOR_ID(0xef, 0x70, 0x18), .fixups = &w25q128_fixups }, + { .id = SNOR_ID(0xef, 0x70, 0x21), .match = winbond_jv_match, + .fixups = &winbond_nor_multi_die_fixups }, + { .id = SNOR_ID(0xef, 0x70, 0x22), .match = winbond_jv_match, + .fixups = &winbond_nor_multi_die_fixups }, + { .id = SNOR_ID(0xef, 0x80), .match = winbond_pw_with_ecc_match, + .fixups = &winbond_nor_ecc_configuration_fixups }, + { .id = SNOR_ID(0xef, 0x80), .match = winbond_pw_match, + .fixups = &winbond_nor_partname_fixups }, +}; + const struct spi_nor_manufacturer spi_nor_winbond = { .name = "winbond", .parts = winbond_nor_parts, .nparts = ARRAY_SIZE(winbond_nor_parts), - .fixups = &winbond_nor_fixups, + .fixups = winbond_fixups, + .nfixups = ARRAY_SIZE(winbond_fixups), }; diff --git a/drivers/mtd/spi-nor/xmc.c b/drivers/mtd/spi-nor/xmc.c index d5a06054b0dd..d3b52eb93e2c 100644 --- a/drivers/mtd/spi-nor/xmc.c +++ b/drivers/mtd/spi-nor/xmc.c @@ -19,6 +19,7 @@ static const struct flash_info xmc_nor_parts[] = { .name = "XM25QH128A", .size = SZ_16M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_4BIT_BP | SPI_NOR_HAS_TB, }, }; diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 54acd8394d67..8dd005ef376b 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -861,7 +861,7 @@ static int ubi_get_ec_info(struct ubi_device *ubi, struct ubi_ecinfo_req __user int ec; if (ubi_io_is_bad(ubi, peb)) { - if (__put_user(UBI_UNKNOWN, ureq->erase_counters+read_cnt)) + if (put_user(UBI_UNKNOWN, ureq->erase_counters+read_cnt)) return -EFAULT; continue; @@ -877,7 +877,7 @@ static int ubi_get_ec_info(struct ubi_device *ubi, struct ubi_ecinfo_req __user spin_unlock(&ubi->wl_lock); - if (__put_user(ec, ureq->erase_counters+read_cnt)) + if (put_user(ec, ureq->erase_counters+read_cnt)) return -EFAULT; } |
