summaryrefslogtreecommitdiff
path: root/drivers/mtd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0001.c47
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0002.c15
-rw-r--r--drivers/mtd/devices/block2mtd.c2
-rw-r--r--drivers/mtd/devices/docg3.c20
-rw-r--r--drivers/mtd/devices/mtd_intel_dg.c3
-rw-r--r--drivers/mtd/devices/powernv_flash.c2
-rw-r--r--drivers/mtd/hyperbus/hbmc-am654.c7
-rw-r--r--drivers/mtd/maps/Kconfig17
-rw-r--r--drivers/mtd/maps/Makefile1
-rw-r--r--drivers/mtd/maps/amd76xrom.c2
-rw-r--r--drivers/mtd/maps/int0800.c115
-rw-r--r--drivers/mtd/maps/l440gx.c2
-rw-r--r--drivers/mtd/maps/pci.c2
-rw-r--r--drivers/mtd/mtd_blkdevs.c5
-rw-r--r--drivers/mtd/mtd_virt_concat.c2
-rw-r--r--drivers/mtd/mtdconcat.c2
-rw-r--r--drivers/mtd/mtdcore.c5
-rw-r--r--drivers/mtd/mtdsuper.c4
-rw-r--r--drivers/mtd/nand/ecc.c7
-rw-r--r--drivers/mtd/nand/onenand/onenand_base.c2
-rw-r--r--drivers/mtd/nand/onenand/onenand_omap2.c4
-rw-r--r--drivers/mtd/nand/qpic_common.c131
-rw-r--r--drivers/mtd/nand/raw/atmel/nand-controller.c6
-rw-r--r--drivers/mtd/nand/raw/brcmnand/brcmnand.c2
-rw-r--r--drivers/mtd/nand/raw/cadence-nand-controller.c8
-rw-r--r--drivers/mtd/nand/raw/fsl_ifc_nand.c49
-rw-r--r--drivers/mtd/nand/raw/fsmc_nand.c4
-rw-r--r--drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c2
-rw-r--r--drivers/mtd/nand/raw/intel-nand-controller.c8
-rw-r--r--drivers/mtd/nand/raw/internals.h1
-rw-r--r--drivers/mtd/nand/raw/loongson-nand-controller.c4
-rw-r--r--drivers/mtd/nand/raw/lpc32xx_mlc.c31
-rw-r--r--drivers/mtd/nand/raw/lpc32xx_slc.c31
-rw-r--r--drivers/mtd/nand/raw/marvell_nand.c7
-rw-r--r--drivers/mtd/nand/raw/mtk_nand.c2
-rw-r--r--drivers/mtd/nand/raw/nand_base.c2
-rw-r--r--drivers/mtd/nand/raw/nand_esmt.c34
-rw-r--r--drivers/mtd/nand/raw/nand_ids.c2
-rw-r--r--drivers/mtd/nand/raw/nand_legacy.c4
-rw-r--r--drivers/mtd/nand/raw/ndfc.c28
-rw-r--r--drivers/mtd/nand/raw/nuvoton-ma35d1-nand-controller.c1
-rw-r--r--drivers/mtd/nand/raw/omap2.c14
-rw-r--r--drivers/mtd/nand/raw/pl35x-nand-controller.c1
-rw-r--r--drivers/mtd/nand/raw/qcom_nandc.c192
-rw-r--r--drivers/mtd/nand/raw/sh_flctl.c8
-rw-r--r--drivers/mtd/nand/raw/stm32_fmc2_nand.c8
-rw-r--r--drivers/mtd/nand/raw/sunxi_nand.c37
-rw-r--r--drivers/mtd/nand/raw/vf610_nfc.c85
-rw-r--r--drivers/mtd/nand/spi/Makefile2
-rw-r--r--drivers/mtd/nand/spi/core.c54
-rw-r--r--drivers/mtd/nand/spi/heyangtek.c10
-rw-r--r--drivers/mtd/nand/spi/issi.c187
-rw-r--r--drivers/mtd/nand/spi/winbond.c87
-rw-r--r--drivers/mtd/parsers/qcomsmempart.c4
-rw-r--r--drivers/mtd/spi-nor/atmel.c73
-rw-r--r--drivers/mtd/spi-nor/core.c871
-rw-r--r--drivers/mtd/spi-nor/core.h144
-rw-r--r--drivers/mtd/spi-nor/debugfs.c33
-rw-r--r--drivers/mtd/spi-nor/everspin.c7
-rw-r--r--drivers/mtd/spi-nor/gigadevice.c15
-rw-r--r--drivers/mtd/spi-nor/issi.c29
-rw-r--r--drivers/mtd/spi-nor/macronix.c73
-rw-r--r--drivers/mtd/spi-nor/micron-st.c70
-rw-r--r--drivers/mtd/spi-nor/otp.c18
-rw-r--r--drivers/mtd/spi-nor/sfdp.c284
-rw-r--r--drivers/mtd/spi-nor/sfdp.h23
-rw-r--r--drivers/mtd/spi-nor/spansion.c109
-rw-r--r--drivers/mtd/spi-nor/sst.c16
-rw-r--r--drivers/mtd/spi-nor/swp.c133
-rw-r--r--drivers/mtd/spi-nor/sysfs.c4
-rw-r--r--drivers/mtd/spi-nor/winbond.c320
-rw-r--r--drivers/mtd/spi-nor/xmc.c1
-rw-r--r--drivers/mtd/ubi/cdev.c4
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,
&macronix_spinand_manufacturer,
&micron_spinand_manufacturer,
&paragon_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, &regval);
+ 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 = &params->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(&params->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 = &macronix_qpp4b_fixups,
}, {
/* MX66L1G45G */
.id = SNOR_ID(0xc2, 0x20, 0x1b),
- .fixups = &macronix_qpp4b_fixups,
}, {
/* MX66L2G45G */
.id = SNOR_ID(0xc2, 0x20, 0x1c),
- .fixups = &macronix_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 = &macronix_qpp4b_fixups,
}, {
- /* MX66U1G45G */
+ /* MX66U1G45G, MX66U1G93G */
.id = SNOR_ID(0xc2, 0x25, 0x3b),
- .fixups = &macronix_qpp4b_fixups,
}, {
/* MX66U2G45G */
.id = SNOR_ID(0xc2, 0x25, 0x3c),
- .fixups = &macronix_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 = &macronix_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 = &macronix_qpp4b_fixups,
+ .fixup_flags = SPI_NOR_4B_OPCODES },
+ { .id = SNOR_ID(0xc2, 0x20, 0x1b), .fixups = &macronix_qpp4b_fixups },
+ { .id = SNOR_ID(0xc2, 0x20, 0x1c), .fixups = &macronix_qpp4b_fixups },
+ { .id = SNOR_ID(0xc2, 0x25, 0x3a), .fixups = &macronix_qpp4b_fixups },
+ { .id = SNOR_ID(0xc2, 0x25, 0x3b), .fixups = &macronix_qpp4b_fixups },
+ { .id = SNOR_ID(0xc2, 0x25, 0x3c), .fixups = &macronix_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 = &macronix_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 = &micron_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 = &micron_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 = &micron_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 = &micron_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(&params_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(&params_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(&params_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 = &param_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;
}