diff options
| author | Linus Torvalds <[email protected]> | 2015-04-22 12:00:44 -0700 | 
|---|---|---|
| committer | Linus Torvalds <[email protected]> | 2015-04-22 12:00:44 -0700 | 
| commit | a62d016cece2fce1d5e4eedf36b17f03a7a5c78e (patch) | |
| tree | ac37b4835be5f4fe0e04611fdb40c85abb98ec78 | |
| parent | 7c034dfd58bbc056280262887acf5b7a98944d0a (diff) | |
| parent | 3e550d2396d9deef77328237ed992e19dcfefca5 (diff) | |
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:	[email protected] +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 <[email protected]>"); -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;  }; |