mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-16 17:57:38 -04:00
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
...
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
new_mtd->next = NULL;
|
||||
|
||||
if ((*curmtd)->mtdinfo) {
|
||||
(*curmtd)->mtdinfo->priv =
|
||||
kzalloc_obj(slram_priv_t);
|
||||
|
||||
if (!(*curmtd)->mtdinfo->priv) {
|
||||
kfree((*curmtd)->mtdinfo);
|
||||
(*curmtd)->mtdinfo = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(*curmtd)->mtdinfo) {
|
||||
mtdinfo = kzalloc_obj(struct mtd_info);
|
||||
if (!mtdinfo) {
|
||||
E("slram: Cannot allocate new MTD device.\n");
|
||||
return(-ENOMEM);
|
||||
goto err_free_list;
|
||||
}
|
||||
new_mtd->mtdinfo = mtdinfo;
|
||||
|
||||
if (!(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start =
|
||||
memremap(start, length,
|
||||
MEMREMAP_WB | MEMREMAP_WT | MEMREMAP_WC))) {
|
||||
priv = kzalloc_obj(slram_priv_t);
|
||||
if (!priv) {
|
||||
E("slram: Cannot allocate new MTD device.\n");
|
||||
goto err_free_mtdinfo;
|
||||
}
|
||||
mtdinfo->priv = priv;
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
(*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)) {
|
||||
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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
@@ -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");
|
||||
@@ -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");
|
||||
|
||||
/****************************************************************************/
|
||||
@@ -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, }
|
||||
{ }
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -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");
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
/****************************************************************************/
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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)
|
||||
@@ -29,6 +31,105 @@
|
||||
#define W35N01JW_VCR_IO_MODE_OCTAL_DDR 0xC7
|
||||
#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.
|
||||
@@ -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 w25n0xjw_hs_cfg(struct spinand_device *spinand,
|
||||
enum spinand_bus_interface iface)
|
||||
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)
|
||||
{
|
||||
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.
|
||||
*/
|
||||
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;
|
||||
u8 sr4;
|
||||
|
||||
ret = spinand_read_reg_op(spinand, W25N0XJW_SR4, &sr4);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (hs)
|
||||
if (enable)
|
||||
sr4 |= W25N0XJW_SR4_HS;
|
||||
else
|
||||
sr4 &= ~W25N0XJW_SR4_HS;
|
||||
|
||||
ret = spinand_write_reg_op(spinand, W25N0XJW_SR4, sr4);
|
||||
if (ret)
|
||||
return ret;
|
||||
return spinand_write_reg_op(spinand, W25N0XJW_SR4, sr4);
|
||||
}
|
||||
|
||||
return 0;
|
||||
/*
|
||||
* 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;
|
||||
|
||||
if (iface != SSDR)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
return w25n0xjw_set_sr4_hs(spinand, w25n0xjw_op_needs_hs(op));
|
||||
}
|
||||
|
||||
static int w25n0xjw_set_cont_read(struct spinand_device *spinand, bool enable)
|
||||
{
|
||||
u8 mask = enable ? 0 : WINBOND_CFG_BUF_READ;
|
||||
|
||||
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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -976,6 +976,54 @@ int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -993,6 +1041,23 @@ int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 sr1)
|
||||
return spi_nor_write_sr1_and_check(nor, 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.
|
||||
@@ -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 &&
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 = ¶ms->erase_map;
|
||||
struct spi_nor_erase_region *region = erase_map->regions;
|
||||
const struct flash_info *info = nor->info;
|
||||
char buf[16], *str;
|
||||
loff_t lock_start;
|
||||
u64 lock_length;
|
||||
unsigned int i;
|
||||
|
||||
seq_printf(s, "name\t\t%s\n", info->name);
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
ret = spi_nor_build_sr(nor, status_old, status_new, pow, use_top, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
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;
|
||||
|
||||
/* Don't "lock" with no region! */
|
||||
if (!(val & mask))
|
||||
return -EINVAL;
|
||||
/*
|
||||
* 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;
|
||||
|
||||
if (!use_top)
|
||||
status_new |= tb_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);
|
||||
|
||||
/* 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);
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user