diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-04-22 22:00:44 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-04-22 22:00:44 +0300 |
commit | a62d016cece2fce1d5e4eedf36b17f03a7a5c78e (patch) | |
tree | ac37b4835be5f4fe0e04611fdb40c85abb98ec78 | |
parent | 7c034dfd58bbc056280262887acf5b7a98944d0a (diff) | |
parent | 3e550d2396d9deef77328237ed992e19dcfefca5 (diff) | |
download | linux-a62d016cece2fce1d5e4eedf36b17f03a7a5c78e.tar.xz |
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
...
42 files changed, 642 insertions, 320 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-mtd b/Documentation/ABI/testing/sysfs-class-mtd index 76ee192f80a0..3b5c3bca9186 100644 --- a/Documentation/ABI/testing/sysfs-class-mtd +++ b/Documentation/ABI/testing/sysfs-class-mtd @@ -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. diff --git a/Documentation/devicetree/bindings/mtd/m25p80.txt b/Documentation/devicetree/bindings/mtd/m25p80.txt index 4611aa83531b..f20b111b502a 100644 --- a/Documentation/devicetree/bindings/mtd/m25p80.txt +++ b/Documentation/devicetree/bindings/mtd/m25p80.txt @@ -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; diff --git a/Documentation/devicetree/bindings/mtd/pxa3xx-nand.txt b/Documentation/devicetree/bindings/mtd/pxa3xx-nand.txt index de8b517a5521..4f833e3c4f51 100644 --- a/Documentation/devicetree/bindings/mtd/pxa3xx-nand.txt +++ b/Documentation/devicetree/bindings/mtd/pxa3xx-nand.txt @@ -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 diff --git a/Documentation/devicetree/bindings/mtd/sunxi-nand.txt b/Documentation/devicetree/bindings/mtd/sunxi-nand.txt index 0273adb8638c..086d6f44c4b9 100644 --- a/Documentation/devicetree/bindings/mtd/sunxi-nand.txt +++ b/Documentation/devicetree/bindings/mtd/sunxi-nand.txt @@ -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: diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 71fea895ce38..a03ad2951c7b 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -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" diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index 423666b51efb..9a1a6ffd16b8 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -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; diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index 66f0405f7e53..b16f3cda97ff 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -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 rootfs mounted at this point. Try - to resolve the device name by other means. */ - dev_t devt = name_to_dev_t(devname); - if (devt) - bdev = blkdev_get_by_dev(devt, mode, dev); +#ifndef MODULE + /* + * 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; + + 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(); + + 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"); diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 448ce42f951e..866d31904475 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -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); diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 85e35467fba6..7c8b1694a134 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -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); diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index ba801d2c6dcc..e715ae90632f 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -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. diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index ea697202935a..892ad6ac63f2 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -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", }, diff --git a/drivers/mtd/maps/ts5500_flash.c b/drivers/mtd/maps/ts5500_flash.c index d1d671daf235..9969fedb1f13 100644 --- a/drivers/mtd/maps/ts5500_flash.c +++ b/drivers/mtd/maps/ts5500_flash.c @@ -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"); diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index d08229eb44d8..2b0c52870999 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -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); } diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 11883bd26d9d..d172195fbd15 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -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); diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index e779de315ade..cafdb8855a79 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -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; } diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index d93c849b70b5..46010bd895b1 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -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; } } diff --git a/drivers/mtd/nand/atmel_nand_ecc.h b/drivers/mtd/nand/atmel_nand_ecc.h index d4035e335ad8..668e7358f19b 100644 --- a/drivers/mtd/nand/atmel_nand_ecc.h +++ b/drivers/mtd/nand/atmel_nand_ecc.h @@ -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 diff --git a/drivers/mtd/nand/atmel_nand_nfc.h b/drivers/mtd/nand/atmel_nand_nfc.h index 85b8ca6af7d2..4d5d26221a7e 100644 --- a/drivers/mtd/nand/atmel_nand_nfc.h +++ b/drivers/mtd/nand/atmel_nand_nfc.h @@ -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) diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index f44c6061536a..870c7fc0f759 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -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 diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index 4c05f4f6a5c6..51394e59901b 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -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"); diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index edfaa21b1817..e58af4bfa8c8 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -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; diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 33f3c3c54dbc..1b8f3500e6d2 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -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); diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index a8f550fec35e..372e0e38f59b 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -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)) - break; + int max_retries = 8000; + int done; + do { udelay(1); + + done = host->devtype_data->check_int(host); + if (done) + break; + + } 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; } } diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index df7eb4ff07d1..c2e1232cd45c 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -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) { @@ -566,6 +566,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 * @command: the command to be sent @@ -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; diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 10b1f7a4fe50..a4615fcc3d00 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -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; diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 35aef5edb588..0e02be47ce1d 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -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: diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 635ee0027691..43b3392ffee7 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -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; } /** diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 1c7308c2c77d..5d5d36272bb5 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -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; } diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index b6a5a0c269e1..14a5d2325dac 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -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; } diff --git a/drivers/mtd/tests/mtd_nandecctest.c b/drivers/mtd/tests/mtd_nandecctest.c index e579f9027c47..79316159eec6 100644 --- a/drivers/mtd/tests/mtd_nandecctest.c +++ b/drivers/mtd/tests/mtd_nandecctest.c @@ -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); diff --git a/drivers/mtd/tests/mtd_test.h b/drivers/mtd/tests/mtd_test.h index f437c776c54f..4b7bee17c924 100644 --- a/drivers/mtd/tests/mtd_test.h +++ b/drivers/mtd/tests/mtd_test.h @@ -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, diff --git a/drivers/mtd/tests/nandbiterrs.c b/drivers/mtd/tests/nandbiterrs.c index 273f7e553954..09a4ccac53a2 100644 --- a/drivers/mtd/tests/nandbiterrs.c +++ b/drivers/mtd/tests/nandbiterrs.c @@ -320,6 +320,10 @@ static int overwrite_test(void) break; } + err = mtdtest_relax(); + if (err) + break; + opno++; } diff --git a/drivers/mtd/tests/oobtest.c b/drivers/mtd/tests/oobtest.c index 5e061186eab1..8e8525f0202f 100644 --- a/drivers/mtd/tests/oobtest.c +++ b/drivers/mtd/tests/oobtest.c @@ -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); diff --git a/drivers/mtd/tests/pagetest.c b/drivers/mtd/tests/pagetest.c index 88296e888e9d..ba1890d5632c 100644 --- a/drivers/mtd/tests/pagetest.c +++ b/drivers/mtd/tests/pagetest.c @@ -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); diff --git a/drivers/mtd/tests/readtest.c b/drivers/mtd/tests/readtest.c index a54cf1511114..a3196b750a22 100644 --- a/drivers/mtd/tests/readtest.c +++ b/drivers/mtd/tests/readtest.c @@ -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) diff --git a/drivers/mtd/tests/speedtest.c b/drivers/mtd/tests/speedtest.c index 5ee9f7021020..5a6f31af06f9 100644 --- a/drivers/mtd/tests/speedtest.c +++ b/drivers/mtd/tests/speedtest.c @@ -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(); diff --git a/drivers/mtd/tests/stresstest.c b/drivers/mtd/tests/stresstest.c index c9d42cc2df1b..e509f8aa9a7e 100644 --- a/drivers/mtd/tests/stresstest.c +++ b/drivers/mtd/tests/stresstest.c @@ -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); diff --git a/drivers/mtd/tests/subpagetest.c b/drivers/mtd/tests/subpagetest.c index 7b59ef522d5e..aecc6ce5a9e1 100644 --- a/drivers/mtd/tests/subpagetest.c +++ b/drivers/mtd/tests/subpagetest.c @@ -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); diff --git a/drivers/mtd/tests/torturetest.c b/drivers/mtd/tests/torturetest.c index b55bc52a1340..e5d6e6d9532f 100644 --- a/drivers/mtd/tests/torturetest.c +++ b/drivers/mtd/tests/torturetest.c @@ -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; } } diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c index 762c7a3cf43d..2eac55379239 100644 --- a/fs/jffs2/xattr.c +++ b/fs/jffs2/xattr.c @@ -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); diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index 5f487d776411..29975c73a953 100644 --- a/include/linux/mtd/map.h +++ b/include/linux/mtd/map.h @@ -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(); diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 4720b86ee73d..e5409524bb0a 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -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; }; |