summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-06-21 12:25:17 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-06-21 12:25:17 -0700
commit09e3b4a76bb6047ec0b99dc668b313469d8a73d0 (patch)
tree44b6a838649677298911ba27eee68861cb7a53ba
parent8cd8cf7a07e5d141b0c75ce6cf470630e11aa11a (diff)
parent49420dfdedd676befaa999b165a76d8d7eec4fab (diff)
downloadlwn-09e3b4a76bb6047ec0b99dc668b313469d8a73d0.tar.gz
lwn-09e3b4a76bb6047ec0b99dc668b313469d8a73d0.zip
Merge tag 'mtd/for-7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux
Pull mtd updates from Miquel Raynal: "NAND changes: - Extend SPI NAND continuous read to Winbond devices, which requires numerous changes in the spi-{mem,nand} layers such as the need for a secondary read operation template - Continuous reads in general have also been enhanced/fixed for avoiding potential issues at probe time and at block boundaries SPI NOR changes: - Big set of cleanups and improvements to the locking support. This series contains some cleanups and bug fixes for code and documentation around write protection. Then support is added for complement locking, which allows finer grained configuration of what is considered locked and unlocked. Then complement locking is enabled on a bunch of Winbond W25 flashes - Fix die erase support on Spansion flashes. Die erase is only supported on multi-die flashes, but the die erase opcode was set for all. When the opcode is set, it overrides the default chip erase opcode which should be used for single-die flashes. Only set the opcode on multi-die flashes. Also, the opcode was not set on multi-die s28hx-t flashes. Set it so they can use die-erase correctly General changes: - A few drivers and mappings have been removed following SoCs support removal - And again, there is the usual load of misc improvements and fixes" * tag 'mtd/for-7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux: (63 commits) mtd: cfi: Use common error handling code in two functions mtd: slram: simplify register_device() cleanup mtd: slram: remove failed entries from the device list mtd: rawnand: ndfc: use ioread32be/iowrite32be and allow COMPILE_TEST mtd: spi-nor: spansion: add die erase support in s28hx-t mtd: spi-nor: spansion: use die erase for multi-die devices only mtd: spi-nor: winbond: Add W25Q02NWxxIM CMP locking support mtd: spi-nor: winbond: Add W25Q01NWxxIM CMP locking support mtd: spi-nor: winbond: Add W25Q01NWxxIQ CMP locking support mtd: spi-nor: winbond: Add W25H02NWxxAM CMP locking support mtd: spi-nor: winbond: Add W25H01NWxxAM CMP locking support mtd: spi-nor: winbond: Add W25H512NWxxAM CMP locking support mtd: spi-nor: Add steps for testing locking with CMP mtd: spi-nor: swp: Add support for the complement feature mtd: spi-nor: Add steps for testing locking support mtd: maps: remove obsolete impa7 map driver mtd: maps: remove uclinux map driver mtd: maps: remove AMD Élan specific drivers mtd: inftlmount: convert printk(KERN_WARNING) to pr_warn mtd: Consistently define pci_device_ids ...
-rw-r--r--Documentation/devicetree/bindings/mtd/nand-chip.yaml9
-rw-r--r--Documentation/driver-api/mtd/spi-nor.rst170
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0002.c13
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0020.c24
-rw-r--r--drivers/mtd/devices/slram.c96
-rw-r--r--drivers/mtd/inftlmount.c44
-rw-r--r--drivers/mtd/maps/Kconfig53
-rw-r--r--drivers/mtd/maps/Makefile6
-rw-r--r--drivers/mtd/maps/amd76xrom.c10
-rw-r--r--drivers/mtd/maps/ck804xrom.c2
-rw-r--r--drivers/mtd/maps/esb2rom.c20
-rw-r--r--drivers/mtd/maps/ichxrom.c17
-rw-r--r--drivers/mtd/maps/impa7.c115
-rw-r--r--drivers/mtd/maps/netsc520.c127
-rw-r--r--drivers/mtd/maps/nettel.c462
-rw-r--r--drivers/mtd/maps/pci.c16
-rw-r--r--drivers/mtd/maps/sc520cdp.c294
-rw-r--r--drivers/mtd/maps/scb2_flash.c9
-rw-r--r--drivers/mtd/maps/ts5500_flash.c108
-rw-r--r--drivers/mtd/maps/uclinux.c118
-rw-r--r--drivers/mtd/maps/vmu-flash.c7
-rw-r--r--drivers/mtd/mtdoops.c7
-rw-r--r--drivers/mtd/mtdsuper.c8
-rw-r--r--drivers/mtd/nand/raw/Kconfig4
-rw-r--r--drivers/mtd/nand/raw/cafe_nand.c3
-rw-r--r--drivers/mtd/nand/raw/denali_pci.c4
-rw-r--r--drivers/mtd/nand/raw/nand_base.c24
-rw-r--r--drivers/mtd/nand/raw/ndfc.c20
-rw-r--r--drivers/mtd/nand/raw/pl35x-nand-controller.c2
-rw-r--r--drivers/mtd/nand/raw/qcom_nandc.c16
-rw-r--r--drivers/mtd/nand/raw/r852.c4
-rw-r--r--drivers/mtd/nand/spi/core.c93
-rw-r--r--drivers/mtd/nand/spi/macronix.c38
-rw-r--r--drivers/mtd/nand/spi/winbond.c313
-rw-r--r--drivers/mtd/sm_ftl.c30
-rw-r--r--drivers/mtd/sm_ftl.h2
-rw-r--r--drivers/mtd/spi-nor/Kconfig1
-rw-r--r--drivers/mtd/spi-nor/core.c76
-rw-r--r--drivers/mtd/spi-nor/core.h25
-rw-r--r--drivers/mtd/spi-nor/debugfs.c72
-rw-r--r--drivers/mtd/spi-nor/spansion.c7
-rw-r--r--drivers/mtd/spi-nor/swp.c365
-rw-r--r--drivers/mtd/spi-nor/winbond.c41
-rw-r--r--include/linux/mtd/nand-qpic-common.h4
-rw-r--r--include/linux/mtd/spi-nor.h7
-rw-r--r--include/linux/mtd/spinand.h21
46 files changed, 1247 insertions, 1660 deletions
diff --git a/Documentation/devicetree/bindings/mtd/nand-chip.yaml b/Documentation/devicetree/bindings/mtd/nand-chip.yaml
index 8800d1d07266..effdc4f99017 100644
--- a/Documentation/devicetree/bindings/mtd/nand-chip.yaml
+++ b/Documentation/devicetree/bindings/mtd/nand-chip.yaml
@@ -23,6 +23,15 @@ properties:
description:
Contains the chip-select IDs.
+ nand-randomizer:
+ description: |
+ Control the data randomizer feature.
+ 0: Disable randomizer.
+ 1: Enable randomizer.
+ If absent, the current hardware state is left unchanged.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [0, 1]
+
required:
- reg
diff --git a/Documentation/driver-api/mtd/spi-nor.rst b/Documentation/driver-api/mtd/spi-nor.rst
index 148fa4288760..747a326fb6c0 100644
--- a/Documentation/driver-api/mtd/spi-nor.rst
+++ b/Documentation/driver-api/mtd/spi-nor.rst
@@ -203,3 +203,173 @@ section, after the ``---`` marker.
mtd.writesize = 1
mtd.oobsize = 0
regions = 0
+
+5) If your flash supports locking, please go through the following test
+ procedure to make sure it correctly behaves. The below example
+ expects the typical situation where eraseblocks and lock sectors have
+ the same size. In case you enabled MTD_SPI_NOR_USE_4K_SECTORS, you
+ must adapt `bs` accordingly.
+
+ Warning: These tests may hard lock your device! Make sure:
+
+ - The device is not hard locked already (#WP strapped to low and
+ SR_SRWD bit set)
+ - If you have a WPn pin, you may want to set `no-wp` in your DT for
+ the time of the test, to only make use of software protection.
+ Otherwise, clearing the locking state depends on the WPn
+ signal and if it is tied to low, the flash will be permanently
+ locked.
+
+ Test full chip locking and make sure expectations, the MEMISLOCKED
+ ioctl output, the debugfs output and experimental results are all
+ aligned::
+
+ root@1:~# alias show_sectors='grep -A4 "locked sectors" /sys/kernel/debug/spi-nor/spi0.0/params'
+ root@1:~# flash_lock -u /dev/mtd0
+ root@1:~# flash_lock -i /dev/mtd0
+ Device: /dev/mtd0
+ Start: 0
+ Len: 0x4000000
+ Lock status: unlocked
+ Return code: 0
+ root@1:~# mtd_debug erase /dev/mtd0 0 2097152
+ Erased 2097152 bytes from address 0x00000000 in flash
+ root@1:~# mtd_debug write /dev/mtd0 0 2097152 spi_test
+ Copied 2097152 bytes from spi_test to address 0x00000000 in flash
+ root@1:~# mtd_debug read /dev/mtd0 0 2097152 spi_read
+ Copied 2097152 bytes from address 0x00000000 in flash to spi_read
+ root@1:~# sha256sum spi*
+ c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_read
+ c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_test
+ root@1:~# show_sectors
+ software locked sectors
+ region (in hex) | status | #sectors
+ ------------------+----------+---------
+ 00000000-03ffffff | unlocked | 1024
+
+ root@1:~# flash_lock -l /dev/mtd0
+ root@1:~# flash_lock -i /dev/mtd0
+ Device: /dev/mtd0
+ Start: 0
+ Len: 0x4000000
+ Lock status: locked
+ Return code: 1
+ root@1:~# mtd_debug erase /dev/mtd0 0 2097152
+ Erased 2097152 bytes from address 0x00000000 in flash
+ root@1:~# mtd_debug read /dev/mtd0 0 2097152 spi_read
+ Copied 2097152 bytes from address 0x00000000 in flash to spi_read
+ root@1:~# sha256sum spi*
+ c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_read
+ c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_test
+ root@1:~# dd if=/dev/urandom of=./spi_test2 bs=1M count=2
+ 2+0 records in
+ 2+0 records out
+ root@1:~# mtd_debug write /dev/mtd0 0 2097152 spi_test2
+ Copied 2097152 bytes from spi_test2 to address 0x00000000 in flash
+ root@1:~# mtd_debug read /dev/mtd0 0 2097152 spi_read2
+ Copied 2097152 bytes from address 0x00000000 in flash to spi_read2
+ root@1:~# sha256sum spi*
+ c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_read
+ c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_read2
+ c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_test
+ bea9334df51c620440f86751cba0799214a016329f1736f9456d40cf40efdc88 spi_test2
+ root@1:~# show_sectors
+ software locked sectors
+ region (in hex) | status | #sectors
+ ------------------+----------+---------
+ 00000000-03ffffff | locked | 1024
+ root@1:~# flash_lock -u /dev/mtd0
+
+ Once we trust the debugfs output we can use it to test various
+ situations. Check top locking/unlocking (end of the device)::
+
+ root@1:~# size=$(cat /sys/class/mtd/mtd0/size)
+ root@1:~# bs=$(cat /sys/class/mtd/mtd0/erasesize)
+ root@1:~# nsectors=$(grep unlocked /sys/kernel/debug/spi-nor/spi0.0/params | sed -e 's/.*unlocked | //')
+ root@1:~# ss=$(($size / $nsectors))
+ root@1:~# bps=$(($ss / $bs))
+
+ root@1:~# flash_lock -u /dev/mtd0
+ root@1:~# flash_lock -l /dev/mtd0 $(($size - (2 * $ss))) $((2 * $bps)) # last two
+ root@1:~# show_sectors
+ software locked sectors
+ region (in hex) | status | #sectors
+ ------------------+----------+---------
+ 00000000-03fdffff | unlocked | 1022
+ 03fe0000-03ffffff | locked | 2
+ root@1:~# flash_lock -u /dev/mtd0 $(($size - (2 * $ss))) $((1 * $bps)) # last one
+ root@1:~# show_sectors
+ software locked sectors
+ region (in hex) | status | #sectors
+ ------------------+----------+---------
+ 00000000-03feffff | unlocked | 1023
+ 03ff0000-03ffffff | locked | 1
+
+ If the flash features 4 block protection bits (BP), we can protect
+ more than 4MB (typically 128 64kiB-blocks or more), with a finer
+ grain than locking the entire device::
+
+ root@1:~# flash_lock -u /dev/mtd0
+ root@1:~# flash_lock -l /dev/mtd0 $(($size - (2**7 * $ss))) $((2**7 * $bps))
+ root@1:~# show_sectors
+ software locked sectors
+ region (in hex) | status | #sectors
+ ------------------+----------+---------
+ 00000000-037fffff | unlocked | 896
+ 03800000-03ffffff | locked | 128
+
+ If the flash features a Top/Bottom (TB) bit, we can protect the
+ beginning of the flash::
+
+ root@1:~# flash_lock -u /dev/mtd0
+ root@1:~# flash_lock -l /dev/mtd0 0 $((2 * $bps)) # first two
+ root@1:~# show_sectors
+ software locked sectors
+ region (in hex) | status | #sectors
+ ------------------+----------+---------
+ 00000000-0001ffff | locked | 2
+ 00020000-03ffffff | unlocked | 1022
+ root@1:~# flash_lock -u /dev/mtd0 $ss $((1 * $bps)) # first one
+ root@1:~# show_sectors
+ software locked sectors
+ region (in hex) | status | #sectors
+ ------------------+----------+---------
+ 00000000-0000ffff | locked | 1
+ 00010000-03ffffff | unlocked | 1023
+
+ If the flash features a Complement (CMP) bit, we can protect with
+ more granularity above half of the capacity. Let's lock all but one
+ block, then unlock one more block::
+
+ root@1:~# all_but_one=$((($size / $bs) - ($ss / $bs)))
+
+ root@1:~# flash_lock -u /dev/mtd0
+ root@1:~# flash_lock -l /dev/mtd0 $ss $all_but_one # all but the first
+ root@1:~# show_sectors
+ software locked sectors
+ region (in hex) | status | #sectors
+ ------------------+----------+---------
+ 00000000-0000ffff | unlocked | 1
+ 00010000-03ffffff | locked | 1023
+ root@1:~# flash_lock -u /dev/mtd0 $ss $(($ss / $bs)) # all but the two first
+ root@1:~# show_sectors
+ software locked sectors
+ region (in hex) | status | #sectors
+ ------------------+----------+---------
+ 00000000-0001ffff | unlocked | 2
+ 00020000-03ffffff | locked | 1022
+ root@1:~# flash_lock -u /dev/mtd0
+ root@1:~# flash_lock -l /dev/mtd0 0 $all_but_one # same from the other side
+ root@1:~# show_sectors
+ software locked sectors
+ region (in hex) | status | #sectors
+ ------------------+----------+---------
+ 00000000-03feffff | locked | 1023
+ 03ff0000-03ffffff | unlocked | 1
+ root@1:~# flash_lock -u /dev/mtd0 $(($size - (2 * $ss))) $(($ss / $bs)) # all but two
+ root@1:~# show_sectors
+ software locked sectors
+ region (in hex) | status | #sectors
+ ------------------+----------+---------
+ 00000000-03fdffff | locked | 1022
+ 03fe0000-03ffffff | unlocked | 2
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c
index a38aceb6612d..517db2f2707f 100644
--- a/drivers/mtd/chips/cfi_cmdset_0002.c
+++ b/drivers/mtd/chips/cfi_cmdset_0002.c
@@ -661,8 +661,7 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
extp->MajorVersion, extp->MinorVersion,
extp->MajorVersion, extp->MinorVersion);
kfree(extp);
- kfree(mtd);
- return NULL;
+ goto free_mtd;
}
printk(KERN_INFO " Amd/Fujitsu Extended Query version %c.%c.\n",
@@ -714,10 +713,8 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
}
cfi_fixup(mtd, cfi_nopri_fixup_table);
- if (!cfi->addr_unlock1 || !cfi->addr_unlock2) {
- kfree(mtd);
- return NULL;
- }
+ if (!cfi->addr_unlock1 || !cfi->addr_unlock2)
+ goto free_mtd;
} /* CFI mode */
else if (cfi->cfi_mode == CFI_MODE_JEDEC) {
@@ -755,6 +752,10 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
map->fldrv = &cfi_amdstd_chipdrv;
return cfi_amdstd_setup(mtd);
+
+free_mtd:
+ kfree(mtd);
+ return NULL;
}
struct mtd_info *cfi_cmdset_0006(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0002")));
struct mtd_info *cfi_cmdset_0701(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0002")));
diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c
index 6b5727eaae69..593ac65a7e2f 100644
--- a/drivers/mtd/chips/cfi_cmdset_0020.c
+++ b/drivers/mtd/chips/cfi_cmdset_0020.c
@@ -174,11 +174,8 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map)
mtd = kzalloc_obj(*mtd);
//printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips);
-
- if (!mtd) {
- kfree(cfi->cmdset_priv);
- return NULL;
- }
+ if (!mtd)
+ goto free_cmdset_priv;
mtd->priv = map;
mtd->type = MTD_NORFLASH;
@@ -187,11 +184,8 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map)
mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
mtd->eraseregions = kmalloc_objs(struct mtd_erase_region_info,
mtd->numeraseregions);
- if (!mtd->eraseregions) {
- kfree(cfi->cmdset_priv);
- kfree(mtd);
- return NULL;
- }
+ if (!mtd->eraseregions)
+ goto free_mtd;
for (i=0; i<cfi->cfiq->NumEraseRegions; i++) {
unsigned long ernum, ersize;
@@ -213,9 +207,7 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map)
/* Argh */
printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize);
kfree(mtd->eraseregions);
- kfree(cfi->cmdset_priv);
- kfree(mtd);
- return NULL;
+ goto free_mtd;
}
for (i=0; i<mtd->numeraseregions;i++){
@@ -242,6 +234,12 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map)
__module_get(THIS_MODULE);
mtd->name = map->name;
return mtd;
+
+free_mtd:
+ kfree(mtd);
+free_cmdset_priv:
+ kfree(cfi->cmdset_priv);
+ return NULL;
}
diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c
index 69cb63d99f57..aa38ecdffc97 100644
--- a/drivers/mtd/devices/slram.c
+++ b/drivers/mtd/devices/slram.c
@@ -129,71 +129,79 @@ static int slram_write(struct mtd_info *mtd, loff_t to, size_t len,
static int register_device(char *name, unsigned long start, unsigned long length)
{
slram_mtd_list_t **curmtd;
+ slram_mtd_list_t *new_mtd;
+ struct mtd_info *mtdinfo;
+ slram_priv_t *priv;
+ int ret = -ENOMEM;
curmtd = &slram_mtdlist;
while (*curmtd) {
curmtd = &(*curmtd)->next;
}
- *curmtd = kmalloc_obj(slram_mtd_list_t);
- if (!(*curmtd)) {
+ new_mtd = kmalloc_obj(slram_mtd_list_t);
+ if (!new_mtd) {
E("slram: Cannot allocate new MTD device.\n");
return(-ENOMEM);
}
- (*curmtd)->mtdinfo = kzalloc_obj(struct mtd_info);
- (*curmtd)->next = NULL;
-
- if ((*curmtd)->mtdinfo) {
- (*curmtd)->mtdinfo->priv =
- kzalloc_obj(slram_priv_t);
+ new_mtd->next = NULL;
- if (!(*curmtd)->mtdinfo->priv) {
- kfree((*curmtd)->mtdinfo);
- (*curmtd)->mtdinfo = NULL;
- }
+ mtdinfo = kzalloc_obj(struct mtd_info);
+ if (!mtdinfo) {
+ E("slram: Cannot allocate new MTD device.\n");
+ goto err_free_list;
}
+ new_mtd->mtdinfo = mtdinfo;
- if (!(*curmtd)->mtdinfo) {
+ priv = kzalloc_obj(slram_priv_t);
+ if (!priv) {
E("slram: Cannot allocate new MTD device.\n");
- return(-ENOMEM);
+ goto err_free_mtdinfo;
}
+ mtdinfo->priv = priv;
- if (!(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start =
- memremap(start, length,
- MEMREMAP_WB | MEMREMAP_WT | MEMREMAP_WC))) {
+ priv->start = memremap(start, length,
+ MEMREMAP_WB | MEMREMAP_WT | MEMREMAP_WC);
+ if (!priv->start) {
E("slram: memremap failed\n");
- return -EIO;
+ ret = -EIO;
+ goto err_free_priv;
}
- ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->end =
- ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start + length;
-
-
- (*curmtd)->mtdinfo->name = name;
- (*curmtd)->mtdinfo->size = length;
- (*curmtd)->mtdinfo->flags = MTD_CAP_RAM;
- (*curmtd)->mtdinfo->_erase = slram_erase;
- (*curmtd)->mtdinfo->_point = slram_point;
- (*curmtd)->mtdinfo->_unpoint = slram_unpoint;
- (*curmtd)->mtdinfo->_read = slram_read;
- (*curmtd)->mtdinfo->_write = slram_write;
- (*curmtd)->mtdinfo->owner = THIS_MODULE;
- (*curmtd)->mtdinfo->type = MTD_RAM;
- (*curmtd)->mtdinfo->erasesize = SLRAM_BLK_SZ;
- (*curmtd)->mtdinfo->writesize = 1;
-
- if (mtd_device_register((*curmtd)->mtdinfo, NULL, 0)) {
+ priv->end = priv->start + length;
+
+ mtdinfo->name = name;
+ mtdinfo->size = length;
+ mtdinfo->flags = MTD_CAP_RAM;
+ mtdinfo->_erase = slram_erase;
+ mtdinfo->_point = slram_point;
+ mtdinfo->_unpoint = slram_unpoint;
+ mtdinfo->_read = slram_read;
+ mtdinfo->_write = slram_write;
+ mtdinfo->owner = THIS_MODULE;
+ mtdinfo->type = MTD_RAM;
+ mtdinfo->erasesize = SLRAM_BLK_SZ;
+ mtdinfo->writesize = 1;
+
+ if (mtd_device_register(mtdinfo, NULL, 0)) {
E("slram: Failed to register new device\n");
- memunmap(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start);
- kfree((*curmtd)->mtdinfo->priv);
- kfree((*curmtd)->mtdinfo);
- return(-EAGAIN);
+ ret = -EAGAIN;
+ goto err_unmap;
}
+ *curmtd = new_mtd;
T("slram: Registered device %s from %luKiB to %luKiB\n", name,
(start / 1024), ((start + length) / 1024));
- T("slram: Mapped from 0x%p to 0x%p\n",
- ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start,
- ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->end);
- return(0);
+ T("slram: Mapped from 0x%p to 0x%p\n", priv->start, priv->end);
+ return 0;
+
+err_unmap:
+ memunmap(priv->start);
+err_free_priv:
+ kfree(priv);
+err_free_mtdinfo:
+ kfree(mtdinfo);
+err_free_list:
+ kfree(new_mtd);
+ return ret;
}
static void unregister_devices(void)
diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c
index 6276daa296da..87e246a6f488 100644
--- a/drivers/mtd/inftlmount.c
+++ b/drivers/mtd/inftlmount.c
@@ -67,12 +67,12 @@ static int find_boot_record(struct INFTLrecord *inftl)
static int warncount = 5;
if (warncount) {
- printk(KERN_WARNING "INFTL: block read at 0x%x "
+ pr_warn("INFTL: block read at 0x%x "
"of mtd%d failed: %d\n",
block * inftl->EraseSize,
inftl->mbd.mtd->index, ret);
if (!--warncount)
- printk(KERN_WARNING "INFTL: further "
+ pr_warn("INFTL: further "
"failures for this block will "
"not be printed\n");
}
@@ -89,7 +89,7 @@ static int find_boot_record(struct INFTLrecord *inftl)
block * inftl->EraseSize + SECTORSIZE + 8,
8, &retlen,(char *)&h1);
if (ret < 0) {
- printk(KERN_WARNING "INFTL: ANAND header found at "
+ pr_warn("INFTL: ANAND header found at "
"0x%x in mtd%d, but OOB data read failed "
"(err %d)\n", block * inftl->EraseSize,
inftl->mbd.mtd->index, ret);
@@ -107,13 +107,13 @@ static int find_boot_record(struct INFTLrecord *inftl)
mtd_read(mtd, block * inftl->EraseSize + 4096, SECTORSIZE,
&retlen, buf);
if (retlen != SECTORSIZE) {
- printk(KERN_WARNING "INFTL: Unable to read spare "
+ pr_warn("INFTL: Unable to read spare "
"Media Header\n");
return -1;
}
/* Check if this one is the same as the first one we found. */
if (memcmp(mh, buf, sizeof(struct INFTLMediaHeader))) {
- printk(KERN_WARNING "INFTL: Primary and spare Media "
+ pr_warn("INFTL: Primary and spare Media "
"Headers disagree.\n");
return -1;
}
@@ -141,14 +141,14 @@ static int find_boot_record(struct INFTLrecord *inftl)
mh->OsakVersion, mh->PercentUsed);
if (mh->NoOfBDTLPartitions == 0) {
- printk(KERN_WARNING "INFTL: Media Header sanity check "
+ pr_warn("INFTL: Media Header sanity check "
"failed: NoOfBDTLPartitions (%d) == 0, "
"must be at least 1\n", mh->NoOfBDTLPartitions);
return -1;
}
if ((mh->NoOfBDTLPartitions + mh->NoOfBinaryPartitions) > 4) {
- printk(KERN_WARNING "INFTL: Media Header sanity check "
+ pr_warn("INFTL: Media Header sanity check "
"failed: Total Partitions (%d) > 4, "
"BDTL=%d Binary=%d\n", mh->NoOfBDTLPartitions +
mh->NoOfBinaryPartitions,
@@ -158,12 +158,12 @@ static int find_boot_record(struct INFTLrecord *inftl)
}
if (mh->BlockMultiplierBits > 1) {
- printk(KERN_WARNING "INFTL: sorry, we don't support "
+ pr_warn("INFTL: sorry, we don't support "
"UnitSizeFactor 0x%02x\n",
mh->BlockMultiplierBits);
return -1;
} else if (mh->BlockMultiplierBits == 1) {
- printk(KERN_WARNING "INFTL: support for INFTL with "
+ pr_warn("INFTL: support for INFTL with "
"UnitSizeFactor 0x%02x is experimental\n",
mh->BlockMultiplierBits);
inftl->EraseSize = inftl->mbd.mtd->erasesize <<
@@ -207,7 +207,7 @@ static int find_boot_record(struct INFTLrecord *inftl)
mtd_erase(mtd, instr);
}
if ((ip->lastUnit - ip->firstUnit + 1) < ip->virtualUnits) {
- printk(KERN_WARNING "INFTL: Media Header "
+ pr_warn("INFTL: Media Header "
"Partition %d sanity check failed\n"
" firstUnit %d : lastUnit %d > "
"virtualUnits %d\n", i, ip->lastUnit,
@@ -215,7 +215,7 @@ static int find_boot_record(struct INFTLrecord *inftl)
return -1;
}
if (ip->Reserved1 != 0) {
- printk(KERN_WARNING "INFTL: Media Header "
+ pr_warn("INFTL: Media Header "
"Partition %d sanity check failed: "
"Reserved1 %d != 0\n",
i, ip->Reserved1);
@@ -227,7 +227,7 @@ static int find_boot_record(struct INFTLrecord *inftl)
}
if (i >= 4) {
- printk(KERN_WARNING "INFTL: Media Header Partition "
+ pr_warn("INFTL: Media Header Partition "
"sanity check failed:\n No partition "
"marked as Disk Partition\n");
return -1;
@@ -237,7 +237,7 @@ static int find_boot_record(struct INFTLrecord *inftl)
inftl->numvunits = ip->virtualUnits;
if (inftl->numvunits > (inftl->nb_blocks -
inftl->nb_boot_blocks - 2)) {
- printk(KERN_WARNING "INFTL: Media Header sanity check "
+ pr_warn("INFTL: Media Header sanity check "
"failed:\n numvunits (%d) > nb_blocks "
"(%d) - nb_boot_blocks(%d) - 2\n",
inftl->numvunits, inftl->nb_blocks,
@@ -385,7 +385,7 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block)
ret = mtd_erase(inftl->mbd.mtd, instr);
if (ret) {
- printk(KERN_WARNING "INFTL: error while formatting block %d\n",
+ pr_warn("INFTL: error while formatting block %d\n",
block);
goto fail;
}
@@ -428,13 +428,13 @@ static void format_chain(struct INFTLrecord *inftl, unsigned int first_block)
{
unsigned int block = first_block, block1;
- printk(KERN_WARNING "INFTL: formatting chain at block %d\n",
+ pr_warn("INFTL: formatting chain at block %d\n",
first_block);
for (;;) {
block1 = inftl->PUtable[block];
- printk(KERN_WARNING "INFTL: formatting block %d\n", block);
+ pr_warn("INFTL: formatting block %d\n", block);
if (INFTL_formatblock(inftl, block) < 0) {
/*
* Cannot format !!!! Mark it as Bad Unit,
@@ -539,7 +539,7 @@ int INFTL_mount(struct INFTLrecord *s)
/* Search for INFTL MediaHeader and Spare INFTL Media Header */
if (find_boot_record(s) < 0) {
- printk(KERN_WARNING "INFTL: could not find valid boot record?\n");
+ pr_warn("INFTL: could not find valid boot record?\n");
return -ENXIO;
}
@@ -610,7 +610,7 @@ int INFTL_mount(struct INFTLrecord *s)
/* Check for invalid block */
if (erase_mark != ERASE_MARK) {
- printk(KERN_WARNING "INFTL: corrupt block %d "
+ pr_warn("INFTL: corrupt block %d "
"in chain %d, chain length %d, erase "
"mark 0x%x?\n", block, first_block,
chain_length, erase_mark);
@@ -635,7 +635,7 @@ int INFTL_mount(struct INFTLrecord *s)
((prev_block >= s->nb_blocks) &&
(prev_block != BLOCK_NIL))) {
if (chain_length > 0) {
- printk(KERN_WARNING "INFTL: corrupt "
+ pr_warn("INFTL: corrupt "
"block %d in chain %d?\n",
block, first_block);
do_format_chain++;
@@ -670,7 +670,7 @@ int INFTL_mount(struct INFTLrecord *s)
/* Validate next block before following it... */
if (block > s->lastEUN) {
- printk(KERN_WARNING "INFTL: invalid previous "
+ pr_warn("INFTL: invalid previous "
"block %d in chain %d?\n", block,
first_block);
do_format_chain++;
@@ -714,7 +714,7 @@ int INFTL_mount(struct INFTLrecord *s)
if (s->PUtable[block] == BLOCK_NIL)
break;
if (s->PUtable[block] > s->lastEUN) {
- printk(KERN_WARNING "INFTL: invalid prev %d, "
+ pr_warn("INFTL: invalid prev %d, "
"in virtual chain %d\n",
s->PUtable[block], logical_block);
s->PUtable[block] = BLOCK_NIL;
@@ -757,7 +757,7 @@ int INFTL_mount(struct INFTLrecord *s)
pr_debug("INFTL: pass 3, format unused blocks\n");
for (block = s->firstEUN; block <= s->lastEUN; block++) {
if (s->PUtable[block] == BLOCK_NOTEXPLORED) {
- printk("INFTL: unreferenced block %d, formatting it\n",
+ pr_warn("INFTL: unreferenced block %d, formatting it\n",
block);
if (INFTL_formatblock(s, block) < 0)
s->PUtable[block] = BLOCK_RESERVED;
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index 99d5ff9a1fbe..dce5e67ce3c2 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -125,40 +125,6 @@ config MTD_SUN_UFLASH
Sun Microsystems boardsets. This driver will require CFI support
in the kernel, so if you did not enable CFI previously, do that now.
-config MTD_SC520CDP
- tristate "CFI Flash device mapped on AMD SC520 CDP"
- depends on (MELAN || COMPILE_TEST) && MTD_CFI
- help
- The SC520 CDP board has two banks of CFI-compliant chips and one
- Dual-in-line JEDEC chip. This 'mapping' driver supports that
- arrangement, implementing three MTD devices.
-
-config MTD_NETSC520
- tristate "CFI Flash device mapped on AMD NetSc520"
- depends on (MELAN || COMPILE_TEST) && MTD_CFI
- help
- This enables access routines for the flash chips on the AMD NetSc520
- demonstration board. If you have one of these boards and would like
- to use the flash chips on it, say 'Y'.
-
-config MTD_TS5500
- tristate "JEDEC Flash device mapped on Technologic Systems TS-5500"
- depends on TS5500 || COMPILE_TEST
- select MTD_JEDECPROBE
- select MTD_CFI_AMDSTD
- help
- This provides a driver for the on-board flash of the Technologic
- System's TS-5500 board. The 2MB flash is split into 3 partitions
- which are accessed as separate MTD devices.
-
- mtd0 and mtd2 are the two BIOS drives, which use the resident
- flash disk (RFD) flash translation layer.
-
- mtd1 allows you to reprogram your BIOS. BE VERY CAREFUL.
-
- Note that jumper 3 ("Write Enable Drive A") must be set
- otherwise detection won't succeed.
-
config MTD_SBC_GXX
tristate "CFI Flash device mapped on Arcom SBC-GXx boards"
depends on X86 && MTD_CFI_INTELEXT && MTD_COMPLEX_MAPPINGS
@@ -238,12 +204,6 @@ config MTD_TSUNAMI
help
Support for the flash chip on Tsunami TIG bus.
-config MTD_NETtel
- tristate "CFI flash device on SnapGear/SecureEdge"
- depends on X86 && MTD_JEDECPROBE
- help
- Support for flash chips on NETtel/SecureEdge/SnapGear boards.
-
config MTD_LANTIQ
tristate "Lantiq SoC NOR support"
depends on LANTIQ
@@ -289,13 +249,6 @@ config MTD_DC21285
21285 bridge used with Intel's StrongARM processors. More info at
<https://www.intel.com/design/bridge/docs/21285_documentation.htm>.
-config MTD_IMPA7
- tristate "JEDEC Flash device mapped on impA7"
- depends on ARM && MTD_JEDECPROBE
- help
- This enables access to the NOR Flash on the impA7 board of
- implementa GmbH. If you have such a board, say 'Y' here.
-
# This needs CFI or JEDEC, depending on the cards found.
config MTD_PCI
tristate "PCI MTD driver"
@@ -324,12 +277,6 @@ config MTD_PCMCIA_ANONYMOUS
If unsure, say N.
-config MTD_UCLINUX
- bool "Generic uClinux RAM/ROM filesystem support"
- depends on (MTD_RAM=y || MTD_ROM=y) && (!MMU || COLDFIRE)
- help
- Map driver to support image based filesystems for uClinux.
-
config MTD_PLATRAM
tristate "Map driver for platform device RAM (mtd-ram)"
select MTD_RAM
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
index 51af1d2ebd52..fbed278157f6 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -26,16 +26,10 @@ obj-$(CONFIG_MTD_PISMO) += pismo.o
obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o
obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o
obj-$(CONFIG_MTD_SBC_GXX) += sbc_gxx.o
-obj-$(CONFIG_MTD_SC520CDP) += sc520cdp.o
-obj-$(CONFIG_MTD_NETSC520) += netsc520.o
-obj-$(CONFIG_MTD_TS5500) += ts5500_flash.o
obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o
obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o
obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
obj-$(CONFIG_MTD_PCI) += pci.o
-obj-$(CONFIG_MTD_IMPA7) += impa7.o
-obj-$(CONFIG_MTD_UCLINUX) += uclinux.o
-obj-$(CONFIG_MTD_NETtel) += nettel.o
obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o
obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o
obj-$(CONFIG_MTD_VMU) += vmu-flash.o
diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c
index 95f9b99ce5fc..457e6ab7f3fb 100644
--- a/drivers/mtd/maps/amd76xrom.c
+++ b/drivers/mtd/maps/amd76xrom.c
@@ -296,12 +296,10 @@ static void amd76xrom_remove_one(struct pci_dev *pdev)
}
static const struct pci_device_id amd76xrom_pci_tbl[] = {
- { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7410,
- PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7440,
- PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_AMD, 0x7468 }, /* amd8111 support */
- { 0, }
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7410) },
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7440) },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_AMD, 0x7468, 0, 0) }, /* amd8111 support */
+ { }
};
MODULE_DEVICE_TABLE(pci, amd76xrom_pci_tbl);
diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c
index 5e44134caa8e..ef4afa8c1931 100644
--- a/drivers/mtd/maps/ck804xrom.c
+++ b/drivers/mtd/maps/ck804xrom.c
@@ -335,7 +335,7 @@ static const struct pci_device_id ck804xrom_pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, 0x0365), .driver_data = DEV_MCP55 },
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, 0x0366), .driver_data = DEV_MCP55 },
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, 0x0367), .driver_data = DEV_MCP55 },
- { 0, }
+ { }
};
#if 0
diff --git a/drivers/mtd/maps/esb2rom.c b/drivers/mtd/maps/esb2rom.c
index fc7f2c3a62b0..043bad544414 100644
--- a/drivers/mtd/maps/esb2rom.c
+++ b/drivers/mtd/maps/esb2rom.c
@@ -385,19 +385,13 @@ static void esb2rom_remove_one(struct pci_dev *pdev)
}
static const struct pci_device_id esb2rom_pci_tbl[] = {
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0,
- PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0,
- PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0,
- PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0,
- PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1,
- PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_0,
- PCI_ANY_ID, PCI_ANY_ID, },
- { 0, },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_0) },
+ { },
};
#if 0
diff --git a/drivers/mtd/maps/ichxrom.c b/drivers/mtd/maps/ichxrom.c
index 6608b782149b..8fecd1820f6e 100644
--- a/drivers/mtd/maps/ichxrom.c
+++ b/drivers/mtd/maps/ichxrom.c
@@ -323,17 +323,12 @@ static void ichxrom_remove_one(struct pci_dev *pdev)
}
static const struct pci_device_id ichxrom_pci_tbl[] = {
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0,
- PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0,
- PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0,
- PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0,
- PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1,
- PCI_ANY_ID, PCI_ANY_ID, },
- { 0, },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0), },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0), },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0), },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0), },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1), },
+ { },
};
#if 0
diff --git a/drivers/mtd/maps/impa7.c b/drivers/mtd/maps/impa7.c
deleted file mode 100644
index b41401852fb7..000000000000
--- a/drivers/mtd/maps/impa7.c
+++ /dev/null
@@ -1,115 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Handle mapping of the NOR flash on implementa A7 boards
- *
- * Copyright 2002 SYSGO Real-Time Solutions GmbH
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <asm/io.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-
-#define WINDOW_ADDR0 0x00000000 /* physical properties of flash */
-#define WINDOW_SIZE0 0x00800000
-#define WINDOW_ADDR1 0x10000000 /* physical properties of flash */
-#define WINDOW_SIZE1 0x00800000
-#define NUM_FLASHBANKS 2
-#define BUSWIDTH 4
-
-#define MSG_PREFIX "impA7:" /* prefix for our printk()'s */
-#define MTDID "impa7-%d" /* for mtdparts= partitioning */
-
-static struct mtd_info *impa7_mtd[NUM_FLASHBANKS];
-
-static const char * const rom_probe_types[] = { "jedec_probe", NULL };
-
-static struct map_info impa7_map[NUM_FLASHBANKS] = {
- {
- .name = "impA7 NOR Flash Bank #0",
- .size = WINDOW_SIZE0,
- .bankwidth = BUSWIDTH,
- },
- {
- .name = "impA7 NOR Flash Bank #1",
- .size = WINDOW_SIZE1,
- .bankwidth = BUSWIDTH,
- },
-};
-
-/*
- * MTD partitioning stuff
- */
-static const struct mtd_partition partitions[] =
-{
- {
- .name = "FileSystem",
- .size = 0x800000,
- .offset = 0x00000000
- },
-};
-
-static int __init init_impa7(void)
-{
- const char * const *type;
- int i;
- static struct { u_long addr; u_long size; } pt[NUM_FLASHBANKS] = {
- { WINDOW_ADDR0, WINDOW_SIZE0 },
- { WINDOW_ADDR1, WINDOW_SIZE1 },
- };
- int devicesfound = 0;
-
- for(i=0; i<NUM_FLASHBANKS; i++)
- {
- printk(KERN_NOTICE MSG_PREFIX "probing 0x%08lx at 0x%08lx\n",
- pt[i].size, pt[i].addr);
-
- impa7_map[i].phys = pt[i].addr;
- impa7_map[i].virt = ioremap(pt[i].addr, pt[i].size);
- if (!impa7_map[i].virt) {
- printk(MSG_PREFIX "failed to ioremap\n");
- return -EIO;
- }
- simple_map_init(&impa7_map[i]);
-
- impa7_mtd[i] = NULL;
- type = rom_probe_types;
- for(; !impa7_mtd[i] && *type; type++) {
- impa7_mtd[i] = do_map_probe(*type, &impa7_map[i]);
- }
-
- if (impa7_mtd[i]) {
- impa7_mtd[i]->owner = THIS_MODULE;
- devicesfound++;
- mtd_device_register(impa7_mtd[i], partitions,
- ARRAY_SIZE(partitions));
- } else {
- iounmap((void __iomem *)impa7_map[i].virt);
- }
- }
- return devicesfound == 0 ? -ENXIO : 0;
-}
-
-static void __exit cleanup_impa7(void)
-{
- int i;
- for (i=0; i<NUM_FLASHBANKS; i++) {
- if (impa7_mtd[i]) {
- mtd_device_unregister(impa7_mtd[i]);
- map_destroy(impa7_mtd[i]);
- iounmap((void __iomem *)impa7_map[i].virt);
- impa7_map[i].virt = NULL;
- }
- }
-}
-
-module_init(init_impa7);
-module_exit(cleanup_impa7);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Pavel Bartusek <pba@sysgo.de>");
-MODULE_DESCRIPTION("MTD map driver for implementa impA7");
diff --git a/drivers/mtd/maps/netsc520.c b/drivers/mtd/maps/netsc520.c
deleted file mode 100644
index 0bb651624f05..000000000000
--- a/drivers/mtd/maps/netsc520.c
+++ /dev/null
@@ -1,127 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* netsc520.c -- MTD map driver for AMD NetSc520 Demonstration Board
- *
- * Copyright (C) 2001 Mark Langsdorf (mark.langsdorf@amd.com)
- * based on sc520cdp.c by Sysgo Real-Time Solutions GmbH
- *
- * The NetSc520 is a demonstration board for the Elan Sc520 processor available
- * from AMD. It has a single back of 16 megs of 32-bit Flash ROM and another
- * 16 megs of SDRAM.
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <asm/io.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-
-
-/*
-** The single, 16 megabyte flash bank is divided into four virtual
-** partitions. The first partition is 768 KiB and is intended to
-** store the kernel image loaded by the bootstrap loader. The second
-** partition is 256 KiB and holds the BIOS image. The third
-** partition is 14.5 MiB and is intended for the flash file system
-** image. The last partition is 512 KiB and contains another copy
-** of the BIOS image and the reset vector.
-**
-** Only the third partition should be mounted. The first partition
-** should not be mounted, but it can erased and written to using the
-** MTD character routines. The second and fourth partitions should
-** not be touched - it is possible to corrupt the BIOS image by
-** mounting these partitions, and potentially the board will not be
-** recoverable afterwards.
-*/
-
-/* partition_info gives details on the logical partitions that the split the
- * single flash device into. If the size if zero we use up to the end of the
- * device. */
-static const struct mtd_partition partition_info[] = {
- {
- .name = "NetSc520 boot kernel",
- .offset = 0,
- .size = 0xc0000
- },
- {
- .name = "NetSc520 Low BIOS",
- .offset = 0xc0000,
- .size = 0x40000
- },
- {
- .name = "NetSc520 file system",
- .offset = 0x100000,
- .size = 0xe80000
- },
- {
- .name = "NetSc520 High BIOS",
- .offset = 0xf80000,
- .size = 0x80000
- },
-};
-#define NUM_PARTITIONS ARRAY_SIZE(partition_info)
-
-#define WINDOW_SIZE 0x00100000
-#define WINDOW_ADDR 0x00200000
-
-static struct map_info netsc520_map = {
- .name = "netsc520 Flash Bank",
- .size = WINDOW_SIZE,
- .bankwidth = 4,
- .phys = WINDOW_ADDR,
-};
-
-#define NUM_FLASH_BANKS ARRAY_SIZE(netsc520_map)
-
-static struct mtd_info *mymtd;
-
-static int __init init_netsc520(void)
-{
- printk(KERN_NOTICE "NetSc520 flash device: 0x%Lx at 0x%Lx\n",
- (unsigned long long)netsc520_map.size,
- (unsigned long long)netsc520_map.phys);
- netsc520_map.virt = ioremap(netsc520_map.phys, netsc520_map.size);
-
- if (!netsc520_map.virt) {
- printk("Failed to ioremap\n");
- return -EIO;
- }
-
- simple_map_init(&netsc520_map);
-
- mymtd = do_map_probe("cfi_probe", &netsc520_map);
- if(!mymtd)
- mymtd = do_map_probe("map_ram", &netsc520_map);
- if(!mymtd)
- mymtd = do_map_probe("map_rom", &netsc520_map);
-
- if (!mymtd) {
- iounmap(netsc520_map.virt);
- return -ENXIO;
- }
-
- mymtd->owner = THIS_MODULE;
- mtd_device_register(mymtd, partition_info, NUM_PARTITIONS);
- return 0;
-}
-
-static void __exit cleanup_netsc520(void)
-{
- if (mymtd) {
- mtd_device_unregister(mymtd);
- map_destroy(mymtd);
- }
- if (netsc520_map.virt) {
- iounmap(netsc520_map.virt);
- netsc520_map.virt = NULL;
- }
-}
-
-module_init(init_netsc520);
-module_exit(cleanup_netsc520);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mark Langsdorf <mark.langsdorf@amd.com>");
-MODULE_DESCRIPTION("MTD map driver for AMD NetSc520 Demonstration Board");
diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c
deleted file mode 100644
index 7d349874ffeb..000000000000
--- a/drivers/mtd/maps/nettel.c
+++ /dev/null
@@ -1,462 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/****************************************************************************/
-
-/*
- * nettel.c -- mappings for NETtel/SecureEdge/SnapGear (x86) boards.
- *
- * (C) Copyright 2000-2001, Greg Ungerer (gerg@snapgear.com)
- * (C) Copyright 2001-2002, SnapGear (www.snapgear.com)
- */
-
-/****************************************************************************/
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-#include <linux/mtd/cfi.h>
-#include <linux/reboot.h>
-#include <linux/err.h>
-#include <linux/kdev_t.h>
-#include <linux/root_dev.h>
-#include <asm/io.h>
-
-/****************************************************************************/
-
-#define INTEL_BUSWIDTH 1
-#define AMD_WINDOW_MAXSIZE 0x00200000
-#define AMD_BUSWIDTH 1
-
-/*
- * PAR masks and shifts, assuming 64K pages.
- */
-#define SC520_PAR_ADDR_MASK 0x00003fff
-#define SC520_PAR_ADDR_SHIFT 16
-#define SC520_PAR_TO_ADDR(par) \
- (((par)&SC520_PAR_ADDR_MASK) << SC520_PAR_ADDR_SHIFT)
-
-#define SC520_PAR_SIZE_MASK 0x01ffc000
-#define SC520_PAR_SIZE_SHIFT 2
-#define SC520_PAR_TO_SIZE(par) \
- ((((par)&SC520_PAR_SIZE_MASK) << SC520_PAR_SIZE_SHIFT) + (64*1024))
-
-#define SC520_PAR(cs, addr, size) \
- ((cs) | \
- ((((size)-(64*1024)) >> SC520_PAR_SIZE_SHIFT) & SC520_PAR_SIZE_MASK) | \
- (((addr) >> SC520_PAR_ADDR_SHIFT) & SC520_PAR_ADDR_MASK))
-
-#define SC520_PAR_BOOTCS 0x8a000000
-#define SC520_PAR_ROMCS1 0xaa000000
-#define SC520_PAR_ROMCS2 0xca000000 /* Cache disabled, 64K page */
-
-static void *nettel_mmcrp = NULL;
-
-#ifdef CONFIG_MTD_CFI_INTELEXT
-static struct mtd_info *intel_mtd;
-#endif
-static struct mtd_info *amd_mtd;
-
-/****************************************************************************/
-
-/****************************************************************************/
-
-#ifdef CONFIG_MTD_CFI_INTELEXT
-static struct map_info nettel_intel_map = {
- .name = "SnapGear Intel",
- .size = 0,
- .bankwidth = INTEL_BUSWIDTH,
-};
-
-static struct mtd_partition nettel_intel_partitions[] = {
- {
- .name = "SnapGear kernel",
- .offset = 0,
- .size = 0x000e0000
- },
- {
- .name = "SnapGear filesystem",
- .offset = 0x00100000,
- },
- {
- .name = "SnapGear config",
- .offset = 0x000e0000,
- .size = 0x00020000
- },
- {
- .name = "SnapGear Intel",
- .offset = 0
- },
- {
- .name = "SnapGear BIOS Config",
- .offset = 0x007e0000,
- .size = 0x00020000
- },
- {
- .name = "SnapGear BIOS",
- .offset = 0x007e0000,
- .size = 0x00020000
- },
-};
-#endif
-
-static struct map_info nettel_amd_map = {
- .name = "SnapGear AMD",
- .size = AMD_WINDOW_MAXSIZE,
- .bankwidth = AMD_BUSWIDTH,
-};
-
-static const struct mtd_partition nettel_amd_partitions[] = {
- {
- .name = "SnapGear BIOS config",
- .offset = 0x000e0000,
- .size = 0x00010000
- },
- {
- .name = "SnapGear BIOS",
- .offset = 0x000f0000,
- .size = 0x00010000
- },
- {
- .name = "SnapGear AMD",
- .offset = 0
- },
- {
- .name = "SnapGear high BIOS",
- .offset = 0x001f0000,
- .size = 0x00010000
- }
-};
-
-#define NUM_AMD_PARTITIONS ARRAY_SIZE(nettel_amd_partitions)
-
-/****************************************************************************/
-
-#ifdef CONFIG_MTD_CFI_INTELEXT
-
-/*
- * Set the Intel flash back to read mode since some old boot
- * loaders don't.
- */
-static int nettel_reboot_notifier(struct notifier_block *nb, unsigned long val, void *v)
-{
- struct cfi_private *cfi = nettel_intel_map.fldrv_priv;
- unsigned long b;
-
- /* Make sure all FLASH chips are put back into read mode */
- for (b = 0; (b < nettel_intel_partitions[3].size); b += 0x100000) {
- cfi_send_gen_cmd(0xff, 0x55, b, &nettel_intel_map, cfi,
- cfi->device_type, NULL);
- }
- return(NOTIFY_OK);
-}
-
-static struct notifier_block nettel_notifier_block = {
- nettel_reboot_notifier, NULL, 0
-};
-
-#endif
-
-/****************************************************************************/
-
-static int __init nettel_init(void)
-{
- volatile unsigned long *amdpar;
- unsigned long amdaddr, maxsize;
- int num_amd_partitions=0;
-#ifdef CONFIG_MTD_CFI_INTELEXT
- volatile unsigned long *intel0par, *intel1par;
- unsigned long orig_bootcspar, orig_romcs1par;
- unsigned long intel0addr, intel0size;
- unsigned long intel1addr, intel1size;
- int intelboot, intel0cs, intel1cs;
- int num_intel_partitions;
-#endif
- int rc = 0;
-
- nettel_mmcrp = (void *) ioremap(0xfffef000, 4096);
- if (nettel_mmcrp == NULL) {
- printk("SNAPGEAR: failed to disable MMCR cache??\n");
- return(-EIO);
- }
-
- /* Set CPU clock to be 33.000MHz */
- *((unsigned char *) (nettel_mmcrp + 0xc64)) = 0x01;
-
- amdpar = (volatile unsigned long *) (nettel_mmcrp + 0xc4);
-
-#ifdef CONFIG_MTD_CFI_INTELEXT
- intelboot = 0;
- intel0cs = SC520_PAR_ROMCS1;
- intel0par = (volatile unsigned long *) (nettel_mmcrp + 0xc0);
- intel1cs = SC520_PAR_ROMCS2;
- intel1par = (volatile unsigned long *) (nettel_mmcrp + 0xbc);
-
- /*
- * Save the CS settings then ensure ROMCS1 and ROMCS2 are off,
- * otherwise they might clash with where we try to map BOOTCS.
- */
- orig_bootcspar = *amdpar;
- orig_romcs1par = *intel0par;
- *intel0par = 0;
- *intel1par = 0;
-#endif
-
- /*
- * The first thing to do is determine if we have a separate
- * boot FLASH device. Typically this is a small (1 to 2MB)
- * AMD FLASH part. It seems that device size is about the
- * only way to tell if this is the case...
- */
- amdaddr = 0x20000000;
- maxsize = AMD_WINDOW_MAXSIZE;
-
- *amdpar = SC520_PAR(SC520_PAR_BOOTCS, amdaddr, maxsize);
- __asm__ ("wbinvd");
-
- nettel_amd_map.phys = amdaddr;
- nettel_amd_map.virt = ioremap(amdaddr, maxsize);
- if (!nettel_amd_map.virt) {
- printk("SNAPGEAR: failed to ioremap() BOOTCS\n");
- iounmap(nettel_mmcrp);
- return(-EIO);
- }
- simple_map_init(&nettel_amd_map);
-
- if ((amd_mtd = do_map_probe("jedec_probe", &nettel_amd_map))) {
- printk(KERN_NOTICE "SNAPGEAR: AMD flash device size = %dK\n",
- (int)(amd_mtd->size>>10));
-
- amd_mtd->owner = THIS_MODULE;
-
- /* The high BIOS partition is only present for 2MB units */
- num_amd_partitions = NUM_AMD_PARTITIONS;
- if (amd_mtd->size < AMD_WINDOW_MAXSIZE)
- num_amd_partitions--;
- /* Don't add the partition until after the primary INTEL's */
-
-#ifdef CONFIG_MTD_CFI_INTELEXT
- /*
- * Map the Intel flash into memory after the AMD
- * It has to start on a multiple of maxsize.
- */
- maxsize = SC520_PAR_TO_SIZE(orig_romcs1par);
- if (maxsize < (32 * 1024 * 1024))
- maxsize = (32 * 1024 * 1024);
- intel0addr = amdaddr + maxsize;
-#endif
- } else {
-#ifdef CONFIG_MTD_CFI_INTELEXT
- /* INTEL boot FLASH */
- intelboot++;
-
- if (!orig_romcs1par) {
- intel0cs = SC520_PAR_BOOTCS;
- intel0par = (volatile unsigned long *)
- (nettel_mmcrp + 0xc4);
- intel1cs = SC520_PAR_ROMCS1;
- intel1par = (volatile unsigned long *)
- (nettel_mmcrp + 0xc0);
-
- intel0addr = SC520_PAR_TO_ADDR(orig_bootcspar);
- maxsize = SC520_PAR_TO_SIZE(orig_bootcspar);
- } else {
- /* Kernel base is on ROMCS1, not BOOTCS */
- intel0cs = SC520_PAR_ROMCS1;
- intel0par = (volatile unsigned long *)
- (nettel_mmcrp + 0xc0);
- intel1cs = SC520_PAR_BOOTCS;
- intel1par = (volatile unsigned long *)
- (nettel_mmcrp + 0xc4);
-
- intel0addr = SC520_PAR_TO_ADDR(orig_romcs1par);
- maxsize = SC520_PAR_TO_SIZE(orig_romcs1par);
- }
-
- /* Destroy useless AMD MTD mapping */
- amd_mtd = NULL;
- iounmap(nettel_amd_map.virt);
- nettel_amd_map.virt = NULL;
-#else
- /* Only AMD flash supported */
- rc = -ENXIO;
- goto out_unmap2;
-#endif
- }
-
-#ifdef CONFIG_MTD_CFI_INTELEXT
- /*
- * We have determined the INTEL FLASH configuration, so lets
- * go ahead and probe for them now.
- */
-
- /* Set PAR to the maximum size */
- if (maxsize < (32 * 1024 * 1024))
- maxsize = (32 * 1024 * 1024);
- *intel0par = SC520_PAR(intel0cs, intel0addr, maxsize);
-
- /* Turn other PAR off so the first probe doesn't find it */
- *intel1par = 0;
-
- /* Probe for the size of the first Intel flash */
- nettel_intel_map.size = maxsize;
- nettel_intel_map.phys = intel0addr;
- nettel_intel_map.virt = ioremap(intel0addr, maxsize);
- if (!nettel_intel_map.virt) {
- printk("SNAPGEAR: failed to ioremap() ROMCS1\n");
- rc = -EIO;
- goto out_unmap2;
- }
- simple_map_init(&nettel_intel_map);
-
- intel_mtd = do_map_probe("cfi_probe", &nettel_intel_map);
- if (!intel_mtd) {
- rc = -ENXIO;
- goto out_unmap1;
- }
-
- /* Set PAR to the detected size */
- intel0size = intel_mtd->size;
- *intel0par = SC520_PAR(intel0cs, intel0addr, intel0size);
-
- /*
- * Map second Intel FLASH right after first. Set its size to the
- * same maxsize used for the first Intel FLASH.
- */
- intel1addr = intel0addr + intel0size;
- *intel1par = SC520_PAR(intel1cs, intel1addr, maxsize);
- __asm__ ("wbinvd");
-
- maxsize += intel0size;
-
- /* Delete the old map and probe again to do both chips */
- map_destroy(intel_mtd);
- intel_mtd = NULL;
- iounmap(nettel_intel_map.virt);
-
- nettel_intel_map.size = maxsize;
- nettel_intel_map.virt = ioremap(intel0addr, maxsize);
- if (!nettel_intel_map.virt) {
- printk("SNAPGEAR: failed to ioremap() ROMCS1/2\n");
- rc = -EIO;
- goto out_unmap2;
- }
-
- intel_mtd = do_map_probe("cfi_probe", &nettel_intel_map);
- if (! intel_mtd) {
- rc = -ENXIO;
- goto out_unmap1;
- }
-
- intel1size = intel_mtd->size - intel0size;
- if (intel1size > 0) {
- *intel1par = SC520_PAR(intel1cs, intel1addr, intel1size);
- __asm__ ("wbinvd");
- } else {
- *intel1par = 0;
- }
-
- printk(KERN_NOTICE "SNAPGEAR: Intel flash device size = %lldKiB\n",
- (unsigned long long)(intel_mtd->size >> 10));
-
- intel_mtd->owner = THIS_MODULE;
-
- num_intel_partitions = ARRAY_SIZE(nettel_intel_partitions);
-
- if (intelboot) {
- /*
- * Adjust offset and size of last boot partition.
- * Must allow for BIOS region at end of FLASH.
- */
- nettel_intel_partitions[1].size = (intel0size + intel1size) -
- (1024*1024 + intel_mtd->erasesize);
- nettel_intel_partitions[3].size = intel0size + intel1size;
- nettel_intel_partitions[4].offset =
- (intel0size + intel1size) - intel_mtd->erasesize;
- nettel_intel_partitions[4].size = intel_mtd->erasesize;
- nettel_intel_partitions[5].offset =
- nettel_intel_partitions[4].offset;
- nettel_intel_partitions[5].size =
- nettel_intel_partitions[4].size;
- } else {
- /* No BIOS regions when AMD boot */
- num_intel_partitions -= 2;
- }
- rc = mtd_device_register(intel_mtd, nettel_intel_partitions,
- num_intel_partitions);
- if (rc)
- goto out_map_destroy;
-#endif
-
- if (amd_mtd) {
- rc = mtd_device_register(amd_mtd, nettel_amd_partitions,
- num_amd_partitions);
- if (rc)
- goto out_mtd_unreg;
- }
-
-#ifdef CONFIG_MTD_CFI_INTELEXT
- register_reboot_notifier(&nettel_notifier_block);
-#endif
-
- return rc;
-
-out_mtd_unreg:
-#ifdef CONFIG_MTD_CFI_INTELEXT
- mtd_device_unregister(intel_mtd);
-out_map_destroy:
- map_destroy(intel_mtd);
-out_unmap1:
- iounmap(nettel_intel_map.virt);
-#endif
-
-out_unmap2:
- iounmap(nettel_mmcrp);
- iounmap(nettel_amd_map.virt);
-
- return rc;
-}
-
-/****************************************************************************/
-
-static void __exit nettel_cleanup(void)
-{
-#ifdef CONFIG_MTD_CFI_INTELEXT
- unregister_reboot_notifier(&nettel_notifier_block);
-#endif
- if (amd_mtd) {
- mtd_device_unregister(amd_mtd);
- map_destroy(amd_mtd);
- }
- if (nettel_mmcrp) {
- iounmap(nettel_mmcrp);
- nettel_mmcrp = NULL;
- }
- if (nettel_amd_map.virt) {
- iounmap(nettel_amd_map.virt);
- nettel_amd_map.virt = NULL;
- }
-#ifdef CONFIG_MTD_CFI_INTELEXT
- if (intel_mtd) {
- mtd_device_unregister(intel_mtd);
- map_destroy(intel_mtd);
- }
- if (nettel_intel_map.virt) {
- iounmap(nettel_intel_map.virt);
- nettel_intel_map.virt = NULL;
- }
-#endif
-}
-
-/****************************************************************************/
-
-module_init(nettel_init);
-module_exit(nettel_cleanup);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>");
-MODULE_DESCRIPTION("SnapGear/SecureEdge FLASH support");
-
-/****************************************************************************/
diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c
index 4a84ca8aed40..34041cc8f173 100644
--- a/drivers/mtd/maps/pci.c
+++ b/drivers/mtd/maps/pci.c
@@ -227,22 +227,16 @@ static struct mtd_pci_info intel_dc21285_info = {
static const struct pci_device_id mtd_pci_ids[] = {
{
- .vendor = PCI_VENDOR_ID_INTEL,
- .device = 0x530d,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x530d),
.class = PCI_CLASS_MEMORY_OTHER << 8,
.class_mask = 0xffff00,
.driver_data = (unsigned long)&intel_iq80310_info,
- },
- {
- .vendor = PCI_VENDOR_ID_DEC,
- .device = PCI_DEVICE_ID_DEC_21285,
- .subvendor = 0, /* DC21285 defaults to 0 on reset */
- .subdevice = 0, /* DC21285 defaults to 0 on reset */
+ }, {
+ /* DC21285 defaults to 0 for .subvendor and .subdevice on reset */
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285, 0, 0),
.driver_data = (unsigned long)&intel_dc21285_info,
},
- { 0, }
+ { }
};
/*
diff --git a/drivers/mtd/maps/sc520cdp.c b/drivers/mtd/maps/sc520cdp.c
deleted file mode 100644
index 8ef7aec634c7..000000000000
--- a/drivers/mtd/maps/sc520cdp.c
+++ /dev/null
@@ -1,294 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* sc520cdp.c -- MTD map driver for AMD SC520 Customer Development Platform
- *
- * Copyright (C) 2001 Sysgo Real-Time Solutions GmbH
- *
- * The SC520CDP is an evaluation board for the Elan SC520 processor available
- * from AMD. It has two banks of 32-bit Flash ROM, each 8 Megabytes in size,
- * and up to 512 KiB of 8-bit DIL Flash ROM.
- * For details see https://www.amd.com/products/epd/desiging/evalboards/18.elansc520/520_cdp_brief/index.html
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <asm/io.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/concat.h>
-
-/*
-** The Embedded Systems BIOS decodes the first FLASH starting at
-** 0x8400000. This is a *terrible* place for it because accessing
-** the flash at this location causes the A22 address line to be high
-** (that's what 0x8400000 binary's ought to be). But this is the highest
-** order address line on the raw flash devices themselves!!
-** This causes the top HALF of the flash to be accessed first. Beyond
-** the physical limits of the flash, the flash chip aliases over (to
-** 0x880000 which causes the bottom half to be accessed. This splits the
-** flash into two and inverts it! If you then try to access this from another
-** program that does NOT do this insanity, then you *will* access the
-** first half of the flash, but not find what you expect there. That
-** stuff is in the *second* half! Similarly, the address used by the
-** BIOS for the second FLASH bank is also quite a bad choice.
-** If REPROGRAM_PAR is defined below (the default), then this driver will
-** choose more useful addresses for the FLASH banks by reprogramming the
-** responsible PARxx registers in the SC520's MMCR region. This will
-** cause the settings to be incompatible with the BIOS's settings, which
-** shouldn't be a problem since you are running Linux, (i.e. the BIOS is
-** not much use anyway). However, if you need to be compatible with
-** the BIOS for some reason, just undefine REPROGRAM_PAR.
-*/
-#define REPROGRAM_PAR
-
-
-
-#ifdef REPROGRAM_PAR
-
-/* These are the addresses we want.. */
-#define WINDOW_ADDR_0 0x08800000
-#define WINDOW_ADDR_1 0x09000000
-#define WINDOW_ADDR_2 0x09800000
-
-/* .. and these are the addresses the BIOS gives us */
-#define WINDOW_ADDR_0_BIOS 0x08400000
-#define WINDOW_ADDR_1_BIOS 0x08c00000
-#define WINDOW_ADDR_2_BIOS 0x09400000
-
-#else
-
-#define WINDOW_ADDR_0 0x08400000
-#define WINDOW_ADDR_1 0x08C00000
-#define WINDOW_ADDR_2 0x09400000
-
-#endif
-
-#define WINDOW_SIZE_0 0x00800000
-#define WINDOW_SIZE_1 0x00800000
-#define WINDOW_SIZE_2 0x00080000
-
-
-static struct map_info sc520cdp_map[] = {
- {
- .name = "SC520CDP Flash Bank #0",
- .size = WINDOW_SIZE_0,
- .bankwidth = 4,
- .phys = WINDOW_ADDR_0
- },
- {
- .name = "SC520CDP Flash Bank #1",
- .size = WINDOW_SIZE_1,
- .bankwidth = 4,
- .phys = WINDOW_ADDR_1
- },
- {
- .name = "SC520CDP DIL Flash",
- .size = WINDOW_SIZE_2,
- .bankwidth = 1,
- .phys = WINDOW_ADDR_2
- },
-};
-
-#define NUM_FLASH_BANKS ARRAY_SIZE(sc520cdp_map)
-
-static struct mtd_info *mymtd[NUM_FLASH_BANKS];
-static struct mtd_info *merged_mtd;
-
-#ifdef REPROGRAM_PAR
-
-/*
-** The SC520 MMCR (memory mapped control register) region resides
-** at 0xFFFEF000. The 16 Programmable Address Region (PAR) registers
-** are at offset 0x88 in the MMCR:
-*/
-#define SC520_MMCR_BASE 0xFFFEF000
-#define SC520_MMCR_EXTENT 0x1000
-#define SC520_PAR(x) ((0x88/sizeof(unsigned long)) + (x))
-#define NUM_SC520_PAR 16 /* total number of PAR registers */
-
-/*
-** The highest three bits in a PAR register determine what target
-** device is controlled by this PAR. Here, only ROMCS? and BOOTCS
-** devices are of interest.
-*/
-#define SC520_PAR_BOOTCS (0x4<<29)
-#define SC520_PAR_ROMCS0 (0x5<<29)
-#define SC520_PAR_ROMCS1 (0x6<<29)
-#define SC520_PAR_TRGDEV (0x7<<29)
-
-/*
-** Bits 28 thru 26 determine some attributes for the
-** region controlled by the PAR. (We only use non-cacheable)
-*/
-#define SC520_PAR_WRPROT (1<<26) /* write protected */
-#define SC520_PAR_NOCACHE (1<<27) /* non-cacheable */
-#define SC520_PAR_NOEXEC (1<<28) /* code execution denied */
-
-
-/*
-** Bit 25 determines the granularity: 4K or 64K
-*/
-#define SC520_PAR_PG_SIZ4 (0<<25)
-#define SC520_PAR_PG_SIZ64 (1<<25)
-
-/*
-** Build a value to be written into a PAR register.
-** We only need ROM entries, 64K page size:
-*/
-#define SC520_PAR_ENTRY(trgdev, address, size) \
- ((trgdev) | SC520_PAR_NOCACHE | SC520_PAR_PG_SIZ64 | \
- (address) >> 16 | (((size) >> 16) - 1) << 14)
-
-struct sc520_par_table
-{
- unsigned long trgdev;
- unsigned long new_par;
- unsigned long default_address;
-};
-
-static const struct sc520_par_table par_table[NUM_FLASH_BANKS] =
-{
- { /* Flash Bank #0: selected by ROMCS0 */
- SC520_PAR_ROMCS0,
- SC520_PAR_ENTRY(SC520_PAR_ROMCS0, WINDOW_ADDR_0, WINDOW_SIZE_0),
- WINDOW_ADDR_0_BIOS
- },
- { /* Flash Bank #1: selected by ROMCS1 */
- SC520_PAR_ROMCS1,
- SC520_PAR_ENTRY(SC520_PAR_ROMCS1, WINDOW_ADDR_1, WINDOW_SIZE_1),
- WINDOW_ADDR_1_BIOS
- },
- { /* DIL (BIOS) Flash: selected by BOOTCS */
- SC520_PAR_BOOTCS,
- SC520_PAR_ENTRY(SC520_PAR_BOOTCS, WINDOW_ADDR_2, WINDOW_SIZE_2),
- WINDOW_ADDR_2_BIOS
- }
-};
-
-
-static void sc520cdp_setup_par(void)
-{
- unsigned long __iomem *mmcr;
- unsigned long mmcr_val;
- int i, j;
-
- /* map in SC520's MMCR area */
- mmcr = ioremap(SC520_MMCR_BASE, SC520_MMCR_EXTENT);
- if(!mmcr) { /* ioremap failed: skip the PAR reprogramming */
- /* force physical address fields to BIOS defaults: */
- for(i = 0; i < NUM_FLASH_BANKS; i++)
- sc520cdp_map[i].phys = par_table[i].default_address;
- return;
- }
-
- /*
- ** Find the PARxx registers that are responsible for activating
- ** ROMCS0, ROMCS1 and BOOTCS. Reprogram each of these with a
- ** new value from the table.
- */
- for(i = 0; i < NUM_FLASH_BANKS; i++) { /* for each par_table entry */
- for(j = 0; j < NUM_SC520_PAR; j++) { /* for each PAR register */
- mmcr_val = readl(&mmcr[SC520_PAR(j)]);
- /* if target device field matches, reprogram the PAR */
- if((mmcr_val & SC520_PAR_TRGDEV) == par_table[i].trgdev)
- {
- writel(par_table[i].new_par, &mmcr[SC520_PAR(j)]);
- break;
- }
- }
- if(j == NUM_SC520_PAR)
- { /* no matching PAR found: try default BIOS address */
- printk(KERN_NOTICE "Could not find PAR responsible for %s\n",
- sc520cdp_map[i].name);
- printk(KERN_NOTICE "Trying default address 0x%lx\n",
- par_table[i].default_address);
- sc520cdp_map[i].phys = par_table[i].default_address;
- }
- }
- iounmap(mmcr);
-}
-#endif
-
-
-static int __init init_sc520cdp(void)
-{
- int i, j, devices_found = 0;
-
-#ifdef REPROGRAM_PAR
- /* reprogram PAR registers so flash appears at the desired addresses */
- sc520cdp_setup_par();
-#endif
-
- for (i = 0; i < NUM_FLASH_BANKS; i++) {
- printk(KERN_NOTICE "SC520 CDP flash device: 0x%Lx at 0x%Lx\n",
- (unsigned long long)sc520cdp_map[i].size,
- (unsigned long long)sc520cdp_map[i].phys);
-
- sc520cdp_map[i].virt = ioremap(sc520cdp_map[i].phys, sc520cdp_map[i].size);
-
- if (!sc520cdp_map[i].virt) {
- printk("Failed to ioremap\n");
- for (j = 0; j < i; j++) {
- if (mymtd[j]) {
- map_destroy(mymtd[j]);
- iounmap(sc520cdp_map[j].virt);
- }
- }
- return -EIO;
- }
-
- simple_map_init(&sc520cdp_map[i]);
-
- mymtd[i] = do_map_probe("cfi_probe", &sc520cdp_map[i]);
- if(!mymtd[i])
- mymtd[i] = do_map_probe("jedec_probe", &sc520cdp_map[i]);
- if(!mymtd[i])
- mymtd[i] = do_map_probe("map_rom", &sc520cdp_map[i]);
-
- if (mymtd[i]) {
- mymtd[i]->owner = THIS_MODULE;
- ++devices_found;
- }
- else {
- iounmap(sc520cdp_map[i].virt);
- }
- }
- if(devices_found >= 2) {
- /* Combine the two flash banks into a single MTD device & register it: */
- merged_mtd = mtd_concat_create(mymtd, 2, "SC520CDP Flash Banks #0 and #1");
- if(merged_mtd)
- mtd_device_register(merged_mtd, NULL, 0);
- }
- if(devices_found == 3) /* register the third (DIL-Flash) device */
- mtd_device_register(mymtd[2], NULL, 0);
- return(devices_found ? 0 : -ENXIO);
-}
-
-static void __exit cleanup_sc520cdp(void)
-{
- int i;
-
- if (merged_mtd) {
- mtd_device_unregister(merged_mtd);
- mtd_concat_destroy(merged_mtd);
- }
- if (mymtd[2])
- mtd_device_unregister(mymtd[2]);
-
- for (i = 0; i < NUM_FLASH_BANKS; i++) {
- if (mymtd[i])
- map_destroy(mymtd[i]);
- if (sc520cdp_map[i].virt) {
- iounmap(sc520cdp_map[i].virt);
- sc520cdp_map[i].virt = NULL;
- }
- }
-}
-
-module_init(init_sc520cdp);
-module_exit(cleanup_sc520cdp);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Sysgo Real-Time Solutions GmbH");
-MODULE_DESCRIPTION("MTD map driver for AMD SC520 Customer Development Platform");
diff --git a/drivers/mtd/maps/scb2_flash.c b/drivers/mtd/maps/scb2_flash.c
index 57303f904bc1..5c7e1dad101b 100644
--- a/drivers/mtd/maps/scb2_flash.c
+++ b/drivers/mtd/maps/scb2_flash.c
@@ -215,13 +215,8 @@ static void scb2_flash_remove(struct pci_dev *dev)
}
static struct pci_device_id scb2_flash_pci_ids[] = {
- {
- .vendor = PCI_VENDOR_ID_SERVERWORKS,
- .device = PCI_DEVICE_ID_SERVERWORKS_CSB5,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID
- },
- { 0, }
+ { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5) },
+ { }
};
static struct pci_driver scb2_flash_driver = {
diff --git a/drivers/mtd/maps/ts5500_flash.c b/drivers/mtd/maps/ts5500_flash.c
deleted file mode 100644
index 70d6e865f555..000000000000
--- a/drivers/mtd/maps/ts5500_flash.c
+++ /dev/null
@@ -1,108 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * ts5500_flash.c -- MTD map driver for Technology Systems TS-5500 board
- *
- * Copyright (C) 2004 Sean Young <sean@mess.org>
- *
- * Note:
- * - In order for detection to work, jumper 3 must be set.
- * - Drive A and B use the resident flash disk (RFD) flash translation layer.
- * - If you have created your own jffs file system and the bios overwrites
- * it during boot, try disabling Drive A: and B: in the boot order.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-#include <linux/types.h>
-
-
-#define WINDOW_ADDR 0x09400000
-#define WINDOW_SIZE 0x00200000
-
-static struct map_info ts5500_map = {
- .name = "TS-5500 Flash",
- .size = WINDOW_SIZE,
- .bankwidth = 1,
- .phys = WINDOW_ADDR
-};
-
-static const struct mtd_partition ts5500_partitions[] = {
- {
- .name = "Drive A",
- .offset = 0,
- .size = 0x0e0000
- },
- {
- .name = "BIOS",
- .offset = 0x0e0000,
- .size = 0x020000,
- },
- {
- .name = "Drive B",
- .offset = 0x100000,
- .size = 0x100000
- }
-};
-
-#define NUM_PARTITIONS ARRAY_SIZE(ts5500_partitions)
-
-static struct mtd_info *mymtd;
-
-static int __init init_ts5500_map(void)
-{
- int rc = 0;
-
- ts5500_map.virt = ioremap(ts5500_map.phys, ts5500_map.size);
-
- if (!ts5500_map.virt) {
- printk(KERN_ERR "Failed to ioremap\n");
- rc = -EIO;
- goto err2;
- }
-
- simple_map_init(&ts5500_map);
-
- mymtd = do_map_probe("jedec_probe", &ts5500_map);
- if (!mymtd)
- mymtd = do_map_probe("map_rom", &ts5500_map);
-
- if (!mymtd) {
- rc = -ENXIO;
- goto err1;
- }
-
- mymtd->owner = THIS_MODULE;
- mtd_device_register(mymtd, ts5500_partitions, NUM_PARTITIONS);
-
- return 0;
-
-err1:
- iounmap(ts5500_map.virt);
-err2:
- return rc;
-}
-
-static void __exit cleanup_ts5500_map(void)
-{
- if (mymtd) {
- mtd_device_unregister(mymtd);
- map_destroy(mymtd);
- }
-
- if (ts5500_map.virt) {
- iounmap(ts5500_map.virt);
- ts5500_map.virt = NULL;
- }
-}
-
-module_init(init_ts5500_map);
-module_exit(cleanup_ts5500_map);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Sean Young <sean@mess.org>");
-MODULE_DESCRIPTION("MTD map driver for Technology Systems TS-5500 board");
-
diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c
deleted file mode 100644
index de4c46318abb..000000000000
--- a/drivers/mtd/maps/uclinux.c
+++ /dev/null
@@ -1,118 +0,0 @@
-/****************************************************************************/
-
-/*
- * uclinux.c -- generic memory mapped MTD driver for uclinux
- *
- * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
- *
- * License: GPL
- */
-
-/****************************************************************************/
-
-#include <linux/moduleparam.h>
-#include <linux/types.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/major.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-#include <asm/io.h>
-#include <asm/sections.h>
-
-/****************************************************************************/
-
-#ifdef CONFIG_MTD_ROM
-#define MAP_NAME "rom"
-#else
-#define MAP_NAME "ram"
-#endif
-
-static struct map_info uclinux_ram_map = {
- .name = MAP_NAME,
- .size = 0,
-};
-
-static unsigned long physaddr = -1;
-module_param(physaddr, ulong, S_IRUGO);
-
-static struct mtd_info *uclinux_ram_mtdinfo;
-
-/****************************************************************************/
-
-static const struct mtd_partition uclinux_romfs[] = {
- { .name = "ROMfs" }
-};
-
-#define NUM_PARTITIONS ARRAY_SIZE(uclinux_romfs)
-
-/****************************************************************************/
-
-static int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, void **virt, resource_size_t *phys)
-{
- struct map_info *map = mtd->priv;
- *virt = map->virt + from;
- if (phys)
- *phys = map->phys + from;
- *retlen = len;
- return(0);
-}
-
-/****************************************************************************/
-
-static int __init uclinux_mtd_init(void)
-{
- struct mtd_info *mtd;
- struct map_info *mapp;
-
- mapp = &uclinux_ram_map;
-
- if (physaddr == -1)
- mapp->phys = (resource_size_t)__bss_stop;
- else
- mapp->phys = physaddr;
-
- if (!mapp->size)
- mapp->size = PAGE_ALIGN(ntohl(*((unsigned long *)(mapp->phys + 8))));
- mapp->bankwidth = 4;
-
- printk("uclinux[mtd]: probe address=0x%x size=0x%x\n",
- (int) mapp->phys, (int) mapp->size);
-
- /*
- * The filesystem is guaranteed to be in direct mapped memory. It is
- * directly following the kernels own bss region. Following the same
- * mechanism used by architectures setting up traditional initrds we
- * use phys_to_virt to get the virtual address of its start.
- */
- mapp->virt = phys_to_virt(mapp->phys);
-
- if (mapp->virt == 0) {
- printk("uclinux[mtd]: no virtual mapping?\n");
- return(-EIO);
- }
-
- simple_map_init(mapp);
-
- mtd = do_map_probe("map_" MAP_NAME, mapp);
- if (!mtd) {
- printk("uclinux[mtd]: failed to find a mapping?\n");
- return(-ENXIO);
- }
-
- mtd->owner = THIS_MODULE;
- mtd->_point = uclinux_point;
- mtd->priv = mapp;
-
- uclinux_ram_mtdinfo = mtd;
- mtd_device_register(mtd, uclinux_romfs, NUM_PARTITIONS);
-
- return(0);
-}
-device_initcall(uclinux_mtd_init);
-
-/****************************************************************************/
diff --git a/drivers/mtd/maps/vmu-flash.c b/drivers/mtd/maps/vmu-flash.c
index 75e06d249ce9..10244e6731d0 100644
--- a/drivers/mtd/maps/vmu-flash.c
+++ b/drivers/mtd/maps/vmu-flash.c
@@ -547,6 +547,7 @@ static void vmu_queryblocks(struct mapleq *mq)
mpart->partition = card->partition;
mtd_cur->priv = mpart;
mtd_cur->owner = THIS_MODULE;
+ mtd_cur->dev.parent = &mdev->dev;
pcache = kzalloc_obj(struct vmu_cache);
if (!pcache)
@@ -609,7 +610,7 @@ static int vmu_connect(struct maple_device *mdev)
basic_flash_data = be32_to_cpu(mdev->devinfo.function_data[c - 1]);
- card = kmalloc_obj(struct memcard);
+ card = kzalloc_obj(struct memcard);
if (!card) {
error = -ENOMEM;
goto fail_nomem;
@@ -627,13 +628,13 @@ static int vmu_connect(struct maple_device *mdev)
* Not sure there are actually any multi-partition devices in the
* real world, but the hardware supports them, so, so will we
*/
- card->parts = kmalloc_objs(struct vmupart, card->partitions);
+ card->parts = kzalloc_objs(struct vmupart, card->partitions);
if (!card->parts) {
error = -ENOMEM;
goto fail_partitions;
}
- card->mtd = kmalloc_objs(struct mtd_info, card->partitions);
+ card->mtd = kzalloc_objs(struct mtd_info, card->partitions);
if (!card->mtd) {
error = -ENOMEM;
goto fail_mtd_info;
diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c
index b88083751a0c..39df7ce8f55f 100644
--- a/drivers/mtd/mtdoops.c
+++ b/drivers/mtd/mtdoops.c
@@ -403,8 +403,7 @@ static struct mtd_notifier mtdoops_notifier = {
static int __init mtdoops_init(void)
{
struct mtdoops_context *cxt = &oops_cxt;
- int mtd_index;
- char *endp;
+ unsigned int mtd_index;
if (strlen(mtddev) == 0) {
pr_err("mtd device (mtddev=name/number) must be supplied\n");
@@ -421,9 +420,9 @@ static int __init mtdoops_init(void)
/* Setup the MTD device to use */
cxt->mtd_index = -1;
- mtd_index = simple_strtoul(mtddev, &endp, 0);
- if (*endp == '\0')
+ if (kstrtouint(mtddev, 0, &mtd_index) == 0) {
cxt->mtd_index = mtd_index;
+ }
cxt->oops_buf = vmalloc(record_size);
if (!cxt->oops_buf)
diff --git a/drivers/mtd/mtdsuper.c b/drivers/mtd/mtdsuper.c
index b7e3763c47f0..c709ff7aa6b5 100644
--- a/drivers/mtd/mtdsuper.c
+++ b/drivers/mtd/mtdsuper.c
@@ -132,12 +132,12 @@ int get_tree_mtd(struct fs_context *fc,
} else if (isdigit(fc->source[3])) {
/* mount by MTD device number name */
- char *endptr;
+ unsigned int mtdnr_val;
- mtdnr = simple_strtoul(fc->source + 3, &endptr, 0);
- if (!*endptr) {
+ if (kstrtouint(fc->source + 3, 0, &mtdnr_val) == 0) {
+ mtdnr = mtdnr_val;
/* It was a valid number */
- pr_debug("MTDSB: mtd%%d, mtdnr %d\n", mtdnr);
+ pr_debug("MTDSB: mtd%%d, mtdnr %u\n", mtdnr_val);
return mtd_get_sb_by_nr(fc, mtdnr, fill_super);
}
}
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index 7408f34f0c68..64b8b99a3a68 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -71,7 +71,7 @@ config MTD_NAND_AU1550
config MTD_NAND_NDFC
tristate "IBM/MCC 4xx NAND controller"
- depends on 4xx
+ depends on 44x || COMPILE_TEST
select MTD_NAND_ECC_SW_HAMMING
select MTD_NAND_ECC_SW_HAMMING_SMC
help
@@ -304,7 +304,7 @@ config MTD_NAND_HISI504
Enables support for NAND controller on Hisilicon SoC Hip04.
config MTD_NAND_QCOM
- tristate "QCOM NAND controller"
+ tristate "Qualcomm NAND controller"
depends on ARCH_QCOM || COMPILE_TEST
depends on HAS_IOMEM
help
diff --git a/drivers/mtd/nand/raw/cafe_nand.c b/drivers/mtd/nand/raw/cafe_nand.c
index c4018bc59670..f3117b031cd2 100644
--- a/drivers/mtd/nand/raw/cafe_nand.c
+++ b/drivers/mtd/nand/raw/cafe_nand.c
@@ -830,8 +830,7 @@ static void cafe_nand_remove(struct pci_dev *pdev)
}
static const struct pci_device_id cafe_nand_tbl[] = {
- { PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_88ALP01_NAND,
- PCI_ANY_ID, PCI_ANY_ID },
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_88ALP01_NAND) },
{ }
};
diff --git a/drivers/mtd/nand/raw/denali_pci.c b/drivers/mtd/nand/raw/denali_pci.c
index 97fa32d73441..f53df1b32dda 100644
--- a/drivers/mtd/nand/raw/denali_pci.c
+++ b/drivers/mtd/nand/raw/denali_pci.c
@@ -19,8 +19,8 @@
/* List of platforms this NAND controller has be integrated into */
static const struct pci_device_id denali_pci_ids[] = {
- { PCI_VDEVICE(INTEL, 0x0701), INTEL_CE4100 },
- { PCI_VDEVICE(INTEL, 0x0809), INTEL_MRST },
+ { PCI_VDEVICE(INTEL, 0x0701), .driver_data = INTEL_CE4100 },
+ { PCI_VDEVICE(INTEL, 0x0809), .driver_data = INTEL_MRST },
{ /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE(pci, denali_pci_ids);
diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index d6d3e17ab407..a5b278ab9384 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -175,7 +175,7 @@ void nand_select_target(struct nand_chip *chip, unsigned int cs)
* cs should always lie between 0 and nanddev_ntargets(), when that's
* not the case it's a bug and the caller should be fixed.
*/
- if (WARN_ON(cs > nanddev_ntargets(&chip->base)))
+ if (WARN_ON(cs >= nanddev_ntargets(&chip->base)))
return;
chip->cur_cs = cs;
@@ -1216,32 +1216,32 @@ static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page,
return nand_exec_op(chip, &op);
}
-static unsigned int rawnand_last_page_of_lun(unsigned int pages_per_lun, unsigned int lun)
+static unsigned int rawnand_last_page_of_block(unsigned int ppb, unsigned int block)
{
- /* lun is expected to be very small */
- return (lun * pages_per_lun) + pages_per_lun - 1;
+ /* block is expected to be very small */
+ return (block * ppb) + ppb - 1;
}
static void rawnand_cap_cont_reads(struct nand_chip *chip)
{
struct nand_memory_organization *memorg;
- unsigned int ppl, first_lun, last_lun;
+ unsigned int ppb, first_block, last_block;
memorg = nanddev_get_memorg(&chip->base);
- ppl = memorg->pages_per_eraseblock * memorg->eraseblocks_per_lun;
- first_lun = chip->cont_read.first_page / ppl;
- last_lun = chip->cont_read.last_page / ppl;
+ ppb = memorg->pages_per_eraseblock;
+ first_block = chip->cont_read.first_page / ppb;
+ last_block = chip->cont_read.last_page / ppb;
- /* Prevent sequential cache reads across LUN boundaries */
- if (first_lun != last_lun)
- chip->cont_read.pause_page = rawnand_last_page_of_lun(ppl, first_lun);
+ /* Prevent sequential cache reads across block boundaries */
+ if (first_block != last_block)
+ chip->cont_read.pause_page = rawnand_last_page_of_block(ppb, first_block);
else
chip->cont_read.pause_page = chip->cont_read.last_page;
if (chip->cont_read.first_page == chip->cont_read.pause_page) {
chip->cont_read.first_page++;
chip->cont_read.pause_page = min(chip->cont_read.last_page,
- rawnand_last_page_of_lun(ppl, first_lun + 1));
+ rawnand_last_page_of_block(ppb, first_block + 1));
}
if (chip->cont_read.first_page >= chip->cont_read.last_page)
diff --git a/drivers/mtd/nand/raw/ndfc.c b/drivers/mtd/nand/raw/ndfc.c
index 7ad8bc04be1a..a937ca3eeff5 100644
--- a/drivers/mtd/nand/raw/ndfc.c
+++ b/drivers/mtd/nand/raw/ndfc.c
@@ -44,13 +44,13 @@ static void ndfc_select_chip(struct nand_chip *nchip, int chip)
uint32_t ccr;
struct ndfc_controller *ndfc = nand_get_controller_data(nchip);
- ccr = in_be32(ndfc->ndfcbase + NDFC_CCR);
+ ccr = ioread32be(ndfc->ndfcbase + NDFC_CCR);
if (chip >= 0) {
ccr &= ~NDFC_CCR_BS_MASK;
ccr |= NDFC_CCR_BS(chip + ndfc->chip_select);
} else
ccr |= NDFC_CCR_RESET_CE;
- out_be32(ndfc->ndfcbase + NDFC_CCR, ccr);
+ iowrite32be(ccr, ndfc->ndfcbase + NDFC_CCR);
}
static void ndfc_hwcontrol(struct nand_chip *chip, int cmd, unsigned int ctrl)
@@ -70,7 +70,7 @@ static int ndfc_ready(struct nand_chip *chip)
{
struct ndfc_controller *ndfc = nand_get_controller_data(chip);
- return in_be32(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY;
+ return ioread32be(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY;
}
static void ndfc_enable_hwecc(struct nand_chip *chip, int mode)
@@ -78,9 +78,9 @@ static void ndfc_enable_hwecc(struct nand_chip *chip, int mode)
uint32_t ccr;
struct ndfc_controller *ndfc = nand_get_controller_data(chip);
- ccr = in_be32(ndfc->ndfcbase + NDFC_CCR);
+ ccr = ioread32be(ndfc->ndfcbase + NDFC_CCR);
ccr |= NDFC_CCR_RESET_ECC;
- out_be32(ndfc->ndfcbase + NDFC_CCR, ccr);
+ iowrite32be(ccr, ndfc->ndfcbase + NDFC_CCR);
wmb();
}
@@ -92,7 +92,7 @@ static int ndfc_calculate_ecc(struct nand_chip *chip,
uint8_t *p = (uint8_t *)&ecc;
wmb();
- ecc = in_be32(ndfc->ndfcbase + NDFC_ECC);
+ ecc = ioread32be(ndfc->ndfcbase + NDFC_ECC);
/* The NDFC uses Smart Media (SMC) bytes order */
ecc_code[0] = p[1];
ecc_code[1] = p[2];
@@ -114,7 +114,7 @@ static void ndfc_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
uint32_t *p = (uint32_t *) buf;
for(;len > 0; len -= 4)
- *p++ = in_be32(ndfc->ndfcbase + NDFC_DATA);
+ *p++ = ioread32be(ndfc->ndfcbase + NDFC_DATA);
}
static void ndfc_write_buf(struct nand_chip *chip, const uint8_t *buf, int len)
@@ -123,7 +123,7 @@ static void ndfc_write_buf(struct nand_chip *chip, const uint8_t *buf, int len)
uint32_t *p = (uint32_t *) buf;
for(;len > 0; len -= 4)
- out_be32(ndfc->ndfcbase + NDFC_DATA, *p++);
+ iowrite32be(*p++, ndfc->ndfcbase + NDFC_DATA);
}
/*
@@ -223,13 +223,13 @@ static int ndfc_probe(struct platform_device *ofdev)
if (reg)
ccr |= be32_to_cpup(reg);
- out_be32(ndfc->ndfcbase + NDFC_CCR, ccr);
+ iowrite32be(ccr, ndfc->ndfcbase + NDFC_CCR);
/* Set the bank settings if given */
reg = of_get_property(ofdev->dev.of_node, "bank-settings", NULL);
if (reg) {
int offset = NDFC_BCFG0 + (ndfc->chip_select << 2);
- out_be32(ndfc->ndfcbase + offset, be32_to_cpup(reg));
+ iowrite32be(be32_to_cpup(reg), ndfc->ndfcbase + offset);
}
err = ndfc_chip_init(ndfc, ofdev->dev.of_node);
diff --git a/drivers/mtd/nand/raw/pl35x-nand-controller.c b/drivers/mtd/nand/raw/pl35x-nand-controller.c
index f2c65eb7a8d9..06f8f1e14b9c 100644
--- a/drivers/mtd/nand/raw/pl35x-nand-controller.c
+++ b/drivers/mtd/nand/raw/pl35x-nand-controller.c
@@ -1155,7 +1155,7 @@ static int pl35x_nand_probe(struct platform_device *pdev)
nfc->controller.ops = &pl35x_nandc_ops;
INIT_LIST_HEAD(&nfc->chips);
- nfc->conf_regs = devm_ioremap_resource(&smc_amba->dev, &smc_amba->res);
+ nfc->conf_regs = devm_ioremap_resource(nfc->dev, &smc_amba->res);
if (IS_ERR(nfc->conf_regs))
return PTR_ERR(nfc->conf_regs);
diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c
index b7e79b76654d..4b80ce084d9a 100644
--- a/drivers/mtd/nand/raw/qcom_nandc.c
+++ b/drivers/mtd/nand/raw/qcom_nandc.c
@@ -128,8 +128,8 @@ static struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip)
static struct qcom_nand_controller *
get_qcom_nand_controller(struct nand_chip *chip)
{
- return (struct qcom_nand_controller *)
- ((u8 *)chip->controller - sizeof(struct qcom_nand_controller));
+ return container_of(chip->controller, struct qcom_nand_controller,
+ controller);
}
static u32 nandc_read(struct qcom_nand_controller *nandc, int offset)
@@ -2034,8 +2034,8 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
{
u32 nand_ctrl;
- nand_controller_init(nandc->controller);
- nandc->controller->ops = &qcom_nandc_ops;
+ nand_controller_init(&nandc->controller);
+ nandc->controller.ops = &qcom_nandc_ops;
/* kill onenand */
if (!nandc->props->nandc_part_of_qpic)
@@ -2175,7 +2175,7 @@ static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc,
chip->legacy.block_bad = qcom_nandc_block_bad;
chip->legacy.block_markbad = qcom_nandc_block_markbad;
- chip->controller = nandc->controller;
+ chip->controller = &nandc->controller;
chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USES_DMA |
NAND_SKIP_BBTSCAN;
@@ -2256,21 +2256,17 @@ static int qcom_nandc_parse_dt(struct platform_device *pdev)
static int qcom_nandc_probe(struct platform_device *pdev)
{
struct qcom_nand_controller *nandc;
- struct nand_controller *controller;
const void *dev_data;
struct device *dev = &pdev->dev;
struct resource *res;
int ret;
- nandc = devm_kzalloc(&pdev->dev, sizeof(*nandc) + sizeof(*controller),
- GFP_KERNEL);
+ nandc = devm_kzalloc(&pdev->dev, sizeof(*nandc), GFP_KERNEL);
if (!nandc)
return -ENOMEM;
- controller = (struct nand_controller *)&nandc[1];
platform_set_drvdata(pdev, nandc);
nandc->dev = dev;
- nandc->controller = controller;
dev_data = of_device_get_match_data(dev);
if (!dev_data) {
diff --git a/drivers/mtd/nand/raw/r852.c b/drivers/mtd/nand/raw/r852.c
index 8a5572b30893..92c47d351c27 100644
--- a/drivers/mtd/nand/raw/r852.c
+++ b/drivers/mtd/nand/raw/r852.c
@@ -1070,8 +1070,8 @@ static int r852_resume(struct device *device)
static const struct pci_device_id r852_pci_id_tbl[] = {
- { PCI_VDEVICE(RICOH, 0x0852), },
- { },
+ { PCI_VDEVICE(RICOH, 0x0852) },
+ { }
};
MODULE_DEVICE_TABLE(pci, r852_pci_id_tbl);
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 786e3fb4874d..f86786344d52 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -503,6 +503,11 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand,
rdesc = spinand->dirmaps[req->pos.plane].rdesc;
+ if (spinand->op_templates->cont_read_cache && req->continuous)
+ rdesc->info.op_tmpl = &rdesc->info.secondary_op_tmpl;
+ 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)
rdesc->info.op_tmpl->data.ecc = true;
@@ -960,13 +965,10 @@ static void spinand_cont_read_init(struct spinand_device *spinand)
{
struct nand_device *nand = spinand_to_nand(spinand);
enum nand_ecc_engine_type engine_type = nand->ecc.ctx.conf.engine_type;
+ struct spi_controller *ctlr = spinand->spimem->spi->controller;
/* OOBs cannot be retrieved so external/on-host ECC engine won't work */
- if (spinand->set_cont_read &&
- (engine_type == NAND_ECC_ENGINE_TYPE_ON_DIE ||
- engine_type == NAND_ECC_ENGINE_TYPE_NONE)) {
- spinand->cont_read_possible = true;
-
+ if (spinand->set_cont_read) {
/*
* Ensure continuous read is disabled on probe.
* Some devices retain this state across soft reset,
@@ -974,6 +976,11 @@ static void spinand_cont_read_init(struct spinand_device *spinand)
* in false positive returns from spinand_isbad().
*/
spinand_cont_read_enable(spinand, false);
+
+ if ((engine_type == NAND_ECC_ENGINE_TYPE_ON_DIE ||
+ engine_type == NAND_ECC_ENGINE_TYPE_NONE) &&
+ !spi_mem_controller_is_capable(ctlr, no_cs_assertion))
+ spinand->cont_read_possible = true;
}
}
@@ -1235,6 +1242,7 @@ static struct spi_mem_dirmap_desc *spinand_create_rdesc(
* its spi controller, use regular reading
*/
spinand->cont_read_possible = false;
+ memset(&info->secondary_op_tmpl, 0, sizeof(info->secondary_op_tmpl));
info->length = nanddev_page_size(nand) +
nanddev_per_page_oobsize(nand);
@@ -1251,11 +1259,24 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
struct nand_device *nand = spinand_to_nand(spinand);
struct spi_mem_dirmap_info info = { 0 };
struct spi_mem_dirmap_desc *desc;
- bool enable_ecc = false;
+ bool enable_ecc = false, secondary_op = false;
if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED)
enable_ecc = true;
+ if (spinand->cont_read_possible && spinand->op_templates->cont_read_cache)
+ secondary_op = true;
+
+ /*
+ * Continuous read implies that only the main data is retrieved, backed
+ * by an on-die ECC engine. It is not possible to use a pipelind ECC
+ * engine with continuous read.
+ */
+ if (enable_ecc && secondary_op) {
+ secondary_op = false;
+ spinand->cont_read_possible = false;
+ }
+
/* The plane number is passed in MSB just above the column address */
info.offset = plane << fls(nand->memorg.pagesize);
@@ -1273,6 +1294,10 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
/* Read descriptor */
info.primary_op_tmpl = *spinand->op_templates->read_cache;
info.primary_op_tmpl.data.ecc = enable_ecc;
+ if (secondary_op) {
+ info.secondary_op_tmpl = *spinand->op_templates->cont_read_cache;
+ info.secondary_op_tmpl.data.ecc = enable_ecc;
+ }
desc = spinand_create_rdesc(spinand, &info);
if (IS_ERR(desc))
return PTR_ERR(desc);
@@ -1303,6 +1328,22 @@ static int spinand_create_dirmaps(struct spinand_device *spinand)
return 0;
}
+static int spinand_randomizer_init(struct spinand_device *spinand)
+{
+ struct device_node *np = spinand->spimem->spi->dev.of_node;
+ u32 rand_val;
+ int ret;
+
+ if (!spinand->set_randomizer)
+ return 0;
+
+ ret = of_property_read_u32(np, "nand-randomizer", &rand_val);
+ if (ret)
+ return 0;
+
+ return spinand->set_randomizer(spinand, rand_val);
+}
+
static const struct nand_ops spinand_ops = {
.erase = spinand_erase,
.markbad = spinand_markbad,
@@ -1594,6 +1635,7 @@ int spinand_match_and_init(struct spinand_device *spinand,
spinand->user_otp = &table[i].user_otp;
spinand->read_retries = table[i].read_retries;
spinand->set_read_retry = table[i].set_read_retry;
+ spinand->set_randomizer = table[i].set_randomizer;
/* I/O variants selection with single-spi SDR commands */
@@ -1622,6 +1664,33 @@ int spinand_match_and_init(struct spinand_device *spinand,
if (ret)
return ret;
+ if (info->op_variants.cont_read_cache) {
+ op = spinand_select_op_variant(spinand, SSDR,
+ info->op_variants.cont_read_cache);
+ if (op) {
+ const struct spi_mem_op *read_op;
+
+ read_op = spinand->ssdr_op_templates.read_cache;
+
+ /*
+ * Sometimes the fastest continuous read variant may not
+ * be supported. In this case, prefer to use the fastest
+ * read from cache variant and disable continuous reads.
+ */
+ if (read_op->cmd.buswidth > op->cmd.buswidth ||
+ (read_op->cmd.dtr && !op->cmd.dtr) ||
+ read_op->addr.buswidth > op->addr.buswidth ||
+ (read_op->addr.dtr && !op->addr.dtr) ||
+ read_op->data.buswidth > op->data.buswidth ||
+ (read_op->data.dtr && !op->data.dtr))
+ spinand->cont_read_possible = false;
+ else
+ spinand->ssdr_op_templates.cont_read_cache = op;
+ } else {
+ spinand->cont_read_possible = false;
+ }
+ }
+
/* I/O variants selection with octo-spi DDR commands (optional) */
ret = spinand_init_odtr_instruction_set(spinand);
@@ -1644,6 +1713,15 @@ int spinand_match_and_init(struct spinand_device *spinand,
info->op_variants.update_cache);
spinand->odtr_op_templates.update_cache = op;
+ if (info->op_variants.cont_read_cache) {
+ op = spinand_select_op_variant(spinand, ODTR,
+ info->op_variants.cont_read_cache);
+ if (op)
+ spinand->odtr_op_templates.cont_read_cache = op;
+ else
+ spinand->cont_read_possible = false;
+ }
+
return 0;
}
@@ -1881,6 +1959,9 @@ static int spinand_init(struct spinand_device *spinand)
* ECC initialization must have happened previously.
*/
spinand_cont_read_init(spinand);
+ ret = spinand_randomizer_init(spinand);
+ if (ret)
+ goto err_cleanup_nanddev;
mtd->_read_oob = spinand_mtd_read;
mtd->_write_oob = spinand_mtd_write;
diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c
index 67cafa1bb8ef..7dfcc34e9b72 100644
--- a/drivers/mtd/nand/spi/macronix.c
+++ b/drivers/mtd/nand/spi/macronix.c
@@ -14,6 +14,8 @@
#define MACRONIX_ECCSR_BF_LAST_PAGE(eccsr) FIELD_GET(GENMASK(3, 0), eccsr)
#define MACRONIX_ECCSR_BF_ACCUMULATED_PAGES(eccsr) FIELD_GET(GENMASK(7, 4), eccsr)
#define MACRONIX_CFG_CONT_READ BIT(2)
+#define MACRONIX_CFG_RANDOMIZER_EN BIT(1)
+#define MACRONIX_FEATURE_ADDR_RANDOMIZER 0x10
#define MACRONIX_FEATURE_ADDR_READ_RETRY 0x70
#define MACRONIX_NUM_READ_RETRY_MODES 5
@@ -170,6 +172,12 @@ static int macronix_set_read_retry(struct spinand_device *spinand,
return spi_mem_exec_op(spinand->spimem, &op);
}
+static int macronix_set_randomizer(struct spinand_device *spinand, bool enable)
+{
+ return spinand_write_reg_op(spinand, MACRONIX_FEATURE_ADDR_RANDOMIZER,
+ enable ? MACRONIX_CFG_RANDOMIZER_EN : 0);
+}
+
static const struct spinand_info macronix_spinand_table[] = {
SPINAND_INFO("MX35LF1GE4AB",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x12),
@@ -231,7 +239,8 @@ static const struct spinand_info macronix_spinand_table[] = {
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL),
SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
- macronix_set_read_retry)),
+ macronix_set_read_retry),
+ SPINAND_RANDOMIZER(macronix_set_randomizer)),
SPINAND_INFO("MX35LF2G24AD",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24, 0x03),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1),
@@ -243,7 +252,8 @@ static const struct spinand_info macronix_spinand_table[] = {
SPINAND_HAS_PROG_PLANE_SELECT_BIT,
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL),
SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
- macronix_set_read_retry)),
+ macronix_set_read_retry),
+ SPINAND_RANDOMIZER(macronix_set_randomizer)),
SPINAND_INFO("MX35LF2G24AD-Z4I8",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x64, 0x03),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
@@ -254,7 +264,8 @@ static const struct spinand_info macronix_spinand_table[] = {
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL),
SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
- macronix_set_read_retry)),
+ macronix_set_read_retry),
+ SPINAND_RANDOMIZER(macronix_set_randomizer)),
SPINAND_INFO("MX35LF4G24AD",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35, 0x03),
NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 2, 1, 1),
@@ -266,7 +277,8 @@ static const struct spinand_info macronix_spinand_table[] = {
SPINAND_HAS_PROG_PLANE_SELECT_BIT,
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL),
SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
- macronix_set_read_retry)),
+ macronix_set_read_retry),
+ SPINAND_RANDOMIZER(macronix_set_randomizer)),
SPINAND_INFO("MX35LF4G24AD-Z4I8",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x75, 0x03),
NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
@@ -277,7 +289,8 @@ static const struct spinand_info macronix_spinand_table[] = {
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL),
SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
- macronix_set_read_retry)),
+ macronix_set_read_retry),
+ SPINAND_RANDOMIZER(macronix_set_randomizer)),
SPINAND_INFO("MX31LF1GE4BC",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x1e),
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
@@ -327,7 +340,8 @@ static const struct spinand_info macronix_spinand_table[] = {
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
- macronix_set_read_retry)),
+ macronix_set_read_retry),
+ SPINAND_RANDOMIZER(macronix_set_randomizer)),
SPINAND_INFO("MX35UF4G24AD-Z4I8",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xf5, 0x03),
NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
@@ -340,7 +354,8 @@ static const struct spinand_info macronix_spinand_table[] = {
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
- macronix_set_read_retry)),
+ macronix_set_read_retry),
+ SPINAND_RANDOMIZER(macronix_set_randomizer)),
SPINAND_INFO("MX35UF4GE4AD",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb7, 0x03),
NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
@@ -381,7 +396,8 @@ static const struct spinand_info macronix_spinand_table[] = {
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
- macronix_set_read_retry)),
+ macronix_set_read_retry),
+ SPINAND_RANDOMIZER(macronix_set_randomizer)),
SPINAND_INFO("MX35UF2G24AD-Z4I8",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xe4, 0x03),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
@@ -394,7 +410,8 @@ static const struct spinand_info macronix_spinand_table[] = {
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
- macronix_set_read_retry)),
+ macronix_set_read_retry),
+ SPINAND_RANDOMIZER(macronix_set_randomizer)),
SPINAND_INFO("MX35UF2GE4AD",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa6, 0x03),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
@@ -444,7 +461,8 @@ static const struct spinand_info macronix_spinand_table[] = {
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
- macronix_set_read_retry)),
+ macronix_set_read_retry),
+ SPINAND_RANDOMIZER(macronix_set_randomizer)),
SPINAND_INFO("MX35UF1GE4AD",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x96, 0x03),
NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index 7cc0f0091430..9b78c1e6cbc9 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -15,9 +15,11 @@
#define SPINAND_MFR_WINBOND 0xEF
+#define WINBOND_CFG_HFREQ BIT(0)
#define WINBOND_CFG_BUF_READ BIT(3)
#define W25N04KV_STATUS_ECC_5_8_BITFLIPS (3 << 4)
+#define W25W35NXXJW_STATUS_ECC_MULT_UNCOR (3 << 4)
#define W25N0XJW_SR4 0xD0
#define W25N0XJW_SR4_HS BIT(2)
@@ -30,6 +32,105 @@
#define W35N01JW_VCR_DUMMY_CLOCK_REG 0x01
/*
+ * Winbond chips ignore the address bytes during continuous reads, and
+ * because the dummy cycles are enough they indicate dropping the
+ * address cycles from the continuous read from cache variants. This is
+ * very poorly supported by SPI controller drivers which are "wired" to
+ * always at least provide the column. Keep using address cycles, but
+ * reduce the number of dummy cycles accordingly.
+ */
+#define WINBOND_CONT_READ_FROM_CACHE_FAST_1S_1S_1S_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x0b, 1), \
+ SPI_MEM_OP_ADDR(1, 0, 1), \
+ SPI_MEM_OP_DUMMY(ndummy - 1, 1), \
+ SPI_MEM_OP_DATA_IN(len, buf, 1), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_1D_1D_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x0d, 1), \
+ SPI_MEM_DTR_OP_ADDR(2, 0, 1), \
+ SPI_MEM_DTR_OP_DUMMY(ndummy, 1), \
+ SPI_MEM_DTR_OP_DATA_IN(len, buf, 1), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_1S_2S_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x3b, 1), \
+ SPI_MEM_OP_ADDR(1, 0, 1), \
+ SPI_MEM_OP_DUMMY(ndummy - 1, 1), \
+ SPI_MEM_OP_DATA_IN(len, buf, 2), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_2S_2S_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0xbb, 1), \
+ SPI_MEM_OP_ADDR(1, 0, 2), \
+ SPI_MEM_OP_DUMMY(ndummy - 1, 2), \
+ SPI_MEM_OP_DATA_IN(len, buf, 2), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_2D_2D_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0xbd, 1), \
+ SPI_MEM_DTR_OP_ADDR(1, 0, 2), \
+ SPI_MEM_DTR_OP_DUMMY(ndummy - 1, 2), \
+ SPI_MEM_DTR_OP_DATA_IN(len, buf, 2), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_1S_4S_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1), \
+ SPI_MEM_OP_ADDR(1, 0, 1), \
+ SPI_MEM_OP_DUMMY(ndummy - 1, 1), \
+ SPI_MEM_OP_DATA_IN(len, buf, 4), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_1D_4D_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x6d, 1), \
+ SPI_MEM_DTR_OP_ADDR(1, 0, 1), \
+ SPI_MEM_DTR_OP_DUMMY(ndummy - 1, 1), \
+ SPI_MEM_DTR_OP_DATA_IN(len, buf, 4), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_4S_4S_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0xeb, 1), \
+ SPI_MEM_OP_ADDR(1, 0, 4), \
+ SPI_MEM_OP_DUMMY(ndummy - 1, 4), \
+ SPI_MEM_OP_DATA_IN(len, buf, 4), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_4D_4D_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0xed, 1), \
+ SPI_MEM_DTR_OP_ADDR(1, 0, 4), \
+ SPI_MEM_DTR_OP_DUMMY(ndummy - 1, 4), \
+ SPI_MEM_DTR_OP_DATA_IN(len, buf, 4), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_1S_8S_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x8b, 1), \
+ SPI_MEM_OP_ADDR(1, 0, 1), \
+ SPI_MEM_OP_DUMMY(ndummy - 1, 1), \
+ SPI_MEM_OP_DATA_IN(len, buf, 8), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_1D_8D_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x9d, 1), \
+ SPI_MEM_DTR_OP_ADDR(1, 0, 1), \
+ SPI_MEM_DTR_OP_DUMMY(ndummy - 1, 1), \
+ SPI_MEM_DTR_OP_DATA_IN(len, buf, 8), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_8S_8S_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0xcb, 1), \
+ SPI_MEM_OP_ADDR(1, 0, 8), \
+ SPI_MEM_OP_DUMMY(ndummy - 1, 8), \
+ SPI_MEM_OP_DATA_IN(len, buf, 8), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_8D_8D_8D_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x9d, 8), \
+ SPI_MEM_DTR_OP_ADDR(2, 0, 8), \
+ SPI_MEM_DTR_OP_DUMMY(ndummy - 2, 8), \
+ SPI_MEM_DTR_OP_DATA_IN(len, buf, 8), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+/*
* "X2" in the core is equivalent to "dual output" in the datasheets,
* "X4" in the core is equivalent to "quad output" in the datasheets.
* Quad and octal capable chips feature an absolute maximum frequency of 166MHz.
@@ -49,6 +150,19 @@ static SPINAND_OP_VARIANTS(read_cache_octal_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_octal_variants,
+ WINBOND_CONT_READ_FROM_CACHE_8D_8D_8D_OP(24, NULL, 0, 120 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_8D_8D_8D_OP(16, NULL, 0, 86 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_1D_8D_OP(3, NULL, 0, 120 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_1D_8D_OP(2, NULL, 0, 105 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_8S_8S_OP(20, NULL, 0, 0),
+ WINBOND_CONT_READ_FROM_CACHE_1S_8S_8S_OP(16, NULL, 0, 162 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_8S_8S_OP(12, NULL, 0, 124 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_8S_8S_OP(8, NULL, 0, 86 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_1S_8S_OP(2, NULL, 0, 0),
+ WINBOND_CONT_READ_FROM_CACHE_1S_1S_8S_OP(1, NULL, 0, 133 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_FAST_1S_1S_1S_OP(1, NULL, 0, 0));
+
static SPINAND_OP_VARIANTS(write_cache_octal_variants,
SPINAND_PROG_LOAD_8D_8D_8D_OP(true, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_8S_8S_OP(true, 0, NULL, 0),
@@ -75,6 +189,20 @@ static SPINAND_OP_VARIANTS(read_cache_dual_quad_dtr_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, 54 * HZ_PER_MHZ));
+static SPINAND_OP_VARIANTS(cont_read_cache_dual_quad_dtr_variants,
+ WINBOND_CONT_READ_FROM_CACHE_1S_4D_4D_OP(11, NULL, 0, 80 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_1D_4D_OP(5, NULL, 0, 80 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_4S_4S_OP(7, NULL, 0, 0),
+ WINBOND_CONT_READ_FROM_CACHE_1S_4S_4S_OP(6, NULL, 0, 104 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_1S_4S_OP(4, NULL, 0, 0),
+ WINBOND_CONT_READ_FROM_CACHE_1S_2D_2D_OP(6, NULL, 0, 80 * HZ_PER_MHZ),
+ /* The 1S_1D_2D variant would require 4.5 dummy bytes, this is not possible */
+ WINBOND_CONT_READ_FROM_CACHE_1S_2S_2S_OP(5, NULL, 0, 0),
+ WINBOND_CONT_READ_FROM_CACHE_1S_2S_2S_OP(4, NULL, 0, 104 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_1S_2S_OP(4, NULL, 0, 0),
+ /* The 1S_1D_1D variant would require 4.5 dummy bytes, this is not possible */
+ WINBOND_CONT_READ_FROM_CACHE_FAST_1S_1S_1S_OP(4, NULL, 0, 0));
+
static SPINAND_OP_VARIANTS(read_cache_variants,
SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 2, NULL, 0, 0),
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0),
@@ -326,45 +454,84 @@ static int w25n02kv_ecc_get_status(struct spinand_device *spinand,
return -EINVAL;
}
+static int w25w35nxxjw_ecc_get_status(struct spinand_device *spinand, u8 status)
+{
+ switch (status & STATUS_ECC_MASK) {
+ case STATUS_ECC_NO_BITFLIPS:
+ return 0;
+
+ case STATUS_ECC_HAS_BITFLIPS:
+ return 1;
+
+ case STATUS_ECC_UNCOR_ERROR:
+ case W25W35NXXJW_STATUS_ECC_MULT_UNCOR:
+ return -EBADMSG;
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int w25n0xjw_set_sr4_hs(struct spinand_device *spinand, bool enable)
+{
+ int ret;
+ u8 sr4;
+
+ ret = spinand_read_reg_op(spinand, W25N0XJW_SR4, &sr4);
+ if (ret)
+ return ret;
+
+ if (enable)
+ sr4 |= W25N0XJW_SR4_HS;
+ else
+ sr4 &= ~W25N0XJW_SR4_HS;
+
+ return spinand_write_reg_op(spinand, W25N0XJW_SR4, sr4);
+}
+
+/*
+ * SDR dual and quad I/O operations over 104MHz require the HS bit to
+ * enable a few more dummy cycles.
+ */
+static bool w25n0xjw_op_needs_hs(const struct spi_mem_op *op)
+{
+ if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr)
+ return false;
+ else if (op->cmd.buswidth != 1 || op->addr.buswidth == 1)
+ return false;
+ else if (op->max_freq && op->max_freq <= 104 * HZ_PER_MHZ)
+ return false;
+
+ return true;
+}
+
static int w25n0xjw_hs_cfg(struct spinand_device *spinand,
enum spinand_bus_interface iface)
{
const struct spi_mem_op *op;
- bool hs;
- u8 sr4;
- int ret;
if (iface != SSDR)
return -EOPNOTSUPP;
/*
- * SDR dual and quad I/O operations over 104MHz require the HS bit to
- * enable a few more dummy cycles.
+ * At this stage, we do not yet know the continuous read template, nor
+ * if there is going to be one. Let's assume the continuous read
+ * template will be selected with the same heuristics as the buffered
+ * read variant, as there cannot be a HS configuration mismatch between
+ * them.
*/
op = spinand->op_templates->read_cache;
- if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr)
- hs = false;
- else if (op->cmd.buswidth != 1 || op->addr.buswidth == 1)
- hs = false;
- else if (op->max_freq && op->max_freq <= 104 * HZ_PER_MHZ)
- hs = false;
- else
- hs = true;
-
- ret = spinand_read_reg_op(spinand, W25N0XJW_SR4, &sr4);
- if (ret)
- return ret;
- if (hs)
- sr4 |= W25N0XJW_SR4_HS;
- else
- sr4 &= ~W25N0XJW_SR4_HS;
+ return w25n0xjw_set_sr4_hs(spinand, w25n0xjw_op_needs_hs(op));
+}
- ret = spinand_write_reg_op(spinand, W25N0XJW_SR4, sr4);
- if (ret)
- return ret;
+static int w25n0xjw_set_cont_read(struct spinand_device *spinand, bool enable)
+{
+ u8 mask = enable ? 0 : WINBOND_CFG_BUF_READ;
- return 0;
+ return spinand_upd_cfg(spinand, WINBOND_CFG_BUF_READ, mask);
}
static int w35n0xjw_write_vcr(struct spinand_device *spinand, u8 reg, u8 val)
@@ -451,6 +618,18 @@ static int w35n0xjw_vcr_cfg(struct spinand_device *spinand,
return 0;
}
+static int w35n0xjw_set_cont_read(struct spinand_device *spinand, bool enable)
+{
+ const struct spi_mem_op *cont_op = spinand->op_templates->cont_read_cache;
+ u8 mask = enable ? 0 : WINBOND_CFG_BUF_READ;
+
+ if (cont_op && enable && spinand_op_is_odtr(cont_op) &&
+ cont_op->max_freq >= 90 * HZ_PER_MHZ)
+ mask |= WINBOND_CFG_HFREQ;
+
+ return spinand_upd_cfg(spinand, WINBOND_CFG_BUF_READ | WINBOND_CFG_HFREQ, mask);
+}
+
static const struct spinand_info winbond_spinand_table[] = {
/* 512M-bit densities */
SPINAND_INFO("W25N512GW", /* 1.8V */
@@ -485,12 +664,14 @@ static const struct spinand_info winbond_spinand_table[] = {
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xbc, 0x21),
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(1, 512),
- SPINAND_INFO_OP_VARIANTS(&read_cache_dual_quad_dtr_variants,
- &write_cache_variants,
- &update_cache_variants),
+ SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_dual_quad_dtr_variants,
+ &write_cache_variants,
+ &update_cache_variants,
+ &cont_read_cache_dual_quad_dtr_variants),
SPINAND_HAS_QE_BIT,
- SPINAND_ECCINFO(&w25n01jw_ooblayout, NULL),
- SPINAND_CONFIGURE_CHIP(w25n0xjw_hs_cfg)),
+ SPINAND_ECCINFO(&w25n01jw_ooblayout, w25w35nxxjw_ecc_get_status),
+ SPINAND_CONFIGURE_CHIP(w25n0xjw_hs_cfg),
+ SPINAND_CONT_READ(w25n0xjw_set_cont_read)),
SPINAND_INFO("W25N01KV", /* 3.3V */
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xae, 0x21),
NAND_MEMORG(1, 2048, 96, 64, 1024, 20, 1, 1, 1),
@@ -504,35 +685,15 @@ static const struct spinand_info winbond_spinand_table[] = {
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdc, 0x21),
NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 1, 1),
NAND_ECCREQ(1, 512),
- SPINAND_INFO_OP_VARIANTS(&read_cache_octal_variants,
- &write_cache_octal_variants,
- &update_cache_octal_variants),
+ SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_octal_variants,
+ &write_cache_octal_variants,
+ &update_cache_octal_variants,
+ &cont_read_cache_octal_variants),
0,
SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops),
- SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL),
- SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)),
- SPINAND_INFO("W35N02JW", /* 1.8V */
- SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdf, 0x22),
- NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 2, 1),
- NAND_ECCREQ(1, 512),
- SPINAND_INFO_OP_VARIANTS(&read_cache_octal_variants,
- &write_cache_octal_variants,
- &update_cache_octal_variants),
- SPINAND_ODTR_PACKED_PAGE_READ,
- SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops),
- SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL),
- SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)),
- 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),
- NAND_ECCREQ(1, 512),
- SPINAND_INFO_OP_VARIANTS(&read_cache_octal_variants,
- &write_cache_octal_variants,
- &update_cache_octal_variants),
- SPINAND_ODTR_PACKED_PAGE_READ,
- SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops),
- SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL),
- SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)),
+ SPINAND_ECCINFO(&w35n01jw_ooblayout, w25w35nxxjw_ecc_get_status),
+ SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg),
+ SPINAND_CONT_READ(w35n0xjw_set_cont_read)),
/* 2G-bit densities */
SPINAND_INFO("W25M02GV", /* 2x1G-bit 3.3V */
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab, 0x21),
@@ -549,12 +710,14 @@ static const struct spinand_info winbond_spinand_table[] = {
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xbf, 0x22),
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 2, 1),
NAND_ECCREQ(1, 512),
- SPINAND_INFO_OP_VARIANTS(&read_cache_dual_quad_dtr_variants,
- &write_cache_variants,
- &update_cache_variants),
+ SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_dual_quad_dtr_variants,
+ &write_cache_variants,
+ &update_cache_variants,
+ &cont_read_cache_dual_quad_dtr_variants),
SPINAND_HAS_QE_BIT,
- SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
- SPINAND_CONFIGURE_CHIP(w25n0xjw_hs_cfg)),
+ SPINAND_ECCINFO(&w25m02gv_ooblayout, w25w35nxxjw_ecc_get_status),
+ SPINAND_CONFIGURE_CHIP(w25n0xjw_hs_cfg),
+ SPINAND_CONT_READ(w25n0xjw_set_cont_read)),
SPINAND_INFO("W25N02KV", /* 3.3V */
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x22),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
@@ -573,6 +736,19 @@ static const struct spinand_info winbond_spinand_table[] = {
&update_cache_variants),
0,
SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
+ SPINAND_INFO("W35N02JW", /* 1.8V */
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdf, 0x22),
+ NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 2, 1),
+ NAND_ECCREQ(1, 512),
+ SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_octal_variants,
+ &write_cache_octal_variants,
+ &update_cache_octal_variants,
+ &cont_read_cache_octal_variants),
+ SPINAND_ODTR_PACKED_PAGE_READ,
+ SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops),
+ SPINAND_ECCINFO(&w35n01jw_ooblayout, w25w35nxxjw_ecc_get_status),
+ SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg),
+ SPINAND_CONT_READ(w35n0xjw_set_cont_read)),
/* 4G-bit densities */
SPINAND_INFO("W25N04KV", /* 3.3V */
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x23),
@@ -592,6 +768,19 @@ static const struct spinand_info winbond_spinand_table[] = {
&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),
+ NAND_ECCREQ(1, 512),
+ SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_octal_variants,
+ &write_cache_octal_variants,
+ &update_cache_octal_variants,
+ &cont_read_cache_octal_variants),
+ SPINAND_ODTR_PACKED_PAGE_READ,
+ SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops),
+ SPINAND_ECCINFO(&w35n01jw_ooblayout, w25w35nxxjw_ecc_get_status),
+ SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg),
+ SPINAND_CONT_READ(w35n0xjw_set_cont_read)),
};
static int winbond_spinand_init(struct spinand_device *spinand)
diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c
index c8032755f9a4..fb0f97fb94f0 100644
--- a/drivers/mtd/sm_ftl.c
+++ b/drivers/mtd/sm_ftl.c
@@ -1133,7 +1133,7 @@ static void sm_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
struct sm_ftl *ftl;
/* Allocate & initialize our private structure */
- ftl = kzalloc_obj(struct sm_ftl);
+ ftl = kzalloc_flex(*ftl, cis_buffer, SM_SECTOR_SIZE);
if (!ftl)
goto error1;
@@ -1145,25 +1145,20 @@ static void sm_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
/* Read media information */
if (sm_get_media_info(ftl, mtd)) {
dbg("found unsupported mtd device, aborting");
- goto error2;
+ goto error1;
}
- /* Allocate temporary CIS buffer for read retry support */
- ftl->cis_buffer = kzalloc(SM_SECTOR_SIZE, GFP_KERNEL);
- if (!ftl->cis_buffer)
- goto error2;
-
/* Allocate zone array, it will be initialized on demand */
ftl->zones = kzalloc_objs(struct ftl_zone, ftl->zone_count);
if (!ftl->zones)
- goto error3;
+ goto error2;
/* Allocate the cache*/
ftl->cache_data = kzalloc(ftl->block_size, GFP_KERNEL);
if (!ftl->cache_data)
- goto error4;
+ goto error3;
sm_cache_init(ftl);
@@ -1171,7 +1166,7 @@ static void sm_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
/* Allocate upper layer structure and initialize it */
trans = kzalloc_obj(struct mtd_blktrans_dev);
if (!trans)
- goto error5;
+ goto error4;
ftl->trans = trans;
trans->priv = ftl;
@@ -1184,12 +1179,12 @@ static void sm_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
if (sm_find_cis(ftl)) {
dbg("CIS not found on mtd device, aborting");
- goto error6;
+ goto error5;
}
ftl->disk_attributes = sm_create_sysfs_attributes(ftl);
if (!ftl->disk_attributes)
- goto error6;
+ goto error5;
trans->disk_attributes = ftl->disk_attributes;
sm_printk("Found %d MiB xD/SmartMedia FTL on mtd%d",
@@ -1206,17 +1201,15 @@ static void sm_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
/* Register device*/
if (add_mtd_blktrans_dev(trans)) {
dbg("error in mtdblktrans layer");
- goto error6;
+ goto error5;
}
return;
-error6:
- kfree(trans);
error5:
- kfree(ftl->cache_data);
+ kfree(trans);
error4:
- kfree(ftl->zones);
+ kfree(ftl->cache_data);
error3:
- kfree(ftl->cis_buffer);
+ kfree(ftl->zones);
error2:
kfree(ftl);
error1:
@@ -1242,7 +1235,6 @@ static void sm_remove_dev(struct mtd_blktrans_dev *dev)
}
sm_delete_sysfs_attributes(ftl);
- kfree(ftl->cis_buffer);
kfree(ftl->zones);
kfree(ftl->cache_data);
kfree(ftl);
diff --git a/drivers/mtd/sm_ftl.h b/drivers/mtd/sm_ftl.h
index 6aed8b60de16..1a1a54ce836a 100644
--- a/drivers/mtd/sm_ftl.h
+++ b/drivers/mtd/sm_ftl.h
@@ -39,7 +39,6 @@ struct sm_ftl {
int cis_block; /* CIS block location */
int cis_boffset; /* CIS offset in the block */
int cis_page_offset; /* CIS offset in the page */
- void *cis_buffer; /* tmp buffer for cis reads */
/* Cache */
int cache_block; /* block number of cached block */
@@ -56,6 +55,7 @@ struct sm_ftl {
int cylinders;
struct attribute_group *disk_attributes;
+ u8 cis_buffer[]; /* tmp buffer for cis reads */
};
struct chs_entry {
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig
index 24cd25de2b8b..fd05a24d64a9 100644
--- a/drivers/mtd/spi-nor/Kconfig
+++ b/drivers/mtd/spi-nor/Kconfig
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
menuconfig MTD_SPI_NOR
tristate "SPI NOR device support"
- depends on MTD
depends on MTD && SPI_MASTER
select SPI_MEM
help
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index a7bc458edc5c..ccf4396cdcd0 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -869,8 +869,8 @@ static int spi_nor_write_16bit_sr_and_check(struct spi_nor *nor, u8 sr1)
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 &&
+ } 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
@@ -977,6 +977,54 @@ int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr)
}
/**
+ * 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.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_write_16bit_sr_cr_and_check(struct spi_nor *nor, const u8 *regs)
+{
+ 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);
+ 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;
+
+ ret = spi_nor_read_cr(nor, &nor->bouncebuf[1]);
+ if (ret)
+ return ret;
+
+ if (written_regs[1] != nor->bouncebuf[1]) {
+ dev_dbg(nor->dev, "CR: read back test failed\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
* 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.
@@ -994,6 +1042,23 @@ int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 sr1)
}
/**
+ * 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.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int spi_nor_write_sr_cr_and_check(struct spi_nor *nor, const u8 *regs)
+{
+ if (nor->flags & SNOR_F_HAS_16BIT_SR)
+ return spi_nor_write_16bit_sr_cr_and_check(nor, regs);
+
+ return spi_nor_write_sr1_and_check(nor, regs[0]);
+}
+
+/**
* spi_nor_write_sr2() - Write the Status Register 2 using the
* SPINOR_OP_WRSR2 (3eh) command.
* @nor: pointer to 'struct spi_nor'.
@@ -2909,6 +2974,9 @@ static void spi_nor_init_flags(struct spi_nor *nor)
nor->flags |= SNOR_F_HAS_SR_BP3_BIT6;
}
+ if (flags & SPI_NOR_HAS_CMP)
+ nor->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;
@@ -3261,10 +3329,12 @@ static int spi_nor_init(struct spi_nor *nor)
* protection bits are volatile. The latter is indicated by
* SNOR_F_SWP_IS_VOLATILE.
*/
+ 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))
+ nor->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 &&
diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index e838c40a2589..ba2d1a862c9d 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -141,6 +141,7 @@ enum spi_nor_option_flags {
SNOR_F_ECC = BIT(15),
SNOR_F_NO_WP = BIT(16),
SNOR_F_SWAP16 = BIT(17),
+ SNOR_F_HAS_SR2_CMP_BIT6 = BIT(18),
};
struct spi_nor_read_command {
@@ -279,9 +280,17 @@ struct spi_nor_erase_map {
/**
* struct spi_nor_locking_ops - SPI NOR locking methods
- * @lock: lock a region of the SPI NOR.
- * @unlock: unlock a region of the SPI NOR.
- * @is_locked: check if a region of the SPI NOR is completely locked
+ * @lock: lock a region of the SPI NOR, never locks more than what is
+ * requested, ie. may lock less.
+ * @unlock: unlock a region of the SPI NOR, may unlock more than what is
+ * requested.
+ * @is_locked: check if a region of the SPI NOR is completely locked, returns
+ * false otherwise. This feedback may be misleading because users
+ * may get an "unlocked" status even though a subpart of the region
+ * is effectively locked.
+ *
+ * If in doubt during development, check-out the debugfs output which tries to
+ * be more user friendly.
*/
struct spi_nor_locking_ops {
int (*lock)(struct spi_nor *nor, loff_t ofs, u64 len);
@@ -483,6 +492,8 @@ struct spi_nor_id {
* SPI_NOR_NO_ERASE: no erase command needed.
* SPI_NOR_QUAD_PP: flash supports Quad Input Page Program.
* SPI_NOR_RWW: flash supports reads while write.
+ * SPI_NOR_HAS_CMP: flash SR2 has complement (CMP) protect bit. Must
+ * 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
@@ -531,6 +542,7 @@ struct flash_info {
#define SPI_NOR_NO_ERASE BIT(6)
#define SPI_NOR_QUAD_PP BIT(8)
#define SPI_NOR_RWW BIT(9)
+#define SPI_NOR_HAS_CMP BIT(10)
u8 no_sfdp_flags;
#define SPI_NOR_SKIP_SFDP BIT(0)
@@ -632,6 +644,7 @@ 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);
ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len,
u8 *buf);
@@ -672,7 +685,9 @@ int spi_nor_post_bfpt_fixups(struct spi_nor *nor,
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);
+void spi_nor_cache_sr_lock_bits(struct spi_nor *nor, u8 *sr);
void spi_nor_set_mtd_locking_ops(struct spi_nor *nor);
void spi_nor_set_mtd_otp_ops(struct spi_nor *nor);
@@ -705,6 +720,10 @@ static inline bool spi_nor_needs_sfdp(const struct spi_nor *nor)
return !nor->info->size;
}
+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);
+bool spi_nor_is_locked_sr(struct spi_nor *nor, loff_t ofs, u64 len, const u8 *sr);
+
#ifdef CONFIG_DEBUG_FS
void spi_nor_debugfs_register(struct spi_nor *nor);
void spi_nor_debugfs_shutdown(void);
diff --git a/drivers/mtd/spi-nor/debugfs.c b/drivers/mtd/spi-nor/debugfs.c
index 14ba1680c315..288e2866daed 100644
--- a/drivers/mtd/spi-nor/debugfs.c
+++ b/drivers/mtd/spi-nor/debugfs.c
@@ -2,6 +2,7 @@
#include <linux/array_size.h>
#include <linux/debugfs.h>
+#include <linux/math64.h>
#include <linux/mtd/spi-nor.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>
@@ -29,6 +30,8 @@ static const char *const snor_f_names[] = {
SNOR_F_NAME(RWW),
SNOR_F_NAME(ECC),
SNOR_F_NAME(NO_WP),
+ SNOR_F_NAME(SWAP16),
+ SNOR_F_NAME(HAS_SR2_CMP_BIT6),
};
#undef SNOR_F_NAME
@@ -77,11 +80,14 @@ static void spi_nor_print_flags(struct seq_file *s, unsigned long flags,
static int spi_nor_params_show(struct seq_file *s, void *data)
{
struct spi_nor *nor = s->private;
+ u64 min_prot_len = spi_nor_get_min_prot_length_sr(nor);
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);
@@ -140,12 +146,12 @@ 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", nor->params->die_erase_opcode, buf);
+ seq_printf(s, " %02x (%s)\n", params->die_erase_opcode, buf);
}
seq_puts(s, "\nsector map\n");
seq_puts(s, " region (in hex) | erase mask | overlaid\n");
- seq_puts(s, " ------------------+------------+----------\n");
+ seq_puts(s, " ------------------+------------+---------\n");
for (i = 0; i < erase_map->n_regions; i++) {
u64 start = region[i].offset;
u64 end = start + region[i].size - 1;
@@ -160,10 +166,69 @@ static int spi_nor_params_show(struct seq_file *s, void *data)
region[i].overlaid ? "yes" : "no");
}
+ if (!spi_nor_has_default_locking_ops(nor))
+ return 0;
+
+ seq_puts(s, "\nlocked sectors\n");
+ seq_puts(s, " region (in hex) | status | #sectors\n");
+ seq_puts(s, " ------------------+----------+---------\n");
+
+ spi_nor_get_locked_range_sr(nor, nor->dfs_sr_cache, &lock_start, &lock_length);
+ if (!lock_length || lock_length == params->size) {
+ seq_printf(s, " %08llx-%08llx | %s | %llu\n", 0ULL, params->size - 1,
+ lock_length ? " locked" : "unlocked",
+ div_u64(params->size, min_prot_len));
+ } else if (!lock_start) {
+ seq_printf(s, " %08llx-%08llx | %s | %llu\n", 0ULL, lock_length - 1,
+ " locked", div_u64(lock_length, min_prot_len));
+ seq_printf(s, " %08llx-%08llx | %s | %llu\n", lock_length, params->size - 1,
+ "unlocked", div_u64(params->size - lock_length, min_prot_len));
+ } else {
+ seq_printf(s, " %08llx-%08llx | %s | %llu\n", 0ULL, lock_start - 1,
+ "unlocked", div_u64(lock_start, min_prot_len));
+ seq_printf(s, " %08llx-%08llx | %s | %llu\n", lock_start, params->size - 1,
+ " locked", div_u64(lock_length, min_prot_len));
+ }
+
return 0;
}
DEFINE_SHOW_ATTRIBUTE(spi_nor_params);
+static int spi_nor_locked_sectors_map_show(struct seq_file *s, void *data)
+{
+ struct spi_nor *nor = s->private;
+ struct spi_nor_flash_parameter *params = nor->params;
+ u64 min_prot_len = spi_nor_get_min_prot_length_sr(nor);
+ unsigned int sector = 0;
+ u64 offset = 0;
+ bool locked;
+ int i;
+
+ seq_printf(s, "Locked sectors map (x: locked, .: unlocked, unit: %lldkiB)\n",
+ min_prot_len / 1024);
+ while (offset < params->size) {
+ seq_printf(s, " 0x%08llx (#%5d): ", offset, sector);
+ for (i = 0; i < 64 && offset < params->size; i++) {
+ locked = spi_nor_is_locked_sr(nor, offset, min_prot_len,
+ nor->dfs_sr_cache);
+ if (locked)
+ seq_puts(s, "x");
+ else
+ seq_puts(s, ".");
+
+ if (((i + 1) % 16) == 0)
+ seq_puts(s, " ");
+
+ offset += min_prot_len;
+ sector++;
+ }
+ seq_puts(s, "\n");
+ }
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(spi_nor_locked_sectors_map);
+
static void spi_nor_print_read_cmd(struct seq_file *s, u32 cap,
struct spi_nor_read_command *cmd)
{
@@ -249,6 +314,9 @@ void spi_nor_debugfs_register(struct spi_nor *nor)
debugfs_create_file("params", 0444, d, nor, &spi_nor_params_fops);
debugfs_create_file("capabilities", 0444, d, nor,
&spi_nor_capabilities_fops);
+ if (spi_nor_has_default_locking_ops(nor))
+ debugfs_create_file("locked-sectors-map", 0444, d, nor,
+ &spi_nor_locked_sectors_map_fops);
}
void spi_nor_debugfs_shutdown(void)
diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c
index 8498c7003d88..65227d989de1 100644
--- a/drivers/mtd/spi-nor/spansion.c
+++ b/drivers/mtd/spi-nor/spansion.c
@@ -674,7 +674,9 @@ static int s25hx_t_late_init(struct spi_nor *nor)
params->ready = cypress_nor_sr_ready_and_clear;
cypress_nor_ecc_init(nor);
- params->die_erase_opcode = SPINOR_OP_CYPRESS_DIE_ERASE;
+ if (params->n_dice > 1)
+ params->die_erase_opcode = SPINOR_OP_CYPRESS_DIE_ERASE;
+
return 0;
}
@@ -760,6 +762,9 @@ static int s28hx_t_late_init(struct spi_nor *nor)
params->ready = cypress_nor_sr_ready_and_clear;
cypress_nor_ecc_init(nor);
+ if (params->n_dice > 1)
+ params->die_erase_opcode = SPINOR_OP_CYPRESS_DIE_ERASE;
+
return 0;
}
diff --git a/drivers/mtd/spi-nor/swp.c b/drivers/mtd/spi-nor/swp.c
index e67a81dbb6bf..235070b215d1 100644
--- a/drivers/mtd/spi-nor/swp.c
+++ b/drivers/mtd/spi-nor/swp.c
@@ -34,7 +34,16 @@ static u8 spi_nor_get_sr_tb_mask(struct spi_nor *nor)
return 0;
}
-static u64 spi_nor_get_min_prot_length_sr(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)
+ return SR2_CMP_BIT6;
+ else
+ return 0;
+}
+
+u64 spi_nor_get_min_prot_length_sr(struct spi_nor *nor)
{
unsigned int bp_slots, bp_slots_needed;
/*
@@ -55,13 +64,16 @@ static u64 spi_nor_get_min_prot_length_sr(struct spi_nor *nor)
return sector_size;
}
-static void spi_nor_get_locked_range_sr(struct spi_nor *nor, u8 sr, loff_t *ofs,
- u64 *len)
+void spi_nor_get_locked_range_sr(struct spi_nor *nor, const u8 *sr, loff_t *ofs,
+ u64 *len)
{
u64 min_prot_len;
- u8 mask = spi_nor_get_sr_bp_mask(nor);
+ u8 bp_mask = spi_nor_get_sr_bp_mask(nor);
u8 tb_mask = spi_nor_get_sr_tb_mask(nor);
- u8 bp, val = sr & mask;
+ 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 cmp = sr[1] & cmp_mask;
if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3_BIT6)
val = (val & ~SR_BP3_BIT6) | SR_BP3;
@@ -69,22 +81,37 @@ static void spi_nor_get_locked_range_sr(struct spi_nor *nor, u8 sr, loff_t *ofs,
bp = val >> SR_BP_SHIFT;
if (!bp) {
- /* No protection */
- *ofs = 0;
- *len = 0;
+ if (!cmp) {
+ /* No protection */
+ *ofs = 0;
+ *len = 0;
+ } else {
+ /* Full protection */
+ *ofs = 0;
+ *len = nor->params->size;
+ }
return;
}
min_prot_len = spi_nor_get_min_prot_length_sr(nor);
*len = min_prot_len << (bp - 1);
-
if (*len > nor->params->size)
*len = nor->params->size;
- if (nor->flags & SNOR_F_HAS_SR_TB && sr & tb_mask)
- *ofs = 0;
- else
- *ofs = nor->params->size - *len;
+ if (cmp)
+ *len = nor->params->size - *len;
+
+ if (!cmp) {
+ if (tb)
+ *ofs = 0;
+ else
+ *ofs = nor->params->size - *len;
+ } else {
+ if (tb)
+ *ofs = nor->params->size - *len;
+ else
+ *ofs = 0;
+ }
}
/*
@@ -92,7 +119,7 @@ static void spi_nor_get_locked_range_sr(struct spi_nor *nor, u8 sr, loff_t *ofs,
* (if @locked is false); false otherwise.
*/
static bool spi_nor_check_lock_status_sr(struct spi_nor *nor, loff_t ofs,
- u64 len, u8 sr, bool locked)
+ u64 len, const u8 *sr, bool locked)
{
loff_t lock_offs, lock_offs_max, offs_max;
u64 lock_len;
@@ -113,27 +140,102 @@ static bool spi_nor_check_lock_status_sr(struct spi_nor *nor, loff_t ofs,
return (ofs >= lock_offs_max) || (offs_max <= lock_offs);
}
-static bool spi_nor_is_locked_sr(struct spi_nor *nor, loff_t ofs, u64 len, u8 sr)
+bool spi_nor_is_locked_sr(struct spi_nor *nor, loff_t ofs, u64 len, const u8 *sr)
{
return spi_nor_check_lock_status_sr(nor, ofs, len, sr, true);
}
static bool spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, u64 len,
- u8 sr)
+ const u8 *sr)
{
return spi_nor_check_lock_status_sr(nor, ofs, len, sr, false);
}
+static int spi_nor_sr_set_bp_mask(struct spi_nor *nor, u8 *sr, u8 pow)
+{
+ 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)
+ val = (val & ~SR_BP3) | SR_BP3_BIT6;
+
+ if (val & ~mask)
+ return -EINVAL;
+
+ sr[0] |= val;
+
+ return 0;
+}
+
+static int spi_nor_build_sr(struct spi_nor *nor, const u8 *old_sr, u8 *new_sr,
+ u8 pow, bool use_top, bool cmp)
+{
+ 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);
+ int ret;
+
+ new_sr[0] = old_sr[0] & ~bp_mask & ~tb_mask;
+ new_sr[1] = old_sr[1] & ~cmp_mask;
+
+ /* Build BP field */
+ ret = spi_nor_sr_set_bp_mask(nor, &new_sr[0], pow);
+ if (ret)
+ return ret;
+
+ /* Build TB field */
+ if ((!cmp && !use_top) || (cmp && use_top))
+ new_sr[0] |= tb_mask;
+
+ /* Build CMP field */
+ if (cmp)
+ new_sr[1] |= cmp_mask;
+
+ return 0;
+}
+
+/*
+ * Keep a local cache containing all lock-related bits for debugfs use only.
+ * This way, debugfs never needs to access the flash directly.
+ */
+void spi_nor_cache_sr_lock_bits(struct spi_nor *nor, u8 *sr)
+{
+ 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 sr_cr[2] = {};
+
+
+ if (!sr) {
+ if (spi_nor_read_sr(nor, nor->bouncebuf))
+ 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;
+ }
+
+ nor->dfs_sr_cache[0] = sr[0] & (bp_mask | tb_mask | SR_SRWD);
+ nor->dfs_sr_cache[1] = sr[1] & cmp_mask;
+}
+
/*
* Lock a region of the flash. Compatible with ST Micro and similar flash.
* Supports the block protection bits BP{0,1,2}/BP{0,1,2,3} in the status
* register
* (SR). Does not support these features found in newer SR bitfields:
* - SEC: sector/block protect - only handle SEC=0 (block protect)
- * - CMP: complement protect - only support CMP=0 (range is not complemented)
*
* Support for the following is provided conditionally for some flash:
* - TB: top/bottom protect
+ * - CMP: complement protect (BP and TP describe the unlocked part, while
+ * the reminder is locked)
*
* Sample table portion for 8MB flash (Winbond w25q64fw):
*
@@ -159,20 +261,31 @@ static bool spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, u64 len,
*/
static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len)
{
- u64 min_prot_len;
- int ret, status_old, status_new;
- u8 mask = spi_nor_get_sr_bp_mask(nor);
- u8 tb_mask = spi_nor_get_sr_tb_mask(nor);
- u8 pow, val;
+ 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 = nor->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);
if (ret)
return ret;
- status_old = nor->bouncebuf[0];
+ 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))
@@ -199,46 +312,82 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len)
else
lock_len = ofs + len;
- if (lock_len == nor->params->size) {
- val = mask;
- } else {
- min_prot_len = spi_nor_get_min_prot_length_sr(nor);
+ if (lock_len == nor->params->size)
+ pow = (nor->flags & SNOR_F_HAS_4BIT_BP) ? GENMASK(3, 0) : GENMASK(2, 0);
+ else
pow = ilog2(lock_len) - ilog2(min_prot_len) + 1;
- val = pow << SR_BP_SHIFT;
-
- if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3)
- val = (val & ~SR_BP3) | SR_BP3_BIT6;
- if (val & ~mask)
- return -EINVAL;
+ ret = spi_nor_build_sr(nor, status_old, status_new, pow, use_top, false);
+ if (ret)
+ return ret;
- /* Don't "lock" with no region! */
- if (!(val & mask))
- return -EINVAL;
+ /*
+ * In case the region asked is not fully met, maybe we can try with the
+ * complement feature
+ */
+ spi_nor_get_locked_range_sr(nor, status_new, &ofs_new, &len_new);
+ if (can_be_cmp && len_new != lock_len) {
+ pow = ilog2(nor->params->size - lock_len) - ilog2(min_prot_len) + 1;
+ ret = spi_nor_build_sr(nor, status_old, status_new_cmp, pow, use_top, true);
+ if (ret)
+ return ret;
+
+ /*
+ * ilog2() "floors" the result, which means in some cases we may have to
+ * manually reduce the scope when the complement feature is used.
+ * The uAPI is to never lock more than what is requested, but less is accepted.
+ * Make sure we are not covering a too wide range, reduce it otherwise.
+ */
+ spi_nor_get_locked_range_sr(nor, status_new_cmp, &ofs_new_cmp, &len_new_cmp);
+ if (len_new_cmp > lock_len) {
+ pow++;
+ ret = spi_nor_build_sr(nor, status_old, status_new_cmp, pow, use_top, true);
+ if (ret)
+ return ret;
+ }
+
+ /* Pick the CMP configuration if we cover a closer range */
+ spi_nor_get_locked_range_sr(nor, status_new, &ofs_new, &len_new);
+ spi_nor_get_locked_range_sr(nor, status_new_cmp, &ofs_new_cmp, &len_new_cmp);
+ if (len_new_cmp <= lock_len &&
+ (lock_len - len_new_cmp) < (lock_len - len_new))
+ best_status_new = status_new_cmp;
}
- status_new = (status_old & ~mask & ~tb_mask) | val;
-
/*
* Disallow further writes if WP# pin is neither left floating nor
* 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))
- status_new |= SR_SRWD;
+ best_status_new[0] |= SR_SRWD;
+
+ spi_nor_get_locked_range_sr(nor, status_old, &ofs_old, &len_old);
+ spi_nor_get_locked_range_sr(nor, best_status_new, &ofs_new, &len_new);
- if (!use_top)
- status_new |= tb_mask;
+ /* Don't "lock" with no region! */
+ if (!len_new)
+ return -EINVAL;
/* Don't bother if they're the same */
- if (status_new == status_old)
+ if (best_status_new[0] == status_old[0] && best_status_new[1] == status_old[1])
return 0;
/* Only modify protection if it will not unlock other areas */
- if ((status_new & mask) < (status_old & mask))
+ if (len_old &&
+ (ofs_old < ofs_new || (ofs_new + len_new) < (ofs_old + len_old)))
return -EINVAL;
- return spi_nor_write_sr_and_check(nor, status_new);
+ 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);
+ if (ret)
+ return ret;
+
+ spi_nor_cache_sr_lock_bits(nor, best_status_new);
+
+ return 0;
}
/*
@@ -248,20 +397,31 @@ 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)
{
- u64 min_prot_len;
- int ret, status_old, status_new;
- u8 mask = spi_nor_get_sr_bp_mask(nor);
- u8 tb_mask = spi_nor_get_sr_tb_mask(nor);
- u8 pow, val;
+ 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 = nor->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);
if (ret)
return ret;
- status_old = nor->bouncebuf[0];
+ 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))
@@ -282,45 +442,86 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len)
/* Prefer top, if both are valid */
use_top = can_be_top;
- /* lock_len: length of region that should remain locked */
- if (use_top)
+ /*
+ * lock_len: length of region that should remain locked.
+ *
+ * When can_be_top and can_be_bottom booleans are true, both adjacent
+ * regions are unlocked, thus the entire flash can be unlocked.
+ */
+ if (can_be_top && can_be_bottom)
+ lock_len = 0;
+ else if (use_top)
lock_len = nor->params->size - (ofs + len);
else
lock_len = ofs;
- if (lock_len == 0) {
- val = 0; /* fully unlocked */
- } else {
- min_prot_len = spi_nor_get_min_prot_length_sr(nor);
+ if (lock_len == 0)
+ pow = 0; /* fully unlocked */
+ else
pow = ilog2(lock_len) - ilog2(min_prot_len) + 1;
- val = pow << SR_BP_SHIFT;
- if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3)
- val = (val & ~SR_BP3) | SR_BP3_BIT6;
+ ret = spi_nor_build_sr(nor, status_old, status_new, pow, use_top, false);
+ if (ret)
+ return ret;
- /* Some power-of-two sizes are not supported */
- if (val & ~mask)
- return -EINVAL;
+ /*
+ * In case the region asked is not fully met, maybe we can try with the
+ * complement feature
+ */
+ spi_nor_get_locked_range_sr(nor, status_new, &ofs_new, &len_new);
+ if (can_be_cmp && len_new != lock_len) {
+ pow = ilog2(nor->params->size - lock_len) - ilog2(min_prot_len) + 1;
+ ret = spi_nor_build_sr(nor, status_old, status_new_cmp, pow, use_top, true);
+ if (ret)
+ return ret;
+
+ /*
+ * ilog2() "floors" the result, which means in some cases we may have to
+ * manually reduce the scope when the complement feature is used.
+ * The uAPI is to never unlock more than what is requested, but less is accepted.
+ * Make sure we are not covering a too small range, increase it otherwise.
+ */
+ spi_nor_get_locked_range_sr(nor, status_new_cmp, &ofs_new_cmp, &len_new_cmp);
+ if (len_new_cmp < lock_len) {
+ pow--;
+ ret = spi_nor_build_sr(nor, status_old, status_new_cmp, pow, use_top, true);
+ if (ret)
+ return ret;
+ }
+
+ /* Pick the CMP configuration if we cover a closer range */
+ spi_nor_get_locked_range_sr(nor, status_new, &ofs_new, &len_new);
+ spi_nor_get_locked_range_sr(nor, status_new_cmp, &ofs_new_cmp, &len_new_cmp);
+ if (len_new_cmp <= lock_len &&
+ (lock_len - len_new_cmp) < (lock_len - len_new))
+ best_status_new = status_new_cmp;
}
- status_new = (status_old & ~mask & ~tb_mask) | val;
-
/* Don't protect status register if we're fully unlocked */
if (lock_len == 0)
- status_new &= ~SR_SRWD;
-
- if (!use_top)
- status_new |= tb_mask;
+ best_status_new[0] &= ~SR_SRWD;
/* Don't bother if they're the same */
- if (status_new == status_old)
+ if (best_status_new[0] == status_old[0] && best_status_new[1] == status_old[1])
return 0;
/* Only modify protection if it will not lock other areas */
- if ((status_new & mask) > (status_old & mask))
+ spi_nor_get_locked_range_sr(nor, status_old, &ofs_old, &len_old);
+ spi_nor_get_locked_range_sr(nor, best_status_new, &ofs_new, &len_new);
+ if (len_old && len_new &&
+ (ofs_new < ofs_old || (ofs_old + len_old) < (ofs_new + len_new)))
return -EINVAL;
- return spi_nor_write_sr_and_check(nor, status_new);
+ 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);
+ if (ret)
+ return ret;
+
+ spi_nor_cache_sr_lock_bits(nor, best_status_new);
+
+ return 0;
}
/*
@@ -332,13 +533,24 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len)
*/
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);
if (ret)
return ret;
- return spi_nor_is_locked_sr(nor, ofs, len, nor->bouncebuf[0]);
+ 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);
}
static const struct spi_nor_locking_ops spi_nor_sr_locking_ops = {
@@ -352,6 +564,11 @@ void spi_nor_init_default_locking_ops(struct spi_nor *nor)
nor->params->locking_ops = &spi_nor_sr_locking_ops;
}
+bool spi_nor_has_default_locking_ops(struct spi_nor *nor)
+{
+ return nor->params->locking_ops == &spi_nor_sr_locking_ops;
+}
+
static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, u64 len)
{
struct spi_nor *nor = mtd_to_spi_nor(mtd);
diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c
index eaa547d36aad..8ebdbcec0b3f 100644
--- a/drivers/mtd/spi-nor/winbond.c
+++ b/drivers/mtd/spi-nor/winbond.c
@@ -73,6 +73,26 @@ 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'.
@@ -348,27 +368,36 @@ static const struct flash_info winbond_nor_parts[] = {
}, {
/* 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,
+ .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 */
.id = SNOR_ID(0xef, 0x80, 0x21),
- .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,
}, {
/* W25Q02NWxxIM */
.id = SNOR_ID(0xef, 0x80, 0x22),
- .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,
+ .fixups = &winbond_rdcr_fixup,
}, {
/* W25H512NWxxAM */
.id = SNOR_ID(0xef, 0xa0, 0x20),
- .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,
}, {
/* W25H01NWxxAM */
.id = SNOR_ID(0xef, 0xa0, 0x21),
- .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,
}, {
/* W25H02NWxxAM */
.id = SNOR_ID(0xef, 0xa0, 0x22),
- .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,
+ .fixups = &winbond_rdcr_fixup,
},
};
diff --git a/include/linux/mtd/nand-qpic-common.h b/include/linux/mtd/nand-qpic-common.h
index e8201d1b7cf9..006ca8c978a9 100644
--- a/include/linux/mtd/nand-qpic-common.h
+++ b/include/linux/mtd/nand-qpic-common.h
@@ -9,6 +9,8 @@
#ifndef __MTD_NAND_QPIC_COMMON_H__
#define __MTD_NAND_QPIC_COMMON_H__
+#include <linux/mtd/rawnand.h>
+
/* NANDc reg offsets */
#define NAND_FLASH_CMD 0x00
#define NAND_ADDR0 0x04
@@ -394,7 +396,7 @@ struct qcom_nand_controller {
const struct qcom_nandc_props *props;
- struct nand_controller *controller;
+ struct nand_controller controller;
struct qpic_spi_nand *qspi;
struct list_head host_list;
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index cdcfe0fd2e7d..4b92494827b1 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -21,8 +21,8 @@
/* Flash opcodes. */
#define SPINOR_OP_WRDI 0x04 /* Write disable */
#define SPINOR_OP_WREN 0x06 /* Write enable */
-#define SPINOR_OP_RDSR 0x05 /* Read status register */
-#define SPINOR_OP_WRSR 0x01 /* Write status register 1 byte */
+#define SPINOR_OP_RDSR 0x05 /* Read status register 1 */
+#define SPINOR_OP_WRSR 0x01 /* Write status register 1 */
#define SPINOR_OP_RDSR2 0x3f /* Read status register 2 */
#define SPINOR_OP_WRSR2 0x3e /* Write status register 2 */
#define SPINOR_OP_READ 0x03 /* Read data bytes (low frequency) */
@@ -125,6 +125,7 @@
#define SR2_LB1 BIT(3) /* Security Register Lock Bit 1 */
#define SR2_LB2 BIT(4) /* Security Register Lock Bit 2 */
#define SR2_LB3 BIT(5) /* Security Register Lock Bit 3 */
+#define SR2_CMP_BIT6 BIT(6)
#define SR2_QUAD_EN_BIT7 BIT(7)
/* Supported SPI protocols */
@@ -371,6 +372,7 @@ struct spi_nor_flash_parameter;
* @reg_proto: the SPI protocol for read_reg/write_reg/erase operations
* @sfdp: the SFDP data of the flash
* @debugfs_root: pointer to the debugfs directory
+ * @dfs_sr_cache: Status Register cached value for debugfs use only
* @controller_ops: SPI NOR controller driver specific operations.
* @params: [FLASH-SPECIFIC] SPI NOR flash parameters and settings.
* The structure includes legacy flash parameters and
@@ -409,6 +411,7 @@ struct spi_nor {
enum spi_nor_cmd_ext cmd_ext_type;
struct sfdp *sfdp;
struct dentry *debugfs_root;
+ u8 dfs_sr_cache[2];
const struct spi_nor_controller_ops *controller_ops;
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index f53f5f0499b4..ec6efcfeef83 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -583,6 +583,7 @@ enum spinand_bus_interface {
* @op_variants.read_cache: variants of the read-cache operation
* @op_variants.write_cache: variants of the write-cache operation
* @op_variants.update_cache: variants of the update-cache operation
+ * @op_variants.cont_read_cache: variants of the continuous read-cache operation
* @vendor_ops: vendor specific operations
* @select_target: function used to select a target/die. Required only for
* multi-die chips
@@ -592,6 +593,7 @@ enum spinand_bus_interface {
* @user_otp: SPI NAND user OTP info.
* @read_retries: the number of read retry modes supported
* @set_read_retry: enable/disable read retry for data recovery
+ * @set_randomizer: enable/disable randomizer support
*
* Each SPI NAND manufacturer driver should have a spinand_info table
* describing all the chips supported by the driver.
@@ -607,6 +609,7 @@ struct spinand_info {
const struct spinand_op_variants *read_cache;
const struct spinand_op_variants *write_cache;
const struct spinand_op_variants *update_cache;
+ const struct spinand_op_variants *cont_read_cache;
} op_variants;
const struct spinand_op_variants *vendor_ops;
int (*select_target)(struct spinand_device *spinand,
@@ -620,6 +623,8 @@ struct spinand_info {
unsigned int read_retries;
int (*set_read_retry)(struct spinand_device *spinand,
unsigned int read_retry);
+ int (*set_randomizer)(struct spinand_device *spinand,
+ bool enable);
};
#define SPINAND_ID(__method, ...) \
@@ -636,6 +641,14 @@ struct spinand_info {
.update_cache = __update, \
}
+#define SPINAND_INFO_OP_VARIANTS_WITH_CONT(__read, __write, __update, __cont_read) \
+ { \
+ .read_cache = __read, \
+ .write_cache = __write, \
+ .update_cache = __update, \
+ .cont_read_cache = __cont_read, \
+ }
+
#define SPINAND_INFO_VENDOR_OPS(__ops) \
.vendor_ops = __ops
@@ -676,6 +689,9 @@ struct spinand_info {
.read_retries = __read_retries, \
.set_read_retry = __set_read_retry
+#define SPINAND_RANDOMIZER(__set_randomizer) \
+ .set_randomizer = __set_randomizer
+
#define SPINAND_INFO(__model, __id, __memorg, __eccreq, __op_variants, \
__flags, ...) \
{ \
@@ -707,6 +723,7 @@ struct spinand_dirmap {
* @read_cache: read cache op template
* @write_cache: write cache op template
* @update_cache: update cache op template
+ * @cont_read_cache: continuous read cache op template (optional)
*/
struct spinand_mem_ops {
struct spi_mem_op reset;
@@ -721,6 +738,7 @@ struct spinand_mem_ops {
const struct spi_mem_op *read_cache;
const struct spi_mem_op *write_cache;
const struct spi_mem_op *update_cache;
+ const struct spi_mem_op *cont_read_cache;
};
/**
@@ -759,6 +777,7 @@ struct spinand_mem_ops {
* @user_otp: SPI NAND user OTP info.
* @read_retries: the number of read retry modes supported
* @set_read_retry: Enable/disable the read retry feature
+ * @set_randomizer: Enable/disable the randomizer feature
*/
struct spinand_device {
struct nand_device base;
@@ -792,6 +811,8 @@ struct spinand_device {
bool cont_read_possible;
int (*set_cont_read)(struct spinand_device *spinand,
bool enable);
+ int (*set_randomizer)(struct spinand_device *spinand,
+ bool enable);
const struct spinand_fact_otp *fact_otp;
const struct spinand_user_otp *user_otp;