Common MTD:
* Add Kconfig option for keeping both the 'master' and 'partition' MTDs registered as devices. This would really make a better default if we could do it over, as it allows a lot more flexibility in (1) determining the flash topology of the system from user-space and (2) adding temporary partitions at runtime (ioctl(BLKPG)). Unfortunately, this would possibly cause user-space breakage, as it will cause renumbering of the /dev/mtdX devices. We'll see if we can change this in the future, as there have already been a few people looking for this feature, and I know others have just been working around our current limitations instead of fixing them this way. * Along with the previous change, add some additional information to sysfs, so user-space can read the offset of each partition within its master device SPI NOR: * add new device tree compatible binding to represent the mostly-compatible class of SPI NOR flash which can be detected by their extended JEDEC ID bytes, cutting down the duplication of our ID tables * misc. new IDs Various other miscellaneous fixes and changes -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJVN9ypAAoJEFySrpd9RFgtaUQQAKmlCVMrxAKtF6U5jpzf07hA 7ZrcMdUTSwS++dBIAgDl6JSuSGT5KRLrS1FOp60p+VAjbD9VFcRLUUQxahXW1tAh Dr8a3Akwd+lgIp77bZhWBY35dXmjIJ1GSzo7jdbJMDwAeDd3gBeSFTDoePsrCt6K 0/NPOsQzCFDDr1lwuQh1LzkLLQfVAC3ImNCBm5smvyEfhxXqzC02HOLf8Z9VMGnY OxM9i0T6Ik3xeaaP/vH91sApmdn598gP5DB5cNr61YrZeVZmEoI4EWlOmagcYVC2 Tef9Ng4YmHGXo65k7XcKRykAVWECYAGr4HKCDZ8tsbvpfdbQMS5wHEgxMsAdvb01 aChcBNxf4w/Mh49fzjZppTlPN25FERRMnXt7CkUqQkqet9uDkD/5RNPl65ermeC7 EKx2MoxnpXrfZ0EkSxqrfdzP0oQx0AqAkbCyLIN42Vbxl7ckFMN3WAPQ2NR2Aaoh SUiKwwaFFiK+C9qEytj0s+cmKPzsTzeQVYgp9NX64EfVQumqpsfbu6XIPV+FGy2i DvHvmTEvm4SpqMPSnhkmZ6DFSjuzvQdqzKtDyZmRppxHKgWUsXYdftGPMG0+ZbaG t4zysWfJG897TMVYLKY9pGqvouMuAVJ4kX1+iZbJc8dr4bwIzXIYuEGPLVv58gUO KjjlYk91/jFNmBW5anxC =aIsV -----END PGP SIGNATURE----- Merge tag 'for-linus-20150422' of git://git.infradead.org/linux-mtd Pull MTD updates from Brian Norris: "Common MTD: - Add Kconfig option for keeping both the 'master' and 'partition' MTDs registered as devices. This would really make a better default if we could do it over, as it allows a lot more flexibility in (1) determining the flash topology of the system from user-space and (2) adding temporary partitions at runtime (ioctl(BLKPG)). Unfortunately, this would possibly cause user-space breakage, as it will cause renumbering of the /dev/mtdX devices. We'll see if we can change this in the future, as there have already been a few people looking for this feature, and I know others have just been working around our current limitations instead of fixing them this way. - Along with the previous change, add some additional information to sysfs, so user-space can read the offset of each partition within its master device SPI NOR: - add new device tree compatible binding to represent the mostly-compatible class of SPI NOR flash which can be detected by their extended JEDEC ID bytes, cutting down the duplication of our ID tables - misc. new IDs Various other miscellaneous fixes and changes" * tag 'for-linus-20150422' of git://git.infradead.org/linux-mtd: (53 commits) mtd: spi-nor: Add support for Macronix mx25u6435f serial flash mtd: spi-nor: Add support for Winbond w25q64dw serial flash mtd: spi-nor: add support for the Winbond W25X05 flash mtd: spi-nor: support en25s64 device mtd: m25p80: bind to "nor-jedec" ID, for auto-detection Documentation: devicetree: m25p80: add "nor-jedec" binding mtd: Make MTD tests cancelable mtd: mtd_oobtest: Fix bitflip_limit usage in test case 3 mtd: docg3: remove invalid __exit annotations mtd: fsl_ifc_nand: use msecs_to_jiffies for time conversion mtd: atmel_nand: don't map the ROM table if no pmecc table offset in DT mtd: atmel_nand: add a definition for the oob reserved bytes mtd: part: Remove partition overlap checks mtd: part: Add sysfs variable for offset of partition mtd: part: Create the master device node when partitioned mtd: ts5500_flash: Fix typo in MODULE_DESCRIPTION in ts5500_flash.c mtd: denali: Disable sub-page writes in Denali NAND driver mtd: pxa3xx_nand: cleanup wait_for_completion handling mtd: nand: gpmi: Check for scan_bbt() error mtd: nand: gpmi: fixup return type of wait_for_completion_timeout ...
This commit is contained in:
Коммит
a62d016cec
|
@ -222,3 +222,13 @@ Description:
|
|||
The number of blocks that are marked as reserved, if any, in
|
||||
this partition. These are typically used to store the in-flash
|
||||
bad block table (BBT).
|
||||
|
||||
What: /sys/class/mtd/mtdX/offset
|
||||
Date: March 2015
|
||||
KernelVersion: 4.1
|
||||
Contact: linux-mtd@lists.infradead.org
|
||||
Description:
|
||||
For a partition, the offset of that partition from the start
|
||||
of the master device in bytes. This attribute is absent on
|
||||
main devices, so it can be used to distinguish between
|
||||
partitions and devices that aren't partitions.
|
||||
|
|
|
@ -3,10 +3,13 @@
|
|||
Required properties:
|
||||
- #address-cells, #size-cells : Must be present if the device has sub-nodes
|
||||
representing partitions.
|
||||
- compatible : Should be the manufacturer and the name of the chip. Bear in mind
|
||||
the DT binding is not Linux-only, but in case of Linux, see the
|
||||
"spi_nor_ids" table in drivers/mtd/spi-nor/spi-nor.c for the list
|
||||
of supported chips.
|
||||
- compatible : May include a device-specific string consisting of the
|
||||
manufacturer and name of the chip. Bear in mind the DT binding
|
||||
is not Linux-only, but in case of Linux, see the "m25p_ids"
|
||||
table in drivers/mtd/devices/m25p80.c for the list of supported
|
||||
chips.
|
||||
Must also include "nor-jedec" for any SPI NOR flash that can be
|
||||
identified by the JEDEC READ ID opcode (0x9F).
|
||||
- reg : Chip-Select number
|
||||
- spi-max-frequency : Maximum frequency of the SPI bus the chip can operate at
|
||||
|
||||
|
@ -22,7 +25,7 @@ Example:
|
|||
flash: m25p80@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "spansion,m25p80";
|
||||
compatible = "spansion,m25p80", "nor-jedec";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <40000000>;
|
||||
m25p,fast-read;
|
||||
|
|
|
@ -14,7 +14,7 @@ Optional properties:
|
|||
- marvell,nand-enable-arbiter: Set to enable the bus arbiter
|
||||
- marvell,nand-keep-config: Set to keep the NAND controller config as set
|
||||
by the bootloader
|
||||
- num-cs: Number of chipselect lines to usw
|
||||
- num-cs: Number of chipselect lines to use
|
||||
- nand-on-flash-bbt: boolean to enable on flash bbt option if
|
||||
not present false
|
||||
- nand-ecc-strength: number of bits to correct per ECC step
|
||||
|
|
|
@ -21,7 +21,7 @@ Optional properties:
|
|||
- nand-ecc-mode : one of the supported ECC modes ("hw", "hw_syndrome", "soft",
|
||||
"soft_bch" or "none")
|
||||
|
||||
see Documentation/devicetree/mtd/nand.txt for generic bindings.
|
||||
see Documentation/devicetree/bindings/mtd/nand.txt for generic bindings.
|
||||
|
||||
|
||||
Examples:
|
||||
|
|
|
@ -309,6 +309,19 @@ config MTD_SWAP
|
|||
The driver provides wear leveling by storing erase counter into the
|
||||
OOB.
|
||||
|
||||
config MTD_PARTITIONED_MASTER
|
||||
bool "Retain master device when partitioned"
|
||||
default n
|
||||
depends on MTD
|
||||
help
|
||||
For historical reasons, by default, either a master is present or
|
||||
several partitions are present, but not both. The concern was that
|
||||
data listed in multiple partitions was dangerous; however, SCSI does
|
||||
this and it is frequently useful for applications. This config option
|
||||
leaves the master in even if the device is partitioned. It also makes
|
||||
the parent of the partition device be the master device, rather than
|
||||
what lies behind the master.
|
||||
|
||||
source "drivers/mtd/chips/Kconfig"
|
||||
|
||||
source "drivers/mtd/maps/Kconfig"
|
||||
|
|
|
@ -206,23 +206,23 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map)
|
|||
mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum;
|
||||
}
|
||||
offset += (ersize * ernum);
|
||||
}
|
||||
}
|
||||
|
||||
if (offset != devsize) {
|
||||
/* 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;
|
||||
}
|
||||
if (offset != devsize) {
|
||||
/* 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;
|
||||
}
|
||||
|
||||
for (i=0; i<mtd->numeraseregions;i++){
|
||||
printk(KERN_DEBUG "%d: offset=0x%llx,size=0x%x,blocks=%d\n",
|
||||
i, (unsigned long long)mtd->eraseregions[i].offset,
|
||||
mtd->eraseregions[i].erasesize,
|
||||
mtd->eraseregions[i].numblocks);
|
||||
}
|
||||
for (i=0; i<mtd->numeraseregions;i++){
|
||||
printk(KERN_DEBUG "%d: offset=0x%llx,size=0x%x,blocks=%d\n",
|
||||
i, (unsigned long long)mtd->eraseregions[i].offset,
|
||||
mtd->eraseregions[i].erasesize,
|
||||
mtd->eraseregions[i].numblocks);
|
||||
}
|
||||
|
||||
/* Also select the correct geometry setup too */
|
||||
mtd->_erase = cfi_staa_erase_varsize;
|
||||
|
|
|
@ -9,7 +9,15 @@
|
|||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
/*
|
||||
* When the first attempt at device initialization fails, we may need to
|
||||
* wait a little bit and retry. This timeout, by default 3 seconds, gives
|
||||
* device time to start up. Required on BCM2708 and a few other chipsets.
|
||||
*/
|
||||
#define MTD_DEFAULT_TIMEOUT 3
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/bio.h>
|
||||
|
@ -209,10 +217,14 @@ static void block2mtd_free_device(struct block2mtd_dev *dev)
|
|||
}
|
||||
|
||||
|
||||
static struct block2mtd_dev *add_device(char *devname, int erase_size)
|
||||
static struct block2mtd_dev *add_device(char *devname, int erase_size,
|
||||
int timeout)
|
||||
{
|
||||
#ifndef MODULE
|
||||
int i;
|
||||
#endif
|
||||
const fmode_t mode = FMODE_READ | FMODE_WRITE | FMODE_EXCL;
|
||||
struct block_device *bdev;
|
||||
struct block_device *bdev = ERR_PTR(-ENODEV);
|
||||
struct block2mtd_dev *dev;
|
||||
char *name;
|
||||
|
||||
|
@ -225,15 +237,28 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
|
|||
|
||||
/* Get a handle on the device */
|
||||
bdev = blkdev_get_by_path(devname, mode, dev);
|
||||
|
||||
#ifndef MODULE
|
||||
if (IS_ERR(bdev)) {
|
||||
/*
|
||||
* We might not have the root device mounted at this point.
|
||||
* Try to resolve the device name by other means.
|
||||
*/
|
||||
for (i = 0; IS_ERR(bdev) && i <= timeout; i++) {
|
||||
dev_t devt;
|
||||
|
||||
/* We might not have rootfs mounted at this point. Try
|
||||
to resolve the device name by other means. */
|
||||
if (i)
|
||||
/*
|
||||
* Calling wait_for_device_probe in the first loop
|
||||
* was not enough, sleep for a bit in subsequent
|
||||
* go-arounds.
|
||||
*/
|
||||
msleep(1000);
|
||||
wait_for_device_probe();
|
||||
|
||||
dev_t devt = name_to_dev_t(devname);
|
||||
if (devt)
|
||||
bdev = blkdev_get_by_dev(devt, mode, dev);
|
||||
devt = name_to_dev_t(devname);
|
||||
if (!devt)
|
||||
continue;
|
||||
bdev = blkdev_get_by_dev(devt, mode, dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -280,6 +305,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
|
|||
/* Device didn't get added, so free the entry */
|
||||
goto err_destroy_mutex;
|
||||
}
|
||||
|
||||
list_add(&dev->list, &blkmtd_device_list);
|
||||
pr_info("mtd%d: [%s] erase_size = %dKiB [%d]\n",
|
||||
dev->mtd.index,
|
||||
|
@ -348,16 +374,19 @@ static inline void kill_final_newline(char *str)
|
|||
|
||||
#ifndef MODULE
|
||||
static int block2mtd_init_called = 0;
|
||||
static char block2mtd_paramline[80 + 12]; /* 80 for device, 12 for erase size */
|
||||
/* 80 for device, 12 for erase size */
|
||||
static char block2mtd_paramline[80 + 12];
|
||||
#endif
|
||||
|
||||
static int block2mtd_setup2(const char *val)
|
||||
{
|
||||
char buf[80 + 12]; /* 80 for device, 12 for erase size */
|
||||
/* 80 for device, 12 for erase size, 80 for name, 8 for timeout */
|
||||
char buf[80 + 12 + 80 + 8];
|
||||
char *str = buf;
|
||||
char *token[2];
|
||||
char *name;
|
||||
size_t erase_size = PAGE_SIZE;
|
||||
unsigned long timeout = MTD_DEFAULT_TIMEOUT;
|
||||
int i, ret;
|
||||
|
||||
if (strnlen(val, sizeof(buf)) >= sizeof(buf)) {
|
||||
|
@ -395,7 +424,7 @@ static int block2mtd_setup2(const char *val)
|
|||
}
|
||||
}
|
||||
|
||||
add_device(name, erase_size);
|
||||
add_device(name, erase_size, timeout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -463,8 +492,7 @@ static void block2mtd_exit(void)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
module_init(block2mtd_init);
|
||||
late_initcall(block2mtd_init);
|
||||
module_exit(block2mtd_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -1805,7 +1805,7 @@ static int __init doc_dbg_register(struct docg3 *docg3)
|
|||
}
|
||||
}
|
||||
|
||||
static void __exit doc_dbg_unregister(struct docg3 *docg3)
|
||||
static void doc_dbg_unregister(struct docg3 *docg3)
|
||||
{
|
||||
debugfs_remove_recursive(docg3->debugfs_root);
|
||||
}
|
||||
|
@ -2033,7 +2033,7 @@ static int __init docg3_probe(struct platform_device *pdev)
|
|||
struct mtd_info *mtd;
|
||||
struct resource *ress;
|
||||
void __iomem *base;
|
||||
int ret, floor, found = 0;
|
||||
int ret, floor;
|
||||
struct docg3_cascade *cascade;
|
||||
|
||||
ret = -ENXIO;
|
||||
|
@ -2073,14 +2073,11 @@ static int __init docg3_probe(struct platform_device *pdev)
|
|||
0);
|
||||
if (ret)
|
||||
goto err_probe;
|
||||
found++;
|
||||
}
|
||||
|
||||
ret = doc_register_sysfs(pdev, cascade);
|
||||
if (ret)
|
||||
goto err_probe;
|
||||
if (!found)
|
||||
goto notfound;
|
||||
|
||||
platform_set_drvdata(pdev, cascade);
|
||||
doc_dbg_register(cascade->floors[0]->priv);
|
||||
|
@ -2103,7 +2100,7 @@ err_probe:
|
|||
*
|
||||
* Returns 0
|
||||
*/
|
||||
static int __exit docg3_release(struct platform_device *pdev)
|
||||
static int docg3_release(struct platform_device *pdev)
|
||||
{
|
||||
struct docg3_cascade *cascade = platform_get_drvdata(pdev);
|
||||
struct docg3 *docg3 = cascade->floors[0]->priv;
|
||||
|
@ -2134,7 +2131,7 @@ static struct platform_driver g3_driver = {
|
|||
},
|
||||
.suspend = docg3_suspend,
|
||||
.resume = docg3_resume,
|
||||
.remove = __exit_p(docg3_release),
|
||||
.remove = docg3_release,
|
||||
};
|
||||
|
||||
module_platform_driver_probe(g3_driver, docg3_probe);
|
||||
|
|
|
@ -223,6 +223,8 @@ static int m25p_probe(struct spi_device *spi)
|
|||
*/
|
||||
if (data && data->type)
|
||||
flash_name = data->type;
|
||||
else if (!strcmp(spi->modalias, "nor-jedec"))
|
||||
flash_name = NULL; /* auto-detect */
|
||||
else
|
||||
flash_name = spi->modalias;
|
||||
|
||||
|
@ -247,9 +249,16 @@ static int m25p_remove(struct spi_device *spi)
|
|||
}
|
||||
|
||||
/*
|
||||
* XXX This needs to be kept in sync with spi_nor_ids. We can't share
|
||||
* it with spi-nor, because if this is built as a module then modpost
|
||||
* won't be able to read it and add appropriate aliases.
|
||||
* Do NOT add to this array without reading the following:
|
||||
*
|
||||
* Historically, many flash devices are bound to this driver by their name. But
|
||||
* since most of these flash are compatible to some extent, and their
|
||||
* differences can often be differentiated by the JEDEC read-ID command, we
|
||||
* encourage new users to add support to the spi-nor library, and simply bind
|
||||
* against a generic string here (e.g., "nor-jedec").
|
||||
*
|
||||
* Many flash names are kept here in this list (as well as in spi-nor.c) to
|
||||
* keep them available as module aliases for existing platforms.
|
||||
*/
|
||||
static const struct spi_device_id m25p_ids[] = {
|
||||
{"at25fs010"}, {"at25fs040"}, {"at25df041a"}, {"at25df321a"},
|
||||
|
@ -291,6 +300,12 @@ static const struct spi_device_id m25p_ids[] = {
|
|||
{"w25x64"}, {"w25q64"}, {"w25q80"}, {"w25q80bl"},
|
||||
{"w25q128"}, {"w25q256"}, {"cat25c11"},
|
||||
{"cat25c03"}, {"cat25c09"}, {"cat25c17"}, {"cat25128"},
|
||||
|
||||
/*
|
||||
* Generic support for SPI NOR that can be identified by the JEDEC READ
|
||||
* ID opcode (0x9F). Use this, if possible.
|
||||
*/
|
||||
{"nor-jedec"},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, m25p_ids);
|
||||
|
|
|
@ -242,7 +242,7 @@ config MTD_L440GX
|
|||
|
||||
config MTD_CFI_FLAGADM
|
||||
tristate "CFI Flash device mapping on FlagaDM"
|
||||
depends on 8xx && MTD_CFI
|
||||
depends on PPC_8xx && MTD_CFI
|
||||
help
|
||||
Mapping for the Flaga digital module. If you don't have one, ignore
|
||||
this setting.
|
||||
|
|
|
@ -274,7 +274,7 @@ static int sa1100_mtd_probe(struct platform_device *pdev)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int __exit sa1100_mtd_remove(struct platform_device *pdev)
|
||||
static int sa1100_mtd_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sa_info *info = platform_get_drvdata(pdev);
|
||||
struct flash_platform_data *plat = dev_get_platdata(&pdev->dev);
|
||||
|
@ -286,7 +286,7 @@ static int __exit sa1100_mtd_remove(struct platform_device *pdev)
|
|||
|
||||
static struct platform_driver sa1100_mtd_driver = {
|
||||
.probe = sa1100_mtd_probe,
|
||||
.remove = __exit_p(sa1100_mtd_remove),
|
||||
.remove = sa1100_mtd_remove,
|
||||
.driver = {
|
||||
.name = "sa1100-mtd",
|
||||
},
|
||||
|
|
|
@ -117,5 +117,5 @@ module_exit(cleanup_ts5500_map);
|
|||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Sean Young <sean@mess.org>");
|
||||
MODULE_DESCRIPTION("MTD map driver for Techology Systems TS-5500 board");
|
||||
MODULE_DESCRIPTION("MTD map driver for Technology Systems TS-5500 board");
|
||||
|
||||
|
|
|
@ -171,9 +171,6 @@ static void mtd_blktrans_work(struct work_struct *work)
|
|||
background_done = 0;
|
||||
}
|
||||
|
||||
if (req)
|
||||
__blk_end_request_all(req, -EIO);
|
||||
|
||||
spin_unlock_irq(rq->queue_lock);
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include <linux/gfp.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/kconfig.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
@ -501,6 +502,29 @@ out_error:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int mtd_add_device_partitions(struct mtd_info *mtd,
|
||||
struct mtd_partition *real_parts,
|
||||
int nbparts)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (nbparts == 0 || IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) {
|
||||
ret = add_mtd_device(mtd);
|
||||
if (ret == 1)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (nbparts > 0) {
|
||||
ret = add_mtd_partitions(mtd, real_parts, nbparts);
|
||||
if (ret && IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER))
|
||||
del_mtd_device(mtd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mtd_device_parse_register - parse partitions and register an MTD device.
|
||||
*
|
||||
|
@ -523,7 +547,8 @@ out_error:
|
|||
* found this functions tries to fallback to information specified in
|
||||
* @parts/@nr_parts.
|
||||
* * If any partitioning info was found, this function registers the found
|
||||
* partitions.
|
||||
* partitions. If the MTD_PARTITIONED_MASTER option is set, then the device
|
||||
* as a whole is registered first.
|
||||
* * If no partitions were found this function just registers the MTD device
|
||||
* @mtd and exits.
|
||||
*
|
||||
|
@ -534,27 +559,21 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
|
|||
const struct mtd_partition *parts,
|
||||
int nr_parts)
|
||||
{
|
||||
int err;
|
||||
struct mtd_partition *real_parts;
|
||||
int ret;
|
||||
struct mtd_partition *real_parts = NULL;
|
||||
|
||||
err = parse_mtd_partitions(mtd, types, &real_parts, parser_data);
|
||||
if (err <= 0 && nr_parts && parts) {
|
||||
ret = parse_mtd_partitions(mtd, types, &real_parts, parser_data);
|
||||
if (ret <= 0 && nr_parts && parts) {
|
||||
real_parts = kmemdup(parts, sizeof(*parts) * nr_parts,
|
||||
GFP_KERNEL);
|
||||
if (!real_parts)
|
||||
err = -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
else
|
||||
err = nr_parts;
|
||||
ret = nr_parts;
|
||||
}
|
||||
|
||||
if (err > 0) {
|
||||
err = add_mtd_partitions(mtd, real_parts, err);
|
||||
kfree(real_parts);
|
||||
} else if (err == 0) {
|
||||
err = add_mtd_device(mtd);
|
||||
if (err == 1)
|
||||
err = -ENODEV;
|
||||
}
|
||||
if (ret >= 0)
|
||||
ret = mtd_add_device_partitions(mtd, real_parts, ret);
|
||||
|
||||
/*
|
||||
* FIXME: some drivers unfortunately call this function more than once.
|
||||
|
@ -569,7 +588,8 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
|
|||
register_reboot_notifier(&mtd->reboot_notifier);
|
||||
}
|
||||
|
||||
return err;
|
||||
kfree(real_parts);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_device_parse_register);
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kconfig.h>
|
||||
|
||||
#include "mtdcore.h"
|
||||
|
||||
|
@ -379,10 +380,17 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
|
|||
slave->mtd.name = name;
|
||||
slave->mtd.owner = master->owner;
|
||||
|
||||
/* NOTE: we don't arrange MTDs as a tree; it'd be error-prone
|
||||
* to have the same data be in two different partitions.
|
||||
/* NOTE: Historically, we didn't arrange MTDs as a tree out of
|
||||
* concern for showing the same data in multiple partitions.
|
||||
* However, it is very useful to have the master node present,
|
||||
* so the MTD_PARTITIONED_MASTER option allows that. The master
|
||||
* will have device nodes etc only if this is set, so make the
|
||||
* parent conditional on that option. Note, this is a way to
|
||||
* distinguish between the master and the partition in sysfs.
|
||||
*/
|
||||
slave->mtd.dev.parent = master->dev.parent;
|
||||
slave->mtd.dev.parent = IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) ?
|
||||
&master->dev :
|
||||
master->dev.parent;
|
||||
|
||||
slave->mtd._read = part_read;
|
||||
slave->mtd._write = part_write;
|
||||
|
@ -546,12 +554,35 @@ out_register:
|
|||
return slave;
|
||||
}
|
||||
|
||||
static ssize_t mtd_partition_offset_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct mtd_info *mtd = dev_get_drvdata(dev);
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return snprintf(buf, PAGE_SIZE, "%lld\n", part->offset);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(offset, S_IRUGO, mtd_partition_offset_show, NULL);
|
||||
|
||||
static const struct attribute *mtd_partition_attrs[] = {
|
||||
&dev_attr_offset.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static int mtd_add_partition_attrs(struct mtd_part *new)
|
||||
{
|
||||
int ret = sysfs_create_files(&new->mtd.dev.kobj, mtd_partition_attrs);
|
||||
if (ret)
|
||||
printk(KERN_WARNING
|
||||
"mtd: failed to create partition attrs, err=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mtd_add_partition(struct mtd_info *master, const char *name,
|
||||
long long offset, long long length)
|
||||
{
|
||||
struct mtd_partition part;
|
||||
struct mtd_part *p, *new;
|
||||
uint64_t start, end;
|
||||
struct mtd_part *new;
|
||||
int ret = 0;
|
||||
|
||||
/* the direct offset is expected */
|
||||
|
@ -575,31 +606,15 @@ int mtd_add_partition(struct mtd_info *master, const char *name,
|
|||
if (IS_ERR(new))
|
||||
return PTR_ERR(new);
|
||||
|
||||
start = offset;
|
||||
end = offset + length;
|
||||
|
||||
mutex_lock(&mtd_partitions_mutex);
|
||||
list_for_each_entry(p, &mtd_partitions, list)
|
||||
if (p->master == master) {
|
||||
if ((start >= p->offset) &&
|
||||
(start < (p->offset + p->mtd.size)))
|
||||
goto err_inv;
|
||||
|
||||
if ((end >= p->offset) &&
|
||||
(end < (p->offset + p->mtd.size)))
|
||||
goto err_inv;
|
||||
}
|
||||
|
||||
list_add(&new->list, &mtd_partitions);
|
||||
mutex_unlock(&mtd_partitions_mutex);
|
||||
|
||||
add_mtd_device(&new->mtd);
|
||||
|
||||
mtd_add_partition_attrs(new);
|
||||
|
||||
return ret;
|
||||
err_inv:
|
||||
mutex_unlock(&mtd_partitions_mutex);
|
||||
free_partition(new);
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_add_partition);
|
||||
|
||||
|
@ -612,6 +627,8 @@ int mtd_del_partition(struct mtd_info *master, int partno)
|
|||
list_for_each_entry_safe(slave, next, &mtd_partitions, list)
|
||||
if ((slave->master == master) &&
|
||||
(slave->mtd.index == partno)) {
|
||||
sysfs_remove_files(&slave->mtd.dev.kobj,
|
||||
mtd_partition_attrs);
|
||||
ret = del_mtd_device(&slave->mtd);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
@ -631,8 +648,8 @@ EXPORT_SYMBOL_GPL(mtd_del_partition);
|
|||
* and registers slave MTD objects which are bound to the master according to
|
||||
* the partition definitions.
|
||||
*
|
||||
* We don't register the master, or expect the caller to have done so,
|
||||
* for reasons of data integrity.
|
||||
* For historical reasons, this function's caller only registers the master
|
||||
* if the MTD_PARTITIONED_MASTER config option is set.
|
||||
*/
|
||||
|
||||
int add_mtd_partitions(struct mtd_info *master,
|
||||
|
@ -655,6 +672,7 @@ int add_mtd_partitions(struct mtd_info *master,
|
|||
mutex_unlock(&mtd_partitions_mutex);
|
||||
|
||||
add_mtd_device(&slave->mtd);
|
||||
mtd_add_partition_attrs(slave);
|
||||
|
||||
cur_offset = slave->offset + slave->mtd.size;
|
||||
}
|
||||
|
|
|
@ -485,7 +485,7 @@ static void pmecc_config_ecc_layout(struct nand_ecclayout *layout,
|
|||
for (i = 0; i < ecc_len; i++)
|
||||
layout->eccpos[i] = oobsize - ecc_len + i;
|
||||
|
||||
layout->oobfree[0].offset = 2;
|
||||
layout->oobfree[0].offset = PMECC_OOB_RESERVED_BYTES;
|
||||
layout->oobfree[0].length =
|
||||
oobsize - ecc_len - layout->oobfree[0].offset;
|
||||
}
|
||||
|
@ -1204,14 +1204,14 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
|
|||
goto err;
|
||||
}
|
||||
|
||||
regs_rom = platform_get_resource(pdev, IORESOURCE_MEM, 3);
|
||||
host->pmecc_rom_base = devm_ioremap_resource(&pdev->dev, regs_rom);
|
||||
if (IS_ERR(host->pmecc_rom_base)) {
|
||||
if (!host->has_no_lookup_table)
|
||||
/* Don't display the information again */
|
||||
if (!host->has_no_lookup_table) {
|
||||
regs_rom = platform_get_resource(pdev, IORESOURCE_MEM, 3);
|
||||
host->pmecc_rom_base = devm_ioremap_resource(&pdev->dev,
|
||||
regs_rom);
|
||||
if (IS_ERR(host->pmecc_rom_base)) {
|
||||
dev_err(host->dev, "Can not get I/O resource for ROM, will build a lookup table in runtime!\n");
|
||||
|
||||
host->has_no_lookup_table = true;
|
||||
host->has_no_lookup_table = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (host->has_no_lookup_table) {
|
||||
|
@ -1254,7 +1254,8 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
|
|||
nand_chip->ecc.steps = mtd->writesize / sector_size;
|
||||
nand_chip->ecc.total = nand_chip->ecc.bytes *
|
||||
nand_chip->ecc.steps;
|
||||
if (nand_chip->ecc.total > mtd->oobsize - 2) {
|
||||
if (nand_chip->ecc.total >
|
||||
mtd->oobsize - PMECC_OOB_RESERVED_BYTES) {
|
||||
dev_err(host->dev, "No room for ECC bytes\n");
|
||||
err_no = -EINVAL;
|
||||
goto err;
|
||||
|
@ -1719,7 +1720,7 @@ static int nfc_wait_interrupt(struct atmel_nand_host *host, u32 flag)
|
|||
comp[index++] = &host->nfc->comp_cmd_done;
|
||||
|
||||
if (index == 0) {
|
||||
dev_err(host->dev, "Unkown interrupt flag: 0x%08x\n", flag);
|
||||
dev_err(host->dev, "Unknown interrupt flag: 0x%08x\n", flag);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -1752,11 +1753,10 @@ static int nfc_send_command(struct atmel_nand_host *host,
|
|||
cmd, addr, cycle0);
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(NFC_TIME_OUT_MS);
|
||||
while (nfc_cmd_readl(NFCADDR_CMD_NFCBUSY, host->nfc->base_cmd_regs)
|
||||
& NFCADDR_CMD_NFCBUSY) {
|
||||
while (nfc_readl(host->nfc->hsmc_regs, SR) & NFC_SR_BUSY) {
|
||||
if (time_after(jiffies, timeout)) {
|
||||
dev_err(host->dev,
|
||||
"Time out to wait CMD_NFCBUSY ready!\n");
|
||||
"Time out to wait for NFC ready!\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -152,4 +152,7 @@
|
|||
/* Time out value for reading PMECC status register */
|
||||
#define PMECC_MAX_TIMEOUT_MS 100
|
||||
|
||||
/* Reserved bytes in oob area */
|
||||
#define PMECC_OOB_RESERVED_BYTES 2
|
||||
|
||||
#endif
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#define NFC_CTRL_DISABLE (1 << 1)
|
||||
|
||||
#define ATMEL_HSMC_NFC_SR 0x08 /* NFC Status Register */
|
||||
#define NFC_SR_BUSY (1 << 8)
|
||||
#define NFC_SR_XFR_DONE (1 << 16)
|
||||
#define NFC_SR_CMD_DONE (1 << 17)
|
||||
#define NFC_SR_DTOE (1 << 20)
|
||||
|
|
|
@ -225,7 +225,6 @@ static void nand_onfi_timing_set(struct denali_nand_info *denali,
|
|||
uint16_t Twhr[6] = {120, 80, 80, 60, 60, 60};
|
||||
uint16_t Tcs[6] = {70, 35, 25, 25, 20, 15};
|
||||
|
||||
uint16_t TclsRising = 1;
|
||||
uint16_t data_invalid_rhoh, data_invalid_rloh, data_invalid;
|
||||
uint16_t dv_window = 0;
|
||||
uint16_t en_lo, en_hi;
|
||||
|
@ -276,8 +275,6 @@ static void nand_onfi_timing_set(struct denali_nand_info *denali,
|
|||
re_2_re = CEIL_DIV(Trhz[mode], CLK_X);
|
||||
we_2_re = CEIL_DIV(Twhr[mode], CLK_X);
|
||||
cs_cnt = CEIL_DIV((Tcs[mode] - Trp[mode]), CLK_X);
|
||||
if (!TclsRising)
|
||||
cs_cnt = CEIL_DIV(Tcs[mode], CLK_X);
|
||||
if (cs_cnt == 0)
|
||||
cs_cnt = 1;
|
||||
|
||||
|
@ -1536,6 +1533,9 @@ int denali_init(struct denali_nand_info *denali)
|
|||
denali->nand.options |= NAND_SKIP_BBTSCAN;
|
||||
denali->nand.ecc.mode = NAND_ECC_HW_SYNDROME;
|
||||
|
||||
/* no subpage writes on denali */
|
||||
denali->nand.options |= NAND_NO_SUBPAGE_WRITE;
|
||||
|
||||
/*
|
||||
* Denali Controller only support 15bit and 8bit ECC in MRST,
|
||||
* so just let controller do 15bit ECC for MLC and 8bit ECC for
|
||||
|
|
|
@ -317,7 +317,7 @@ static void fsl_ifc_run_command(struct mtd_info *mtd)
|
|||
|
||||
/* wait for command complete flag or timeout */
|
||||
wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat,
|
||||
IFC_TIMEOUT_MSECS * HZ/1000);
|
||||
msecs_to_jiffies(IFC_TIMEOUT_MSECS));
|
||||
|
||||
/* ctrl->nand_stat will be updated from IRQ context */
|
||||
if (!ctrl->nand_stat)
|
||||
|
@ -860,7 +860,7 @@ static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv)
|
|||
|
||||
/* wait for command complete flag or timeout */
|
||||
wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat,
|
||||
IFC_TIMEOUT_MSECS * HZ/1000);
|
||||
msecs_to_jiffies(IFC_TIMEOUT_MSECS));
|
||||
|
||||
if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC)
|
||||
printk(KERN_ERR "fsl-ifc: Failed to Initialise SRAM\n");
|
||||
|
|
|
@ -873,6 +873,7 @@ static int fsmc_nand_probe_config_dt(struct platform_device *pdev,
|
|||
{
|
||||
struct fsmc_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
/* Set default NAND width to 8 bits */
|
||||
pdata->width = 8;
|
||||
|
@ -891,8 +892,12 @@ static int fsmc_nand_probe_config_dt(struct platform_device *pdev,
|
|||
sizeof(*pdata->nand_timings), GFP_KERNEL);
|
||||
if (!pdata->nand_timings)
|
||||
return -ENOMEM;
|
||||
of_property_read_u8_array(np, "timings", (u8 *)pdata->nand_timings,
|
||||
ret = of_property_read_u8_array(np, "timings", (u8 *)pdata->nand_timings,
|
||||
sizeof(*pdata->nand_timings));
|
||||
if (ret) {
|
||||
dev_info(&pdev->dev, "No timings in dts specified, using default timings!\n");
|
||||
pdata->nand_timings = NULL;
|
||||
}
|
||||
|
||||
/* Set default NAND bank to 0 */
|
||||
pdata->bank = 0;
|
||||
|
|
|
@ -446,7 +446,7 @@ int start_dma_without_bch_irq(struct gpmi_nand_data *this,
|
|||
struct dma_async_tx_descriptor *desc)
|
||||
{
|
||||
struct completion *dma_c = &this->dma_done;
|
||||
int err;
|
||||
unsigned long timeout;
|
||||
|
||||
init_completion(dma_c);
|
||||
|
||||
|
@ -456,8 +456,8 @@ int start_dma_without_bch_irq(struct gpmi_nand_data *this,
|
|||
dma_async_issue_pending(get_dma_chan(this));
|
||||
|
||||
/* Wait for the interrupt from the DMA block. */
|
||||
err = wait_for_completion_timeout(dma_c, msecs_to_jiffies(1000));
|
||||
if (!err) {
|
||||
timeout = wait_for_completion_timeout(dma_c, msecs_to_jiffies(1000));
|
||||
if (!timeout) {
|
||||
dev_err(this->dev, "DMA timeout, last DMA :%d\n",
|
||||
this->last_dma_type);
|
||||
gpmi_dump_info(this);
|
||||
|
@ -477,7 +477,7 @@ int start_dma_with_bch_irq(struct gpmi_nand_data *this,
|
|||
struct dma_async_tx_descriptor *desc)
|
||||
{
|
||||
struct completion *bch_c = &this->bch_done;
|
||||
int err;
|
||||
unsigned long timeout;
|
||||
|
||||
/* Prepare to receive an interrupt from the BCH block. */
|
||||
init_completion(bch_c);
|
||||
|
@ -486,8 +486,8 @@ int start_dma_with_bch_irq(struct gpmi_nand_data *this,
|
|||
start_dma_without_bch_irq(this, desc);
|
||||
|
||||
/* Wait for the interrupt from the BCH block. */
|
||||
err = wait_for_completion_timeout(bch_c, msecs_to_jiffies(1000));
|
||||
if (!err) {
|
||||
timeout = wait_for_completion_timeout(bch_c, msecs_to_jiffies(1000));
|
||||
if (!timeout) {
|
||||
dev_err(this->dev, "BCH timeout, last DMA :%d\n",
|
||||
this->last_dma_type);
|
||||
gpmi_dump_info(this);
|
||||
|
@ -1950,7 +1950,9 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
|
|||
ret = nand_boot_init(this);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
chip->scan_bbt(mtd);
|
||||
ret = chip->scan_bbt(mtd);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
ppdata.of_node = this->pdev->dev.of_node;
|
||||
ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
|
||||
|
|
|
@ -386,26 +386,51 @@ static irqreturn_t mxc_nfc_irq(int irq, void *dev_id)
|
|||
/* This function polls the NANDFC to wait for the basic operation to
|
||||
* complete by checking the INT bit of config2 register.
|
||||
*/
|
||||
static void wait_op_done(struct mxc_nand_host *host, int useirq)
|
||||
static int wait_op_done(struct mxc_nand_host *host, int useirq)
|
||||
{
|
||||
int max_retries = 8000;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* If operation is already complete, don't bother to setup an irq or a
|
||||
* loop.
|
||||
*/
|
||||
if (host->devtype_data->check_int(host))
|
||||
return 0;
|
||||
|
||||
if (useirq) {
|
||||
if (!host->devtype_data->check_int(host)) {
|
||||
reinit_completion(&host->op_completion);
|
||||
irq_control(host, 1);
|
||||
wait_for_completion(&host->op_completion);
|
||||
unsigned long timeout;
|
||||
|
||||
reinit_completion(&host->op_completion);
|
||||
|
||||
irq_control(host, 1);
|
||||
|
||||
timeout = wait_for_completion_timeout(&host->op_completion, HZ);
|
||||
if (!timeout && !host->devtype_data->check_int(host)) {
|
||||
dev_dbg(host->dev, "timeout waiting for irq\n");
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
} else {
|
||||
while (max_retries-- > 0) {
|
||||
if (host->devtype_data->check_int(host))
|
||||
int max_retries = 8000;
|
||||
int done;
|
||||
|
||||
do {
|
||||
udelay(1);
|
||||
|
||||
done = host->devtype_data->check_int(host);
|
||||
if (done)
|
||||
break;
|
||||
|
||||
udelay(1);
|
||||
} while (--max_retries);
|
||||
|
||||
if (!done) {
|
||||
dev_dbg(host->dev, "timeout polling for completion\n");
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
if (max_retries < 0)
|
||||
pr_debug("%s: INT not set\n", __func__);
|
||||
}
|
||||
|
||||
WARN_ONCE(ret < 0, "timeout! useirq=%d\n", useirq);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void send_cmd_v3(struct mxc_nand_host *host, uint16_t cmd, int useirq)
|
||||
|
@ -527,30 +552,17 @@ static void send_page_v1(struct mtd_info *mtd, unsigned int ops)
|
|||
|
||||
static void send_read_id_v3(struct mxc_nand_host *host)
|
||||
{
|
||||
struct nand_chip *this = &host->nand;
|
||||
|
||||
/* Read ID into main buffer */
|
||||
writel(NFC_ID, NFC_V3_LAUNCH);
|
||||
|
||||
wait_op_done(host, true);
|
||||
|
||||
memcpy32_fromio(host->data_buf, host->main_area0, 16);
|
||||
|
||||
if (this->options & NAND_BUSWIDTH_16) {
|
||||
/* compress the ID info */
|
||||
host->data_buf[1] = host->data_buf[2];
|
||||
host->data_buf[2] = host->data_buf[4];
|
||||
host->data_buf[3] = host->data_buf[6];
|
||||
host->data_buf[4] = host->data_buf[8];
|
||||
host->data_buf[5] = host->data_buf[10];
|
||||
}
|
||||
}
|
||||
|
||||
/* Request the NANDFC to perform a read of the NAND device ID. */
|
||||
static void send_read_id_v1_v2(struct mxc_nand_host *host)
|
||||
{
|
||||
struct nand_chip *this = &host->nand;
|
||||
|
||||
/* NANDFC buffer 0 is used for device ID output */
|
||||
writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR);
|
||||
|
||||
|
@ -560,15 +572,6 @@ static void send_read_id_v1_v2(struct mxc_nand_host *host)
|
|||
wait_op_done(host, true);
|
||||
|
||||
memcpy32_fromio(host->data_buf, host->main_area0, 16);
|
||||
|
||||
if (this->options & NAND_BUSWIDTH_16) {
|
||||
/* compress the ID info */
|
||||
host->data_buf[1] = host->data_buf[2];
|
||||
host->data_buf[2] = host->data_buf[4];
|
||||
host->data_buf[3] = host->data_buf[6];
|
||||
host->data_buf[4] = host->data_buf[8];
|
||||
host->data_buf[5] = host->data_buf[10];
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t get_dev_status_v3(struct mxc_nand_host *host)
|
||||
|
@ -694,9 +697,17 @@ static u_char mxc_nand_read_byte(struct mtd_info *mtd)
|
|||
if (host->status_request)
|
||||
return host->devtype_data->get_dev_status(host) & 0xFF;
|
||||
|
||||
ret = *(uint8_t *)(host->data_buf + host->buf_start);
|
||||
host->buf_start++;
|
||||
if (nand_chip->options & NAND_BUSWIDTH_16) {
|
||||
/* only take the lower byte of each word */
|
||||
ret = *(uint16_t *)(host->data_buf + host->buf_start);
|
||||
|
||||
host->buf_start += 2;
|
||||
} else {
|
||||
ret = *(uint8_t *)(host->data_buf + host->buf_start);
|
||||
host->buf_start++;
|
||||
}
|
||||
|
||||
pr_debug("%s: ret=0x%hhx (start=%u)\n", __func__, ret, host->buf_start);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -825,6 +836,12 @@ static void copy_spare(struct mtd_info *mtd, bool bfrom)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* MXC NANDFC can only perform full page+spare or spare-only read/write. When
|
||||
* the upper layers perform a read/write buf operation, the saved column address
|
||||
* is used to index into the full page. So usually this function is called with
|
||||
* column == 0 (unless no column cycle is needed indicated by column == -1)
|
||||
*/
|
||||
static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr)
|
||||
{
|
||||
struct nand_chip *nand_chip = mtd->priv;
|
||||
|
@ -832,16 +849,13 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr)
|
|||
|
||||
/* Write out column address, if necessary */
|
||||
if (column != -1) {
|
||||
/*
|
||||
* MXC NANDFC can only perform full page+spare or
|
||||
* spare-only read/write. When the upper layers
|
||||
* perform a read/write buf operation, the saved column
|
||||
* address is used to index into the full page.
|
||||
*/
|
||||
host->devtype_data->send_addr(host, 0, page_addr == -1);
|
||||
host->devtype_data->send_addr(host, column & 0xff,
|
||||
page_addr == -1);
|
||||
if (mtd->writesize > 512)
|
||||
/* another col addr cycle for 2k page */
|
||||
host->devtype_data->send_addr(host, 0, false);
|
||||
host->devtype_data->send_addr(host,
|
||||
(column >> 8) & 0xff,
|
||||
false);
|
||||
}
|
||||
|
||||
/* Write out page address, if necessary */
|
||||
|
@ -903,7 +917,7 @@ static void preset_v1(struct mtd_info *mtd)
|
|||
struct mxc_nand_host *host = nand_chip->priv;
|
||||
uint16_t config1 = 0;
|
||||
|
||||
if (nand_chip->ecc.mode == NAND_ECC_HW)
|
||||
if (nand_chip->ecc.mode == NAND_ECC_HW && mtd->writesize)
|
||||
config1 |= NFC_V1_V2_CONFIG1_ECC_EN;
|
||||
|
||||
if (!host->devtype_data->irqpending_quirk)
|
||||
|
@ -931,9 +945,6 @@ static void preset_v2(struct mtd_info *mtd)
|
|||
struct mxc_nand_host *host = nand_chip->priv;
|
||||
uint16_t config1 = 0;
|
||||
|
||||
if (nand_chip->ecc.mode == NAND_ECC_HW)
|
||||
config1 |= NFC_V1_V2_CONFIG1_ECC_EN;
|
||||
|
||||
config1 |= NFC_V2_CONFIG1_FP_INT;
|
||||
|
||||
if (!host->devtype_data->irqpending_quirk)
|
||||
|
@ -942,6 +953,9 @@ static void preset_v2(struct mtd_info *mtd)
|
|||
if (mtd->writesize) {
|
||||
uint16_t pages_per_block = mtd->erasesize / mtd->writesize;
|
||||
|
||||
if (nand_chip->ecc.mode == NAND_ECC_HW)
|
||||
config1 |= NFC_V1_V2_CONFIG1_ECC_EN;
|
||||
|
||||
host->eccsize = get_eccsize(mtd);
|
||||
if (host->eccsize == 4)
|
||||
config1 |= NFC_V2_CONFIG1_ECC_MODE_4;
|
||||
|
@ -999,9 +1013,6 @@ static void preset_v3(struct mtd_info *mtd)
|
|||
NFC_V3_CONFIG2_INT_MSK |
|
||||
NFC_V3_CONFIG2_NUM_ADDR_PHASE0;
|
||||
|
||||
if (chip->ecc.mode == NAND_ECC_HW)
|
||||
config2 |= NFC_V3_CONFIG2_ECC_EN;
|
||||
|
||||
addr_phases = fls(chip->pagemask) >> 3;
|
||||
|
||||
if (mtd->writesize == 2048) {
|
||||
|
@ -1016,6 +1027,9 @@ static void preset_v3(struct mtd_info *mtd)
|
|||
}
|
||||
|
||||
if (mtd->writesize) {
|
||||
if (chip->ecc.mode == NAND_ECC_HW)
|
||||
config2 |= NFC_V3_CONFIG2_ECC_EN;
|
||||
|
||||
config2 |= NFC_V3_CONFIG2_PPB(
|
||||
ffs(mtd->erasesize / mtd->writesize) - 6,
|
||||
host->devtype_data->ppb_shift);
|
||||
|
@ -1066,6 +1080,9 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
|
|||
host->status_request = true;
|
||||
|
||||
host->devtype_data->send_cmd(host, command, true);
|
||||
WARN_ONCE(column != -1 || page_addr != -1,
|
||||
"Unexpected column/row value (cmd=%u, col=%d, row=%d)\n",
|
||||
command, column, page_addr);
|
||||
mxc_do_addr_cycle(mtd, column, page_addr);
|
||||
break;
|
||||
|
||||
|
@ -1079,7 +1096,10 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
|
|||
command = NAND_CMD_READ0; /* only READ0 is valid */
|
||||
|
||||
host->devtype_data->send_cmd(host, command, false);
|
||||
mxc_do_addr_cycle(mtd, column, page_addr);
|
||||
WARN_ONCE(column < 0,
|
||||
"Unexpected column/row value (cmd=%u, col=%d, row=%d)\n",
|
||||
command, column, page_addr);
|
||||
mxc_do_addr_cycle(mtd, 0, page_addr);
|
||||
|
||||
if (mtd->writesize > 512)
|
||||
host->devtype_data->send_cmd(host,
|
||||
|
@ -1100,7 +1120,10 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
|
|||
host->buf_start = column;
|
||||
|
||||
host->devtype_data->send_cmd(host, command, false);
|
||||
mxc_do_addr_cycle(mtd, column, page_addr);
|
||||
WARN_ONCE(column < -1,
|
||||
"Unexpected column/row value (cmd=%u, col=%d, row=%d)\n",
|
||||
command, column, page_addr);
|
||||
mxc_do_addr_cycle(mtd, 0, page_addr);
|
||||
break;
|
||||
|
||||
case NAND_CMD_PAGEPROG:
|
||||
|
@ -1108,6 +1131,9 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
|
|||
copy_spare(mtd, false);
|
||||
host->devtype_data->send_page(mtd, NFC_INPUT);
|
||||
host->devtype_data->send_cmd(host, command, true);
|
||||
WARN_ONCE(column != -1 || page_addr != -1,
|
||||
"Unexpected column/row value (cmd=%u, col=%d, row=%d)\n",
|
||||
command, column, page_addr);
|
||||
mxc_do_addr_cycle(mtd, column, page_addr);
|
||||
break;
|
||||
|
||||
|
@ -1115,15 +1141,29 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
|
|||
host->devtype_data->send_cmd(host, command, true);
|
||||
mxc_do_addr_cycle(mtd, column, page_addr);
|
||||
host->devtype_data->send_read_id(host);
|
||||
host->buf_start = column;
|
||||
host->buf_start = 0;
|
||||
break;
|
||||
|
||||
case NAND_CMD_ERASE1:
|
||||
case NAND_CMD_ERASE2:
|
||||
host->devtype_data->send_cmd(host, command, false);
|
||||
WARN_ONCE(column != -1,
|
||||
"Unexpected column value (cmd=%u, col=%d)\n",
|
||||
command, column);
|
||||
mxc_do_addr_cycle(mtd, column, page_addr);
|
||||
|
||||
break;
|
||||
case NAND_CMD_PARAM:
|
||||
host->devtype_data->send_cmd(host, command, false);
|
||||
mxc_do_addr_cycle(mtd, column, page_addr);
|
||||
host->devtype_data->send_page(mtd, NFC_OUTPUT);
|
||||
memcpy32_fromio(host->data_buf, host->main_area0, 512);
|
||||
host->buf_start = 0;
|
||||
break;
|
||||
default:
|
||||
WARN_ONCE(1, "Unimplemented command (cmd=%u)\n",
|
||||
command);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -386,7 +386,7 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
|||
uint8_t buf[2] = { 0, 0 };
|
||||
int ret = 0, res, i = 0;
|
||||
|
||||
ops.datbuf = NULL;
|
||||
memset(&ops, 0, sizeof(ops));
|
||||
ops.oobbuf = buf;
|
||||
ops.ooboffs = chip->badblockpos;
|
||||
if (chip->options & NAND_BUSWIDTH_16) {
|
||||
|
@ -565,6 +565,25 @@ void nand_wait_ready(struct mtd_info *mtd)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(nand_wait_ready);
|
||||
|
||||
/**
|
||||
* nand_wait_status_ready - [GENERIC] Wait for the ready status after commands.
|
||||
* @mtd: MTD device structure
|
||||
* @timeo: Timeout in ms
|
||||
*
|
||||
* Wait for status ready (i.e. command done) or timeout.
|
||||
*/
|
||||
static void nand_wait_status_ready(struct mtd_info *mtd, unsigned long timeo)
|
||||
{
|
||||
register struct nand_chip *chip = mtd->priv;
|
||||
|
||||
timeo = jiffies + msecs_to_jiffies(timeo);
|
||||
do {
|
||||
if ((chip->read_byte(mtd) & NAND_STATUS_READY))
|
||||
break;
|
||||
touch_softlockup_watchdog();
|
||||
} while (time_before(jiffies, timeo));
|
||||
};
|
||||
|
||||
/**
|
||||
* nand_command - [DEFAULT] Send command to NAND device
|
||||
* @mtd: MTD device structure
|
||||
|
@ -643,8 +662,8 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
|
|||
NAND_CTRL_CLE | NAND_CTRL_CHANGE);
|
||||
chip->cmd_ctrl(mtd,
|
||||
NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
|
||||
while (!(chip->read_byte(mtd) & NAND_STATUS_READY))
|
||||
;
|
||||
/* EZ-NAND can take upto 250ms as per ONFi v4.0 */
|
||||
nand_wait_status_ready(mtd, 250);
|
||||
return;
|
||||
|
||||
/* This applies to read commands */
|
||||
|
@ -740,8 +759,8 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
|
|||
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
|
||||
chip->cmd_ctrl(mtd, NAND_CMD_NONE,
|
||||
NAND_NCE | NAND_CTRL_CHANGE);
|
||||
while (!(chip->read_byte(mtd) & NAND_STATUS_READY))
|
||||
;
|
||||
/* EZ-NAND can take upto 250ms as per ONFi v4.0 */
|
||||
nand_wait_status_ready(mtd, 250);
|
||||
return;
|
||||
|
||||
case NAND_CMD_RNDOUT:
|
||||
|
@ -968,7 +987,7 @@ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
|||
__func__, (unsigned long long)ofs, len);
|
||||
|
||||
if (check_offs_len(mtd, ofs, len))
|
||||
ret = -EINVAL;
|
||||
return -EINVAL;
|
||||
|
||||
/* Align to last block address if size addresses end of the device */
|
||||
if (ofs + len == mtd->size)
|
||||
|
@ -1031,7 +1050,7 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
|||
__func__, (unsigned long long)ofs, len);
|
||||
|
||||
if (check_offs_len(mtd, ofs, len))
|
||||
ret = -EINVAL;
|
||||
return -EINVAL;
|
||||
|
||||
nand_get_device(mtd, FL_LOCKING);
|
||||
|
||||
|
@ -1716,9 +1735,9 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|||
int ret;
|
||||
|
||||
nand_get_device(mtd, FL_READING);
|
||||
memset(&ops, 0, sizeof(ops));
|
||||
ops.len = len;
|
||||
ops.datbuf = buf;
|
||||
ops.oobbuf = NULL;
|
||||
ops.mode = MTD_OPS_PLACE_OOB;
|
||||
ret = nand_do_read_ops(mtd, from, &ops);
|
||||
*retlen = ops.retlen;
|
||||
|
@ -2124,7 +2143,7 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
|
||||
|
||||
/**
|
||||
* nand_write_subpage_hwecc - [REPLACABLE] hardware ECC based subpage write
|
||||
* nand_write_subpage_hwecc - [REPLACEABLE] hardware ECC based subpage write
|
||||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @offset: column address of subpage within the page
|
||||
|
@ -2508,9 +2527,9 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
/* Grab the device */
|
||||
panic_nand_get_device(chip, mtd, FL_WRITING);
|
||||
|
||||
memset(&ops, 0, sizeof(ops));
|
||||
ops.len = len;
|
||||
ops.datbuf = (uint8_t *)buf;
|
||||
ops.oobbuf = NULL;
|
||||
ops.mode = MTD_OPS_PLACE_OOB;
|
||||
|
||||
ret = nand_do_write_ops(mtd, to, &ops);
|
||||
|
@ -2536,9 +2555,9 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
int ret;
|
||||
|
||||
nand_get_device(mtd, FL_WRITING);
|
||||
memset(&ops, 0, sizeof(ops));
|
||||
ops.len = len;
|
||||
ops.datbuf = (uint8_t *)buf;
|
||||
ops.oobbuf = NULL;
|
||||
ops.mode = MTD_OPS_PLACE_OOB;
|
||||
ret = nand_do_write_ops(mtd, to, &ops);
|
||||
*retlen = ops.retlen;
|
||||
|
|
|
@ -38,8 +38,8 @@
|
|||
|
||||
#include <linux/platform_data/mtd-nand-pxa3xx.h>
|
||||
|
||||
#define CHIP_DELAY_TIMEOUT (2 * HZ/10)
|
||||
#define NAND_STOP_DELAY (2 * HZ/50)
|
||||
#define CHIP_DELAY_TIMEOUT msecs_to_jiffies(200)
|
||||
#define NAND_STOP_DELAY msecs_to_jiffies(40)
|
||||
#define PAGE_CHUNK_SIZE (2048)
|
||||
|
||||
/*
|
||||
|
@ -605,11 +605,24 @@ static void start_data_dma(struct pxa3xx_nand_info *info)
|
|||
{}
|
||||
#endif
|
||||
|
||||
static irqreturn_t pxa3xx_nand_irq_thread(int irq, void *data)
|
||||
{
|
||||
struct pxa3xx_nand_info *info = data;
|
||||
|
||||
handle_data_pio(info);
|
||||
|
||||
info->state = STATE_CMD_DONE;
|
||||
nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
|
||||
{
|
||||
struct pxa3xx_nand_info *info = devid;
|
||||
unsigned int status, is_completed = 0, is_ready = 0;
|
||||
unsigned int ready, cmd_done;
|
||||
irqreturn_t ret = IRQ_HANDLED;
|
||||
|
||||
if (info->cs == 0) {
|
||||
ready = NDSR_FLASH_RDY;
|
||||
|
@ -651,7 +664,8 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
|
|||
} else {
|
||||
info->state = (status & NDSR_RDDREQ) ?
|
||||
STATE_PIO_READING : STATE_PIO_WRITING;
|
||||
handle_data_pio(info);
|
||||
ret = IRQ_WAKE_THREAD;
|
||||
goto NORMAL_IRQ_EXIT;
|
||||
}
|
||||
}
|
||||
if (status & cmd_done) {
|
||||
|
@ -692,7 +706,7 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
|
|||
if (is_ready)
|
||||
complete(&info->dev_ready);
|
||||
NORMAL_IRQ_EXIT:
|
||||
return IRQ_HANDLED;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int is_buf_blank(uint8_t *buf, size_t len)
|
||||
|
@ -951,7 +965,7 @@ static void nand_cmdfunc(struct mtd_info *mtd, unsigned command,
|
|||
{
|
||||
struct pxa3xx_nand_host *host = mtd->priv;
|
||||
struct pxa3xx_nand_info *info = host->info_data;
|
||||
int ret, exec_cmd;
|
||||
int exec_cmd;
|
||||
|
||||
/*
|
||||
* if this is a x16 device ,then convert the input
|
||||
|
@ -983,9 +997,8 @@ static void nand_cmdfunc(struct mtd_info *mtd, unsigned command,
|
|||
info->need_wait = 1;
|
||||
pxa3xx_nand_start(info);
|
||||
|
||||
ret = wait_for_completion_timeout(&info->cmd_complete,
|
||||
CHIP_DELAY_TIMEOUT);
|
||||
if (!ret) {
|
||||
if (!wait_for_completion_timeout(&info->cmd_complete,
|
||||
CHIP_DELAY_TIMEOUT)) {
|
||||
dev_err(&info->pdev->dev, "Wait time out!!!\n");
|
||||
/* Stop State Machine for next command cycle */
|
||||
pxa3xx_nand_stop(info);
|
||||
|
@ -1000,7 +1013,7 @@ static void nand_cmdfunc_extended(struct mtd_info *mtd,
|
|||
{
|
||||
struct pxa3xx_nand_host *host = mtd->priv;
|
||||
struct pxa3xx_nand_info *info = host->info_data;
|
||||
int ret, exec_cmd, ext_cmd_type;
|
||||
int exec_cmd, ext_cmd_type;
|
||||
|
||||
/*
|
||||
* if this is a x16 device then convert the input
|
||||
|
@ -1063,9 +1076,8 @@ static void nand_cmdfunc_extended(struct mtd_info *mtd,
|
|||
init_completion(&info->cmd_complete);
|
||||
pxa3xx_nand_start(info);
|
||||
|
||||
ret = wait_for_completion_timeout(&info->cmd_complete,
|
||||
CHIP_DELAY_TIMEOUT);
|
||||
if (!ret) {
|
||||
if (!wait_for_completion_timeout(&info->cmd_complete,
|
||||
CHIP_DELAY_TIMEOUT)) {
|
||||
dev_err(&info->pdev->dev, "Wait time out!!!\n");
|
||||
/* Stop State Machine for next command cycle */
|
||||
pxa3xx_nand_stop(info);
|
||||
|
@ -1198,13 +1210,11 @@ static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
|
|||
{
|
||||
struct pxa3xx_nand_host *host = mtd->priv;
|
||||
struct pxa3xx_nand_info *info = host->info_data;
|
||||
int ret;
|
||||
|
||||
if (info->need_wait) {
|
||||
ret = wait_for_completion_timeout(&info->dev_ready,
|
||||
CHIP_DELAY_TIMEOUT);
|
||||
info->need_wait = 0;
|
||||
if (!ret) {
|
||||
if (!wait_for_completion_timeout(&info->dev_ready,
|
||||
CHIP_DELAY_TIMEOUT)) {
|
||||
dev_err(&info->pdev->dev, "Ready time out!!!\n");
|
||||
return NAND_STATUS_FAIL;
|
||||
}
|
||||
|
@ -1508,6 +1518,8 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
|
|||
return ret;
|
||||
}
|
||||
|
||||
memset(pxa3xx_flash_ids, 0, sizeof(pxa3xx_flash_ids));
|
||||
|
||||
pxa3xx_flash_ids[0].name = f->name;
|
||||
pxa3xx_flash_ids[0].dev_id = (f->chip_id >> 8) & 0xffff;
|
||||
pxa3xx_flash_ids[0].pagesize = f->page_size;
|
||||
|
@ -1710,7 +1722,9 @@ static int alloc_nand_resource(struct platform_device *pdev)
|
|||
/* initialize all interrupts to be disabled */
|
||||
disable_int(info, NDSR_MASK);
|
||||
|
||||
ret = request_irq(irq, pxa3xx_nand_irq, 0, pdev->name, info);
|
||||
ret = request_threaded_irq(irq, pxa3xx_nand_irq,
|
||||
pxa3xx_nand_irq_thread, IRQF_ONESHOT,
|
||||
pdev->name, info);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to request IRQ\n");
|
||||
goto fail_free_buf;
|
||||
|
|
|
@ -948,8 +948,6 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
|
|||
|
||||
cpu_type = platform_get_device_id(pdev)->driver_data;
|
||||
|
||||
pr_debug("s3c2410_nand_probe(%p)\n", pdev);
|
||||
|
||||
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (info == NULL) {
|
||||
err = -ENOMEM;
|
||||
|
@ -1045,7 +1043,6 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
|
|||
s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND);
|
||||
}
|
||||
|
||||
pr_debug("initialised ok\n");
|
||||
return 0;
|
||||
|
||||
exit_error:
|
||||
|
|
|
@ -1743,7 +1743,6 @@ static int onenand_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
struct onenand_chip *this = mtd->priv;
|
||||
int column, subpage;
|
||||
int written = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (this->state == FL_PM_SUSPENDED)
|
||||
return -EBUSY;
|
||||
|
@ -1786,15 +1785,10 @@ static int onenand_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
onenand_panic_wait(mtd);
|
||||
|
||||
/* In partial page write we don't update bufferram */
|
||||
onenand_update_bufferram(mtd, to, !ret && !subpage);
|
||||
onenand_update_bufferram(mtd, to, !subpage);
|
||||
if (ONENAND_IS_2PLANE(this)) {
|
||||
ONENAND_SET_BUFFERRAM1(this);
|
||||
onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
printk(KERN_ERR "%s: write failed %d\n", __func__, ret);
|
||||
break;
|
||||
onenand_update_bufferram(mtd, to + this->writesize, !subpage);
|
||||
}
|
||||
|
||||
written += thislen;
|
||||
|
@ -1808,7 +1802,7 @@ static int onenand_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
}
|
||||
|
||||
*retlen = written;
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -460,8 +460,7 @@ fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len)
|
|||
writel((seqid << QUADSPI_IPCR_SEQID_SHIFT) | len, base + QUADSPI_IPCR);
|
||||
|
||||
/* Wait for the interrupt. */
|
||||
err = wait_for_completion_timeout(&q->c, msecs_to_jiffies(1000));
|
||||
if (!err) {
|
||||
if (!wait_for_completion_timeout(&q->c, msecs_to_jiffies(1000))) {
|
||||
dev_err(q->dev,
|
||||
"cmd 0x%.2x timeout, addr@%.8x, FR:0x%.8x, SR:0x%.8x\n",
|
||||
cmd, addr, readl(base + QUADSPI_FR),
|
||||
|
@ -830,27 +829,27 @@ static int fsl_qspi_probe(struct platform_device *pdev)
|
|||
|
||||
ret = clk_prepare_enable(q->clk_en);
|
||||
if (ret) {
|
||||
dev_err(dev, "can not enable the qspi_en clock\n");
|
||||
dev_err(dev, "cannot enable the qspi_en clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(q->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "can not enable the qspi clock\n");
|
||||
dev_err(dev, "cannot enable the qspi clock: %d\n", ret);
|
||||
goto clk_failed;
|
||||
}
|
||||
|
||||
/* find the irq */
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to get the irq\n");
|
||||
dev_err(dev, "failed to get the irq: %d\n", ret);
|
||||
goto irq_failed;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, ret,
|
||||
fsl_qspi_irq_handler, 0, pdev->name, q);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request irq.\n");
|
||||
dev_err(dev, "failed to request irq: %d\n", ret);
|
||||
goto irq_failed;
|
||||
}
|
||||
|
||||
|
|
|
@ -369,17 +369,13 @@ erase_err:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
|
||||
{
|
||||
struct spi_nor *nor = mtd_to_spi_nor(mtd);
|
||||
struct mtd_info *mtd = nor->mtd;
|
||||
uint32_t offset = ofs;
|
||||
uint8_t status_old, status_new;
|
||||
int ret = 0;
|
||||
|
||||
ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_LOCK);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
status_old = read_sr(nor);
|
||||
|
||||
if (offset < mtd->size - (mtd->size / 2))
|
||||
|
@ -402,26 +398,18 @@ static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
|||
(status_old & (SR_BP2 | SR_BP1 | SR_BP0))) {
|
||||
write_enable(nor);
|
||||
ret = write_sr(nor, status_new);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
err:
|
||||
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
|
||||
{
|
||||
struct spi_nor *nor = mtd_to_spi_nor(mtd);
|
||||
struct mtd_info *mtd = nor->mtd;
|
||||
uint32_t offset = ofs;
|
||||
uint8_t status_old, status_new;
|
||||
int ret = 0;
|
||||
|
||||
ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_UNLOCK);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
status_old = read_sr(nor);
|
||||
|
||||
if (offset+len > mtd->size - (mtd->size / 64))
|
||||
|
@ -444,15 +432,41 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
|||
(status_old & (SR_BP2 | SR_BP1 | SR_BP0))) {
|
||||
write_enable(nor);
|
||||
ret = write_sr(nor, status_new);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
{
|
||||
struct spi_nor *nor = mtd_to_spi_nor(mtd);
|
||||
int ret;
|
||||
|
||||
ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_LOCK);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = nor->flash_lock(nor, ofs, len);
|
||||
|
||||
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_UNLOCK);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
{
|
||||
struct spi_nor *nor = mtd_to_spi_nor(mtd);
|
||||
int ret;
|
||||
|
||||
ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_UNLOCK);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = nor->flash_unlock(nor, ofs, len);
|
||||
|
||||
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Used when the "_ext_id" is two bytes at most */
|
||||
#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
|
||||
((kernel_ulong_t)&(struct flash_info) { \
|
||||
|
@ -524,6 +538,7 @@ static const struct spi_device_id spi_nor_ids[] = {
|
|||
{ "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
{ "en25qh128", INFO(0x1c7018, 0, 64 * 1024, 256, 0) },
|
||||
{ "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) },
|
||||
{ "en25s64", INFO(0x1c3817, 0, 64 * 1024, 128, 0) },
|
||||
|
||||
/* ESMT */
|
||||
{ "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K) },
|
||||
|
@ -553,6 +568,7 @@ static const struct spi_device_id spi_nor_ids[] = {
|
|||
{ "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, 0) },
|
||||
{ "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) },
|
||||
{ "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, 0) },
|
||||
{ "mx25u6435f", INFO(0xc22537, 0, 64 * 1024, 128, SECT_4K) },
|
||||
{ "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) },
|
||||
{ "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },
|
||||
{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
|
||||
|
@ -648,6 +664,7 @@ static const struct spi_device_id spi_nor_ids[] = {
|
|||
{ "m25px80", INFO(0x207114, 0, 64 * 1024, 16, 0) },
|
||||
|
||||
/* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
|
||||
{ "w25x05", INFO(0xef3010, 0, 64 * 1024, 1, SECT_4K) },
|
||||
{ "w25x10", INFO(0xef3011, 0, 64 * 1024, 2, SECT_4K) },
|
||||
{ "w25x20", INFO(0xef3012, 0, 64 * 1024, 4, SECT_4K) },
|
||||
{ "w25x40", INFO(0xef3013, 0, 64 * 1024, 8, SECT_4K) },
|
||||
|
@ -658,6 +675,7 @@ static const struct spi_device_id spi_nor_ids[] = {
|
|||
{ "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, SECT_4K) },
|
||||
{ "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
{ "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
{ "w25q64dw", INFO(0xef6017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
{ "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) },
|
||||
{ "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
|
||||
{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
|
||||
|
@ -1045,6 +1063,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
|||
|
||||
/* nor protection support for STmicro chips */
|
||||
if (JEDEC_MFR(info) == CFI_MFR_ST) {
|
||||
nor->flash_lock = stm_lock;
|
||||
nor->flash_unlock = stm_unlock;
|
||||
}
|
||||
|
||||
if (nor->flash_lock && nor->flash_unlock) {
|
||||
mtd->_lock = spi_nor_lock;
|
||||
mtd->_unlock = spi_nor_unlock;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
|
||||
#include "mtd_test.h"
|
||||
|
||||
/*
|
||||
* Test the implementation for software ECC
|
||||
*
|
||||
|
@ -274,6 +276,10 @@ static int nand_ecc_test_run(const size_t size)
|
|||
}
|
||||
pr_info("ok - %s-%zd\n",
|
||||
nand_ecc_test[i].name, size);
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
error:
|
||||
kfree(error_data);
|
||||
|
|
|
@ -1,4 +1,16 @@
|
|||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
static inline int mtdtest_relax(void)
|
||||
{
|
||||
cond_resched();
|
||||
if (signal_pending(current)) {
|
||||
pr_info("aborting test due to pending signal!\n");
|
||||
return -EINTR;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mtdtest_erase_eraseblock(struct mtd_info *mtd, unsigned int ebnum);
|
||||
int mtdtest_scan_for_bad_eraseblocks(struct mtd_info *mtd, unsigned char *bbt,
|
||||
|
|
|
@ -320,6 +320,10 @@ static int overwrite_test(void)
|
|||
break;
|
||||
}
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
break;
|
||||
|
||||
opno++;
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ static int write_eraseblock(int ebnum)
|
|||
int i;
|
||||
struct mtd_oob_ops ops;
|
||||
int err = 0;
|
||||
loff_t addr = ebnum * mtd->erasesize;
|
||||
loff_t addr = (loff_t)ebnum * mtd->erasesize;
|
||||
|
||||
prandom_bytes_state(&rnd_state, writebuf, use_len_max * pgcnt);
|
||||
for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
|
||||
|
@ -112,7 +112,10 @@ static int write_whole_device(void)
|
|||
return err;
|
||||
if (i % 256 == 0)
|
||||
pr_info("written up to eraseblock %u\n", i);
|
||||
cond_resched();
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
pr_info("written %u eraseblocks\n", i);
|
||||
return 0;
|
||||
|
@ -141,6 +144,31 @@ static size_t memcmpshow(loff_t addr, const void *cs, const void *ct, size_t cou
|
|||
return bitflips;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare with 0xff and show the address, offset and data bytes at
|
||||
* comparison failure. Return number of bitflips encountered.
|
||||
*/
|
||||
static size_t memffshow(loff_t addr, loff_t offset, const void *cs,
|
||||
size_t count)
|
||||
{
|
||||
const unsigned char *su1;
|
||||
int res;
|
||||
size_t i = 0;
|
||||
size_t bitflips = 0;
|
||||
|
||||
for (su1 = cs; 0 < count; ++su1, count--, i++) {
|
||||
res = *su1 ^ 0xff;
|
||||
if (res) {
|
||||
pr_info("error @addr[0x%lx:0x%lx] 0x%x -> 0xff diff 0x%x\n",
|
||||
(unsigned long)addr, (unsigned long)offset + i,
|
||||
*su1, res);
|
||||
bitflips += hweight8(res);
|
||||
}
|
||||
}
|
||||
|
||||
return bitflips;
|
||||
}
|
||||
|
||||
static int verify_eraseblock(int ebnum)
|
||||
{
|
||||
int i;
|
||||
|
@ -203,6 +231,15 @@ static int verify_eraseblock(int ebnum)
|
|||
bitflips = memcmpshow(addr, readbuf + use_offset,
|
||||
writebuf + (use_len_max * i) + use_offset,
|
||||
use_len);
|
||||
|
||||
/* verify pre-offset area for 0xff */
|
||||
bitflips += memffshow(addr, 0, readbuf, use_offset);
|
||||
|
||||
/* verify post-(use_offset + use_len) area for 0xff */
|
||||
k = use_offset + use_len;
|
||||
bitflips += memffshow(addr, k, readbuf + k,
|
||||
mtd->ecclayout->oobavail - k);
|
||||
|
||||
if (bitflips > bitflip_limit) {
|
||||
pr_err("error: verify failed at %#llx\n",
|
||||
(long long)addr);
|
||||
|
@ -212,34 +249,8 @@ static int verify_eraseblock(int ebnum)
|
|||
return -1;
|
||||
}
|
||||
} else if (bitflips) {
|
||||
pr_info("ignoring error as within bitflip_limit\n");
|
||||
pr_info("ignoring errors as within bitflip limit\n");
|
||||
}
|
||||
|
||||
for (k = 0; k < use_offset; ++k)
|
||||
if (readbuf[k] != 0xff) {
|
||||
pr_err("error: verify 0xff "
|
||||
"failed at %#llx\n",
|
||||
(long long)addr);
|
||||
errcnt += 1;
|
||||
if (errcnt > 1000) {
|
||||
pr_err("error: too "
|
||||
"many errors\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
for (k = use_offset + use_len;
|
||||
k < mtd->ecclayout->oobavail; ++k)
|
||||
if (readbuf[k] != 0xff) {
|
||||
pr_err("error: verify 0xff "
|
||||
"failed at %#llx\n",
|
||||
(long long)addr);
|
||||
errcnt += 1;
|
||||
if (errcnt > 1000) {
|
||||
pr_err("error: too "
|
||||
"many errors\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (vary_offset)
|
||||
do_vary_offset();
|
||||
|
@ -310,7 +321,10 @@ static int verify_all_eraseblocks(void)
|
|||
return err;
|
||||
if (i % 256 == 0)
|
||||
pr_info("verified up to eraseblock %u\n", i);
|
||||
cond_resched();
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
pr_info("verified %u eraseblocks\n", i);
|
||||
return 0;
|
||||
|
@ -421,7 +435,10 @@ static int __init mtd_oobtest_init(void)
|
|||
goto out;
|
||||
if (i % 256 == 0)
|
||||
pr_info("verified up to eraseblock %u\n", i);
|
||||
cond_resched();
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
pr_info("verified %u eraseblocks\n", i);
|
||||
|
||||
|
@ -634,7 +651,11 @@ static int __init mtd_oobtest_init(void)
|
|||
goto out;
|
||||
if (i % 256 == 0)
|
||||
pr_info("written up to eraseblock %u\n", i);
|
||||
cond_resched();
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
addr += mtd->writesize;
|
||||
}
|
||||
}
|
||||
|
@ -672,7 +693,10 @@ static int __init mtd_oobtest_init(void)
|
|||
}
|
||||
if (i % 256 == 0)
|
||||
pr_info("verified up to eraseblock %u\n", i);
|
||||
cond_resched();
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
pr_info("verified %u eraseblocks\n", i);
|
||||
|
||||
|
|
|
@ -407,7 +407,10 @@ static int __init mtd_pagetest_init(void)
|
|||
goto out;
|
||||
if (i % 256 == 0)
|
||||
pr_info("written up to eraseblock %u\n", i);
|
||||
cond_resched();
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
pr_info("written %u eraseblocks\n", i);
|
||||
|
||||
|
@ -422,7 +425,10 @@ static int __init mtd_pagetest_init(void)
|
|||
goto out;
|
||||
if (i % 256 == 0)
|
||||
pr_info("verified up to eraseblock %u\n", i);
|
||||
cond_resched();
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
pr_info("verified %u eraseblocks\n", i);
|
||||
|
||||
|
|
|
@ -190,7 +190,10 @@ static int __init mtd_readtest_init(void)
|
|||
if (!err)
|
||||
err = ret;
|
||||
}
|
||||
cond_resched();
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (err)
|
||||
|
|
|
@ -185,7 +185,7 @@ static long calc_speed(void)
|
|||
(finish.tv_usec - start.tv_usec) / 1000;
|
||||
if (ms == 0)
|
||||
return 0;
|
||||
k = goodebcnt * (mtd->erasesize / 1024) * 1000;
|
||||
k = (uint64_t)goodebcnt * (mtd->erasesize / 1024) * 1000;
|
||||
do_div(k, ms);
|
||||
return k;
|
||||
}
|
||||
|
@ -269,7 +269,10 @@ static int __init mtd_speedtest_init(void)
|
|||
err = write_eraseblock(i);
|
||||
if (err)
|
||||
goto out;
|
||||
cond_resched();
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
stop_timing();
|
||||
speed = calc_speed();
|
||||
|
@ -284,7 +287,10 @@ static int __init mtd_speedtest_init(void)
|
|||
err = read_eraseblock(i);
|
||||
if (err)
|
||||
goto out;
|
||||
cond_resched();
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
stop_timing();
|
||||
speed = calc_speed();
|
||||
|
@ -303,7 +309,10 @@ static int __init mtd_speedtest_init(void)
|
|||
err = write_eraseblock_by_page(i);
|
||||
if (err)
|
||||
goto out;
|
||||
cond_resched();
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
stop_timing();
|
||||
speed = calc_speed();
|
||||
|
@ -318,7 +327,10 @@ static int __init mtd_speedtest_init(void)
|
|||
err = read_eraseblock_by_page(i);
|
||||
if (err)
|
||||
goto out;
|
||||
cond_resched();
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
stop_timing();
|
||||
speed = calc_speed();
|
||||
|
@ -337,7 +349,10 @@ static int __init mtd_speedtest_init(void)
|
|||
err = write_eraseblock_by_2pages(i);
|
||||
if (err)
|
||||
goto out;
|
||||
cond_resched();
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
stop_timing();
|
||||
speed = calc_speed();
|
||||
|
@ -352,7 +367,10 @@ static int __init mtd_speedtest_init(void)
|
|||
err = read_eraseblock_by_2pages(i);
|
||||
if (err)
|
||||
goto out;
|
||||
cond_resched();
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
stop_timing();
|
||||
speed = calc_speed();
|
||||
|
@ -385,7 +403,11 @@ static int __init mtd_speedtest_init(void)
|
|||
err = multiblock_erase(i, j);
|
||||
if (err)
|
||||
goto out;
|
||||
cond_resched();
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
i += j;
|
||||
}
|
||||
stop_timing();
|
||||
|
|
|
@ -96,7 +96,7 @@ static int do_read(void)
|
|||
if (offs + len > mtd->erasesize)
|
||||
len = mtd->erasesize - offs;
|
||||
}
|
||||
addr = eb * mtd->erasesize + offs;
|
||||
addr = (loff_t)eb * mtd->erasesize + offs;
|
||||
return mtdtest_read(mtd, addr, len, readbuf);
|
||||
}
|
||||
|
||||
|
@ -124,7 +124,7 @@ static int do_write(void)
|
|||
offsets[eb + 1] = 0;
|
||||
}
|
||||
}
|
||||
addr = eb * mtd->erasesize + offs;
|
||||
addr = (loff_t)eb * mtd->erasesize + offs;
|
||||
err = mtdtest_write(mtd, addr, len, writebuf);
|
||||
if (unlikely(err))
|
||||
return err;
|
||||
|
@ -221,7 +221,10 @@ static int __init mtd_stresstest_init(void)
|
|||
err = do_operation();
|
||||
if (err)
|
||||
goto out;
|
||||
cond_resched();
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
pr_info("finished, %d operations done\n", op);
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ static int write_eraseblock2(int ebnum)
|
|||
loff_t addr = (loff_t)ebnum * mtd->erasesize;
|
||||
|
||||
for (k = 1; k < 33; ++k) {
|
||||
if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize)
|
||||
if (addr + (subpgsize * k) > (loff_t)(ebnum + 1) * mtd->erasesize)
|
||||
break;
|
||||
prandom_bytes_state(&rnd_state, writebuf, subpgsize * k);
|
||||
err = mtd_write(mtd, addr, subpgsize * k, &written, writebuf);
|
||||
|
@ -195,7 +195,7 @@ static int verify_eraseblock2(int ebnum)
|
|||
loff_t addr = (loff_t)ebnum * mtd->erasesize;
|
||||
|
||||
for (k = 1; k < 33; ++k) {
|
||||
if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize)
|
||||
if (addr + (subpgsize * k) > (loff_t)(ebnum + 1) * mtd->erasesize)
|
||||
break;
|
||||
prandom_bytes_state(&rnd_state, writebuf, subpgsize * k);
|
||||
clear_data(readbuf, subpgsize * k);
|
||||
|
@ -269,7 +269,10 @@ static int verify_all_eraseblocks_ff(void)
|
|||
return err;
|
||||
if (i % 256 == 0)
|
||||
pr_info("verified up to eraseblock %u\n", i);
|
||||
cond_resched();
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
pr_info("verified %u eraseblocks\n", i);
|
||||
return 0;
|
||||
|
@ -346,7 +349,10 @@ static int __init mtd_subpagetest_init(void)
|
|||
goto out;
|
||||
if (i % 256 == 0)
|
||||
pr_info("written up to eraseblock %u\n", i);
|
||||
cond_resched();
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
pr_info("written %u eraseblocks\n", i);
|
||||
|
||||
|
@ -360,7 +366,10 @@ static int __init mtd_subpagetest_init(void)
|
|||
goto out;
|
||||
if (i % 256 == 0)
|
||||
pr_info("verified up to eraseblock %u\n", i);
|
||||
cond_resched();
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
pr_info("verified %u eraseblocks\n", i);
|
||||
|
||||
|
@ -383,7 +392,10 @@ static int __init mtd_subpagetest_init(void)
|
|||
goto out;
|
||||
if (i % 256 == 0)
|
||||
pr_info("written up to eraseblock %u\n", i);
|
||||
cond_resched();
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
pr_info("written %u eraseblocks\n", i);
|
||||
|
||||
|
@ -398,7 +410,10 @@ static int __init mtd_subpagetest_init(void)
|
|||
goto out;
|
||||
if (i % 256 == 0)
|
||||
pr_info("verified up to eraseblock %u\n", i);
|
||||
cond_resched();
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
pr_info("verified %u eraseblocks\n", i);
|
||||
|
||||
|
|
|
@ -101,11 +101,11 @@ static inline int check_eraseblock(int ebnum, unsigned char *buf)
|
|||
{
|
||||
int err, retries = 0;
|
||||
size_t read;
|
||||
loff_t addr = ebnum * mtd->erasesize;
|
||||
loff_t addr = (loff_t)ebnum * mtd->erasesize;
|
||||
size_t len = mtd->erasesize;
|
||||
|
||||
if (pgcnt) {
|
||||
addr = (ebnum + 1) * mtd->erasesize - pgcnt * pgsize;
|
||||
addr = (loff_t)(ebnum + 1) * mtd->erasesize - pgcnt * pgsize;
|
||||
len = pgcnt * pgsize;
|
||||
}
|
||||
|
||||
|
@ -155,11 +155,11 @@ static inline int write_pattern(int ebnum, void *buf)
|
|||
{
|
||||
int err;
|
||||
size_t written;
|
||||
loff_t addr = ebnum * mtd->erasesize;
|
||||
loff_t addr = (loff_t)ebnum * mtd->erasesize;
|
||||
size_t len = mtd->erasesize;
|
||||
|
||||
if (pgcnt) {
|
||||
addr = (ebnum + 1) * mtd->erasesize - pgcnt * pgsize;
|
||||
addr = (loff_t)(ebnum + 1) * mtd->erasesize - pgcnt * pgsize;
|
||||
len = pgcnt * pgsize;
|
||||
}
|
||||
err = mtd_write(mtd, addr, len, &written, buf);
|
||||
|
@ -279,7 +279,10 @@ static int __init tort_init(void)
|
|||
" for 0xFF... pattern\n");
|
||||
goto out;
|
||||
}
|
||||
cond_resched();
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -294,7 +297,10 @@ static int __init tort_init(void)
|
|||
err = write_pattern(i, patt);
|
||||
if (err)
|
||||
goto out;
|
||||
cond_resched();
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Verify what we wrote */
|
||||
|
@ -314,7 +320,10 @@ static int __init tort_init(void)
|
|||
"0x55AA55..." : "0xAA55AA...");
|
||||
goto out;
|
||||
}
|
||||
cond_resched();
|
||||
|
||||
err = mtdtest_relax();
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1266,7 +1266,6 @@ int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_
|
|||
if (rc) {
|
||||
JFFS2_WARNING("%s: jffs2_reserve_space_gc() = %d, request = %u\n",
|
||||
__func__, rc, totlen);
|
||||
rc = rc ? rc : -EBADFD;
|
||||
goto out;
|
||||
}
|
||||
rc = save_xattr_ref(c, ref);
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
/* ensure we never evaluate anything shorted than an unsigned long
|
||||
* to zero, and ensure we'll never miss the end of an comparison (bjd) */
|
||||
|
||||
#define map_calc_words(map) ((map_bankwidth(map) + (sizeof(unsigned long)-1))/ sizeof(unsigned long))
|
||||
#define map_calc_words(map) ((map_bankwidth(map) + (sizeof(unsigned long)-1)) / sizeof(unsigned long))
|
||||
|
||||
#ifdef CONFIG_MTD_MAP_BANK_WIDTH_8
|
||||
# ifdef map_bankwidth
|
||||
|
@ -181,7 +181,7 @@ static inline int map_bankwidth_supported(int w)
|
|||
}
|
||||
}
|
||||
|
||||
#define MAX_MAP_LONGS ( ((MAX_MAP_BANKWIDTH*8) + BITS_PER_LONG - 1) / BITS_PER_LONG )
|
||||
#define MAX_MAP_LONGS (((MAX_MAP_BANKWIDTH * 8) + BITS_PER_LONG - 1) / BITS_PER_LONG)
|
||||
|
||||
typedef union {
|
||||
unsigned long x[MAX_MAP_LONGS];
|
||||
|
@ -264,20 +264,22 @@ void unregister_mtd_chip_driver(struct mtd_chip_driver *);
|
|||
struct mtd_info *do_map_probe(const char *name, struct map_info *map);
|
||||
void map_destroy(struct mtd_info *mtd);
|
||||
|
||||
#define ENABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 1); } while(0)
|
||||
#define DISABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 0); } while(0)
|
||||
#define ENABLE_VPP(map) do { if (map->set_vpp) map->set_vpp(map, 1); } while (0)
|
||||
#define DISABLE_VPP(map) do { if (map->set_vpp) map->set_vpp(map, 0); } while (0)
|
||||
|
||||
#define INVALIDATE_CACHED_RANGE(map, from, size) \
|
||||
do { if(map->inval_cache) map->inval_cache(map, from, size); } while(0)
|
||||
do { if (map->inval_cache) map->inval_cache(map, from, size); } while (0)
|
||||
|
||||
|
||||
static inline int map_word_equal(struct map_info *map, map_word val1, map_word val2)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i<map_words(map); i++) {
|
||||
|
||||
for (i = 0; i < map_words(map); i++) {
|
||||
if (val1.x[i] != val2.x[i])
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -286,9 +288,9 @@ static inline map_word map_word_and(struct map_info *map, map_word val1, map_wor
|
|||
map_word r;
|
||||
int i;
|
||||
|
||||
for (i=0; i<map_words(map); i++) {
|
||||
for (i = 0; i < map_words(map); i++)
|
||||
r.x[i] = val1.x[i] & val2.x[i];
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -297,9 +299,9 @@ static inline map_word map_word_clr(struct map_info *map, map_word val1, map_wor
|
|||
map_word r;
|
||||
int i;
|
||||
|
||||
for (i=0; i<map_words(map); i++) {
|
||||
for (i = 0; i < map_words(map); i++)
|
||||
r.x[i] = val1.x[i] & ~val2.x[i];
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -308,22 +310,33 @@ static inline map_word map_word_or(struct map_info *map, map_word val1, map_word
|
|||
map_word r;
|
||||
int i;
|
||||
|
||||
for (i=0; i<map_words(map); i++) {
|
||||
for (i = 0; i < map_words(map); i++)
|
||||
r.x[i] = val1.x[i] | val2.x[i];
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
#define map_word_andequal(m, a, b, z) map_word_equal(m, z, map_word_and(m, a, b))
|
||||
static inline int map_word_andequal(struct map_info *map, map_word val1, map_word val2, map_word val3)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < map_words(map); i++) {
|
||||
if ((val1.x[i] & val2.x[i]) != val3.x[i])
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int map_word_bitsset(struct map_info *map, map_word val1, map_word val2)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<map_words(map); i++) {
|
||||
for (i = 0; i < map_words(map); i++) {
|
||||
if (val1.x[i] & val2.x[i])
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -355,14 +368,16 @@ static inline map_word map_word_load_partial(struct map_info *map, map_word orig
|
|||
|
||||
if (map_bankwidth_is_large(map)) {
|
||||
char *dest = (char *)&orig;
|
||||
|
||||
memcpy(dest+start, buf, len);
|
||||
} else {
|
||||
for (i=start; i < start+len; i++) {
|
||||
for (i = start; i < start+len; i++) {
|
||||
int bitpos;
|
||||
|
||||
#ifdef __LITTLE_ENDIAN
|
||||
bitpos = i*8;
|
||||
bitpos = i * 8;
|
||||
#else /* __BIG_ENDIAN */
|
||||
bitpos = (map_bankwidth(map)-1-i)*8;
|
||||
bitpos = (map_bankwidth(map) - 1 - i) * 8;
|
||||
#endif
|
||||
orig.x[0] &= ~(0xff << bitpos);
|
||||
orig.x[0] |= (unsigned long)buf[i-start] << bitpos;
|
||||
|
@ -384,9 +399,10 @@ static inline map_word map_word_ff(struct map_info *map)
|
|||
|
||||
if (map_bankwidth(map) < MAP_FF_LIMIT) {
|
||||
int bw = 8 * map_bankwidth(map);
|
||||
|
||||
r.x[0] = (1UL << bw) - 1;
|
||||
} else {
|
||||
for (i=0; i<map_words(map); i++)
|
||||
for (i = 0; i < map_words(map); i++)
|
||||
r.x[i] = ~0UL;
|
||||
}
|
||||
return r;
|
||||
|
@ -407,7 +423,7 @@ static inline map_word inline_map_read(struct map_info *map, unsigned long ofs)
|
|||
r.x[0] = __raw_readq(map->virt + ofs);
|
||||
#endif
|
||||
else if (map_bankwidth_is_large(map))
|
||||
memcpy_fromio(r.x, map->virt+ofs, map->bankwidth);
|
||||
memcpy_fromio(r.x, map->virt + ofs, map->bankwidth);
|
||||
else
|
||||
BUG();
|
||||
|
||||
|
|
|
@ -155,6 +155,8 @@ enum spi_nor_option_flags {
|
|||
* @write: [DRIVER-SPECIFIC] write data to the SPI NOR
|
||||
* @erase: [DRIVER-SPECIFIC] erase a sector of the SPI NOR
|
||||
* at the offset @offs
|
||||
* @lock: [FLASH-SPECIFIC] lock a region of the SPI NOR
|
||||
* @unlock: [FLASH-SPECIFIC] unlock a region of the SPI NOR
|
||||
* @priv: the private data
|
||||
*/
|
||||
struct spi_nor {
|
||||
|
@ -189,6 +191,9 @@ struct spi_nor {
|
|||
size_t len, size_t *retlen, const u_char *write_buf);
|
||||
int (*erase)(struct spi_nor *nor, loff_t offs);
|
||||
|
||||
int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
|
||||
int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
|
||||
|
||||
void *priv;
|
||||
};
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче