diff options
Diffstat (limited to 'drivers/mtd')
37 files changed, 1331 insertions, 929 deletions
| diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 79a53cb8507b..00a79489067c 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -1353,7 +1353,7 @@ static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t a  {  	unsigned long cmd_addr;  	struct cfi_private *cfi = map->fldrv_priv; -	int ret = 0; +	int ret;  	adr += chip->start; @@ -1383,7 +1383,7 @@ static int cfi_intelext_point(struct mtd_info *mtd, loff_t from, size_t len,  	struct cfi_private *cfi = map->fldrv_priv;  	unsigned long ofs, last_end = 0;  	int chipnum; -	int ret = 0; +	int ret;  	if (!map->virt)  		return -EINVAL; @@ -1550,7 +1550,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,  {  	struct cfi_private *cfi = map->fldrv_priv;  	map_word status, write_cmd; -	int ret=0; +	int ret;  	adr += chip->start; @@ -1624,7 +1624,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le  {  	struct map_info *map = mtd->priv;  	struct cfi_private *cfi = map->fldrv_priv; -	int ret = 0; +	int ret;  	int chipnum;  	unsigned long ofs; @@ -1871,7 +1871,7 @@ static int cfi_intelext_writev (struct mtd_info *mtd, const struct kvec *vecs,  	struct map_info *map = mtd->priv;  	struct cfi_private *cfi = map->fldrv_priv;  	int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; -	int ret = 0; +	int ret;  	int chipnum;  	unsigned long ofs, vec_seek, i;  	size_t len = 0; diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index cf8c8be40a9c..04b383bc3947 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -123,19 +123,23 @@ static int cfi_use_status_reg(struct cfi_private *cfi)  		(extp->SoftwareFeatures & poll_mask) == CFI_POLL_STATUS_REG;  } -static void cfi_check_err_status(struct map_info *map, struct flchip *chip, -				 unsigned long adr) +static int cfi_check_err_status(struct map_info *map, struct flchip *chip, +				unsigned long adr)  {  	struct cfi_private *cfi = map->fldrv_priv;  	map_word status;  	if (!cfi_use_status_reg(cfi)) -		return; +		return 0;  	cfi_send_gen_cmd(0x70, cfi->addr_unlock1, chip->start, map, cfi,  			 cfi->device_type, NULL);  	status = map_read(map, adr); +	/* The error bits are invalid while the chip's busy */ +	if (!map_word_bitsset(map, status, CMD(CFI_SR_DRB))) +		return 0; +  	if (map_word_bitsset(map, status, CMD(0x3a))) {  		unsigned long chipstatus = MERGESTATUS(status); @@ -151,7 +155,12 @@ static void cfi_check_err_status(struct map_info *map, struct flchip *chip,  		if (chipstatus & CFI_SR_SLSB)  			pr_err("%s sector write protected, status %lx\n",  			       map->name, chipstatus); + +		/* Erase/Program status bits are set on the operation failure */ +		if (chipstatus & (CFI_SR_ESB | CFI_SR_PSB)) +			return 1;  	} +	return 0;  }  /* #define DEBUG_CFI_FEATURES */ @@ -785,7 +794,6 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)  	kfree(mtd->eraseregions);  	kfree(mtd);  	kfree(cfi->cmdset_priv); -	kfree(cfi->cfiq);  	return NULL;  } @@ -848,20 +856,16 @@ static int __xipram chip_good(struct map_info *map, struct flchip *chip,  	if (cfi_use_status_reg(cfi)) {  		map_word ready = CMD(CFI_SR_DRB); -		map_word err = CMD(CFI_SR_PSB | CFI_SR_ESB); +  		/*  		 * For chips that support status register, check device -		 * ready bit and Erase/Program status bit to know if -		 * operation succeeded. +		 * ready bit  		 */  		cfi_send_gen_cmd(0x70, cfi->addr_unlock1, chip->start, map, cfi,  				 cfi->device_type, NULL);  		curd = map_read(map, addr); -		if (map_word_andequal(map, curd, ready, ready)) -			return !map_word_bitsset(map, curd, err); - -		return 0; +		return map_word_andequal(map, curd, ready, ready);  	}  	oldd = map_read(map, addr); @@ -1699,8 +1703,11 @@ static int __xipram do_write_oneword_once(struct map_info *map,  			break;  		} -		if (chip_good(map, chip, adr, datum)) +		if (chip_good(map, chip, adr, datum)) { +			if (cfi_check_err_status(map, chip, adr)) +				ret = -EIO;  			break; +		}  		/* Latency issues. Drop the lock, wait a while and retry */  		UDELAY(map, chip, adr, 1); @@ -1713,7 +1720,7 @@ static int __xipram do_write_oneword_start(struct map_info *map,  					   struct flchip *chip,  					   unsigned long adr, int mode)  { -	int ret = 0; +	int ret;  	mutex_lock(&chip->mutex); @@ -1773,7 +1780,6 @@ static int __xipram do_write_oneword_retry(struct map_info *map,  	ret = do_write_oneword_once(map, chip, adr, datum, mode, cfi);  	if (ret) {  		/* reset on all failures. */ -		cfi_check_err_status(map, chip, adr);  		map_write(map, CMD(0xF0), chip->start);  		/* FIXME - should have reset delay before continuing */ @@ -1791,7 +1797,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,  				     unsigned long adr, map_word datum,  				     int mode)  { -	int ret = 0; +	int ret;  	adr += chip->start; @@ -1815,7 +1821,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,  {  	struct map_info *map = mtd->priv;  	struct cfi_private *cfi = map->fldrv_priv; -	int ret = 0; +	int ret;  	int chipnum;  	unsigned long ofs, chipstart;  	DECLARE_WAITQUEUE(wait, current); @@ -1970,12 +1976,17 @@ static int __xipram do_write_buffer_wait(struct map_info *map,  		 */  		if (time_after(jiffies, timeo) &&  		    !chip_good(map, chip, adr, datum)) { +			pr_err("MTD %s(): software timeout, address:0x%.8lx.\n", +			       __func__, adr);  			ret = -EIO;  			break;  		} -		if (chip_good(map, chip, adr, datum)) +		if (chip_good(map, chip, adr, datum)) { +			if (cfi_check_err_status(map, chip, adr)) +				ret = -EIO;  			break; +		}  		/* Latency issues. Drop the lock, wait a while and retry */  		UDELAY(map, chip, adr, 1); @@ -2014,7 +2025,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,  				    int len)  {  	struct cfi_private *cfi = map->fldrv_priv; -	int ret = -EIO; +	int ret;  	unsigned long cmd_adr;  	int z, words;  	map_word datum; @@ -2071,12 +2082,8 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,  				chip->word_write_time);  	ret = do_write_buffer_wait(map, chip, adr, datum); -	if (ret) { -		cfi_check_err_status(map, chip, adr); +	if (ret)  		do_write_buffer_reset(map, chip, cfi); -		pr_err("MTD %s(): software timeout, address:0x%.8lx.\n", -		       __func__, adr); -	}  	xip_enable(map, chip, adr); @@ -2095,7 +2102,7 @@ static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,  	struct map_info *map = mtd->priv;  	struct cfi_private *cfi = map->fldrv_priv;  	int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; -	int ret = 0; +	int ret;  	int chipnum;  	unsigned long ofs; @@ -2232,7 +2239,7 @@ static int do_panic_write_oneword(struct map_info *map, struct flchip *chip,  	struct cfi_private *cfi = map->fldrv_priv;  	int retry_cnt = 0;  	map_word oldd; -	int ret = 0; +	int ret;  	int i;  	adr += chip->start; @@ -2271,9 +2278,9 @@ retry:  		udelay(1);  	} -	if (!chip_good(map, chip, adr, datum)) { +	if (!chip_good(map, chip, adr, datum) || +	    cfi_check_err_status(map, chip, adr)) {  		/* reset on all failures. */ -		cfi_check_err_status(map, chip, adr);  		map_write(map, CMD(0xF0), chip->start);  		/* FIXME - should have reset delay before continuing */ @@ -2307,7 +2314,7 @@ static int cfi_amdstd_panic_write(struct mtd_info *mtd, loff_t to, size_t len,  	struct map_info *map = mtd->priv;  	struct cfi_private *cfi = map->fldrv_priv;  	unsigned long ofs, chipstart; -	int ret = 0; +	int ret;  	int chipnum;  	chipnum = to >> cfi->chipshift; @@ -2411,7 +2418,7 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip)  	unsigned long timeo = jiffies + HZ;  	unsigned long int adr;  	DECLARE_WAITQUEUE(wait, current); -	int ret = 0; +	int ret;  	int retry_cnt = 0;  	adr = cfi->addr_unlock1; @@ -2467,8 +2474,11 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip)  			chip->erase_suspended = 0;  		} -		if (chip_good(map, chip, adr, map_word_ff(map))) +		if (chip_good(map, chip, adr, map_word_ff(map))) { +			if (cfi_check_err_status(map, chip, adr)) +				ret = -EIO;  			break; +		}  		if (time_after(jiffies, timeo)) {  			printk(KERN_WARNING "MTD %s(): software timeout\n", @@ -2483,7 +2493,6 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip)  	/* Did we succeed? */  	if (ret) {  		/* reset on all failures. */ -		cfi_check_err_status(map, chip, adr);  		map_write(map, CMD(0xF0), chip->start);  		/* FIXME - should have reset delay before continuing */ @@ -2508,7 +2517,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,  	struct cfi_private *cfi = map->fldrv_priv;  	unsigned long timeo = jiffies + HZ;  	DECLARE_WAITQUEUE(wait, current); -	int ret = 0; +	int ret;  	int retry_cnt = 0;  	adr += chip->start; @@ -2564,8 +2573,11 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,  			chip->erase_suspended = 0;  		} -		if (chip_good(map, chip, adr, map_word_ff(map))) +		if (chip_good(map, chip, adr, map_word_ff(map))) { +			if (cfi_check_err_status(map, chip, adr)) +				ret = -EIO;  			break; +		}  		if (time_after(jiffies, timeo)) {  			printk(KERN_WARNING "MTD %s(): software timeout\n", @@ -2580,7 +2592,6 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,  	/* Did we succeed? */  	if (ret) {  		/* reset on all failures. */ -		cfi_check_err_status(map, chip, adr);  		map_write(map, CMD(0xF0), chip->start);  		/* FIXME - should have reset delay before continuing */ diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index e752067526a5..54edae63b92d 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -611,7 +611,7 @@ static int cfi_staa_write_buffers (struct mtd_info *mtd, loff_t to,  	struct map_info *map = mtd->priv;  	struct cfi_private *cfi = map->fldrv_priv;  	int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; -	int ret = 0; +	int ret;  	int chipnum;  	unsigned long ofs; @@ -895,7 +895,7 @@ static int cfi_staa_erase_varsize(struct mtd_info *mtd,  {	struct map_info *map = mtd->priv;  	struct cfi_private *cfi = map->fldrv_priv;  	unsigned long adr, len; -	int chipnum, ret = 0; +	int chipnum, ret;  	int i, first;  	struct mtd_erase_region_info *regions = mtd->eraseregions; @@ -1132,7 +1132,7 @@ static int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)  	struct map_info *map = mtd->priv;  	struct cfi_private *cfi = map->fldrv_priv;  	unsigned long adr; -	int chipnum, ret = 0; +	int chipnum, ret;  #ifdef DEBUG_LOCK_BITS  	int ofs_factor = cfi->interleave * cfi->device_type;  #endif @@ -1279,7 +1279,7 @@ static int cfi_staa_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)  	struct map_info *map = mtd->priv;  	struct cfi_private *cfi = map->fldrv_priv;  	unsigned long adr; -	int chipnum, ret = 0; +	int chipnum, ret;  #ifdef DEBUG_LOCK_BITS  	int ofs_factor = cfi->interleave * cfi->device_type;  #endif diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c index e3b266ee06af..e2d4db05aeb3 100644 --- a/drivers/mtd/chips/cfi_util.c +++ b/drivers/mtd/chips/cfi_util.c @@ -26,7 +26,7 @@  void cfi_udelay(int us)  {  	if (us >= 1000) { -		msleep((us+999)/1000); +		msleep(DIV_ROUND_UP(us, 1000));  	} else {  		udelay(us);  		cond_resched(); diff --git a/drivers/mtd/devices/mchp23k256.c b/drivers/mtd/devices/mchp23k256.c index b20d02b4f830..77c872fd3d83 100644 --- a/drivers/mtd/devices/mchp23k256.c +++ b/drivers/mtd/devices/mchp23k256.c @@ -64,15 +64,17 @@ static int mchp23k256_write(struct mtd_info *mtd, loff_t to, size_t len,  	struct spi_transfer transfer[2] = {};  	struct spi_message message;  	unsigned char command[MAX_CMD_SIZE]; -	int ret; +	int ret, cmd_len;  	spi_message_init(&message); +	cmd_len = mchp23k256_cmdsz(flash); +  	command[0] = MCHP23K256_CMD_WRITE;  	mchp23k256_addr2cmd(flash, to, command);  	transfer[0].tx_buf = command; -	transfer[0].len = mchp23k256_cmdsz(flash); +	transfer[0].len = cmd_len;  	spi_message_add_tail(&transfer[0], &message);  	transfer[1].tx_buf = buf; @@ -88,8 +90,8 @@ static int mchp23k256_write(struct mtd_info *mtd, loff_t to, size_t len,  	if (ret)  		return ret; -	if (retlen && message.actual_length > sizeof(command)) -		*retlen += message.actual_length - sizeof(command); +	if (retlen && message.actual_length > cmd_len) +		*retlen += message.actual_length - cmd_len;  	return 0;  } @@ -101,16 +103,18 @@ static int mchp23k256_read(struct mtd_info *mtd, loff_t from, size_t len,  	struct spi_transfer transfer[2] = {};  	struct spi_message message;  	unsigned char command[MAX_CMD_SIZE]; -	int ret; +	int ret, cmd_len;  	spi_message_init(&message); +	cmd_len = mchp23k256_cmdsz(flash); +  	memset(&transfer, 0, sizeof(transfer));  	command[0] = MCHP23K256_CMD_READ;  	mchp23k256_addr2cmd(flash, from, command);  	transfer[0].tx_buf = command; -	transfer[0].len = mchp23k256_cmdsz(flash); +	transfer[0].len = cmd_len;  	spi_message_add_tail(&transfer[0], &message);  	transfer[1].rx_buf = buf; @@ -126,8 +130,8 @@ static int mchp23k256_read(struct mtd_info *mtd, loff_t from, size_t len,  	if (ret)  		return ret; -	if (retlen && message.actual_length > sizeof(command)) -		*retlen += message.actual_length - sizeof(command); +	if (retlen && message.actual_length > cmd_len) +		*retlen += message.actual_length - cmd_len;  	return 0;  } diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c index 986f81d2f93e..79dcca16481d 100644 --- a/drivers/mtd/devices/spear_smi.c +++ b/drivers/mtd/devices/spear_smi.c @@ -592,6 +592,26 @@ static int spear_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,  	return 0;  } +/* + * The purpose of this function is to ensure a memcpy_toio() with byte writes + * only. Its structure is inspired from the ARM implementation of _memcpy_toio() + * which also does single byte writes but cannot be used here as this is just an + * implementation detail and not part of the API. Not mentioning the comment + * stating that _memcpy_toio() should be optimized. + */ +static void spear_smi_memcpy_toio_b(volatile void __iomem *dest, +				    const void *src, size_t len) +{ +	const unsigned char *from = src; + +	while (len) { +		len--; +		writeb(*from, dest); +		from++; +		dest++; +	} +} +  static inline int spear_smi_cpy_toio(struct spear_smi *dev, u32 bank,  		void __iomem *dest, const void *src, size_t len)  { @@ -614,7 +634,23 @@ static inline int spear_smi_cpy_toio(struct spear_smi *dev, u32 bank,  	ctrlreg1 = readl(dev->io_base + SMI_CR1);  	writel((ctrlreg1 | WB_MODE) & ~SW_MODE, dev->io_base + SMI_CR1); -	memcpy_toio(dest, src, len); +	/* +	 * In Write Burst mode (WB_MODE), the specs states that writes must be: +	 * - incremental +	 * - of the same size +	 * The ARM implementation of memcpy_toio() will optimize the number of +	 * I/O by using as much 4-byte writes as possible, surrounded by +	 * 2-byte/1-byte access if: +	 * - the destination is not 4-byte aligned +	 * - the length is not a multiple of 4-byte. +	 * Avoid this alternance of write access size by using our own 'byte +	 * access' helper if at least one of the two conditions above is true. +	 */ +	if (IS_ALIGNED(len, sizeof(u32)) && +	    IS_ALIGNED((uintptr_t)dest, sizeof(u32))) +		memcpy_toio(dest, src, len); +	else +		spear_smi_memcpy_toio_b(dest, src, len);  	writel(ctrlreg1, dev->io_base + SMI_CR1); @@ -777,9 +813,6 @@ static int spear_smi_probe_config_dt(struct platform_device *pdev,  	/* Fill structs for each subnode (flash device) */  	while ((pp = of_get_next_child(np, pp))) { -		struct spear_smi_flash_info *flash_info; - -		flash_info = &pdata->board_flash_info[i];  		pdata->np[i] = pp;  		/* Read base-addr and size from DT */ @@ -933,7 +966,6 @@ static int spear_smi_probe(struct platform_device *pdev)  	irq = platform_get_irq(pdev, 0);  	if (irq < 0) {  		ret = -ENODEV; -		dev_err(&pdev->dev, "invalid smi irq\n");  		goto err;  	} diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index f4d1667daaf9..1888523d9745 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -255,7 +255,6 @@ struct stfsm_seq {  struct stfsm {  	struct device		*dev;  	void __iomem		*base; -	struct resource		*region;  	struct mtd_info		mtd;  	struct mutex		lock;  	struct flash_info       *info; diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index bc82305ebb4c..b28225a7c4f3 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -96,6 +96,17 @@ config MTD_PHYSMAP_GEMINI  	  platforms, some detection and setting up parallel mode on the  	  external interface. +config MTD_PHYSMAP_IXP4XX +	bool "Intel IXP4xx OF-based physical memory map handling" +	depends on MTD_PHYSMAP_OF +	depends on ARM +	select MTD_COMPLEX_MAPPINGS +	select MTD_CFI_BE_BYTE_SWAP if CPU_BIG_ENDIAN +	default ARCH_IXP4XX +	help +	  This provides some extra DT physmap parsing for the Intel IXP4xx +	  platforms, some elaborate endianness handling in particular. +  config MTD_PHYSMAP_GPIO_ADDR  	bool "GPIO-assisted Flash Chip Support"  	depends on MTD_PHYSMAP diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 1146009f41df..c0da86a5d26f 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_MTD_PXA2XX)	+= pxa2xx-flash.o  physmap-objs-y			+= physmap-core.o  physmap-objs-$(CONFIG_MTD_PHYSMAP_VERSATILE) += physmap-versatile.o  physmap-objs-$(CONFIG_MTD_PHYSMAP_GEMINI) += physmap-gemini.o +physmap-objs-$(CONFIG_MTD_PHYSMAP_IXP4XX) += physmap-ixp4xx.o  physmap-objs			:= $(physmap-objs-y)  obj-$(CONFIG_MTD_PHYSMAP)	+= physmap.o  obj-$(CONFIG_MTD_PISMO)		+= pismo.o diff --git a/drivers/mtd/maps/l440gx.c b/drivers/mtd/maps/l440gx.c index 876f12f40018..0eeadfeb620d 100644 --- a/drivers/mtd/maps/l440gx.c +++ b/drivers/mtd/maps/l440gx.c @@ -86,7 +86,7 @@ static int __init init_l440gx(void)  		return -ENOMEM;  	}  	simple_map_init(&l440gx_map); -	printk(KERN_NOTICE "window_addr = 0x%08lx\n", (unsigned long)l440gx_map.virt); +	pr_debug("window_addr = %p\n", l440gx_map.virt);  	/* Setup the pm iobase resource  	 * This code should move into some kind of generic bridge diff --git a/drivers/mtd/maps/physmap-core.c b/drivers/mtd/maps/physmap-core.c index 21b556afc305..a9f7964e2edb 100644 --- a/drivers/mtd/maps/physmap-core.c +++ b/drivers/mtd/maps/physmap-core.c @@ -41,6 +41,7 @@  #include <linux/gpio/consumer.h>  #include "physmap-gemini.h" +#include "physmap-ixp4xx.h"  #include "physmap-versatile.h"  struct physmap_flash_info { @@ -370,6 +371,10 @@ static int physmap_flash_of_init(struct platform_device *dev)  		if (err)  			return err; +		err = of_flash_probe_ixp4xx(dev, dp, &info->maps[i]); +		if (err) +			return err; +  		err = of_flash_probe_versatile(dev, dp, &info->maps[i]);  		if (err)  			return err; diff --git a/drivers/mtd/maps/physmap-ixp4xx.c b/drivers/mtd/maps/physmap-ixp4xx.c new file mode 100644 index 000000000000..6a054229a8a0 --- /dev/null +++ b/drivers/mtd/maps/physmap-ixp4xx.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel IXP4xx OF physmap add-on + * Copyright (C) 2019 Linus Walleij <linus.walleij@linaro.org> + * + * Based on the ixp4xx.c map driver, originally written by: + * Intel Corporation + * Deepak Saxena <dsaxena@mvista.com> + * Copyright (C) 2002 Intel Corporation + * Copyright (C) 2003-2004 MontaVista Software, Inc. + */ +#include <linux/export.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/mtd/map.h> +#include <linux/mtd/xip.h> +#include "physmap-ixp4xx.h" + +/* + * Read/write a 16 bit word from flash address 'addr'. + * + * When the cpu is in little-endian mode it swizzles the address lines + * ('address coherency') so we need to undo the swizzling to ensure commands + * and the like end up on the correct flash address. + * + * To further complicate matters, due to the way the expansion bus controller + * handles 32 bit reads, the byte stream ABCD is stored on the flash as: + *     D15    D0 + *     +---+---+ + *     | A | B | 0 + *     +---+---+ + *     | C | D | 2 + *     +---+---+ + * This means that on LE systems each 16 bit word must be swapped. Note that + * this requires CONFIG_MTD_CFI_BE_BYTE_SWAP to be enabled to 'unswap' the CFI + * data and other flash commands which are always in D7-D0. + */ +#ifndef CONFIG_CPU_BIG_ENDIAN + +static inline u16 flash_read16(void __iomem *addr) +{ +	return be16_to_cpu(__raw_readw((void __iomem *)((unsigned long)addr ^ 0x2))); +} + +static inline void flash_write16(u16 d, void __iomem *addr) +{ +	__raw_writew(cpu_to_be16(d), (void __iomem *)((unsigned long)addr ^ 0x2)); +} + +#define	BYTE0(h)	((h) & 0xFF) +#define	BYTE1(h)	(((h) >> 8) & 0xFF) + +#else + +static inline u16 flash_read16(const void __iomem *addr) +{ +	return __raw_readw(addr); +} + +static inline void flash_write16(u16 d, void __iomem *addr) +{ +	__raw_writew(d, addr); +} + +#define	BYTE0(h)	(((h) >> 8) & 0xFF) +#define	BYTE1(h)	((h) & 0xFF) +#endif + +static map_word ixp4xx_read16(struct map_info *map, unsigned long ofs) +{ +	map_word val; + +	val.x[0] = flash_read16(map->virt + ofs); +	return val; +} + +/* + * The IXP4xx expansion bus only allows 16-bit wide acceses + * when attached to a 16-bit wide device (such as the 28F128J3A), + * so we can't just memcpy_fromio(). + */ +static void ixp4xx_copy_from(struct map_info *map, void *to, +			     unsigned long from, ssize_t len) +{ +	u8 *dest = (u8 *) to; +	void __iomem *src = map->virt + from; + +	if (len <= 0) +		return; + +	if (from & 1) { +		*dest++ = BYTE1(flash_read16(src-1)); +		src++; +		--len; +	} + +	while (len >= 2) { +		u16 data = flash_read16(src); +		*dest++ = BYTE0(data); +		*dest++ = BYTE1(data); +		src += 2; +		len -= 2; +	} + +	if (len > 0) +		*dest++ = BYTE0(flash_read16(src)); +} + +static void ixp4xx_write16(struct map_info *map, map_word d, unsigned long adr) +{ +	flash_write16(d.x[0], map->virt + adr); +} + +int of_flash_probe_ixp4xx(struct platform_device *pdev, +			  struct device_node *np, +			  struct map_info *map) +{ +	struct device *dev = &pdev->dev; + +	/* Multiplatform guard */ +	if (!of_device_is_compatible(np, "intel,ixp4xx-flash")) +		return 0; + +	map->read = ixp4xx_read16; +	map->write = ixp4xx_write16; +	map->copy_from = ixp4xx_copy_from; +	map->copy_to = NULL; + +	dev_info(dev, "initialized Intel IXP4xx-specific physmap control\n"); + +	return 0; +} diff --git a/drivers/mtd/maps/physmap-ixp4xx.h b/drivers/mtd/maps/physmap-ixp4xx.h new file mode 100644 index 000000000000..b0fc49b7f3ed --- /dev/null +++ b/drivers/mtd/maps/physmap-ixp4xx.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include <linux/of.h> +#include <linux/mtd/map.h> + +#ifdef CONFIG_MTD_PHYSMAP_IXP4XX +int of_flash_probe_ixp4xx(struct platform_device *pdev, +			  struct device_node *np, +			  struct map_info *map); +#else +static inline +int of_flash_probe_ixp4xx(struct platform_device *pdev, +			  struct device_node *np, +			  struct map_info *map) +{ +	return 0; +} +#endif diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 975aed94f06c..b841008a9eb7 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -174,7 +174,7 @@ static ssize_t mtdchar_read(struct file *file, char __user *buf, size_t count,  			break;  		case MTD_FILE_MODE_RAW:  		{ -			struct mtd_oob_ops ops; +			struct mtd_oob_ops ops = {};  			ops.mode = MTD_OPS_RAW;  			ops.datbuf = kbuf; @@ -268,7 +268,7 @@ static ssize_t mtdchar_write(struct file *file, const char __user *buf, size_t c  		case MTD_FILE_MODE_RAW:  		{ -			struct mtd_oob_ops ops; +			struct mtd_oob_ops ops = {};  			ops.mode = MTD_OPS_RAW;  			ops.datbuf = kbuf; @@ -350,7 +350,7 @@ static int mtdchar_writeoob(struct file *file, struct mtd_info *mtd,  	uint32_t __user *retp)  {  	struct mtd_file_info *mfi = file->private_data; -	struct mtd_oob_ops ops; +	struct mtd_oob_ops ops = {};  	uint32_t retlen;  	int ret = 0; @@ -394,7 +394,7 @@ static int mtdchar_readoob(struct file *file, struct mtd_info *mtd,  	uint32_t __user *retp)  {  	struct mtd_file_info *mfi = file->private_data; -	struct mtd_oob_ops ops; +	struct mtd_oob_ops ops = {};  	int ret = 0;  	if (length > 4096) @@ -587,7 +587,7 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd,  		struct mtd_write_req __user *argp)  {  	struct mtd_write_req req; -	struct mtd_oob_ops ops; +	struct mtd_oob_ops ops = {};  	const void __user *usr_data, *usr_oob;  	int ret; diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 6cc7ecb0c788..5fac4355b9c2 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -382,33 +382,21 @@ static struct dentry *dfs_dir_mtd;  static void mtd_debugfs_populate(struct mtd_info *mtd)  {  	struct device *dev = &mtd->dev; -	struct dentry *root, *dent; +	struct dentry *root;  	if (IS_ERR_OR_NULL(dfs_dir_mtd))  		return;  	root = debugfs_create_dir(dev_name(dev), dfs_dir_mtd); -	if (IS_ERR_OR_NULL(root)) { -		dev_dbg(dev, "won't show data in debugfs\n"); -		return; -	} -  	mtd->dbg.dfs_dir = root; -	if (mtd->dbg.partid) { -		dent = debugfs_create_file("partid", 0400, root, mtd, -					   &mtd_partid_debug_fops); -		if (IS_ERR_OR_NULL(dent)) -			dev_err(dev, "can't create debugfs entry for partid\n"); -	} +	if (mtd->dbg.partid) +		debugfs_create_file("partid", 0400, root, mtd, +				    &mtd_partid_debug_fops); -	if (mtd->dbg.partname) { -		dent = debugfs_create_file("partname", 0400, root, mtd, -					   &mtd_partname_debug_fops); -		if (IS_ERR_OR_NULL(dent)) -			dev_err(dev, -				"can't create debugfs entry for partname\n"); -	} +	if (mtd->dbg.partname) +		debugfs_create_file("partname", 0400, root, mtd, +				    &mtd_partname_debug_fops);  }  #ifndef CONFIG_MMU diff --git a/drivers/mtd/mtdswap.c b/drivers/mtd/mtdswap.c index f92414eb4c86..58eefa43af14 100644 --- a/drivers/mtd/mtdswap.c +++ b/drivers/mtd/mtdswap.c @@ -1257,7 +1257,6 @@ DEFINE_SHOW_ATTRIBUTE(mtdswap);  static int mtdswap_add_debugfs(struct mtdswap_dev *d)  {  	struct dentry *root = d->mtd->dbg.dfs_dir; -	struct dentry *dent;  	if (!IS_ENABLED(CONFIG_DEBUG_FS))  		return 0; @@ -1265,12 +1264,7 @@ static int mtdswap_add_debugfs(struct mtdswap_dev *d)  	if (IS_ERR_OR_NULL(root))  		return -1; -	dent = debugfs_create_file("mtdswap_stats", S_IRUSR, root, d, -				&mtdswap_fops); -	if (!dent) { -		dev_err(d->dev, "debugfs_create_file failed\n"); -		return -1; -	} +	debugfs_create_file("mtdswap_stats", S_IRUSR, root, d, &mtdswap_fops);  	return 0;  } diff --git a/drivers/mtd/nand/raw/au1550nd.c b/drivers/mtd/nand/raw/au1550nd.c index 97a97a9ccc36..e10b76089048 100644 --- a/drivers/mtd/nand/raw/au1550nd.c +++ b/drivers/mtd/nand/raw/au1550nd.c @@ -134,16 +134,15 @@ static void au_write_buf16(struct nand_chip *this, const u_char *buf, int len)  /**   * au_read_buf16 -  read chip data into buffer - * @mtd:	MTD device structure + * @this:	NAND chip object   * @buf:	buffer to store date   * @len:	number of bytes to read   *   * read function for 16bit buswidth   */ -static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len) +static void au_read_buf16(struct nand_chip *this, u_char *buf, int len)  {  	int i; -	struct nand_chip *this = mtd_to_nand(mtd);  	u16 *p = (u16 *) buf;  	len >>= 1; diff --git a/drivers/mtd/nand/raw/denali_dt.c b/drivers/mtd/nand/raw/denali_dt.c index 4cce9ae33b8e..8b779a899dcf 100644 --- a/drivers/mtd/nand/raw/denali_dt.c +++ b/drivers/mtd/nand/raw/denali_dt.c @@ -126,10 +126,8 @@ static int denali_dt_probe(struct platform_device *pdev)  	denali->dev = dev;  	denali->irq = platform_get_irq(pdev, 0); -	if (denali->irq < 0) { -		dev_err(dev, "no irq defined\n"); +	if (denali->irq < 0)  		return denali->irq; -	}  	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "denali_reg");  	denali->reg = devm_ioremap_resource(dev, res); diff --git a/drivers/mtd/nand/raw/hisi504_nand.c b/drivers/mtd/nand/raw/hisi504_nand.c index 6a4626a8bf95..0b48be54ba6f 100644 --- a/drivers/mtd/nand/raw/hisi504_nand.c +++ b/drivers/mtd/nand/raw/hisi504_nand.c @@ -751,10 +751,8 @@ static int hisi_nfc_probe(struct platform_device *pdev)  	mtd  = nand_to_mtd(chip);  	irq = platform_get_irq(pdev, 0); -	if (irq < 0) { -		dev_err(dev, "no IRQ resource defined\n"); +	if (irq < 0)  		return -ENXIO; -	}  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  	host->iobase = devm_ioremap_resource(dev, res); diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c index 78b31f845c50..241b58b83240 100644 --- a/drivers/mtd/nand/raw/lpc32xx_mlc.c +++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c @@ -773,7 +773,6 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)  	host->irq = platform_get_irq(pdev, 0);  	if (host->irq < 0) { -		dev_err(&pdev->dev, "failed to get platform irq\n");  		res = -EINVAL;  		goto release_dma_chan;  	} diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index fc49e13d81ec..fb5abdcfb007 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -2862,10 +2862,8 @@ static int marvell_nfc_probe(struct platform_device *pdev)  		return PTR_ERR(nfc->regs);  	irq = platform_get_irq(pdev, 0); -	if (irq < 0) { -		dev_err(dev, "failed to retrieve irq\n"); +	if (irq < 0)  		return irq; -	}  	nfc->core_clk = devm_clk_get(&pdev->dev, "core"); diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c index 1b82b687e5a5..9f17b5b8efbf 100644 --- a/drivers/mtd/nand/raw/meson_nand.c +++ b/drivers/mtd/nand/raw/meson_nand.c @@ -1399,10 +1399,8 @@ static int meson_nfc_probe(struct platform_device *pdev)  	}  	irq = platform_get_irq(pdev, 0); -	if (irq < 0) { -		dev_err(dev, "no NFC IRQ resource\n"); +	if (irq < 0)  		return -EINVAL; -	}  	ret = meson_nfc_clk_init(nfc);  	if (ret) { diff --git a/drivers/mtd/nand/raw/mtk_ecc.c b/drivers/mtd/nand/raw/mtk_ecc.c index 74595b644b7c..75f1fa3d4d35 100644 --- a/drivers/mtd/nand/raw/mtk_ecc.c +++ b/drivers/mtd/nand/raw/mtk_ecc.c @@ -527,10 +527,8 @@ static int mtk_ecc_probe(struct platform_device *pdev)  	}  	irq = platform_get_irq(pdev, 0); -	if (irq < 0) { -		dev_err(dev, "failed to get irq: %d\n", irq); +	if (irq < 0)  		return irq; -	}  	ret = dma_set_mask(dev, DMA_BIT_MASK(32));  	if (ret) { diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c index 373d47d1ba4c..b8305e39ab51 100644 --- a/drivers/mtd/nand/raw/mtk_nand.c +++ b/drivers/mtd/nand/raw/mtk_nand.c @@ -1540,7 +1540,6 @@ static int mtk_nfc_probe(struct platform_device *pdev)  	irq = platform_get_irq(pdev, 0);  	if (irq < 0) { -		dev_err(dev, "no nfi irq resource\n");  		ret = -EINVAL;  		goto clk_disable;  	} diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c index 6ec65f48501c..ad77c112a78a 100644 --- a/drivers/mtd/nand/raw/omap2.c +++ b/drivers/mtd/nand/raw/omap2.c @@ -1967,10 +1967,8 @@ static int omap_nand_attach_chip(struct nand_chip *chip)  	case NAND_OMAP_PREFETCH_IRQ:  		info->gpmc_irq_fifo = platform_get_irq(info->pdev, 0); -		if (info->gpmc_irq_fifo <= 0) { -			dev_err(dev, "Error getting fifo IRQ\n"); +		if (info->gpmc_irq_fifo <= 0)  			return -ENODEV; -		}  		err = devm_request_irq(dev, info->gpmc_irq_fifo,  				       omap_nand_irq, IRQF_SHARED,  				       "gpmc-nand-fifo", info); @@ -1982,10 +1980,8 @@ static int omap_nand_attach_chip(struct nand_chip *chip)  		}  		info->gpmc_irq_count = platform_get_irq(info->pdev, 1); -		if (info->gpmc_irq_count <= 0) { -			dev_err(dev, "Error getting IRQ count\n"); +		if (info->gpmc_irq_count <= 0)  			return -ENODEV; -		}  		err = devm_request_irq(dev, info->gpmc_irq_count,  				       omap_nand_irq, IRQF_SHARED,  				       "gpmc-nand-count", info); diff --git a/drivers/mtd/nand/raw/sh_flctl.c b/drivers/mtd/nand/raw/sh_flctl.c index e509c93737c4..058e99d0cbcf 100644 --- a/drivers/mtd/nand/raw/sh_flctl.c +++ b/drivers/mtd/nand/raw/sh_flctl.c @@ -1129,10 +1129,8 @@ static int flctl_probe(struct platform_device *pdev)  	flctl->fifo = res->start + 0x24; /* FLDTFIFO */  	irq = platform_get_irq(pdev, 0); -	if (irq < 0) { -		dev_err(&pdev->dev, "failed to get flste irq data: %d\n", irq); +	if (irq < 0)  		return irq; -	}  	ret = devm_request_irq(&pdev->dev, irq, flctl_handle_flste, IRQF_SHARED,  			       "flste", flctl); diff --git a/drivers/mtd/nand/raw/stm32_fmc2_nand.c b/drivers/mtd/nand/raw/stm32_fmc2_nand.c index 8cc852dc7d54..9e63800f768a 100644 --- a/drivers/mtd/nand/raw/stm32_fmc2_nand.c +++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c @@ -1880,11 +1880,8 @@ static int stm32_fmc2_probe(struct platform_device *pdev)  	}  	irq = platform_get_irq(pdev, 0); -	if (irq < 0) { -		if (irq != -EPROBE_DEFER) -			dev_err(dev, "IRQ error missing or invalid\n"); +	if (irq < 0)  		return irq; -	}  	ret = devm_request_irq(dev, irq, stm32_fmc2_irq, 0,  			       dev_name(dev), fmc2); diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 89773293c64d..37a4ac0dd85b 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -2071,10 +2071,8 @@ static int sunxi_nfc_probe(struct platform_device *pdev)  		return PTR_ERR(nfc->regs);  	irq = platform_get_irq(pdev, 0); -	if (irq < 0) { -		dev_err(dev, "failed to retrieve irq\n"); +	if (irq < 0)  		return irq; -	}  	nfc->ahb_clk = devm_clk_get(dev, "ahb");  	if (IS_ERR(nfc->ahb_clk)) { diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c index 009c1da8574c..2b7cabbb680c 100644 --- a/drivers/mtd/spi-nor/aspeed-smc.c +++ b/drivers/mtd/spi-nor/aspeed-smc.c @@ -320,7 +320,8 @@ static void aspeed_smc_unprep(struct spi_nor *nor, enum spi_nor_ops ops)  	mutex_unlock(&chip->controller->mutex);  } -static int aspeed_smc_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +static int aspeed_smc_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, +			       size_t len)  {  	struct aspeed_smc_chip *chip = nor->priv; @@ -331,8 +332,8 @@ static int aspeed_smc_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)  	return 0;  } -static int aspeed_smc_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, -				int len) +static int aspeed_smc_write_reg(struct spi_nor *nor, u8 opcode, const u8 *buf, +				size_t len)  {  	struct aspeed_smc_chip *chip = nor->priv; @@ -746,6 +747,15 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)  	return 0;  } +static const struct spi_nor_controller_ops aspeed_smc_controller_ops = { +	.prepare = aspeed_smc_prep, +	.unprepare = aspeed_smc_unprep, +	.read_reg = aspeed_smc_read_reg, +	.write_reg = aspeed_smc_write_reg, +	.read = aspeed_smc_read_user, +	.write = aspeed_smc_write_user, +}; +  static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,  				  struct device_node *np, struct resource *r)  { @@ -805,12 +815,7 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,  		nor->dev = dev;  		nor->priv = chip;  		spi_nor_set_flash_node(nor, child); -		nor->read = aspeed_smc_read_user; -		nor->write = aspeed_smc_write_user; -		nor->read_reg = aspeed_smc_read_reg; -		nor->write_reg = aspeed_smc_write_reg; -		nor->prepare = aspeed_smc_prep; -		nor->unprepare = aspeed_smc_unprep; +		nor->controller_ops = &aspeed_smc_controller_ops;  		ret = aspeed_smc_chip_setup_init(chip, r);  		if (ret) diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c index 7bef63947b29..06f997247d0f 100644 --- a/drivers/mtd/spi-nor/cadence-quadspi.c +++ b/drivers/mtd/spi-nor/cadence-quadspi.c @@ -285,7 +285,7 @@ static irqreturn_t cqspi_irq_handler(int this_irq, void *dev)  	return IRQ_HANDLED;  } -static unsigned int cqspi_calc_rdreg(struct spi_nor *nor, const u8 opcode) +static unsigned int cqspi_calc_rdreg(struct spi_nor *nor)  {  	struct cqspi_flash_pdata *f_pdata = nor->priv;  	u32 rdreg = 0; @@ -354,27 +354,27 @@ static int cqspi_exec_flash_cmd(struct cqspi_st *cqspi, unsigned int reg)  	return cqspi_wait_idle(cqspi);  } -static int cqspi_command_read(struct spi_nor *nor, -			      const u8 *txbuf, const unsigned n_tx, -			      u8 *rxbuf, const unsigned n_rx) +static int cqspi_command_read(struct spi_nor *nor, u8 opcode, +			      u8 *rxbuf, size_t n_rx)  {  	struct cqspi_flash_pdata *f_pdata = nor->priv;  	struct cqspi_st *cqspi = f_pdata->cqspi;  	void __iomem *reg_base = cqspi->iobase;  	unsigned int rdreg;  	unsigned int reg; -	unsigned int read_len; +	size_t read_len;  	int status;  	if (!n_rx || n_rx > CQSPI_STIG_DATA_LEN_MAX || !rxbuf) { -		dev_err(nor->dev, "Invalid input argument, len %d rxbuf 0x%p\n", +		dev_err(nor->dev, +			"Invalid input argument, len %zu rxbuf 0x%p\n",  			n_rx, rxbuf);  		return -EINVAL;  	} -	reg = txbuf[0] << CQSPI_REG_CMDCTRL_OPCODE_LSB; +	reg = opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB; -	rdreg = cqspi_calc_rdreg(nor, txbuf[0]); +	rdreg = cqspi_calc_rdreg(nor);  	writel(rdreg, reg_base + CQSPI_REG_RD_INSTR);  	reg |= (0x1 << CQSPI_REG_CMDCTRL_RD_EN_LSB); @@ -404,19 +404,19 @@ static int cqspi_command_read(struct spi_nor *nor,  }  static int cqspi_command_write(struct spi_nor *nor, const u8 opcode, -			       const u8 *txbuf, const unsigned n_tx) +			       const u8 *txbuf, size_t n_tx)  {  	struct cqspi_flash_pdata *f_pdata = nor->priv;  	struct cqspi_st *cqspi = f_pdata->cqspi;  	void __iomem *reg_base = cqspi->iobase;  	unsigned int reg;  	unsigned int data; -	u32 write_len; +	size_t write_len;  	int ret;  	if (n_tx > CQSPI_STIG_DATA_LEN_MAX || (n_tx && !txbuf)) {  		dev_err(nor->dev, -			"Invalid input argument, cmdlen %d txbuf 0x%p\n", +			"Invalid input argument, cmdlen %zu txbuf 0x%p\n",  			n_tx, txbuf);  		return -EINVAL;  	} @@ -470,7 +470,7 @@ static int cqspi_read_setup(struct spi_nor *nor)  	unsigned int reg;  	reg = nor->read_opcode << CQSPI_REG_RD_INSTR_OPCODE_LSB; -	reg |= cqspi_calc_rdreg(nor, nor->read_opcode); +	reg |= cqspi_calc_rdreg(nor);  	/* Setup dummy clock cycles */  	dummy_clk = nor->read_dummy; @@ -603,7 +603,7 @@ static int cqspi_write_setup(struct spi_nor *nor)  	/* Set opcode. */  	reg = nor->program_opcode << CQSPI_REG_WR_INSTR_OPCODE_LSB;  	writel(reg, reg_base + CQSPI_REG_WR_INSTR); -	reg = cqspi_calc_rdreg(nor, nor->program_opcode); +	reg = cqspi_calc_rdreg(nor);  	writel(reg, reg_base + CQSPI_REG_RD_INSTR);  	reg = readl(reg_base + CQSPI_REG_SIZE); @@ -1050,7 +1050,7 @@ static int cqspi_erase(struct spi_nor *nor, loff_t offs)  		return ret;  	/* Send write enable, then erase commands. */ -	ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0); +	ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WREN, NULL, 0);  	if (ret)  		return ret; @@ -1080,18 +1080,19 @@ static void cqspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops)  	mutex_unlock(&cqspi->bus_mutex);  } -static int cqspi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +static int cqspi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, size_t len)  {  	int ret;  	ret = cqspi_set_protocol(nor, 0);  	if (!ret) -		ret = cqspi_command_read(nor, &opcode, 1, buf, len); +		ret = cqspi_command_read(nor, opcode, buf, len);  	return ret;  } -static int cqspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +static int cqspi_write_reg(struct spi_nor *nor, u8 opcode, const u8 *buf, +			   size_t len)  {  	int ret; @@ -1216,6 +1217,16 @@ static void cqspi_request_mmap_dma(struct cqspi_st *cqspi)  	init_completion(&cqspi->rx_dma_complete);  } +static const struct spi_nor_controller_ops cqspi_controller_ops = { +	.prepare = cqspi_prep, +	.unprepare = cqspi_unprep, +	.read_reg = cqspi_read_reg, +	.write_reg = cqspi_write_reg, +	.read = cqspi_read, +	.write = cqspi_write, +	.erase = cqspi_erase, +}; +  static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)  {  	struct platform_device *pdev = cqspi->pdev; @@ -1265,14 +1276,7 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)  		nor->dev = dev;  		spi_nor_set_flash_node(nor, np);  		nor->priv = f_pdata; - -		nor->read_reg = cqspi_read_reg; -		nor->write_reg = cqspi_write_reg; -		nor->read = cqspi_read; -		nor->write = cqspi_write; -		nor->erase = cqspi_erase; -		nor->prepare = cqspi_prep; -		nor->unprepare = cqspi_unprep; +		nor->controller_ops = &cqspi_controller_ops;  		mtd->name = devm_kasprintf(dev, GFP_KERNEL, "%s.%d",  					   dev_name(dev), cs); @@ -1366,10 +1370,8 @@ static int cqspi_probe(struct platform_device *pdev)  	/* Obtain IRQ line. */  	irq = platform_get_irq(pdev, 0); -	if (irq < 0) { -		dev_err(dev, "Cannot obtain IRQ.\n"); +	if (irq < 0)  		return -ENXIO; -	}  	pm_runtime_enable(dev);  	ret = pm_runtime_get_sync(dev); diff --git a/drivers/mtd/spi-nor/hisi-sfc.c b/drivers/mtd/spi-nor/hisi-sfc.c index 6dac9dd8bf42..a1258216f89d 100644 --- a/drivers/mtd/spi-nor/hisi-sfc.c +++ b/drivers/mtd/spi-nor/hisi-sfc.c @@ -177,7 +177,7 @@ static void hisi_spi_nor_unprep(struct spi_nor *nor, enum spi_nor_ops ops)  }  static int hisi_spi_nor_op_reg(struct spi_nor *nor, -				u8 opcode, int len, u8 optype) +				u8 opcode, size_t len, u8 optype)  {  	struct hifmc_priv *priv = nor->priv;  	struct hifmc_host *host = priv->host; @@ -200,7 +200,7 @@ static int hisi_spi_nor_op_reg(struct spi_nor *nor,  }  static int hisi_spi_nor_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, -		int len) +				 size_t len)  {  	struct hifmc_priv *priv = nor->priv;  	struct hifmc_host *host = priv->host; @@ -215,7 +215,7 @@ static int hisi_spi_nor_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf,  }  static int hisi_spi_nor_write_reg(struct spi_nor *nor, u8 opcode, -				u8 *buf, int len) +				  const u8 *buf, size_t len)  {  	struct hifmc_priv *priv = nor->priv;  	struct hifmc_host *host = priv->host; @@ -311,6 +311,15 @@ static ssize_t hisi_spi_nor_write(struct spi_nor *nor, loff_t to,  	return len;  } +static const struct spi_nor_controller_ops hisi_controller_ops = { +	.prepare = hisi_spi_nor_prep, +	.unprepare = hisi_spi_nor_unprep, +	.read_reg = hisi_spi_nor_read_reg, +	.write_reg = hisi_spi_nor_write_reg, +	.read = hisi_spi_nor_read, +	.write = hisi_spi_nor_write, +}; +  /**   * Get spi flash device information and register it as a mtd device.   */ @@ -357,14 +366,8 @@ static int hisi_spi_nor_register(struct device_node *np,  	}  	priv->host = host;  	nor->priv = priv; +	nor->controller_ops = &hisi_controller_ops; -	nor->prepare = hisi_spi_nor_prep; -	nor->unprepare = hisi_spi_nor_unprep; -	nor->read_reg = hisi_spi_nor_read_reg; -	nor->write_reg = hisi_spi_nor_write_reg; -	nor->read = hisi_spi_nor_read; -	nor->write = hisi_spi_nor_write; -	nor->erase = NULL;  	ret = spi_nor_scan(nor, NULL, &hwcaps);  	if (ret)  		return ret; diff --git a/drivers/mtd/spi-nor/intel-spi-pci.c b/drivers/mtd/spi-nor/intel-spi-pci.c index 3cda8e7a68f8..3d8987baea2a 100644 --- a/drivers/mtd/spi-nor/intel-spi-pci.c +++ b/drivers/mtd/spi-nor/intel-spi-pci.c @@ -20,6 +20,10 @@ static const struct intel_spi_boardinfo bxt_info = {  	.type = INTEL_SPI_BXT,  }; +static const struct intel_spi_boardinfo cnl_info = { +	.type = INTEL_SPI_CNL, +}; +  static int intel_spi_pci_probe(struct pci_dev *pdev,  			       const struct pci_device_id *id)  { @@ -61,6 +65,7 @@ static void intel_spi_pci_remove(struct pci_dev *pdev)  static const struct pci_device_id intel_spi_pci_ids[] = {  	{ PCI_VDEVICE(INTEL, 0x02a4), (unsigned long)&bxt_info }, +	{ PCI_VDEVICE(INTEL, 0x06a4), (unsigned long)&bxt_info },  	{ PCI_VDEVICE(INTEL, 0x18e0), (unsigned long)&bxt_info },  	{ PCI_VDEVICE(INTEL, 0x19e0), (unsigned long)&bxt_info },  	{ PCI_VDEVICE(INTEL, 0x34a4), (unsigned long)&bxt_info }, @@ -68,6 +73,7 @@ static const struct pci_device_id intel_spi_pci_ids[] = {  	{ PCI_VDEVICE(INTEL, 0xa0a4), (unsigned long)&bxt_info },  	{ PCI_VDEVICE(INTEL, 0xa1a4), (unsigned long)&bxt_info },  	{ PCI_VDEVICE(INTEL, 0xa224), (unsigned long)&bxt_info }, +	{ PCI_VDEVICE(INTEL, 0xa324), (unsigned long)&cnl_info },  	{ },  };  MODULE_DEVICE_TABLE(pci, intel_spi_pci_ids); diff --git a/drivers/mtd/spi-nor/intel-spi.c b/drivers/mtd/spi-nor/intel-spi.c index 43e55a2e9b27..61d2a0ad2131 100644 --- a/drivers/mtd/spi-nor/intel-spi.c +++ b/drivers/mtd/spi-nor/intel-spi.c @@ -108,6 +108,10 @@  #define BXT_FREG_NUM			12  #define BXT_PR_NUM			6 +#define CNL_PR				0x84 +#define CNL_FREG_NUM			6 +#define CNL_PR_NUM			5 +  #define LVSCC				0xc4  #define UVSCC				0xc8  #define ERASE_OPCODE_SHIFT		8 @@ -187,12 +191,16 @@ static void intel_spi_dump_regs(struct intel_spi *ispi)  		dev_dbg(ispi->dev, "PR(%d)=0x%08x\n", i,  			readl(ispi->pregs + PR(i))); -	value = readl(ispi->sregs + SSFSTS_CTL); -	dev_dbg(ispi->dev, "SSFSTS_CTL=0x%08x\n", value); -	dev_dbg(ispi->dev, "PREOP_OPTYPE=0x%08x\n", -		readl(ispi->sregs + PREOP_OPTYPE)); -	dev_dbg(ispi->dev, "OPMENU0=0x%08x\n", readl(ispi->sregs + OPMENU0)); -	dev_dbg(ispi->dev, "OPMENU1=0x%08x\n", readl(ispi->sregs + OPMENU1)); +	if (ispi->sregs) { +		value = readl(ispi->sregs + SSFSTS_CTL); +		dev_dbg(ispi->dev, "SSFSTS_CTL=0x%08x\n", value); +		dev_dbg(ispi->dev, "PREOP_OPTYPE=0x%08x\n", +			readl(ispi->sregs + PREOP_OPTYPE)); +		dev_dbg(ispi->dev, "OPMENU0=0x%08x\n", +			readl(ispi->sregs + OPMENU0)); +		dev_dbg(ispi->dev, "OPMENU1=0x%08x\n", +			readl(ispi->sregs + OPMENU1)); +	}  	if (ispi->info->type == INTEL_SPI_BYT)  		dev_dbg(ispi->dev, "BCR=0x%08x\n", readl(ispi->base + BYT_BCR)); @@ -340,6 +348,13 @@ static int intel_spi_init(struct intel_spi *ispi)  		ispi->erase_64k = true;  		break; +	case INTEL_SPI_CNL: +		ispi->sregs = NULL; +		ispi->pregs = ispi->base + CNL_PR; +		ispi->nregions = CNL_FREG_NUM; +		ispi->pr_num = CNL_PR_NUM; +		break; +  	default:  		return -EINVAL;  	} @@ -367,6 +382,11 @@ static int intel_spi_init(struct intel_spi *ispi)  		    !(uvscc & ERASE_64K_OPCODE_MASK))  			ispi->erase_64k = false; +	if (ispi->sregs == NULL && (ispi->swseq_reg || ispi->swseq_erase)) { +		dev_err(ispi->dev, "software sequencer not supported, but required\n"); +		return -EINVAL; +	} +  	/*  	 * Some controllers can only do basic operations using hardware  	 * sequencer. All other operations are supposed to be carried out @@ -383,7 +403,7 @@ static int intel_spi_init(struct intel_spi *ispi)  	val = readl(ispi->base + HSFSTS_CTL);  	ispi->locked = !!(val & HSFSTS_CTL_FLOCKDN); -	if (ispi->locked) { +	if (ispi->locked && ispi->sregs) {  		/*  		 * BIOS programs allowed opcodes and then locks down the  		 * register. So read back what opcodes it decided to support. @@ -426,7 +446,7 @@ static int intel_spi_opcode_index(struct intel_spi *ispi, u8 opcode, int optype)  	return 0;  } -static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, int len) +static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, size_t len)  {  	u32 val, status;  	int ret; @@ -469,7 +489,7 @@ static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, int len)  	return 0;  } -static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, int len, +static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, size_t len,  			      int optype)  {  	u32 val = 0, status; @@ -535,7 +555,8 @@ static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, int len,  	return 0;  } -static int intel_spi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +static int intel_spi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, +			      size_t len)  {  	struct intel_spi *ispi = nor->priv;  	int ret; @@ -555,7 +576,8 @@ static int intel_spi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)  	return intel_spi_read_block(ispi, buf, len);  } -static int intel_spi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +static int intel_spi_write_reg(struct spi_nor *nor, u8 opcode, const u8 *buf, +			       size_t len)  {  	struct intel_spi *ispi = nor->priv;  	int ret; @@ -864,6 +886,14 @@ static void intel_spi_fill_partition(struct intel_spi *ispi,  	}  } +static const struct spi_nor_controller_ops intel_spi_controller_ops = { +	.read_reg = intel_spi_read_reg, +	.write_reg = intel_spi_write_reg, +	.read = intel_spi_read, +	.write = intel_spi_write, +	.erase = intel_spi_erase, +}; +  struct intel_spi *intel_spi_probe(struct device *dev,  	struct resource *mem, const struct intel_spi_boardinfo *info)  { @@ -897,11 +927,7 @@ struct intel_spi *intel_spi_probe(struct device *dev,  	ispi->nor.dev = ispi->dev;  	ispi->nor.priv = ispi; -	ispi->nor.read_reg = intel_spi_read_reg; -	ispi->nor.write_reg = intel_spi_write_reg; -	ispi->nor.read = intel_spi_read; -	ispi->nor.write = intel_spi_write; -	ispi->nor.erase = intel_spi_erase; +	ispi->nor.controller_ops = &intel_spi_controller_ops;  	ret = spi_nor_scan(&ispi->nor, NULL, &hwcaps);  	if (ret) { diff --git a/drivers/mtd/spi-nor/mtk-quadspi.c b/drivers/mtd/spi-nor/mtk-quadspi.c index 34db01ab6cab..b1691680d174 100644 --- a/drivers/mtd/spi-nor/mtk-quadspi.c +++ b/drivers/mtd/spi-nor/mtk-quadspi.c @@ -151,9 +151,9 @@ static int mtk_nor_execute_cmd(struct mtk_nor *mtk_nor, u8 cmdval)  }  static int mtk_nor_do_tx_rx(struct mtk_nor *mtk_nor, u8 op, -			    u8 *tx, int txlen, u8 *rx, int rxlen) +			    const u8 *tx, size_t txlen, u8 *rx, size_t rxlen)  { -	int len = 1 + txlen + rxlen; +	size_t len = 1 + txlen + rxlen;  	int i, ret, idx;  	if (len > MTK_NOR_MAX_SHIFT) @@ -193,7 +193,7 @@ static int mtk_nor_do_tx_rx(struct mtk_nor *mtk_nor, u8 op,  }  /* Do a WRSR (Write Status Register) command */ -static int mtk_nor_wr_sr(struct mtk_nor *mtk_nor, u8 sr) +static int mtk_nor_wr_sr(struct mtk_nor *mtk_nor, const u8 sr)  {  	writeb(sr, mtk_nor->base + MTK_NOR_PRGDATA5_REG);  	writeb(8, mtk_nor->base + MTK_NOR_CNT_REG); @@ -354,7 +354,7 @@ static ssize_t mtk_nor_write(struct spi_nor *nor, loff_t to, size_t len,  	return len;  } -static int mtk_nor_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +static int mtk_nor_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, size_t len)  {  	int ret;  	struct mtk_nor *mtk_nor = nor->priv; @@ -376,8 +376,8 @@ static int mtk_nor_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)  	return ret;  } -static int mtk_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, -			     int len) +static int mtk_nor_write_reg(struct spi_nor *nor, u8 opcode, const u8 *buf, +			     size_t len)  {  	int ret;  	struct mtk_nor *mtk_nor = nor->priv; @@ -419,6 +419,13 @@ static int mtk_nor_enable_clk(struct mtk_nor *mtk_nor)  	return 0;  } +static const struct spi_nor_controller_ops mtk_controller_ops = { +	.read_reg = mtk_nor_read_reg, +	.write_reg = mtk_nor_write_reg, +	.read = mtk_nor_read, +	.write = mtk_nor_write, +}; +  static int mtk_nor_init(struct mtk_nor *mtk_nor,  			struct device_node *flash_node)  { @@ -438,12 +445,8 @@ static int mtk_nor_init(struct mtk_nor *mtk_nor,  	nor->dev = mtk_nor->dev;  	nor->priv = mtk_nor;  	spi_nor_set_flash_node(nor, flash_node); +	nor->controller_ops = &mtk_controller_ops; -	/* fill the hooks to spi nor */ -	nor->read = mtk_nor_read; -	nor->read_reg = mtk_nor_read_reg; -	nor->write = mtk_nor_write; -	nor->write_reg = mtk_nor_write_reg;  	nor->mtd.name = "mtk_nor";  	/* initialized with NULL */  	ret = spi_nor_scan(nor, NULL, &hwcaps); diff --git a/drivers/mtd/spi-nor/nxp-spifi.c b/drivers/mtd/spi-nor/nxp-spifi.c index 4a871587392b..9a5b1a7c636a 100644 --- a/drivers/mtd/spi-nor/nxp-spifi.c +++ b/drivers/mtd/spi-nor/nxp-spifi.c @@ -123,7 +123,8 @@ static int nxp_spifi_set_memory_mode_on(struct nxp_spifi *spifi)  	return ret;  } -static int nxp_spifi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +static int nxp_spifi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, +			      size_t len)  {  	struct nxp_spifi *spifi = nor->priv;  	u32 cmd; @@ -145,7 +146,8 @@ static int nxp_spifi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)  	return nxp_spifi_wait_for_cmd(spifi);  } -static int nxp_spifi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +static int nxp_spifi_write_reg(struct spi_nor *nor, u8 opcode, const u8 *buf, +			       size_t len)  {  	struct nxp_spifi *spifi = nor->priv;  	u32 cmd; @@ -263,9 +265,18 @@ static int nxp_spifi_setup_memory_cmd(struct nxp_spifi *spifi)  static void nxp_spifi_dummy_id_read(struct spi_nor *nor)  {  	u8 id[SPI_NOR_MAX_ID_LEN]; -	nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN); +	nor->controller_ops->read_reg(nor, SPINOR_OP_RDID, id, +				      SPI_NOR_MAX_ID_LEN);  } +static const struct spi_nor_controller_ops nxp_spifi_controller_ops = { +	.read_reg  = nxp_spifi_read_reg, +	.write_reg = nxp_spifi_write_reg, +	.read  = nxp_spifi_read, +	.write = nxp_spifi_write, +	.erase = nxp_spifi_erase, +}; +  static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,  				 struct device_node *np)  { @@ -332,11 +343,7 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,  	spifi->nor.dev   = spifi->dev;  	spi_nor_set_flash_node(&spifi->nor, np);  	spifi->nor.priv  = spifi; -	spifi->nor.read  = nxp_spifi_read; -	spifi->nor.write = nxp_spifi_write; -	spifi->nor.erase = nxp_spifi_erase; -	spifi->nor.read_reg  = nxp_spifi_read_reg; -	spifi->nor.write_reg = nxp_spifi_write_reg; +	spifi->nor.controller_ops = &nxp_spifi_controller_ops;  	/*  	 * The first read on a hard reset isn't reliable so do a diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 1d8621d43160..f4afe123e9dc 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -338,7 +338,7 @@ static ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len,  	if (nor->spimem)  		return spi_nor_spimem_read_data(nor, from, len, buf); -	return nor->read(nor, from, len, buf); +	return nor->controller_ops->read(nor, from, len, buf);  }  /** @@ -385,239 +385,172 @@ static ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len,  	if (nor->spimem)  		return spi_nor_spimem_write_data(nor, to, len, buf); -	return nor->write(nor, to, len, buf); +	return nor->controller_ops->write(nor, to, len, buf);  } -/* - * Read the status register, returning its value in the location - * Return the status register value. - * Returns negative if error occurred. +/** + * spi_nor_write_enable() - Set write enable latch with Write Enable command. + * @nor:	pointer to 'struct spi_nor'. + * + * Return: 0 on success, -errno otherwise.   */ -static int read_sr(struct spi_nor *nor) +static int spi_nor_write_enable(struct spi_nor *nor)  {  	int ret;  	if (nor->spimem) {  		struct spi_mem_op op = -			SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 1), +			SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREN, 1),  				   SPI_MEM_OP_NO_ADDR,  				   SPI_MEM_OP_NO_DUMMY, -				   SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1)); +				   SPI_MEM_OP_NO_DATA);  		ret = spi_mem_exec_op(nor->spimem, &op);  	} else { -		ret = nor->read_reg(nor, SPINOR_OP_RDSR, nor->bouncebuf, 1); +		ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WREN, +						     NULL, 0);  	} -	if (ret < 0) { -		pr_err("error %d reading SR\n", (int) ret); -		return ret; -	} +	if (ret) +		dev_dbg(nor->dev, "error %d on Write Enable\n", ret); -	return nor->bouncebuf[0]; +	return ret;  } -/* - * Read the flag status register, returning its value in the location - * Return the status register value. - * Returns negative if error occurred. +/** + * spi_nor_write_disable() - Send Write Disable instruction to the chip. + * @nor:	pointer to 'struct spi_nor'. + * + * Return: 0 on success, -errno otherwise.   */ -static int read_fsr(struct spi_nor *nor) +static int spi_nor_write_disable(struct spi_nor *nor)  {  	int ret;  	if (nor->spimem) {  		struct spi_mem_op op = -			SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 1), +			SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRDI, 1),  				   SPI_MEM_OP_NO_ADDR,  				   SPI_MEM_OP_NO_DUMMY, -				   SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1)); +				   SPI_MEM_OP_NO_DATA);  		ret = spi_mem_exec_op(nor->spimem, &op);  	} else { -		ret = nor->read_reg(nor, SPINOR_OP_RDFSR, nor->bouncebuf, 1); +		ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WRDI, +						     NULL, 0);  	} -	if (ret < 0) { -		pr_err("error %d reading FSR\n", ret); -		return ret; -	} +	if (ret) +		dev_dbg(nor->dev, "error %d on Write Disable\n", ret); -	return nor->bouncebuf[0]; +	return ret;  } -/* - * Read configuration register, returning its value in the - * location. Return the configuration register value. - * Returns negative if error occurred. +/** + * spi_nor_read_sr() - Read the Status Register. + * @nor:	pointer to 'struct spi_nor'. + * @sr:		pointer to a DMA-able buffer where the value of the + *              Status Register will be written. + * + * Return: 0 on success, -errno otherwise.   */ -static int read_cr(struct spi_nor *nor) +static int spi_nor_read_sr(struct spi_nor *nor, u8 *sr)  {  	int ret;  	if (nor->spimem) {  		struct spi_mem_op op = -			SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDCR, 1), +			SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 1),  				   SPI_MEM_OP_NO_ADDR,  				   SPI_MEM_OP_NO_DUMMY, -				   SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1)); +				   SPI_MEM_OP_DATA_IN(1, sr, 1));  		ret = spi_mem_exec_op(nor->spimem, &op);  	} else { -		ret = nor->read_reg(nor, SPINOR_OP_RDCR, nor->bouncebuf, 1); +		ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDSR, +						    sr, 1);  	} -	if (ret < 0) { -		dev_err(nor->dev, "error %d reading CR\n", ret); -		return ret; -	} +	if (ret) +		dev_dbg(nor->dev, "error %d reading SR\n", ret); -	return nor->bouncebuf[0]; +	return ret;  } -/* - * Write status register 1 byte - * Returns negative if error occurred. +/** + * spi_nor_read_fsr() - Read the Flag Status Register. + * @nor:	pointer to 'struct spi_nor' + * @fsr:	pointer to a DMA-able buffer where the value of the + *              Flag Status Register will be written. + * + * Return: 0 on success, -errno otherwise.   */ -static int write_sr(struct spi_nor *nor, u8 val) +static int spi_nor_read_fsr(struct spi_nor *nor, u8 *fsr)  { -	nor->bouncebuf[0] = val; -	if (nor->spimem) { -		struct spi_mem_op op = -			SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 1), -				   SPI_MEM_OP_NO_ADDR, -				   SPI_MEM_OP_NO_DUMMY, -				   SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1)); - -		return spi_mem_exec_op(nor->spimem, &op); -	} - -	return nor->write_reg(nor, SPINOR_OP_WRSR, nor->bouncebuf, 1); -} +	int ret; -/* - * Set write enable latch with Write Enable command. - * Returns negative if error occurred. - */ -static int write_enable(struct spi_nor *nor) -{  	if (nor->spimem) {  		struct spi_mem_op op = -			SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREN, 1), +			SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 1),  				   SPI_MEM_OP_NO_ADDR,  				   SPI_MEM_OP_NO_DUMMY, -				   SPI_MEM_OP_NO_DATA); +				   SPI_MEM_OP_DATA_IN(1, fsr, 1)); -		return spi_mem_exec_op(nor->spimem, &op); +		ret = spi_mem_exec_op(nor->spimem, &op); +	} else { +		ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDFSR, +						    fsr, 1);  	} -	return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0); +	if (ret) +		dev_dbg(nor->dev, "error %d reading FSR\n", ret); + +	return ret;  } -/* - * Send write disable instruction to the chip. +/** + * spi_nor_read_cr() - Read the Configuration Register using the + * SPINOR_OP_RDCR (35h) command. + * @nor:	pointer to 'struct spi_nor' + * @cr:		pointer to a DMA-able buffer where the value of the + *              Configuration Register will be written. + * + * Return: 0 on success, -errno otherwise.   */ -static int write_disable(struct spi_nor *nor) +static int spi_nor_read_cr(struct spi_nor *nor, u8 *cr)  { +	int ret; +  	if (nor->spimem) {  		struct spi_mem_op op = -			SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRDI, 1), +			SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDCR, 1),  				   SPI_MEM_OP_NO_ADDR,  				   SPI_MEM_OP_NO_DUMMY, -				   SPI_MEM_OP_NO_DATA); +				   SPI_MEM_OP_DATA_IN(1, cr, 1)); -		return spi_mem_exec_op(nor->spimem, &op); +		ret = spi_mem_exec_op(nor->spimem, &op); +	} else { +		ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDCR, cr, 1);  	} -	return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0); -} - -static struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd) -{ -	return mtd->priv; -} - - -static u8 spi_nor_convert_opcode(u8 opcode, const u8 table[][2], size_t size) -{ -	size_t i; - -	for (i = 0; i < size; i++) -		if (table[i][0] == opcode) -			return table[i][1]; - -	/* No conversion found, keep input op code. */ -	return opcode; -} - -static u8 spi_nor_convert_3to4_read(u8 opcode) -{ -	static const u8 spi_nor_3to4_read[][2] = { -		{ SPINOR_OP_READ,	SPINOR_OP_READ_4B }, -		{ SPINOR_OP_READ_FAST,	SPINOR_OP_READ_FAST_4B }, -		{ SPINOR_OP_READ_1_1_2,	SPINOR_OP_READ_1_1_2_4B }, -		{ SPINOR_OP_READ_1_2_2,	SPINOR_OP_READ_1_2_2_4B }, -		{ SPINOR_OP_READ_1_1_4,	SPINOR_OP_READ_1_1_4_4B }, -		{ SPINOR_OP_READ_1_4_4,	SPINOR_OP_READ_1_4_4_4B }, -		{ SPINOR_OP_READ_1_1_8,	SPINOR_OP_READ_1_1_8_4B }, -		{ SPINOR_OP_READ_1_8_8,	SPINOR_OP_READ_1_8_8_4B }, - -		{ SPINOR_OP_READ_1_1_1_DTR,	SPINOR_OP_READ_1_1_1_DTR_4B }, -		{ SPINOR_OP_READ_1_2_2_DTR,	SPINOR_OP_READ_1_2_2_DTR_4B }, -		{ SPINOR_OP_READ_1_4_4_DTR,	SPINOR_OP_READ_1_4_4_DTR_4B }, -	}; - -	return spi_nor_convert_opcode(opcode, spi_nor_3to4_read, -				      ARRAY_SIZE(spi_nor_3to4_read)); -} - -static u8 spi_nor_convert_3to4_program(u8 opcode) -{ -	static const u8 spi_nor_3to4_program[][2] = { -		{ SPINOR_OP_PP,		SPINOR_OP_PP_4B }, -		{ SPINOR_OP_PP_1_1_4,	SPINOR_OP_PP_1_1_4_4B }, -		{ SPINOR_OP_PP_1_4_4,	SPINOR_OP_PP_1_4_4_4B }, -		{ SPINOR_OP_PP_1_1_8,	SPINOR_OP_PP_1_1_8_4B }, -		{ SPINOR_OP_PP_1_8_8,	SPINOR_OP_PP_1_8_8_4B }, -	}; - -	return spi_nor_convert_opcode(opcode, spi_nor_3to4_program, -				      ARRAY_SIZE(spi_nor_3to4_program)); -} - -static u8 spi_nor_convert_3to4_erase(u8 opcode) -{ -	static const u8 spi_nor_3to4_erase[][2] = { -		{ SPINOR_OP_BE_4K,	SPINOR_OP_BE_4K_4B }, -		{ SPINOR_OP_BE_32K,	SPINOR_OP_BE_32K_4B }, -		{ SPINOR_OP_SE,		SPINOR_OP_SE_4B }, -	}; - -	return spi_nor_convert_opcode(opcode, spi_nor_3to4_erase, -				      ARRAY_SIZE(spi_nor_3to4_erase)); -} - -static void spi_nor_set_4byte_opcodes(struct spi_nor *nor) -{ -	nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode); -	nor->program_opcode = spi_nor_convert_3to4_program(nor->program_opcode); -	nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode); - -	if (!spi_nor_has_uniform_erase(nor)) { -		struct spi_nor_erase_map *map = &nor->params.erase_map; -		struct spi_nor_erase_type *erase; -		int i; +	if (ret) +		dev_dbg(nor->dev, "error %d reading CR\n", ret); -		for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) { -			erase = &map->erase_type[i]; -			erase->opcode = -				spi_nor_convert_3to4_erase(erase->opcode); -		} -	} +	return ret;  } +/** + * macronix_set_4byte() - Set 4-byte address mode for Macronix flashes. + * @nor:	pointer to 'struct spi_nor'. + * @enable:	true to enter the 4-byte address mode, false to exit the 4-byte + *		address mode. + * + * Return: 0 on success, -errno otherwise. + */  static int macronix_set_4byte(struct spi_nor *nor, bool enable)  { +	int ret; +  	if (nor->spimem) {  		struct spi_mem_op op =  			SPI_MEM_OP(SPI_MEM_OP_CMD(enable ? @@ -628,26 +561,55 @@ static int macronix_set_4byte(struct spi_nor *nor, bool enable)  				  SPI_MEM_OP_NO_DUMMY,  				  SPI_MEM_OP_NO_DATA); -		return spi_mem_exec_op(nor->spimem, &op); +		ret = spi_mem_exec_op(nor->spimem, &op); +	} else { +		ret = nor->controller_ops->write_reg(nor, +						     enable ? SPINOR_OP_EN4B : +							      SPINOR_OP_EX4B, +						     NULL, 0);  	} -	return nor->write_reg(nor, enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B, -			      NULL, 0); +	if (ret) +		dev_dbg(nor->dev, "error %d setting 4-byte mode\n", ret); + +	return ret;  } +/** + * st_micron_set_4byte() - Set 4-byte address mode for ST and Micron flashes. + * @nor:	pointer to 'struct spi_nor'. + * @enable:	true to enter the 4-byte address mode, false to exit the 4-byte + *		address mode. + * + * Return: 0 on success, -errno otherwise. + */  static int st_micron_set_4byte(struct spi_nor *nor, bool enable)  {  	int ret; -	write_enable(nor); +	ret = spi_nor_write_enable(nor); +	if (ret) +		return ret; +  	ret = macronix_set_4byte(nor, enable); -	write_disable(nor); +	if (ret) +		return ret; -	return ret; +	return spi_nor_write_disable(nor);  } +/** + * spansion_set_4byte() - Set 4-byte address mode for Spansion flashes. + * @nor:	pointer to 'struct spi_nor'. + * @enable:	true to enter the 4-byte address mode, false to exit the 4-byte + *		address mode. + * + * Return: 0 on success, -errno otherwise. + */  static int spansion_set_4byte(struct spi_nor *nor, bool enable)  { +	int ret; +  	nor->bouncebuf[0] = enable << 7;  	if (nor->spimem) { @@ -657,14 +619,29 @@ static int spansion_set_4byte(struct spi_nor *nor, bool enable)  				   SPI_MEM_OP_NO_DUMMY,  				   SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1)); -		return spi_mem_exec_op(nor->spimem, &op); +		ret = spi_mem_exec_op(nor->spimem, &op); +	} else { +		ret = nor->controller_ops->write_reg(nor, SPINOR_OP_BRWR, +						     nor->bouncebuf, 1);  	} -	return nor->write_reg(nor, SPINOR_OP_BRWR, nor->bouncebuf, 1); +	if (ret) +		dev_dbg(nor->dev, "error %d setting 4-byte mode\n", ret); + +	return ret;  } +/** + * spi_nor_write_ear() - Write Extended Address Register. + * @nor:	pointer to 'struct spi_nor'. + * @ear:	value to write to the Extended Address Register. + * + * Return: 0 on success, -errno otherwise. + */  static int spi_nor_write_ear(struct spi_nor *nor, u8 ear)  { +	int ret; +  	nor->bouncebuf[0] = ear;  	if (nor->spimem) { @@ -674,12 +651,26 @@ static int spi_nor_write_ear(struct spi_nor *nor, u8 ear)  				   SPI_MEM_OP_NO_DUMMY,  				   SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1)); -		return spi_mem_exec_op(nor->spimem, &op); +		ret = spi_mem_exec_op(nor->spimem, &op); +	} else { +		ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WREAR, +						     nor->bouncebuf, 1);  	} -	return nor->write_reg(nor, SPINOR_OP_WREAR, nor->bouncebuf, 1); +	if (ret) +		dev_dbg(nor->dev, "error %d writing EAR\n", ret); + +	return ret;  } +/** + * winbond_set_4byte() - Set 4-byte address mode for Winbond flashes. + * @nor:	pointer to 'struct spi_nor'. + * @enable:	true to enter the 4-byte address mode, false to exit the 4-byte + *		address mode. + * + * Return: 0 on success, -errno otherwise. + */  static int winbond_set_4byte(struct spi_nor *nor, bool enable)  {  	int ret; @@ -693,15 +684,29 @@ static int winbond_set_4byte(struct spi_nor *nor, bool enable)  	 * Register to be set to 1, so all 3-byte-address reads come from the  	 * second 16M. We must clear the register to enable normal behavior.  	 */ -	write_enable(nor); +	ret = spi_nor_write_enable(nor); +	if (ret) +		return ret; +  	ret = spi_nor_write_ear(nor, 0); -	write_disable(nor); +	if (ret) +		return ret; -	return ret; +	return spi_nor_write_disable(nor);  } +/** + * spi_nor_xread_sr() - Read the Status Register on S3AN flashes. + * @nor:	pointer to 'struct spi_nor'. + * @sr:		pointer to a DMA-able buffer where the value of the + *              Status Register will be written. + * + * Return: 0 on success, -errno otherwise. + */  static int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr)  { +	int ret; +  	if (nor->spimem) {  		struct spi_mem_op op =  			SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_XRDSR, 1), @@ -709,27 +714,44 @@ static int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr)  				   SPI_MEM_OP_NO_DUMMY,  				   SPI_MEM_OP_DATA_IN(1, sr, 1)); -		return spi_mem_exec_op(nor->spimem, &op); +		ret = spi_mem_exec_op(nor->spimem, &op); +	} else { +		ret = nor->controller_ops->read_reg(nor, SPINOR_OP_XRDSR, +						    sr, 1);  	} -	return nor->read_reg(nor, SPINOR_OP_XRDSR, sr, 1); +	if (ret) +		dev_dbg(nor->dev, "error %d reading XRDSR\n", ret); + +	return ret;  } +/** + * s3an_sr_ready() - Query the Status Register of the S3AN flash to see if the + * flash is ready for new commands. + * @nor:	pointer to 'struct spi_nor'. + * + * Return: 0 on success, -errno otherwise. + */  static int s3an_sr_ready(struct spi_nor *nor)  {  	int ret;  	ret = spi_nor_xread_sr(nor, nor->bouncebuf); -	if (ret < 0) { -		dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret); +	if (ret)  		return ret; -	}  	return !!(nor->bouncebuf[0] & XSR_RDY);  } -static int spi_nor_clear_sr(struct spi_nor *nor) +/** + * spi_nor_clear_sr() - Clear the Status Register. + * @nor:	pointer to 'struct spi_nor'. + */ +static void spi_nor_clear_sr(struct spi_nor *nor)  { +	int ret; +  	if (nor->spimem) {  		struct spi_mem_op op =  			SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 1), @@ -737,20 +759,33 @@ static int spi_nor_clear_sr(struct spi_nor *nor)  				   SPI_MEM_OP_NO_DUMMY,  				   SPI_MEM_OP_NO_DATA); -		return spi_mem_exec_op(nor->spimem, &op); +		ret = spi_mem_exec_op(nor->spimem, &op); +	} else { +		ret = nor->controller_ops->write_reg(nor, SPINOR_OP_CLSR, +						     NULL, 0);  	} -	return nor->write_reg(nor, SPINOR_OP_CLSR, NULL, 0); +	if (ret) +		dev_dbg(nor->dev, "error %d clearing SR\n", ret);  } +/** + * spi_nor_sr_ready() - Query the Status Register to see if the flash is ready + * for new commands. + * @nor:	pointer to 'struct spi_nor'. + * + * Return: 0 on success, -errno otherwise. + */  static int spi_nor_sr_ready(struct spi_nor *nor)  { -	int sr = read_sr(nor); -	if (sr < 0) -		return sr; +	int ret = spi_nor_read_sr(nor, nor->bouncebuf); + +	if (ret) +		return ret; -	if (nor->flags & SNOR_F_USE_CLSR && sr & (SR_E_ERR | SR_P_ERR)) { -		if (sr & SR_E_ERR) +	if (nor->flags & SNOR_F_USE_CLSR && +	    nor->bouncebuf[0] & (SR_E_ERR | SR_P_ERR)) { +		if (nor->bouncebuf[0] & SR_E_ERR)  			dev_err(nor->dev, "Erase Error occurred\n");  		else  			dev_err(nor->dev, "Programming Error occurred\n"); @@ -759,11 +794,17 @@ static int spi_nor_sr_ready(struct spi_nor *nor)  		return -EIO;  	} -	return !(sr & SR_WIP); +	return !(nor->bouncebuf[0] & SR_WIP);  } -static int spi_nor_clear_fsr(struct spi_nor *nor) +/** + * spi_nor_clear_fsr() - Clear the Flag Status Register. + * @nor:	pointer to 'struct spi_nor'. + */ +static void spi_nor_clear_fsr(struct spi_nor *nor)  { +	int ret; +  	if (nor->spimem) {  		struct spi_mem_op op =  			SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLFSR, 1), @@ -771,25 +812,37 @@ static int spi_nor_clear_fsr(struct spi_nor *nor)  				   SPI_MEM_OP_NO_DUMMY,  				   SPI_MEM_OP_NO_DATA); -		return spi_mem_exec_op(nor->spimem, &op); +		ret = spi_mem_exec_op(nor->spimem, &op); +	} else { +		ret = nor->controller_ops->write_reg(nor, SPINOR_OP_CLFSR, +						     NULL, 0);  	} -	return nor->write_reg(nor, SPINOR_OP_CLFSR, NULL, 0); +	if (ret) +		dev_dbg(nor->dev, "error %d clearing FSR\n", ret);  } +/** + * spi_nor_fsr_ready() - Query the Flag Status Register to see if the flash is + * ready for new commands. + * @nor:	pointer to 'struct spi_nor'. + * + * Return: 0 on success, -errno otherwise. + */  static int spi_nor_fsr_ready(struct spi_nor *nor)  { -	int fsr = read_fsr(nor); -	if (fsr < 0) -		return fsr; +	int ret = spi_nor_read_fsr(nor, nor->bouncebuf); -	if (fsr & (FSR_E_ERR | FSR_P_ERR)) { -		if (fsr & FSR_E_ERR) +	if (ret) +		return ret; + +	if (nor->bouncebuf[0] & (FSR_E_ERR | FSR_P_ERR)) { +		if (nor->bouncebuf[0] & FSR_E_ERR)  			dev_err(nor->dev, "Erase operation failed.\n");  		else  			dev_err(nor->dev, "Program operation failed.\n"); -		if (fsr & FSR_PT_ERR) +		if (nor->bouncebuf[0] & FSR_PT_ERR)  			dev_err(nor->dev,  			"Attempted to modify a protected sector.\n"); @@ -797,9 +850,15 @@ static int spi_nor_fsr_ready(struct spi_nor *nor)  		return -EIO;  	} -	return fsr & FSR_READY; +	return nor->bouncebuf[0] & FSR_READY;  } +/** + * spi_nor_ready() - Query the flash to see if it is ready for new commands. + * @nor:	pointer to 'struct spi_nor'. + * + * Return: 0 on success, -errno otherwise. + */  static int spi_nor_ready(struct spi_nor *nor)  {  	int sr, fsr; @@ -816,9 +875,13 @@ static int spi_nor_ready(struct spi_nor *nor)  	return sr && fsr;  } -/* - * Service routine to read status register until ready, or timeout occurs. - * Returns non-zero if error. +/** + * spi_nor_wait_till_ready_with_timeout() - Service routine to read the + * Status Register until ready, or timeout occurs. + * @nor:		pointer to "struct spi_nor". + * @timeout_jiffies:	jiffies to wait until timeout. + * + * Return: 0 on success, -errno otherwise.   */  static int spi_nor_wait_till_ready_with_timeout(struct spi_nor *nor,  						unsigned long timeout_jiffies) @@ -841,24 +904,305 @@ static int spi_nor_wait_till_ready_with_timeout(struct spi_nor *nor,  		cond_resched();  	} -	dev_err(nor->dev, "flash operation timed out\n"); +	dev_dbg(nor->dev, "flash operation timed out\n");  	return -ETIMEDOUT;  } +/** + * spi_nor_wait_till_ready() - Wait for a predefined amount of time for the + * flash to be ready, or timeout occurs. + * @nor:	pointer to "struct spi_nor". + * + * Return: 0 on success, -errno otherwise. + */  static int spi_nor_wait_till_ready(struct spi_nor *nor)  {  	return spi_nor_wait_till_ready_with_timeout(nor,  						    DEFAULT_READY_WAIT_JIFFIES);  } -/* - * Erase the whole flash memory +/** + * spi_nor_write_sr() - Write the Status Register. + * @nor:	pointer to 'struct spi_nor'. + * @sr:		pointer to DMA-able buffer to write to the Status Register. + * @len:	number of bytes to write to the Status Register. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len) +{ +	int ret; + +	ret = spi_nor_write_enable(nor); +	if (ret) +		return ret; + +	if (nor->spimem) { +		struct spi_mem_op op = +			SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 1), +				   SPI_MEM_OP_NO_ADDR, +				   SPI_MEM_OP_NO_DUMMY, +				   SPI_MEM_OP_DATA_OUT(len, sr, 1)); + +		ret = spi_mem_exec_op(nor->spimem, &op); +	} else { +		ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WRSR, +						     sr, len); +	} + +	if (ret) { +		dev_dbg(nor->dev, "error %d writing SR\n", ret); +		return ret; +	} + +	return spi_nor_wait_till_ready(nor); +} + +/** + * spi_nor_write_sr1_and_check() - Write one byte to the Status Register 1 and + * ensure that the byte written match the received value. + * @nor:	pointer to a 'struct spi_nor'. + * @sr1:	byte value to be written to the Status Register. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_write_sr1_and_check(struct spi_nor *nor, u8 sr1) +{ +	int ret; + +	nor->bouncebuf[0] = sr1; + +	ret = spi_nor_write_sr(nor, nor->bouncebuf, 1); +	if (ret) +		return ret; + +	ret = spi_nor_read_sr(nor, nor->bouncebuf); +	if (ret) +		return ret; + +	if (nor->bouncebuf[0] != sr1) { +		dev_dbg(nor->dev, "SR1: read back test failed\n"); +		return -EIO; +	} + +	return 0; +} + +/** + * spi_nor_write_16bit_sr_and_check() - Write the Status Register 1 and the + * Status Register 2 in one shot. Ensure that the byte written in the Status + * Register 1 match the received value, and that the 16-bit Write did not + * affect what was already in the Status Register 2. + * @nor:	pointer to a 'struct spi_nor'. + * @sr1:	byte value to be written to the Status Register 1. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_write_16bit_sr_and_check(struct spi_nor *nor, u8 sr1) +{ +	int ret; +	u8 *sr_cr = nor->bouncebuf; +	u8 cr_written; + +	/* Make sure we don't overwrite the contents of Status Register 2. */ +	if (!(nor->flags & SNOR_F_NO_READ_CR)) { +		ret = spi_nor_read_cr(nor, &sr_cr[1]); +		if (ret) +			return ret; +	} else if (nor->params.quad_enable) { +		/* +		 * If the Status Register 2 Read command (35h) is not +		 * supported, we should at least be sure we don't +		 * change the value of the SR2 Quad Enable bit. +		 * +		 * We can safely assume that when the Quad Enable method is +		 * set, the value of the QE bit is one, as a consequence of the +		 * nor->params.quad_enable() call. +		 * +		 * We can safely assume that the Quad Enable bit is present in +		 * the Status Register 2 at BIT(1). According to the JESD216 +		 * revB standard, BFPT DWORDS[15], bits 22:20, the 16-bit +		 * Write Status (01h) command is available just for the cases +		 * in which the QE bit is described in SR2 at BIT(1). +		 */ +		sr_cr[1] = SR2_QUAD_EN_BIT1; +	} else { +		sr_cr[1] = 0; +	} + +	sr_cr[0] = sr1; + +	ret = spi_nor_write_sr(nor, sr_cr, 2); +	if (ret) +		return ret; + +	if (nor->flags & SNOR_F_NO_READ_CR) +		return 0; + +	cr_written = sr_cr[1]; + +	ret = spi_nor_read_cr(nor, &sr_cr[1]); +	if (ret) +		return ret; + +	if (cr_written != sr_cr[1]) { +		dev_dbg(nor->dev, "CR: read back test failed\n"); +		return -EIO; +	} + +	return 0; +} + +/** + * spi_nor_write_16bit_cr_and_check() - Write the Status Register 1 and the + * Configuration Register in one shot. Ensure that the byte written in the + * Configuration Register match the received value, and that the 16-bit Write + * did not affect what was already in the Status Register 1. + * @nor:	pointer to a 'struct spi_nor'. + * @cr:		byte value to be written to the Configuration Register. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr) +{ +	int ret; +	u8 *sr_cr = nor->bouncebuf; +	u8 sr_written; + +	/* Keep the current value of the Status Register 1. */ +	ret = spi_nor_read_sr(nor, sr_cr); +	if (ret) +		return ret; + +	sr_cr[1] = cr; + +	ret = spi_nor_write_sr(nor, sr_cr, 2); +	if (ret) +		return ret; + +	sr_written = sr_cr[0]; + +	ret = spi_nor_read_sr(nor, sr_cr); +	if (ret) +		return ret; + +	if (sr_written != sr_cr[0]) { +		dev_dbg(nor->dev, "SR: Read back test failed\n"); +		return -EIO; +	} + +	if (nor->flags & SNOR_F_NO_READ_CR) +		return 0; + +	ret = spi_nor_read_cr(nor, &sr_cr[1]); +	if (ret) +		return ret; + +	if (cr != sr_cr[1]) { +		dev_dbg(nor->dev, "CR: read back test failed\n"); +		return -EIO; +	} + +	return 0; +} + +/** + * spi_nor_write_sr_and_check() - Write the Status Register 1 and ensure that + * the byte written match the received value without affecting other bits in the + * Status Register 1 and 2. + * @nor:	pointer to a 'struct spi_nor'. + * @sr1:	byte value to be written to the Status Register. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 sr1) +{ +	if (nor->flags & SNOR_F_HAS_16BIT_SR) +		return spi_nor_write_16bit_sr_and_check(nor, sr1); + +	return spi_nor_write_sr1_and_check(nor, sr1); +} + +/** + * spi_nor_write_sr2() - Write the Status Register 2 using the + * SPINOR_OP_WRSR2 (3eh) command. + * @nor:	pointer to 'struct spi_nor'. + * @sr2:	pointer to DMA-able buffer to write to the Status Register 2.   * - * Returns 0 if successful, non-zero otherwise. + * Return: 0 on success, -errno otherwise.   */ -static int erase_chip(struct spi_nor *nor) +static int spi_nor_write_sr2(struct spi_nor *nor, const u8 *sr2)  { +	int ret; + +	ret = spi_nor_write_enable(nor); +	if (ret) +		return ret; + +	if (nor->spimem) { +		struct spi_mem_op op = +			SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR2, 1), +				   SPI_MEM_OP_NO_ADDR, +				   SPI_MEM_OP_NO_DUMMY, +				   SPI_MEM_OP_DATA_OUT(1, sr2, 1)); + +		ret = spi_mem_exec_op(nor->spimem, &op); +	} else { +		ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WRSR2, +						     sr2, 1); +	} + +	if (ret) { +		dev_dbg(nor->dev, "error %d writing SR2\n", ret); +		return ret; +	} + +	return spi_nor_wait_till_ready(nor); +} + +/** + * spi_nor_read_sr2() - Read the Status Register 2 using the + * SPINOR_OP_RDSR2 (3fh) command. + * @nor:	pointer to 'struct spi_nor'. + * @sr2:	pointer to DMA-able buffer where the value of the + *		Status Register 2 will be written. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_read_sr2(struct spi_nor *nor, u8 *sr2) +{ +	int ret; + +	if (nor->spimem) { +		struct spi_mem_op op = +			SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR2, 1), +				   SPI_MEM_OP_NO_ADDR, +				   SPI_MEM_OP_NO_DUMMY, +				   SPI_MEM_OP_DATA_IN(1, sr2, 1)); + +		ret = spi_mem_exec_op(nor->spimem, &op); +	} else { +		ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDSR2, +						    sr2, 1); +	} + +	if (ret) +		dev_dbg(nor->dev, "error %d reading SR2\n", ret); + +	return ret; +} + +/** + * spi_nor_erase_chip() - Erase the entire flash memory. + * @nor:	pointer to 'struct spi_nor'. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_erase_chip(struct spi_nor *nor) +{ +	int ret; +  	dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd.size >> 10));  	if (nor->spimem) { @@ -868,10 +1212,99 @@ static int erase_chip(struct spi_nor *nor)  				   SPI_MEM_OP_NO_DUMMY,  				   SPI_MEM_OP_NO_DATA); -		return spi_mem_exec_op(nor->spimem, &op); +		ret = spi_mem_exec_op(nor->spimem, &op); +	} else { +		ret = nor->controller_ops->write_reg(nor, SPINOR_OP_CHIP_ERASE, +						     NULL, 0);  	} -	return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0); +	if (ret) +		dev_dbg(nor->dev, "error %d erasing chip\n", ret); + +	return ret; +} + +static struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd) +{ +	return mtd->priv; +} + +static u8 spi_nor_convert_opcode(u8 opcode, const u8 table[][2], size_t size) +{ +	size_t i; + +	for (i = 0; i < size; i++) +		if (table[i][0] == opcode) +			return table[i][1]; + +	/* No conversion found, keep input op code. */ +	return opcode; +} + +static u8 spi_nor_convert_3to4_read(u8 opcode) +{ +	static const u8 spi_nor_3to4_read[][2] = { +		{ SPINOR_OP_READ,	SPINOR_OP_READ_4B }, +		{ SPINOR_OP_READ_FAST,	SPINOR_OP_READ_FAST_4B }, +		{ SPINOR_OP_READ_1_1_2,	SPINOR_OP_READ_1_1_2_4B }, +		{ SPINOR_OP_READ_1_2_2,	SPINOR_OP_READ_1_2_2_4B }, +		{ SPINOR_OP_READ_1_1_4,	SPINOR_OP_READ_1_1_4_4B }, +		{ SPINOR_OP_READ_1_4_4,	SPINOR_OP_READ_1_4_4_4B }, +		{ SPINOR_OP_READ_1_1_8,	SPINOR_OP_READ_1_1_8_4B }, +		{ SPINOR_OP_READ_1_8_8,	SPINOR_OP_READ_1_8_8_4B }, + +		{ SPINOR_OP_READ_1_1_1_DTR,	SPINOR_OP_READ_1_1_1_DTR_4B }, +		{ SPINOR_OP_READ_1_2_2_DTR,	SPINOR_OP_READ_1_2_2_DTR_4B }, +		{ SPINOR_OP_READ_1_4_4_DTR,	SPINOR_OP_READ_1_4_4_DTR_4B }, +	}; + +	return spi_nor_convert_opcode(opcode, spi_nor_3to4_read, +				      ARRAY_SIZE(spi_nor_3to4_read)); +} + +static u8 spi_nor_convert_3to4_program(u8 opcode) +{ +	static const u8 spi_nor_3to4_program[][2] = { +		{ SPINOR_OP_PP,		SPINOR_OP_PP_4B }, +		{ SPINOR_OP_PP_1_1_4,	SPINOR_OP_PP_1_1_4_4B }, +		{ SPINOR_OP_PP_1_4_4,	SPINOR_OP_PP_1_4_4_4B }, +		{ SPINOR_OP_PP_1_1_8,	SPINOR_OP_PP_1_1_8_4B }, +		{ SPINOR_OP_PP_1_8_8,	SPINOR_OP_PP_1_8_8_4B }, +	}; + +	return spi_nor_convert_opcode(opcode, spi_nor_3to4_program, +				      ARRAY_SIZE(spi_nor_3to4_program)); +} + +static u8 spi_nor_convert_3to4_erase(u8 opcode) +{ +	static const u8 spi_nor_3to4_erase[][2] = { +		{ SPINOR_OP_BE_4K,	SPINOR_OP_BE_4K_4B }, +		{ SPINOR_OP_BE_32K,	SPINOR_OP_BE_32K_4B }, +		{ SPINOR_OP_SE,		SPINOR_OP_SE_4B }, +	}; + +	return spi_nor_convert_opcode(opcode, spi_nor_3to4_erase, +				      ARRAY_SIZE(spi_nor_3to4_erase)); +} + +static void spi_nor_set_4byte_opcodes(struct spi_nor *nor) +{ +	nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode); +	nor->program_opcode = spi_nor_convert_3to4_program(nor->program_opcode); +	nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode); + +	if (!spi_nor_has_uniform_erase(nor)) { +		struct spi_nor_erase_map *map = &nor->params.erase_map; +		struct spi_nor_erase_type *erase; +		int i; + +		for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) { +			erase = &map->erase_type[i]; +			erase->opcode = +				spi_nor_convert_3to4_erase(erase->opcode); +		} +	}  }  static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops) @@ -880,10 +1313,9 @@ static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops)  	mutex_lock(&nor->lock); -	if (nor->prepare) { -		ret = nor->prepare(nor, ops); +	if (nor->controller_ops &&  nor->controller_ops->prepare) { +		ret = nor->controller_ops->prepare(nor, ops);  		if (ret) { -			dev_err(nor->dev, "failed in the preparation.\n");  			mutex_unlock(&nor->lock);  			return ret;  		} @@ -893,8 +1325,8 @@ static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops)  static void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops)  { -	if (nor->unprepare) -		nor->unprepare(nor, ops); +	if (nor->controller_ops && nor->controller_ops->unprepare) +		nor->controller_ops->unprepare(nor, ops);  	mutex_unlock(&nor->lock);  } @@ -935,9 +1367,6 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)  	addr = spi_nor_convert_addr(nor, addr); -	if (nor->erase) -		return nor->erase(nor, addr); -  	if (nor->spimem) {  		struct spi_mem_op op =  			SPI_MEM_OP(SPI_MEM_OP_CMD(nor->erase_opcode, 1), @@ -946,6 +1375,8 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)  				   SPI_MEM_OP_NO_DATA);  		return spi_mem_exec_op(nor->spimem, &op); +	} else if (nor->controller_ops->erase) { +		return nor->controller_ops->erase(nor, addr);  	}  	/* @@ -957,8 +1388,8 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)  		addr >>= 8;  	} -	return nor->write_reg(nor, nor->erase_opcode, nor->bouncebuf, -			      nor->addr_width); +	return nor->controller_ops->write_reg(nor, nor->erase_opcode, +					      nor->bouncebuf, nor->addr_width);  }  /** @@ -1208,7 +1639,9 @@ static int spi_nor_erase_multi_sectors(struct spi_nor *nor, u64 addr, u32 len)  	list_for_each_entry_safe(cmd, next, &erase_list, list) {  		nor->erase_opcode = cmd->opcode;  		while (cmd->count) { -			write_enable(nor); +			ret = spi_nor_write_enable(nor); +			if (ret) +				goto destroy_erase_cmd_list;  			ret = spi_nor_erase_sector(nor, addr);  			if (ret) @@ -1263,12 +1696,13 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)  	if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {  		unsigned long timeout; -		write_enable(nor); +		ret = spi_nor_write_enable(nor); +		if (ret) +			goto erase_err; -		if (erase_chip(nor)) { -			ret = -EIO; +		ret = spi_nor_erase_chip(nor); +		if (ret)  			goto erase_err; -		}  		/*  		 * Scale the timeout linearly with the size of the flash, with @@ -1291,7 +1725,9 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)  	/* "sector"-at-a-time erase */  	} else if (spi_nor_has_uniform_erase(nor)) {  		while (len) { -			write_enable(nor); +			ret = spi_nor_write_enable(nor); +			if (ret) +				goto erase_err;  			ret = spi_nor_erase_sector(nor, addr);  			if (ret) @@ -1312,7 +1748,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)  			goto erase_err;  	} -	write_disable(nor); +	ret = spi_nor_write_disable(nor);  erase_err:  	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE); @@ -1320,27 +1756,6 @@ erase_err:  	return ret;  } -/* Write status register and ensure bits in mask match written values */ -static int write_sr_and_check(struct spi_nor *nor, u8 status_new, u8 mask) -{ -	int ret; - -	write_enable(nor); -	ret = write_sr(nor, status_new); -	if (ret) -		return ret; - -	ret = spi_nor_wait_till_ready(nor); -	if (ret) -		return ret; - -	ret = read_sr(nor); -	if (ret < 0) -		return ret; - -	return ((ret & mask) != (status_new & mask)) ? -EIO : 0; -} -  static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs,  				 uint64_t *len)  { @@ -1433,16 +1848,18 @@ static int stm_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,  static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)  {  	struct mtd_info *mtd = &nor->mtd; -	int status_old, status_new; +	int ret, status_old, status_new;  	u8 mask = SR_BP2 | SR_BP1 | SR_BP0;  	u8 shift = ffs(mask) - 1, pow, val;  	loff_t lock_len;  	bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB;  	bool use_top; -	status_old = read_sr(nor); -	if (status_old < 0) -		return status_old; +	ret = spi_nor_read_sr(nor, nor->bouncebuf); +	if (ret) +		return ret; + +	status_old = nor->bouncebuf[0];  	/* If nothing in our range is unlocked, we don't need to do anything */  	if (stm_is_locked_sr(nor, ofs, len, status_old)) @@ -1502,7 +1919,7 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)  	if ((status_new & mask) < (status_old & mask))  		return -EINVAL; -	return write_sr_and_check(nor, status_new, mask); +	return spi_nor_write_sr_and_check(nor, status_new);  }  /* @@ -1513,16 +1930,18 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)  static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)  {  	struct mtd_info *mtd = &nor->mtd; -	int status_old, status_new; +	int ret, status_old, status_new;  	u8 mask = SR_BP2 | SR_BP1 | SR_BP0;  	u8 shift = ffs(mask) - 1, pow, val;  	loff_t lock_len;  	bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB;  	bool use_top; -	status_old = read_sr(nor); -	if (status_old < 0) -		return status_old; +	ret = spi_nor_read_sr(nor, nor->bouncebuf); +	if (ret) +		return ret; + +	status_old = nor->bouncebuf[0];  	/* If nothing in our range is locked, we don't need to do anything */  	if (stm_is_unlocked_sr(nor, ofs, len, status_old)) @@ -1585,7 +2004,7 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)  	if ((status_new & mask) > (status_old & mask))  		return -EINVAL; -	return write_sr_and_check(nor, status_new, mask); +	return spi_nor_write_sr_and_check(nor, status_new);  }  /* @@ -1597,13 +2016,13 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)   */  static int stm_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)  { -	int status; +	int ret; -	status = read_sr(nor); -	if (status < 0) -		return status; +	ret = spi_nor_read_sr(nor, nor->bouncebuf); +	if (ret) +		return ret; -	return stm_is_locked_sr(nor, ofs, len, status); +	return stm_is_locked_sr(nor, ofs, len, nor->bouncebuf[0]);  }  static const struct spi_nor_locking_ops stm_locking_ops = { @@ -1657,242 +2076,59 @@ static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)  	return ret;  } -/* - * Write status Register and configuration register with 2 bytes - * The first byte will be written to the status register, while the - * second byte will be written to the configuration register. - * Return negative if error occurred. - */ -static int write_sr_cr(struct spi_nor *nor, u8 *sr_cr) -{ -	int ret; - -	write_enable(nor); - -	if (nor->spimem) { -		struct spi_mem_op op = -			SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 1), -				   SPI_MEM_OP_NO_ADDR, -				   SPI_MEM_OP_NO_DUMMY, -				   SPI_MEM_OP_DATA_OUT(2, sr_cr, 1)); - -		ret = spi_mem_exec_op(nor->spimem, &op); -	} else { -		ret = nor->write_reg(nor, SPINOR_OP_WRSR, sr_cr, 2); -	} - -	if (ret < 0) { -		dev_err(nor->dev, -			"error while writing configuration register\n"); -		return -EINVAL; -	} - -	ret = spi_nor_wait_till_ready(nor); -	if (ret) { -		dev_err(nor->dev, -			"timeout while writing configuration register\n"); -		return ret; -	} - -	return 0; -} - -/** - * macronix_quad_enable() - set QE bit in Status Register. - * @nor:	pointer to a 'struct spi_nor' - * - * Set the Quad Enable (QE) bit in the Status Register. - * - * bit 6 of the Status Register is the QE bit for Macronix like QSPI memories. - * - * Return: 0 on success, -errno otherwise. - */ -static int macronix_quad_enable(struct spi_nor *nor) -{ -	int ret, val; - -	val = read_sr(nor); -	if (val < 0) -		return val; -	if (val & SR_QUAD_EN_MX) -		return 0; - -	write_enable(nor); - -	write_sr(nor, val | SR_QUAD_EN_MX); - -	ret = spi_nor_wait_till_ready(nor); -	if (ret) -		return ret; - -	ret = read_sr(nor); -	if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) { -		dev_err(nor->dev, "Macronix Quad bit not set\n"); -		return -EINVAL; -	} - -	return 0; -} -  /** - * spansion_quad_enable() - set QE bit in Configuraiton Register. + * spi_nor_sr1_bit6_quad_enable() - Set the Quad Enable BIT(6) in the Status + * Register 1.   * @nor:	pointer to a 'struct spi_nor'   * - * Set the Quad Enable (QE) bit in the Configuration Register. - * This function is kept for legacy purpose because it has been used for a - * long time without anybody complaining but it should be considered as - * deprecated and maybe buggy. - * First, this function doesn't care about the previous values of the Status - * and Configuration Registers when it sets the QE bit (bit 1) in the - * Configuration Register: all other bits are cleared, which may have unwanted - * side effects like removing some block protections. - * Secondly, it uses the Read Configuration Register (35h) instruction though - * some very old and few memories don't support this instruction. If a pull-up - * resistor is present on the MISO/IO1 line, we might still be able to pass the - * "read back" test because the QSPI memory doesn't recognize the command, - * so leaves the MISO/IO1 line state unchanged, hence read_cr() returns 0xFF. - * - * bit 1 of the Configuration Register is the QE bit for Spansion like QSPI - * memories. + * Bit 6 of the Status Register 1 is the QE bit for Macronix like QSPI memories.   *   * Return: 0 on success, -errno otherwise.   */ -static int spansion_quad_enable(struct spi_nor *nor) +static int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor)  { -	u8 *sr_cr = nor->bouncebuf;  	int ret; -	sr_cr[0] = 0; -	sr_cr[1] = CR_QUAD_EN_SPAN; -	ret = write_sr_cr(nor, sr_cr); +	ret = spi_nor_read_sr(nor, nor->bouncebuf);  	if (ret)  		return ret; -	/* read back and check it */ -	ret = read_cr(nor); -	if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { -		dev_err(nor->dev, "Spansion Quad bit not set\n"); -		return -EINVAL; -	} - -	return 0; -} - -/** - * spansion_no_read_cr_quad_enable() - set QE bit in Configuration Register. - * @nor:	pointer to a 'struct spi_nor' - * - * Set the Quad Enable (QE) bit in the Configuration Register. - * This function should be used with QSPI memories not supporting the Read - * Configuration Register (35h) instruction. - * - * bit 1 of the Configuration Register is the QE bit for Spansion like QSPI - * memories. - * - * Return: 0 on success, -errno otherwise. - */ -static int spansion_no_read_cr_quad_enable(struct spi_nor *nor) -{ -	u8 *sr_cr = nor->bouncebuf; -	int ret; +	if (nor->bouncebuf[0] & SR1_QUAD_EN_BIT6) +		return 0; -	/* Keep the current value of the Status Register. */ -	ret = read_sr(nor); -	if (ret < 0) { -		dev_err(nor->dev, "error while reading status register\n"); -		return -EINVAL; -	} -	sr_cr[0] = ret; -	sr_cr[1] = CR_QUAD_EN_SPAN; +	nor->bouncebuf[0] |= SR1_QUAD_EN_BIT6; -	return write_sr_cr(nor, sr_cr); +	return spi_nor_write_sr1_and_check(nor, nor->bouncebuf[0]);  }  /** - * spansion_read_cr_quad_enable() - set QE bit in Configuration Register. - * @nor:	pointer to a 'struct spi_nor' - * - * Set the Quad Enable (QE) bit in the Configuration Register. - * This function should be used with QSPI memories supporting the Read - * Configuration Register (35h) instruction. + * spi_nor_sr2_bit1_quad_enable() - set the Quad Enable BIT(1) in the Status + * Register 2. + * @nor:       pointer to a 'struct spi_nor'.   * - * bit 1 of the Configuration Register is the QE bit for Spansion like QSPI - * memories. + * Bit 1 of the Status Register 2 is the QE bit for Spansion like QSPI memories.   *   * Return: 0 on success, -errno otherwise.   */ -static int spansion_read_cr_quad_enable(struct spi_nor *nor) +static int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor)  { -	struct device *dev = nor->dev; -	u8 *sr_cr = nor->bouncebuf;  	int ret; -	/* Check current Quad Enable bit value. */ -	ret = read_cr(nor); -	if (ret < 0) { -		dev_err(dev, "error while reading configuration register\n"); -		return -EINVAL; -	} - -	if (ret & CR_QUAD_EN_SPAN) -		return 0; - -	sr_cr[1] = ret | CR_QUAD_EN_SPAN; - -	/* Keep the current value of the Status Register. */ -	ret = read_sr(nor); -	if (ret < 0) { -		dev_err(dev, "error while reading status register\n"); -		return -EINVAL; -	} -	sr_cr[0] = ret; +	if (nor->flags & SNOR_F_NO_READ_CR) +		return spi_nor_write_16bit_cr_and_check(nor, SR2_QUAD_EN_BIT1); -	ret = write_sr_cr(nor, sr_cr); +	ret = spi_nor_read_cr(nor, nor->bouncebuf);  	if (ret)  		return ret; -	/* Read back and check it. */ -	ret = read_cr(nor); -	if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { -		dev_err(nor->dev, "Spansion Quad bit not set\n"); -		return -EINVAL; -	} - -	return 0; -} - -static int spi_nor_write_sr2(struct spi_nor *nor, u8 *sr2) -{ -	if (nor->spimem) { -		struct spi_mem_op op = -			SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR2, 1), -				   SPI_MEM_OP_NO_ADDR, -				   SPI_MEM_OP_NO_DUMMY, -				   SPI_MEM_OP_DATA_OUT(1, sr2, 1)); - -		return spi_mem_exec_op(nor->spimem, &op); -	} - -	return nor->write_reg(nor, SPINOR_OP_WRSR2, sr2, 1); -} - -static int spi_nor_read_sr2(struct spi_nor *nor, u8 *sr2) -{ -	if (nor->spimem) { -		struct spi_mem_op op = -			SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR2, 1), -				   SPI_MEM_OP_NO_ADDR, -				   SPI_MEM_OP_NO_DUMMY, -				   SPI_MEM_OP_DATA_IN(1, sr2, 1)); - -		return spi_mem_exec_op(nor->spimem, &op); -	} +	if (nor->bouncebuf[0] & SR2_QUAD_EN_BIT1) +		return 0; -	return nor->read_reg(nor, SPINOR_OP_RDSR2, sr2, 1); +	return spi_nor_write_16bit_cr_and_check(nor, nor->bouncebuf[0]);  }  /** - * sr2_bit7_quad_enable() - set QE bit in Status Register 2. + * spi_nor_sr2_bit7_quad_enable() - set QE bit in Status Register 2.   * @nor:	pointer to a 'struct spi_nor'   *   * Set the Quad Enable (QE) bit in the Status Register 2. @@ -1903,10 +2139,11 @@ static int spi_nor_read_sr2(struct spi_nor *nor, u8 *sr2)   *   * Return: 0 on success, -errno otherwise.   */ -static int sr2_bit7_quad_enable(struct spi_nor *nor) +static int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor)  {  	u8 *sr2 = nor->bouncebuf;  	int ret; +	u8 sr2_written;  	/* Check current Quad Enable bit value. */  	ret = spi_nor_read_sr2(nor, sr2); @@ -1918,117 +2155,23 @@ static int sr2_bit7_quad_enable(struct spi_nor *nor)  	/* Update the Quad Enable bit. */  	*sr2 |= SR2_QUAD_EN_BIT7; -	write_enable(nor); -  	ret = spi_nor_write_sr2(nor, sr2); -	if (ret < 0) { -		dev_err(nor->dev, "error while writing status register 2\n"); -		return -EINVAL; -	} - -	ret = spi_nor_wait_till_ready(nor); -	if (ret < 0) { -		dev_err(nor->dev, "timeout while writing status register 2\n"); +	if (ret)  		return ret; -	} + +	sr2_written = *sr2;  	/* Read back and check it. */  	ret = spi_nor_read_sr2(nor, sr2); -	if (!(ret > 0 && (*sr2 & SR2_QUAD_EN_BIT7))) { -		dev_err(nor->dev, "SR2 Quad bit not set\n"); -		return -EINVAL; -	} - -	return 0; -} - -/** - * spi_nor_clear_sr_bp() - clear the Status Register Block Protection bits. - * @nor:        pointer to a 'struct spi_nor' - * - * Read-modify-write function that clears the Block Protection bits from the - * Status Register without affecting other bits. - * - * Return: 0 on success, -errno otherwise. - */ -static int spi_nor_clear_sr_bp(struct spi_nor *nor) -{ -	int ret; -	u8 mask = SR_BP2 | SR_BP1 | SR_BP0; - -	ret = read_sr(nor); -	if (ret < 0) { -		dev_err(nor->dev, "error while reading status register\n"); -		return ret; -	} - -	write_enable(nor); - -	ret = write_sr(nor, ret & ~mask); -	if (ret) { -		dev_err(nor->dev, "write to status register failed\n"); -		return ret; -	} - -	ret = spi_nor_wait_till_ready(nor);  	if (ret) -		dev_err(nor->dev, "timeout while writing status register\n"); -	return ret; -} - -/** - * spi_nor_spansion_clear_sr_bp() - clear the Status Register Block Protection - * bits on spansion flashes. - * @nor:        pointer to a 'struct spi_nor' - * - * Read-modify-write function that clears the Block Protection bits from the - * Status Register without affecting other bits. The function is tightly - * coupled with the spansion_quad_enable() function. Both assume that the Write - * Register with 16 bits, together with the Read Configuration Register (35h) - * instructions are supported. - * - * Return: 0 on success, -errno otherwise. - */ -static int spi_nor_spansion_clear_sr_bp(struct spi_nor *nor) -{ -	int ret; -	u8 mask = SR_BP2 | SR_BP1 | SR_BP0; -	u8 *sr_cr =  nor->bouncebuf; - -	/* Check current Quad Enable bit value. */ -	ret = read_cr(nor); -	if (ret < 0) { -		dev_err(nor->dev, -			"error while reading configuration register\n");  		return ret; -	} - -	/* -	 * When the configuration register Quad Enable bit is one, only the -	 * Write Status (01h) command with two data bytes may be used. -	 */ -	if (ret & CR_QUAD_EN_SPAN) { -		sr_cr[1] = ret; - -		ret = read_sr(nor); -		if (ret < 0) { -			dev_err(nor->dev, -				"error while reading status register\n"); -			return ret; -		} -		sr_cr[0] = ret & ~mask; -		ret = write_sr_cr(nor, sr_cr); -		if (ret) -			dev_err(nor->dev, "16-bit write register failed\n"); -		return ret; +	if (*sr2 != sr2_written) { +		dev_dbg(nor->dev, "SR2: Read back test failed\n"); +		return -EIO;  	} -	/* -	 * If the Quad Enable bit is zero, use the Write Status (01h) command -	 * with one data byte. -	 */ -	return spi_nor_clear_sr_bp(nor); +	return 0;  }  /* Used when the "_ext_id" is two bytes at most */ @@ -2136,7 +2279,7 @@ static void gd25q256_default_init(struct spi_nor *nor)  	 * indicate the quad_enable method for this case, we need  	 * to set it in the default_init fixup hook.  	 */ -	nor->params.quad_enable = macronix_quad_enable; +	nor->params.quad_enable = spi_nor_sr1_bit6_quad_enable;  }  static struct spi_nor_fixups gd25q256_fixups = { @@ -2179,6 +2322,8 @@ static const struct flash_info spi_nor_ids[] = {  	{ "en25q64",    INFO(0x1c3017, 0, 64 * 1024,  128, SECT_4K) },  	{ "en25q80a",   INFO(0x1c3014, 0, 64 * 1024,   16,  			SECT_4K | SPI_NOR_DUAL_READ) }, +	{ "en25qh16",   INFO(0x1c7015, 0, 64 * 1024,   32, +			SECT_4K | SPI_NOR_DUAL_READ) },  	{ "en25qh32",   INFO(0x1c7016, 0, 64 * 1024,   64, 0) },  	{ "en25qh64",   INFO(0x1c7017, 0, 64 * 1024,  128,  			SECT_4K | SPI_NOR_DUAL_READ) }, @@ -2267,6 +2412,10 @@ static const struct flash_info spi_nor_ids[] = {  			SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },  	{ "is25wp128",  INFO(0x9d7018, 0, 64 * 1024, 256,  			SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, +	{ "is25wp256", INFO(0x9d7019, 0, 64 * 1024, 512, +			    SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | +			    SPI_NOR_4B_OPCODES) +		       .fixups = &is25lp256_fixups },  	/* Macronix */  	{ "mx25l512e",   INFO(0xc22010, 0, 64 * 1024,   1, SECT_4K) }, @@ -2482,6 +2631,8 @@ static const struct flash_info spi_nor_ids[] = {  	{ "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },  	{ "w25q256jvm", INFO(0xef7019, 0, 64 * 1024, 512,  			     SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, +	{ "w25q256jw", INFO(0xef6019, 0, 64 * 1024, 512, +			     SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },  	{ "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024,  			SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ) }, @@ -2520,11 +2671,11 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)  		tmp = spi_mem_exec_op(nor->spimem, &op);  	} else { -		tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, -				    SPI_NOR_MAX_ID_LEN); +		tmp = nor->controller_ops->read_reg(nor, SPINOR_OP_RDID, id, +						    SPI_NOR_MAX_ID_LEN);  	} -	if (tmp < 0) { -		dev_err(nor->dev, "error %d reading JEDEC ID\n", tmp); +	if (tmp) { +		dev_dbg(nor->dev, "error %d reading JEDEC ID\n", tmp);  		return ERR_PTR(tmp);  	} @@ -2544,7 +2695,7 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,  			size_t *retlen, u_char *buf)  {  	struct spi_nor *nor = mtd_to_spi_nor(mtd); -	int ret; +	ssize_t ret;  	dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len); @@ -2583,7 +2734,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,  		size_t *retlen, const u_char *buf)  {  	struct spi_nor *nor = mtd_to_spi_nor(mtd); -	size_t actual; +	size_t actual = 0;  	int ret;  	dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len); @@ -2592,26 +2743,28 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,  	if (ret)  		return ret; -	write_enable(nor); +	ret = spi_nor_write_enable(nor); +	if (ret) +		goto out;  	nor->sst_write_second = false; -	actual = to % 2;  	/* Start write from odd address. */ -	if (actual) { +	if (to % 2) {  		nor->program_opcode = SPINOR_OP_BP;  		/* write one byte. */  		ret = spi_nor_write_data(nor, to, 1, buf);  		if (ret < 0) -			goto sst_write_err; -		WARN(ret != 1, "While writing 1 byte written %i bytes\n", -		     (int)ret); +			goto out; +		WARN(ret != 1, "While writing 1 byte written %i bytes\n", ret);  		ret = spi_nor_wait_till_ready(nor);  		if (ret) -			goto sst_write_err; +			goto out; + +		to++; +		actual++;  	} -	to += actual;  	/* Write out most of the data here. */  	for (; actual < len - 1; actual += 2) { @@ -2620,39 +2773,44 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,  		/* write two bytes. */  		ret = spi_nor_write_data(nor, to, 2, buf + actual);  		if (ret < 0) -			goto sst_write_err; -		WARN(ret != 2, "While writing 2 bytes written %i bytes\n", -		     (int)ret); +			goto out; +		WARN(ret != 2, "While writing 2 bytes written %i bytes\n", ret);  		ret = spi_nor_wait_till_ready(nor);  		if (ret) -			goto sst_write_err; +			goto out;  		to += 2;  		nor->sst_write_second = true;  	}  	nor->sst_write_second = false; -	write_disable(nor); +	ret = spi_nor_write_disable(nor); +	if (ret) +		goto out; +  	ret = spi_nor_wait_till_ready(nor);  	if (ret) -		goto sst_write_err; +		goto out;  	/* Write out trailing byte if it exists. */  	if (actual != len) { -		write_enable(nor); +		ret = spi_nor_write_enable(nor); +		if (ret) +			goto out;  		nor->program_opcode = SPINOR_OP_BP;  		ret = spi_nor_write_data(nor, to, 1, buf + actual);  		if (ret < 0) -			goto sst_write_err; -		WARN(ret != 1, "While writing 1 byte written %i bytes\n", -		     (int)ret); +			goto out; +		WARN(ret != 1, "While writing 1 byte written %i bytes\n", ret);  		ret = spi_nor_wait_till_ready(nor);  		if (ret) -			goto sst_write_err; -		write_disable(nor); +			goto out; +  		actual += 1; + +		ret = spi_nor_write_disable(nor);  	} -sst_write_err: +out:  	*retlen += actual;  	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);  	return ret; @@ -2701,7 +2859,10 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,  		addr = spi_nor_convert_addr(nor, addr); -		write_enable(nor); +		ret = spi_nor_write_enable(nor); +		if (ret) +			goto write_err; +  		ret = spi_nor_write_data(nor, addr, page_remain, buf + i);  		if (ret < 0)  			goto write_err; @@ -2722,13 +2883,21 @@ write_err:  static int spi_nor_check(struct spi_nor *nor)  {  	if (!nor->dev || -	    (!nor->spimem && -	    (!nor->read || !nor->write || !nor->read_reg || -	      !nor->write_reg))) { +	    (!nor->spimem && !nor->controller_ops) || +	    (!nor->spimem && nor->controller_ops && +	    (!nor->controller_ops->read || +	     !nor->controller_ops->write || +	     !nor->controller_ops->read_reg || +	     !nor->controller_ops->write_reg))) {  		pr_err("spi-nor: please fill all the necessary fields!\n");  		return -EINVAL;  	} +	if (nor->spimem && nor->controller_ops) { +		dev_err(nor->dev, "nor->spimem and nor->controller_ops are mutually exclusive, please set just one of them.\n"); +		return -EINVAL; +	} +  	return 0;  } @@ -2738,10 +2907,8 @@ static int s3an_nor_setup(struct spi_nor *nor,  	int ret;  	ret = spi_nor_xread_sr(nor, nor->bouncebuf); -	if (ret < 0) { -		dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret); +	if (ret)  		return ret; -	}  	nor->erase_opcode = SPINOR_OP_XSE;  	nor->program_opcode = SPINOR_OP_XPP; @@ -2865,7 +3032,7 @@ static int spi_nor_hwcaps_pp2cmd(u32 hwcaps)   */  static int spi_nor_read_raw(struct spi_nor *nor, u32 addr, size_t len, u8 *buf)  { -	int ret; +	ssize_t ret;  	while (len) {  		ret = spi_nor_read_data(nor, addr, len, buf); @@ -3489,20 +3656,39 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,  		break;  	case BFPT_DWORD15_QER_SR2_BIT1_BUGGY: +		/* +		 * Writing only one byte to the Status Register has the +		 * side-effect of clearing Status Register 2. +		 */  	case BFPT_DWORD15_QER_SR2_BIT1_NO_RD: -		params->quad_enable = spansion_no_read_cr_quad_enable; +		/* +		 * Read Configuration Register (35h) instruction is not +		 * supported. +		 */ +		nor->flags |= SNOR_F_HAS_16BIT_SR | SNOR_F_NO_READ_CR; +		params->quad_enable = spi_nor_sr2_bit1_quad_enable;  		break;  	case BFPT_DWORD15_QER_SR1_BIT6: -		params->quad_enable = macronix_quad_enable; +		nor->flags &= ~SNOR_F_HAS_16BIT_SR; +		params->quad_enable = spi_nor_sr1_bit6_quad_enable;  		break;  	case BFPT_DWORD15_QER_SR2_BIT7: -		params->quad_enable = sr2_bit7_quad_enable; +		nor->flags &= ~SNOR_F_HAS_16BIT_SR; +		params->quad_enable = spi_nor_sr2_bit7_quad_enable;  		break;  	case BFPT_DWORD15_QER_SR2_BIT1: -		params->quad_enable = spansion_read_cr_quad_enable; +		/* +		 * JESD216 rev B or later does not specify if writing only one +		 * byte to the Status Register clears or not the Status +		 * Register 2, so let's be cautious and keep the default +		 * assumption of a 16-bit Write Status (01h) command. +		 */ +		nor->flags |= SNOR_F_HAS_16BIT_SR; + +		params->quad_enable = spi_nor_sr2_bit1_quad_enable;  		break;  	default: @@ -4101,7 +4287,7 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor,  		err = spi_nor_read_sfdp(nor, sizeof(header),  					psize, param_headers);  		if (err < 0) { -			dev_err(dev, "failed to read SFDP parameter headers\n"); +			dev_dbg(dev, "failed to read SFDP parameter headers\n");  			goto exit;  		}  	} @@ -4348,7 +4534,7 @@ static int spi_nor_default_setup(struct spi_nor *nor,  	/* Select the (Fast) Read command. */  	err = spi_nor_select_read(nor, shared_mask);  	if (err) { -		dev_err(nor->dev, +		dev_dbg(nor->dev,  			"can't select read settings supported by both the SPI controller and memory.\n");  		return err;  	} @@ -4356,7 +4542,7 @@ static int spi_nor_default_setup(struct spi_nor *nor,  	/* Select the Page Program command. */  	err = spi_nor_select_pp(nor, shared_mask);  	if (err) { -		dev_err(nor->dev, +		dev_dbg(nor->dev,  			"can't select write settings supported by both the SPI controller and memory.\n");  		return err;  	} @@ -4364,7 +4550,7 @@ static int spi_nor_default_setup(struct spi_nor *nor,  	/* Select the Sector Erase command. */  	err = spi_nor_select_erase(nor);  	if (err) { -		dev_err(nor->dev, +		dev_dbg(nor->dev,  			"can't select erase settings supported by both the SPI controller and memory.\n");  		return err;  	} @@ -4381,12 +4567,32 @@ static int spi_nor_setup(struct spi_nor *nor,  	return nor->params.setup(nor, hwcaps);  } +static void atmel_set_default_init(struct spi_nor *nor) +{ +	nor->flags |= SNOR_F_HAS_LOCK; +} + +static void intel_set_default_init(struct spi_nor *nor) +{ +	nor->flags |= SNOR_F_HAS_LOCK; +} + +static void issi_set_default_init(struct spi_nor *nor) +{ +	nor->params.quad_enable = spi_nor_sr1_bit6_quad_enable; +} +  static void macronix_set_default_init(struct spi_nor *nor)  { -	nor->params.quad_enable = macronix_quad_enable; +	nor->params.quad_enable = spi_nor_sr1_bit6_quad_enable;  	nor->params.set_4byte = macronix_set_4byte;  } +static void sst_set_default_init(struct spi_nor *nor) +{ +	nor->flags |= SNOR_F_HAS_LOCK; +} +  static void st_micron_set_default_init(struct spi_nor *nor)  {  	nor->flags |= SNOR_F_HAS_LOCK; @@ -4408,6 +4614,18 @@ static void spi_nor_manufacturer_init_params(struct spi_nor *nor)  {  	/* Init flash parameters based on MFR */  	switch (JEDEC_MFR(nor->info)) { +	case SNOR_MFR_ATMEL: +		atmel_set_default_init(nor); +		break; + +	case SNOR_MFR_INTEL: +		intel_set_default_init(nor); +		break; + +	case SNOR_MFR_ISSI: +		issi_set_default_init(nor); +		break; +  	case SNOR_MFR_MACRONIX:  		macronix_set_default_init(nor);  		break; @@ -4417,6 +4635,10 @@ static void spi_nor_manufacturer_init_params(struct spi_nor *nor)  		st_micron_set_default_init(nor);  		break; +	case SNOR_MFR_SST: +		sst_set_default_init(nor); +		break; +  	case SNOR_MFR_WINBOND:  		winbond_set_default_init(nor);  		break; @@ -4465,9 +4687,11 @@ static void spi_nor_info_init_params(struct spi_nor *nor)  	u8 i, erase_mask;  	/* Initialize legacy flash parameters and settings. */ -	params->quad_enable = spansion_quad_enable; +	params->quad_enable = spi_nor_sr2_bit1_quad_enable;  	params->set_4byte = spansion_set_4byte;  	params->setup = spi_nor_default_setup; +	/* Default to 16-bit Write Status (01h) Command */ +	nor->flags |= SNOR_F_HAS_16BIT_SR;  	/* Set SPI NOR sizes. */  	params->size = (u64)info->sector_size * info->n_sectors; @@ -4675,25 +4899,36 @@ static int spi_nor_quad_enable(struct spi_nor *nor)  	return nor->params.quad_enable(nor);  } +/** + * spi_nor_unlock_all() - Unlocks the entire flash memory array. + * @nor:	pointer to a 'struct spi_nor'. + * + * Some SPI NOR flashes are write protected by default after a power-on reset + * cycle, in order to avoid inadvertent writes during power-up. Backward + * compatibility imposes to unlock the entire flash memory array at power-up + * by default. + */ +static int spi_nor_unlock_all(struct spi_nor *nor) +{ +	if (nor->flags & SNOR_F_HAS_LOCK) +		return spi_nor_unlock(&nor->mtd, 0, nor->params.size); + +	return 0; +} +  static int spi_nor_init(struct spi_nor *nor)  {  	int err; -	if (nor->clear_sr_bp) { -		if (nor->params.quad_enable == spansion_quad_enable) -			nor->clear_sr_bp = spi_nor_spansion_clear_sr_bp; - -		err = nor->clear_sr_bp(nor); -		if (err) { -			dev_err(nor->dev, -				"fail to clear block protection bits\n"); -			return err; -		} +	err = spi_nor_quad_enable(nor); +	if (err) { +		dev_dbg(nor->dev, "quad mode not supported\n"); +		return err;  	} -	err = spi_nor_quad_enable(nor); +	err = spi_nor_unlock_all(nor);  	if (err) { -		dev_err(nor->dev, "quad mode not supported\n"); +		dev_dbg(nor->dev, "Failed to unlock the entire flash memory array\n");  		return err;  	} @@ -4761,7 +4996,7 @@ static int spi_nor_set_addr_width(struct spi_nor *nor)  	}  	if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) { -		dev_err(nor->dev, "address width is too large: %u\n", +		dev_dbg(nor->dev, "address width is too large: %u\n",  			nor->addr_width);  		return -EINVAL;  	} @@ -4879,16 +5114,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,  	if (info->flags & SPI_NOR_HAS_LOCK)  		nor->flags |= SNOR_F_HAS_LOCK; -	/* -	 * Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up -	 * with the software protection bits set. -	 */ -	if (JEDEC_MFR(nor->info) == SNOR_MFR_ATMEL || -	    JEDEC_MFR(nor->info) == SNOR_MFR_INTEL || -	    JEDEC_MFR(nor->info) == SNOR_MFR_SST || -	    nor->info->flags & SPI_NOR_HAS_LOCK) -		nor->clear_sr_bp = spi_nor_clear_sr_bp; -  	/* Init flash parameters based on flash_info struct and SFDP */  	spi_nor_init_params(nor); diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c index a1dff92ceedf..0f847d510950 100644 --- a/drivers/mtd/ubi/debug.c +++ b/drivers/mtd/ubi/debug.c @@ -509,11 +509,9 @@ static const struct file_operations eraseblk_count_fops = {   */  int ubi_debugfs_init_dev(struct ubi_device *ubi)  { -	int err, n;  	unsigned long ubi_num = ubi->ubi_num; -	const char *fname; -	struct dentry *dent;  	struct ubi_debug_info *d = &ubi->dbg; +	int n;  	if (!IS_ENABLED(CONFIG_DEBUG_FS))  		return 0; @@ -522,95 +520,52 @@ int ubi_debugfs_init_dev(struct ubi_device *ubi)  		     ubi->ubi_num);  	if (n == UBI_DFS_DIR_LEN) {  		/* The array size is too small */ -		fname = UBI_DFS_DIR_NAME; -		dent = ERR_PTR(-EINVAL); -		goto out; +		return -EINVAL;  	} -	fname = d->dfs_dir_name; -	dent = debugfs_create_dir(fname, dfs_rootdir); -	if (IS_ERR_OR_NULL(dent)) -		goto out; -	d->dfs_dir = dent; - -	fname = "chk_gen"; -	dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, -				   &dfs_fops); -	if (IS_ERR_OR_NULL(dent)) -		goto out_remove; -	d->dfs_chk_gen = dent; - -	fname = "chk_io"; -	dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, -				   &dfs_fops); -	if (IS_ERR_OR_NULL(dent)) -		goto out_remove; -	d->dfs_chk_io = dent; - -	fname = "chk_fastmap"; -	dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, -				   &dfs_fops); -	if (IS_ERR_OR_NULL(dent)) -		goto out_remove; -	d->dfs_chk_fastmap = dent; - -	fname = "tst_disable_bgt"; -	dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, -				   &dfs_fops); -	if (IS_ERR_OR_NULL(dent)) -		goto out_remove; -	d->dfs_disable_bgt = dent; - -	fname = "tst_emulate_bitflips"; -	dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, -				   &dfs_fops); -	if (IS_ERR_OR_NULL(dent)) -		goto out_remove; -	d->dfs_emulate_bitflips = dent; - -	fname = "tst_emulate_io_failures"; -	dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, -				   &dfs_fops); -	if (IS_ERR_OR_NULL(dent)) -		goto out_remove; -	d->dfs_emulate_io_failures = dent; - -	fname = "tst_emulate_power_cut"; -	dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, -				   &dfs_fops); -	if (IS_ERR_OR_NULL(dent)) -		goto out_remove; -	d->dfs_emulate_power_cut = dent; - -	fname = "tst_emulate_power_cut_min"; -	dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, -				   &dfs_fops); -	if (IS_ERR_OR_NULL(dent)) -		goto out_remove; -	d->dfs_power_cut_min = dent; - -	fname = "tst_emulate_power_cut_max"; -	dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, -				   &dfs_fops); -	if (IS_ERR_OR_NULL(dent)) -		goto out_remove; -	d->dfs_power_cut_max = dent; - -	fname = "detailed_erase_block_info"; -	dent = debugfs_create_file(fname, S_IRUSR, d->dfs_dir, (void *)ubi_num, -				   &eraseblk_count_fops); -	if (IS_ERR_OR_NULL(dent)) -		goto out_remove; +	d->dfs_dir = debugfs_create_dir(d->dfs_dir_name, dfs_rootdir); -	return 0; +	d->dfs_chk_gen = debugfs_create_file("chk_gen", S_IWUSR, d->dfs_dir, +					     (void *)ubi_num, &dfs_fops); -out_remove: -	debugfs_remove_recursive(d->dfs_dir); -out: -	err = dent ? PTR_ERR(dent) : -ENODEV; -	ubi_err(ubi, "cannot create \"%s\" debugfs file or directory, error %d\n", -		fname, err); -	return err; +	d->dfs_chk_io = debugfs_create_file("chk_io", S_IWUSR, d->dfs_dir, +					    (void *)ubi_num, &dfs_fops); + +	d->dfs_chk_fastmap = debugfs_create_file("chk_fastmap", S_IWUSR, +						 d->dfs_dir, (void *)ubi_num, +						 &dfs_fops); + +	d->dfs_disable_bgt = debugfs_create_file("tst_disable_bgt", S_IWUSR, +						 d->dfs_dir, (void *)ubi_num, +						 &dfs_fops); + +	d->dfs_emulate_bitflips = debugfs_create_file("tst_emulate_bitflips", +						      S_IWUSR, d->dfs_dir, +						      (void *)ubi_num, +						      &dfs_fops); + +	d->dfs_emulate_io_failures = debugfs_create_file("tst_emulate_io_failures", +							 S_IWUSR, d->dfs_dir, +							 (void *)ubi_num, +							 &dfs_fops); + +	d->dfs_emulate_power_cut = debugfs_create_file("tst_emulate_power_cut", +						       S_IWUSR, d->dfs_dir, +						       (void *)ubi_num, +						       &dfs_fops); + +	d->dfs_power_cut_min = debugfs_create_file("tst_emulate_power_cut_min", +						   S_IWUSR, d->dfs_dir, +						   (void *)ubi_num, &dfs_fops); + +	d->dfs_power_cut_max = debugfs_create_file("tst_emulate_power_cut_max", +						   S_IWUSR, d->dfs_dir, +						   (void *)ubi_num, &dfs_fops); + +	debugfs_create_file("detailed_erase_block_info", S_IRUSR, d->dfs_dir, +			    (void *)ubi_num, &eraseblk_count_fops); + +	return 0;  }  /** |