diff options
Diffstat (limited to 'drivers/mtd/devices')
| -rw-r--r-- | drivers/mtd/devices/Kconfig | 17 | ||||
| -rw-r--r-- | drivers/mtd/devices/Makefile | 1 | ||||
| -rw-r--r-- | drivers/mtd/devices/block2mtd.c | 20 | ||||
| -rw-r--r-- | drivers/mtd/devices/docg3.c | 26 | ||||
| -rw-r--r-- | drivers/mtd/devices/elm.c | 89 | ||||
| -rw-r--r-- | drivers/mtd/devices/m25p80.c | 1123 | ||||
| -rw-r--r-- | drivers/mtd/devices/ms02-nv.c | 2 | ||||
| -rw-r--r-- | drivers/mtd/devices/mtd_dataflash.c | 33 | ||||
| -rw-r--r-- | drivers/mtd/devices/mtdram.c | 2 | ||||
| -rw-r--r-- | drivers/mtd/devices/phram.c | 105 | ||||
| -rw-r--r-- | drivers/mtd/devices/pmc551.c | 7 | ||||
| -rw-r--r-- | drivers/mtd/devices/serial_flash_cmds.h | 61 | ||||
| -rw-r--r-- | drivers/mtd/devices/slram.c | 4 | ||||
| -rw-r--r-- | drivers/mtd/devices/spear_smi.c | 6 | ||||
| -rw-r--r-- | drivers/mtd/devices/sst25l.c | 14 | ||||
| -rw-r--r-- | drivers/mtd/devices/st_spi_fsm.c | 2080 | 
16 files changed, 2456 insertions, 1134 deletions
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 74ab4b7e523..c49d0b127fe 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -80,7 +80,7 @@ config MTD_DATAFLASH_OTP  config MTD_M25P80  	tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)" -	depends on SPI_MASTER +	depends on SPI_MASTER && MTD_SPI_NOR  	help  	  This enables access to most modern SPI flash chips, used for  	  program and data storage.   Series supported include Atmel AT26DF, @@ -95,13 +95,6 @@ config MTD_M25P80  	  if you want to specify device partitioning or to use a device which  	  doesn't support the JEDEC ID instruction. -config M25PXX_USE_FAST_READ -	bool "Use FAST_READ OPCode allowing SPI CLK >= 50MHz" -	depends on MTD_M25P80 -	default y -	help -	  This option enables FAST_READ access supported by ST M25Pxx. -  config MTD_SPEAR_SMI  	tristate "SPEAR MTD NOR Support through SMI controller"  	depends on PLAT_SPEAR @@ -217,6 +210,14 @@ config MTD_DOCG3  	  M-Systems and now Sandisk. The support is very experimental,  	  and doesn't give access to any write operations. +config MTD_ST_SPI_FSM +	tristate "ST Microelectronics SPI FSM Serial Flash Controller" +	depends on ARCH_STI +	help +	  This provides an MTD device driver for the ST Microelectronics +	  SPI Fast Sequence Mode (FSM) Serial Flash Controller and support +	  for a subset of connected Serial Flash devices. +  if MTD_DOCG3  config BCH_CONST_M  	default 14 diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index d83bd73096f..c68868f6058 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_MTD_NAND_OMAP_BCH)	+= elm.o  obj-$(CONFIG_MTD_SPEAR_SMI)	+= spear_smi.o  obj-$(CONFIG_MTD_SST25L)	+= sst25l.o  obj-$(CONFIG_MTD_BCM47XXSFLASH)	+= bcm47xxsflash.o +obj-$(CONFIG_MTD_ST_SPI_FSM)    += st_spi_fsm.o  CFLAGS_docg3.o			+= -I$(src) diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index 5cb4c04726b..66f0405f7e5 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -20,6 +20,7 @@  #include <linux/mutex.h>  #include <linux/mount.h>  #include <linux/slab.h> +#include <linux/major.h>  /* Info for the block device */  struct block2mtd_dev { @@ -208,7 +209,6 @@ static void block2mtd_free_device(struct block2mtd_dev *dev)  } -/* FIXME: ensure that mtd->size % erase_size == 0 */  static struct block2mtd_dev *add_device(char *devname, int erase_size)  {  	const fmode_t mode = FMODE_READ | FMODE_WRITE | FMODE_EXCL; @@ -239,13 +239,18 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)  	if (IS_ERR(bdev)) {  		pr_err("error: cannot open device %s\n", devname); -		goto devinit_err; +		goto err_free_block2mtd;  	}  	dev->blkdev = bdev;  	if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) {  		pr_err("attempting to use an MTD device as a block device\n"); -		goto devinit_err; +		goto err_free_block2mtd; +	} + +	if ((long)dev->blkdev->bd_inode->i_size % erase_size) { +		pr_err("erasesize must be a divisor of device size\n"); +		goto err_free_block2mtd;  	}  	mutex_init(&dev->write_mutex); @@ -254,7 +259,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)  	/* make the name contain the block device in */  	name = kasprintf(GFP_KERNEL, "block2mtd: %s", devname);  	if (!name) -		goto devinit_err; +		goto err_destroy_mutex;  	dev->mtd.name = name; @@ -273,7 +278,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)  	if (mtd_device_register(&dev->mtd, NULL, 0)) {  		/* Device didn't get added, so free the entry */ -		goto devinit_err; +		goto err_destroy_mutex;  	}  	list_add(&dev->list, &blkmtd_device_list);  	pr_info("mtd%d: [%s] erase_size = %dKiB [%d]\n", @@ -282,7 +287,9 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)  		dev->mtd.erasesize >> 10, dev->mtd.erasesize);  	return dev; -devinit_err: +err_destroy_mutex: +	mutex_destroy(&dev->write_mutex); +err_free_block2mtd:  	block2mtd_free_device(dev);  	return NULL;  } @@ -447,6 +454,7 @@ static void block2mtd_exit(void)  		struct block2mtd_dev *dev = list_entry(pos, typeof(*dev), list);  		block2mtd_sync(&dev->mtd);  		mtd_device_unregister(&dev->mtd); +		mutex_destroy(&dev->write_mutex);  		pr_info("mtd%d: [%s] removed\n",  			dev->mtd.index,  			dev->mtd.name + strlen("block2mtd: ")); diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 3e1b0a0ef4d..91a169c44b3 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -1608,8 +1608,8 @@ static ssize_t dps1_insert_key(struct device *dev,  #define FLOOR_SYSFS(id) { \  	__ATTR(f##id##_dps0_is_keylocked, S_IRUGO, dps0_is_key_locked, NULL), \  	__ATTR(f##id##_dps1_is_keylocked, S_IRUGO, dps1_is_key_locked, NULL), \ -	__ATTR(f##id##_dps0_protection_key, S_IWUGO, NULL, dps0_insert_key), \ -	__ATTR(f##id##_dps1_protection_key, S_IWUGO, NULL, dps1_insert_key), \ +	__ATTR(f##id##_dps0_protection_key, S_IWUSR|S_IWGRP, NULL, dps0_insert_key), \ +	__ATTR(f##id##_dps1_protection_key, S_IWUSR|S_IWGRP, NULL, dps1_insert_key), \  }  static struct device_attribute doc_sys_attrs[DOC_MAX_NBFLOORS][4] = { @@ -2047,21 +2047,21 @@ static int __init docg3_probe(struct platform_device *pdev)  	ress = platform_get_resource(pdev, IORESOURCE_MEM, 0);  	if (!ress) {  		dev_err(dev, "No I/O memory resource defined\n"); -		goto noress; +		return ret;  	} -	base = ioremap(ress->start, DOC_IOSPACE_SIZE); +	base = devm_ioremap(dev, ress->start, DOC_IOSPACE_SIZE);  	ret = -ENOMEM; -	cascade = kzalloc(sizeof(*cascade) * DOC_MAX_NBFLOORS, -			  GFP_KERNEL); +	cascade = devm_kzalloc(dev, sizeof(*cascade) * DOC_MAX_NBFLOORS, +			       GFP_KERNEL);  	if (!cascade) -		goto nomem1; +		return ret;  	cascade->base = base;  	mutex_init(&cascade->lock);  	cascade->bch = init_bch(DOC_ECC_BCH_M, DOC_ECC_BCH_T,  			     DOC_ECC_BCH_PRIMPOLY);  	if (!cascade->bch) -		goto nomem2; +		return ret;  	for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) {  		mtd = doc_probe_device(cascade, floor, dev); @@ -2097,15 +2097,10 @@ notfound:  	ret = -ENODEV;  	dev_info(dev, "No supported DiskOnChip found\n");  err_probe: -	kfree(cascade->bch); +	free_bch(cascade->bch);  	for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++)  		if (cascade->floors[floor])  			doc_release_device(cascade->floors[floor]); -nomem2: -	kfree(cascade); -nomem1: -	iounmap(base); -noress:  	return ret;  } @@ -2119,7 +2114,6 @@ static int __exit docg3_release(struct platform_device *pdev)  {  	struct docg3_cascade *cascade = platform_get_drvdata(pdev);  	struct docg3 *docg3 = cascade->floors[0]->priv; -	void __iomem *base = cascade->base;  	int floor;  	doc_unregister_sysfs(pdev, cascade); @@ -2129,8 +2123,6 @@ static int __exit docg3_release(struct platform_device *pdev)  			doc_release_device(cascade->floors[floor]);  	free_bch(docg3->cascade->bch); -	kfree(cascade); -	iounmap(base);  	return 0;  } diff --git a/drivers/mtd/devices/elm.c b/drivers/mtd/devices/elm.c index d1dd6a33a05..b4f61c7fc16 100644 --- a/drivers/mtd/devices/elm.c +++ b/drivers/mtd/devices/elm.c @@ -15,6 +15,8 @@   *   */ +#define DRIVER_NAME	"omap-elm" +  #include <linux/platform_device.h>  #include <linux/module.h>  #include <linux/interrupt.h> @@ -84,6 +86,8 @@ struct elm_info {  	struct list_head list;  	enum bch_ecc bch_type;  	struct elm_registers elm_regs; +	int ecc_steps; +	int ecc_syndrome_size;  };  static LIST_HEAD(elm_devices); @@ -103,7 +107,8 @@ static u32 elm_read_reg(struct elm_info *info, int offset)   * @dev:	ELM device   * @bch_type:	Type of BCH ecc   */ -int elm_config(struct device *dev, enum bch_ecc bch_type) +int elm_config(struct device *dev, enum bch_ecc bch_type, +	int ecc_steps, int ecc_step_size, int ecc_syndrome_size)  {  	u32 reg_val;  	struct elm_info *info = dev_get_drvdata(dev); @@ -112,10 +117,22 @@ int elm_config(struct device *dev, enum bch_ecc bch_type)  		dev_err(dev, "Unable to configure elm - device not probed?\n");  		return -ENODEV;  	} +	/* ELM cannot detect ECC errors for chunks > 1KB */ +	if (ecc_step_size > ((ELM_ECC_SIZE + 1) / 2)) { +		dev_err(dev, "unsupported config ecc-size=%d\n", ecc_step_size); +		return -EINVAL; +	} +	/* ELM support 8 error syndrome process */ +	if (ecc_steps > ERROR_VECTOR_MAX) { +		dev_err(dev, "unsupported config ecc-step=%d\n", ecc_steps); +		return -EINVAL; +	}  	reg_val = (bch_type & ECC_BCH_LEVEL_MASK) | (ELM_ECC_SIZE << 16);  	elm_write_reg(info, ELM_LOCATION_CONFIG, reg_val); -	info->bch_type = bch_type; +	info->bch_type		= bch_type; +	info->ecc_steps		= ecc_steps; +	info->ecc_syndrome_size	= ecc_syndrome_size;  	return 0;  } @@ -157,17 +174,15 @@ static void elm_load_syndrome(struct elm_info *info,  	int i, offset;  	u32 val; -	for (i = 0; i < ERROR_VECTOR_MAX; i++) { +	for (i = 0; i < info->ecc_steps; i++) {  		/* Check error reported */  		if (err_vec[i].error_reported) {  			elm_configure_page_mode(info, i, true);  			offset = ELM_SYNDROME_FRAGMENT_0 +  				SYNDROME_FRAGMENT_REG_SIZE * i; - -			/* BCH8 */ -			if (info->bch_type) { - +			switch (info->bch_type) { +			case BCH8_ECC:  				/* syndrome fragment 0 = ecc[9-12B] */  				val = cpu_to_be32(*(u32 *) &ecc[9]);  				elm_write_reg(info, offset, val); @@ -186,7 +201,8 @@ static void elm_load_syndrome(struct elm_info *info,  				offset += 4;  				val = ecc[0];  				elm_write_reg(info, offset, val); -			} else { +				break; +			case BCH4_ECC:  				/* syndrome fragment 0 = ecc[20-52b] bits */  				val = (cpu_to_be32(*(u32 *) &ecc[3]) >> 4) |  					((ecc[2] & 0xf) << 28); @@ -196,11 +212,36 @@ static void elm_load_syndrome(struct elm_info *info,  				offset += 4;  				val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12;  				elm_write_reg(info, offset, val); +				break; +			case BCH16_ECC: +				val = cpu_to_be32(*(u32 *) &ecc[22]); +				elm_write_reg(info, offset, val); +				offset += 4; +				val = cpu_to_be32(*(u32 *) &ecc[18]); +				elm_write_reg(info, offset, val); +				offset += 4; +				val = cpu_to_be32(*(u32 *) &ecc[14]); +				elm_write_reg(info, offset, val); +				offset += 4; +				val = cpu_to_be32(*(u32 *) &ecc[10]); +				elm_write_reg(info, offset, val); +				offset += 4; +				val = cpu_to_be32(*(u32 *) &ecc[6]); +				elm_write_reg(info, offset, val); +				offset += 4; +				val = cpu_to_be32(*(u32 *) &ecc[2]); +				elm_write_reg(info, offset, val); +				offset += 4; +				val = cpu_to_be32(*(u32 *) &ecc[0]) >> 16; +				elm_write_reg(info, offset, val); +				break; +			default: +				pr_err("invalid config bch_type\n");  			}  		}  		/* Update ecc pointer with ecc byte size */ -		ecc += info->bch_type ? BCH8_SIZE : BCH4_SIZE; +		ecc += info->ecc_syndrome_size;  	}  } @@ -223,7 +264,7 @@ static void elm_start_processing(struct elm_info *info,  	 * Set syndrome vector valid, so that ELM module  	 * will process it for vectors error is reported  	 */ -	for (i = 0; i < ERROR_VECTOR_MAX; i++) { +	for (i = 0; i < info->ecc_steps; i++) {  		if (err_vec[i].error_reported) {  			offset = ELM_SYNDROME_FRAGMENT_6 +  				SYNDROME_FRAGMENT_REG_SIZE * i; @@ -252,7 +293,7 @@ static void elm_error_correction(struct elm_info *info,  	int offset;  	u32 reg_val; -	for (i = 0; i < ERROR_VECTOR_MAX; i++) { +	for (i = 0; i < info->ecc_steps; i++) {  		/* Check error reported */  		if (err_vec[i].error_reported) { @@ -354,10 +395,8 @@ static int elm_probe(struct platform_device *pdev)  	struct elm_info *info;  	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); -	if (!info) { -		dev_err(&pdev->dev, "failed to allocate memory\n"); +	if (!info)  		return -ENOMEM; -	}  	info->dev = &pdev->dev; @@ -380,7 +419,7 @@ static int elm_probe(struct platform_device *pdev)  	}  	pm_runtime_enable(&pdev->dev); -	if (pm_runtime_get_sync(&pdev->dev)) { +	if (pm_runtime_get_sync(&pdev->dev) < 0) {  		ret = -EINVAL;  		pm_runtime_disable(&pdev->dev);  		dev_err(&pdev->dev, "can't enable clock\n"); @@ -401,6 +440,7 @@ static int elm_remove(struct platform_device *pdev)  	return 0;  } +#ifdef CONFIG_PM_SLEEP  /**   * elm_context_save   * saves ELM configurations to preserve them across Hardware powered-down @@ -418,6 +458,13 @@ static int elm_context_save(struct elm_info *info)  	for (i = 0; i < ERROR_VECTOR_MAX; i++) {  		offset = i * SYNDROME_FRAGMENT_REG_SIZE;  		switch (bch_type) { +		case BCH16_ECC: +			regs->elm_syndrome_fragment_6[i] = elm_read_reg(info, +					ELM_SYNDROME_FRAGMENT_6 + offset); +			regs->elm_syndrome_fragment_5[i] = elm_read_reg(info, +					ELM_SYNDROME_FRAGMENT_5 + offset); +			regs->elm_syndrome_fragment_4[i] = elm_read_reg(info, +					ELM_SYNDROME_FRAGMENT_4 + offset);  		case BCH8_ECC:  			regs->elm_syndrome_fragment_3[i] = elm_read_reg(info,  					ELM_SYNDROME_FRAGMENT_3 + offset); @@ -428,6 +475,7 @@ static int elm_context_save(struct elm_info *info)  					ELM_SYNDROME_FRAGMENT_1 + offset);  			regs->elm_syndrome_fragment_0[i] = elm_read_reg(info,  					ELM_SYNDROME_FRAGMENT_0 + offset); +			break;  		default:  			return -EINVAL;  		} @@ -456,6 +504,13 @@ static int elm_context_restore(struct elm_info *info)  	for (i = 0; i < ERROR_VECTOR_MAX; i++) {  		offset = i * SYNDROME_FRAGMENT_REG_SIZE;  		switch (bch_type) { +		case BCH16_ECC: +			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset, +					regs->elm_syndrome_fragment_6[i]); +			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_5 + offset, +					regs->elm_syndrome_fragment_5[i]); +			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_4 + offset, +					regs->elm_syndrome_fragment_4[i]);  		case BCH8_ECC:  			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_3 + offset,  					regs->elm_syndrome_fragment_3[i]); @@ -466,6 +521,7 @@ static int elm_context_restore(struct elm_info *info)  					regs->elm_syndrome_fragment_1[i]);  			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_0 + offset,  					regs->elm_syndrome_fragment_0[i]); +			break;  		default:  			return -EINVAL;  		} @@ -492,6 +548,7 @@ static int elm_resume(struct device *dev)  	elm_context_restore(info);  	return 0;  } +#endif  static SIMPLE_DEV_PM_OPS(elm_pm_ops, elm_suspend, elm_resume); @@ -505,7 +562,7 @@ MODULE_DEVICE_TABLE(of, elm_of_match);  static struct platform_driver elm_driver = {  	.driver	= { -		.name	= "elm", +		.name	= DRIVER_NAME,  		.owner	= THIS_MODULE,  		.of_match_table = of_match_ptr(elm_of_match),  		.pm	= &elm_pm_ops, diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 26b14f9fcac..ed7e0a1bed3 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -15,905 +15,175 @@   *   */ -#include <linux/init.h>  #include <linux/err.h>  #include <linux/errno.h>  #include <linux/module.h>  #include <linux/device.h> -#include <linux/interrupt.h> -#include <linux/mutex.h> -#include <linux/math64.h> -#include <linux/slab.h> -#include <linux/sched.h> -#include <linux/mod_devicetable.h> -#include <linux/mtd/cfi.h>  #include <linux/mtd/mtd.h>  #include <linux/mtd/partitions.h> -#include <linux/of_platform.h>  #include <linux/spi/spi.h>  #include <linux/spi/flash.h> +#include <linux/mtd/spi-nor.h> -/* Flash opcodes. */ -#define	OPCODE_WREN		0x06	/* Write enable */ -#define	OPCODE_RDSR		0x05	/* Read status register */ -#define	OPCODE_WRSR		0x01	/* Write status register 1 byte */ -#define	OPCODE_NORM_READ	0x03	/* Read data bytes (low frequency) */ -#define	OPCODE_FAST_READ	0x0b	/* Read data bytes (high frequency) */ -#define	OPCODE_PP		0x02	/* Page program (up to 256 bytes) */ -#define	OPCODE_BE_4K		0x20	/* Erase 4KiB block */ -#define	OPCODE_BE_4K_PMC	0xd7	/* Erase 4KiB block on PMC chips */ -#define	OPCODE_BE_32K		0x52	/* Erase 32KiB block */ -#define	OPCODE_CHIP_ERASE	0xc7	/* Erase whole flash chip */ -#define	OPCODE_SE		0xd8	/* Sector erase (usually 64KiB) */ -#define	OPCODE_RDID		0x9f	/* Read JEDEC ID */ - -/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ -#define	OPCODE_NORM_READ_4B	0x13	/* Read data bytes (low frequency) */ -#define	OPCODE_FAST_READ_4B	0x0c	/* Read data bytes (high frequency) */ -#define	OPCODE_PP_4B		0x12	/* Page program (up to 256 bytes) */ -#define	OPCODE_SE_4B		0xdc	/* Sector erase (usually 64KiB) */ - -/* Used for SST flashes only. */ -#define	OPCODE_BP		0x02	/* Byte program */ -#define	OPCODE_WRDI		0x04	/* Write disable */ -#define	OPCODE_AAI_WP		0xad	/* Auto address increment word program */ - -/* Used for Macronix and Winbond flashes. */ -#define	OPCODE_EN4B		0xb7	/* Enter 4-byte mode */ -#define	OPCODE_EX4B		0xe9	/* Exit 4-byte mode */ - -/* Used for Spansion flashes only. */ -#define	OPCODE_BRWR		0x17	/* Bank register write */ - -/* Status Register bits. */ -#define	SR_WIP			1	/* Write in progress */ -#define	SR_WEL			2	/* Write enable latch */ -/* meaning of other SR_* bits may differ between vendors */ -#define	SR_BP0			4	/* Block protect 0 */ -#define	SR_BP1			8	/* Block protect 1 */ -#define	SR_BP2			0x10	/* Block protect 2 */ -#define	SR_SRWD			0x80	/* SR write protect */ - -/* Define max times to check status register before we give up. */ -#define	MAX_READY_WAIT_JIFFIES	(40 * HZ)	/* M25P16 specs 40s max chip erase */ -#define	MAX_CMD_SIZE		5 - -#define JEDEC_MFR(_jedec_id)	((_jedec_id) >> 16) - -/****************************************************************************/ - +#define	MAX_CMD_SIZE		6  struct m25p {  	struct spi_device	*spi; -	struct mutex		lock; +	struct spi_nor		spi_nor;  	struct mtd_info		mtd; -	u16			page_size; -	u16			addr_width; -	u8			erase_opcode; -	u8			read_opcode; -	u8			program_opcode; -	u8			*command; -	bool			fast_read; +	u8			command[MAX_CMD_SIZE];  }; -static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd) +static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)  { -	return container_of(mtd, struct m25p, mtd); -} +	struct m25p *flash = nor->priv; +	struct spi_device *spi = flash->spi; +	int ret; -/****************************************************************************/ +	ret = spi_write_then_read(spi, &code, 1, val, len); +	if (ret < 0) +		dev_err(&spi->dev, "error %d reading %x\n", ret, code); -/* - * Internal helper functions - */ - -/* - * Read the status register, returning its value in the location - * Return the status register value. - * Returns negative if error occurred. - */ -static int read_sr(struct m25p *flash) -{ -	ssize_t retval; -	u8 code = OPCODE_RDSR; -	u8 val; - -	retval = spi_write_then_read(flash->spi, &code, 1, &val, 1); - -	if (retval < 0) { -		dev_err(&flash->spi->dev, "error %d reading SR\n", -				(int) retval); -		return retval; -	} - -	return val; +	return ret;  } -/* - * Write status register 1 byte - * Returns negative if error occurred. - */ -static int write_sr(struct m25p *flash, u8 val) +static void m25p_addr2cmd(struct spi_nor *nor, unsigned int addr, u8 *cmd)  { -	flash->command[0] = OPCODE_WRSR; -	flash->command[1] = val; - -	return spi_write(flash->spi, flash->command, 2); +	/* opcode is in cmd[0] */ +	cmd[1] = addr >> (nor->addr_width * 8 -  8); +	cmd[2] = addr >> (nor->addr_width * 8 - 16); +	cmd[3] = addr >> (nor->addr_width * 8 - 24); +	cmd[4] = addr >> (nor->addr_width * 8 - 32);  } -/* - * Set write enable latch with Write Enable command. - * Returns negative if error occurred. - */ -static inline int write_enable(struct m25p *flash) +static int m25p_cmdsz(struct spi_nor *nor)  { -	u8	code = OPCODE_WREN; - -	return spi_write_then_read(flash->spi, &code, 1, NULL, 0); +	return 1 + nor->addr_width;  } -/* - * Send write disble instruction to the chip. - */ -static inline int write_disable(struct m25p *flash) +static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len, +			int wr_en)  { -	u8	code = OPCODE_WRDI; +	struct m25p *flash = nor->priv; +	struct spi_device *spi = flash->spi; -	return spi_write_then_read(flash->spi, &code, 1, NULL, 0); -} - -/* - * Enable/disable 4-byte addressing mode. - */ -static inline int set_4byte(struct m25p *flash, u32 jedec_id, int enable) -{ -	switch (JEDEC_MFR(jedec_id)) { -	case CFI_MFR_MACRONIX: -	case CFI_MFR_ST: /* Micron, actually */ -	case 0xEF /* winbond */: -		flash->command[0] = enable ? OPCODE_EN4B : OPCODE_EX4B; -		return spi_write(flash->spi, flash->command, 1); -	default: -		/* Spansion style */ -		flash->command[0] = OPCODE_BRWR; -		flash->command[1] = enable << 7; -		return spi_write(flash->spi, flash->command, 2); -	} -} - -/* - * Service routine to read status register until ready, or timeout occurs. - * Returns non-zero if error. - */ -static int wait_till_ready(struct m25p *flash) -{ -	unsigned long deadline; -	int sr; - -	deadline = jiffies + MAX_READY_WAIT_JIFFIES; - -	do { -		if ((sr = read_sr(flash)) < 0) -			break; -		else if (!(sr & SR_WIP)) -			return 0; - -		cond_resched(); - -	} while (!time_after_eq(jiffies, deadline)); +	flash->command[0] = opcode; +	if (buf) +		memcpy(&flash->command[1], buf, len); -	return 1; +	return spi_write(spi, flash->command, len + 1);  } -/* - * Erase the whole flash memory - * - * Returns 0 if successful, non-zero otherwise. - */ -static int erase_chip(struct m25p *flash) +static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len, +			size_t *retlen, const u_char *buf)  { -	pr_debug("%s: %s %lldKiB\n", dev_name(&flash->spi->dev), __func__, -			(long long)(flash->mtd.size >> 10)); - -	/* Wait until finished previous write command. */ -	if (wait_till_ready(flash)) -		return 1; - -	/* Send write enable, then erase commands. */ -	write_enable(flash); - -	/* Set up command buffer. */ -	flash->command[0] = OPCODE_CHIP_ERASE; - -	spi_write(flash->spi, flash->command, 1); - -	return 0; -} +	struct m25p *flash = nor->priv; +	struct spi_device *spi = flash->spi; +	struct spi_transfer t[2] = {}; +	struct spi_message m; +	int cmd_sz = m25p_cmdsz(nor); -static void m25p_addr2cmd(struct m25p *flash, unsigned int addr, u8 *cmd) -{ -	/* opcode is in cmd[0] */ -	cmd[1] = addr >> (flash->addr_width * 8 -  8); -	cmd[2] = addr >> (flash->addr_width * 8 - 16); -	cmd[3] = addr >> (flash->addr_width * 8 - 24); -	cmd[4] = addr >> (flash->addr_width * 8 - 32); -} +	spi_message_init(&m); -static int m25p_cmdsz(struct m25p *flash) -{ -	return 1 + flash->addr_width; -} +	if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) +		cmd_sz = 1; -/* - * Erase one sector of flash memory at offset ``offset'' which is any - * address within the sector which should be erased. - * - * Returns 0 if successful, non-zero otherwise. - */ -static int erase_sector(struct m25p *flash, u32 offset) -{ -	pr_debug("%s: %s %dKiB at 0x%08x\n", dev_name(&flash->spi->dev), -			__func__, flash->mtd.erasesize / 1024, offset); +	flash->command[0] = nor->program_opcode; +	m25p_addr2cmd(nor, to, flash->command); -	/* Wait until finished previous write command. */ -	if (wait_till_ready(flash)) -		return 1; - -	/* Send write enable, then erase commands. */ -	write_enable(flash); +	t[0].tx_buf = flash->command; +	t[0].len = cmd_sz; +	spi_message_add_tail(&t[0], &m); -	/* Set up command buffer. */ -	flash->command[0] = flash->erase_opcode; -	m25p_addr2cmd(flash, offset, flash->command); +	t[1].tx_buf = buf; +	t[1].len = len; +	spi_message_add_tail(&t[1], &m); -	spi_write(flash->spi, flash->command, m25p_cmdsz(flash)); +	spi_sync(spi, &m); -	return 0; +	*retlen += m.actual_length - cmd_sz;  } -/****************************************************************************/ - -/* - * MTD implementation - */ - -/* - * Erase an address range on the flash chip.  The address range may extend - * one or more erase sectors.  Return an error is there is a problem erasing. - */ -static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) +static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)  { -	struct m25p *flash = mtd_to_m25p(mtd); -	u32 addr,len; -	uint32_t rem; - -	pr_debug("%s: %s at 0x%llx, len %lld\n", dev_name(&flash->spi->dev), -			__func__, (long long)instr->addr, -			(long long)instr->len); - -	div_u64_rem(instr->len, mtd->erasesize, &rem); -	if (rem) -		return -EINVAL; - -	addr = instr->addr; -	len = instr->len; - -	mutex_lock(&flash->lock); - -	/* whole-chip erase? */ -	if (len == flash->mtd.size) { -		if (erase_chip(flash)) { -			instr->state = MTD_ERASE_FAILED; -			mutex_unlock(&flash->lock); -			return -EIO; -		} - -	/* REVISIT in some cases we could speed up erasing large regions -	 * by using OPCODE_SE instead of OPCODE_BE_4K.  We may have set up -	 * to use "small sector erase", but that's not always optimal. -	 */ - -	/* "sector"-at-a-time erase */ -	} else { -		while (len) { -			if (erase_sector(flash, addr)) { -				instr->state = MTD_ERASE_FAILED; -				mutex_unlock(&flash->lock); -				return -EIO; -			} - -			addr += mtd->erasesize; -			len -= mtd->erasesize; -		} +	switch (nor->flash_read) { +	case SPI_NOR_DUAL: +		return 2; +	case SPI_NOR_QUAD: +		return 4; +	default: +		return 0;  	} - -	mutex_unlock(&flash->lock); - -	instr->state = MTD_ERASE_DONE; -	mtd_erase_callback(instr); - -	return 0;  }  /* - * Read an address range from the flash chip.  The address range + * Read an address range from the nor chip.  The address range   * may be any size provided it is within the physical boundaries.   */ -static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, -	size_t *retlen, u_char *buf) +static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len, +			size_t *retlen, u_char *buf)  { -	struct m25p *flash = mtd_to_m25p(mtd); +	struct m25p *flash = nor->priv; +	struct spi_device *spi = flash->spi;  	struct spi_transfer t[2];  	struct spi_message m; -	uint8_t opcode; +	int dummy = nor->read_dummy; +	int ret; -	pr_debug("%s: %s from 0x%08x, len %zd\n", dev_name(&flash->spi->dev), -			__func__, (u32)from, len); +	/* Wait till previous write/erase is done. */ +	ret = nor->wait_till_ready(nor); +	if (ret) +		return ret;  	spi_message_init(&m);  	memset(t, 0, (sizeof t)); -	/* NOTE: -	 * OPCODE_FAST_READ (if available) is faster. -	 * Should add 1 byte DUMMY_BYTE. -	 */ +	flash->command[0] = nor->read_opcode; +	m25p_addr2cmd(nor, from, flash->command); +  	t[0].tx_buf = flash->command; -	t[0].len = m25p_cmdsz(flash) + (flash->fast_read ? 1 : 0); +	t[0].len = m25p_cmdsz(nor) + dummy;  	spi_message_add_tail(&t[0], &m);  	t[1].rx_buf = buf; +	t[1].rx_nbits = m25p80_rx_nbits(nor);  	t[1].len = len;  	spi_message_add_tail(&t[1], &m); -	mutex_lock(&flash->lock); - -	/* Wait till previous write/erase is done. */ -	if (wait_till_ready(flash)) { -		/* REVISIT status return?? */ -		mutex_unlock(&flash->lock); -		return 1; -	} - -	/* FIXME switch to OPCODE_FAST_READ.  It's required for higher -	 * clocks; and at this writing, every chip this driver handles -	 * supports that opcode. -	 */ - -	/* Set up the write data buffer. */ -	opcode = flash->read_opcode; -	flash->command[0] = opcode; -	m25p_addr2cmd(flash, from, flash->command); - -	spi_sync(flash->spi, &m); - -	*retlen = m.actual_length - m25p_cmdsz(flash) - -			(flash->fast_read ? 1 : 0); - -	mutex_unlock(&flash->lock); - -	return 0; -} - -/* - * Write an address range to the flash chip.  Data must be written in - * FLASH_PAGESIZE chunks.  The address range may be any size provided - * it is within the physical boundaries. - */ -static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, -	size_t *retlen, const u_char *buf) -{ -	struct m25p *flash = mtd_to_m25p(mtd); -	u32 page_offset, page_size; -	struct spi_transfer t[2]; -	struct spi_message m; - -	pr_debug("%s: %s to 0x%08x, len %zd\n", dev_name(&flash->spi->dev), -			__func__, (u32)to, len); - -	spi_message_init(&m); -	memset(t, 0, (sizeof t)); - -	t[0].tx_buf = flash->command; -	t[0].len = m25p_cmdsz(flash); -	spi_message_add_tail(&t[0], &m); - -	t[1].tx_buf = buf; -	spi_message_add_tail(&t[1], &m); - -	mutex_lock(&flash->lock); - -	/* Wait until finished previous write command. */ -	if (wait_till_ready(flash)) { -		mutex_unlock(&flash->lock); -		return 1; -	} - -	write_enable(flash); - -	/* Set up the opcode in the write buffer. */ -	flash->command[0] = flash->program_opcode; -	m25p_addr2cmd(flash, to, flash->command); - -	page_offset = to & (flash->page_size - 1); - -	/* do all the bytes fit onto one page? */ -	if (page_offset + len <= flash->page_size) { -		t[1].len = len; - -		spi_sync(flash->spi, &m); - -		*retlen = m.actual_length - m25p_cmdsz(flash); -	} else { -		u32 i; - -		/* the size of data remaining on the first page */ -		page_size = flash->page_size - page_offset; - -		t[1].len = page_size; -		spi_sync(flash->spi, &m); - -		*retlen = m.actual_length - m25p_cmdsz(flash); - -		/* write everything in flash->page_size chunks */ -		for (i = page_size; i < len; i += page_size) { -			page_size = len - i; -			if (page_size > flash->page_size) -				page_size = flash->page_size; - -			/* write the next page to flash */ -			m25p_addr2cmd(flash, to + i, flash->command); - -			t[1].tx_buf = buf + i; -			t[1].len = page_size; - -			wait_till_ready(flash); - -			write_enable(flash); - -			spi_sync(flash->spi, &m); - -			*retlen += m.actual_length - m25p_cmdsz(flash); -		} -	} - -	mutex_unlock(&flash->lock); +	spi_sync(spi, &m); +	*retlen = m.actual_length - m25p_cmdsz(nor) - dummy;  	return 0;  } -static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, -		size_t *retlen, const u_char *buf) +static int m25p80_erase(struct spi_nor *nor, loff_t offset)  { -	struct m25p *flash = mtd_to_m25p(mtd); -	struct spi_transfer t[2]; -	struct spi_message m; -	size_t actual; -	int cmd_sz, ret; +	struct m25p *flash = nor->priv; +	int ret; -	pr_debug("%s: %s to 0x%08x, len %zd\n", dev_name(&flash->spi->dev), -			__func__, (u32)to, len); - -	spi_message_init(&m); -	memset(t, 0, (sizeof t)); - -	t[0].tx_buf = flash->command; -	t[0].len = m25p_cmdsz(flash); -	spi_message_add_tail(&t[0], &m); - -	t[1].tx_buf = buf; -	spi_message_add_tail(&t[1], &m); - -	mutex_lock(&flash->lock); +	dev_dbg(nor->dev, "%dKiB at 0x%08x\n", +		flash->mtd.erasesize / 1024, (u32)offset);  	/* Wait until finished previous write command. */ -	ret = wait_till_ready(flash); +	ret = nor->wait_till_ready(nor);  	if (ret) -		goto time_out; - -	write_enable(flash); - -	actual = to % 2; -	/* Start write from odd address. */ -	if (actual) { -		flash->command[0] = OPCODE_BP; -		m25p_addr2cmd(flash, to, flash->command); - -		/* write one byte. */ -		t[1].len = 1; -		spi_sync(flash->spi, &m); -		ret = wait_till_ready(flash); -		if (ret) -			goto time_out; -		*retlen += m.actual_length - m25p_cmdsz(flash); -	} -	to += actual; +		return ret; -	flash->command[0] = OPCODE_AAI_WP; -	m25p_addr2cmd(flash, to, flash->command); - -	/* Write out most of the data here. */ -	cmd_sz = m25p_cmdsz(flash); -	for (; actual < len - 1; actual += 2) { -		t[0].len = cmd_sz; -		/* write two bytes. */ -		t[1].len = 2; -		t[1].tx_buf = buf + actual; - -		spi_sync(flash->spi, &m); -		ret = wait_till_ready(flash); -		if (ret) -			goto time_out; -		*retlen += m.actual_length - cmd_sz; -		cmd_sz = 1; -		to += 2; -	} -	write_disable(flash); -	ret = wait_till_ready(flash); +	/* Send write enable, then erase commands. */ +	ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0);  	if (ret) -		goto time_out; +		return ret; -	/* Write out trailing byte if it exists. */ -	if (actual != len) { -		write_enable(flash); -		flash->command[0] = OPCODE_BP; -		m25p_addr2cmd(flash, to, flash->command); -		t[0].len = m25p_cmdsz(flash); -		t[1].len = 1; -		t[1].tx_buf = buf + actual; - -		spi_sync(flash->spi, &m); -		ret = wait_till_ready(flash); -		if (ret) -			goto time_out; -		*retlen += m.actual_length - m25p_cmdsz(flash); -		write_disable(flash); -	} - -time_out: -	mutex_unlock(&flash->lock); -	return ret; -} - -static int m25p80_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) -{ -	struct m25p *flash = mtd_to_m25p(mtd); -	uint32_t offset = ofs; -	uint8_t status_old, status_new; -	int res = 0; - -	mutex_lock(&flash->lock); -	/* Wait until finished previous command */ -	if (wait_till_ready(flash)) { -		res = 1; -		goto err; -	} - -	status_old = read_sr(flash); - -	if (offset < flash->mtd.size-(flash->mtd.size/2)) -		status_new = status_old | SR_BP2 | SR_BP1 | SR_BP0; -	else if (offset < flash->mtd.size-(flash->mtd.size/4)) -		status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; -	else if (offset < flash->mtd.size-(flash->mtd.size/8)) -		status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; -	else if (offset < flash->mtd.size-(flash->mtd.size/16)) -		status_new = (status_old & ~(SR_BP0|SR_BP1)) | SR_BP2; -	else if (offset < flash->mtd.size-(flash->mtd.size/32)) -		status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; -	else if (offset < flash->mtd.size-(flash->mtd.size/64)) -		status_new = (status_old & ~(SR_BP2|SR_BP0)) | SR_BP1; -	else -		status_new = (status_old & ~(SR_BP2|SR_BP1)) | SR_BP0; - -	/* Only modify protection if it will not unlock other areas */ -	if ((status_new&(SR_BP2|SR_BP1|SR_BP0)) > -					(status_old&(SR_BP2|SR_BP1|SR_BP0))) { -		write_enable(flash); -		if (write_sr(flash, status_new) < 0) { -			res = 1; -			goto err; -		} -	} - -err:	mutex_unlock(&flash->lock); -	return res; -} - -static int m25p80_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) -{ -	struct m25p *flash = mtd_to_m25p(mtd); -	uint32_t offset = ofs; -	uint8_t status_old, status_new; -	int res = 0; - -	mutex_lock(&flash->lock); -	/* Wait until finished previous command */ -	if (wait_till_ready(flash)) { -		res = 1; -		goto err; -	} - -	status_old = read_sr(flash); - -	if (offset+len > flash->mtd.size-(flash->mtd.size/64)) -		status_new = status_old & ~(SR_BP2|SR_BP1|SR_BP0); -	else if (offset+len > flash->mtd.size-(flash->mtd.size/32)) -		status_new = (status_old & ~(SR_BP2|SR_BP1)) | SR_BP0; -	else if (offset+len > flash->mtd.size-(flash->mtd.size/16)) -		status_new = (status_old & ~(SR_BP2|SR_BP0)) | SR_BP1; -	else if (offset+len > flash->mtd.size-(flash->mtd.size/8)) -		status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; -	else if (offset+len > flash->mtd.size-(flash->mtd.size/4)) -		status_new = (status_old & ~(SR_BP0|SR_BP1)) | SR_BP2; -	else if (offset+len > flash->mtd.size-(flash->mtd.size/2)) -		status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; -	else -		status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; - -	/* Only modify protection if it will not lock other areas */ -	if ((status_new&(SR_BP2|SR_BP1|SR_BP0)) < -					(status_old&(SR_BP2|SR_BP1|SR_BP0))) { -		write_enable(flash); -		if (write_sr(flash, status_new) < 0) { -			res = 1; -			goto err; -		} -	} - -err:	mutex_unlock(&flash->lock); -	return res; -} - -/****************************************************************************/ - -/* - * SPI device driver setup and teardown - */ - -struct flash_info { -	/* JEDEC id zero means "no ID" (most older chips); otherwise it has -	 * a high byte of zero plus three data bytes: the manufacturer id, -	 * then a two byte device id. -	 */ -	u32		jedec_id; -	u16             ext_id; - -	/* The size listed here is what works with OPCODE_SE, which isn't -	 * necessarily called a "sector" by the vendor. -	 */ -	unsigned	sector_size; -	u16		n_sectors; - -	u16		page_size; -	u16		addr_width; - -	u16		flags; -#define	SECT_4K		0x01		/* OPCODE_BE_4K works uniformly */ -#define	M25P_NO_ERASE	0x02		/* No erase command needed */ -#define	SST_WRITE	0x04		/* use SST byte programming */ -#define	M25P_NO_FR	0x08		/* Can't do fastread */ -#define	SECT_4K_PMC	0x10		/* OPCODE_BE_4K_PMC works uniformly */ -}; - -#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)	\ -	((kernel_ulong_t)&(struct flash_info) {				\ -		.jedec_id = (_jedec_id),				\ -		.ext_id = (_ext_id),					\ -		.sector_size = (_sector_size),				\ -		.n_sectors = (_n_sectors),				\ -		.page_size = 256,					\ -		.flags = (_flags),					\ -	}) - -#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags)	\ -	((kernel_ulong_t)&(struct flash_info) {				\ -		.sector_size = (_sector_size),				\ -		.n_sectors = (_n_sectors),				\ -		.page_size = (_page_size),				\ -		.addr_width = (_addr_width),				\ -		.flags = (_flags),					\ -	}) - -/* NOTE: double check command sets and memory organization when you add - * more flash chips.  This current list focusses on newer chips, which - * have been converging on command sets which including JEDEC ID. - */ -static const struct spi_device_id m25p_ids[] = { -	/* Atmel -- some are (confusingly) marketed as "DataFlash" */ -	{ "at25fs010",  INFO(0x1f6601, 0, 32 * 1024,   4, SECT_4K) }, -	{ "at25fs040",  INFO(0x1f6604, 0, 64 * 1024,   8, SECT_4K) }, - -	{ "at25df041a", INFO(0x1f4401, 0, 64 * 1024,   8, SECT_4K) }, -	{ "at25df321a", INFO(0x1f4701, 0, 64 * 1024,  64, SECT_4K) }, -	{ "at25df641",  INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) }, - -	{ "at26f004",   INFO(0x1f0400, 0, 64 * 1024,  8, SECT_4K) }, -	{ "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) }, -	{ "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) }, -	{ "at26df321",  INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) }, - -	{ "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) }, - -	/* EON -- en25xxx */ -	{ "en25f32", INFO(0x1c3116, 0, 64 * 1024,  64, SECT_4K) }, -	{ "en25p32", INFO(0x1c2016, 0, 64 * 1024,  64, 0) }, -	{ "en25q32b", INFO(0x1c3016, 0, 64 * 1024,  64, 0) }, -	{ "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, -	{ "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) }, -	{ "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) }, - -	/* Everspin */ -	{ "mr25h256", CAT25_INFO(  32 * 1024, 1, 256, 2, M25P_NO_ERASE | M25P_NO_FR) }, -	{ "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, M25P_NO_ERASE | M25P_NO_FR) }, - -	/* GigaDevice */ -	{ "gd25q32", INFO(0xc84016, 0, 64 * 1024,  64, SECT_4K) }, -	{ "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) }, - -	/* Intel/Numonyx -- xxxs33b */ -	{ "160s33b",  INFO(0x898911, 0, 64 * 1024,  32, 0) }, -	{ "320s33b",  INFO(0x898912, 0, 64 * 1024,  64, 0) }, -	{ "640s33b",  INFO(0x898913, 0, 64 * 1024, 128, 0) }, - -	/* Macronix */ -	{ "mx25l2005a",  INFO(0xc22012, 0, 64 * 1024,   4, SECT_4K) }, -	{ "mx25l4005a",  INFO(0xc22013, 0, 64 * 1024,   8, SECT_4K) }, -	{ "mx25l8005",   INFO(0xc22014, 0, 64 * 1024,  16, 0) }, -	{ "mx25l1606e",  INFO(0xc22015, 0, 64 * 1024,  32, SECT_4K) }, -	{ "mx25l3205d",  INFO(0xc22016, 0, 64 * 1024,  64, 0) }, -	{ "mx25l6405d",  INFO(0xc22017, 0, 64 * 1024, 128, 0) }, -	{ "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, -	{ "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, -	{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) }, -	{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, -	{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, 0) }, - -	/* Micron */ -	{ "n25q064",  INFO(0x20ba17, 0, 64 * 1024, 128, 0) }, -	{ "n25q128a11",  INFO(0x20bb18, 0, 64 * 1024, 256, 0) }, -	{ "n25q128a13",  INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, -	{ "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, - -	/* PMC */ -	{ "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, -	{ "pm25lv010", INFO(0, 0, 32 * 1024, 4, SECT_4K_PMC) }, -	{ "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024,  64, SECT_4K) }, - -	/* Spansion -- single (large) sector size only, at least -	 * for the chips listed here (without boot sectors). -	 */ -	{ "s25sl032p",  INFO(0x010215, 0x4d00,  64 * 1024,  64, 0) }, -	{ "s25sl064p",  INFO(0x010216, 0x4d00,  64 * 1024, 128, 0) }, -	{ "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) }, -	{ "s25fl256s1", INFO(0x010219, 0x4d01,  64 * 1024, 512, 0) }, -	{ "s25fl512s",  INFO(0x010220, 0x4d00, 256 * 1024, 256, 0) }, -	{ "s70fl01gs",  INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, -	{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024,  64, 0) }, -	{ "s25sl12801", INFO(0x012018, 0x0301,  64 * 1024, 256, 0) }, -	{ "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024,  64, 0) }, -	{ "s25fl129p1", INFO(0x012018, 0x4d01,  64 * 1024, 256, 0) }, -	{ "s25sl004a",  INFO(0x010212,      0,  64 * 1024,   8, 0) }, -	{ "s25sl008a",  INFO(0x010213,      0,  64 * 1024,  16, 0) }, -	{ "s25sl016a",  INFO(0x010214,      0,  64 * 1024,  32, 0) }, -	{ "s25sl032a",  INFO(0x010215,      0,  64 * 1024,  64, 0) }, -	{ "s25sl064a",  INFO(0x010216,      0,  64 * 1024, 128, 0) }, -	{ "s25fl016k",  INFO(0xef4015,      0,  64 * 1024,  32, SECT_4K) }, -	{ "s25fl064k",  INFO(0xef4017,      0,  64 * 1024, 128, SECT_4K) }, - -	/* SST -- large erase sizes are "overlays", "sectors" are 4K */ -	{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024,  8, SECT_4K | SST_WRITE) }, -	{ "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) }, -	{ "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K | SST_WRITE) }, -	{ "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K | SST_WRITE) }, -	{ "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, SECT_4K) }, -	{ "sst25wf512",  INFO(0xbf2501, 0, 64 * 1024,  1, SECT_4K | SST_WRITE) }, -	{ "sst25wf010",  INFO(0xbf2502, 0, 64 * 1024,  2, SECT_4K | SST_WRITE) }, -	{ "sst25wf020",  INFO(0xbf2503, 0, 64 * 1024,  4, SECT_4K | SST_WRITE) }, -	{ "sst25wf040",  INFO(0xbf2504, 0, 64 * 1024,  8, SECT_4K | SST_WRITE) }, - -	/* ST Microelectronics -- newer production may have feature updates */ -	{ "m25p05",  INFO(0x202010,  0,  32 * 1024,   2, 0) }, -	{ "m25p10",  INFO(0x202011,  0,  32 * 1024,   4, 0) }, -	{ "m25p20",  INFO(0x202012,  0,  64 * 1024,   4, 0) }, -	{ "m25p40",  INFO(0x202013,  0,  64 * 1024,   8, 0) }, -	{ "m25p80",  INFO(0x202014,  0,  64 * 1024,  16, 0) }, -	{ "m25p16",  INFO(0x202015,  0,  64 * 1024,  32, 0) }, -	{ "m25p32",  INFO(0x202016,  0,  64 * 1024,  64, 0) }, -	{ "m25p64",  INFO(0x202017,  0,  64 * 1024, 128, 0) }, -	{ "m25p128", INFO(0x202018,  0, 256 * 1024,  64, 0) }, -	{ "n25q032", INFO(0x20ba16,  0,  64 * 1024,  64, 0) }, - -	{ "m25p05-nonjedec",  INFO(0, 0,  32 * 1024,   2, 0) }, -	{ "m25p10-nonjedec",  INFO(0, 0,  32 * 1024,   4, 0) }, -	{ "m25p20-nonjedec",  INFO(0, 0,  64 * 1024,   4, 0) }, -	{ "m25p40-nonjedec",  INFO(0, 0,  64 * 1024,   8, 0) }, -	{ "m25p80-nonjedec",  INFO(0, 0,  64 * 1024,  16, 0) }, -	{ "m25p16-nonjedec",  INFO(0, 0,  64 * 1024,  32, 0) }, -	{ "m25p32-nonjedec",  INFO(0, 0,  64 * 1024,  64, 0) }, -	{ "m25p64-nonjedec",  INFO(0, 0,  64 * 1024, 128, 0) }, -	{ "m25p128-nonjedec", INFO(0, 0, 256 * 1024,  64, 0) }, - -	{ "m45pe10", INFO(0x204011,  0, 64 * 1024,    2, 0) }, -	{ "m45pe80", INFO(0x204014,  0, 64 * 1024,   16, 0) }, -	{ "m45pe16", INFO(0x204015,  0, 64 * 1024,   32, 0) }, - -	{ "m25pe20", INFO(0x208012,  0, 64 * 1024,  4,       0) }, -	{ "m25pe80", INFO(0x208014,  0, 64 * 1024, 16,       0) }, -	{ "m25pe16", INFO(0x208015,  0, 64 * 1024, 32, SECT_4K) }, - -	{ "m25px32",    INFO(0x207116,  0, 64 * 1024, 64, SECT_4K) }, -	{ "m25px32-s0", INFO(0x207316,  0, 64 * 1024, 64, SECT_4K) }, -	{ "m25px32-s1", INFO(0x206316,  0, 64 * 1024, 64, SECT_4K) }, -	{ "m25px64",    INFO(0x207117,  0, 64 * 1024, 128, 0) }, - -	/* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ -	{ "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) }, -	{ "w25x80", INFO(0xef3014, 0, 64 * 1024,  16, SECT_4K) }, -	{ "w25x16", INFO(0xef3015, 0, 64 * 1024,  32, SECT_4K) }, -	{ "w25x32", INFO(0xef3016, 0, 64 * 1024,  64, SECT_4K) }, -	{ "w25q32", INFO(0xef4016, 0, 64 * 1024,  64, SECT_4K) }, -	{ "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) }, -	{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, 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) }, -	{ "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) }, - -	/* Catalyst / On Semiconductor -- non-JEDEC */ -	{ "cat25c11", CAT25_INFO(  16, 8, 16, 1, M25P_NO_ERASE | M25P_NO_FR) }, -	{ "cat25c03", CAT25_INFO(  32, 8, 16, 2, M25P_NO_ERASE | M25P_NO_FR) }, -	{ "cat25c09", CAT25_INFO( 128, 8, 32, 2, M25P_NO_ERASE | M25P_NO_FR) }, -	{ "cat25c17", CAT25_INFO( 256, 8, 32, 2, M25P_NO_ERASE | M25P_NO_FR) }, -	{ "cat25128", CAT25_INFO(2048, 8, 64, 2, M25P_NO_ERASE | M25P_NO_FR) }, -	{ }, -}; -MODULE_DEVICE_TABLE(spi, m25p_ids); - -static const struct spi_device_id *jedec_probe(struct spi_device *spi) -{ -	int			tmp; -	u8			code = OPCODE_RDID; -	u8			id[5]; -	u32			jedec; -	u16                     ext_jedec; -	struct flash_info	*info; - -	/* JEDEC also defines an optional "extended device information" -	 * string for after vendor-specific data, after the three bytes -	 * we use here.  Supporting some chips might require using it. -	 */ -	tmp = spi_write_then_read(spi, &code, 1, id, 5); -	if (tmp < 0) { -		pr_debug("%s: error %d reading JEDEC ID\n", -				dev_name(&spi->dev), tmp); -		return ERR_PTR(tmp); -	} -	jedec = id[0]; -	jedec = jedec << 8; -	jedec |= id[1]; -	jedec = jedec << 8; -	jedec |= id[2]; +	/* Set up command buffer. */ +	flash->command[0] = nor->erase_opcode; +	m25p_addr2cmd(nor, offset, flash->command); -	ext_jedec = id[3] << 8 | id[4]; +	spi_write(flash->spi, flash->command, m25p_cmdsz(nor)); -	for (tmp = 0; tmp < ARRAY_SIZE(m25p_ids) - 1; tmp++) { -		info = (void *)m25p_ids[tmp].driver_data; -		if (info->jedec_id == jedec) { -			if (info->ext_id != 0 && info->ext_id != ext_jedec) -				continue; -			return &m25p_ids[tmp]; -		} -	} -	dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec); -	return ERR_PTR(-ENODEV); +	return 0;  } -  /*   * board specific setup should have ensured the SPI clock used here   * matches what the READ command supports, at least until this driver @@ -921,196 +191,45 @@ static const struct spi_device_id *jedec_probe(struct spi_device *spi)   */  static int m25p_probe(struct spi_device *spi)  { -	const struct spi_device_id	*id = spi_get_device_id(spi); -	struct flash_platform_data	*data; -	struct m25p			*flash; -	struct flash_info		*info; -	unsigned			i;  	struct mtd_part_parser_data	ppdata; -	struct device_node __maybe_unused *np = spi->dev.of_node; - -#ifdef CONFIG_MTD_OF_PARTS -	if (!of_device_is_available(np)) -		return -ENODEV; -#endif - -	/* Platform data helps sort out which chip type we have, as -	 * well as how this board partitions it.  If we don't have -	 * a chip ID, try the JEDEC id commands; they'll work for most -	 * newer chips, even if we don't recognize the particular chip. -	 */ -	data = dev_get_platdata(&spi->dev); -	if (data && data->type) { -		const struct spi_device_id *plat_id; - -		for (i = 0; i < ARRAY_SIZE(m25p_ids) - 1; i++) { -			plat_id = &m25p_ids[i]; -			if (strcmp(data->type, plat_id->name)) -				continue; -			break; -		} - -		if (i < ARRAY_SIZE(m25p_ids) - 1) -			id = plat_id; -		else -			dev_warn(&spi->dev, "unrecognized id %s\n", data->type); -	} - -	info = (void *)id->driver_data; - -	if (info->jedec_id) { -		const struct spi_device_id *jid; - -		jid = jedec_probe(spi); -		if (IS_ERR(jid)) { -			return PTR_ERR(jid); -		} else if (jid != id) { -			/* -			 * JEDEC knows better, so overwrite platform ID. We -			 * can't trust partitions any longer, but we'll let -			 * mtd apply them anyway, since some partitions may be -			 * marked read-only, and we don't want to lose that -			 * information, even if it's not 100% accurate. -			 */ -			dev_warn(&spi->dev, "found %s, expected %s\n", -				 jid->name, id->name); -			id = jid; -			info = (void *)jid->driver_data; -		} -	} +	struct flash_platform_data	*data; +	struct m25p *flash; +	struct spi_nor *nor; +	enum read_mode mode = SPI_NOR_NORMAL; +	int ret; -	flash = kzalloc(sizeof *flash, GFP_KERNEL); +	flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL);  	if (!flash)  		return -ENOMEM; -	flash->command = kmalloc(MAX_CMD_SIZE + (flash->fast_read ? 1 : 0), -					GFP_KERNEL); -	if (!flash->command) { -		kfree(flash); -		return -ENOMEM; -	} -	flash->spi = spi; -	mutex_init(&flash->lock); -	spi_set_drvdata(spi, flash); +	nor = &flash->spi_nor; -	/* -	 * Atmel, SST and Intel/Numonyx serial flash tend to power -	 * up with the software protection bits set -	 */ - -	if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ATMEL || -	    JEDEC_MFR(info->jedec_id) == CFI_MFR_INTEL || -	    JEDEC_MFR(info->jedec_id) == CFI_MFR_SST) { -		write_enable(flash); -		write_sr(flash, 0); -	} - -	if (data && data->name) -		flash->mtd.name = data->name; -	else -		flash->mtd.name = dev_name(&spi->dev); - -	flash->mtd.type = MTD_NORFLASH; -	flash->mtd.writesize = 1; -	flash->mtd.flags = MTD_CAP_NORFLASH; -	flash->mtd.size = info->sector_size * info->n_sectors; -	flash->mtd._erase = m25p80_erase; -	flash->mtd._read = m25p80_read; - -	/* flash protection support for STmicro chips */ -	if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) { -		flash->mtd._lock = m25p80_lock; -		flash->mtd._unlock = m25p80_unlock; -	} +	/* install the hooks */ +	nor->read = m25p80_read; +	nor->write = m25p80_write; +	nor->erase = m25p80_erase; +	nor->write_reg = m25p80_write_reg; +	nor->read_reg = m25p80_read_reg; -	/* sst flash chips use AAI word program */ -	if (info->flags & SST_WRITE) -		flash->mtd._write = sst_write; -	else -		flash->mtd._write = m25p80_write; +	nor->dev = &spi->dev; +	nor->mtd = &flash->mtd; +	nor->priv = flash; -	/* prefer "small sector" erase if possible */ -	if (info->flags & SECT_4K) { -		flash->erase_opcode = OPCODE_BE_4K; -		flash->mtd.erasesize = 4096; -	} else if (info->flags & SECT_4K_PMC) { -		flash->erase_opcode = OPCODE_BE_4K_PMC; -		flash->mtd.erasesize = 4096; -	} else { -		flash->erase_opcode = OPCODE_SE; -		flash->mtd.erasesize = info->sector_size; -	} +	spi_set_drvdata(spi, flash); +	flash->mtd.priv = nor; +	flash->spi = spi; -	if (info->flags & M25P_NO_ERASE) -		flash->mtd.flags |= MTD_NO_ERASE; +	if (spi->mode & SPI_RX_QUAD) +		mode = SPI_NOR_QUAD; +	else if (spi->mode & SPI_RX_DUAL) +		mode = SPI_NOR_DUAL; +	ret = spi_nor_scan(nor, spi_get_device_id(spi), mode); +	if (ret) +		return ret; +	data = dev_get_platdata(&spi->dev);  	ppdata.of_node = spi->dev.of_node; -	flash->mtd.dev.parent = &spi->dev; -	flash->page_size = info->page_size; -	flash->mtd.writebufsize = flash->page_size; - -	flash->fast_read = false; -	if (np && of_property_read_bool(np, "m25p,fast-read")) -		flash->fast_read = true; - -#ifdef CONFIG_M25PXX_USE_FAST_READ -	flash->fast_read = true; -#endif -	if (info->flags & M25P_NO_FR) -		flash->fast_read = false; - -	/* Default commands */ -	if (flash->fast_read) -		flash->read_opcode = OPCODE_FAST_READ; -	else -		flash->read_opcode = OPCODE_NORM_READ; - -	flash->program_opcode = OPCODE_PP; -	if (info->addr_width) -		flash->addr_width = info->addr_width; -	else if (flash->mtd.size > 0x1000000) { -		/* enable 4-byte addressing if the device exceeds 16MiB */ -		flash->addr_width = 4; -		if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) { -			/* Dedicated 4-byte command set */ -			flash->read_opcode = flash->fast_read ? -				OPCODE_FAST_READ_4B : -				OPCODE_NORM_READ_4B; -			flash->program_opcode = OPCODE_PP_4B; -			/* No small sector erase for 4-byte command set */ -			flash->erase_opcode = OPCODE_SE_4B; -			flash->mtd.erasesize = info->sector_size; -		} else -			set_4byte(flash, info->jedec_id, 1); -	} else { -		flash->addr_width = 3; -	} - -	dev_info(&spi->dev, "%s (%lld Kbytes)\n", id->name, -			(long long)flash->mtd.size >> 10); - -	pr_debug("mtd .name = %s, .size = 0x%llx (%lldMiB) " -			".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n", -		flash->mtd.name, -		(long long)flash->mtd.size, (long long)(flash->mtd.size >> 20), -		flash->mtd.erasesize, flash->mtd.erasesize / 1024, -		flash->mtd.numeraseregions); - -	if (flash->mtd.numeraseregions) -		for (i = 0; i < flash->mtd.numeraseregions; i++) -			pr_debug("mtd.eraseregions[%d] = { .offset = 0x%llx, " -				".erasesize = 0x%.8x (%uKiB), " -				".numblocks = %d }\n", -				i, (long long)flash->mtd.eraseregions[i].offset, -				flash->mtd.eraseregions[i].erasesize, -				flash->mtd.eraseregions[i].erasesize / 1024, -				flash->mtd.eraseregions[i].numblocks); - - -	/* partitions should match sector boundaries; and it may be good to -	 * use readonly partitions for writeprotected sectors (BP2..BP0). -	 */  	return mtd_device_parse_register(&flash->mtd, NULL, &ppdata,  			data ? data->parts : NULL,  			data ? data->nr_parts : 0); @@ -1120,15 +239,9 @@ static int m25p_probe(struct spi_device *spi)  static int m25p_remove(struct spi_device *spi)  {  	struct m25p	*flash = spi_get_drvdata(spi); -	int		status;  	/* Clean up MTD stuff. */ -	status = mtd_device_unregister(&flash->mtd); -	if (status == 0) { -		kfree(flash->command); -		kfree(flash); -	} -	return 0; +	return mtd_device_unregister(&flash->mtd);  } @@ -1137,7 +250,7 @@ static struct spi_driver m25p80_driver = {  		.name	= "m25p80",  		.owner	= THIS_MODULE,  	}, -	.id_table	= m25p_ids, +	.id_table	= spi_nor_ids,  	.probe	= m25p_probe,  	.remove	= m25p_remove, diff --git a/drivers/mtd/devices/ms02-nv.c b/drivers/mtd/devices/ms02-nv.c index 182849d39c6..5c8b322ba90 100644 --- a/drivers/mtd/devices/ms02-nv.c +++ b/drivers/mtd/devices/ms02-nv.c @@ -205,7 +205,7 @@ static int __init ms02nv_init_one(ulong addr)  	mtd->type = MTD_RAM;  	mtd->flags = MTD_CAP_RAM;  	mtd->size = fixsize; -	mtd->name = (char *)ms02nv_name; +	mtd->name = ms02nv_name;  	mtd->owner = THIS_MODULE;  	mtd->_read = ms02nv_read;  	mtd->_write = ms02nv_write; diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 0e8cbfeba11..dd22ce2cc9a 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -10,7 +10,6 @@   * 2 of the License, or (at your option) any later version.  */  #include <linux/module.h> -#include <linux/init.h>  #include <linux/slab.h>  #include <linux/delay.h>  #include <linux/device.h> @@ -88,8 +87,6 @@ struct dataflash {  	uint8_t			command[4];  	char			name[24]; -	unsigned		partitioned:1; -  	unsigned short		page_offset;	/* offset in flash address */  	unsigned int		page_size;	/* of bytes per page */ @@ -442,8 +439,8 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,  #ifdef CONFIG_MTD_DATAFLASH_OTP -static int dataflash_get_otp_info(struct mtd_info *mtd, -		struct otp_info *info, size_t len) +static int dataflash_get_otp_info(struct mtd_info *mtd, size_t len, +				  size_t *retlen, struct otp_info *info)  {  	/* Report both blocks as identical:  bytes 0..64, locked.  	 * Unless the user block changed from all-ones, we can't @@ -452,7 +449,8 @@ static int dataflash_get_otp_info(struct mtd_info *mtd,  	info->start = 0;  	info->length = 64;  	info->locked = 1; -	return sizeof(*info); +	*retlen = sizeof(*info); +	return 0;  }  static ssize_t otp_read(struct spi_device *spi, unsigned base, @@ -544,14 +542,18 @@ static int dataflash_write_user_otp(struct mtd_info *mtd,  	struct dataflash	*priv = mtd->priv;  	int			status; -	if (len > 64) -		return -EINVAL; +	if (from >= 64) { +		/* +		 * Attempting to write beyond the end of OTP memory, +		 * no data can be written. +		 */ +		*retlen = 0; +		return 0; +	} -	/* Strictly speaking, we *could* truncate the write ... but -	 * let's not do that for the only write that's ever possible. -	 */ +	/* Truncate the write to fit into OTP memory. */  	if ((from + len) > 64) -		return -EINVAL; +		len = 64 - from;  	/* OUT: OP_WRITE_SECURITY, 3 zeroes, 64 data-or-zero bytes  	 * IN:  ignore all @@ -671,7 +673,6 @@ static int add_dataflash_otp(struct spi_device *spi, char *name, int nr_pages,  	if (!err)  		return 0; -	spi_set_drvdata(spi, NULL);  	kfree(priv);  	return err;  } @@ -881,7 +882,7 @@ static int dataflash_probe(struct spi_device *spi)  		break;  	/* obsolete AT45DB1282 not (yet?) supported */  	default: -		pr_debug("%s: unsupported device (%x)\n", dev_name(&spi->dev), +		dev_info(&spi->dev, "unsupported device (%x)\n",  				status & 0x3c);  		status = -ENODEV;  	} @@ -901,10 +902,8 @@ static int dataflash_remove(struct spi_device *spi)  	pr_debug("%s: remove\n", dev_name(&spi->dev));  	status = mtd_device_unregister(&flash->mtd); -	if (status == 0) { -		spi_set_drvdata(spi, NULL); +	if (status == 0)  		kfree(flash); -	}  	return status;  } diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c index ec59d65897f..8e285089229 100644 --- a/drivers/mtd/devices/mtdram.c +++ b/drivers/mtd/devices/mtdram.c @@ -92,7 +92,7 @@ static void __exit cleanup_mtdram(void)  }  int mtdram_init_device(struct mtd_info *mtd, void *mapped_address, -		unsigned long size, char *name) +		unsigned long size, const char *name)  {  	memset(mtd, 0, sizeof(*mtd)); diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index 67823de68db..2cceebfb251 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -94,7 +94,7 @@ static void unregister_devices(void)  	}  } -static int register_device(char *name, unsigned long start, unsigned long len) +static int register_device(char *name, phys_addr_t start, size_t len)  {  	struct phram_mtd_list *new;  	int ret = -ENOMEM; @@ -141,35 +141,35 @@ out0:  	return ret;  } -static int ustrtoul(const char *cp, char **endp, unsigned int base) +static int parse_num64(uint64_t *num64, char *token)  { -	unsigned long result = simple_strtoul(cp, endp, base); - -	switch (**endp) { -	case 'G': -		result *= 1024; -	case 'M': -		result *= 1024; -	case 'k': -		result *= 1024; +	size_t len; +	int shift = 0; +	int ret; + +	len = strlen(token);  	/* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */ -		if ((*endp)[1] == 'i') -			(*endp) += 2; +	if (len > 2) { +		if (token[len - 1] == 'i') { +			switch (token[len - 2]) { +			case 'G': +				shift += 10; +			case 'M': +				shift += 10; +			case 'k': +				shift += 10; +				token[len - 2] = 0; +				break; +			default: +				return -EINVAL; +			} +		}  	} -	return result; -} - -static int parse_num32(uint32_t *num32, const char *token) -{ -	char *endp; -	unsigned long n; -	n = ustrtoul(token, &endp, 0); -	if (*endp) -		return -EINVAL; +	ret = kstrtou64(token, 0, num64); +	*num64 <<= shift; -	*num32 = n; -	return 0; +	return ret;  }  static int parse_name(char **pname, const char *token) @@ -205,23 +205,26 @@ static inline void kill_final_newline(char *str)  	return 1;		\  } while (0) +#ifndef MODULE +static int phram_init_called;  /*   * This shall contain the module parameter if any. It is of the form:   * - phram=<device>,<address>,<size> for module case   * - phram.phram=<device>,<address>,<size> for built-in case - * We leave 64 bytes for the device name, 12 for the address and 12 for the + * We leave 64 bytes for the device name, 20 for the address and 20 for the   * size.   * Example: phram.phram=rootfs,0xa0000000,512Mi   */ -static __initdata char phram_paramline[64+12+12]; +static char phram_paramline[64 + 20 + 20]; +#endif -static int __init phram_setup(const char *val) +static int phram_setup(const char *val)  { -	char buf[64+12+12], *str = buf; +	char buf[64 + 20 + 20], *str = buf;  	char *token[3];  	char *name; -	uint32_t start; -	uint32_t len; +	uint64_t start; +	uint64_t len;  	int i, ret;  	if (strnlen(val, sizeof(buf)) >= sizeof(buf)) @@ -243,13 +246,13 @@ static int __init phram_setup(const char *val)  	if (ret)  		return ret; -	ret = parse_num32(&start, token[1]); +	ret = parse_num64(&start, token[1]);  	if (ret) {  		kfree(name);  		parse_err("illegal start address\n");  	} -	ret = parse_num32(&len, token[2]); +	ret = parse_num64(&len, token[2]);  	if (ret) {  		kfree(name);  		parse_err("illegal device length\n"); @@ -257,24 +260,43 @@ static int __init phram_setup(const char *val)  	ret = register_device(name, start, len);  	if (!ret) -		pr_info("%s device: %#x at %#x\n", name, len, start); +		pr_info("%s device: %#llx at %#llx\n", name, len, start);  	else  		kfree(name);  	return ret;  } -static int __init phram_param_call(const char *val, struct kernel_param *kp) +static int phram_param_call(const char *val, struct kernel_param *kp)  { +#ifdef MODULE +	return phram_setup(val); +#else  	/* -	 * This function is always called before 'init_phram()', whether -	 * built-in or module. +	 * If more parameters are later passed in via +	 * /sys/module/phram/parameters/phram +	 * and init_phram() has already been called, +	 * we can parse the argument now.  	 */ + +	if (phram_init_called) +		return phram_setup(val); + +	/* +	 * During early boot stage, we only save the parameters +	 * here. We must parse them later: if the param passed +	 * from kernel boot command line, phram_param_call() is +	 * called so early that it is not possible to resolve +	 * the device (even kmalloc() fails). Defer that work to +	 * phram_setup(). +	 */ +  	if (strlen(val) >= sizeof(phram_paramline))  		return -ENOSPC;  	strcpy(phram_paramline, val);  	return 0; +#endif  }  module_param_call(phram, phram_param_call, NULL, NULL, 000); @@ -283,10 +305,15 @@ MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>\"  static int __init init_phram(void)  { +	int ret = 0; + +#ifndef MODULE  	if (phram_paramline[0]) -		return phram_setup(phram_paramline); +		ret = phram_setup(phram_paramline); +	phram_init_called = 1; +#endif -	return 0; +	return ret;  }  static void __exit cleanup_phram(void) diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index 0c51b988e1f..f02603e1bfe 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -725,16 +725,11 @@ static int __init init_pmc551(void)  		}  		mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); -		if (!mtd) { -			printk(KERN_NOTICE "pmc551: Cannot allocate new MTD " -				"device.\n"); +		if (!mtd)  			break; -		}  		priv = kzalloc(sizeof(struct mypriv), GFP_KERNEL);  		if (!priv) { -			printk(KERN_NOTICE "pmc551: Cannot allocate new MTD " -				"device.\n");  			kfree(mtd);  			break;  		} diff --git a/drivers/mtd/devices/serial_flash_cmds.h b/drivers/mtd/devices/serial_flash_cmds.h new file mode 100644 index 00000000000..f59a125295d --- /dev/null +++ b/drivers/mtd/devices/serial_flash_cmds.h @@ -0,0 +1,61 @@ +/* + * Generic/SFDP Flash Commands and Device Capabilities + * + * Copyright (C) 2013 Lee Jones <lee.jones@lianro.org> + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef _MTD_SERIAL_FLASH_CMDS_H +#define _MTD_SERIAL_FLASH_CMDS_H + +/* Generic Flash Commands/OPCODEs */ +#define SPINOR_OP_RDSR2		0x35 +#define SPINOR_OP_WRVCR		0x81 +#define SPINOR_OP_RDVCR		0x85 + +/* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */ +#define SPINOR_OP_READ_1_2_2	0xbb	/* DUAL I/O READ */ +#define SPINOR_OP_READ_1_4_4	0xeb	/* QUAD I/O READ */ + +#define SPINOR_OP_WRITE		0x02	/* PAGE PROGRAM */ +#define SPINOR_OP_WRITE_1_1_2	0xa2	/* DUAL INPUT PROGRAM */ +#define SPINOR_OP_WRITE_1_2_2	0xd2	/* DUAL INPUT EXT PROGRAM */ +#define SPINOR_OP_WRITE_1_1_4	0x32	/* QUAD INPUT PROGRAM */ +#define SPINOR_OP_WRITE_1_4_4	0x12	/* QUAD INPUT EXT PROGRAM */ + +/* READ commands with 32-bit addressing */ +#define SPINOR_OP_READ4_1_2_2	0xbc +#define SPINOR_OP_READ4_1_4_4	0xec + +/* Configuration flags */ +#define FLASH_FLAG_SINGLE	0x000000ff +#define FLASH_FLAG_READ_WRITE	0x00000001 +#define FLASH_FLAG_READ_FAST	0x00000002 +#define FLASH_FLAG_SE_4K	0x00000004 +#define FLASH_FLAG_SE_32K	0x00000008 +#define FLASH_FLAG_CE		0x00000010 +#define FLASH_FLAG_32BIT_ADDR	0x00000020 +#define FLASH_FLAG_RESET	0x00000040 +#define FLASH_FLAG_DYB_LOCKING	0x00000080 + +#define FLASH_FLAG_DUAL		0x0000ff00 +#define FLASH_FLAG_READ_1_1_2	0x00000100 +#define FLASH_FLAG_READ_1_2_2	0x00000200 +#define FLASH_FLAG_READ_2_2_2	0x00000400 +#define FLASH_FLAG_WRITE_1_1_2	0x00001000 +#define FLASH_FLAG_WRITE_1_2_2	0x00002000 +#define FLASH_FLAG_WRITE_2_2_2	0x00004000 + +#define FLASH_FLAG_QUAD		0x00ff0000 +#define FLASH_FLAG_READ_1_1_4	0x00010000 +#define FLASH_FLAG_READ_1_4_4	0x00020000 +#define FLASH_FLAG_READ_4_4_4	0x00040000 +#define FLASH_FLAG_WRITE_1_1_4	0x00100000 +#define FLASH_FLAG_WRITE_1_4_4	0x00200000 +#define FLASH_FLAG_WRITE_4_4_4	0x00400000 + +#endif /* _MTD_SERIAL_FLASH_CMDS_H */ diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c index 5a5cd2ace4a..2fc4957cbe7 100644 --- a/drivers/mtd/devices/slram.c +++ b/drivers/mtd/devices/slram.c @@ -280,14 +280,11 @@ __setup("slram=", mtd_slram_setup);  static int __init init_slram(void)  {  	char *devname; -	int i;  #ifndef MODULE  	char *devstart;  	char *devlength; -	i = 0; -  	if (!map) {  		E("slram: not enough parameters.\n");  		return(-EINVAL); @@ -314,6 +311,7 @@ static int __init init_slram(void)  	}  #else  	int count; +	int i;  	for (count = 0; count < SLRAM_MAX_DEVICES_PARAMS && map[count];  			count++) { diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c index 42382141206..c4176b0f382 100644 --- a/drivers/mtd/devices/spear_smi.c +++ b/drivers/mtd/devices/spear_smi.c @@ -6,7 +6,7 @@   *   * Copyright © 2010 STMicroelectronics.   * Ashish Priyadarshi - * Shiraz Hashim <shiraz.hashim@st.com> + * Shiraz Hashim <shiraz.linux.kernel@gmail.com>   *   * This file is licensed under the terms of the GNU General Public   * License version 2. This program is licensed "as is" without any @@ -913,7 +913,6 @@ static int spear_smi_probe(struct platform_device *pdev)  	if (np) {  		pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);  		if (!pdata) { -			pr_err("%s: ERROR: no memory", __func__);  			ret = -ENOMEM;  			goto err;  		} @@ -943,7 +942,6 @@ static int spear_smi_probe(struct platform_device *pdev)  	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);  	if (!dev) {  		ret = -ENOMEM; -		dev_err(&pdev->dev, "mem alloc fail\n");  		goto err;  	} @@ -1091,5 +1089,5 @@ static struct platform_driver spear_smi_driver = {  module_platform_driver(spear_smi_driver);  MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Ashish Priyadarshi, Shiraz Hashim <shiraz.hashim@st.com>"); +MODULE_AUTHOR("Ashish Priyadarshi, Shiraz Hashim <shiraz.linux.kernel@gmail.com>");  MODULE_DESCRIPTION("MTD SMI driver for serial nor flash chips"); diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c index a42f1f0e728..c63ecbcad0b 100644 --- a/drivers/mtd/devices/sst25l.c +++ b/drivers/mtd/devices/sst25l.c @@ -15,7 +15,6 @@   *   */ -#include <linux/init.h>  #include <linux/module.h>  #include <linux/device.h>  #include <linux/mutex.h> @@ -364,7 +363,7 @@ static int sst25l_probe(struct spi_device *spi)  	if (!flash_info)  		return -ENODEV; -	flash = kzalloc(sizeof(struct sst25l_flash), GFP_KERNEL); +	flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL);  	if (!flash)  		return -ENOMEM; @@ -402,11 +401,8 @@ static int sst25l_probe(struct spi_device *spi)  	ret = mtd_device_parse_register(&flash->mtd, NULL, NULL,  					data ? data->parts : NULL,  					data ? data->nr_parts : 0); -	if (ret) { -		kfree(flash); -		spi_set_drvdata(spi, NULL); +	if (ret)  		return -ENODEV; -	}  	return 0;  } @@ -414,12 +410,8 @@ static int sst25l_probe(struct spi_device *spi)  static int sst25l_remove(struct spi_device *spi)  {  	struct sst25l_flash *flash = spi_get_drvdata(spi); -	int ret; -	ret = mtd_device_unregister(&flash->mtd); -	if (ret == 0) -		kfree(flash); -	return ret; +	return mtd_device_unregister(&flash->mtd);  }  static struct spi_driver sst25l_driver = { diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c new file mode 100644 index 00000000000..d252514d3e9 --- /dev/null +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -0,0 +1,2080 @@ +/* + * st_spi_fsm.c	- ST Fast Sequence Mode (FSM) Serial Flash Controller + * + * Author: Angus Clark <angus.clark@st.com> + * + * Copyright (C) 2010-2014 STMicroelectronics Limited + * + * JEDEC probe based on drivers/mtd/devices/m25p80.c + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/platform_device.h> +#include <linux/mfd/syscon.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/spi-nor.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/of.h> + +#include "serial_flash_cmds.h" + +/* + * FSM SPI Controller Registers + */ +#define SPI_CLOCKDIV			0x0010 +#define SPI_MODESELECT			0x0018 +#define SPI_CONFIGDATA			0x0020 +#define SPI_STA_MODE_CHANGE		0x0028 +#define SPI_FAST_SEQ_TRANSFER_SIZE	0x0100 +#define SPI_FAST_SEQ_ADD1		0x0104 +#define SPI_FAST_SEQ_ADD2		0x0108 +#define SPI_FAST_SEQ_ADD_CFG		0x010c +#define SPI_FAST_SEQ_OPC1		0x0110 +#define SPI_FAST_SEQ_OPC2		0x0114 +#define SPI_FAST_SEQ_OPC3		0x0118 +#define SPI_FAST_SEQ_OPC4		0x011c +#define SPI_FAST_SEQ_OPC5		0x0120 +#define SPI_MODE_BITS			0x0124 +#define SPI_DUMMY_BITS			0x0128 +#define SPI_FAST_SEQ_FLASH_STA_DATA	0x012c +#define SPI_FAST_SEQ_1			0x0130 +#define SPI_FAST_SEQ_2			0x0134 +#define SPI_FAST_SEQ_3			0x0138 +#define SPI_FAST_SEQ_4			0x013c +#define SPI_FAST_SEQ_CFG		0x0140 +#define SPI_FAST_SEQ_STA		0x0144 +#define SPI_QUAD_BOOT_SEQ_INIT_1	0x0148 +#define SPI_QUAD_BOOT_SEQ_INIT_2	0x014c +#define SPI_QUAD_BOOT_READ_SEQ_1	0x0150 +#define SPI_QUAD_BOOT_READ_SEQ_2	0x0154 +#define SPI_PROGRAM_ERASE_TIME		0x0158 +#define SPI_MULT_PAGE_REPEAT_SEQ_1	0x015c +#define SPI_MULT_PAGE_REPEAT_SEQ_2	0x0160 +#define SPI_STATUS_WR_TIME_REG		0x0164 +#define SPI_FAST_SEQ_DATA_REG		0x0300 + +/* + * Register: SPI_MODESELECT + */ +#define SPI_MODESELECT_CONTIG		0x01 +#define SPI_MODESELECT_FASTREAD		0x02 +#define SPI_MODESELECT_DUALIO		0x04 +#define SPI_MODESELECT_FSM		0x08 +#define SPI_MODESELECT_QUADBOOT		0x10 + +/* + * Register: SPI_CONFIGDATA + */ +#define SPI_CFG_DEVICE_ST		0x1 +#define SPI_CFG_DEVICE_ATMEL		0x4 +#define SPI_CFG_MIN_CS_HIGH(x)		(((x) & 0xfff) << 4) +#define SPI_CFG_CS_SETUPHOLD(x)		(((x) & 0xff) << 16) +#define SPI_CFG_DATA_HOLD(x)		(((x) & 0xff) << 24) + +#define SPI_CFG_DEFAULT_MIN_CS_HIGH    SPI_CFG_MIN_CS_HIGH(0x0AA) +#define SPI_CFG_DEFAULT_CS_SETUPHOLD   SPI_CFG_CS_SETUPHOLD(0xA0) +#define SPI_CFG_DEFAULT_DATA_HOLD      SPI_CFG_DATA_HOLD(0x00) + +/* + * Register: SPI_FAST_SEQ_TRANSFER_SIZE + */ +#define TRANSFER_SIZE(x)		((x) * 8) + +/* + * Register: SPI_FAST_SEQ_ADD_CFG + */ +#define ADR_CFG_CYCLES_ADD1(x)		((x) << 0) +#define ADR_CFG_PADS_1_ADD1		(0x0 << 6) +#define ADR_CFG_PADS_2_ADD1		(0x1 << 6) +#define ADR_CFG_PADS_4_ADD1		(0x3 << 6) +#define ADR_CFG_CSDEASSERT_ADD1		(1   << 8) +#define ADR_CFG_CYCLES_ADD2(x)		((x) << (0+16)) +#define ADR_CFG_PADS_1_ADD2		(0x0 << (6+16)) +#define ADR_CFG_PADS_2_ADD2		(0x1 << (6+16)) +#define ADR_CFG_PADS_4_ADD2		(0x3 << (6+16)) +#define ADR_CFG_CSDEASSERT_ADD2		(1   << (8+16)) + +/* + * Register: SPI_FAST_SEQ_n + */ +#define SEQ_OPC_OPCODE(x)		((x) << 0) +#define SEQ_OPC_CYCLES(x)		((x) << 8) +#define SEQ_OPC_PADS_1			(0x0 << 14) +#define SEQ_OPC_PADS_2			(0x1 << 14) +#define SEQ_OPC_PADS_4			(0x3 << 14) +#define SEQ_OPC_CSDEASSERT		(1   << 16) + +/* + * Register: SPI_FAST_SEQ_CFG + */ +#define SEQ_CFG_STARTSEQ		(1 << 0) +#define SEQ_CFG_SWRESET			(1 << 5) +#define SEQ_CFG_CSDEASSERT		(1 << 6) +#define SEQ_CFG_READNOTWRITE		(1 << 7) +#define SEQ_CFG_ERASE			(1 << 8) +#define SEQ_CFG_PADS_1			(0x0 << 16) +#define SEQ_CFG_PADS_2			(0x1 << 16) +#define SEQ_CFG_PADS_4			(0x3 << 16) + +/* + * Register: SPI_MODE_BITS + */ +#define MODE_DATA(x)			(x & 0xff) +#define MODE_CYCLES(x)			((x & 0x3f) << 16) +#define MODE_PADS_1			(0x0 << 22) +#define MODE_PADS_2			(0x1 << 22) +#define MODE_PADS_4			(0x3 << 22) +#define DUMMY_CSDEASSERT		(1   << 24) + +/* + * Register: SPI_DUMMY_BITS + */ +#define DUMMY_CYCLES(x)			((x & 0x3f) << 16) +#define DUMMY_PADS_1			(0x0 << 22) +#define DUMMY_PADS_2			(0x1 << 22) +#define DUMMY_PADS_4			(0x3 << 22) +#define DUMMY_CSDEASSERT		(1   << 24) + +/* + * Register: SPI_FAST_SEQ_FLASH_STA_DATA + */ +#define STA_DATA_BYTE1(x)		((x & 0xff) << 0) +#define STA_DATA_BYTE2(x)		((x & 0xff) << 8) +#define STA_PADS_1			(0x0 << 16) +#define STA_PADS_2			(0x1 << 16) +#define STA_PADS_4			(0x3 << 16) +#define STA_CSDEASSERT			(0x1 << 20) +#define STA_RDNOTWR			(0x1 << 21) + +/* + * FSM SPI Instruction Opcodes + */ +#define STFSM_OPC_CMD			0x1 +#define STFSM_OPC_ADD			0x2 +#define STFSM_OPC_STA			0x3 +#define STFSM_OPC_MODE			0x4 +#define STFSM_OPC_DUMMY		0x5 +#define STFSM_OPC_DATA			0x6 +#define STFSM_OPC_WAIT			0x7 +#define STFSM_OPC_JUMP			0x8 +#define STFSM_OPC_GOTO			0x9 +#define STFSM_OPC_STOP			0xF + +/* + * FSM SPI Instructions (== opcode + operand). + */ +#define STFSM_INSTR(cmd, op)		((cmd) | ((op) << 4)) + +#define STFSM_INST_CMD1			STFSM_INSTR(STFSM_OPC_CMD,	1) +#define STFSM_INST_CMD2			STFSM_INSTR(STFSM_OPC_CMD,	2) +#define STFSM_INST_CMD3			STFSM_INSTR(STFSM_OPC_CMD,	3) +#define STFSM_INST_CMD4			STFSM_INSTR(STFSM_OPC_CMD,	4) +#define STFSM_INST_CMD5			STFSM_INSTR(STFSM_OPC_CMD,	5) +#define STFSM_INST_ADD1			STFSM_INSTR(STFSM_OPC_ADD,	1) +#define STFSM_INST_ADD2			STFSM_INSTR(STFSM_OPC_ADD,	2) + +#define STFSM_INST_DATA_WRITE		STFSM_INSTR(STFSM_OPC_DATA,	1) +#define STFSM_INST_DATA_READ		STFSM_INSTR(STFSM_OPC_DATA,	2) + +#define STFSM_INST_STA_RD1		STFSM_INSTR(STFSM_OPC_STA,	0x1) +#define STFSM_INST_STA_WR1		STFSM_INSTR(STFSM_OPC_STA,	0x1) +#define STFSM_INST_STA_RD2		STFSM_INSTR(STFSM_OPC_STA,	0x2) +#define STFSM_INST_STA_WR1_2		STFSM_INSTR(STFSM_OPC_STA,	0x3) + +#define STFSM_INST_MODE			STFSM_INSTR(STFSM_OPC_MODE,	0) +#define STFSM_INST_DUMMY		STFSM_INSTR(STFSM_OPC_DUMMY,	0) +#define STFSM_INST_WAIT			STFSM_INSTR(STFSM_OPC_WAIT,	0) +#define STFSM_INST_STOP			STFSM_INSTR(STFSM_OPC_STOP,	0) + +#define STFSM_DEFAULT_EMI_FREQ 100000000UL                        /* 100 MHz */ +#define STFSM_DEFAULT_WR_TIME  (STFSM_DEFAULT_EMI_FREQ * (15/1000)) /* 15ms */ + +#define STFSM_FLASH_SAFE_FREQ  10000000UL                         /* 10 MHz */ + +#define STFSM_MAX_WAIT_SEQ_MS  1000     /* FSM execution time */ + +/* S25FLxxxS commands */ +#define S25FL_CMD_WRITE4_1_1_4 0x34 +#define S25FL_CMD_SE4          0xdc +#define S25FL_CMD_CLSR         0x30 +#define S25FL_CMD_DYBWR                0xe1 +#define S25FL_CMD_DYBRD                0xe0 +#define S25FL_CMD_WRITE4       0x12    /* Note, opcode clashes with +					* 'SPINOR_OP_WRITE_1_4_4' +					* as found on N25Qxxx devices! */ + +/* Status register */ +#define FLASH_STATUS_BUSY      0x01 +#define FLASH_STATUS_WEL       0x02 +#define FLASH_STATUS_BP0       0x04 +#define FLASH_STATUS_BP1       0x08 +#define FLASH_STATUS_BP2       0x10 +#define FLASH_STATUS_SRWP0     0x80 +#define FLASH_STATUS_TIMEOUT   0xff +/* S25FL Error Flags */ +#define S25FL_STATUS_E_ERR     0x20 +#define S25FL_STATUS_P_ERR     0x40 + +#define N25Q_CMD_WRVCR         0x81 +#define N25Q_CMD_RDVCR         0x85 +#define N25Q_CMD_RDVECR        0x65 +#define N25Q_CMD_RDNVCR        0xb5 +#define N25Q_CMD_WRNVCR        0xb1 + +#define FLASH_PAGESIZE         256			/* In Bytes    */ +#define FLASH_PAGESIZE_32      (FLASH_PAGESIZE / 4)	/* In uint32_t */ +#define FLASH_MAX_BUSY_WAIT    (300 * HZ)	/* Maximum 'CHIPERASE' time */ + +/* + * Flags to tweak operation of default read/write/erase routines + */ +#define CFG_READ_TOGGLE_32BIT_ADDR     0x00000001 +#define CFG_WRITE_TOGGLE_32BIT_ADDR    0x00000002 +#define CFG_ERASESEC_TOGGLE_32BIT_ADDR 0x00000008 +#define CFG_S25FL_CHECK_ERROR_FLAGS    0x00000010 + +struct stfsm_seq { +	uint32_t data_size; +	uint32_t addr1; +	uint32_t addr2; +	uint32_t addr_cfg; +	uint32_t seq_opc[5]; +	uint32_t mode; +	uint32_t dummy; +	uint32_t status; +	uint8_t  seq[16]; +	uint32_t seq_cfg; +} __packed __aligned(4); + +struct stfsm { +	struct device		*dev; +	void __iomem		*base; +	struct resource		*region; +	struct mtd_info		mtd; +	struct mutex		lock; +	struct flash_info       *info; + +	uint32_t                configuration; +	uint32_t                fifo_dir_delay; +	bool                    booted_from_spi; +	bool                    reset_signal; +	bool                    reset_por; + +	struct stfsm_seq stfsm_seq_read; +	struct stfsm_seq stfsm_seq_write; +	struct stfsm_seq stfsm_seq_en_32bit_addr; +}; + +/* Parameters to configure a READ or WRITE FSM sequence */ +struct seq_rw_config { +	uint32_t        flags;          /* flags to support config */ +	uint8_t         cmd;            /* FLASH command */ +	int             write;          /* Write Sequence */ +	uint8_t         addr_pads;      /* No. of addr pads (MODE & DUMMY) */ +	uint8_t         data_pads;      /* No. of data pads */ +	uint8_t         mode_data;      /* MODE data */ +	uint8_t         mode_cycles;    /* No. of MODE cycles */ +	uint8_t         dummy_cycles;   /* No. of DUMMY cycles */ +}; + +/* SPI Flash Device Table */ +struct flash_info { +	char            *name; +	/* +	 * JEDEC id zero means "no ID" (most older chips); otherwise it has +	 * a high byte of zero plus three data bytes: the manufacturer id, +	 * then a two byte device id. +	 */ +	u32             jedec_id; +	u16             ext_id; +	/* +	 * The size listed here is what works with SPINOR_OP_SE, which isn't +	 * necessarily called a "sector" by the vendor. +	 */ +	unsigned        sector_size; +	u16             n_sectors; +	u32             flags; +	/* +	 * Note, where FAST_READ is supported, freq_max specifies the +	 * FAST_READ frequency, not the READ frequency. +	 */ +	u32             max_freq; +	int             (*config)(struct stfsm *); +}; + +static int stfsm_n25q_config(struct stfsm *fsm); +static int stfsm_mx25_config(struct stfsm *fsm); +static int stfsm_s25fl_config(struct stfsm *fsm); +static int stfsm_w25q_config(struct stfsm *fsm); + +static struct flash_info flash_types[] = { +	/* +	 * ST Microelectronics/Numonyx -- +	 * (newer production versions may have feature updates +	 * (eg faster operating frequency) +	 */ +#define M25P_FLAG (FLASH_FLAG_READ_WRITE | FLASH_FLAG_READ_FAST) +	{ "m25p40",  0x202013, 0,  64 * 1024,   8, M25P_FLAG, 25, NULL }, +	{ "m25p80",  0x202014, 0,  64 * 1024,  16, M25P_FLAG, 25, NULL }, +	{ "m25p16",  0x202015, 0,  64 * 1024,  32, M25P_FLAG, 25, NULL }, +	{ "m25p32",  0x202016, 0,  64 * 1024,  64, M25P_FLAG, 50, NULL }, +	{ "m25p64",  0x202017, 0,  64 * 1024, 128, M25P_FLAG, 50, NULL }, +	{ "m25p128", 0x202018, 0, 256 * 1024,  64, M25P_FLAG, 50, NULL }, + +#define M25PX_FLAG (FLASH_FLAG_READ_WRITE      |	\ +		    FLASH_FLAG_READ_FAST        |	\ +		    FLASH_FLAG_READ_1_1_2       |	\ +		    FLASH_FLAG_WRITE_1_1_2) +	{ "m25px32", 0x207116, 0,  64 * 1024,  64, M25PX_FLAG, 75, NULL }, +	{ "m25px64", 0x207117, 0,  64 * 1024, 128, M25PX_FLAG, 75, NULL }, + +	/* Macronix MX25xxx +	 *     - Support for 'FLASH_FLAG_WRITE_1_4_4' is omitted for devices +	 *       where operating frequency must be reduced. +	 */ +#define MX25_FLAG (FLASH_FLAG_READ_WRITE       |	\ +		   FLASH_FLAG_READ_FAST         |	\ +		   FLASH_FLAG_READ_1_1_2        |	\ +		   FLASH_FLAG_READ_1_2_2        |	\ +		   FLASH_FLAG_READ_1_1_4        |	\ +		   FLASH_FLAG_SE_4K             |	\ +		   FLASH_FLAG_SE_32K) +	{ "mx25l3255e",  0xc29e16, 0, 64 * 1024, 64, +	  (MX25_FLAG | FLASH_FLAG_WRITE_1_4_4), 86, +	  stfsm_mx25_config}, +	{ "mx25l25635e", 0xc22019, 0, 64*1024, 512, +	  (MX25_FLAG | FLASH_FLAG_32BIT_ADDR | FLASH_FLAG_RESET), 70, +	  stfsm_mx25_config }, +	{ "mx25l25655e", 0xc22619, 0, 64*1024, 512, +	  (MX25_FLAG | FLASH_FLAG_32BIT_ADDR | FLASH_FLAG_RESET), 70, +	  stfsm_mx25_config}, + +#define N25Q_FLAG (FLASH_FLAG_READ_WRITE       |	\ +		   FLASH_FLAG_READ_FAST         |	\ +		   FLASH_FLAG_READ_1_1_2        |	\ +		   FLASH_FLAG_READ_1_2_2        |	\ +		   FLASH_FLAG_READ_1_1_4        |	\ +		   FLASH_FLAG_READ_1_4_4        |	\ +		   FLASH_FLAG_WRITE_1_1_2       |	\ +		   FLASH_FLAG_WRITE_1_2_2       |	\ +		   FLASH_FLAG_WRITE_1_1_4       |	\ +		   FLASH_FLAG_WRITE_1_4_4) +	{ "n25q128", 0x20ba18, 0, 64 * 1024,  256, N25Q_FLAG, 108, +	  stfsm_n25q_config }, +	{ "n25q256", 0x20ba19, 0, 64 * 1024,  512, +	  N25Q_FLAG | FLASH_FLAG_32BIT_ADDR, 108, stfsm_n25q_config }, + +	/* +	 * Spansion S25FLxxxP +	 *     - 256KiB and 64KiB sector variants (identified by ext. JEDEC) +	 */ +#define S25FLXXXP_FLAG (FLASH_FLAG_READ_WRITE  |	\ +			FLASH_FLAG_READ_1_1_2   |	\ +			FLASH_FLAG_READ_1_2_2   |	\ +			FLASH_FLAG_READ_1_1_4   |	\ +			FLASH_FLAG_READ_1_4_4   |	\ +			FLASH_FLAG_WRITE_1_1_4  |	\ +			FLASH_FLAG_READ_FAST) +	{ "s25fl032p",  0x010215, 0x4d00,  64 * 1024,  64, S25FLXXXP_FLAG, 80, +	  stfsm_s25fl_config}, +	{ "s25fl129p0", 0x012018, 0x4d00, 256 * 1024,  64, S25FLXXXP_FLAG, 80, +	  stfsm_s25fl_config }, +	{ "s25fl129p1", 0x012018, 0x4d01,  64 * 1024, 256, S25FLXXXP_FLAG, 80, +	  stfsm_s25fl_config }, + +	/* +	 * Spansion S25FLxxxS +	 *     - 256KiB and 64KiB sector variants (identified by ext. JEDEC) +	 *     - RESET# signal supported by die but not bristled out on all +	 *       package types.  The package type is a function of board design, +	 *       so this information is captured in the board's flags. +	 *     - Supports 'DYB' sector protection. Depending on variant, sectors +	 *       may default to locked state on power-on. +	 */ +#define S25FLXXXS_FLAG (S25FLXXXP_FLAG         |	\ +			FLASH_FLAG_RESET        |	\ +			FLASH_FLAG_DYB_LOCKING) +	{ "s25fl128s0", 0x012018, 0x0300,  256 * 1024, 64, S25FLXXXS_FLAG, 80, +	  stfsm_s25fl_config }, +	{ "s25fl128s1", 0x012018, 0x0301,  64 * 1024, 256, S25FLXXXS_FLAG, 80, +	  stfsm_s25fl_config }, +	{ "s25fl256s0", 0x010219, 0x4d00, 256 * 1024, 128, +	  S25FLXXXS_FLAG | FLASH_FLAG_32BIT_ADDR, 80, stfsm_s25fl_config }, +	{ "s25fl256s1", 0x010219, 0x4d01,  64 * 1024, 512, +	  S25FLXXXS_FLAG | FLASH_FLAG_32BIT_ADDR, 80, stfsm_s25fl_config }, + +	/* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ +#define W25X_FLAG (FLASH_FLAG_READ_WRITE       |	\ +		   FLASH_FLAG_READ_FAST         |	\ +		   FLASH_FLAG_READ_1_1_2        |	\ +		   FLASH_FLAG_WRITE_1_1_2) +	{ "w25x40",  0xef3013, 0,  64 * 1024,   8, W25X_FLAG, 75, NULL }, +	{ "w25x80",  0xef3014, 0,  64 * 1024,  16, W25X_FLAG, 75, NULL }, +	{ "w25x16",  0xef3015, 0,  64 * 1024,  32, W25X_FLAG, 75, NULL }, +	{ "w25x32",  0xef3016, 0,  64 * 1024,  64, W25X_FLAG, 75, NULL }, +	{ "w25x64",  0xef3017, 0,  64 * 1024, 128, W25X_FLAG, 75, NULL }, + +	/* Winbond -- w25q "blocks" are 64K, "sectors" are 4KiB */ +#define W25Q_FLAG (FLASH_FLAG_READ_WRITE       |	\ +		   FLASH_FLAG_READ_FAST         |	\ +		   FLASH_FLAG_READ_1_1_2        |	\ +		   FLASH_FLAG_READ_1_2_2        |	\ +		   FLASH_FLAG_READ_1_1_4        |	\ +		   FLASH_FLAG_READ_1_4_4        |	\ +		   FLASH_FLAG_WRITE_1_1_4) +	{ "w25q80",  0xef4014, 0,  64 * 1024,  16, W25Q_FLAG, 80, +	  stfsm_w25q_config }, +	{ "w25q16",  0xef4015, 0,  64 * 1024,  32, W25Q_FLAG, 80, +	  stfsm_w25q_config }, +	{ "w25q32",  0xef4016, 0,  64 * 1024,  64, W25Q_FLAG, 80, +	  stfsm_w25q_config }, +	{ "w25q64",  0xef4017, 0,  64 * 1024, 128, W25Q_FLAG, 80, +	  stfsm_w25q_config }, + +	/* Sentinel */ +	{ NULL, 0x000000, 0, 0, 0, 0, 0, NULL }, +}; + +/* + * FSM message sequence configurations: + * + * All configs are presented in order of preference + */ + +/* Default READ configurations, in order of preference */ +static struct seq_rw_config default_read_configs[] = { +	{FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4,	0, 4, 4, 0x00, 2, 4}, +	{FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4,	0, 1, 4, 0x00, 4, 0}, +	{FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2,	0, 2, 2, 0x00, 4, 0}, +	{FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2,	0, 1, 2, 0x00, 0, 8}, +	{FLASH_FLAG_READ_FAST,	SPINOR_OP_READ_FAST,	0, 1, 1, 0x00, 0, 8}, +	{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ,		0, 1, 1, 0x00, 0, 0}, +	{0x00,			0,			0, 0, 0, 0x00, 0, 0}, +}; + +/* Default WRITE configurations */ +static struct seq_rw_config default_write_configs[] = { +	{FLASH_FLAG_WRITE_1_4_4, SPINOR_OP_WRITE_1_4_4, 1, 4, 4, 0x00, 0, 0}, +	{FLASH_FLAG_WRITE_1_1_4, SPINOR_OP_WRITE_1_1_4, 1, 1, 4, 0x00, 0, 0}, +	{FLASH_FLAG_WRITE_1_2_2, SPINOR_OP_WRITE_1_2_2, 1, 2, 2, 0x00, 0, 0}, +	{FLASH_FLAG_WRITE_1_1_2, SPINOR_OP_WRITE_1_1_2, 1, 1, 2, 0x00, 0, 0}, +	{FLASH_FLAG_READ_WRITE,  SPINOR_OP_WRITE,       1, 1, 1, 0x00, 0, 0}, +	{0x00,			 0,			0, 0, 0, 0x00, 0, 0}, +}; + +/* + * [N25Qxxx] Configuration + */ +#define N25Q_VCR_DUMMY_CYCLES(x)	(((x) & 0xf) << 4) +#define N25Q_VCR_XIP_DISABLED		((uint8_t)0x1 << 3) +#define N25Q_VCR_WRAP_CONT		0x3 + +/* N25Q 3-byte Address READ configurations + *	- 'FAST' variants configured for 8 dummy cycles. + * + * Note, the number of dummy cycles used for 'FAST' READ operations is + * configurable and would normally be tuned according to the READ command and + * operating frequency.  However, this applies universally to all 'FAST' READ + * commands, including those used by the SPIBoot controller, and remains in + * force until the device is power-cycled.  Since the SPIBoot controller is + * hard-wired to use 8 dummy cycles, we must configure the device to also use 8 + * cycles. + */ +static struct seq_rw_config n25q_read3_configs[] = { +	{FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4,	0, 4, 4, 0x00, 0, 8}, +	{FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4,	0, 1, 4, 0x00, 0, 8}, +	{FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2,	0, 2, 2, 0x00, 0, 8}, +	{FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2,	0, 1, 2, 0x00, 0, 8}, +	{FLASH_FLAG_READ_FAST,	SPINOR_OP_READ_FAST,	0, 1, 1, 0x00, 0, 8}, +	{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ,	        0, 1, 1, 0x00, 0, 0}, +	{0x00,			0,			0, 0, 0, 0x00, 0, 0}, +}; + +/* N25Q 4-byte Address READ configurations + *	- use special 4-byte address READ commands (reduces overheads, and + *        reduces risk of hitting watchdog reset issues). + *	- 'FAST' variants configured for 8 dummy cycles (see note above.) + */ +static struct seq_rw_config n25q_read4_configs[] = { +	{FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4,	0, 4, 4, 0x00, 0, 8}, +	{FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4,	0, 1, 4, 0x00, 0, 8}, +	{FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2,	0, 2, 2, 0x00, 0, 8}, +	{FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2,	0, 1, 2, 0x00, 0, 8}, +	{FLASH_FLAG_READ_FAST,	SPINOR_OP_READ4_FAST,	0, 1, 1, 0x00, 0, 8}, +	{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4,	0, 1, 1, 0x00, 0, 0}, +	{0x00,			0,			0, 0, 0, 0x00, 0, 0}, +}; + +/* + * [MX25xxx] Configuration + */ +#define MX25_STATUS_QE			(0x1 << 6) + +static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq) +{ +	seq->seq_opc[0] = (SEQ_OPC_PADS_1 | +			   SEQ_OPC_CYCLES(8) | +			   SEQ_OPC_OPCODE(SPINOR_OP_EN4B) | +			   SEQ_OPC_CSDEASSERT); + +	seq->seq[0] = STFSM_INST_CMD1; +	seq->seq[1] = STFSM_INST_WAIT; +	seq->seq[2] = STFSM_INST_STOP; + +	seq->seq_cfg = (SEQ_CFG_PADS_1 | +			SEQ_CFG_ERASE | +			SEQ_CFG_READNOTWRITE | +			SEQ_CFG_CSDEASSERT | +			SEQ_CFG_STARTSEQ); + +	return 0; +} + +/* + * [S25FLxxx] Configuration + */ +#define STFSM_S25FL_CONFIG_QE		(0x1 << 1) + +/* + * S25FLxxxS devices provide three ways of supporting 32-bit addressing: Bank + * Register, Extended Address Modes, and a 32-bit address command set.  The + * 32-bit address command set is used here, since it avoids any problems with + * entering a state that is incompatible with the SPIBoot Controller. + */ +static struct seq_rw_config stfsm_s25fl_read4_configs[] = { +	{FLASH_FLAG_READ_1_4_4,  SPINOR_OP_READ4_1_4_4,  0, 4, 4, 0x00, 2, 4}, +	{FLASH_FLAG_READ_1_1_4,  SPINOR_OP_READ4_1_1_4,  0, 1, 4, 0x00, 0, 8}, +	{FLASH_FLAG_READ_1_2_2,  SPINOR_OP_READ4_1_2_2,  0, 2, 2, 0x00, 4, 0}, +	{FLASH_FLAG_READ_1_1_2,  SPINOR_OP_READ4_1_1_2,  0, 1, 2, 0x00, 0, 8}, +	{FLASH_FLAG_READ_FAST,   SPINOR_OP_READ4_FAST,   0, 1, 1, 0x00, 0, 8}, +	{FLASH_FLAG_READ_WRITE,  SPINOR_OP_READ4,        0, 1, 1, 0x00, 0, 0}, +	{0x00,                   0,                      0, 0, 0, 0x00, 0, 0}, +}; + +static struct seq_rw_config stfsm_s25fl_write4_configs[] = { +	{FLASH_FLAG_WRITE_1_1_4, S25FL_CMD_WRITE4_1_1_4, 1, 1, 4, 0x00, 0, 0}, +	{FLASH_FLAG_READ_WRITE,  S25FL_CMD_WRITE4,       1, 1, 1, 0x00, 0, 0}, +	{0x00,                   0,                      0, 0, 0, 0x00, 0, 0}, +}; + +/* + * [W25Qxxx] Configuration + */ +#define W25Q_STATUS_QE			(0x1 << 1) + +static struct stfsm_seq stfsm_seq_read_jedec = { +	.data_size = TRANSFER_SIZE(8), +	.seq_opc[0] = (SEQ_OPC_PADS_1 | +		       SEQ_OPC_CYCLES(8) | +		       SEQ_OPC_OPCODE(SPINOR_OP_RDID)), +	.seq = { +		STFSM_INST_CMD1, +		STFSM_INST_DATA_READ, +		STFSM_INST_STOP, +	}, +	.seq_cfg = (SEQ_CFG_PADS_1 | +		    SEQ_CFG_READNOTWRITE | +		    SEQ_CFG_CSDEASSERT | +		    SEQ_CFG_STARTSEQ), +}; + +static struct stfsm_seq stfsm_seq_read_status_fifo = { +	.data_size = TRANSFER_SIZE(4), +	.seq_opc[0] = (SEQ_OPC_PADS_1 | +		       SEQ_OPC_CYCLES(8) | +		       SEQ_OPC_OPCODE(SPINOR_OP_RDSR)), +	.seq = { +		STFSM_INST_CMD1, +		STFSM_INST_DATA_READ, +		STFSM_INST_STOP, +	}, +	.seq_cfg = (SEQ_CFG_PADS_1 | +		    SEQ_CFG_READNOTWRITE | +		    SEQ_CFG_CSDEASSERT | +		    SEQ_CFG_STARTSEQ), +}; + +static struct stfsm_seq stfsm_seq_erase_sector = { +	/* 'addr_cfg' configured during initialisation */ +	.seq_opc = { +		(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | +		 SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT), + +		(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | +		 SEQ_OPC_OPCODE(SPINOR_OP_SE)), +	}, +	.seq = { +		STFSM_INST_CMD1, +		STFSM_INST_CMD2, +		STFSM_INST_ADD1, +		STFSM_INST_ADD2, +		STFSM_INST_STOP, +	}, +	.seq_cfg = (SEQ_CFG_PADS_1 | +		    SEQ_CFG_READNOTWRITE | +		    SEQ_CFG_CSDEASSERT | +		    SEQ_CFG_STARTSEQ), +}; + +static struct stfsm_seq stfsm_seq_erase_chip = { +	.seq_opc = { +		(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | +		 SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT), + +		(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | +		 SEQ_OPC_OPCODE(SPINOR_OP_CHIP_ERASE) | SEQ_OPC_CSDEASSERT), +	}, +	.seq = { +		STFSM_INST_CMD1, +		STFSM_INST_CMD2, +		STFSM_INST_WAIT, +		STFSM_INST_STOP, +	}, +	.seq_cfg = (SEQ_CFG_PADS_1 | +		    SEQ_CFG_ERASE | +		    SEQ_CFG_READNOTWRITE | +		    SEQ_CFG_CSDEASSERT | +		    SEQ_CFG_STARTSEQ), +}; + +static struct stfsm_seq stfsm_seq_write_status = { +	.seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | +		       SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT), +	.seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | +		       SEQ_OPC_OPCODE(SPINOR_OP_WRSR)), +	.seq = { +		STFSM_INST_CMD1, +		STFSM_INST_CMD2, +		STFSM_INST_STA_WR1, +		STFSM_INST_STOP, +	}, +	.seq_cfg = (SEQ_CFG_PADS_1 | +		    SEQ_CFG_READNOTWRITE | +		    SEQ_CFG_CSDEASSERT | +		    SEQ_CFG_STARTSEQ), +}; + +static int stfsm_n25q_en_32bit_addr_seq(struct stfsm_seq *seq) +{ +	seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | +			   SEQ_OPC_OPCODE(SPINOR_OP_EN4B)); +	seq->seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | +			   SEQ_OPC_OPCODE(SPINOR_OP_WREN) | +			   SEQ_OPC_CSDEASSERT); + +	seq->seq[0] = STFSM_INST_CMD2; +	seq->seq[1] = STFSM_INST_CMD1; +	seq->seq[2] = STFSM_INST_WAIT; +	seq->seq[3] = STFSM_INST_STOP; + +	seq->seq_cfg = (SEQ_CFG_PADS_1 | +			SEQ_CFG_ERASE | +			SEQ_CFG_READNOTWRITE | +			SEQ_CFG_CSDEASSERT | +			SEQ_CFG_STARTSEQ); + +	return 0; +} + +static inline int stfsm_is_idle(struct stfsm *fsm) +{ +	return readl(fsm->base + SPI_FAST_SEQ_STA) & 0x10; +} + +static inline uint32_t stfsm_fifo_available(struct stfsm *fsm) +{ +	return (readl(fsm->base + SPI_FAST_SEQ_STA) >> 5) & 0x7f; +} + +static void stfsm_clear_fifo(struct stfsm *fsm) +{ +	uint32_t avail; + +	for (;;) { +		avail = stfsm_fifo_available(fsm); +		if (!avail) +			break; + +		while (avail) { +			readl(fsm->base + SPI_FAST_SEQ_DATA_REG); +			avail--; +		} +	} +} + +static inline void stfsm_load_seq(struct stfsm *fsm, +				  const struct stfsm_seq *seq) +{ +	void __iomem *dst = fsm->base + SPI_FAST_SEQ_TRANSFER_SIZE; +	const uint32_t *src = (const uint32_t *)seq; +	int words = sizeof(*seq) / sizeof(*src); + +	BUG_ON(!stfsm_is_idle(fsm)); + +	while (words--) { +		writel(*src, dst); +		src++; +		dst += 4; +	} +} + +static void stfsm_wait_seq(struct stfsm *fsm) +{ +	unsigned long deadline; +	int timeout = 0; + +	deadline = jiffies + msecs_to_jiffies(STFSM_MAX_WAIT_SEQ_MS); + +	while (!timeout) { +		if (time_after_eq(jiffies, deadline)) +			timeout = 1; + +		if (stfsm_is_idle(fsm)) +			return; + +		cond_resched(); +	} + +	dev_err(fsm->dev, "timeout on sequence completion\n"); +} + +static void stfsm_read_fifo(struct stfsm *fsm, uint32_t *buf, uint32_t size) +{ +	uint32_t remaining = size >> 2; +	uint32_t avail; +	uint32_t words; + +	dev_dbg(fsm->dev, "Reading %d bytes from FIFO\n", size); + +	BUG_ON((((uintptr_t)buf) & 0x3) || (size & 0x3)); + +	while (remaining) { +		for (;;) { +			avail = stfsm_fifo_available(fsm); +			if (avail) +				break; +			udelay(1); +		} +		words = min(avail, remaining); +		remaining -= words; + +		readsl(fsm->base + SPI_FAST_SEQ_DATA_REG, buf, words); +		buf += words; +	} +} + +static int stfsm_write_fifo(struct stfsm *fsm, const uint32_t *buf, +			    uint32_t size) +{ +	uint32_t words = size >> 2; + +	dev_dbg(fsm->dev, "writing %d bytes to FIFO\n", size); + +	BUG_ON((((uintptr_t)buf) & 0x3) || (size & 0x3)); + +	writesl(fsm->base + SPI_FAST_SEQ_DATA_REG, buf, words); + +	return size; +} + +static int stfsm_enter_32bit_addr(struct stfsm *fsm, int enter) +{ +	struct stfsm_seq *seq = &fsm->stfsm_seq_en_32bit_addr; +	uint32_t cmd = enter ? SPINOR_OP_EN4B : SPINOR_OP_EX4B; + +	seq->seq_opc[0] = (SEQ_OPC_PADS_1 | +			   SEQ_OPC_CYCLES(8) | +			   SEQ_OPC_OPCODE(cmd) | +			   SEQ_OPC_CSDEASSERT); + +	stfsm_load_seq(fsm, seq); + +	stfsm_wait_seq(fsm); + +	return 0; +} + +static uint8_t stfsm_wait_busy(struct stfsm *fsm) +{ +	struct stfsm_seq *seq = &stfsm_seq_read_status_fifo; +	unsigned long deadline; +	uint32_t status; +	int timeout = 0; + +	/* Use RDRS1 */ +	seq->seq_opc[0] = (SEQ_OPC_PADS_1 | +			   SEQ_OPC_CYCLES(8) | +			   SEQ_OPC_OPCODE(SPINOR_OP_RDSR)); + +	/* Load read_status sequence */ +	stfsm_load_seq(fsm, seq); + +	/* +	 * Repeat until busy bit is deasserted, or timeout, or error (S25FLxxxS) +	 */ +	deadline = jiffies + FLASH_MAX_BUSY_WAIT; +	while (!timeout) { +		if (time_after_eq(jiffies, deadline)) +			timeout = 1; + +		stfsm_wait_seq(fsm); + +		stfsm_read_fifo(fsm, &status, 4); + +		if ((status & FLASH_STATUS_BUSY) == 0) +			return 0; + +		if ((fsm->configuration & CFG_S25FL_CHECK_ERROR_FLAGS) && +		    ((status & S25FL_STATUS_P_ERR) || +		     (status & S25FL_STATUS_E_ERR))) +			return (uint8_t)(status & 0xff); + +		if (!timeout) +			/* Restart */ +			writel(seq->seq_cfg, fsm->base + SPI_FAST_SEQ_CFG); + +		cond_resched(); +	} + +	dev_err(fsm->dev, "timeout on wait_busy\n"); + +	return FLASH_STATUS_TIMEOUT; +} + +static int stfsm_read_status(struct stfsm *fsm, uint8_t cmd, +			     uint8_t *data, int bytes) +{ +	struct stfsm_seq *seq = &stfsm_seq_read_status_fifo; +	uint32_t tmp; +	uint8_t *t = (uint8_t *)&tmp; +	int i; + +	dev_dbg(fsm->dev, "read 'status' register [0x%02x], %d byte(s)\n", +		cmd, bytes); + +	BUG_ON(bytes != 1 && bytes != 2); + +	seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | +			   SEQ_OPC_OPCODE(cmd)), + +	stfsm_load_seq(fsm, seq); + +	stfsm_read_fifo(fsm, &tmp, 4); + +	for (i = 0; i < bytes; i++) +		data[i] = t[i]; + +	stfsm_wait_seq(fsm); + +	return 0; +} + +static int stfsm_write_status(struct stfsm *fsm, uint8_t cmd, +			    uint16_t data, int bytes, int wait_busy) +{ +	struct stfsm_seq *seq = &stfsm_seq_write_status; + +	dev_dbg(fsm->dev, +		"write 'status' register [0x%02x], %d byte(s), 0x%04x\n" +		" %s wait-busy\n", cmd, bytes, data, wait_busy ? "with" : "no"); + +	BUG_ON(bytes != 1 && bytes != 2); + +	seq->seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | +			   SEQ_OPC_OPCODE(cmd)); + +	seq->status = (uint32_t)data | STA_PADS_1 | STA_CSDEASSERT; +	seq->seq[2] = (bytes == 1) ? STFSM_INST_STA_WR1 : STFSM_INST_STA_WR1_2; + +	stfsm_load_seq(fsm, seq); + +	stfsm_wait_seq(fsm); + +	if (wait_busy) +		stfsm_wait_busy(fsm); + +	return 0; +} + +/* + * SoC reset on 'boot-from-spi' systems + * + * Certain modes of operation cause the Flash device to enter a particular state + * for a period of time (e.g. 'Erase Sector', 'Quad Enable', and 'Enter 32-bit + * Addr' commands).  On boot-from-spi systems, it is important to consider what + * happens if a warm reset occurs during this period.  The SPIBoot controller + * assumes that Flash device is in its default reset state, 24-bit address mode, + * and ready to accept commands.  This can be achieved using some form of + * on-board logic/controller to force a device POR in response to a SoC-level + * reset or by making use of the device reset signal if available (limited + * number of devices only). + * + * Failure to take such precautions can cause problems following a warm reset. + * For some operations (e.g. ERASE), there is little that can be done.  For + * other modes of operation (e.g. 32-bit addressing), options are often + * available that can help minimise the window in which a reset could cause a + * problem. + * + */ +static bool stfsm_can_handle_soc_reset(struct stfsm *fsm) +{ +	/* Reset signal is available on the board and supported by the device */ +	if (fsm->reset_signal && fsm->info->flags & FLASH_FLAG_RESET) +		return true; + +	/* Board-level logic forces a power-on-reset */ +	if (fsm->reset_por) +		return true; + +	/* Reset is not properly handled and may result in failure to reboot */ +	return false; +} + +/* Configure 'addr_cfg' according to addressing mode */ +static void stfsm_prepare_erasesec_seq(struct stfsm *fsm, +				       struct stfsm_seq *seq) +{ +	int addr1_cycles = fsm->info->flags & FLASH_FLAG_32BIT_ADDR ? 16 : 8; + +	seq->addr_cfg = (ADR_CFG_CYCLES_ADD1(addr1_cycles) | +			 ADR_CFG_PADS_1_ADD1 | +			 ADR_CFG_CYCLES_ADD2(16) | +			 ADR_CFG_PADS_1_ADD2 | +			 ADR_CFG_CSDEASSERT_ADD2); +} + +/* Search for preferred configuration based on available flags */ +static struct seq_rw_config * +stfsm_search_seq_rw_configs(struct stfsm *fsm, +			    struct seq_rw_config cfgs[]) +{ +	struct seq_rw_config *config; +	int flags = fsm->info->flags; + +	for (config = cfgs; config->cmd != 0; config++) +		if ((config->flags & flags) == config->flags) +			return config; + +	return NULL; +} + +/* Prepare a READ/WRITE sequence according to configuration parameters */ +static void stfsm_prepare_rw_seq(struct stfsm *fsm, +				 struct stfsm_seq *seq, +				 struct seq_rw_config *cfg) +{ +	int addr1_cycles, addr2_cycles; +	int i = 0; + +	memset(seq, 0, sizeof(*seq)); + +	/* Add READ/WRITE OPC  */ +	seq->seq_opc[i++] = (SEQ_OPC_PADS_1 | +			     SEQ_OPC_CYCLES(8) | +			     SEQ_OPC_OPCODE(cfg->cmd)); + +	/* Add WREN OPC for a WRITE sequence */ +	if (cfg->write) +		seq->seq_opc[i++] = (SEQ_OPC_PADS_1 | +				     SEQ_OPC_CYCLES(8) | +				     SEQ_OPC_OPCODE(SPINOR_OP_WREN) | +				     SEQ_OPC_CSDEASSERT); + +	/* Address configuration (24 or 32-bit addresses) */ +	addr1_cycles  = (fsm->info->flags & FLASH_FLAG_32BIT_ADDR) ? 16 : 8; +	addr1_cycles /= cfg->addr_pads; +	addr2_cycles  = 16 / cfg->addr_pads; +	seq->addr_cfg = ((addr1_cycles & 0x3f) << 0 |	/* ADD1 cycles */ +			 (cfg->addr_pads - 1) << 6 |	/* ADD1 pads */ +			 (addr2_cycles & 0x3f) << 16 |	/* ADD2 cycles */ +			 ((cfg->addr_pads - 1) << 22));	/* ADD2 pads */ + +	/* Data/Sequence configuration */ +	seq->seq_cfg = ((cfg->data_pads - 1) << 16 | +			SEQ_CFG_STARTSEQ | +			SEQ_CFG_CSDEASSERT); +	if (!cfg->write) +		seq->seq_cfg |= SEQ_CFG_READNOTWRITE; + +	/* Mode configuration (no. of pads taken from addr cfg) */ +	seq->mode = ((cfg->mode_data & 0xff) << 0 |	/* data */ +		     (cfg->mode_cycles & 0x3f) << 16 |	/* cycles */ +		     (cfg->addr_pads - 1) << 22);	/* pads */ + +	/* Dummy configuration (no. of pads taken from addr cfg) */ +	seq->dummy = ((cfg->dummy_cycles & 0x3f) << 16 |	/* cycles */ +		      (cfg->addr_pads - 1) << 22);		/* pads */ + + +	/* Instruction sequence */ +	i = 0; +	if (cfg->write) +		seq->seq[i++] = STFSM_INST_CMD2; + +	seq->seq[i++] = STFSM_INST_CMD1; + +	seq->seq[i++] = STFSM_INST_ADD1; +	seq->seq[i++] = STFSM_INST_ADD2; + +	if (cfg->mode_cycles) +		seq->seq[i++] = STFSM_INST_MODE; + +	if (cfg->dummy_cycles) +		seq->seq[i++] = STFSM_INST_DUMMY; + +	seq->seq[i++] = +		cfg->write ? STFSM_INST_DATA_WRITE : STFSM_INST_DATA_READ; +	seq->seq[i++] = STFSM_INST_STOP; +} + +static int stfsm_search_prepare_rw_seq(struct stfsm *fsm, +				       struct stfsm_seq *seq, +				       struct seq_rw_config *cfgs) +{ +	struct seq_rw_config *config; + +	config = stfsm_search_seq_rw_configs(fsm, cfgs); +	if (!config) { +		dev_err(fsm->dev, "failed to find suitable config\n"); +		return -EINVAL; +	} + +	stfsm_prepare_rw_seq(fsm, seq, config); + +	return 0; +} + +/* Prepare a READ/WRITE/ERASE 'default' sequences */ +static int stfsm_prepare_rwe_seqs_default(struct stfsm *fsm) +{ +	uint32_t flags = fsm->info->flags; +	int ret; + +	/* Configure 'READ' sequence */ +	ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read, +					  default_read_configs); +	if (ret) { +		dev_err(fsm->dev, +			"failed to prep READ sequence with flags [0x%08x]\n", +			flags); +		return ret; +	} + +	/* Configure 'WRITE' sequence */ +	ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_write, +					  default_write_configs); +	if (ret) { +		dev_err(fsm->dev, +			"failed to prep WRITE sequence with flags [0x%08x]\n", +			flags); +		return ret; +	} + +	/* Configure 'ERASE_SECTOR' sequence */ +	stfsm_prepare_erasesec_seq(fsm, &stfsm_seq_erase_sector); + +	return 0; +} + +static int stfsm_mx25_config(struct stfsm *fsm) +{ +	uint32_t flags = fsm->info->flags; +	uint32_t data_pads; +	uint8_t sta; +	int ret; +	bool soc_reset; + +	/* +	 * Use default READ/WRITE sequences +	 */ +	ret = stfsm_prepare_rwe_seqs_default(fsm); +	if (ret) +		return ret; + +	/* +	 * Configure 32-bit Address Support +	 */ +	if (flags & FLASH_FLAG_32BIT_ADDR) { +		/* Configure 'enter_32bitaddr' FSM sequence */ +		stfsm_mx25_en_32bit_addr_seq(&fsm->stfsm_seq_en_32bit_addr); + +		soc_reset = stfsm_can_handle_soc_reset(fsm); +		if (soc_reset || !fsm->booted_from_spi) +			/* If we can handle SoC resets, we enable 32-bit address +			 * mode pervasively */ +			stfsm_enter_32bit_addr(fsm, 1); + +		else +			/* Else, enable/disable 32-bit addressing before/after +			 * each operation */ +			fsm->configuration = (CFG_READ_TOGGLE_32BIT_ADDR | +					      CFG_WRITE_TOGGLE_32BIT_ADDR | +					      CFG_ERASESEC_TOGGLE_32BIT_ADDR); +	} + +	/* Check status of 'QE' bit, update if required. */ +	stfsm_read_status(fsm, SPINOR_OP_RDSR, &sta, 1); +	data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1; +	if (data_pads == 4) { +		if (!(sta & MX25_STATUS_QE)) { +			/* Set 'QE' */ +			sta |= MX25_STATUS_QE; + +			stfsm_write_status(fsm, SPINOR_OP_WRSR, sta, 1, 1); +		} +	} else { +		if (sta & MX25_STATUS_QE) { +			/* Clear 'QE' */ +			sta &= ~MX25_STATUS_QE; + +			stfsm_write_status(fsm, SPINOR_OP_WRSR, sta, 1, 1); +		} +	} + +	return 0; +} + +static int stfsm_n25q_config(struct stfsm *fsm) +{ +	uint32_t flags = fsm->info->flags; +	uint8_t vcr; +	int ret = 0; +	bool soc_reset; + +	/* Configure 'READ' sequence */ +	if (flags & FLASH_FLAG_32BIT_ADDR) +		ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read, +						  n25q_read4_configs); +	else +		ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read, +						  n25q_read3_configs); +	if (ret) { +		dev_err(fsm->dev, +			"failed to prepare READ sequence with flags [0x%08x]\n", +			flags); +		return ret; +	} + +	/* Configure 'WRITE' sequence (default configs) */ +	ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_write, +					  default_write_configs); +	if (ret) { +		dev_err(fsm->dev, +			"preparing WRITE sequence using flags [0x%08x] failed\n", +			flags); +		return ret; +	} + +	/* * Configure 'ERASE_SECTOR' sequence */ +	stfsm_prepare_erasesec_seq(fsm, &stfsm_seq_erase_sector); + +	/* Configure 32-bit address support */ +	if (flags & FLASH_FLAG_32BIT_ADDR) { +		stfsm_n25q_en_32bit_addr_seq(&fsm->stfsm_seq_en_32bit_addr); + +		soc_reset = stfsm_can_handle_soc_reset(fsm); +		if (soc_reset || !fsm->booted_from_spi) { +			/* +			 * If we can handle SoC resets, we enable 32-bit +			 * address mode pervasively +			 */ +			stfsm_enter_32bit_addr(fsm, 1); +		} else { +			/* +			 * If not, enable/disable for WRITE and ERASE +			 * operations (READ uses special commands) +			 */ +			fsm->configuration = (CFG_WRITE_TOGGLE_32BIT_ADDR | +					      CFG_ERASESEC_TOGGLE_32BIT_ADDR); +		} +	} + +	/* +	 * Configure device to use 8 dummy cycles +	 */ +	vcr = (N25Q_VCR_DUMMY_CYCLES(8) | N25Q_VCR_XIP_DISABLED | +	       N25Q_VCR_WRAP_CONT); +	stfsm_write_status(fsm, N25Q_CMD_WRVCR, vcr, 1, 0); + +	return 0; +} + +static void stfsm_s25fl_prepare_erasesec_seq_32(struct stfsm_seq *seq) +{ +	seq->seq_opc[1] = (SEQ_OPC_PADS_1 | +			   SEQ_OPC_CYCLES(8) | +			   SEQ_OPC_OPCODE(S25FL_CMD_SE4)); + +	seq->addr_cfg = (ADR_CFG_CYCLES_ADD1(16) | +			 ADR_CFG_PADS_1_ADD1 | +			 ADR_CFG_CYCLES_ADD2(16) | +			 ADR_CFG_PADS_1_ADD2 | +			 ADR_CFG_CSDEASSERT_ADD2); +} + +static void stfsm_s25fl_read_dyb(struct stfsm *fsm, uint32_t offs, uint8_t *dby) +{ +	uint32_t tmp; +	struct stfsm_seq seq = { +		.data_size = TRANSFER_SIZE(4), +		.seq_opc[0] = (SEQ_OPC_PADS_1 | +			       SEQ_OPC_CYCLES(8) | +			       SEQ_OPC_OPCODE(S25FL_CMD_DYBRD)), +		.addr_cfg = (ADR_CFG_CYCLES_ADD1(16) | +			     ADR_CFG_PADS_1_ADD1 | +			     ADR_CFG_CYCLES_ADD2(16) | +			     ADR_CFG_PADS_1_ADD2), +		.addr1 = (offs >> 16) & 0xffff, +		.addr2 = offs & 0xffff, +		.seq = { +			STFSM_INST_CMD1, +			STFSM_INST_ADD1, +			STFSM_INST_ADD2, +			STFSM_INST_DATA_READ, +			STFSM_INST_STOP, +		}, +		.seq_cfg = (SEQ_CFG_PADS_1 | +			    SEQ_CFG_READNOTWRITE | +			    SEQ_CFG_CSDEASSERT | +			    SEQ_CFG_STARTSEQ), +	}; + +	stfsm_load_seq(fsm, &seq); + +	stfsm_read_fifo(fsm, &tmp, 4); + +	*dby = (uint8_t)(tmp >> 24); + +	stfsm_wait_seq(fsm); +} + +static void stfsm_s25fl_write_dyb(struct stfsm *fsm, uint32_t offs, uint8_t dby) +{ +	struct stfsm_seq seq = { +		.seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | +			       SEQ_OPC_OPCODE(SPINOR_OP_WREN) | +			       SEQ_OPC_CSDEASSERT), +		.seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | +			       SEQ_OPC_OPCODE(S25FL_CMD_DYBWR)), +		.addr_cfg = (ADR_CFG_CYCLES_ADD1(16) | +			     ADR_CFG_PADS_1_ADD1 | +			     ADR_CFG_CYCLES_ADD2(16) | +			     ADR_CFG_PADS_1_ADD2), +		.status = (uint32_t)dby | STA_PADS_1 | STA_CSDEASSERT, +		.addr1 = (offs >> 16) & 0xffff, +		.addr2 = offs & 0xffff, +		.seq = { +			STFSM_INST_CMD1, +			STFSM_INST_CMD2, +			STFSM_INST_ADD1, +			STFSM_INST_ADD2, +			STFSM_INST_STA_WR1, +			STFSM_INST_STOP, +		}, +		.seq_cfg = (SEQ_CFG_PADS_1 | +			    SEQ_CFG_READNOTWRITE | +			    SEQ_CFG_CSDEASSERT | +			    SEQ_CFG_STARTSEQ), +	}; + +	stfsm_load_seq(fsm, &seq); +	stfsm_wait_seq(fsm); + +	stfsm_wait_busy(fsm); +} + +static int stfsm_s25fl_clear_status_reg(struct stfsm *fsm) +{ +	struct stfsm_seq seq = { +		.seq_opc[0] = (SEQ_OPC_PADS_1 | +			       SEQ_OPC_CYCLES(8) | +			       SEQ_OPC_OPCODE(S25FL_CMD_CLSR) | +			       SEQ_OPC_CSDEASSERT), +		.seq_opc[1] = (SEQ_OPC_PADS_1 | +			       SEQ_OPC_CYCLES(8) | +			       SEQ_OPC_OPCODE(SPINOR_OP_WRDI) | +			       SEQ_OPC_CSDEASSERT), +		.seq = { +			STFSM_INST_CMD1, +			STFSM_INST_CMD2, +			STFSM_INST_WAIT, +			STFSM_INST_STOP, +		}, +		.seq_cfg = (SEQ_CFG_PADS_1 | +			    SEQ_CFG_ERASE | +			    SEQ_CFG_READNOTWRITE | +			    SEQ_CFG_CSDEASSERT | +			    SEQ_CFG_STARTSEQ), +	}; + +	stfsm_load_seq(fsm, &seq); + +	stfsm_wait_seq(fsm); + +	return 0; +} + +static int stfsm_s25fl_config(struct stfsm *fsm) +{ +	struct flash_info *info = fsm->info; +	uint32_t flags = info->flags; +	uint32_t data_pads; +	uint32_t offs; +	uint16_t sta_wr; +	uint8_t sr1, cr1, dyb; +	int update_sr = 0; +	int ret; + +	if (flags & FLASH_FLAG_32BIT_ADDR) { +		/* +		 * Prepare Read/Write/Erase sequences according to S25FLxxx +		 * 32-bit address command set +		 */ +		ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read, +						  stfsm_s25fl_read4_configs); +		if (ret) +			return ret; + +		ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_write, +						  stfsm_s25fl_write4_configs); +		if (ret) +			return ret; + +		stfsm_s25fl_prepare_erasesec_seq_32(&stfsm_seq_erase_sector); + +	} else { +		/* Use default configurations for 24-bit addressing */ +		ret = stfsm_prepare_rwe_seqs_default(fsm); +		if (ret) +			return ret; +	} + +	/* +	 * For devices that support 'DYB' sector locking, check lock status and +	 * unlock sectors if necessary (some variants power-on with sectors +	 * locked by default) +	 */ +	if (flags & FLASH_FLAG_DYB_LOCKING) { +		offs = 0; +		for (offs = 0; offs < info->sector_size * info->n_sectors;) { +			stfsm_s25fl_read_dyb(fsm, offs, &dyb); +			if (dyb == 0x00) +				stfsm_s25fl_write_dyb(fsm, offs, 0xff); + +			/* Handle bottom/top 4KiB parameter sectors */ +			if ((offs < info->sector_size * 2) || +			    (offs >= (info->sector_size - info->n_sectors * 4))) +				offs += 0x1000; +			else +				offs += 0x10000; +		} +	} + +	/* Check status of 'QE' bit, update if required. */ +	stfsm_read_status(fsm, SPINOR_OP_RDSR2, &cr1, 1); +	data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1; +	if (data_pads == 4) { +		if (!(cr1 & STFSM_S25FL_CONFIG_QE)) { +			/* Set 'QE' */ +			cr1 |= STFSM_S25FL_CONFIG_QE; + +			update_sr = 1; +		} +	} else { +		if (cr1 & STFSM_S25FL_CONFIG_QE) { +			/* Clear 'QE' */ +			cr1 &= ~STFSM_S25FL_CONFIG_QE; + +			update_sr = 1; +		} +	} +	if (update_sr) { +		stfsm_read_status(fsm, SPINOR_OP_RDSR, &sr1, 1); +		sta_wr = ((uint16_t)cr1  << 8) | sr1; +		stfsm_write_status(fsm, SPINOR_OP_WRSR, sta_wr, 2, 1); +	} + +	/* +	 * S25FLxxx devices support Program and Error error flags. +	 * Configure driver to check flags and clear if necessary. +	 */ +	fsm->configuration |= CFG_S25FL_CHECK_ERROR_FLAGS; + +	return 0; +} + +static int stfsm_w25q_config(struct stfsm *fsm) +{ +	uint32_t data_pads; +	uint8_t sr1, sr2; +	uint16_t sr_wr; +	int update_sr = 0; +	int ret; + +	ret = stfsm_prepare_rwe_seqs_default(fsm); +	if (ret) +		return ret; + +	/* Check status of 'QE' bit, update if required. */ +	stfsm_read_status(fsm, SPINOR_OP_RDSR2, &sr2, 1); +	data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1; +	if (data_pads == 4) { +		if (!(sr2 & W25Q_STATUS_QE)) { +			/* Set 'QE' */ +			sr2 |= W25Q_STATUS_QE; +			update_sr = 1; +		} +	} else { +		if (sr2 & W25Q_STATUS_QE) { +			/* Clear 'QE' */ +			sr2 &= ~W25Q_STATUS_QE; +			update_sr = 1; +		} +	} +	if (update_sr) { +		/* Write status register */ +		stfsm_read_status(fsm, SPINOR_OP_RDSR, &sr1, 1); +		sr_wr = ((uint16_t)sr2 << 8) | sr1; +		stfsm_write_status(fsm, SPINOR_OP_WRSR, sr_wr, 2, 1); +	} + +	return 0; +} + +static int stfsm_read(struct stfsm *fsm, uint8_t *buf, uint32_t size, +		      uint32_t offset) +{ +	struct stfsm_seq *seq = &fsm->stfsm_seq_read; +	uint32_t data_pads; +	uint32_t read_mask; +	uint32_t size_ub; +	uint32_t size_lb; +	uint32_t size_mop; +	uint32_t tmp[4]; +	uint32_t page_buf[FLASH_PAGESIZE_32]; +	uint8_t *p; + +	dev_dbg(fsm->dev, "reading %d bytes from 0x%08x\n", size, offset); + +	/* Enter 32-bit address mode, if required */ +	if (fsm->configuration & CFG_READ_TOGGLE_32BIT_ADDR) +		stfsm_enter_32bit_addr(fsm, 1); + +	/* Must read in multiples of 32 cycles (or 32*pads/8 Bytes) */ +	data_pads = ((seq->seq_cfg >> 16) & 0x3) + 1; +	read_mask = (data_pads << 2) - 1; + +	/* Handle non-aligned buf */ +	p = ((uintptr_t)buf & 0x3) ? (uint8_t *)page_buf : buf; + +	/* Handle non-aligned size */ +	size_ub = (size + read_mask) & ~read_mask; +	size_lb = size & ~read_mask; +	size_mop = size & read_mask; + +	seq->data_size = TRANSFER_SIZE(size_ub); +	seq->addr1 = (offset >> 16) & 0xffff; +	seq->addr2 = offset & 0xffff; + +	stfsm_load_seq(fsm, seq); + +	if (size_lb) +		stfsm_read_fifo(fsm, (uint32_t *)p, size_lb); + +	if (size_mop) { +		stfsm_read_fifo(fsm, tmp, read_mask + 1); +		memcpy(p + size_lb, &tmp, size_mop); +	} + +	/* Handle non-aligned buf */ +	if ((uintptr_t)buf & 0x3) +		memcpy(buf, page_buf, size); + +	/* Wait for sequence to finish */ +	stfsm_wait_seq(fsm); + +	stfsm_clear_fifo(fsm); + +	/* Exit 32-bit address mode, if required */ +	if (fsm->configuration & CFG_READ_TOGGLE_32BIT_ADDR) +		stfsm_enter_32bit_addr(fsm, 0); + +	return 0; +} + +static int stfsm_write(struct stfsm *fsm, const uint8_t *buf, +		       uint32_t size, uint32_t offset) +{ +	struct stfsm_seq *seq = &fsm->stfsm_seq_write; +	uint32_t data_pads; +	uint32_t write_mask; +	uint32_t size_ub; +	uint32_t size_lb; +	uint32_t size_mop; +	uint32_t tmp[4]; +	uint32_t page_buf[FLASH_PAGESIZE_32]; +	uint8_t *t = (uint8_t *)&tmp; +	const uint8_t *p; +	int ret; +	int i; + +	dev_dbg(fsm->dev, "writing %d bytes to 0x%08x\n", size, offset); + +	/* Enter 32-bit address mode, if required */ +	if (fsm->configuration & CFG_WRITE_TOGGLE_32BIT_ADDR) +		stfsm_enter_32bit_addr(fsm, 1); + +	/* Must write in multiples of 32 cycles (or 32*pads/8 bytes) */ +	data_pads = ((seq->seq_cfg >> 16) & 0x3) + 1; +	write_mask = (data_pads << 2) - 1; + +	/* Handle non-aligned buf */ +	if ((uintptr_t)buf & 0x3) { +		memcpy(page_buf, buf, size); +		p = (uint8_t *)page_buf; +	} else { +		p = buf; +	} + +	/* Handle non-aligned size */ +	size_ub = (size + write_mask) & ~write_mask; +	size_lb = size & ~write_mask; +	size_mop = size & write_mask; + +	seq->data_size = TRANSFER_SIZE(size_ub); +	seq->addr1 = (offset >> 16) & 0xffff; +	seq->addr2 = offset & 0xffff; + +	/* Need to set FIFO to write mode, before writing data to FIFO (see +	 * GNBvb79594) +	 */ +	writel(0x00040000, fsm->base + SPI_FAST_SEQ_CFG); + +	/* +	 * Before writing data to the FIFO, apply a small delay to allow a +	 * potential change of FIFO direction to complete. +	 */ +	if (fsm->fifo_dir_delay == 0) +		readl(fsm->base + SPI_FAST_SEQ_CFG); +	else +		udelay(fsm->fifo_dir_delay); + + +	/* Write data to FIFO, before starting sequence (see GNBvd79593) */ +	if (size_lb) { +		stfsm_write_fifo(fsm, (uint32_t *)p, size_lb); +		p += size_lb; +	} + +	/* Handle non-aligned size */ +	if (size_mop) { +		memset(t, 0xff, write_mask + 1);	/* fill with 0xff's */ +		for (i = 0; i < size_mop; i++) +			t[i] = *p++; + +		stfsm_write_fifo(fsm, tmp, write_mask + 1); +	} + +	/* Start sequence */ +	stfsm_load_seq(fsm, seq); + +	/* Wait for sequence to finish */ +	stfsm_wait_seq(fsm); + +	/* Wait for completion */ +	ret = stfsm_wait_busy(fsm); +	if (ret && fsm->configuration & CFG_S25FL_CHECK_ERROR_FLAGS) +		stfsm_s25fl_clear_status_reg(fsm); + +	/* Exit 32-bit address mode, if required */ +	if (fsm->configuration & CFG_WRITE_TOGGLE_32BIT_ADDR) +		stfsm_enter_32bit_addr(fsm, 0); + +	return 0; +} + +/* + * Read an address range from the flash chip. The address range + * may be any size provided it is within the physical boundaries. + */ +static int stfsm_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, +			  size_t *retlen, u_char *buf) +{ +	struct stfsm *fsm = dev_get_drvdata(mtd->dev.parent); +	uint32_t bytes; + +	dev_dbg(fsm->dev, "%s from 0x%08x, len %zd\n", +		__func__, (u32)from, len); + +	mutex_lock(&fsm->lock); + +	while (len > 0) { +		bytes = min_t(size_t, len, FLASH_PAGESIZE); + +		stfsm_read(fsm, buf, bytes, from); + +		buf += bytes; +		from += bytes; +		len -= bytes; + +		*retlen += bytes; +	} + +	mutex_unlock(&fsm->lock); + +	return 0; +} + +static int stfsm_erase_sector(struct stfsm *fsm, uint32_t offset) +{ +	struct stfsm_seq *seq = &stfsm_seq_erase_sector; +	int ret; + +	dev_dbg(fsm->dev, "erasing sector at 0x%08x\n", offset); + +	/* Enter 32-bit address mode, if required */ +	if (fsm->configuration & CFG_ERASESEC_TOGGLE_32BIT_ADDR) +		stfsm_enter_32bit_addr(fsm, 1); + +	seq->addr1 = (offset >> 16) & 0xffff; +	seq->addr2 = offset & 0xffff; + +	stfsm_load_seq(fsm, seq); + +	stfsm_wait_seq(fsm); + +	/* Wait for completion */ +	ret = stfsm_wait_busy(fsm); +	if (ret && fsm->configuration & CFG_S25FL_CHECK_ERROR_FLAGS) +		stfsm_s25fl_clear_status_reg(fsm); + +	/* Exit 32-bit address mode, if required */ +	if (fsm->configuration & CFG_ERASESEC_TOGGLE_32BIT_ADDR) +		stfsm_enter_32bit_addr(fsm, 0); + +	return ret; +} + +static int stfsm_erase_chip(struct stfsm *fsm) +{ +	const struct stfsm_seq *seq = &stfsm_seq_erase_chip; + +	dev_dbg(fsm->dev, "erasing chip\n"); + +	stfsm_load_seq(fsm, seq); + +	stfsm_wait_seq(fsm); + +	return stfsm_wait_busy(fsm); +} + +/* + * Write an address range to the flash chip.  Data must be written in + * FLASH_PAGESIZE chunks.  The address range may be any size provided + * it is within the physical boundaries. + */ +static int stfsm_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, +			   size_t *retlen, const u_char *buf) +{ +	struct stfsm *fsm = dev_get_drvdata(mtd->dev.parent); + +	u32 page_offs; +	u32 bytes; +	uint8_t *b = (uint8_t *)buf; +	int ret = 0; + +	dev_dbg(fsm->dev, "%s to 0x%08x, len %zd\n", __func__, (u32)to, len); + +	/* Offset within page */ +	page_offs = to % FLASH_PAGESIZE; + +	mutex_lock(&fsm->lock); + +	while (len) { +		/* Write up to page boundary */ +		bytes = min_t(size_t, FLASH_PAGESIZE - page_offs, len); + +		ret = stfsm_write(fsm, b, bytes, to); +		if (ret) +			goto out1; + +		b += bytes; +		len -= bytes; +		to += bytes; + +		/* We are now page-aligned */ +		page_offs = 0; + +		*retlen += bytes; + +	} + +out1: +	mutex_unlock(&fsm->lock); + +	return ret; +} + +/* + * Erase an address range on the flash chip. The address range may extend + * one or more erase sectors.  Return an error is there is a problem erasing. + */ +static int stfsm_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) +{ +	struct stfsm *fsm = dev_get_drvdata(mtd->dev.parent); +	u32 addr, len; +	int ret; + +	dev_dbg(fsm->dev, "%s at 0x%llx, len %lld\n", __func__, +		(long long)instr->addr, (long long)instr->len); + +	addr = instr->addr; +	len = instr->len; + +	mutex_lock(&fsm->lock); + +	/* Whole-chip erase? */ +	if (len == mtd->size) { +		ret = stfsm_erase_chip(fsm); +		if (ret) +			goto out1; +	} else { +		while (len) { +			ret = stfsm_erase_sector(fsm, addr); +			if (ret) +				goto out1; + +			addr += mtd->erasesize; +			len -= mtd->erasesize; +		} +	} + +	mutex_unlock(&fsm->lock); + +	instr->state = MTD_ERASE_DONE; +	mtd_erase_callback(instr); + +	return 0; + +out1: +	instr->state = MTD_ERASE_FAILED; +	mutex_unlock(&fsm->lock); + +	return ret; +} + +static void stfsm_read_jedec(struct stfsm *fsm, uint8_t *jedec) +{ +	const struct stfsm_seq *seq = &stfsm_seq_read_jedec; +	uint32_t tmp[2]; + +	stfsm_load_seq(fsm, seq); + +	stfsm_read_fifo(fsm, tmp, 8); + +	memcpy(jedec, tmp, 5); + +	stfsm_wait_seq(fsm); +} + +static struct flash_info *stfsm_jedec_probe(struct stfsm *fsm) +{ +	struct flash_info	*info; +	u16                     ext_jedec; +	u32			jedec; +	u8			id[5]; + +	stfsm_read_jedec(fsm, id); + +	jedec     = id[0] << 16 | id[1] << 8 | id[2]; +	/* +	 * JEDEC also defines an optional "extended device information" +	 * string for after vendor-specific data, after the three bytes +	 * we use here. Supporting some chips might require using it. +	 */ +	ext_jedec = id[3] << 8  | id[4]; + +	dev_dbg(fsm->dev, "JEDEC =  0x%08x [%02x %02x %02x %02x %02x]\n", +		jedec, id[0], id[1], id[2], id[3], id[4]); + +	for (info = flash_types; info->name; info++) { +		if (info->jedec_id == jedec) { +			if (info->ext_id && info->ext_id != ext_jedec) +				continue; +			return info; +		} +	} +	dev_err(fsm->dev, "Unrecognized JEDEC id %06x\n", jedec); + +	return NULL; +} + +static int stfsm_set_mode(struct stfsm *fsm, uint32_t mode) +{ +	int ret, timeout = 10; + +	/* Wait for controller to accept mode change */ +	while (--timeout) { +		ret = readl(fsm->base + SPI_STA_MODE_CHANGE); +		if (ret & 0x1) +			break; +		udelay(1); +	} + +	if (!timeout) +		return -EBUSY; + +	writel(mode, fsm->base + SPI_MODESELECT); + +	return 0; +} + +static void stfsm_set_freq(struct stfsm *fsm, uint32_t spi_freq) +{ +	uint32_t emi_freq; +	uint32_t clk_div; + +	/* TODO: Make this dynamic */ +	emi_freq = STFSM_DEFAULT_EMI_FREQ; + +	/* +	 * Calculate clk_div - values between 2 and 128 +	 * Multiple of 2, rounded up +	 */ +	clk_div = 2 * DIV_ROUND_UP(emi_freq, 2 * spi_freq); +	if (clk_div < 2) +		clk_div = 2; +	else if (clk_div > 128) +		clk_div = 128; + +	/* +	 * Determine a suitable delay for the IP to complete a change of +	 * direction of the FIFO. The required delay is related to the clock +	 * divider used. The following heuristics are based on empirical tests, +	 * using a 100MHz EMI clock. +	 */ +	if (clk_div <= 4) +		fsm->fifo_dir_delay = 0; +	else if (clk_div <= 10) +		fsm->fifo_dir_delay = 1; +	else +		fsm->fifo_dir_delay = DIV_ROUND_UP(clk_div, 10); + +	dev_dbg(fsm->dev, "emi_clk = %uHZ, spi_freq = %uHZ, clk_div = %u\n", +		emi_freq, spi_freq, clk_div); + +	writel(clk_div, fsm->base + SPI_CLOCKDIV); +} + +static int stfsm_init(struct stfsm *fsm) +{ +	int ret; + +	/* Perform a soft reset of the FSM controller */ +	writel(SEQ_CFG_SWRESET, fsm->base + SPI_FAST_SEQ_CFG); +	udelay(1); +	writel(0, fsm->base + SPI_FAST_SEQ_CFG); + +	/* Set clock to 'safe' frequency initially */ +	stfsm_set_freq(fsm, STFSM_FLASH_SAFE_FREQ); + +	/* Switch to FSM */ +	ret = stfsm_set_mode(fsm, SPI_MODESELECT_FSM); +	if (ret) +		return ret; + +	/* Set timing parameters */ +	writel(SPI_CFG_DEVICE_ST            | +	       SPI_CFG_DEFAULT_MIN_CS_HIGH  | +	       SPI_CFG_DEFAULT_CS_SETUPHOLD | +	       SPI_CFG_DEFAULT_DATA_HOLD, +	       fsm->base + SPI_CONFIGDATA); +	writel(STFSM_DEFAULT_WR_TIME, fsm->base + SPI_STATUS_WR_TIME_REG); + +	/* +	 * Set the FSM 'WAIT' delay to the minimum workable value.  Note, for +	 * our purposes, the WAIT instruction is used purely to achieve +	 * "sequence validity" rather than actually implement a delay. +	 */ +	writel(0x00000001, fsm->base + SPI_PROGRAM_ERASE_TIME); + +	/* Clear FIFO, just in case */ +	stfsm_clear_fifo(fsm); + +	return 0; +} + +static void stfsm_fetch_platform_configs(struct platform_device *pdev) +{ +	struct stfsm *fsm = platform_get_drvdata(pdev); +	struct device_node *np = pdev->dev.of_node; +	struct regmap *regmap; +	uint32_t boot_device_reg; +	uint32_t boot_device_spi; +	uint32_t boot_device;     /* Value we read from *boot_device_reg */ +	int ret; + +	/* Booting from SPI NOR Flash is the default */ +	fsm->booted_from_spi = true; + +	regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); +	if (IS_ERR(regmap)) +		goto boot_device_fail; + +	fsm->reset_signal = of_property_read_bool(np, "st,reset-signal"); + +	fsm->reset_por = of_property_read_bool(np, "st,reset-por"); + +	/* Where in the syscon the boot device information lives */ +	ret = of_property_read_u32(np, "st,boot-device-reg", &boot_device_reg); +	if (ret) +		goto boot_device_fail; + +	/* Boot device value when booted from SPI NOR */ +	ret = of_property_read_u32(np, "st,boot-device-spi", &boot_device_spi); +	if (ret) +		goto boot_device_fail; + +	ret = regmap_read(regmap, boot_device_reg, &boot_device); +	if (ret) +		goto boot_device_fail; + +	if (boot_device != boot_device_spi) +		fsm->booted_from_spi = false; + +	return; + +boot_device_fail: +	dev_warn(&pdev->dev, +		 "failed to fetch boot device, assuming boot from SPI\n"); +} + +static int stfsm_probe(struct platform_device *pdev) +{ +	struct device_node *np = pdev->dev.of_node; +	struct mtd_part_parser_data ppdata; +	struct flash_info *info; +	struct resource *res; +	struct stfsm *fsm; +	int ret; + +	if (!np) { +		dev_err(&pdev->dev, "No DT found\n"); +		return -EINVAL; +	} +	ppdata.of_node = np; + +	fsm = devm_kzalloc(&pdev->dev, sizeof(*fsm), GFP_KERNEL); +	if (!fsm) +		return -ENOMEM; + +	fsm->dev = &pdev->dev; + +	platform_set_drvdata(pdev, fsm); + +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!res) { +		dev_err(&pdev->dev, "Resource not found\n"); +		return -ENODEV; +	} + +	fsm->base = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(fsm->base)) { +		dev_err(&pdev->dev, +			"Failed to reserve memory region %pR\n", res); +		return PTR_ERR(fsm->base); +	} + +	mutex_init(&fsm->lock); + +	ret = stfsm_init(fsm); +	if (ret) { +		dev_err(&pdev->dev, "Failed to initialise FSM Controller\n"); +		return ret; +	} + +	stfsm_fetch_platform_configs(pdev); + +	/* Detect SPI FLASH device */ +	info = stfsm_jedec_probe(fsm); +	if (!info) +		return -ENODEV; +	fsm->info = info; + +	/* Use device size to determine address width */ +	if (info->sector_size * info->n_sectors > 0x1000000) +		info->flags |= FLASH_FLAG_32BIT_ADDR; + +	/* +	 * Configure READ/WRITE/ERASE sequences according to platform and +	 * device flags. +	 */ +	if (info->config) { +		ret = info->config(fsm); +		if (ret) +			return ret; +	} else { +		ret = stfsm_prepare_rwe_seqs_default(fsm); +		if (ret) +			return ret; +	} + +	fsm->mtd.name		= info->name; +	fsm->mtd.dev.parent	= &pdev->dev; +	fsm->mtd.type		= MTD_NORFLASH; +	fsm->mtd.writesize	= 4; +	fsm->mtd.writebufsize	= fsm->mtd.writesize; +	fsm->mtd.flags		= MTD_CAP_NORFLASH; +	fsm->mtd.size		= info->sector_size * info->n_sectors; +	fsm->mtd.erasesize	= info->sector_size; + +	fsm->mtd._read  = stfsm_mtd_read; +	fsm->mtd._write = stfsm_mtd_write; +	fsm->mtd._erase = stfsm_mtd_erase; + +	dev_info(&pdev->dev, +		"Found serial flash device: %s\n" +		" size = %llx (%lldMiB) erasesize = 0x%08x (%uKiB)\n", +		info->name, +		(long long)fsm->mtd.size, (long long)(fsm->mtd.size >> 20), +		fsm->mtd.erasesize, (fsm->mtd.erasesize >> 10)); + +	return mtd_device_parse_register(&fsm->mtd, NULL, &ppdata, NULL, 0); +} + +static int stfsm_remove(struct platform_device *pdev) +{ +	struct stfsm *fsm = platform_get_drvdata(pdev); + +	return mtd_device_unregister(&fsm->mtd); +} + +static const struct of_device_id stfsm_match[] = { +	{ .compatible = "st,spi-fsm", }, +	{}, +}; +MODULE_DEVICE_TABLE(of, stfsm_match); + +static struct platform_driver stfsm_driver = { +	.probe		= stfsm_probe, +	.remove		= stfsm_remove, +	.driver		= { +		.name	= "st-spi-fsm", +		.owner	= THIS_MODULE, +		.of_match_table = stfsm_match, +	}, +}; +module_platform_driver(stfsm_driver); + +MODULE_AUTHOR("Angus Clark <angus.clark@st.com>"); +MODULE_DESCRIPTION("ST SPI FSM driver"); +MODULE_LICENSE("GPL");  | 
