diff options
Diffstat (limited to 'drivers/mtd/devices')
25 files changed, 7274 insertions, 5566 deletions
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 35081ce77fb..c49d0b127fe 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -1,5 +1,6 @@  menu "Self-contained MTD device drivers"  	depends on MTD!=n +	depends on HAS_IOMEM  config MTD_PMC551  	tristate "Ramix PMC551 PCI Mezzanine RAM card support" @@ -51,7 +52,7 @@ config MTD_MS02NV  config MTD_DATAFLASH  	tristate "Support for AT45xxx DataFlash" -	depends on SPI_MASTER && EXPERIMENTAL +	depends on SPI_MASTER  	help  	  This enables access to AT45xxx DataFlash chips, using SPI.  	  Sometimes DataFlash chips are packaged inside MMC-format @@ -70,7 +71,6 @@ config MTD_DATAFLASH_WRITE_VERIFY  config MTD_DATAFLASH_OTP  	bool "DataFlash OTP support (Security Register)"  	depends on MTD_DATAFLASH -	select HAVE_MTD_OTP  	help  	  Newer DataFlash chips (revisions C and D) support 128 bytes of  	  one-time-programmable (OTP) data.  The first half may be written @@ -80,7 +80,7 @@ config MTD_DATAFLASH_OTP  config MTD_M25P80  	tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)" -	depends on SPI_MASTER && EXPERIMENTAL +	depends on SPI_MASTER && MTD_SPI_NOR  	help  	  This enables access to most modern SPI flash chips, used for  	  program and data storage.   Series supported include Atmel AT26DF, @@ -95,12 +95,12 @@ config MTD_M25P80  	  if you want to specify device partitioning or to use a device which  	  doesn't support the JEDEC ID instruction. -config M25PXX_USE_FAST_READ -	bool "Use FAST_READ OPCode allowing SPI CLK <= 50MHz" -	depends on MTD_M25P80 +config MTD_SPEAR_SMI +	tristate "SPEAR MTD NOR Support through SMI controller" +	depends on PLAT_SPEAR  	default y  	help -	  This option enables FAST_READ access supported by ST M25Pxx. +	  This enable SNOR support on SPEAR platforms using SMI controller  config MTD_SST25L  	tristate "Support SST25L (non JEDEC) SPI Flash chips" @@ -112,6 +112,14 @@ config MTD_SST25L  	  Set up your spi devices with the right board-specific platform data,  	  if you want to specify device partitioning. +config MTD_BCM47XXSFLASH +	tristate "R/O support for serial flash on BCMA bus" +	depends on BCMA_SFLASH +	help +	  BCMA bus can have various flash memories attached, they are +	  registered by bcma as platform devices. This enables driver for +	  serial flash memories (only read-only mode is implemented). +  config MTD_SLRAM  	tristate "Uncached system RAM"  	help @@ -189,120 +197,32 @@ config MTD_BLOCK2MTD  comment "Disk-On-Chip Device Drivers" -config MTD_DOC2000 -	tristate "M-Systems Disk-On-Chip 2000 and Millennium (DEPRECATED)" -	select MTD_DOCPROBE -	select MTD_NAND_IDS +config MTD_DOCG3 +	tristate "M-Systems Disk-On-Chip G3" +	select BCH +	select BCH_CONST_PARAMS +	select BITREVERSE  	---help---  	  This provides an MTD device driver for the M-Systems DiskOnChip -	  2000 and Millennium devices.  Originally designed for the DiskOnChip -	  2000, it also now includes support for the DiskOnChip Millennium. -	  If you have problems with this driver and the DiskOnChip Millennium, -	  you may wish to try the alternative Millennium driver below. To use -	  the alternative driver, you will need to undefine DOC_SINGLE_DRIVER -	  in the <file:drivers/mtd/devices/docprobe.c> source code. - -	  If you use this device, you probably also want to enable the NFTL -	  'NAND Flash Translation Layer' option below, which is used to -	  emulate a block device by using a kind of file system on the flash -	  chips. - -	  NOTE: This driver is deprecated and will probably be removed soon. -	  Please try the new DiskOnChip driver under "NAND Flash Device -	  Drivers". - -config MTD_DOC2001 -	tristate "M-Systems Disk-On-Chip Millennium-only alternative driver (DEPRECATED)" -	select MTD_DOCPROBE -	select MTD_NAND_IDS -	---help--- -	  This provides an alternative MTD device driver for the M-Systems -	  DiskOnChip Millennium devices.  Use this if you have problems with -	  the combined DiskOnChip 2000 and Millennium driver above.  To get -	  the DiskOnChip probe code to load and use this driver instead of -	  the other one, you will need to undefine DOC_SINGLE_DRIVER near -	  the beginning of <file:drivers/mtd/devices/docprobe.c>. - -	  If you use this device, you probably also want to enable the NFTL -	  'NAND Flash Translation Layer' option below, which is used to -	  emulate a block device by using a kind of file system on the flash -	  chips. - -	  NOTE: This driver is deprecated and will probably be removed soon. -	  Please try the new DiskOnChip driver under "NAND Flash Device -	  Drivers". - -config MTD_DOC2001PLUS -	tristate "M-Systems Disk-On-Chip Millennium Plus" -	select MTD_DOCPROBE -	select MTD_NAND_IDS -	---help--- -	  This provides an MTD device driver for the M-Systems DiskOnChip -	  Millennium Plus devices. - -	  If you use this device, you probably also want to enable the INFTL -	  'Inverse NAND Flash Translation Layer' option below, which is used -	  to emulate a block device by using a kind of file system on the -	  flash chips. - -	  NOTE: This driver will soon be replaced by the new DiskOnChip driver -	  under "NAND Flash Device Drivers" (currently that driver does not -	  support all Millennium Plus devices). - -config MTD_DOCPROBE -	tristate -	select MTD_DOCECC - -config MTD_DOCECC -	tristate - -config MTD_DOCPROBE_ADVANCED -	bool "Advanced detection options for DiskOnChip" -	depends on MTD_DOCPROBE -	help -	  This option allows you to specify nonstandard address at which to -	  probe for a DiskOnChip, or to change the detection options.  You -	  are unlikely to need any of this unless you are using LinuxBIOS. -	  Say 'N'. - -config MTD_DOCPROBE_ADDRESS -	hex "Physical address of DiskOnChip" if MTD_DOCPROBE_ADVANCED -	depends on MTD_DOCPROBE -	default "0x0000" if MTD_DOCPROBE_ADVANCED -	default "0" if !MTD_DOCPROBE_ADVANCED -	---help--- -	  By default, the probe for DiskOnChip devices will look for a -	  DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000. -	  This option allows you to specify a single address at which to probe -	  for the device, which is useful if you have other devices in that -	  range which get upset when they are probed. - -	  (Note that on PowerPC, the normal probe will only check at -	  0xE4000000.) - -	  Normally, you should leave this set to zero, to allow the probe at -	  the normal addresses. - -config MTD_DOCPROBE_HIGH -	bool "Probe high addresses" -	depends on MTD_DOCPROBE_ADVANCED -	help -	  By default, the probe for DiskOnChip devices will look for a -	  DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000. -	  This option changes to make it probe between 0xFFFC8000 and -	  0xFFFEE000.  Unless you are using LinuxBIOS, this is unlikely to be -	  useful to you.  Say 'N'. - -config MTD_DOCPROBE_55AA -	bool "Probe for 0x55 0xAA BIOS Extension Signature" -	depends on MTD_DOCPROBE_ADVANCED -	help -	  Check for the 0x55 0xAA signature of a DiskOnChip, and do not -	  continue with probing if it is absent.  The signature will always be -	  present for a DiskOnChip 2000 or a normal DiskOnChip Millennium. -	  Only if you have overwritten the first block of a DiskOnChip -	  Millennium will it be absent.  Enable this option if you are using -	  LinuxBIOS or if you need to recover a DiskOnChip Millennium on which -	  you have managed to wipe the first block. +	  G3 devices. + +	  The driver provides access to G3 DiskOnChip, distributed by +	  M-Systems and now Sandisk. The support is very experimental, +	  and doesn't give access to any write operations. + +config MTD_ST_SPI_FSM +	tristate "ST Microelectronics SPI FSM Serial Flash Controller" +	depends on ARCH_STI +	help +	  This provides an MTD device driver for the ST Microelectronics +	  SPI Fast Sequence Mode (FSM) Serial Flash Controller and support +	  for a subset of connected Serial Flash devices. + +if MTD_DOCG3 +config BCH_CONST_M +	default 14 +config BCH_CONST_T +	default 4 +endif  endmenu diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index f3226b1d38f..c68868f6058 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -2,11 +2,7 @@  # linux/drivers/mtd/devices/Makefile  # -obj-$(CONFIG_MTD_DOC2000)	+= doc2000.o -obj-$(CONFIG_MTD_DOC2001)	+= doc2001.o -obj-$(CONFIG_MTD_DOC2001PLUS)	+= doc2001plus.o -obj-$(CONFIG_MTD_DOCPROBE)	+= docprobe.o -obj-$(CONFIG_MTD_DOCECC)	+= docecc.o +obj-$(CONFIG_MTD_DOCG3)		+= docg3.o  obj-$(CONFIG_MTD_SLRAM)		+= slram.o  obj-$(CONFIG_MTD_PHRAM)		+= phram.o  obj-$(CONFIG_MTD_PMC551)	+= pmc551.o @@ -16,4 +12,11 @@ obj-$(CONFIG_MTD_LART)		+= lart.o  obj-$(CONFIG_MTD_BLOCK2MTD)	+= block2mtd.o  obj-$(CONFIG_MTD_DATAFLASH)	+= mtd_dataflash.o  obj-$(CONFIG_MTD_M25P80)	+= m25p80.o +obj-$(CONFIG_MTD_NAND_OMAP_BCH)	+= elm.o +obj-$(CONFIG_MTD_SPEAR_SMI)	+= spear_smi.o  obj-$(CONFIG_MTD_SST25L)	+= sst25l.o +obj-$(CONFIG_MTD_BCM47XXSFLASH)	+= bcm47xxsflash.o +obj-$(CONFIG_MTD_ST_SPI_FSM)    += st_spi_fsm.o + + +CFLAGS_docg3.o			+= -I$(src) diff --git a/drivers/mtd/devices/bcm47xxsflash.c b/drivers/mtd/devices/bcm47xxsflash.c new file mode 100644 index 00000000000..77de29bc02b --- /dev/null +++ b/drivers/mtd/devices/bcm47xxsflash.c @@ -0,0 +1,340 @@ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/mtd/mtd.h> +#include <linux/platform_device.h> +#include <linux/bcma/bcma.h> + +#include "bcm47xxsflash.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Serial flash driver for BCMA bus"); + +static const char * const probes[] = { "bcm47xxpart", NULL }; + +/************************************************** + * Various helpers + **************************************************/ + +static void bcm47xxsflash_cmd(struct bcm47xxsflash *b47s, u32 opcode) +{ +	int i; + +	b47s->cc_write(b47s, BCMA_CC_FLASHCTL, BCMA_CC_FLASHCTL_START | opcode); +	for (i = 0; i < 1000; i++) { +		if (!(b47s->cc_read(b47s, BCMA_CC_FLASHCTL) & +		      BCMA_CC_FLASHCTL_BUSY)) +			return; +		cpu_relax(); +	} +	pr_err("Control command failed (timeout)!\n"); +} + +static int bcm47xxsflash_poll(struct bcm47xxsflash *b47s, int timeout) +{ +	unsigned long deadline = jiffies + timeout; + +	do { +		switch (b47s->type) { +		case BCM47XXSFLASH_TYPE_ST: +			bcm47xxsflash_cmd(b47s, OPCODE_ST_RDSR); +			if (!(b47s->cc_read(b47s, BCMA_CC_FLASHDATA) & +			      SR_ST_WIP)) +				return 0; +			break; +		case BCM47XXSFLASH_TYPE_ATMEL: +			bcm47xxsflash_cmd(b47s, OPCODE_AT_STATUS); +			if (b47s->cc_read(b47s, BCMA_CC_FLASHDATA) & +			    SR_AT_READY) +				return 0; +			break; +		} + +		cpu_relax(); +		udelay(1); +	} while (!time_after_eq(jiffies, deadline)); + +	pr_err("Timeout waiting for flash to be ready!\n"); + +	return -EBUSY; +} + +/************************************************** + * MTD ops + **************************************************/ + +static int bcm47xxsflash_erase(struct mtd_info *mtd, struct erase_info *erase) +{ +	struct bcm47xxsflash *b47s = mtd->priv; +	int err; + +	switch (b47s->type) { +	case BCM47XXSFLASH_TYPE_ST: +		bcm47xxsflash_cmd(b47s, OPCODE_ST_WREN); +		b47s->cc_write(b47s, BCMA_CC_FLASHADDR, erase->addr); +		/* Newer flashes have "sub-sectors" which can be erased +		 * independently with a new command: ST_SSE. The ST_SE command +		 * erases 64KB just as before. +		 */ +		if (b47s->blocksize < (64 * 1024)) +			bcm47xxsflash_cmd(b47s, OPCODE_ST_SSE); +		else +			bcm47xxsflash_cmd(b47s, OPCODE_ST_SE); +		break; +	case BCM47XXSFLASH_TYPE_ATMEL: +		b47s->cc_write(b47s, BCMA_CC_FLASHADDR, erase->addr << 1); +		bcm47xxsflash_cmd(b47s, OPCODE_AT_PAGE_ERASE); +		break; +	} + +	err = bcm47xxsflash_poll(b47s, HZ); +	if (err) +		erase->state = MTD_ERASE_FAILED; +	else +		erase->state = MTD_ERASE_DONE; + +	if (erase->callback) +		erase->callback(erase); + +	return err; +} + +static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len, +			      size_t *retlen, u_char *buf) +{ +	struct bcm47xxsflash *b47s = mtd->priv; + +	/* Check address range */ +	if ((from + len) > mtd->size) +		return -EINVAL; + +	memcpy_fromio(buf, (void __iomem *)KSEG0ADDR(b47s->window + from), +		      len); +	*retlen = len; + +	return len; +} + +static int bcm47xxsflash_write_st(struct mtd_info *mtd, u32 offset, size_t len, +				  const u_char *buf) +{ +	struct bcm47xxsflash *b47s = mtd->priv; +	int written = 0; + +	/* Enable writes */ +	bcm47xxsflash_cmd(b47s, OPCODE_ST_WREN); + +	/* Write first byte */ +	b47s->cc_write(b47s, BCMA_CC_FLASHADDR, offset); +	b47s->cc_write(b47s, BCMA_CC_FLASHDATA, *buf++); + +	/* Program page */ +	if (b47s->bcma_cc->core->id.rev < 20) { +		bcm47xxsflash_cmd(b47s, OPCODE_ST_PP); +		return 1; /* 1B written */ +	} + +	/* Program page and set CSA (on newer chips we can continue writing) */ +	bcm47xxsflash_cmd(b47s, OPCODE_ST_CSA | OPCODE_ST_PP); +	offset++; +	len--; +	written++; + +	while (len > 0) { +		/* Page boundary, another function call is needed */ +		if ((offset & 0xFF) == 0) +			break; + +		bcm47xxsflash_cmd(b47s, OPCODE_ST_CSA | *buf++); +		offset++; +		len--; +		written++; +	} + +	/* All done, drop CSA & poll */ +	b47s->cc_write(b47s, BCMA_CC_FLASHCTL, 0); +	udelay(1); +	if (bcm47xxsflash_poll(b47s, HZ / 10)) +		pr_err("Flash rejected dropping CSA\n"); + +	return written; +} + +static int bcm47xxsflash_write_at(struct mtd_info *mtd, u32 offset, size_t len, +				  const u_char *buf) +{ +	struct bcm47xxsflash *b47s = mtd->priv; +	u32 mask = b47s->blocksize - 1; +	u32 page = (offset & ~mask) << 1; +	u32 byte = offset & mask; +	int written = 0; + +	/* If we don't overwrite whole page, read it to the buffer first */ +	if (byte || (len < b47s->blocksize)) { +		int err; + +		b47s->cc_write(b47s, BCMA_CC_FLASHADDR, page); +		bcm47xxsflash_cmd(b47s, OPCODE_AT_BUF1_LOAD); +		/* 250 us for AT45DB321B */ +		err = bcm47xxsflash_poll(b47s, HZ / 1000); +		if (err) { +			pr_err("Timeout reading page 0x%X info buffer\n", page); +			return err; +		} +	} + +	/* Change buffer content with our data */ +	while (len > 0) { +		/* Page boundary, another function call is needed */ +		if (byte == b47s->blocksize) +			break; + +		b47s->cc_write(b47s, BCMA_CC_FLASHADDR, byte++); +		b47s->cc_write(b47s, BCMA_CC_FLASHDATA, *buf++); +		bcm47xxsflash_cmd(b47s, OPCODE_AT_BUF1_WRITE); +		len--; +		written++; +	} + +	/* Program page with the buffer content */ +	b47s->cc_write(b47s, BCMA_CC_FLASHADDR, page); +	bcm47xxsflash_cmd(b47s, OPCODE_AT_BUF1_PROGRAM); + +	return written; +} + +static int bcm47xxsflash_write(struct mtd_info *mtd, loff_t to, size_t len, +			       size_t *retlen, const u_char *buf) +{ +	struct bcm47xxsflash *b47s = mtd->priv; +	int written; + +	/* Writing functions can return without writing all passed data, for +	 * example when the hardware is too old or when we git page boundary. +	 */ +	while (len > 0) { +		switch (b47s->type) { +		case BCM47XXSFLASH_TYPE_ST: +			written = bcm47xxsflash_write_st(mtd, to, len, buf); +			break; +		case BCM47XXSFLASH_TYPE_ATMEL: +			written = bcm47xxsflash_write_at(mtd, to, len, buf); +			break; +		default: +			BUG_ON(1); +		} +		if (written < 0) { +			pr_err("Error writing at offset 0x%llX\n", to); +			return written; +		} +		to += (loff_t)written; +		len -= written; +		*retlen += written; +		buf += written; +	} + +	return 0; +} + +static void bcm47xxsflash_fill_mtd(struct bcm47xxsflash *b47s) +{ +	struct mtd_info *mtd = &b47s->mtd; + +	mtd->priv = b47s; +	mtd->name = "bcm47xxsflash"; +	mtd->owner = THIS_MODULE; + +	mtd->type = MTD_NORFLASH; +	mtd->flags = MTD_CAP_NORFLASH; +	mtd->size = b47s->size; +	mtd->erasesize = b47s->blocksize; +	mtd->writesize = 1; +	mtd->writebufsize = 1; + +	mtd->_erase = bcm47xxsflash_erase; +	mtd->_read = bcm47xxsflash_read; +	mtd->_write = bcm47xxsflash_write; +} + +/************************************************** + * BCMA + **************************************************/ + +static int bcm47xxsflash_bcma_cc_read(struct bcm47xxsflash *b47s, u16 offset) +{ +	return bcma_cc_read32(b47s->bcma_cc, offset); +} + +static void bcm47xxsflash_bcma_cc_write(struct bcm47xxsflash *b47s, u16 offset, +					u32 value) +{ +	bcma_cc_write32(b47s->bcma_cc, offset, value); +} + +static int bcm47xxsflash_bcma_probe(struct platform_device *pdev) +{ +	struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev); +	struct bcm47xxsflash *b47s; +	int err; + +	b47s = devm_kzalloc(&pdev->dev, sizeof(*b47s), GFP_KERNEL); +	if (!b47s) +		return -ENOMEM; +	sflash->priv = b47s; + +	b47s->bcma_cc = container_of(sflash, struct bcma_drv_cc, sflash); +	b47s->cc_read = bcm47xxsflash_bcma_cc_read; +	b47s->cc_write = bcm47xxsflash_bcma_cc_write; + +	switch (b47s->bcma_cc->capabilities & BCMA_CC_CAP_FLASHT) { +	case BCMA_CC_FLASHT_STSER: +		b47s->type = BCM47XXSFLASH_TYPE_ST; +		break; +	case BCMA_CC_FLASHT_ATSER: +		b47s->type = BCM47XXSFLASH_TYPE_ATMEL; +		break; +	} + +	b47s->window = sflash->window; +	b47s->blocksize = sflash->blocksize; +	b47s->numblocks = sflash->numblocks; +	b47s->size = sflash->size; +	bcm47xxsflash_fill_mtd(b47s); + +	err = mtd_device_parse_register(&b47s->mtd, probes, NULL, NULL, 0); +	if (err) { +		pr_err("Failed to register MTD device: %d\n", err); +		return err; +	} + +	if (bcm47xxsflash_poll(b47s, HZ / 10)) +		pr_warn("Serial flash busy\n"); + +	return 0; +} + +static int bcm47xxsflash_bcma_remove(struct platform_device *pdev) +{ +	struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev); +	struct bcm47xxsflash *b47s = sflash->priv; + +	mtd_device_unregister(&b47s->mtd); + +	return 0; +} + +static struct platform_driver bcma_sflash_driver = { +	.probe	= bcm47xxsflash_bcma_probe, +	.remove = bcm47xxsflash_bcma_remove, +	.driver = { +		.name = "bcma_sflash", +		.owner = THIS_MODULE, +	}, +}; + +/************************************************** + * Init + **************************************************/ + +module_platform_driver(bcma_sflash_driver); diff --git a/drivers/mtd/devices/bcm47xxsflash.h b/drivers/mtd/devices/bcm47xxsflash.h new file mode 100644 index 00000000000..fe93daf4f48 --- /dev/null +++ b/drivers/mtd/devices/bcm47xxsflash.h @@ -0,0 +1,76 @@ +#ifndef __BCM47XXSFLASH_H +#define __BCM47XXSFLASH_H + +#include <linux/mtd/mtd.h> + +/* Used for ST flashes only. */ +#define OPCODE_ST_WREN		0x0006		/* Write Enable */ +#define OPCODE_ST_WRDIS		0x0004		/* Write Disable */ +#define OPCODE_ST_RDSR		0x0105		/* Read Status Register */ +#define OPCODE_ST_WRSR		0x0101		/* Write Status Register */ +#define OPCODE_ST_READ		0x0303		/* Read Data Bytes */ +#define OPCODE_ST_PP		0x0302		/* Page Program */ +#define OPCODE_ST_SE		0x02d8		/* Sector Erase */ +#define OPCODE_ST_BE		0x00c7		/* Bulk Erase */ +#define OPCODE_ST_DP		0x00b9		/* Deep Power-down */ +#define OPCODE_ST_RES		0x03ab		/* Read Electronic Signature */ +#define OPCODE_ST_CSA		0x1000		/* Keep chip select asserted */ +#define OPCODE_ST_SSE		0x0220		/* Sub-sector Erase */ + +/* Used for Atmel flashes only. */ +#define OPCODE_AT_READ				0x07e8 +#define OPCODE_AT_PAGE_READ			0x07d2 +#define OPCODE_AT_STATUS			0x01d7 +#define OPCODE_AT_BUF1_WRITE			0x0384 +#define OPCODE_AT_BUF2_WRITE			0x0387 +#define OPCODE_AT_BUF1_ERASE_PROGRAM		0x0283 +#define OPCODE_AT_BUF2_ERASE_PROGRAM		0x0286 +#define OPCODE_AT_BUF1_PROGRAM			0x0288 +#define OPCODE_AT_BUF2_PROGRAM			0x0289 +#define OPCODE_AT_PAGE_ERASE			0x0281 +#define OPCODE_AT_BLOCK_ERASE			0x0250 +#define OPCODE_AT_BUF1_WRITE_ERASE_PROGRAM	0x0382 +#define OPCODE_AT_BUF2_WRITE_ERASE_PROGRAM	0x0385 +#define OPCODE_AT_BUF1_LOAD			0x0253 +#define OPCODE_AT_BUF2_LOAD			0x0255 +#define OPCODE_AT_BUF1_COMPARE			0x0260 +#define OPCODE_AT_BUF2_COMPARE			0x0261 +#define OPCODE_AT_BUF1_REPROGRAM		0x0258 +#define OPCODE_AT_BUF2_REPROGRAM		0x0259 + +/* Status register bits for ST flashes */ +#define SR_ST_WIP		0x01		/* Write In Progress */ +#define SR_ST_WEL		0x02		/* Write Enable Latch */ +#define SR_ST_BP_MASK		0x1c		/* Block Protect */ +#define SR_ST_BP_SHIFT		2 +#define SR_ST_SRWD		0x80		/* Status Register Write Disable */ + +/* Status register bits for Atmel flashes */ +#define SR_AT_READY		0x80 +#define SR_AT_MISMATCH		0x40 +#define SR_AT_ID_MASK		0x38 +#define SR_AT_ID_SHIFT		3 + +struct bcma_drv_cc; + +enum bcm47xxsflash_type { +	BCM47XXSFLASH_TYPE_ATMEL, +	BCM47XXSFLASH_TYPE_ST, +}; + +struct bcm47xxsflash { +	struct bcma_drv_cc *bcma_cc; +	int (*cc_read)(struct bcm47xxsflash *b47s, u16 offset); +	void (*cc_write)(struct bcm47xxsflash *b47s, u16 offset, u32 value); + +	enum bcm47xxsflash_type type; + +	u32 window; +	u32 blocksize; +	u16 numblocks; +	u32 size; + +	struct mtd_info mtd; +}; + +#endif /* BCM47XXSFLASH */ diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index 2cf0cc6a418..66f0405f7e5 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -6,6 +6,9 @@   *   * Licence: GPL   */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +  #include <linux/module.h>  #include <linux/fs.h>  #include <linux/blkdev.h> @@ -14,14 +17,10 @@  #include <linux/list.h>  #include <linux/init.h>  #include <linux/mtd/mtd.h> -#include <linux/buffer_head.h>  #include <linux/mutex.h>  #include <linux/mount.h>  #include <linux/slab.h> - -#define ERROR(fmt, args...) printk(KERN_ERR "block2mtd: " fmt "\n" , ## args) -#define INFO(fmt, args...) printk(KERN_INFO "block2mtd: " fmt "\n" , ## args) - +#include <linux/major.h>  /* Info for the block device */  struct block2mtd_dev { @@ -53,8 +52,6 @@ static int _block2mtd_erase(struct block2mtd_dev *dev, loff_t to, size_t len)  	while (pages) {  		page = page_read(mapping, index); -		if (!page) -			return -ENOMEM;  		if (IS_ERR(page))  			return PTR_ERR(page); @@ -65,6 +62,7 @@ static int _block2mtd_erase(struct block2mtd_dev *dev, loff_t to, size_t len)  				memset(page_address(page), 0xff, PAGE_SIZE);  				set_page_dirty(page);  				unlock_page(page); +				balance_dirty_pages_ratelimited(mapping);  				break;  			} @@ -86,7 +84,7 @@ static int block2mtd_erase(struct mtd_info *mtd, struct erase_info *instr)  	err = _block2mtd_erase(dev, from, len);  	mutex_unlock(&dev->write_mutex);  	if (err) { -		ERROR("erase failed err = %d", err); +		pr_err("erase failed err = %d\n", err);  		instr->state = MTD_ERASE_FAILED;  	} else  		instr->state = MTD_ERASE_DONE; @@ -105,14 +103,6 @@ static int block2mtd_read(struct mtd_info *mtd, loff_t from, size_t len,  	int offset = from & (PAGE_SIZE-1);  	int cpylen; -	if (from > mtd->size) -		return -EINVAL; -	if (from + len > mtd->size) -		len = mtd->size - from; - -	if (retlen) -		*retlen = 0; -  	while (len) {  		if ((offset + len) > PAGE_SIZE)  			cpylen = PAGE_SIZE - offset;	// multiple pages @@ -121,8 +111,6 @@ static int block2mtd_read(struct mtd_info *mtd, loff_t from, size_t len,  		len = len - cpylen;  		page = page_read(dev->blkdev->bd_inode->i_mapping, index); -		if (!page) -			return -ENOMEM;  		if (IS_ERR(page))  			return PTR_ERR(page); @@ -149,8 +137,6 @@ static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf,  	int offset = to & ~PAGE_MASK;	// page offset  	int cpylen; -	if (retlen) -		*retlen = 0;  	while (len) {  		if ((offset+len) > PAGE_SIZE)  			cpylen = PAGE_SIZE - offset;	// multiple pages @@ -159,8 +145,6 @@ static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf,  		len = len - cpylen;  		page = page_read(mapping, index); -		if (!page) -			return -ENOMEM;  		if (IS_ERR(page))  			return PTR_ERR(page); @@ -169,6 +153,7 @@ static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf,  			memcpy(page_address(page) + offset, buf, cpylen);  			set_page_dirty(page);  			unlock_page(page); +			balance_dirty_pages_ratelimited(mapping);  		}  		page_cache_release(page); @@ -189,13 +174,6 @@ static int block2mtd_write(struct mtd_info *mtd, loff_t to, size_t len,  	struct block2mtd_dev *dev = mtd->priv;  	int err; -	if (!len) -		return 0; -	if (to >= mtd->size) -		return -ENOSPC; -	if (to + len > mtd->size) -		len = mtd->size - to; -  	mutex_lock(&dev->write_mutex);  	err = _block2mtd_write(dev, buf, to, len, retlen);  	mutex_unlock(&dev->write_mutex); @@ -224,16 +202,16 @@ static void block2mtd_free_device(struct block2mtd_dev *dev)  	if (dev->blkdev) {  		invalidate_mapping_pages(dev->blkdev->bd_inode->i_mapping,  					0, -1); -		close_bdev_exclusive(dev->blkdev, FMODE_READ|FMODE_WRITE); +		blkdev_put(dev->blkdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL);  	}  	kfree(dev);  } -/* FIXME: ensure that mtd->size % erase_size == 0 */  static struct block2mtd_dev *add_device(char *devname, int erase_size)  { +	const fmode_t mode = FMODE_READ | FMODE_WRITE | FMODE_EXCL;  	struct block_device *bdev;  	struct block2mtd_dev *dev;  	char *name; @@ -246,7 +224,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)  		return NULL;  	/* Get a handle on the device */ -	bdev = open_bdev_exclusive(devname, FMODE_READ|FMODE_WRITE, NULL); +	bdev = blkdev_get_by_path(devname, mode, dev);  #ifndef MODULE  	if (IS_ERR(bdev)) { @@ -254,21 +232,25 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)  		   to resolve the device name by other means. */  		dev_t devt = name_to_dev_t(devname); -		if (devt) { -			bdev = open_by_devnum(devt, FMODE_WRITE | FMODE_READ); -		} +		if (devt) +			bdev = blkdev_get_by_dev(devt, mode, dev);  	}  #endif  	if (IS_ERR(bdev)) { -		ERROR("error: cannot open device %s", devname); -		goto devinit_err; +		pr_err("error: cannot open device %s\n", devname); +		goto err_free_block2mtd;  	}  	dev->blkdev = bdev;  	if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) { -		ERROR("attempting to use an MTD device as a block device"); -		goto devinit_err; +		pr_err("attempting to use an MTD device as a block device\n"); +		goto err_free_block2mtd; +	} + +	if ((long)dev->blkdev->bd_inode->i_size % erase_size) { +		pr_err("erasesize must be a divisor of device size\n"); +		goto err_free_block2mtd;  	}  	mutex_init(&dev->write_mutex); @@ -277,34 +259,37 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)  	/* make the name contain the block device in */  	name = kasprintf(GFP_KERNEL, "block2mtd: %s", devname);  	if (!name) -		goto devinit_err; +		goto err_destroy_mutex;  	dev->mtd.name = name;  	dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK;  	dev->mtd.erasesize = erase_size;  	dev->mtd.writesize = 1; +	dev->mtd.writebufsize = PAGE_SIZE;  	dev->mtd.type = MTD_RAM;  	dev->mtd.flags = MTD_CAP_RAM; -	dev->mtd.erase = block2mtd_erase; -	dev->mtd.write = block2mtd_write; -	dev->mtd.writev = default_mtd_writev; -	dev->mtd.sync = block2mtd_sync; -	dev->mtd.read = block2mtd_read; +	dev->mtd._erase = block2mtd_erase; +	dev->mtd._write = block2mtd_write; +	dev->mtd._sync = block2mtd_sync; +	dev->mtd._read = block2mtd_read;  	dev->mtd.priv = dev;  	dev->mtd.owner = THIS_MODULE; -	if (add_mtd_device(&dev->mtd)) { -		/* Device didnt get added, so free the entry */ -		goto devinit_err; +	if (mtd_device_register(&dev->mtd, NULL, 0)) { +		/* Device didn't get added, so free the entry */ +		goto err_destroy_mutex;  	}  	list_add(&dev->list, &blkmtd_device_list); -	INFO("mtd%d: [%s] erase_size = %dKiB [%d]", dev->mtd.index, -			dev->mtd.name + strlen("block2mtd: "), -			dev->mtd.erasesize >> 10, dev->mtd.erasesize); +	pr_info("mtd%d: [%s] erase_size = %dKiB [%d]\n", +		dev->mtd.index, +		dev->mtd.name + strlen("block2mtd: "), +		dev->mtd.erasesize >> 10, dev->mtd.erasesize);  	return dev; -devinit_err: +err_destroy_mutex: +	mutex_destroy(&dev->write_mutex); +err_free_block2mtd:  	block2mtd_free_device(dev);  	return NULL;  } @@ -361,17 +346,11 @@ static inline void kill_final_newline(char *str)  } -#define parse_err(fmt, args...) do {	\ -	ERROR(fmt, ## args);		\ -	return 0;			\ -} while (0) -  #ifndef MODULE  static int block2mtd_init_called = 0;  static char block2mtd_paramline[80 + 12]; /* 80 for device, 12 for erase size */  #endif -  static int block2mtd_setup2(const char *val)  {  	char buf[80 + 12]; /* 80 for device, 12 for erase size */ @@ -381,8 +360,10 @@ static int block2mtd_setup2(const char *val)  	size_t erase_size = PAGE_SIZE;  	int i, ret; -	if (strnlen(val, sizeof(buf)) >= sizeof(buf)) -		parse_err("parameter too long"); +	if (strnlen(val, sizeof(buf)) >= sizeof(buf)) { +		pr_err("parameter too long\n"); +		return 0; +	}  	strcpy(str, val);  	kill_final_newline(str); @@ -390,20 +371,27 @@ static int block2mtd_setup2(const char *val)  	for (i = 0; i < 2; i++)  		token[i] = strsep(&str, ","); -	if (str) -		parse_err("too many arguments"); +	if (str) { +		pr_err("too many arguments\n"); +		return 0; +	} -	if (!token[0]) -		parse_err("no argument"); +	if (!token[0]) { +		pr_err("no argument\n"); +		return 0; +	}  	name = token[0]; -	if (strlen(name) + 1 > 80) -		parse_err("device name too long"); +	if (strlen(name) + 1 > 80) { +		pr_err("device name too long\n"); +		return 0; +	}  	if (token[1]) {  		ret = parse_num(&erase_size, token[1]);  		if (ret) { -			parse_err("illegal erase size"); +			pr_err("illegal erase size\n"); +			return 0;  		}  	} @@ -457,7 +445,7 @@ static int __init block2mtd_init(void)  } -static void __devexit block2mtd_exit(void) +static void block2mtd_exit(void)  {  	struct list_head *pos, *next; @@ -465,9 +453,11 @@ static void __devexit block2mtd_exit(void)  	list_for_each_safe(pos, next, &blkmtd_device_list) {  		struct block2mtd_dev *dev = list_entry(pos, typeof(*dev), list);  		block2mtd_sync(&dev->mtd); -		del_mtd_device(&dev->mtd); -		INFO("mtd%d: [%s] removed", dev->mtd.index, -				dev->mtd.name + strlen("block2mtd: ")); +		mtd_device_unregister(&dev->mtd); +		mutex_destroy(&dev->write_mutex); +		pr_info("mtd%d: [%s] removed\n", +			dev->mtd.index, +			dev->mtd.name + strlen("block2mtd: "));  		list_del(&dev->list);  		block2mtd_free_device(dev);  	} diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c deleted file mode 100644 index 5bf5f460e13..00000000000 --- a/drivers/mtd/devices/doc2000.c +++ /dev/null @@ -1,1201 +0,0 @@ - -/* - * Linux driver for Disk-On-Chip 2000 and Millennium - * (c) 1999 Machine Vision Holdings, Inc. - * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <asm/errno.h> -#include <asm/io.h> -#include <asm/uaccess.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/sched.h> -#include <linux/init.h> -#include <linux/types.h> -#include <linux/bitops.h> -#include <linux/mutex.h> - -#include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> -#include <linux/mtd/doc2000.h> - -#define DOC_SUPPORT_2000 -#define DOC_SUPPORT_2000TSOP -#define DOC_SUPPORT_MILLENNIUM - -#ifdef DOC_SUPPORT_2000 -#define DoC_is_2000(doc) (doc->ChipID == DOC_ChipID_Doc2k) -#else -#define DoC_is_2000(doc) (0) -#endif - -#if defined(DOC_SUPPORT_2000TSOP) || defined(DOC_SUPPORT_MILLENNIUM) -#define DoC_is_Millennium(doc) (doc->ChipID == DOC_ChipID_DocMil) -#else -#define DoC_is_Millennium(doc) (0) -#endif - -/* #define ECC_DEBUG */ - -/* I have no idea why some DoC chips can not use memcpy_from|to_io(). - * This may be due to the different revisions of the ASIC controller built-in or - * simplily a QA/Bug issue. Who knows ?? If you have trouble, please uncomment - * this: - #undef USE_MEMCPY -*/ - -static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, -		    size_t *retlen, u_char *buf); -static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, -		     size_t *retlen, const u_char *buf); -static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, -			struct mtd_oob_ops *ops); -static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, -			 struct mtd_oob_ops *ops); -static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len, -			 size_t *retlen, const u_char *buf); -static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); - -static struct mtd_info *doc2klist = NULL; - -/* Perform the required delay cycles by reading from the appropriate register */ -static void DoC_Delay(struct DiskOnChip *doc, unsigned short cycles) -{ -	volatile char dummy; -	int i; - -	for (i = 0; i < cycles; i++) { -		if (DoC_is_Millennium(doc)) -			dummy = ReadDOC(doc->virtadr, NOP); -		else -			dummy = ReadDOC(doc->virtadr, DOCStatus); -	} - -} - -/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ -static int _DoC_WaitReady(struct DiskOnChip *doc) -{ -	void __iomem *docptr = doc->virtadr; -	unsigned long timeo = jiffies + (HZ * 10); - -	DEBUG(MTD_DEBUG_LEVEL3, -	      "_DoC_WaitReady called for out-of-line wait\n"); - -	/* Out-of-line routine to wait for chip response */ -	while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { -		/* issue 2 read from NOP register after reading from CDSNControl register -	   	see Software Requirement 11.4 item 2. */ -		DoC_Delay(doc, 2); - -		if (time_after(jiffies, timeo)) { -			DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n"); -			return -EIO; -		} -		udelay(1); -		cond_resched(); -	} - -	return 0; -} - -static inline int DoC_WaitReady(struct DiskOnChip *doc) -{ -	void __iomem *docptr = doc->virtadr; - -	/* This is inline, to optimise the common case, where it's ready instantly */ -	int ret = 0; - -	/* 4 read form NOP register should be issued in prior to the read from CDSNControl -	   see Software Requirement 11.4 item 2. */ -	DoC_Delay(doc, 4); - -	if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) -		/* Call the out-of-line routine to wait */ -		ret = _DoC_WaitReady(doc); - -	/* issue 2 read from NOP register after reading from CDSNControl register -	   see Software Requirement 11.4 item 2. */ -	DoC_Delay(doc, 2); - -	return ret; -} - -/* DoC_Command: Send a flash command to the flash chip through the CDSN Slow IO register to -   bypass the internal pipeline. Each of 4 delay cycles (read from the NOP register) is -   required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */ - -static int DoC_Command(struct DiskOnChip *doc, unsigned char command, -			      unsigned char xtraflags) -{ -	void __iomem *docptr = doc->virtadr; - -	if (DoC_is_2000(doc)) -		xtraflags |= CDSN_CTRL_FLASH_IO; - -	/* Assert the CLE (Command Latch Enable) line to the flash chip */ -	WriteDOC(xtraflags | CDSN_CTRL_CLE | CDSN_CTRL_CE, docptr, CDSNControl); -	DoC_Delay(doc, 4);	/* Software requirement 11.4.3 for Millennium */ - -	if (DoC_is_Millennium(doc)) -		WriteDOC(command, docptr, CDSNSlowIO); - -	/* Send the command */ -	WriteDOC_(command, docptr, doc->ioreg); -	if (DoC_is_Millennium(doc)) -		WriteDOC(command, docptr, WritePipeTerm); - -	/* Lower the CLE line */ -	WriteDOC(xtraflags | CDSN_CTRL_CE, docptr, CDSNControl); -	DoC_Delay(doc, 4);	/* Software requirement 11.4.3 for Millennium */ - -	/* Wait for the chip to respond - Software requirement 11.4.1 (extended for any command) */ -	return DoC_WaitReady(doc); -} - -/* DoC_Address: Set the current address for the flash chip through the CDSN Slow IO register to -   bypass the internal pipeline. Each of 4 delay cycles (read from the NOP register) is -   required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */ - -static int DoC_Address(struct DiskOnChip *doc, int numbytes, unsigned long ofs, -		       unsigned char xtraflags1, unsigned char xtraflags2) -{ -	int i; -	void __iomem *docptr = doc->virtadr; - -	if (DoC_is_2000(doc)) -		xtraflags1 |= CDSN_CTRL_FLASH_IO; - -	/* Assert the ALE (Address Latch Enable) line to the flash chip */ -	WriteDOC(xtraflags1 | CDSN_CTRL_ALE | CDSN_CTRL_CE, docptr, CDSNControl); - -	DoC_Delay(doc, 4);	/* Software requirement 11.4.3 for Millennium */ - -	/* Send the address */ -	/* Devices with 256-byte page are addressed as: -	   Column (bits 0-7), Page (bits 8-15, 16-23, 24-31) -	   * there is no device on the market with page256 -	   and more than 24 bits. -	   Devices with 512-byte page are addressed as: -	   Column (bits 0-7), Page (bits 9-16, 17-24, 25-31) -	   * 25-31 is sent only if the chip support it. -	   * bit 8 changes the read command to be sent -	   (NAND_CMD_READ0 or NAND_CMD_READ1). -	 */ - -	if (numbytes == ADDR_COLUMN || numbytes == ADDR_COLUMN_PAGE) { -		if (DoC_is_Millennium(doc)) -			WriteDOC(ofs & 0xff, docptr, CDSNSlowIO); -		WriteDOC_(ofs & 0xff, docptr, doc->ioreg); -	} - -	if (doc->page256) { -		ofs = ofs >> 8; -	} else { -		ofs = ofs >> 9; -	} - -	if (numbytes == ADDR_PAGE || numbytes == ADDR_COLUMN_PAGE) { -		for (i = 0; i < doc->pageadrlen; i++, ofs = ofs >> 8) { -			if (DoC_is_Millennium(doc)) -				WriteDOC(ofs & 0xff, docptr, CDSNSlowIO); -			WriteDOC_(ofs & 0xff, docptr, doc->ioreg); -		} -	} - -	if (DoC_is_Millennium(doc)) -		WriteDOC(ofs & 0xff, docptr, WritePipeTerm); - -	DoC_Delay(doc, 2);	/* Needed for some slow flash chips. mf. */ - -	/* FIXME: The SlowIO's for millennium could be replaced by -	   a single WritePipeTerm here. mf. */ - -	/* Lower the ALE line */ -	WriteDOC(xtraflags1 | xtraflags2 | CDSN_CTRL_CE, docptr, -		 CDSNControl); - -	DoC_Delay(doc, 4);	/* Software requirement 11.4.3 for Millennium */ - -	/* Wait for the chip to respond - Software requirement 11.4.1 */ -	return DoC_WaitReady(doc); -} - -/* Read a buffer from DoC, taking care of Millennium odditys */ -static void DoC_ReadBuf(struct DiskOnChip *doc, u_char * buf, int len) -{ -	volatile int dummy; -	int modulus = 0xffff; -	void __iomem *docptr = doc->virtadr; -	int i; - -	if (len <= 0) -		return; - -	if (DoC_is_Millennium(doc)) { -		/* Read the data via the internal pipeline through CDSN IO register, -		   see Pipelined Read Operations 11.3 */ -		dummy = ReadDOC(docptr, ReadPipeInit); - -		/* Millennium should use the LastDataRead register - Pipeline Reads */ -		len--; - -		/* This is needed for correctly ECC calculation */ -		modulus = 0xff; -	} - -	for (i = 0; i < len; i++) -		buf[i] = ReadDOC_(docptr, doc->ioreg + (i & modulus)); - -	if (DoC_is_Millennium(doc)) { -		buf[i] = ReadDOC(docptr, LastDataRead); -	} -} - -/* Write a buffer to DoC, taking care of Millennium odditys */ -static void DoC_WriteBuf(struct DiskOnChip *doc, const u_char * buf, int len) -{ -	void __iomem *docptr = doc->virtadr; -	int i; - -	if (len <= 0) -		return; - -	for (i = 0; i < len; i++) -		WriteDOC_(buf[i], docptr, doc->ioreg + i); - -	if (DoC_is_Millennium(doc)) { -		WriteDOC(0x00, docptr, WritePipeTerm); -	} -} - - -/* DoC_SelectChip: Select a given flash chip within the current floor */ - -static inline int DoC_SelectChip(struct DiskOnChip *doc, int chip) -{ -	void __iomem *docptr = doc->virtadr; - -	/* Software requirement 11.4.4 before writing DeviceSelect */ -	/* Deassert the CE line to eliminate glitches on the FCE# outputs */ -	WriteDOC(CDSN_CTRL_WP, docptr, CDSNControl); -	DoC_Delay(doc, 4);	/* Software requirement 11.4.3 for Millennium */ - -	/* Select the individual flash chip requested */ -	WriteDOC(chip, docptr, CDSNDeviceSelect); -	DoC_Delay(doc, 4); - -	/* Reassert the CE line */ -	WriteDOC(CDSN_CTRL_CE | CDSN_CTRL_FLASH_IO | CDSN_CTRL_WP, docptr, -		 CDSNControl); -	DoC_Delay(doc, 4);	/* Software requirement 11.4.3 for Millennium */ - -	/* Wait for it to be ready */ -	return DoC_WaitReady(doc); -} - -/* DoC_SelectFloor: Select a given floor (bank of flash chips) */ - -static inline int DoC_SelectFloor(struct DiskOnChip *doc, int floor) -{ -	void __iomem *docptr = doc->virtadr; - -	/* Select the floor (bank) of chips required */ -	WriteDOC(floor, docptr, FloorSelect); - -	/* Wait for the chip to be ready */ -	return DoC_WaitReady(doc); -} - -/* DoC_IdentChip: Identify a given NAND chip given {floor,chip} */ - -static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) -{ -	int mfr, id, i, j; -	volatile char dummy; - -	/* Page in the required floor/chip */ -	DoC_SelectFloor(doc, floor); -	DoC_SelectChip(doc, chip); - -	/* Reset the chip */ -	if (DoC_Command(doc, NAND_CMD_RESET, CDSN_CTRL_WP)) { -		DEBUG(MTD_DEBUG_LEVEL2, -		      "DoC_Command (reset) for %d,%d returned true\n", -		      floor, chip); -		return 0; -	} - - -	/* Read the NAND chip ID: 1. Send ReadID command */ -	if (DoC_Command(doc, NAND_CMD_READID, CDSN_CTRL_WP)) { -		DEBUG(MTD_DEBUG_LEVEL2, -		      "DoC_Command (ReadID) for %d,%d returned true\n", -		      floor, chip); -		return 0; -	} - -	/* Read the NAND chip ID: 2. Send address byte zero */ -	DoC_Address(doc, ADDR_COLUMN, 0, CDSN_CTRL_WP, 0); - -	/* Read the manufacturer and device id codes from the device */ - -	if (DoC_is_Millennium(doc)) { -		DoC_Delay(doc, 2); -		dummy = ReadDOC(doc->virtadr, ReadPipeInit); -		mfr = ReadDOC(doc->virtadr, LastDataRead); - -		DoC_Delay(doc, 2); -		dummy = ReadDOC(doc->virtadr, ReadPipeInit); -		id = ReadDOC(doc->virtadr, LastDataRead); -	} else { -		/* CDSN Slow IO register see Software Req 11.4 item 5. */ -		dummy = ReadDOC(doc->virtadr, CDSNSlowIO); -		DoC_Delay(doc, 2); -		mfr = ReadDOC_(doc->virtadr, doc->ioreg); - -		/* CDSN Slow IO register see Software Req 11.4 item 5. */ -		dummy = ReadDOC(doc->virtadr, CDSNSlowIO); -		DoC_Delay(doc, 2); -		id = ReadDOC_(doc->virtadr, doc->ioreg); -	} - -	/* No response - return failure */ -	if (mfr == 0xff || mfr == 0) -		return 0; - -	/* Check it's the same as the first chip we identified. -	 * M-Systems say that any given DiskOnChip device should only -	 * contain _one_ type of flash part, although that's not a -	 * hardware restriction. */ -	if (doc->mfr) { -		if (doc->mfr == mfr && doc->id == id) -			return 1;	/* This is the same as the first */ -		else -			printk(KERN_WARNING -			       "Flash chip at floor %d, chip %d is different:\n", -			       floor, chip); -	} - -	/* Print and store the manufacturer and ID codes. */ -	for (i = 0; nand_flash_ids[i].name != NULL; i++) { -		if (id == nand_flash_ids[i].id) { -			/* Try to identify manufacturer */ -			for (j = 0; nand_manuf_ids[j].id != 0x0; j++) { -				if (nand_manuf_ids[j].id == mfr) -					break; -			} -			printk(KERN_INFO -			       "Flash chip found: Manufacturer ID: %2.2X, " -			       "Chip ID: %2.2X (%s:%s)\n", mfr, id, -			       nand_manuf_ids[j].name, nand_flash_ids[i].name); -			if (!doc->mfr) { -				doc->mfr = mfr; -				doc->id = id; -				doc->chipshift = -					ffs((nand_flash_ids[i].chipsize << 20)) - 1; -				doc->page256 = (nand_flash_ids[i].pagesize == 256) ? 1 : 0; -				doc->pageadrlen = doc->chipshift > 25 ? 3 : 2; -				doc->erasesize = -				    nand_flash_ids[i].erasesize; -				return 1; -			} -			return 0; -		} -	} - - -	/* We haven't fully identified the chip. Print as much as we know. */ -	printk(KERN_WARNING "Unknown flash chip found: %2.2X %2.2X\n", -	       id, mfr); - -	printk(KERN_WARNING "Please report to dwmw2@infradead.org\n"); -	return 0; -} - -/* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */ - -static void DoC_ScanChips(struct DiskOnChip *this, int maxchips) -{ -	int floor, chip; -	int numchips[MAX_FLOORS]; -	int ret = 1; - -	this->numchips = 0; -	this->mfr = 0; -	this->id = 0; - -	/* For each floor, find the number of valid chips it contains */ -	for (floor = 0; floor < MAX_FLOORS; floor++) { -		ret = 1; -		numchips[floor] = 0; -		for (chip = 0; chip < maxchips && ret != 0; chip++) { - -			ret = DoC_IdentChip(this, floor, chip); -			if (ret) { -				numchips[floor]++; -				this->numchips++; -			} -		} -	} - -	/* If there are none at all that we recognise, bail */ -	if (!this->numchips) { -		printk(KERN_NOTICE "No flash chips recognised.\n"); -		return; -	} - -	/* Allocate an array to hold the information for each chip */ -	this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL); -	if (!this->chips) { -		printk(KERN_NOTICE "No memory for allocating chip info structures\n"); -		return; -	} - -	ret = 0; - -	/* Fill out the chip array with {floor, chipno} for each -	 * detected chip in the device. */ -	for (floor = 0; floor < MAX_FLOORS; floor++) { -		for (chip = 0; chip < numchips[floor]; chip++) { -			this->chips[ret].floor = floor; -			this->chips[ret].chip = chip; -			this->chips[ret].curadr = 0; -			this->chips[ret].curmode = 0x50; -			ret++; -		} -	} - -	/* Calculate and print the total size of the device */ -	this->totlen = this->numchips * (1 << this->chipshift); - -	printk(KERN_INFO "%d flash chips found. Total DiskOnChip size: %ld MiB\n", -	       this->numchips, this->totlen >> 20); -} - -static int DoC2k_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2) -{ -	int tmp1, tmp2, retval; -	if (doc1->physadr == doc2->physadr) -		return 1; - -	/* Use the alias resolution register which was set aside for this -	 * purpose. If it's value is the same on both chips, they might -	 * be the same chip, and we write to one and check for a change in -	 * the other. It's unclear if this register is usuable in the -	 * DoC 2000 (it's in the Millennium docs), but it seems to work. */ -	tmp1 = ReadDOC(doc1->virtadr, AliasResolution); -	tmp2 = ReadDOC(doc2->virtadr, AliasResolution); -	if (tmp1 != tmp2) -		return 0; - -	WriteDOC((tmp1 + 1) % 0xff, doc1->virtadr, AliasResolution); -	tmp2 = ReadDOC(doc2->virtadr, AliasResolution); -	if (tmp2 == (tmp1 + 1) % 0xff) -		retval = 1; -	else -		retval = 0; - -	/* Restore register contents.  May not be necessary, but do it just to -	 * be safe. */ -	WriteDOC(tmp1, doc1->virtadr, AliasResolution); - -	return retval; -} - -/* This routine is found from the docprobe code by symbol_get(), - * which will bump the use count of this module. */ -void DoC2k_init(struct mtd_info *mtd) -{ -	struct DiskOnChip *this = mtd->priv; -	struct DiskOnChip *old = NULL; -	int maxchips; - -	/* We must avoid being called twice for the same device. */ - -	if (doc2klist) -		old = doc2klist->priv; - -	while (old) { -		if (DoC2k_is_alias(old, this)) { -			printk(KERN_NOTICE -			       "Ignoring DiskOnChip 2000 at 0x%lX - already configured\n", -			       this->physadr); -			iounmap(this->virtadr); -			kfree(mtd); -			return; -		} -		if (old->nextdoc) -			old = old->nextdoc->priv; -		else -			old = NULL; -	} - - -	switch (this->ChipID) { -	case DOC_ChipID_Doc2kTSOP: -		mtd->name = "DiskOnChip 2000 TSOP"; -		this->ioreg = DoC_Mil_CDSN_IO; -		/* Pretend it's a Millennium */ -		this->ChipID = DOC_ChipID_DocMil; -		maxchips = MAX_CHIPS; -		break; -	case DOC_ChipID_Doc2k: -		mtd->name = "DiskOnChip 2000"; -		this->ioreg = DoC_2k_CDSN_IO; -		maxchips = MAX_CHIPS; -		break; -	case DOC_ChipID_DocMil: -		mtd->name = "DiskOnChip Millennium"; -		this->ioreg = DoC_Mil_CDSN_IO; -		maxchips = MAX_CHIPS_MIL; -		break; -	default: -		printk("Unknown ChipID 0x%02x\n", this->ChipID); -		kfree(mtd); -		iounmap(this->virtadr); -		return; -	} - -	printk(KERN_NOTICE "%s found at address 0x%lX\n", mtd->name, -	       this->physadr); - -	mtd->type = MTD_NANDFLASH; -	mtd->flags = MTD_CAP_NANDFLASH; -	mtd->size = 0; -	mtd->erasesize = 0; -	mtd->writesize = 512; -	mtd->oobsize = 16; -	mtd->owner = THIS_MODULE; -	mtd->erase = doc_erase; -	mtd->point = NULL; -	mtd->unpoint = NULL; -	mtd->read = doc_read; -	mtd->write = doc_write; -	mtd->read_oob = doc_read_oob; -	mtd->write_oob = doc_write_oob; -	mtd->sync = NULL; - -	this->totlen = 0; -	this->numchips = 0; - -	this->curfloor = -1; -	this->curchip = -1; -	mutex_init(&this->lock); - -	/* Ident all the chips present. */ -	DoC_ScanChips(this, maxchips); - -	if (!this->totlen) { -		kfree(mtd); -		iounmap(this->virtadr); -	} else { -		this->nextdoc = doc2klist; -		doc2klist = mtd; -		mtd->size = this->totlen; -		mtd->erasesize = this->erasesize; -		add_mtd_device(mtd); -		return; -	} -} -EXPORT_SYMBOL_GPL(DoC2k_init); - -static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, -		    size_t * retlen, u_char * buf) -{ -	struct DiskOnChip *this = mtd->priv; -	void __iomem *docptr = this->virtadr; -	struct Nand *mychip; -	unsigned char syndrome[6], eccbuf[6]; -	volatile char dummy; -	int i, len256 = 0, ret=0; -	size_t left = len; - -	/* Don't allow read past end of device */ -	if (from >= this->totlen) -		return -EINVAL; - -	mutex_lock(&this->lock); - -	*retlen = 0; -	while (left) { -		len = left; - -		/* Don't allow a single read to cross a 512-byte block boundary */ -		if (from + len > ((from | 0x1ff) + 1)) -			len = ((from | 0x1ff) + 1) - from; - -		/* The ECC will not be calculated correctly if less than 512 is read */ -		if (len != 0x200) -			printk(KERN_WARNING -			       "ECC needs a full sector read (adr: %lx size %lx)\n", -			       (long) from, (long) len); - -		/* printk("DoC_Read (adr: %lx size %lx)\n", (long) from, (long) len); */ - - -		/* Find the chip which is to be used and select it */ -		mychip = &this->chips[from >> (this->chipshift)]; - -		if (this->curfloor != mychip->floor) { -			DoC_SelectFloor(this, mychip->floor); -			DoC_SelectChip(this, mychip->chip); -		} else if (this->curchip != mychip->chip) { -			DoC_SelectChip(this, mychip->chip); -		} - -		this->curfloor = mychip->floor; -		this->curchip = mychip->chip; - -		DoC_Command(this, -			    (!this->page256 -			     && (from & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0, -			    CDSN_CTRL_WP); -		DoC_Address(this, ADDR_COLUMN_PAGE, from, CDSN_CTRL_WP, -			    CDSN_CTRL_ECC_IO); - -		/* Prime the ECC engine */ -		WriteDOC(DOC_ECC_RESET, docptr, ECCConf); -		WriteDOC(DOC_ECC_EN, docptr, ECCConf); - -		/* treat crossing 256-byte sector for 2M x 8bits devices */ -		if (this->page256 && from + len > (from | 0xff) + 1) { -			len256 = (from | 0xff) + 1 - from; -			DoC_ReadBuf(this, buf, len256); - -			DoC_Command(this, NAND_CMD_READ0, CDSN_CTRL_WP); -			DoC_Address(this, ADDR_COLUMN_PAGE, from + len256, -				    CDSN_CTRL_WP, CDSN_CTRL_ECC_IO); -		} - -		DoC_ReadBuf(this, &buf[len256], len - len256); - -		/* Let the caller know we completed it */ -		*retlen += len; - -		/* Read the ECC data through the DiskOnChip ECC logic */ -		/* Note: this will work even with 2M x 8bit devices as   */ -		/*       they have 8 bytes of OOB per 256 page. mf.      */ -		DoC_ReadBuf(this, eccbuf, 6); - -		/* Flush the pipeline */ -		if (DoC_is_Millennium(this)) { -			dummy = ReadDOC(docptr, ECCConf); -			dummy = ReadDOC(docptr, ECCConf); -			i = ReadDOC(docptr, ECCConf); -		} else { -			dummy = ReadDOC(docptr, 2k_ECCStatus); -			dummy = ReadDOC(docptr, 2k_ECCStatus); -			i = ReadDOC(docptr, 2k_ECCStatus); -		} - -		/* Check the ECC Status */ -		if (i & 0x80) { -			int nb_errors; -			/* There was an ECC error */ -#ifdef ECC_DEBUG -			printk(KERN_ERR "DiskOnChip ECC Error: Read at %lx\n", (long)from); -#endif -			/* Read the ECC syndrom through the DiskOnChip ECC -			   logic.  These syndrome will be all ZERO when there -			   is no error */ -			for (i = 0; i < 6; i++) { -				syndrome[i] = -					ReadDOC(docptr, ECCSyndrome0 + i); -			} -			nb_errors = doc_decode_ecc(buf, syndrome); - -#ifdef ECC_DEBUG -			printk(KERN_ERR "Errors corrected: %x\n", nb_errors); -#endif -			if (nb_errors < 0) { -				/* We return error, but have actually done the -				   read. Not that this can be told to -				   user-space, via sys_read(), but at least -				   MTD-aware stuff can know about it by -				   checking *retlen */ -				ret = -EIO; -			} -		} - -#ifdef PSYCHO_DEBUG -		printk(KERN_DEBUG "ECC DATA at %lxB: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", -		       (long)from, eccbuf[0], eccbuf[1], eccbuf[2], -		       eccbuf[3], eccbuf[4], eccbuf[5]); -#endif - -		/* disable the ECC engine */ -		WriteDOC(DOC_ECC_DIS, docptr , ECCConf); - -		/* according to 11.4.1, we need to wait for the busy line -	         * drop if we read to the end of the page.  */ -		if(0 == ((from + len) & 0x1ff)) -		{ -		    DoC_WaitReady(this); -		} - -		from += len; -		left -= len; -		buf += len; -	} - -	mutex_unlock(&this->lock); - -	return ret; -} - -static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, -		     size_t * retlen, const u_char * buf) -{ -	struct DiskOnChip *this = mtd->priv; -	int di; /* Yes, DI is a hangover from when I was disassembling the binary driver */ -	void __iomem *docptr = this->virtadr; -	unsigned char eccbuf[6]; -	volatile char dummy; -	int len256 = 0; -	struct Nand *mychip; -	size_t left = len; -	int status; - -	/* Don't allow write past end of device */ -	if (to >= this->totlen) -		return -EINVAL; - -	mutex_lock(&this->lock); - -	*retlen = 0; -	while (left) { -		len = left; - -		/* Don't allow a single write to cross a 512-byte block boundary */ -		if (to + len > ((to | 0x1ff) + 1)) -			len = ((to | 0x1ff) + 1) - to; - -		/* The ECC will not be calculated correctly if less than 512 is written */ -/* DBB- -		if (len != 0x200 && eccbuf) -			printk(KERN_WARNING -			       "ECC needs a full sector write (adr: %lx size %lx)\n", -			       (long) to, (long) len); -   -DBB */ - -		/* printk("DoC_Write (adr: %lx size %lx)\n", (long) to, (long) len); */ - -		/* Find the chip which is to be used and select it */ -		mychip = &this->chips[to >> (this->chipshift)]; - -		if (this->curfloor != mychip->floor) { -			DoC_SelectFloor(this, mychip->floor); -			DoC_SelectChip(this, mychip->chip); -		} else if (this->curchip != mychip->chip) { -			DoC_SelectChip(this, mychip->chip); -		} - -		this->curfloor = mychip->floor; -		this->curchip = mychip->chip; - -		/* Set device to main plane of flash */ -		DoC_Command(this, NAND_CMD_RESET, CDSN_CTRL_WP); -		DoC_Command(this, -			    (!this->page256 -			     && (to & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0, -			    CDSN_CTRL_WP); - -		DoC_Command(this, NAND_CMD_SEQIN, 0); -		DoC_Address(this, ADDR_COLUMN_PAGE, to, 0, CDSN_CTRL_ECC_IO); - -		/* Prime the ECC engine */ -		WriteDOC(DOC_ECC_RESET, docptr, ECCConf); -		WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf); - -		/* treat crossing 256-byte sector for 2M x 8bits devices */ -		if (this->page256 && to + len > (to | 0xff) + 1) { -			len256 = (to | 0xff) + 1 - to; -			DoC_WriteBuf(this, buf, len256); - -			DoC_Command(this, NAND_CMD_PAGEPROG, 0); - -			DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP); -			/* There's an implicit DoC_WaitReady() in DoC_Command */ - -			dummy = ReadDOC(docptr, CDSNSlowIO); -			DoC_Delay(this, 2); - -			if (ReadDOC_(docptr, this->ioreg) & 1) { -				printk(KERN_ERR "Error programming flash\n"); -				/* Error in programming */ -				*retlen = 0; -				mutex_unlock(&this->lock); -				return -EIO; -			} - -			DoC_Command(this, NAND_CMD_SEQIN, 0); -			DoC_Address(this, ADDR_COLUMN_PAGE, to + len256, 0, -				    CDSN_CTRL_ECC_IO); -		} - -		DoC_WriteBuf(this, &buf[len256], len - len256); - -		WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_CE, docptr, CDSNControl); - -		if (DoC_is_Millennium(this)) { -			WriteDOC(0, docptr, NOP); -			WriteDOC(0, docptr, NOP); -			WriteDOC(0, docptr, NOP); -		} else { -			WriteDOC_(0, docptr, this->ioreg); -			WriteDOC_(0, docptr, this->ioreg); -			WriteDOC_(0, docptr, this->ioreg); -		} - -		WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_FLASH_IO | CDSN_CTRL_CE, docptr, -			 CDSNControl); - -		/* Read the ECC data through the DiskOnChip ECC logic */ -		for (di = 0; di < 6; di++) { -			eccbuf[di] = ReadDOC(docptr, ECCSyndrome0 + di); -		} - -		/* Reset the ECC engine */ -		WriteDOC(DOC_ECC_DIS, docptr, ECCConf); - -#ifdef PSYCHO_DEBUG -		printk -			("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", -			 (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], -			 eccbuf[4], eccbuf[5]); -#endif -		DoC_Command(this, NAND_CMD_PAGEPROG, 0); - -		DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP); -		/* There's an implicit DoC_WaitReady() in DoC_Command */ - -		if (DoC_is_Millennium(this)) { -			ReadDOC(docptr, ReadPipeInit); -			status = ReadDOC(docptr, LastDataRead); -		} else { -			dummy = ReadDOC(docptr, CDSNSlowIO); -			DoC_Delay(this, 2); -			status = ReadDOC_(docptr, this->ioreg); -		} - -		if (status & 1) { -			printk(KERN_ERR "Error programming flash\n"); -			/* Error in programming */ -			*retlen = 0; -			mutex_unlock(&this->lock); -			return -EIO; -		} - -		/* Let the caller know we completed it */ -		*retlen += len; - -		{ -			unsigned char x[8]; -			size_t dummy; -			int ret; - -			/* Write the ECC data to flash */ -			for (di=0; di<6; di++) -				x[di] = eccbuf[di]; - -			x[6]=0x55; -			x[7]=0x55; - -			ret = doc_write_oob_nolock(mtd, to, 8, &dummy, x); -			if (ret) { -				mutex_unlock(&this->lock); -				return ret; -			} -		} - -		to += len; -		left -= len; -		buf += len; -	} - -	mutex_unlock(&this->lock); -	return 0; -} - -static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, -			struct mtd_oob_ops *ops) -{ -	struct DiskOnChip *this = mtd->priv; -	int len256 = 0, ret; -	struct Nand *mychip; -	uint8_t *buf = ops->oobbuf; -	size_t len = ops->len; - -	BUG_ON(ops->mode != MTD_OOB_PLACE); - -	ofs += ops->ooboffs; - -	mutex_lock(&this->lock); - -	mychip = &this->chips[ofs >> this->chipshift]; - -	if (this->curfloor != mychip->floor) { -		DoC_SelectFloor(this, mychip->floor); -		DoC_SelectChip(this, mychip->chip); -	} else if (this->curchip != mychip->chip) { -		DoC_SelectChip(this, mychip->chip); -	} -	this->curfloor = mychip->floor; -	this->curchip = mychip->chip; - -	/* update address for 2M x 8bit devices. OOB starts on the second */ -	/* page to maintain compatibility with doc_read_ecc. */ -	if (this->page256) { -		if (!(ofs & 0x8)) -			ofs += 0x100; -		else -			ofs -= 0x8; -	} - -	DoC_Command(this, NAND_CMD_READOOB, CDSN_CTRL_WP); -	DoC_Address(this, ADDR_COLUMN_PAGE, ofs, CDSN_CTRL_WP, 0); - -	/* treat crossing 8-byte OOB data for 2M x 8bit devices */ -	/* Note: datasheet says it should automaticaly wrap to the */ -	/*       next OOB block, but it didn't work here. mf.      */ -	if (this->page256 && ofs + len > (ofs | 0x7) + 1) { -		len256 = (ofs | 0x7) + 1 - ofs; -		DoC_ReadBuf(this, buf, len256); - -		DoC_Command(this, NAND_CMD_READOOB, CDSN_CTRL_WP); -		DoC_Address(this, ADDR_COLUMN_PAGE, ofs & (~0x1ff), -			    CDSN_CTRL_WP, 0); -	} - -	DoC_ReadBuf(this, &buf[len256], len - len256); - -	ops->retlen = len; -	/* Reading the full OOB data drops us off of the end of the page, -         * causing the flash device to go into busy mode, so we need -         * to wait until ready 11.4.1 and Toshiba TC58256FT docs */ - -	ret = DoC_WaitReady(this); - -	mutex_unlock(&this->lock); -	return ret; - -} - -static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len, -				size_t * retlen, const u_char * buf) -{ -	struct DiskOnChip *this = mtd->priv; -	int len256 = 0; -	void __iomem *docptr = this->virtadr; -	struct Nand *mychip = &this->chips[ofs >> this->chipshift]; -	volatile int dummy; -	int status; - -	//      printk("doc_write_oob(%lx, %d): %2.2X %2.2X %2.2X %2.2X ... %2.2X %2.2X .. %2.2X %2.2X\n",(long)ofs, len, -	//   buf[0], buf[1], buf[2], buf[3], buf[8], buf[9], buf[14],buf[15]); - -	/* Find the chip which is to be used and select it */ -	if (this->curfloor != mychip->floor) { -		DoC_SelectFloor(this, mychip->floor); -		DoC_SelectChip(this, mychip->chip); -	} else if (this->curchip != mychip->chip) { -		DoC_SelectChip(this, mychip->chip); -	} -	this->curfloor = mychip->floor; -	this->curchip = mychip->chip; - -	/* disable the ECC engine */ -	WriteDOC (DOC_ECC_RESET, docptr, ECCConf); -	WriteDOC (DOC_ECC_DIS, docptr, ECCConf); - -	/* Reset the chip, see Software Requirement 11.4 item 1. */ -	DoC_Command(this, NAND_CMD_RESET, CDSN_CTRL_WP); - -	/* issue the Read2 command to set the pointer to the Spare Data Area. */ -	DoC_Command(this, NAND_CMD_READOOB, CDSN_CTRL_WP); - -	/* update address for 2M x 8bit devices. OOB starts on the second */ -	/* page to maintain compatibility with doc_read_ecc. */ -	if (this->page256) { -		if (!(ofs & 0x8)) -			ofs += 0x100; -		else -			ofs -= 0x8; -	} - -	/* issue the Serial Data In command to initial the Page Program process */ -	DoC_Command(this, NAND_CMD_SEQIN, 0); -	DoC_Address(this, ADDR_COLUMN_PAGE, ofs, 0, 0); - -	/* treat crossing 8-byte OOB data for 2M x 8bit devices */ -	/* Note: datasheet says it should automaticaly wrap to the */ -	/*       next OOB block, but it didn't work here. mf.      */ -	if (this->page256 && ofs + len > (ofs | 0x7) + 1) { -		len256 = (ofs | 0x7) + 1 - ofs; -		DoC_WriteBuf(this, buf, len256); - -		DoC_Command(this, NAND_CMD_PAGEPROG, 0); -		DoC_Command(this, NAND_CMD_STATUS, 0); -		/* DoC_WaitReady() is implicit in DoC_Command */ - -		if (DoC_is_Millennium(this)) { -			ReadDOC(docptr, ReadPipeInit); -			status = ReadDOC(docptr, LastDataRead); -		} else { -			dummy = ReadDOC(docptr, CDSNSlowIO); -			DoC_Delay(this, 2); -			status = ReadDOC_(docptr, this->ioreg); -		} - -		if (status & 1) { -			printk(KERN_ERR "Error programming oob data\n"); -			/* There was an error */ -			*retlen = 0; -			return -EIO; -		} -		DoC_Command(this, NAND_CMD_SEQIN, 0); -		DoC_Address(this, ADDR_COLUMN_PAGE, ofs & (~0x1ff), 0, 0); -	} - -	DoC_WriteBuf(this, &buf[len256], len - len256); - -	DoC_Command(this, NAND_CMD_PAGEPROG, 0); -	DoC_Command(this, NAND_CMD_STATUS, 0); -	/* DoC_WaitReady() is implicit in DoC_Command */ - -	if (DoC_is_Millennium(this)) { -		ReadDOC(docptr, ReadPipeInit); -		status = ReadDOC(docptr, LastDataRead); -	} else { -		dummy = ReadDOC(docptr, CDSNSlowIO); -		DoC_Delay(this, 2); -		status = ReadDOC_(docptr, this->ioreg); -	} - -	if (status & 1) { -		printk(KERN_ERR "Error programming oob data\n"); -		/* There was an error */ -		*retlen = 0; -		return -EIO; -	} - -	*retlen = len; -	return 0; - -} - -static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, -			 struct mtd_oob_ops *ops) -{ -	struct DiskOnChip *this = mtd->priv; -	int ret; - -	BUG_ON(ops->mode != MTD_OOB_PLACE); - -	mutex_lock(&this->lock); -	ret = doc_write_oob_nolock(mtd, ofs + ops->ooboffs, ops->len, -				   &ops->retlen, ops->oobbuf); - -	mutex_unlock(&this->lock); -	return ret; -} - -static int doc_erase(struct mtd_info *mtd, struct erase_info *instr) -{ -	struct DiskOnChip *this = mtd->priv; -	__u32 ofs = instr->addr; -	__u32 len = instr->len; -	volatile int dummy; -	void __iomem *docptr = this->virtadr; -	struct Nand *mychip; -	int status; - - 	mutex_lock(&this->lock); - -	if (ofs & (mtd->erasesize-1) || len & (mtd->erasesize-1)) { -		mutex_unlock(&this->lock); -		return -EINVAL; -	} - -	instr->state = MTD_ERASING; - -	/* FIXME: Do this in the background. Use timers or schedule_task() */ -	while(len) { -		mychip = &this->chips[ofs >> this->chipshift]; - -		if (this->curfloor != mychip->floor) { -			DoC_SelectFloor(this, mychip->floor); -			DoC_SelectChip(this, mychip->chip); -		} else if (this->curchip != mychip->chip) { -			DoC_SelectChip(this, mychip->chip); -		} -		this->curfloor = mychip->floor; -		this->curchip = mychip->chip; - -		DoC_Command(this, NAND_CMD_ERASE1, 0); -		DoC_Address(this, ADDR_PAGE, ofs, 0, 0); -		DoC_Command(this, NAND_CMD_ERASE2, 0); - -		DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP); - -		if (DoC_is_Millennium(this)) { -			ReadDOC(docptr, ReadPipeInit); -			status = ReadDOC(docptr, LastDataRead); -		} else { -			dummy = ReadDOC(docptr, CDSNSlowIO); -			DoC_Delay(this, 2); -			status = ReadDOC_(docptr, this->ioreg); -		} - -		if (status & 1) { -			printk(KERN_ERR "Error erasing at 0x%x\n", ofs); -			/* There was an error */ -			instr->state = MTD_ERASE_FAILED; -			goto callback; -		} -		ofs += mtd->erasesize; -		len -= mtd->erasesize; -	} -	instr->state = MTD_ERASE_DONE; - - callback: -	mtd_erase_callback(instr); - -	mutex_unlock(&this->lock); -	return 0; -} - - -/**************************************************************************** - * - * Module stuff - * - ****************************************************************************/ - -static void __exit cleanup_doc2000(void) -{ -	struct mtd_info *mtd; -	struct DiskOnChip *this; - -	while ((mtd = doc2klist)) { -		this = mtd->priv; -		doc2klist = this->nextdoc; - -		del_mtd_device(mtd); - -		iounmap(this->virtadr); -		kfree(this->chips); -		kfree(mtd); -	} -} - -module_exit(cleanup_doc2000); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org> et al."); -MODULE_DESCRIPTION("MTD driver for DiskOnChip 2000 and Millennium"); - diff --git a/drivers/mtd/devices/doc2001.c b/drivers/mtd/devices/doc2001.c deleted file mode 100644 index 0990f780362..00000000000 --- a/drivers/mtd/devices/doc2001.c +++ /dev/null @@ -1,841 +0,0 @@ - -/* - * Linux driver for Disk-On-Chip Millennium - * (c) 1999 Machine Vision Holdings, Inc. - * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <asm/errno.h> -#include <asm/io.h> -#include <asm/uaccess.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/types.h> -#include <linux/bitops.h> - -#include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> -#include <linux/mtd/doc2000.h> - -/* #define ECC_DEBUG */ - -/* I have no idea why some DoC chips can not use memcop_form|to_io(). - * This may be due to the different revisions of the ASIC controller built-in or - * simplily a QA/Bug issue. Who knows ?? If you have trouble, please uncomment - * this:*/ -#undef USE_MEMCPY - -static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, -		    size_t *retlen, u_char *buf); -static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, -		     size_t *retlen, const u_char *buf); -static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, -			struct mtd_oob_ops *ops); -static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, -			 struct mtd_oob_ops *ops); -static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); - -static struct mtd_info *docmillist = NULL; - -/* Perform the required delay cycles by reading from the NOP register */ -static void DoC_Delay(void __iomem * docptr, unsigned short cycles) -{ -	volatile char dummy; -	int i; - -	for (i = 0; i < cycles; i++) -		dummy = ReadDOC(docptr, NOP); -} - -/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ -static int _DoC_WaitReady(void __iomem * docptr) -{ -	unsigned short c = 0xffff; - -	DEBUG(MTD_DEBUG_LEVEL3, -	      "_DoC_WaitReady called for out-of-line wait\n"); - -	/* Out-of-line routine to wait for chip response */ -	while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B) && --c) -		; - -	if (c == 0) -		DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n"); - -	return (c == 0); -} - -static inline int DoC_WaitReady(void __iomem * docptr) -{ -	/* This is inline, to optimise the common case, where it's ready instantly */ -	int ret = 0; - -	/* 4 read form NOP register should be issued in prior to the read from CDSNControl -	   see Software Requirement 11.4 item 2. */ -	DoC_Delay(docptr, 4); - -	if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) -		/* Call the out-of-line routine to wait */ -		ret = _DoC_WaitReady(docptr); - -	/* issue 2 read from NOP register after reading from CDSNControl register -	   see Software Requirement 11.4 item 2. */ -	DoC_Delay(docptr, 2); - -	return ret; -} - -/* DoC_Command: Send a flash command to the flash chip through the CDSN IO register -   with the internal pipeline. Each of 4 delay cycles (read from the NOP register) is -   required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */ - -static void DoC_Command(void __iomem * docptr, unsigned char command, -			       unsigned char xtraflags) -{ -	/* Assert the CLE (Command Latch Enable) line to the flash chip */ -	WriteDOC(xtraflags | CDSN_CTRL_CLE | CDSN_CTRL_CE, docptr, CDSNControl); -	DoC_Delay(docptr, 4); - -	/* Send the command */ -	WriteDOC(command, docptr, Mil_CDSN_IO); -	WriteDOC(0x00, docptr, WritePipeTerm); - -	/* Lower the CLE line */ -	WriteDOC(xtraflags | CDSN_CTRL_CE, docptr, CDSNControl); -	DoC_Delay(docptr, 4); -} - -/* DoC_Address: Set the current address for the flash chip through the CDSN IO register -   with the internal pipeline. Each of 4 delay cycles (read from the NOP register) is -   required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */ - -static inline void DoC_Address(void __iomem * docptr, int numbytes, unsigned long ofs, -			       unsigned char xtraflags1, unsigned char xtraflags2) -{ -	/* Assert the ALE (Address Latch Enable) line to the flash chip */ -	WriteDOC(xtraflags1 | CDSN_CTRL_ALE | CDSN_CTRL_CE, docptr, CDSNControl); -	DoC_Delay(docptr, 4); - -	/* Send the address */ -	switch (numbytes) -	    { -	    case 1: -		    /* Send single byte, bits 0-7. */ -		    WriteDOC(ofs & 0xff, docptr, Mil_CDSN_IO); -		    WriteDOC(0x00, docptr, WritePipeTerm); -		    break; -	    case 2: -		    /* Send bits 9-16 followed by 17-23 */ -		    WriteDOC((ofs >> 9)  & 0xff, docptr, Mil_CDSN_IO); -		    WriteDOC((ofs >> 17) & 0xff, docptr, Mil_CDSN_IO); -		    WriteDOC(0x00, docptr, WritePipeTerm); -		break; -	    case 3: -		    /* Send 0-7, 9-16, then 17-23 */ -		    WriteDOC(ofs & 0xff, docptr, Mil_CDSN_IO); -		    WriteDOC((ofs >> 9)  & 0xff, docptr, Mil_CDSN_IO); -		    WriteDOC((ofs >> 17) & 0xff, docptr, Mil_CDSN_IO); -		    WriteDOC(0x00, docptr, WritePipeTerm); -		break; -	    default: -		return; -	    } - -	/* Lower the ALE line */ -	WriteDOC(xtraflags1 | xtraflags2 | CDSN_CTRL_CE, docptr, CDSNControl); -	DoC_Delay(docptr, 4); -} - -/* DoC_SelectChip: Select a given flash chip within the current floor */ -static int DoC_SelectChip(void __iomem * docptr, int chip) -{ -	/* Select the individual flash chip requested */ -	WriteDOC(chip, docptr, CDSNDeviceSelect); -	DoC_Delay(docptr, 4); - -	/* Wait for it to be ready */ -	return DoC_WaitReady(docptr); -} - -/* DoC_SelectFloor: Select a given floor (bank of flash chips) */ -static int DoC_SelectFloor(void __iomem * docptr, int floor) -{ -	/* Select the floor (bank) of chips required */ -	WriteDOC(floor, docptr, FloorSelect); - -	/* Wait for the chip to be ready */ -	return DoC_WaitReady(docptr); -} - -/* DoC_IdentChip: Identify a given NAND chip given {floor,chip} */ -static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) -{ -	int mfr, id, i, j; -	volatile char dummy; - -	/* Page in the required floor/chip -	   FIXME: is this supported by Millennium ?? */ -	DoC_SelectFloor(doc->virtadr, floor); -	DoC_SelectChip(doc->virtadr, chip); - -	/* Reset the chip, see Software Requirement 11.4 item 1. */ -	DoC_Command(doc->virtadr, NAND_CMD_RESET, CDSN_CTRL_WP); -	DoC_WaitReady(doc->virtadr); - -	/* Read the NAND chip ID: 1. Send ReadID command */ -	DoC_Command(doc->virtadr, NAND_CMD_READID, CDSN_CTRL_WP); - -	/* Read the NAND chip ID: 2. Send address byte zero */ -	DoC_Address(doc->virtadr, 1, 0x00, CDSN_CTRL_WP, 0x00); - -	/* Read the manufacturer and device id codes of the flash device through -	   CDSN IO register see Software Requirement 11.4 item 5.*/ -	dummy = ReadDOC(doc->virtadr, ReadPipeInit); -	DoC_Delay(doc->virtadr, 2); -	mfr = ReadDOC(doc->virtadr, Mil_CDSN_IO); - -	DoC_Delay(doc->virtadr, 2); -	id  = ReadDOC(doc->virtadr, Mil_CDSN_IO); -	dummy = ReadDOC(doc->virtadr, LastDataRead); - -	/* No response - return failure */ -	if (mfr == 0xff || mfr == 0) -		return 0; - -	/* FIXME: to deal with multi-flash on multi-Millennium case more carefully */ -	for (i = 0; nand_flash_ids[i].name != NULL; i++) { -		if ( id == nand_flash_ids[i].id) { -			/* Try to identify manufacturer */ -			for (j = 0; nand_manuf_ids[j].id != 0x0; j++) { -				if (nand_manuf_ids[j].id == mfr) -					break; -			} -			printk(KERN_INFO "Flash chip found: Manufacturer ID: %2.2X, " -			       "Chip ID: %2.2X (%s:%s)\n", -			       mfr, id, nand_manuf_ids[j].name, nand_flash_ids[i].name); -			doc->mfr = mfr; -			doc->id = id; -			doc->chipshift = ffs((nand_flash_ids[i].chipsize << 20)) - 1; -			break; -		} -	} - -	if (nand_flash_ids[i].name == NULL) -		return 0; -	else -		return 1; -} - -/* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */ -static void DoC_ScanChips(struct DiskOnChip *this) -{ -	int floor, chip; -	int numchips[MAX_FLOORS_MIL]; -	int ret; - -	this->numchips = 0; -	this->mfr = 0; -	this->id = 0; - -	/* For each floor, find the number of valid chips it contains */ -	for (floor = 0,ret = 1; floor < MAX_FLOORS_MIL; floor++) { -		numchips[floor] = 0; -		for (chip = 0; chip < MAX_CHIPS_MIL && ret != 0; chip++) { -			ret = DoC_IdentChip(this, floor, chip); -			if (ret) { -				numchips[floor]++; -				this->numchips++; -			} -		} -	} -	/* If there are none at all that we recognise, bail */ -	if (!this->numchips) { -		printk("No flash chips recognised.\n"); -		return; -	} - -	/* Allocate an array to hold the information for each chip */ -	this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL); -	if (!this->chips){ -		printk("No memory for allocating chip info structures\n"); -		return; -	} - -	/* Fill out the chip array with {floor, chipno} for each -	 * detected chip in the device. */ -	for (floor = 0, ret = 0; floor < MAX_FLOORS_MIL; floor++) { -		for (chip = 0 ; chip < numchips[floor] ; chip++) { -			this->chips[ret].floor = floor; -			this->chips[ret].chip = chip; -			this->chips[ret].curadr = 0; -			this->chips[ret].curmode = 0x50; -			ret++; -		} -	} - -	/* Calculate and print the total size of the device */ -	this->totlen = this->numchips * (1 << this->chipshift); -	printk(KERN_INFO "%d flash chips found. Total DiskOnChip size: %ld MiB\n", -	       this->numchips ,this->totlen >> 20); -} - -static int DoCMil_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2) -{ -	int tmp1, tmp2, retval; - -	if (doc1->physadr == doc2->physadr) -		return 1; - -	/* Use the alias resolution register which was set aside for this -	 * purpose. If it's value is the same on both chips, they might -	 * be the same chip, and we write to one and check for a change in -	 * the other. It's unclear if this register is usuable in the -	 * DoC 2000 (it's in the Millenium docs), but it seems to work. */ -	tmp1 = ReadDOC(doc1->virtadr, AliasResolution); -	tmp2 = ReadDOC(doc2->virtadr, AliasResolution); -	if (tmp1 != tmp2) -		return 0; - -	WriteDOC((tmp1+1) % 0xff, doc1->virtadr, AliasResolution); -	tmp2 = ReadDOC(doc2->virtadr, AliasResolution); -	if (tmp2 == (tmp1+1) % 0xff) -		retval = 1; -	else -		retval = 0; - -	/* Restore register contents.  May not be necessary, but do it just to -	 * be safe. */ -	WriteDOC(tmp1, doc1->virtadr, AliasResolution); - -	return retval; -} - -/* This routine is found from the docprobe code by symbol_get(), - * which will bump the use count of this module. */ -void DoCMil_init(struct mtd_info *mtd) -{ -	struct DiskOnChip *this = mtd->priv; -	struct DiskOnChip *old = NULL; - -	/* We must avoid being called twice for the same device. */ -	if (docmillist) -		old = docmillist->priv; - -	while (old) { -		if (DoCMil_is_alias(this, old)) { -			printk(KERN_NOTICE "Ignoring DiskOnChip Millennium at " -			       "0x%lX - already configured\n", this->physadr); -			iounmap(this->virtadr); -			kfree(mtd); -			return; -		} -		if (old->nextdoc) -			old = old->nextdoc->priv; -		else -			old = NULL; -	} - -	mtd->name = "DiskOnChip Millennium"; -	printk(KERN_NOTICE "DiskOnChip Millennium found at address 0x%lX\n", -	       this->physadr); - -	mtd->type = MTD_NANDFLASH; -	mtd->flags = MTD_CAP_NANDFLASH; -	mtd->size = 0; - -	/* FIXME: erase size is not always 8KiB */ -	mtd->erasesize = 0x2000; - -	mtd->writesize = 512; -	mtd->oobsize = 16; -	mtd->owner = THIS_MODULE; -	mtd->erase = doc_erase; -	mtd->point = NULL; -	mtd->unpoint = NULL; -	mtd->read = doc_read; -	mtd->write = doc_write; -	mtd->read_oob = doc_read_oob; -	mtd->write_oob = doc_write_oob; -	mtd->sync = NULL; - -	this->totlen = 0; -	this->numchips = 0; -	this->curfloor = -1; -	this->curchip = -1; - -	/* Ident all the chips present. */ -	DoC_ScanChips(this); - -	if (!this->totlen) { -		kfree(mtd); -		iounmap(this->virtadr); -	} else { -		this->nextdoc = docmillist; -		docmillist = mtd; -		mtd->size  = this->totlen; -		add_mtd_device(mtd); -		return; -	} -} -EXPORT_SYMBOL_GPL(DoCMil_init); - -static int doc_read (struct mtd_info *mtd, loff_t from, size_t len, -		     size_t *retlen, u_char *buf) -{ -	int i, ret; -	volatile char dummy; -	unsigned char syndrome[6], eccbuf[6]; -	struct DiskOnChip *this = mtd->priv; -	void __iomem *docptr = this->virtadr; -	struct Nand *mychip = &this->chips[from >> (this->chipshift)]; - -	/* Don't allow read past end of device */ -	if (from >= this->totlen) -		return -EINVAL; - -	/* Don't allow a single read to cross a 512-byte block boundary */ -	if (from + len > ((from | 0x1ff) + 1)) -		len = ((from | 0x1ff) + 1) - from; - -	/* Find the chip which is to be used and select it */ -	if (this->curfloor != mychip->floor) { -		DoC_SelectFloor(docptr, mychip->floor); -		DoC_SelectChip(docptr, mychip->chip); -	} else if (this->curchip != mychip->chip) { -		DoC_SelectChip(docptr, mychip->chip); -	} -	this->curfloor = mychip->floor; -	this->curchip = mychip->chip; - -	/* issue the Read0 or Read1 command depend on which half of the page -	   we are accessing. Polling the Flash Ready bit after issue 3 bytes -	   address in Sequence Read Mode, see Software Requirement 11.4 item 1.*/ -	DoC_Command(docptr, (from >> 8) & 1, CDSN_CTRL_WP); -	DoC_Address(docptr, 3, from, CDSN_CTRL_WP, 0x00); -	DoC_WaitReady(docptr); - -	/* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/ -	WriteDOC (DOC_ECC_RESET, docptr, ECCConf); -	WriteDOC (DOC_ECC_EN, docptr, ECCConf); - -	/* Read the data via the internal pipeline through CDSN IO register, -	   see Pipelined Read Operations 11.3 */ -	dummy = ReadDOC(docptr, ReadPipeInit); -#ifndef USE_MEMCPY -	for (i = 0; i < len-1; i++) { -		/* N.B. you have to increase the source address in this way or the -		   ECC logic will not work properly */ -		buf[i] = ReadDOC(docptr, Mil_CDSN_IO + (i & 0xff)); -	} -#else -	memcpy_fromio(buf, docptr + DoC_Mil_CDSN_IO, len - 1); -#endif -	buf[len - 1] = ReadDOC(docptr, LastDataRead); - -	/* Let the caller know we completed it */ -	*retlen = len; -        ret = 0; - -	/* Read the ECC data from Spare Data Area, -	   see Reed-Solomon EDC/ECC 11.1 */ -	dummy = ReadDOC(docptr, ReadPipeInit); -#ifndef USE_MEMCPY -	for (i = 0; i < 5; i++) { -		/* N.B. you have to increase the source address in this way or the -		   ECC logic will not work properly */ -		eccbuf[i] = ReadDOC(docptr, Mil_CDSN_IO + i); -	} -#else -	memcpy_fromio(eccbuf, docptr + DoC_Mil_CDSN_IO, 5); -#endif -	eccbuf[5] = ReadDOC(docptr, LastDataRead); - -	/* Flush the pipeline */ -	dummy = ReadDOC(docptr, ECCConf); -	dummy = ReadDOC(docptr, ECCConf); - -	/* Check the ECC Status */ -	if (ReadDOC(docptr, ECCConf) & 0x80) { -		int nb_errors; -		/* There was an ECC error */ -#ifdef ECC_DEBUG -		printk("DiskOnChip ECC Error: Read at %lx\n", (long)from); -#endif -		/* Read the ECC syndrom through the DiskOnChip ECC logic. -		   These syndrome will be all ZERO when there is no error */ -		for (i = 0; i < 6; i++) { -			syndrome[i] = ReadDOC(docptr, ECCSyndrome0 + i); -		} -		nb_errors = doc_decode_ecc(buf, syndrome); -#ifdef ECC_DEBUG -		printk("ECC Errors corrected: %x\n", nb_errors); -#endif -		if (nb_errors < 0) { -			/* We return error, but have actually done the read. Not that -			   this can be told to user-space, via sys_read(), but at least -			   MTD-aware stuff can know about it by checking *retlen */ -			ret = -EIO; -		} -	} - -#ifdef PSYCHO_DEBUG -	printk("ECC DATA at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", -	       (long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], -	       eccbuf[4], eccbuf[5]); -#endif - -	/* disable the ECC engine */ -	WriteDOC(DOC_ECC_DIS, docptr , ECCConf); - -	return ret; -} - -static int doc_write (struct mtd_info *mtd, loff_t to, size_t len, -		      size_t *retlen, const u_char *buf) -{ -	int i,ret = 0; -	char eccbuf[6]; -	volatile char dummy; -	struct DiskOnChip *this = mtd->priv; -	void __iomem *docptr = this->virtadr; -	struct Nand *mychip = &this->chips[to >> (this->chipshift)]; - -	/* Don't allow write past end of device */ -	if (to >= this->totlen) -		return -EINVAL; - -#if 0 -	/* Don't allow a single write to cross a 512-byte block boundary */ -	if (to + len > ( (to | 0x1ff) + 1)) -		len = ((to | 0x1ff) + 1) - to; -#else -	/* Don't allow writes which aren't exactly one block */ -	if (to & 0x1ff || len != 0x200) -		return -EINVAL; -#endif - -	/* Find the chip which is to be used and select it */ -	if (this->curfloor != mychip->floor) { -		DoC_SelectFloor(docptr, mychip->floor); -		DoC_SelectChip(docptr, mychip->chip); -	} else if (this->curchip != mychip->chip) { -		DoC_SelectChip(docptr, mychip->chip); -	} -	this->curfloor = mychip->floor; -	this->curchip = mychip->chip; - -	/* Reset the chip, see Software Requirement 11.4 item 1. */ -	DoC_Command(docptr, NAND_CMD_RESET, 0x00); -	DoC_WaitReady(docptr); -	/* Set device to main plane of flash */ -	DoC_Command(docptr, NAND_CMD_READ0, 0x00); - -	/* issue the Serial Data In command to initial the Page Program process */ -	DoC_Command(docptr, NAND_CMD_SEQIN, 0x00); -	DoC_Address(docptr, 3, to, 0x00, 0x00); -	DoC_WaitReady(docptr); - -	/* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/ -	WriteDOC (DOC_ECC_RESET, docptr, ECCConf); -	WriteDOC (DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf); - -	/* Write the data via the internal pipeline through CDSN IO register, -	   see Pipelined Write Operations 11.2 */ -#ifndef USE_MEMCPY -	for (i = 0; i < len; i++) { -		/* N.B. you have to increase the source address in this way or the -		   ECC logic will not work properly */ -		WriteDOC(buf[i], docptr, Mil_CDSN_IO + i); -	} -#else -	memcpy_toio(docptr + DoC_Mil_CDSN_IO, buf, len); -#endif -	WriteDOC(0x00, docptr, WritePipeTerm); - -	/* Write ECC data to flash, the ECC info is generated by the DiskOnChip ECC logic -	   see Reed-Solomon EDC/ECC 11.1 */ -	WriteDOC(0, docptr, NOP); -	WriteDOC(0, docptr, NOP); -	WriteDOC(0, docptr, NOP); - -	/* Read the ECC data through the DiskOnChip ECC logic */ -	for (i = 0; i < 6; i++) { -		eccbuf[i] = ReadDOC(docptr, ECCSyndrome0 + i); -	} - -	/* ignore the ECC engine */ -	WriteDOC(DOC_ECC_DIS, docptr , ECCConf); - -#ifndef USE_MEMCPY -	/* Write the ECC data to flash */ -	for (i = 0; i < 6; i++) { -		/* N.B. you have to increase the source address in this way or the -		   ECC logic will not work properly */ -		WriteDOC(eccbuf[i], docptr, Mil_CDSN_IO + i); -	} -#else -	memcpy_toio(docptr + DoC_Mil_CDSN_IO, eccbuf, 6); -#endif - -	/* write the block status BLOCK_USED (0x5555) at the end of ECC data -	   FIXME: this is only a hack for programming the IPL area for LinuxBIOS -	   and should be replace with proper codes in user space utilities */ -	WriteDOC(0x55, docptr, Mil_CDSN_IO); -	WriteDOC(0x55, docptr, Mil_CDSN_IO + 1); - -	WriteDOC(0x00, docptr, WritePipeTerm); - -#ifdef PSYCHO_DEBUG -	printk("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", -	       (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], -	       eccbuf[4], eccbuf[5]); -#endif - -	/* Commit the Page Program command and wait for ready -	   see Software Requirement 11.4 item 1.*/ -	DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00); -	DoC_WaitReady(docptr); - -	/* Read the status of the flash device through CDSN IO register -	   see Software Requirement 11.4 item 5.*/ -	DoC_Command(docptr, NAND_CMD_STATUS, CDSN_CTRL_WP); -	dummy = ReadDOC(docptr, ReadPipeInit); -	DoC_Delay(docptr, 2); -	if (ReadDOC(docptr, Mil_CDSN_IO) & 1) { -		printk("Error programming flash\n"); -		/* Error in programming -		   FIXME: implement Bad Block Replacement (in nftl.c ??) */ -		*retlen = 0; -		ret = -EIO; -	} -	dummy = ReadDOC(docptr, LastDataRead); - -	/* Let the caller know we completed it */ -	*retlen = len; - -	return ret; -} - -static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, -			struct mtd_oob_ops *ops) -{ -#ifndef USE_MEMCPY -	int i; -#endif -	volatile char dummy; -	struct DiskOnChip *this = mtd->priv; -	void __iomem *docptr = this->virtadr; -	struct Nand *mychip = &this->chips[ofs >> this->chipshift]; -	uint8_t *buf = ops->oobbuf; -	size_t len = ops->len; - -	BUG_ON(ops->mode != MTD_OOB_PLACE); - -	ofs += ops->ooboffs; - -	/* Find the chip which is to be used and select it */ -	if (this->curfloor != mychip->floor) { -		DoC_SelectFloor(docptr, mychip->floor); -		DoC_SelectChip(docptr, mychip->chip); -	} else if (this->curchip != mychip->chip) { -		DoC_SelectChip(docptr, mychip->chip); -	} -	this->curfloor = mychip->floor; -	this->curchip = mychip->chip; - -	/* disable the ECC engine */ -	WriteDOC (DOC_ECC_RESET, docptr, ECCConf); -	WriteDOC (DOC_ECC_DIS, docptr, ECCConf); - -	/* issue the Read2 command to set the pointer to the Spare Data Area. -	   Polling the Flash Ready bit after issue 3 bytes address in -	   Sequence Read Mode, see Software Requirement 11.4 item 1.*/ -	DoC_Command(docptr, NAND_CMD_READOOB, CDSN_CTRL_WP); -	DoC_Address(docptr, 3, ofs, CDSN_CTRL_WP, 0x00); -	DoC_WaitReady(docptr); - -	/* Read the data out via the internal pipeline through CDSN IO register, -	   see Pipelined Read Operations 11.3 */ -	dummy = ReadDOC(docptr, ReadPipeInit); -#ifndef USE_MEMCPY -	for (i = 0; i < len-1; i++) { -		/* N.B. you have to increase the source address in this way or the -		   ECC logic will not work properly */ -		buf[i] = ReadDOC(docptr, Mil_CDSN_IO + i); -	} -#else -	memcpy_fromio(buf, docptr + DoC_Mil_CDSN_IO, len - 1); -#endif -	buf[len - 1] = ReadDOC(docptr, LastDataRead); - -	ops->retlen = len; - -	return 0; -} - -static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, -			 struct mtd_oob_ops *ops) -{ -#ifndef USE_MEMCPY -	int i; -#endif -	volatile char dummy; -	int ret = 0; -	struct DiskOnChip *this = mtd->priv; -	void __iomem *docptr = this->virtadr; -	struct Nand *mychip = &this->chips[ofs >> this->chipshift]; -	uint8_t *buf = ops->oobbuf; -	size_t len = ops->len; - -	BUG_ON(ops->mode != MTD_OOB_PLACE); - -	ofs += ops->ooboffs; - -	/* Find the chip which is to be used and select it */ -	if (this->curfloor != mychip->floor) { -		DoC_SelectFloor(docptr, mychip->floor); -		DoC_SelectChip(docptr, mychip->chip); -	} else if (this->curchip != mychip->chip) { -		DoC_SelectChip(docptr, mychip->chip); -	} -	this->curfloor = mychip->floor; -	this->curchip = mychip->chip; - -	/* disable the ECC engine */ -	WriteDOC (DOC_ECC_RESET, docptr, ECCConf); -	WriteDOC (DOC_ECC_DIS, docptr, ECCConf); - -	/* Reset the chip, see Software Requirement 11.4 item 1. */ -	DoC_Command(docptr, NAND_CMD_RESET, CDSN_CTRL_WP); -	DoC_WaitReady(docptr); -	/* issue the Read2 command to set the pointer to the Spare Data Area. */ -	DoC_Command(docptr, NAND_CMD_READOOB, CDSN_CTRL_WP); - -	/* issue the Serial Data In command to initial the Page Program process */ -	DoC_Command(docptr, NAND_CMD_SEQIN, 0x00); -	DoC_Address(docptr, 3, ofs, 0x00, 0x00); - -	/* Write the data via the internal pipeline through CDSN IO register, -	   see Pipelined Write Operations 11.2 */ -#ifndef USE_MEMCPY -	for (i = 0; i < len; i++) { -		/* N.B. you have to increase the source address in this way or the -		   ECC logic will not work properly */ -		WriteDOC(buf[i], docptr, Mil_CDSN_IO + i); -	} -#else -	memcpy_toio(docptr + DoC_Mil_CDSN_IO, buf, len); -#endif -	WriteDOC(0x00, docptr, WritePipeTerm); - -	/* Commit the Page Program command and wait for ready -	   see Software Requirement 11.4 item 1.*/ -	DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00); -	DoC_WaitReady(docptr); - -	/* Read the status of the flash device through CDSN IO register -	   see Software Requirement 11.4 item 5.*/ -	DoC_Command(docptr, NAND_CMD_STATUS, 0x00); -	dummy = ReadDOC(docptr, ReadPipeInit); -	DoC_Delay(docptr, 2); -	if (ReadDOC(docptr, Mil_CDSN_IO) & 1) { -		printk("Error programming oob data\n"); -		/* FIXME: implement Bad Block Replacement (in nftl.c ??) */ -		ops->retlen = 0; -		ret = -EIO; -	} -	dummy = ReadDOC(docptr, LastDataRead); - -	ops->retlen = len; - -	return ret; -} - -int doc_erase (struct mtd_info *mtd, struct erase_info *instr) -{ -	volatile char dummy; -	struct DiskOnChip *this = mtd->priv; -	__u32 ofs = instr->addr; -	__u32 len = instr->len; -	void __iomem *docptr = this->virtadr; -	struct Nand *mychip = &this->chips[ofs >> this->chipshift]; - -	if (len != mtd->erasesize) -		printk(KERN_WARNING "Erase not right size (%x != %x)n", -		       len, mtd->erasesize); - -	/* Find the chip which is to be used and select it */ -	if (this->curfloor != mychip->floor) { -		DoC_SelectFloor(docptr, mychip->floor); -		DoC_SelectChip(docptr, mychip->chip); -	} else if (this->curchip != mychip->chip) { -		DoC_SelectChip(docptr, mychip->chip); -	} -	this->curfloor = mychip->floor; -	this->curchip = mychip->chip; - -	instr->state = MTD_ERASE_PENDING; - -	/* issue the Erase Setup command */ -	DoC_Command(docptr, NAND_CMD_ERASE1, 0x00); -	DoC_Address(docptr, 2, ofs, 0x00, 0x00); - -	/* Commit the Erase Start command and wait for ready -	   see Software Requirement 11.4 item 1.*/ -	DoC_Command(docptr, NAND_CMD_ERASE2, 0x00); -	DoC_WaitReady(docptr); - -	instr->state = MTD_ERASING; - -	/* Read the status of the flash device through CDSN IO register -	   see Software Requirement 11.4 item 5. -	   FIXME: it seems that we are not wait long enough, some blocks are not -	   erased fully */ -	DoC_Command(docptr, NAND_CMD_STATUS, CDSN_CTRL_WP); -	dummy = ReadDOC(docptr, ReadPipeInit); -	DoC_Delay(docptr, 2); -	if (ReadDOC(docptr, Mil_CDSN_IO) & 1) { -		printk("Error Erasing at 0x%x\n", ofs); -		/* There was an error -		   FIXME: implement Bad Block Replacement (in nftl.c ??) */ -		instr->state = MTD_ERASE_FAILED; -	} else -		instr->state = MTD_ERASE_DONE; -	dummy = ReadDOC(docptr, LastDataRead); - -	mtd_erase_callback(instr); - -	return 0; -} - -/**************************************************************************** - * - * Module stuff - * - ****************************************************************************/ - -static void __exit cleanup_doc2001(void) -{ -	struct mtd_info *mtd; -	struct DiskOnChip *this; - -	while ((mtd=docmillist)) { -		this = mtd->priv; -		docmillist = this->nextdoc; - -		del_mtd_device(mtd); - -		iounmap(this->virtadr); -		kfree(this->chips); -		kfree(mtd); -	} -} - -module_exit(cleanup_doc2001); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org> et al."); -MODULE_DESCRIPTION("Alternative driver for DiskOnChip Millennium"); diff --git a/drivers/mtd/devices/doc2001plus.c b/drivers/mtd/devices/doc2001plus.c deleted file mode 100644 index 719b2915dc3..00000000000 --- a/drivers/mtd/devices/doc2001plus.c +++ /dev/null @@ -1,1106 +0,0 @@ -/* - * Linux driver for Disk-On-Chip Millennium Plus - * - * (c) 2002-2003 Greg Ungerer <gerg@snapgear.com> - * (c) 2002-2003 SnapGear Inc - * (c) 1999 Machine Vision Holdings, Inc. - * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> - * - * Released under GPL - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <asm/errno.h> -#include <asm/io.h> -#include <asm/uaccess.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/types.h> -#include <linux/bitops.h> - -#include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> -#include <linux/mtd/doc2000.h> - -/* #define ECC_DEBUG */ - -/* I have no idea why some DoC chips can not use memcop_form|to_io(). - * This may be due to the different revisions of the ASIC controller built-in or - * simplily a QA/Bug issue. Who knows ?? If you have trouble, please uncomment - * this:*/ -#undef USE_MEMCPY - -static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, -		size_t *retlen, u_char *buf); -static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, -		size_t *retlen, const u_char *buf); -static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, -			struct mtd_oob_ops *ops); -static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, -			 struct mtd_oob_ops *ops); -static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); - -static struct mtd_info *docmilpluslist = NULL; - - -/* Perform the required delay cycles by writing to the NOP register */ -static void DoC_Delay(void __iomem * docptr, int cycles) -{ -	int i; - -	for (i = 0; (i < cycles); i++) -		WriteDOC(0, docptr, Mplus_NOP); -} - -#define	CDSN_CTRL_FR_B_MASK	(CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1) - -/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ -static int _DoC_WaitReady(void __iomem * docptr) -{ -	unsigned int c = 0xffff; - -	DEBUG(MTD_DEBUG_LEVEL3, -	      "_DoC_WaitReady called for out-of-line wait\n"); - -	/* Out-of-line routine to wait for chip response */ -	while (((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) && --c) -		; - -	if (c == 0) -		DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n"); - -	return (c == 0); -} - -static inline int DoC_WaitReady(void __iomem * docptr) -{ -	/* This is inline, to optimise the common case, where it's ready instantly */ -	int ret = 0; - -	/* read form NOP register should be issued prior to the read from CDSNControl -	   see Software Requirement 11.4 item 2. */ -	DoC_Delay(docptr, 4); - -	if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) -		/* Call the out-of-line routine to wait */ -		ret = _DoC_WaitReady(docptr); - -	return ret; -} - -/* For some reason the Millennium Plus seems to occassionally put itself - * into reset mode. For me this happens randomly, with no pattern that I - * can detect. M-systems suggest always check this on any block level - * operation and setting to normal mode if in reset mode. - */ -static inline void DoC_CheckASIC(void __iomem * docptr) -{ -	/* Make sure the DoC is in normal mode */ -	if ((ReadDOC(docptr, Mplus_DOCControl) & DOC_MODE_NORMAL) == 0) { -		WriteDOC((DOC_MODE_NORMAL | DOC_MODE_MDWREN), docptr, Mplus_DOCControl); -		WriteDOC(~(DOC_MODE_NORMAL | DOC_MODE_MDWREN), docptr, Mplus_CtrlConfirm); -	} -} - -/* DoC_Command: Send a flash command to the flash chip through the Flash - * command register. Need 2 Write Pipeline Terminates to complete send. - */ -static void DoC_Command(void __iomem * docptr, unsigned char command, -			       unsigned char xtraflags) -{ -	WriteDOC(command, docptr, Mplus_FlashCmd); -	WriteDOC(command, docptr, Mplus_WritePipeTerm); -	WriteDOC(command, docptr, Mplus_WritePipeTerm); -} - -/* DoC_Address: Set the current address for the flash chip through the Flash - * Address register. Need 2 Write Pipeline Terminates to complete send. - */ -static inline void DoC_Address(struct DiskOnChip *doc, int numbytes, -			       unsigned long ofs, unsigned char xtraflags1, -			       unsigned char xtraflags2) -{ -	void __iomem * docptr = doc->virtadr; - -	/* Allow for possible Mill Plus internal flash interleaving */ -	ofs >>= doc->interleave; - -	switch (numbytes) { -	case 1: -		/* Send single byte, bits 0-7. */ -		WriteDOC(ofs & 0xff, docptr, Mplus_FlashAddress); -		break; -	case 2: -		/* Send bits 9-16 followed by 17-23 */ -		WriteDOC((ofs >> 9)  & 0xff, docptr, Mplus_FlashAddress); -		WriteDOC((ofs >> 17) & 0xff, docptr, Mplus_FlashAddress); -		break; -	case 3: -		/* Send 0-7, 9-16, then 17-23 */ -		WriteDOC(ofs & 0xff, docptr, Mplus_FlashAddress); -		WriteDOC((ofs >> 9)  & 0xff, docptr, Mplus_FlashAddress); -		WriteDOC((ofs >> 17) & 0xff, docptr, Mplus_FlashAddress); -		break; -	default: -		return; -	} - -	WriteDOC(0x00, docptr, Mplus_WritePipeTerm); -	WriteDOC(0x00, docptr, Mplus_WritePipeTerm); -} - -/* DoC_SelectChip: Select a given flash chip within the current floor */ -static int DoC_SelectChip(void __iomem * docptr, int chip) -{ -	/* No choice for flash chip on Millennium Plus */ -	return 0; -} - -/* DoC_SelectFloor: Select a given floor (bank of flash chips) */ -static int DoC_SelectFloor(void __iomem * docptr, int floor) -{ -	WriteDOC((floor & 0x3), docptr, Mplus_DeviceSelect); -	return 0; -} - -/* - * Translate the given offset into the appropriate command and offset. - * This does the mapping using the 16bit interleave layout defined by - * M-Systems, and looks like this for a sector pair: - *  +-----------+-------+-------+-------+--------------+---------+-----------+ - *  | 0 --- 511 |512-517|518-519|520-521| 522 --- 1033 |1034-1039|1040 - 1055| - *  +-----------+-------+-------+-------+--------------+---------+-----------+ - *  | Data 0    | ECC 0 |Flags0 |Flags1 | Data 1       |ECC 1    | OOB 1 + 2 | - *  +-----------+-------+-------+-------+--------------+---------+-----------+ - */ -/* FIXME: This lives in INFTL not here. Other users of flash devices -   may not want it */ -static unsigned int DoC_GetDataOffset(struct mtd_info *mtd, loff_t *from) -{ -	struct DiskOnChip *this = mtd->priv; - -	if (this->interleave) { -		unsigned int ofs = *from & 0x3ff; -		unsigned int cmd; - -		if (ofs < 512) { -			cmd = NAND_CMD_READ0; -			ofs &= 0x1ff; -		} else if (ofs < 1014) { -			cmd = NAND_CMD_READ1; -			ofs = (ofs & 0x1ff) + 10; -		} else { -			cmd = NAND_CMD_READOOB; -			ofs = ofs - 1014; -		} - -		*from = (*from & ~0x3ff) | ofs; -		return cmd; -	} else { -		/* No interleave */ -		if ((*from) & 0x100) -			return NAND_CMD_READ1; -		return NAND_CMD_READ0; -	} -} - -static unsigned int DoC_GetECCOffset(struct mtd_info *mtd, loff_t *from) -{ -	unsigned int ofs, cmd; - -	if (*from & 0x200) { -		cmd = NAND_CMD_READOOB; -		ofs = 10 + (*from & 0xf); -	} else { -		cmd = NAND_CMD_READ1; -		ofs = (*from & 0xf); -	} - -	*from = (*from & ~0x3ff) | ofs; -	return cmd; -} - -static unsigned int DoC_GetFlagsOffset(struct mtd_info *mtd, loff_t *from) -{ -	unsigned int ofs, cmd; - -	cmd = NAND_CMD_READ1; -	ofs = (*from & 0x200) ? 8 : 6; -	*from = (*from & ~0x3ff) | ofs; -	return cmd; -} - -static unsigned int DoC_GetHdrOffset(struct mtd_info *mtd, loff_t *from) -{ -	unsigned int ofs, cmd; - -	cmd = NAND_CMD_READOOB; -	ofs = (*from & 0x200) ? 24 : 16; -	*from = (*from & ~0x3ff) | ofs; -	return cmd; -} - -static inline void MemReadDOC(void __iomem * docptr, unsigned char *buf, int len) -{ -#ifndef USE_MEMCPY -	int i; -	for (i = 0; i < len; i++) -		buf[i] = ReadDOC(docptr, Mil_CDSN_IO + i); -#else -	memcpy_fromio(buf, docptr + DoC_Mil_CDSN_IO, len); -#endif -} - -static inline void MemWriteDOC(void __iomem * docptr, unsigned char *buf, int len) -{ -#ifndef USE_MEMCPY -	int i; -	for (i = 0; i < len; i++) -		WriteDOC(buf[i], docptr, Mil_CDSN_IO + i); -#else -	memcpy_toio(docptr + DoC_Mil_CDSN_IO, buf, len); -#endif -} - -/* DoC_IdentChip: Identify a given NAND chip given {floor,chip} */ -static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) -{ -	int mfr, id, i, j; -	volatile char dummy; -	void __iomem * docptr = doc->virtadr; - -	/* Page in the required floor/chip */ -	DoC_SelectFloor(docptr, floor); -	DoC_SelectChip(docptr, chip); - -	/* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ -	WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); - -	/* Reset the chip, see Software Requirement 11.4 item 1. */ -	DoC_Command(docptr, NAND_CMD_RESET, 0); -	DoC_WaitReady(docptr); - -	/* Read the NAND chip ID: 1. Send ReadID command */ -	DoC_Command(docptr, NAND_CMD_READID, 0); - -	/* Read the NAND chip ID: 2. Send address byte zero */ -	DoC_Address(doc, 1, 0x00, 0, 0x00); - -	WriteDOC(0, docptr, Mplus_FlashControl); -	DoC_WaitReady(docptr); - -	/* Read the manufacturer and device id codes of the flash device through -	   CDSN IO register see Software Requirement 11.4 item 5.*/ -	dummy = ReadDOC(docptr, Mplus_ReadPipeInit); -	dummy = ReadDOC(docptr, Mplus_ReadPipeInit); - -	mfr = ReadDOC(docptr, Mil_CDSN_IO); -	if (doc->interleave) -		dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */ - -	id  = ReadDOC(docptr, Mil_CDSN_IO); -	if (doc->interleave) -		dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */ - -	dummy = ReadDOC(docptr, Mplus_LastDataRead); -	dummy = ReadDOC(docptr, Mplus_LastDataRead); - -	/* Disable flash internally */ -	WriteDOC(0, docptr, Mplus_FlashSelect); - -	/* No response - return failure */ -	if (mfr == 0xff || mfr == 0) -		return 0; - -	for (i = 0; nand_flash_ids[i].name != NULL; i++) { -		if (id == nand_flash_ids[i].id) { -			/* Try to identify manufacturer */ -			for (j = 0; nand_manuf_ids[j].id != 0x0; j++) { -				if (nand_manuf_ids[j].id == mfr) -					break; -			} -			printk(KERN_INFO "Flash chip found: Manufacturer ID: %2.2X, " -			       "Chip ID: %2.2X (%s:%s)\n", mfr, id, -			       nand_manuf_ids[j].name, nand_flash_ids[i].name); -			doc->mfr = mfr; -			doc->id = id; -			doc->chipshift = ffs((nand_flash_ids[i].chipsize << 20)) - 1; -			doc->erasesize = nand_flash_ids[i].erasesize << doc->interleave; -			break; -		} -	} - -	if (nand_flash_ids[i].name == NULL) -		return 0; -	return 1; -} - -/* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */ -static void DoC_ScanChips(struct DiskOnChip *this) -{ -	int floor, chip; -	int numchips[MAX_FLOORS_MPLUS]; -	int ret; - -	this->numchips = 0; -	this->mfr = 0; -	this->id = 0; - -	/* Work out the intended interleave setting */ -	this->interleave = 0; -	if (this->ChipID == DOC_ChipID_DocMilPlus32) -		this->interleave = 1; - -	/* Check the ASIC agrees */ -	if ( (this->interleave << 2) != -	     (ReadDOC(this->virtadr, Mplus_Configuration) & 4)) { -		u_char conf = ReadDOC(this->virtadr, Mplus_Configuration); -		printk(KERN_NOTICE "Setting DiskOnChip Millennium Plus interleave to %s\n", -		       this->interleave?"on (16-bit)":"off (8-bit)"); -		conf ^= 4; -		WriteDOC(conf, this->virtadr, Mplus_Configuration); -	} - -	/* For each floor, find the number of valid chips it contains */ -	for (floor = 0,ret = 1; floor < MAX_FLOORS_MPLUS; floor++) { -		numchips[floor] = 0; -		for (chip = 0; chip < MAX_CHIPS_MPLUS && ret != 0; chip++) { -			ret = DoC_IdentChip(this, floor, chip); -			if (ret) { -				numchips[floor]++; -				this->numchips++; -			} -		} -	} -	/* If there are none at all that we recognise, bail */ -	if (!this->numchips) { -		printk("No flash chips recognised.\n"); -		return; -	} - -	/* Allocate an array to hold the information for each chip */ -	this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL); -	if (!this->chips){ -		printk("MTD: No memory for allocating chip info structures\n"); -		return; -	} - -	/* Fill out the chip array with {floor, chipno} for each -	 * detected chip in the device. */ -	for (floor = 0, ret = 0; floor < MAX_FLOORS_MPLUS; floor++) { -		for (chip = 0 ; chip < numchips[floor] ; chip++) { -			this->chips[ret].floor = floor; -			this->chips[ret].chip = chip; -			this->chips[ret].curadr = 0; -			this->chips[ret].curmode = 0x50; -			ret++; -		} -	} - -	/* Calculate and print the total size of the device */ -	this->totlen = this->numchips * (1 << this->chipshift); -	printk(KERN_INFO "%d flash chips found. Total DiskOnChip size: %ld MiB\n", -	       this->numchips ,this->totlen >> 20); -} - -static int DoCMilPlus_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2) -{ -	int tmp1, tmp2, retval; - -	if (doc1->physadr == doc2->physadr) -		return 1; - -	/* Use the alias resolution register which was set aside for this -	 * purpose. If it's value is the same on both chips, they might -	 * be the same chip, and we write to one and check for a change in -	 * the other. It's unclear if this register is usuable in the -	 * DoC 2000 (it's in the Millennium docs), but it seems to work. */ -	tmp1 = ReadDOC(doc1->virtadr, Mplus_AliasResolution); -	tmp2 = ReadDOC(doc2->virtadr, Mplus_AliasResolution); -	if (tmp1 != tmp2) -		return 0; - -	WriteDOC((tmp1+1) % 0xff, doc1->virtadr, Mplus_AliasResolution); -	tmp2 = ReadDOC(doc2->virtadr, Mplus_AliasResolution); -	if (tmp2 == (tmp1+1) % 0xff) -		retval = 1; -	else -		retval = 0; - -	/* Restore register contents.  May not be necessary, but do it just to -	 * be safe. */ -	WriteDOC(tmp1, doc1->virtadr, Mplus_AliasResolution); - -	return retval; -} - -/* This routine is found from the docprobe code by symbol_get(), - * which will bump the use count of this module. */ -void DoCMilPlus_init(struct mtd_info *mtd) -{ -	struct DiskOnChip *this = mtd->priv; -	struct DiskOnChip *old = NULL; - -	/* We must avoid being called twice for the same device. */ -	if (docmilpluslist) -		old = docmilpluslist->priv; - -	while (old) { -		if (DoCMilPlus_is_alias(this, old)) { -			printk(KERN_NOTICE "Ignoring DiskOnChip Millennium " -				"Plus at 0x%lX - already configured\n", -				this->physadr); -			iounmap(this->virtadr); -			kfree(mtd); -			return; -		} -		if (old->nextdoc) -			old = old->nextdoc->priv; -		else -			old = NULL; -	} - -	mtd->name = "DiskOnChip Millennium Plus"; -	printk(KERN_NOTICE "DiskOnChip Millennium Plus found at " -		"address 0x%lX\n", this->physadr); - -	mtd->type = MTD_NANDFLASH; -	mtd->flags = MTD_CAP_NANDFLASH; -	mtd->size = 0; - -	mtd->erasesize = 0; -	mtd->writesize = 512; -	mtd->oobsize = 16; -	mtd->owner = THIS_MODULE; -	mtd->erase = doc_erase; -	mtd->point = NULL; -	mtd->unpoint = NULL; -	mtd->read = doc_read; -	mtd->write = doc_write; -	mtd->read_oob = doc_read_oob; -	mtd->write_oob = doc_write_oob; -	mtd->sync = NULL; - -	this->totlen = 0; -	this->numchips = 0; -	this->curfloor = -1; -	this->curchip = -1; - -	/* Ident all the chips present. */ -	DoC_ScanChips(this); - -	if (!this->totlen) { -		kfree(mtd); -		iounmap(this->virtadr); -	} else { -		this->nextdoc = docmilpluslist; -		docmilpluslist = mtd; -		mtd->size  = this->totlen; -		mtd->erasesize = this->erasesize; -		add_mtd_device(mtd); -		return; -	} -} -EXPORT_SYMBOL_GPL(DoCMilPlus_init); - -#if 0 -static int doc_dumpblk(struct mtd_info *mtd, loff_t from) -{ -	int i; -	loff_t fofs; -	struct DiskOnChip *this = mtd->priv; -	void __iomem * docptr = this->virtadr; -	struct Nand *mychip = &this->chips[from >> (this->chipshift)]; -	unsigned char *bp, buf[1056]; -	char c[32]; - -	from &= ~0x3ff; - -	/* Don't allow read past end of device */ -	if (from >= this->totlen) -		return -EINVAL; - -	DoC_CheckASIC(docptr); - -	/* Find the chip which is to be used and select it */ -	if (this->curfloor != mychip->floor) { -		DoC_SelectFloor(docptr, mychip->floor); -		DoC_SelectChip(docptr, mychip->chip); -	} else if (this->curchip != mychip->chip) { -		DoC_SelectChip(docptr, mychip->chip); -	} -	this->curfloor = mychip->floor; -	this->curchip = mychip->chip; - -	/* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ -	WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); - -	/* Reset the chip, see Software Requirement 11.4 item 1. */ -	DoC_Command(docptr, NAND_CMD_RESET, 0); -	DoC_WaitReady(docptr); - -	fofs = from; -	DoC_Command(docptr, DoC_GetDataOffset(mtd, &fofs), 0); -	DoC_Address(this, 3, fofs, 0, 0x00); -	WriteDOC(0, docptr, Mplus_FlashControl); -	DoC_WaitReady(docptr); - -	/* disable the ECC engine */ -	WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); - -	ReadDOC(docptr, Mplus_ReadPipeInit); -	ReadDOC(docptr, Mplus_ReadPipeInit); - -	/* Read the data via the internal pipeline through CDSN IO -	   register, see Pipelined Read Operations 11.3 */ -	MemReadDOC(docptr, buf, 1054); -	buf[1054] = ReadDOC(docptr, Mplus_LastDataRead); -	buf[1055] = ReadDOC(docptr, Mplus_LastDataRead); - -	memset(&c[0], 0, sizeof(c)); -	printk("DUMP OFFSET=%x:\n", (int)from); - -        for (i = 0, bp = &buf[0]; (i < 1056); i++) { -                if ((i % 16) == 0) -                        printk("%08x: ", i); -                printk(" %02x", *bp); -                c[(i & 0xf)] = ((*bp >= 0x20) && (*bp <= 0x7f)) ? *bp : '.'; -                bp++; -                if (((i + 1) % 16) == 0) -                        printk("    %s\n", c); -        } -	printk("\n"); - -	/* Disable flash internally */ -	WriteDOC(0, docptr, Mplus_FlashSelect); - -	return 0; -} -#endif - -static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, -		    size_t *retlen, u_char *buf) -{ -	int ret, i; -	volatile char dummy; -	loff_t fofs; -	unsigned char syndrome[6], eccbuf[6]; -	struct DiskOnChip *this = mtd->priv; -	void __iomem * docptr = this->virtadr; -	struct Nand *mychip = &this->chips[from >> (this->chipshift)]; - -	/* Don't allow read past end of device */ -	if (from >= this->totlen) -		return -EINVAL; - -	/* Don't allow a single read to cross a 512-byte block boundary */ -	if (from + len > ((from | 0x1ff) + 1)) -		len = ((from | 0x1ff) + 1) - from; - -	DoC_CheckASIC(docptr); - -	/* Find the chip which is to be used and select it */ -	if (this->curfloor != mychip->floor) { -		DoC_SelectFloor(docptr, mychip->floor); -		DoC_SelectChip(docptr, mychip->chip); -	} else if (this->curchip != mychip->chip) { -		DoC_SelectChip(docptr, mychip->chip); -	} -	this->curfloor = mychip->floor; -	this->curchip = mychip->chip; - -	/* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ -	WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); - -	/* Reset the chip, see Software Requirement 11.4 item 1. */ -	DoC_Command(docptr, NAND_CMD_RESET, 0); -	DoC_WaitReady(docptr); - -	fofs = from; -	DoC_Command(docptr, DoC_GetDataOffset(mtd, &fofs), 0); -	DoC_Address(this, 3, fofs, 0, 0x00); -	WriteDOC(0, docptr, Mplus_FlashControl); -	DoC_WaitReady(docptr); - -	/* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/ -	WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); -	WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf); - -	/* Let the caller know we completed it */ -	*retlen = len; -	ret = 0; - -	ReadDOC(docptr, Mplus_ReadPipeInit); -	ReadDOC(docptr, Mplus_ReadPipeInit); - -	/* Read the data via the internal pipeline through CDSN IO -	   register, see Pipelined Read Operations 11.3 */ -	MemReadDOC(docptr, buf, len); - -	/* Read the ECC data following raw data */ -	MemReadDOC(docptr, eccbuf, 4); -	eccbuf[4] = ReadDOC(docptr, Mplus_LastDataRead); -	eccbuf[5] = ReadDOC(docptr, Mplus_LastDataRead); - -	/* Flush the pipeline */ -	dummy = ReadDOC(docptr, Mplus_ECCConf); -	dummy = ReadDOC(docptr, Mplus_ECCConf); - -	/* Check the ECC Status */ -	if (ReadDOC(docptr, Mplus_ECCConf) & 0x80) { -		int nb_errors; -		/* There was an ECC error */ -#ifdef ECC_DEBUG -		printk("DiskOnChip ECC Error: Read at %lx\n", (long)from); -#endif -		/* Read the ECC syndrom through the DiskOnChip ECC logic. -		   These syndrome will be all ZERO when there is no error */ -		for (i = 0; i < 6; i++) -			syndrome[i] = ReadDOC(docptr, Mplus_ECCSyndrome0 + i); - -		nb_errors = doc_decode_ecc(buf, syndrome); -#ifdef ECC_DEBUG -		printk("ECC Errors corrected: %x\n", nb_errors); -#endif -		if (nb_errors < 0) { -			/* We return error, but have actually done the -			   read. Not that this can be told to user-space, via -			   sys_read(), but at least MTD-aware stuff can know -			   about it by checking *retlen */ -#ifdef ECC_DEBUG -			printk("%s(%d): Millennium Plus ECC error (from=0x%x:\n", -				__FILE__, __LINE__, (int)from); -			printk("        syndrome= %02x:%02x:%02x:%02x:%02x:" -				"%02x\n", -				syndrome[0], syndrome[1], syndrome[2], -				syndrome[3], syndrome[4], syndrome[5]); -			printk("          eccbuf= %02x:%02x:%02x:%02x:%02x:" -				"%02x\n", -				eccbuf[0], eccbuf[1], eccbuf[2], -				eccbuf[3], eccbuf[4], eccbuf[5]); -#endif -				ret = -EIO; -		} -	} - -#ifdef PSYCHO_DEBUG -	printk("ECC DATA at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", -	       (long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], -	       eccbuf[4], eccbuf[5]); -#endif -	/* disable the ECC engine */ -	WriteDOC(DOC_ECC_DIS, docptr , Mplus_ECCConf); - -	/* Disable flash internally */ -	WriteDOC(0, docptr, Mplus_FlashSelect); - -	return ret; -} - -static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, -		     size_t *retlen, const u_char *buf) -{ -	int i, before, ret = 0; -	loff_t fto; -	volatile char dummy; -	char eccbuf[6]; -	struct DiskOnChip *this = mtd->priv; -	void __iomem * docptr = this->virtadr; -	struct Nand *mychip = &this->chips[to >> (this->chipshift)]; - -	/* Don't allow write past end of device */ -	if (to >= this->totlen) -		return -EINVAL; - -	/* Don't allow writes which aren't exactly one block (512 bytes) */ -	if ((to & 0x1ff) || (len != 0x200)) -		return -EINVAL; - -	/* Determine position of OOB flags, before or after data */ -	before = (this->interleave && (to & 0x200)); - -	DoC_CheckASIC(docptr); - -	/* Find the chip which is to be used and select it */ -	if (this->curfloor != mychip->floor) { -		DoC_SelectFloor(docptr, mychip->floor); -		DoC_SelectChip(docptr, mychip->chip); -	} else if (this->curchip != mychip->chip) { -		DoC_SelectChip(docptr, mychip->chip); -	} -	this->curfloor = mychip->floor; -	this->curchip = mychip->chip; - -	/* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ -	WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); - -	/* Reset the chip, see Software Requirement 11.4 item 1. */ -	DoC_Command(docptr, NAND_CMD_RESET, 0); -	DoC_WaitReady(docptr); - -	/* Set device to appropriate plane of flash */ -	fto = to; -	WriteDOC(DoC_GetDataOffset(mtd, &fto), docptr, Mplus_FlashCmd); - -	/* On interleaved devices the flags for 2nd half 512 are before data */ -	if (before) -		fto -= 2; - -	/* issue the Serial Data In command to initial the Page Program process */ -	DoC_Command(docptr, NAND_CMD_SEQIN, 0x00); -	DoC_Address(this, 3, fto, 0x00, 0x00); - -	/* Disable the ECC engine */ -	WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); - -	if (before) { -		/* Write the block status BLOCK_USED (0x5555) */ -		WriteDOC(0x55, docptr, Mil_CDSN_IO); -		WriteDOC(0x55, docptr, Mil_CDSN_IO); -	} - -	/* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/ -	WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf); - -	MemWriteDOC(docptr, (unsigned char *) buf, len); - -	/* Write ECC data to flash, the ECC info is generated by -	   the DiskOnChip ECC logic see Reed-Solomon EDC/ECC 11.1 */ -	DoC_Delay(docptr, 3); - -	/* Read the ECC data through the DiskOnChip ECC logic */ -	for (i = 0; i < 6; i++) -		eccbuf[i] = ReadDOC(docptr, Mplus_ECCSyndrome0 + i); - -	/* disable the ECC engine */ -	WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); - -	/* Write the ECC data to flash */ -	MemWriteDOC(docptr, eccbuf, 6); - -	if (!before) { -		/* Write the block status BLOCK_USED (0x5555) */ -		WriteDOC(0x55, docptr, Mil_CDSN_IO+6); -		WriteDOC(0x55, docptr, Mil_CDSN_IO+7); -	} - -#ifdef PSYCHO_DEBUG -	printk("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", -	       (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], -	       eccbuf[4], eccbuf[5]); -#endif - -	WriteDOC(0x00, docptr, Mplus_WritePipeTerm); -	WriteDOC(0x00, docptr, Mplus_WritePipeTerm); - -	/* Commit the Page Program command and wait for ready -	   see Software Requirement 11.4 item 1.*/ -	DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00); -	DoC_WaitReady(docptr); - -	/* Read the status of the flash device through CDSN IO register -	   see Software Requirement 11.4 item 5.*/ -	DoC_Command(docptr, NAND_CMD_STATUS, 0); -	dummy = ReadDOC(docptr, Mplus_ReadPipeInit); -	dummy = ReadDOC(docptr, Mplus_ReadPipeInit); -	DoC_Delay(docptr, 2); -	if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { -		printk("MTD: Error 0x%x programming at 0x%x\n", dummy, (int)to); -		/* Error in programming -		   FIXME: implement Bad Block Replacement (in nftl.c ??) */ -		*retlen = 0; -		ret = -EIO; -	} -	dummy = ReadDOC(docptr, Mplus_LastDataRead); - -	/* Disable flash internally */ -	WriteDOC(0, docptr, Mplus_FlashSelect); - -	/* Let the caller know we completed it */ -	*retlen = len; - -	return ret; -} - -static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, -			struct mtd_oob_ops *ops) -{ -	loff_t fofs, base; -	struct DiskOnChip *this = mtd->priv; -	void __iomem * docptr = this->virtadr; -	struct Nand *mychip = &this->chips[ofs >> this->chipshift]; -	size_t i, size, got, want; -	uint8_t *buf = ops->oobbuf; -	size_t len = ops->len; - -	BUG_ON(ops->mode != MTD_OOB_PLACE); - -	ofs += ops->ooboffs; - -	DoC_CheckASIC(docptr); - -	/* Find the chip which is to be used and select it */ -	if (this->curfloor != mychip->floor) { -		DoC_SelectFloor(docptr, mychip->floor); -		DoC_SelectChip(docptr, mychip->chip); -	} else if (this->curchip != mychip->chip) { -		DoC_SelectChip(docptr, mychip->chip); -	} -	this->curfloor = mychip->floor; -	this->curchip = mychip->chip; - -	/* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ -	WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); - -	/* disable the ECC engine */ -	WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); -	DoC_WaitReady(docptr); - -	/* Maximum of 16 bytes in the OOB region, so limit read to that */ -	if (len > 16) -		len = 16; -	got = 0; -	want = len; - -	for (i = 0; ((i < 3) && (want > 0)); i++) { -		/* Figure out which region we are accessing... */ -		fofs = ofs; -		base = ofs & 0xf; -		if (!this->interleave) { -			DoC_Command(docptr, NAND_CMD_READOOB, 0); -			size = 16 - base; -		} else if (base < 6) { -			DoC_Command(docptr, DoC_GetECCOffset(mtd, &fofs), 0); -			size = 6 - base; -		} else if (base < 8) { -			DoC_Command(docptr, DoC_GetFlagsOffset(mtd, &fofs), 0); -			size = 8 - base; -		} else { -			DoC_Command(docptr, DoC_GetHdrOffset(mtd, &fofs), 0); -			size = 16 - base; -		} -		if (size > want) -			size = want; - -		/* Issue read command */ -		DoC_Address(this, 3, fofs, 0, 0x00); -		WriteDOC(0, docptr, Mplus_FlashControl); -		DoC_WaitReady(docptr); - -		ReadDOC(docptr, Mplus_ReadPipeInit); -		ReadDOC(docptr, Mplus_ReadPipeInit); -		MemReadDOC(docptr, &buf[got], size - 2); -		buf[got + size - 2] = ReadDOC(docptr, Mplus_LastDataRead); -		buf[got + size - 1] = ReadDOC(docptr, Mplus_LastDataRead); - -		ofs += size; -		got += size; -		want -= size; -	} - -	/* Disable flash internally */ -	WriteDOC(0, docptr, Mplus_FlashSelect); - -	ops->retlen = len; -	return 0; -} - -static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, -			 struct mtd_oob_ops *ops) -{ -	volatile char dummy; -	loff_t fofs, base; -	struct DiskOnChip *this = mtd->priv; -	void __iomem * docptr = this->virtadr; -	struct Nand *mychip = &this->chips[ofs >> this->chipshift]; -	size_t i, size, got, want; -	int ret = 0; -	uint8_t *buf = ops->oobbuf; -	size_t len = ops->len; - -	BUG_ON(ops->mode != MTD_OOB_PLACE); - -	ofs += ops->ooboffs; - -	DoC_CheckASIC(docptr); - -	/* Find the chip which is to be used and select it */ -	if (this->curfloor != mychip->floor) { -		DoC_SelectFloor(docptr, mychip->floor); -		DoC_SelectChip(docptr, mychip->chip); -	} else if (this->curchip != mychip->chip) { -		DoC_SelectChip(docptr, mychip->chip); -	} -	this->curfloor = mychip->floor; -	this->curchip = mychip->chip; - -	/* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ -	WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); - - -	/* Maximum of 16 bytes in the OOB region, so limit write to that */ -	if (len > 16) -		len = 16; -	got = 0; -	want = len; - -	for (i = 0; ((i < 3) && (want > 0)); i++) { -		/* Reset the chip, see Software Requirement 11.4 item 1. */ -		DoC_Command(docptr, NAND_CMD_RESET, 0); -		DoC_WaitReady(docptr); - -		/* Figure out which region we are accessing... */ -		fofs = ofs; -		base = ofs & 0x0f; -		if (!this->interleave) { -			WriteDOC(NAND_CMD_READOOB, docptr, Mplus_FlashCmd); -			size = 16 - base; -		} else if (base < 6) { -			WriteDOC(DoC_GetECCOffset(mtd, &fofs), docptr, Mplus_FlashCmd); -			size = 6 - base; -		} else if (base < 8) { -			WriteDOC(DoC_GetFlagsOffset(mtd, &fofs), docptr, Mplus_FlashCmd); -			size = 8 - base; -		} else { -			WriteDOC(DoC_GetHdrOffset(mtd, &fofs), docptr, Mplus_FlashCmd); -			size = 16 - base; -		} -		if (size > want) -			size = want; - -		/* Issue the Serial Data In command to initial the Page Program process */ -		DoC_Command(docptr, NAND_CMD_SEQIN, 0x00); -		DoC_Address(this, 3, fofs, 0, 0x00); - -		/* Disable the ECC engine */ -		WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); - -		/* Write the data via the internal pipeline through CDSN IO -		   register, see Pipelined Write Operations 11.2 */ -		MemWriteDOC(docptr, (unsigned char *) &buf[got], size); -		WriteDOC(0x00, docptr, Mplus_WritePipeTerm); -		WriteDOC(0x00, docptr, Mplus_WritePipeTerm); - -		/* Commit the Page Program command and wait for ready -	 	   see Software Requirement 11.4 item 1.*/ -		DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00); -		DoC_WaitReady(docptr); - -		/* Read the status of the flash device through CDSN IO register -		   see Software Requirement 11.4 item 5.*/ -		DoC_Command(docptr, NAND_CMD_STATUS, 0x00); -		dummy = ReadDOC(docptr, Mplus_ReadPipeInit); -		dummy = ReadDOC(docptr, Mplus_ReadPipeInit); -		DoC_Delay(docptr, 2); -		if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { -			printk("MTD: Error 0x%x programming oob at 0x%x\n", -				dummy, (int)ofs); -			/* FIXME: implement Bad Block Replacement */ -			ops->retlen = 0; -			ret = -EIO; -		} -		dummy = ReadDOC(docptr, Mplus_LastDataRead); - -		ofs += size; -		got += size; -		want -= size; -	} - -	/* Disable flash internally */ -	WriteDOC(0, docptr, Mplus_FlashSelect); - -	ops->retlen = len; -	return ret; -} - -int doc_erase(struct mtd_info *mtd, struct erase_info *instr) -{ -	volatile char dummy; -	struct DiskOnChip *this = mtd->priv; -	__u32 ofs = instr->addr; -	__u32 len = instr->len; -	void __iomem * docptr = this->virtadr; -	struct Nand *mychip = &this->chips[ofs >> this->chipshift]; - -	DoC_CheckASIC(docptr); - -	if (len != mtd->erasesize) -		printk(KERN_WARNING "MTD: Erase not right size (%x != %x)n", -		       len, mtd->erasesize); - -	/* Find the chip which is to be used and select it */ -	if (this->curfloor != mychip->floor) { -		DoC_SelectFloor(docptr, mychip->floor); -		DoC_SelectChip(docptr, mychip->chip); -	} else if (this->curchip != mychip->chip) { -		DoC_SelectChip(docptr, mychip->chip); -	} -	this->curfloor = mychip->floor; -	this->curchip = mychip->chip; - -	instr->state = MTD_ERASE_PENDING; - -	/* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ -	WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); - -	DoC_Command(docptr, NAND_CMD_RESET, 0x00); -	DoC_WaitReady(docptr); - -	DoC_Command(docptr, NAND_CMD_ERASE1, 0); -	DoC_Address(this, 2, ofs, 0, 0x00); -	DoC_Command(docptr, NAND_CMD_ERASE2, 0); -	DoC_WaitReady(docptr); -	instr->state = MTD_ERASING; - -	/* Read the status of the flash device through CDSN IO register -	   see Software Requirement 11.4 item 5. */ -	DoC_Command(docptr, NAND_CMD_STATUS, 0); -	dummy = ReadDOC(docptr, Mplus_ReadPipeInit); -	dummy = ReadDOC(docptr, Mplus_ReadPipeInit); -	if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { -		printk("MTD: Error 0x%x erasing at 0x%x\n", dummy, ofs); -		/* FIXME: implement Bad Block Replacement (in nftl.c ??) */ -		instr->state = MTD_ERASE_FAILED; -	} else { -		instr->state = MTD_ERASE_DONE; -	} -	dummy = ReadDOC(docptr, Mplus_LastDataRead); - -	/* Disable flash internally */ -	WriteDOC(0, docptr, Mplus_FlashSelect); - -	mtd_erase_callback(instr); - -	return 0; -} - -/**************************************************************************** - * - * Module stuff - * - ****************************************************************************/ - -static void __exit cleanup_doc2001plus(void) -{ -	struct mtd_info *mtd; -	struct DiskOnChip *this; - -	while ((mtd=docmilpluslist)) { -		this = mtd->priv; -		docmilpluslist = this->nextdoc; - -		del_mtd_device(mtd); - -		iounmap(this->virtadr); -		kfree(this->chips); -		kfree(mtd); -	} -} - -module_exit(cleanup_doc2001plus); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com> et al."); -MODULE_DESCRIPTION("Driver for DiskOnChip Millennium Plus"); diff --git a/drivers/mtd/devices/docecc.c b/drivers/mtd/devices/docecc.c deleted file mode 100644 index a99838bb2dc..00000000000 --- a/drivers/mtd/devices/docecc.c +++ /dev/null @@ -1,521 +0,0 @@ -/* - * ECC algorithm for M-systems disk on chip. We use the excellent Reed - * Solmon code of Phil Karn (karn@ka9q.ampr.org) available under the - * GNU GPL License. The rest is simply to convert the disk on chip - * syndrom into a standard syndom. - * - * Author: Fabrice Bellard (fabrice.bellard@netgem.com) - * Copyright (C) 2000 Netgem S.A. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA - */ -#include <linux/kernel.h> -#include <linux/module.h> -#include <asm/errno.h> -#include <asm/io.h> -#include <asm/uaccess.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/types.h> - -#include <linux/mtd/mtd.h> -#include <linux/mtd/doc2000.h> - -#define DEBUG_ECC 0 -/* need to undef it (from asm/termbits.h) */ -#undef B0 - -#define MM 10 /* Symbol size in bits */ -#define KK (1023-4) /* Number of data symbols per block */ -#define B0 510 /* First root of generator polynomial, alpha form */ -#define PRIM 1 /* power of alpha used to generate roots of generator poly */ -#define	NN ((1 << MM) - 1) - -typedef unsigned short dtype; - -/* 1+x^3+x^10 */ -static const int Pp[MM+1] = { 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1 }; - -/* This defines the type used to store an element of the Galois Field - * used by the code. Make sure this is something larger than a char if - * if anything larger than GF(256) is used. - * - * Note: unsigned char will work up to GF(256) but int seems to run - * faster on the Pentium. - */ -typedef int gf; - -/* No legal value in index form represents zero, so - * we need a special value for this purpose - */ -#define A0	(NN) - -/* Compute x % NN, where NN is 2**MM - 1, - * without a slow divide - */ -static inline gf -modnn(int x) -{ -  while (x >= NN) { -    x -= NN; -    x = (x >> MM) + (x & NN); -  } -  return x; -} - -#define	CLEAR(a,n) {\ -int ci;\ -for(ci=(n)-1;ci >=0;ci--)\ -(a)[ci] = 0;\ -} - -#define	COPY(a,b,n) {\ -int ci;\ -for(ci=(n)-1;ci >=0;ci--)\ -(a)[ci] = (b)[ci];\ -} - -#define	COPYDOWN(a,b,n) {\ -int ci;\ -for(ci=(n)-1;ci >=0;ci--)\ -(a)[ci] = (b)[ci];\ -} - -#define Ldec 1 - -/* generate GF(2**m) from the irreducible polynomial p(X) in Pp[0]..Pp[m] -   lookup tables:  index->polynomial form   alpha_to[] contains j=alpha**i; -                   polynomial form -> index form  index_of[j=alpha**i] = i -   alpha=2 is the primitive element of GF(2**m) -   HARI's COMMENT: (4/13/94) alpha_to[] can be used as follows: -        Let @ represent the primitive element commonly called "alpha" that -   is the root of the primitive polynomial p(x). Then in GF(2^m), for any -   0 <= i <= 2^m-2, -        @^i = a(0) + a(1) @ + a(2) @^2 + ... + a(m-1) @^(m-1) -   where the binary vector (a(0),a(1),a(2),...,a(m-1)) is the representation -   of the integer "alpha_to[i]" with a(0) being the LSB and a(m-1) the MSB. Thus for -   example the polynomial representation of @^5 would be given by the binary -   representation of the integer "alpha_to[5]". -                   Similarily, index_of[] can be used as follows: -        As above, let @ represent the primitive element of GF(2^m) that is -   the root of the primitive polynomial p(x). In order to find the power -   of @ (alpha) that has the polynomial representation -        a(0) + a(1) @ + a(2) @^2 + ... + a(m-1) @^(m-1) -   we consider the integer "i" whose binary representation with a(0) being LSB -   and a(m-1) MSB is (a(0),a(1),...,a(m-1)) and locate the entry -   "index_of[i]". Now, @^index_of[i] is that element whose polynomial -    representation is (a(0),a(1),a(2),...,a(m-1)). -   NOTE: -        The element alpha_to[2^m-1] = 0 always signifying that the -   representation of "@^infinity" = 0 is (0,0,0,...,0). -        Similarily, the element index_of[0] = A0 always signifying -   that the power of alpha which has the polynomial representation -   (0,0,...,0) is "infinity". - -*/ - -static void -generate_gf(dtype Alpha_to[NN + 1], dtype Index_of[NN + 1]) -{ -  register int i, mask; - -  mask = 1; -  Alpha_to[MM] = 0; -  for (i = 0; i < MM; i++) { -    Alpha_to[i] = mask; -    Index_of[Alpha_to[i]] = i; -    /* If Pp[i] == 1 then, term @^i occurs in poly-repr of @^MM */ -    if (Pp[i] != 0) -      Alpha_to[MM] ^= mask;	/* Bit-wise EXOR operation */ -    mask <<= 1;	/* single left-shift */ -  } -  Index_of[Alpha_to[MM]] = MM; -  /* -   * Have obtained poly-repr of @^MM. Poly-repr of @^(i+1) is given by -   * poly-repr of @^i shifted left one-bit and accounting for any @^MM -   * term that may occur when poly-repr of @^i is shifted. -   */ -  mask >>= 1; -  for (i = MM + 1; i < NN; i++) { -    if (Alpha_to[i - 1] >= mask) -      Alpha_to[i] = Alpha_to[MM] ^ ((Alpha_to[i - 1] ^ mask) << 1); -    else -      Alpha_to[i] = Alpha_to[i - 1] << 1; -    Index_of[Alpha_to[i]] = i; -  } -  Index_of[0] = A0; -  Alpha_to[NN] = 0; -} - -/* - * Performs ERRORS+ERASURES decoding of RS codes. bb[] is the content - * of the feedback shift register after having processed the data and - * the ECC. - * - * Return number of symbols corrected, or -1 if codeword is illegal - * or uncorrectable. If eras_pos is non-null, the detected error locations - * are written back. NOTE! This array must be at least NN-KK elements long. - * The corrected data are written in eras_val[]. They must be xor with the data - * to retrieve the correct data : data[erase_pos[i]] ^= erase_val[i] . - * - * First "no_eras" erasures are declared by the calling program. Then, the - * maximum # of errors correctable is t_after_eras = floor((NN-KK-no_eras)/2). - * If the number of channel errors is not greater than "t_after_eras" the - * transmitted codeword will be recovered. Details of algorithm can be found - * in R. Blahut's "Theory ... of Error-Correcting Codes". - - * Warning: the eras_pos[] array must not contain duplicate entries; decoder failure - * will result. The decoder *could* check for this condition, but it would involve - * extra time on every decoding operation. - * */ -static int -eras_dec_rs(dtype Alpha_to[NN + 1], dtype Index_of[NN + 1], -            gf bb[NN - KK + 1], gf eras_val[NN-KK], int eras_pos[NN-KK], -            int no_eras) -{ -  int deg_lambda, el, deg_omega; -  int i, j, r,k; -  gf u,q,tmp,num1,num2,den,discr_r; -  gf lambda[NN-KK + 1], s[NN-KK + 1];	/* Err+Eras Locator poly -					 * and syndrome poly */ -  gf b[NN-KK + 1], t[NN-KK + 1], omega[NN-KK + 1]; -  gf root[NN-KK], reg[NN-KK + 1], loc[NN-KK]; -  int syn_error, count; - -  syn_error = 0; -  for(i=0;i<NN-KK;i++) -      syn_error |= bb[i]; - -  if (!syn_error) { -    /* if remainder is zero, data[] is a codeword and there are no -     * errors to correct. So return data[] unmodified -     */ -    count = 0; -    goto finish; -  } - -  for(i=1;i<=NN-KK;i++){ -    s[i] = bb[0]; -  } -  for(j=1;j<NN-KK;j++){ -    if(bb[j] == 0) -      continue; -    tmp = Index_of[bb[j]]; - -    for(i=1;i<=NN-KK;i++) -      s[i] ^= Alpha_to[modnn(tmp + (B0+i-1)*PRIM*j)]; -  } - -  /* undo the feedback register implicit multiplication and convert -     syndromes to index form */ - -  for(i=1;i<=NN-KK;i++) { -      tmp = Index_of[s[i]]; -      if (tmp != A0) -          tmp = modnn(tmp + 2 * KK * (B0+i-1)*PRIM); -      s[i] = tmp; -  } - -  CLEAR(&lambda[1],NN-KK); -  lambda[0] = 1; - -  if (no_eras > 0) { -    /* Init lambda to be the erasure locator polynomial */ -    lambda[1] = Alpha_to[modnn(PRIM * eras_pos[0])]; -    for (i = 1; i < no_eras; i++) { -      u = modnn(PRIM*eras_pos[i]); -      for (j = i+1; j > 0; j--) { -	tmp = Index_of[lambda[j - 1]]; -	if(tmp != A0) -	  lambda[j] ^= Alpha_to[modnn(u + tmp)]; -      } -    } -#if DEBUG_ECC >= 1 -    /* Test code that verifies the erasure locator polynomial just constructed -       Needed only for decoder debugging. */ - -    /* find roots of the erasure location polynomial */ -    for(i=1;i<=no_eras;i++) -      reg[i] = Index_of[lambda[i]]; -    count = 0; -    for (i = 1,k=NN-Ldec; i <= NN; i++,k = modnn(NN+k-Ldec)) { -      q = 1; -      for (j = 1; j <= no_eras; j++) -	if (reg[j] != A0) { -	  reg[j] = modnn(reg[j] + j); -	  q ^= Alpha_to[reg[j]]; -	} -      if (q != 0) -	continue; -      /* store root and error location number indices */ -      root[count] = i; -      loc[count] = k; -      count++; -    } -    if (count != no_eras) { -      printf("\n lambda(x) is WRONG\n"); -      count = -1; -      goto finish; -    } -#if DEBUG_ECC >= 2 -    printf("\n Erasure positions as determined by roots of Eras Loc Poly:\n"); -    for (i = 0; i < count; i++) -      printf("%d ", loc[i]); -    printf("\n"); -#endif -#endif -  } -  for(i=0;i<NN-KK+1;i++) -    b[i] = Index_of[lambda[i]]; - -  /* -   * Begin Berlekamp-Massey algorithm to determine error+erasure -   * locator polynomial -   */ -  r = no_eras; -  el = no_eras; -  while (++r <= NN-KK) {	/* r is the step number */ -    /* Compute discrepancy at the r-th step in poly-form */ -    discr_r = 0; -    for (i = 0; i < r; i++){ -      if ((lambda[i] != 0) && (s[r - i] != A0)) { -	discr_r ^= Alpha_to[modnn(Index_of[lambda[i]] + s[r - i])]; -      } -    } -    discr_r = Index_of[discr_r];	/* Index form */ -    if (discr_r == A0) { -      /* 2 lines below: B(x) <-- x*B(x) */ -      COPYDOWN(&b[1],b,NN-KK); -      b[0] = A0; -    } else { -      /* 7 lines below: T(x) <-- lambda(x) - discr_r*x*b(x) */ -      t[0] = lambda[0]; -      for (i = 0 ; i < NN-KK; i++) { -	if(b[i] != A0) -	  t[i+1] = lambda[i+1] ^ Alpha_to[modnn(discr_r + b[i])]; -	else -	  t[i+1] = lambda[i+1]; -      } -      if (2 * el <= r + no_eras - 1) { -	el = r + no_eras - el; -	/* -	 * 2 lines below: B(x) <-- inv(discr_r) * -	 * lambda(x) -	 */ -	for (i = 0; i <= NN-KK; i++) -	  b[i] = (lambda[i] == 0) ? A0 : modnn(Index_of[lambda[i]] - discr_r + NN); -      } else { -	/* 2 lines below: B(x) <-- x*B(x) */ -	COPYDOWN(&b[1],b,NN-KK); -	b[0] = A0; -      } -      COPY(lambda,t,NN-KK+1); -    } -  } - -  /* Convert lambda to index form and compute deg(lambda(x)) */ -  deg_lambda = 0; -  for(i=0;i<NN-KK+1;i++){ -    lambda[i] = Index_of[lambda[i]]; -    if(lambda[i] != A0) -      deg_lambda = i; -  } -  /* -   * Find roots of the error+erasure locator polynomial by Chien -   * Search -   */ -  COPY(®[1],&lambda[1],NN-KK); -  count = 0;		/* Number of roots of lambda(x) */ -  for (i = 1,k=NN-Ldec; i <= NN; i++,k = modnn(NN+k-Ldec)) { -    q = 1; -    for (j = deg_lambda; j > 0; j--){ -      if (reg[j] != A0) { -	reg[j] = modnn(reg[j] + j); -	q ^= Alpha_to[reg[j]]; -      } -    } -    if (q != 0) -      continue; -    /* store root (index-form) and error location number */ -    root[count] = i; -    loc[count] = k; -    /* If we've already found max possible roots, -     * abort the search to save time -     */ -    if(++count == deg_lambda) -      break; -  } -  if (deg_lambda != count) { -    /* -     * deg(lambda) unequal to number of roots => uncorrectable -     * error detected -     */ -    count = -1; -    goto finish; -  } -  /* -   * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo -   * x**(NN-KK)). in index form. Also find deg(omega). -   */ -  deg_omega = 0; -  for (i = 0; i < NN-KK;i++){ -    tmp = 0; -    j = (deg_lambda < i) ? deg_lambda : i; -    for(;j >= 0; j--){ -      if ((s[i + 1 - j] != A0) && (lambda[j] != A0)) -	tmp ^= Alpha_to[modnn(s[i + 1 - j] + lambda[j])]; -    } -    if(tmp != 0) -      deg_omega = i; -    omega[i] = Index_of[tmp]; -  } -  omega[NN-KK] = A0; - -  /* -   * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 = -   * inv(X(l))**(B0-1) and den = lambda_pr(inv(X(l))) all in poly-form -   */ -  for (j = count-1; j >=0; j--) { -    num1 = 0; -    for (i = deg_omega; i >= 0; i--) { -      if (omega[i] != A0) -	num1  ^= Alpha_to[modnn(omega[i] + i * root[j])]; -    } -    num2 = Alpha_to[modnn(root[j] * (B0 - 1) + NN)]; -    den = 0; - -    /* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */ -    for (i = min(deg_lambda,NN-KK-1) & ~1; i >= 0; i -=2) { -      if(lambda[i+1] != A0) -	den ^= Alpha_to[modnn(lambda[i+1] + i * root[j])]; -    } -    if (den == 0) { -#if DEBUG_ECC >= 1 -      printf("\n ERROR: denominator = 0\n"); -#endif -      /* Convert to dual- basis */ -      count = -1; -      goto finish; -    } -    /* Apply error to data */ -    if (num1 != 0) { -        eras_val[j] = Alpha_to[modnn(Index_of[num1] + Index_of[num2] + NN - Index_of[den])]; -    } else { -        eras_val[j] = 0; -    } -  } - finish: -  for(i=0;i<count;i++) -      eras_pos[i] = loc[i]; -  return count; -} - -/***************************************************************************/ -/* The DOC specific code begins here */ - -#define SECTOR_SIZE 512 -/* The sector bytes are packed into NB_DATA MM bits words */ -#define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / MM) - -/* - * Correct the errors in 'sector[]' by using 'ecc1[]' which is the - * content of the feedback shift register applyied to the sector and - * the ECC. Return the number of errors corrected (and correct them in - * sector), or -1 if error - */ -int doc_decode_ecc(unsigned char sector[SECTOR_SIZE], unsigned char ecc1[6]) -{ -    int parity, i, nb_errors; -    gf bb[NN - KK + 1]; -    gf error_val[NN-KK]; -    int error_pos[NN-KK], pos, bitpos, index, val; -    dtype *Alpha_to, *Index_of; - -    /* init log and exp tables here to save memory. However, it is slower */ -    Alpha_to = kmalloc((NN + 1) * sizeof(dtype), GFP_KERNEL); -    if (!Alpha_to) -        return -1; - -    Index_of = kmalloc((NN + 1) * sizeof(dtype), GFP_KERNEL); -    if (!Index_of) { -        kfree(Alpha_to); -        return -1; -    } - -    generate_gf(Alpha_to, Index_of); - -    parity = ecc1[1]; - -    bb[0] =  (ecc1[4] & 0xff) | ((ecc1[5] & 0x03) << 8); -    bb[1] = ((ecc1[5] & 0xfc) >> 2) | ((ecc1[2] & 0x0f) << 6); -    bb[2] = ((ecc1[2] & 0xf0) >> 4) | ((ecc1[3] & 0x3f) << 4); -    bb[3] = ((ecc1[3] & 0xc0) >> 6) | ((ecc1[0] & 0xff) << 2); - -    nb_errors = eras_dec_rs(Alpha_to, Index_of, bb, -                            error_val, error_pos, 0); -    if (nb_errors <= 0) -        goto the_end; - -    /* correct the errors */ -    for(i=0;i<nb_errors;i++) { -        pos = error_pos[i]; -        if (pos >= NB_DATA && pos < KK) { -            nb_errors = -1; -            goto the_end; -        } -        if (pos < NB_DATA) { -            /* extract bit position (MSB first) */ -            pos = 10 * (NB_DATA - 1 - pos) - 6; -            /* now correct the following 10 bits. At most two bytes -               can be modified since pos is even */ -            index = (pos >> 3) ^ 1; -            bitpos = pos & 7; -            if ((index >= 0 && index < SECTOR_SIZE) || -                index == (SECTOR_SIZE + 1)) { -                val = error_val[i] >> (2 + bitpos); -                parity ^= val; -                if (index < SECTOR_SIZE) -                    sector[index] ^= val; -            } -            index = ((pos >> 3) + 1) ^ 1; -            bitpos = (bitpos + 10) & 7; -            if (bitpos == 0) -                bitpos = 8; -            if ((index >= 0 && index < SECTOR_SIZE) || -                index == (SECTOR_SIZE + 1)) { -                val = error_val[i] << (8 - bitpos); -                parity ^= val; -                if (index < SECTOR_SIZE) -                    sector[index] ^= val; -            } -        } -    } - -    /* use parity to test extra errors */ -    if ((parity & 0xff) != 0) -        nb_errors = -1; - - the_end: -    kfree(Alpha_to); -    kfree(Index_of); -    return nb_errors; -} - -EXPORT_SYMBOL_GPL(doc_decode_ecc); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Fabrice Bellard <fabrice.bellard@netgem.com>"); -MODULE_DESCRIPTION("ECC code for correcting errors detected by DiskOnChip 2000 and Millennium ECC hardware"); diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c new file mode 100644 index 00000000000..91a169c44b3 --- /dev/null +++ b/drivers/mtd/devices/docg3.c @@ -0,0 +1,2143 @@ +/* + * Handles the M-Systems DiskOnChip G3 chip + * + * Copyright (C) 2011 Robert Jarzmik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/bitmap.h> +#include <linux/bitrev.h> +#include <linux/bch.h> + +#include <linux/debugfs.h> +#include <linux/seq_file.h> + +#define CREATE_TRACE_POINTS +#include "docg3.h" + +/* + * This driver handles the DiskOnChip G3 flash memory. + * + * As no specification is available from M-Systems/Sandisk, this drivers lacks + * several functions available on the chip, as : + *  - IPL write + * + * The bus data width (8bits versus 16bits) is not handled (if_cfg flag), and + * the driver assumes a 16bits data bus. + * + * DocG3 relies on 2 ECC algorithms, which are handled in hardware : + *  - a 1 byte Hamming code stored in the OOB for each page + *  - a 7 bytes BCH code stored in the OOB for each page + * The BCH ECC is : + *  - BCH is in GF(2^14) + *  - BCH is over data of 520 bytes (512 page + 7 page_info bytes + *                                   + 1 hamming byte) + *  - BCH can correct up to 4 bits (t = 4) + *  - BCH syndroms are calculated in hardware, and checked in hardware as well + * + */ + +static unsigned int reliable_mode; +module_param(reliable_mode, uint, 0); +MODULE_PARM_DESC(reliable_mode, "Set the docg3 mode (0=normal MLC, 1=fast, " +		 "2=reliable) : MLC normal operations are in normal mode"); + +/** + * struct docg3_oobinfo - DiskOnChip G3 OOB layout + * @eccbytes: 8 bytes are used (1 for Hamming ECC, 7 for BCH ECC) + * @eccpos: ecc positions (byte 7 is Hamming ECC, byte 8-14 are BCH ECC) + * @oobfree: free pageinfo bytes (byte 0 until byte 6, byte 15 + * @oobavail: 8 available bytes remaining after ECC toll + */ +static struct nand_ecclayout docg3_oobinfo = { +	.eccbytes = 8, +	.eccpos = {7, 8, 9, 10, 11, 12, 13, 14}, +	.oobfree = {{0, 7}, {15, 1} }, +	.oobavail = 8, +}; + +static inline u8 doc_readb(struct docg3 *docg3, u16 reg) +{ +	u8 val = readb(docg3->cascade->base + reg); + +	trace_docg3_io(0, 8, reg, (int)val); +	return val; +} + +static inline u16 doc_readw(struct docg3 *docg3, u16 reg) +{ +	u16 val = readw(docg3->cascade->base + reg); + +	trace_docg3_io(0, 16, reg, (int)val); +	return val; +} + +static inline void doc_writeb(struct docg3 *docg3, u8 val, u16 reg) +{ +	writeb(val, docg3->cascade->base + reg); +	trace_docg3_io(1, 8, reg, val); +} + +static inline void doc_writew(struct docg3 *docg3, u16 val, u16 reg) +{ +	writew(val, docg3->cascade->base + reg); +	trace_docg3_io(1, 16, reg, val); +} + +static inline void doc_flash_command(struct docg3 *docg3, u8 cmd) +{ +	doc_writeb(docg3, cmd, DOC_FLASHCOMMAND); +} + +static inline void doc_flash_sequence(struct docg3 *docg3, u8 seq) +{ +	doc_writeb(docg3, seq, DOC_FLASHSEQUENCE); +} + +static inline void doc_flash_address(struct docg3 *docg3, u8 addr) +{ +	doc_writeb(docg3, addr, DOC_FLASHADDRESS); +} + +static char const * const part_probes[] = { "cmdlinepart", "saftlpart", NULL }; + +static int doc_register_readb(struct docg3 *docg3, int reg) +{ +	u8 val; + +	doc_writew(docg3, reg, DOC_READADDRESS); +	val = doc_readb(docg3, reg); +	doc_vdbg("Read register %04x : %02x\n", reg, val); +	return val; +} + +static int doc_register_readw(struct docg3 *docg3, int reg) +{ +	u16 val; + +	doc_writew(docg3, reg, DOC_READADDRESS); +	val = doc_readw(docg3, reg); +	doc_vdbg("Read register %04x : %04x\n", reg, val); +	return val; +} + +/** + * doc_delay - delay docg3 operations + * @docg3: the device + * @nbNOPs: the number of NOPs to issue + * + * As no specification is available, the right timings between chip commands are + * unknown. The only available piece of information are the observed nops on a + * working docg3 chip. + * Therefore, doc_delay relies on a busy loop of NOPs, instead of scheduler + * friendlier msleep() functions or blocking mdelay(). + */ +static void doc_delay(struct docg3 *docg3, int nbNOPs) +{ +	int i; + +	doc_vdbg("NOP x %d\n", nbNOPs); +	for (i = 0; i < nbNOPs; i++) +		doc_writeb(docg3, 0, DOC_NOP); +} + +static int is_prot_seq_error(struct docg3 *docg3) +{ +	int ctrl; + +	ctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); +	return ctrl & (DOC_CTRL_PROTECTION_ERROR | DOC_CTRL_SEQUENCE_ERROR); +} + +static int doc_is_ready(struct docg3 *docg3) +{ +	int ctrl; + +	ctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); +	return ctrl & DOC_CTRL_FLASHREADY; +} + +static int doc_wait_ready(struct docg3 *docg3) +{ +	int maxWaitCycles = 100; + +	do { +		doc_delay(docg3, 4); +		cpu_relax(); +	} while (!doc_is_ready(docg3) && maxWaitCycles--); +	doc_delay(docg3, 2); +	if (maxWaitCycles > 0) +		return 0; +	else +		return -EIO; +} + +static int doc_reset_seq(struct docg3 *docg3) +{ +	int ret; + +	doc_writeb(docg3, 0x10, DOC_FLASHCONTROL); +	doc_flash_sequence(docg3, DOC_SEQ_RESET); +	doc_flash_command(docg3, DOC_CMD_RESET); +	doc_delay(docg3, 2); +	ret = doc_wait_ready(docg3); + +	doc_dbg("doc_reset_seq() -> isReady=%s\n", ret ? "false" : "true"); +	return ret; +} + +/** + * doc_read_data_area - Read data from data area + * @docg3: the device + * @buf: the buffer to fill in (might be NULL is dummy reads) + * @len: the length to read + * @first: first time read, DOC_READADDRESS should be set + * + * Reads bytes from flash data. Handles the single byte / even bytes reads. + */ +static void doc_read_data_area(struct docg3 *docg3, void *buf, int len, +			       int first) +{ +	int i, cdr, len4; +	u16 data16, *dst16; +	u8 data8, *dst8; + +	doc_dbg("doc_read_data_area(buf=%p, len=%d)\n", buf, len); +	cdr = len & 0x1; +	len4 = len - cdr; + +	if (first) +		doc_writew(docg3, DOC_IOSPACE_DATA, DOC_READADDRESS); +	dst16 = buf; +	for (i = 0; i < len4; i += 2) { +		data16 = doc_readw(docg3, DOC_IOSPACE_DATA); +		if (dst16) { +			*dst16 = data16; +			dst16++; +		} +	} + +	if (cdr) { +		doc_writew(docg3, DOC_IOSPACE_DATA | DOC_READADDR_ONE_BYTE, +			   DOC_READADDRESS); +		doc_delay(docg3, 1); +		dst8 = (u8 *)dst16; +		for (i = 0; i < cdr; i++) { +			data8 = doc_readb(docg3, DOC_IOSPACE_DATA); +			if (dst8) { +				*dst8 = data8; +				dst8++; +			} +		} +	} +} + +/** + * doc_write_data_area - Write data into data area + * @docg3: the device + * @buf: the buffer to get input bytes from + * @len: the length to write + * + * Writes bytes into flash data. Handles the single byte / even bytes writes. + */ +static void doc_write_data_area(struct docg3 *docg3, const void *buf, int len) +{ +	int i, cdr, len4; +	u16 *src16; +	u8 *src8; + +	doc_dbg("doc_write_data_area(buf=%p, len=%d)\n", buf, len); +	cdr = len & 0x3; +	len4 = len - cdr; + +	doc_writew(docg3, DOC_IOSPACE_DATA, DOC_READADDRESS); +	src16 = (u16 *)buf; +	for (i = 0; i < len4; i += 2) { +		doc_writew(docg3, *src16, DOC_IOSPACE_DATA); +		src16++; +	} + +	src8 = (u8 *)src16; +	for (i = 0; i < cdr; i++) { +		doc_writew(docg3, DOC_IOSPACE_DATA | DOC_READADDR_ONE_BYTE, +			   DOC_READADDRESS); +		doc_writeb(docg3, *src8, DOC_IOSPACE_DATA); +		src8++; +	} +} + +/** + * doc_set_data_mode - Sets the flash to normal or reliable data mode + * @docg3: the device + * + * The reliable data mode is a bit slower than the fast mode, but less errors + * occur.  Entering the reliable mode cannot be done without entering the fast + * mode first. + * + * In reliable mode, pages 2*n and 2*n+1 are clones. Writing to page 0 of blocks + * (4,5) make the hardware write also to page 1 of blocks blocks(4,5). Reading + * from page 0 of blocks (4,5) or from page 1 of blocks (4,5) gives the same + * result, which is a logical and between bytes from page 0 and page 1 (which is + * consistent with the fact that writing to a page is _clearing_ bits of that + * page). + */ +static void doc_set_reliable_mode(struct docg3 *docg3) +{ +	static char *strmode[] = { "normal", "fast", "reliable", "invalid" }; + +	doc_dbg("doc_set_reliable_mode(%s)\n", strmode[docg3->reliable]); +	switch (docg3->reliable) { +	case 0: +		break; +	case 1: +		doc_flash_sequence(docg3, DOC_SEQ_SET_FASTMODE); +		doc_flash_command(docg3, DOC_CMD_FAST_MODE); +		break; +	case 2: +		doc_flash_sequence(docg3, DOC_SEQ_SET_RELIABLEMODE); +		doc_flash_command(docg3, DOC_CMD_FAST_MODE); +		doc_flash_command(docg3, DOC_CMD_RELIABLE_MODE); +		break; +	default: +		doc_err("doc_set_reliable_mode(): invalid mode\n"); +		break; +	} +	doc_delay(docg3, 2); +} + +/** + * doc_set_asic_mode - Set the ASIC mode + * @docg3: the device + * @mode: the mode + * + * The ASIC can work in 3 modes : + *  - RESET: all registers are zeroed + *  - NORMAL: receives and handles commands + *  - POWERDOWN: minimal poweruse, flash parts shut off + */ +static void doc_set_asic_mode(struct docg3 *docg3, u8 mode) +{ +	int i; + +	for (i = 0; i < 12; i++) +		doc_readb(docg3, DOC_IOSPACE_IPL); + +	mode |= DOC_ASICMODE_MDWREN; +	doc_dbg("doc_set_asic_mode(%02x)\n", mode); +	doc_writeb(docg3, mode, DOC_ASICMODE); +	doc_writeb(docg3, ~mode, DOC_ASICMODECONFIRM); +	doc_delay(docg3, 1); +} + +/** + * doc_set_device_id - Sets the devices id for cascaded G3 chips + * @docg3: the device + * @id: the chip to select (amongst 0, 1, 2, 3) + * + * There can be 4 cascaded G3 chips. This function selects the one which will + * should be the active one. + */ +static void doc_set_device_id(struct docg3 *docg3, int id) +{ +	u8 ctrl; + +	doc_dbg("doc_set_device_id(%d)\n", id); +	doc_writeb(docg3, id, DOC_DEVICESELECT); +	ctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); + +	ctrl &= ~DOC_CTRL_VIOLATION; +	ctrl |= DOC_CTRL_CE; +	doc_writeb(docg3, ctrl, DOC_FLASHCONTROL); +} + +/** + * doc_set_extra_page_mode - Change flash page layout + * @docg3: the device + * + * Normally, the flash page is split into the data (512 bytes) and the out of + * band data (16 bytes). For each, 4 more bytes can be accessed, where the wear + * leveling counters are stored.  To access this last area of 4 bytes, a special + * mode must be input to the flash ASIC. + * + * Returns 0 if no error occurred, -EIO else. + */ +static int doc_set_extra_page_mode(struct docg3 *docg3) +{ +	int fctrl; + +	doc_dbg("doc_set_extra_page_mode()\n"); +	doc_flash_sequence(docg3, DOC_SEQ_PAGE_SIZE_532); +	doc_flash_command(docg3, DOC_CMD_PAGE_SIZE_532); +	doc_delay(docg3, 2); + +	fctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); +	if (fctrl & (DOC_CTRL_PROTECTION_ERROR | DOC_CTRL_SEQUENCE_ERROR)) +		return -EIO; +	else +		return 0; +} + +/** + * doc_setup_addr_sector - Setup blocks/page/ofs address for one plane + * @docg3: the device + * @sector: the sector + */ +static void doc_setup_addr_sector(struct docg3 *docg3, int sector) +{ +	doc_delay(docg3, 1); +	doc_flash_address(docg3, sector & 0xff); +	doc_flash_address(docg3, (sector >> 8) & 0xff); +	doc_flash_address(docg3, (sector >> 16) & 0xff); +	doc_delay(docg3, 1); +} + +/** + * doc_setup_writeaddr_sector - Setup blocks/page/ofs address for one plane + * @docg3: the device + * @sector: the sector + * @ofs: the offset in the page, between 0 and (512 + 16 + 512) + */ +static void doc_setup_writeaddr_sector(struct docg3 *docg3, int sector, int ofs) +{ +	ofs = ofs >> 2; +	doc_delay(docg3, 1); +	doc_flash_address(docg3, ofs & 0xff); +	doc_flash_address(docg3, sector & 0xff); +	doc_flash_address(docg3, (sector >> 8) & 0xff); +	doc_flash_address(docg3, (sector >> 16) & 0xff); +	doc_delay(docg3, 1); +} + +/** + * doc_seek - Set both flash planes to the specified block, page for reading + * @docg3: the device + * @block0: the first plane block index + * @block1: the second plane block index + * @page: the page index within the block + * @wear: if true, read will occur on the 4 extra bytes of the wear area + * @ofs: offset in page to read + * + * Programs the flash even and odd planes to the specific block and page. + * Alternatively, programs the flash to the wear area of the specified page. + */ +static int doc_read_seek(struct docg3 *docg3, int block0, int block1, int page, +			 int wear, int ofs) +{ +	int sector, ret = 0; + +	doc_dbg("doc_seek(blocks=(%d,%d), page=%d, ofs=%d, wear=%d)\n", +		block0, block1, page, ofs, wear); + +	if (!wear && (ofs < 2 * DOC_LAYOUT_PAGE_SIZE)) { +		doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE1); +		doc_flash_command(docg3, DOC_CMD_READ_PLANE1); +		doc_delay(docg3, 2); +	} else { +		doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE2); +		doc_flash_command(docg3, DOC_CMD_READ_PLANE2); +		doc_delay(docg3, 2); +	} + +	doc_set_reliable_mode(docg3); +	if (wear) +		ret = doc_set_extra_page_mode(docg3); +	if (ret) +		goto out; + +	doc_flash_sequence(docg3, DOC_SEQ_READ); +	sector = (block0 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK); +	doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR); +	doc_setup_addr_sector(docg3, sector); + +	sector = (block1 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK); +	doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR); +	doc_setup_addr_sector(docg3, sector); +	doc_delay(docg3, 1); + +out: +	return ret; +} + +/** + * doc_write_seek - Set both flash planes to the specified block, page for writing + * @docg3: the device + * @block0: the first plane block index + * @block1: the second plane block index + * @page: the page index within the block + * @ofs: offset in page to write + * + * Programs the flash even and odd planes to the specific block and page. + * Alternatively, programs the flash to the wear area of the specified page. + */ +static int doc_write_seek(struct docg3 *docg3, int block0, int block1, int page, +			 int ofs) +{ +	int ret = 0, sector; + +	doc_dbg("doc_write_seek(blocks=(%d,%d), page=%d, ofs=%d)\n", +		block0, block1, page, ofs); + +	doc_set_reliable_mode(docg3); + +	if (ofs < 2 * DOC_LAYOUT_PAGE_SIZE) { +		doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE1); +		doc_flash_command(docg3, DOC_CMD_READ_PLANE1); +		doc_delay(docg3, 2); +	} else { +		doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE2); +		doc_flash_command(docg3, DOC_CMD_READ_PLANE2); +		doc_delay(docg3, 2); +	} + +	doc_flash_sequence(docg3, DOC_SEQ_PAGE_SETUP); +	doc_flash_command(docg3, DOC_CMD_PROG_CYCLE1); + +	sector = (block0 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK); +	doc_setup_writeaddr_sector(docg3, sector, ofs); + +	doc_flash_command(docg3, DOC_CMD_PROG_CYCLE3); +	doc_delay(docg3, 2); +	ret = doc_wait_ready(docg3); +	if (ret) +		goto out; + +	doc_flash_command(docg3, DOC_CMD_PROG_CYCLE1); +	sector = (block1 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK); +	doc_setup_writeaddr_sector(docg3, sector, ofs); +	doc_delay(docg3, 1); + +out: +	return ret; +} + + +/** + * doc_read_page_ecc_init - Initialize hardware ECC engine + * @docg3: the device + * @len: the number of bytes covered by the ECC (BCH covered) + * + * The function does initialize the hardware ECC engine to compute the Hamming + * ECC (on 1 byte) and the BCH hardware ECC (on 7 bytes). + * + * Return 0 if succeeded, -EIO on error + */ +static int doc_read_page_ecc_init(struct docg3 *docg3, int len) +{ +	doc_writew(docg3, DOC_ECCCONF0_READ_MODE +		   | DOC_ECCCONF0_BCH_ENABLE | DOC_ECCCONF0_HAMMING_ENABLE +		   | (len & DOC_ECCCONF0_DATA_BYTES_MASK), +		   DOC_ECCCONF0); +	doc_delay(docg3, 4); +	doc_register_readb(docg3, DOC_FLASHCONTROL); +	return doc_wait_ready(docg3); +} + +/** + * doc_write_page_ecc_init - Initialize hardware BCH ECC engine + * @docg3: the device + * @len: the number of bytes covered by the ECC (BCH covered) + * + * The function does initialize the hardware ECC engine to compute the Hamming + * ECC (on 1 byte) and the BCH hardware ECC (on 7 bytes). + * + * Return 0 if succeeded, -EIO on error + */ +static int doc_write_page_ecc_init(struct docg3 *docg3, int len) +{ +	doc_writew(docg3, DOC_ECCCONF0_WRITE_MODE +		   | DOC_ECCCONF0_BCH_ENABLE | DOC_ECCCONF0_HAMMING_ENABLE +		   | (len & DOC_ECCCONF0_DATA_BYTES_MASK), +		   DOC_ECCCONF0); +	doc_delay(docg3, 4); +	doc_register_readb(docg3, DOC_FLASHCONTROL); +	return doc_wait_ready(docg3); +} + +/** + * doc_ecc_disable - Disable Hamming and BCH ECC hardware calculator + * @docg3: the device + * + * Disables the hardware ECC generator and checker, for unchecked reads (as when + * reading OOB only or write status byte). + */ +static void doc_ecc_disable(struct docg3 *docg3) +{ +	doc_writew(docg3, DOC_ECCCONF0_READ_MODE, DOC_ECCCONF0); +	doc_delay(docg3, 4); +} + +/** + * doc_hamming_ecc_init - Initialize hardware Hamming ECC engine + * @docg3: the device + * @nb_bytes: the number of bytes covered by the ECC (Hamming covered) + * + * This function programs the ECC hardware to compute the hamming code on the + * last provided N bytes to the hardware generator. + */ +static void doc_hamming_ecc_init(struct docg3 *docg3, int nb_bytes) +{ +	u8 ecc_conf1; + +	ecc_conf1 = doc_register_readb(docg3, DOC_ECCCONF1); +	ecc_conf1 &= ~DOC_ECCCONF1_HAMMING_BITS_MASK; +	ecc_conf1 |= (nb_bytes & DOC_ECCCONF1_HAMMING_BITS_MASK); +	doc_writeb(docg3, ecc_conf1, DOC_ECCCONF1); +} + +/** + * doc_ecc_bch_fix_data - Fix if need be read data from flash + * @docg3: the device + * @buf: the buffer of read data (512 + 7 + 1 bytes) + * @hwecc: the hardware calculated ECC. + *         It's in fact recv_ecc ^ calc_ecc, where recv_ecc was read from OOB + *         area data, and calc_ecc the ECC calculated by the hardware generator. + * + * Checks if the received data matches the ECC, and if an error is detected, + * tries to fix the bit flips (at most 4) in the buffer buf.  As the docg3 + * understands the (data, ecc, syndroms) in an inverted order in comparison to + * the BCH library, the function reverses the order of bits (ie. bit7 and bit0, + * bit6 and bit 1, ...) for all ECC data. + * + * The hardware ecc unit produces oob_ecc ^ calc_ecc.  The kernel's bch + * algorithm is used to decode this.  However the hw operates on page + * data in a bit order that is the reverse of that of the bch alg, + * requiring that the bits be reversed on the result.  Thanks to Ivan + * Djelic for his analysis. + * + * Returns number of fixed bits (0, 1, 2, 3, 4) or -EBADMSG if too many bit + * errors were detected and cannot be fixed. + */ +static int doc_ecc_bch_fix_data(struct docg3 *docg3, void *buf, u8 *hwecc) +{ +	u8 ecc[DOC_ECC_BCH_SIZE]; +	int errorpos[DOC_ECC_BCH_T], i, numerrs; + +	for (i = 0; i < DOC_ECC_BCH_SIZE; i++) +		ecc[i] = bitrev8(hwecc[i]); +	numerrs = decode_bch(docg3->cascade->bch, NULL, +			     DOC_ECC_BCH_COVERED_BYTES, +			     NULL, ecc, NULL, errorpos); +	BUG_ON(numerrs == -EINVAL); +	if (numerrs < 0) +		goto out; + +	for (i = 0; i < numerrs; i++) +		errorpos[i] = (errorpos[i] & ~7) | (7 - (errorpos[i] & 7)); +	for (i = 0; i < numerrs; i++) +		if (errorpos[i] < DOC_ECC_BCH_COVERED_BYTES*8) +			/* error is located in data, correct it */ +			change_bit(errorpos[i], buf); +out: +	doc_dbg("doc_ecc_bch_fix_data: flipped %d bits\n", numerrs); +	return numerrs; +} + + +/** + * doc_read_page_prepare - Prepares reading data from a flash page + * @docg3: the device + * @block0: the first plane block index on flash memory + * @block1: the second plane block index on flash memory + * @page: the page index in the block + * @offset: the offset in the page (must be a multiple of 4) + * + * Prepares the page to be read in the flash memory : + *   - tell ASIC to map the flash pages + *   - tell ASIC to be in read mode + * + * After a call to this method, a call to doc_read_page_finish is mandatory, + * to end the read cycle of the flash. + * + * Read data from a flash page. The length to be read must be between 0 and + * (page_size + oob_size + wear_size), ie. 532, and a multiple of 4 (because + * the extra bytes reading is not implemented). + * + * As pages are grouped by 2 (in 2 planes), reading from a page must be done + * in two steps: + *  - one read of 512 bytes at offset 0 + *  - one read of 512 bytes at offset 512 + 16 + * + * Returns 0 if successful, -EIO if a read error occurred. + */ +static int doc_read_page_prepare(struct docg3 *docg3, int block0, int block1, +				 int page, int offset) +{ +	int wear_area = 0, ret = 0; + +	doc_dbg("doc_read_page_prepare(blocks=(%d,%d), page=%d, ofsInPage=%d)\n", +		block0, block1, page, offset); +	if (offset >= DOC_LAYOUT_WEAR_OFFSET) +		wear_area = 1; +	if (!wear_area && offset > (DOC_LAYOUT_PAGE_OOB_SIZE * 2)) +		return -EINVAL; + +	doc_set_device_id(docg3, docg3->device_id); +	ret = doc_reset_seq(docg3); +	if (ret) +		goto err; + +	/* Program the flash address block and page */ +	ret = doc_read_seek(docg3, block0, block1, page, wear_area, offset); +	if (ret) +		goto err; + +	doc_flash_command(docg3, DOC_CMD_READ_ALL_PLANES); +	doc_delay(docg3, 2); +	doc_wait_ready(docg3); + +	doc_flash_command(docg3, DOC_CMD_SET_ADDR_READ); +	doc_delay(docg3, 1); +	if (offset >= DOC_LAYOUT_PAGE_SIZE * 2) +		offset -= 2 * DOC_LAYOUT_PAGE_SIZE; +	doc_flash_address(docg3, offset >> 2); +	doc_delay(docg3, 1); +	doc_wait_ready(docg3); + +	doc_flash_command(docg3, DOC_CMD_READ_FLASH); + +	return 0; +err: +	doc_writeb(docg3, 0, DOC_DATAEND); +	doc_delay(docg3, 2); +	return -EIO; +} + +/** + * doc_read_page_getbytes - Reads bytes from a prepared page + * @docg3: the device + * @len: the number of bytes to be read (must be a multiple of 4) + * @buf: the buffer to be filled in (or NULL is forget bytes) + * @first: 1 if first time read, DOC_READADDRESS should be set + * @last_odd: 1 if last read ended up on an odd byte + * + * Reads bytes from a prepared page. There is a trickery here : if the last read + * ended up on an odd offset in the 1024 bytes double page, ie. between the 2 + * planes, the first byte must be read apart. If a word (16bit) read was used, + * the read would return the byte of plane 2 as low *and* high endian, which + * will mess the read. + * + */ +static int doc_read_page_getbytes(struct docg3 *docg3, int len, u_char *buf, +				  int first, int last_odd) +{ +	if (last_odd && len > 0) { +		doc_read_data_area(docg3, buf, 1, first); +		doc_read_data_area(docg3, buf ? buf + 1 : buf, len - 1, 0); +	} else { +		doc_read_data_area(docg3, buf, len, first); +	} +	doc_delay(docg3, 2); +	return len; +} + +/** + * doc_write_page_putbytes - Writes bytes into a prepared page + * @docg3: the device + * @len: the number of bytes to be written + * @buf: the buffer of input bytes + * + */ +static void doc_write_page_putbytes(struct docg3 *docg3, int len, +				    const u_char *buf) +{ +	doc_write_data_area(docg3, buf, len); +	doc_delay(docg3, 2); +} + +/** + * doc_get_bch_hw_ecc - Get hardware calculated BCH ECC + * @docg3: the device + * @hwecc:  the array of 7 integers where the hardware ecc will be stored + */ +static void doc_get_bch_hw_ecc(struct docg3 *docg3, u8 *hwecc) +{ +	int i; + +	for (i = 0; i < DOC_ECC_BCH_SIZE; i++) +		hwecc[i] = doc_register_readb(docg3, DOC_BCH_HW_ECC(i)); +} + +/** + * doc_page_finish - Ends reading/writing of a flash page + * @docg3: the device + */ +static void doc_page_finish(struct docg3 *docg3) +{ +	doc_writeb(docg3, 0, DOC_DATAEND); +	doc_delay(docg3, 2); +} + +/** + * doc_read_page_finish - Ends reading of a flash page + * @docg3: the device + * + * As a side effect, resets the chip selector to 0. This ensures that after each + * read operation, the floor 0 is selected. Therefore, if the systems halts, the + * reboot will boot on floor 0, where the IPL is. + */ +static void doc_read_page_finish(struct docg3 *docg3) +{ +	doc_page_finish(docg3); +	doc_set_device_id(docg3, 0); +} + +/** + * calc_block_sector - Calculate blocks, pages and ofs. + + * @from: offset in flash + * @block0: first plane block index calculated + * @block1: second plane block index calculated + * @page: page calculated + * @ofs: offset in page + * @reliable: 0 if docg3 in normal mode, 1 if docg3 in fast mode, 2 if docg3 in + * reliable mode. + * + * The calculation is based on the reliable/normal mode. In normal mode, the 64 + * pages of a block are available. In reliable mode, as pages 2*n and 2*n+1 are + * clones, only 32 pages per block are available. + */ +static void calc_block_sector(loff_t from, int *block0, int *block1, int *page, +			      int *ofs, int reliable) +{ +	uint sector, pages_biblock; + +	pages_biblock = DOC_LAYOUT_PAGES_PER_BLOCK * DOC_LAYOUT_NBPLANES; +	if (reliable == 1 || reliable == 2) +		pages_biblock /= 2; + +	sector = from / DOC_LAYOUT_PAGE_SIZE; +	*block0 = sector / pages_biblock * DOC_LAYOUT_NBPLANES; +	*block1 = *block0 + 1; +	*page = sector % pages_biblock; +	*page /= DOC_LAYOUT_NBPLANES; +	if (reliable == 1 || reliable == 2) +		*page *= 2; +	if (sector % 2) +		*ofs = DOC_LAYOUT_PAGE_OOB_SIZE; +	else +		*ofs = 0; +} + +/** + * doc_read_oob - Read out of band bytes from flash + * @mtd: the device + * @from: the offset from first block and first page, in bytes, aligned on page + *        size + * @ops: the mtd oob structure + * + * Reads flash memory OOB area of pages. + * + * Returns 0 if read successful, of -EIO, -EINVAL if an error occurred + */ +static int doc_read_oob(struct mtd_info *mtd, loff_t from, +			struct mtd_oob_ops *ops) +{ +	struct docg3 *docg3 = mtd->priv; +	int block0, block1, page, ret, skip, ofs = 0; +	u8 *oobbuf = ops->oobbuf; +	u8 *buf = ops->datbuf; +	size_t len, ooblen, nbdata, nboob; +	u8 hwecc[DOC_ECC_BCH_SIZE], eccconf1; +	int max_bitflips = 0; + +	if (buf) +		len = ops->len; +	else +		len = 0; +	if (oobbuf) +		ooblen = ops->ooblen; +	else +		ooblen = 0; + +	if (oobbuf && ops->mode == MTD_OPS_PLACE_OOB) +		oobbuf += ops->ooboffs; + +	doc_dbg("doc_read_oob(from=%lld, mode=%d, data=(%p:%zu), oob=(%p:%zu))\n", +		from, ops->mode, buf, len, oobbuf, ooblen); +	if (ooblen % DOC_LAYOUT_OOB_SIZE) +		return -EINVAL; + +	if (from + len > mtd->size) +		return -EINVAL; + +	ops->oobretlen = 0; +	ops->retlen = 0; +	ret = 0; +	skip = from % DOC_LAYOUT_PAGE_SIZE; +	mutex_lock(&docg3->cascade->lock); +	while (ret >= 0 && (len > 0 || ooblen > 0)) { +		calc_block_sector(from - skip, &block0, &block1, &page, &ofs, +			docg3->reliable); +		nbdata = min_t(size_t, len, DOC_LAYOUT_PAGE_SIZE - skip); +		nboob = min_t(size_t, ooblen, (size_t)DOC_LAYOUT_OOB_SIZE); +		ret = doc_read_page_prepare(docg3, block0, block1, page, ofs); +		if (ret < 0) +			goto out; +		ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES); +		if (ret < 0) +			goto err_in_read; +		ret = doc_read_page_getbytes(docg3, skip, NULL, 1, 0); +		if (ret < skip) +			goto err_in_read; +		ret = doc_read_page_getbytes(docg3, nbdata, buf, 0, skip % 2); +		if (ret < nbdata) +			goto err_in_read; +		doc_read_page_getbytes(docg3, +				       DOC_LAYOUT_PAGE_SIZE - nbdata - skip, +				       NULL, 0, (skip + nbdata) % 2); +		ret = doc_read_page_getbytes(docg3, nboob, oobbuf, 0, 0); +		if (ret < nboob) +			goto err_in_read; +		doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE - nboob, +				       NULL, 0, nboob % 2); + +		doc_get_bch_hw_ecc(docg3, hwecc); +		eccconf1 = doc_register_readb(docg3, DOC_ECCCONF1); + +		if (nboob >= DOC_LAYOUT_OOB_SIZE) { +			doc_dbg("OOB - INFO: %*phC\n", 7, oobbuf); +			doc_dbg("OOB - HAMMING: %02x\n", oobbuf[7]); +			doc_dbg("OOB - BCH_ECC: %*phC\n", 7, oobbuf + 8); +			doc_dbg("OOB - UNUSED: %02x\n", oobbuf[15]); +		} +		doc_dbg("ECC checks: ECCConf1=%x\n", eccconf1); +		doc_dbg("ECC HW_ECC: %*phC\n", 7, hwecc); + +		ret = -EIO; +		if (is_prot_seq_error(docg3)) +			goto err_in_read; +		ret = 0; +		if ((block0 >= DOC_LAYOUT_BLOCK_FIRST_DATA) && +		    (eccconf1 & DOC_ECCCONF1_BCH_SYNDROM_ERR) && +		    (eccconf1 & DOC_ECCCONF1_PAGE_IS_WRITTEN) && +		    (ops->mode != MTD_OPS_RAW) && +		    (nbdata == DOC_LAYOUT_PAGE_SIZE)) { +			ret = doc_ecc_bch_fix_data(docg3, buf, hwecc); +			if (ret < 0) { +				mtd->ecc_stats.failed++; +				ret = -EBADMSG; +			} +			if (ret > 0) { +				mtd->ecc_stats.corrected += ret; +				max_bitflips = max(max_bitflips, ret); +				ret = max_bitflips; +			} +		} + +		doc_read_page_finish(docg3); +		ops->retlen += nbdata; +		ops->oobretlen += nboob; +		buf += nbdata; +		oobbuf += nboob; +		len -= nbdata; +		ooblen -= nboob; +		from += DOC_LAYOUT_PAGE_SIZE; +		skip = 0; +	} + +out: +	mutex_unlock(&docg3->cascade->lock); +	return ret; +err_in_read: +	doc_read_page_finish(docg3); +	goto out; +} + +/** + * doc_read - Read bytes from flash + * @mtd: the device + * @from: the offset from first block and first page, in bytes, aligned on page + *        size + * @len: the number of bytes to read (must be a multiple of 4) + * @retlen: the number of bytes actually read + * @buf: the filled in buffer + * + * Reads flash memory pages. This function does not read the OOB chunk, but only + * the page data. + * + * Returns 0 if read successful, of -EIO, -EINVAL if an error occurred + */ +static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, +	     size_t *retlen, u_char *buf) +{ +	struct mtd_oob_ops ops; +	size_t ret; + +	memset(&ops, 0, sizeof(ops)); +	ops.datbuf = buf; +	ops.len = len; +	ops.mode = MTD_OPS_AUTO_OOB; + +	ret = doc_read_oob(mtd, from, &ops); +	*retlen = ops.retlen; +	return ret; +} + +static int doc_reload_bbt(struct docg3 *docg3) +{ +	int block = DOC_LAYOUT_BLOCK_BBT; +	int ret = 0, nbpages, page; +	u_char *buf = docg3->bbt; + +	nbpages = DIV_ROUND_UP(docg3->max_block + 1, 8 * DOC_LAYOUT_PAGE_SIZE); +	for (page = 0; !ret && (page < nbpages); page++) { +		ret = doc_read_page_prepare(docg3, block, block + 1, +					    page + DOC_LAYOUT_PAGE_BBT, 0); +		if (!ret) +			ret = doc_read_page_ecc_init(docg3, +						     DOC_LAYOUT_PAGE_SIZE); +		if (!ret) +			doc_read_page_getbytes(docg3, DOC_LAYOUT_PAGE_SIZE, +					       buf, 1, 0); +		buf += DOC_LAYOUT_PAGE_SIZE; +	} +	doc_read_page_finish(docg3); +	return ret; +} + +/** + * doc_block_isbad - Checks whether a block is good or not + * @mtd: the device + * @from: the offset to find the correct block + * + * Returns 1 if block is bad, 0 if block is good + */ +static int doc_block_isbad(struct mtd_info *mtd, loff_t from) +{ +	struct docg3 *docg3 = mtd->priv; +	int block0, block1, page, ofs, is_good; + +	calc_block_sector(from, &block0, &block1, &page, &ofs, +		docg3->reliable); +	doc_dbg("doc_block_isbad(from=%lld) => block=(%d,%d), page=%d, ofs=%d\n", +		from, block0, block1, page, ofs); + +	if (block0 < DOC_LAYOUT_BLOCK_FIRST_DATA) +		return 0; +	if (block1 > docg3->max_block) +		return -EINVAL; + +	is_good = docg3->bbt[block0 >> 3] & (1 << (block0 & 0x7)); +	return !is_good; +} + +#if 0 +/** + * doc_get_erase_count - Get block erase count + * @docg3: the device + * @from: the offset in which the block is. + * + * Get the number of times a block was erased. The number is the maximum of + * erase times between first and second plane (which should be equal normally). + * + * Returns The number of erases, or -EINVAL or -EIO on error. + */ +static int doc_get_erase_count(struct docg3 *docg3, loff_t from) +{ +	u8 buf[DOC_LAYOUT_WEAR_SIZE]; +	int ret, plane1_erase_count, plane2_erase_count; +	int block0, block1, page, ofs; + +	doc_dbg("doc_get_erase_count(from=%lld, buf=%p)\n", from, buf); +	if (from % DOC_LAYOUT_PAGE_SIZE) +		return -EINVAL; +	calc_block_sector(from, &block0, &block1, &page, &ofs, docg3->reliable); +	if (block1 > docg3->max_block) +		return -EINVAL; + +	ret = doc_reset_seq(docg3); +	if (!ret) +		ret = doc_read_page_prepare(docg3, block0, block1, page, +					    ofs + DOC_LAYOUT_WEAR_OFFSET, 0); +	if (!ret) +		ret = doc_read_page_getbytes(docg3, DOC_LAYOUT_WEAR_SIZE, +					     buf, 1, 0); +	doc_read_page_finish(docg3); + +	if (ret || (buf[0] != DOC_ERASE_MARK) || (buf[2] != DOC_ERASE_MARK)) +		return -EIO; +	plane1_erase_count = (u8)(~buf[1]) | ((u8)(~buf[4]) << 8) +		| ((u8)(~buf[5]) << 16); +	plane2_erase_count = (u8)(~buf[3]) | ((u8)(~buf[6]) << 8) +		| ((u8)(~buf[7]) << 16); + +	return max(plane1_erase_count, plane2_erase_count); +} +#endif + +/** + * doc_get_op_status - get erase/write operation status + * @docg3: the device + * + * Queries the status from the chip, and returns it + * + * Returns the status (bits DOC_PLANES_STATUS_*) + */ +static int doc_get_op_status(struct docg3 *docg3) +{ +	u8 status; + +	doc_flash_sequence(docg3, DOC_SEQ_PLANES_STATUS); +	doc_flash_command(docg3, DOC_CMD_PLANES_STATUS); +	doc_delay(docg3, 5); + +	doc_ecc_disable(docg3); +	doc_read_data_area(docg3, &status, 1, 1); +	return status; +} + +/** + * doc_write_erase_wait_status - wait for write or erase completion + * @docg3: the device + * + * Wait for the chip to be ready again after erase or write operation, and check + * erase/write status. + * + * Returns 0 if erase successful, -EIO if erase/write issue, -ETIMEOUT if + * timeout + */ +static int doc_write_erase_wait_status(struct docg3 *docg3) +{ +	int i, status, ret = 0; + +	for (i = 0; !doc_is_ready(docg3) && i < 5; i++) +		msleep(20); +	if (!doc_is_ready(docg3)) { +		doc_dbg("Timeout reached and the chip is still not ready\n"); +		ret = -EAGAIN; +		goto out; +	} + +	status = doc_get_op_status(docg3); +	if (status & DOC_PLANES_STATUS_FAIL) { +		doc_dbg("Erase/Write failed on (a) plane(s), status = %x\n", +			status); +		ret = -EIO; +	} + +out: +	doc_page_finish(docg3); +	return ret; +} + +/** + * doc_erase_block - Erase a couple of blocks + * @docg3: the device + * @block0: the first block to erase (leftmost plane) + * @block1: the second block to erase (rightmost plane) + * + * Erase both blocks, and return operation status + * + * Returns 0 if erase successful, -EIO if erase issue, -ETIMEOUT if chip not + * ready for too long + */ +static int doc_erase_block(struct docg3 *docg3, int block0, int block1) +{ +	int ret, sector; + +	doc_dbg("doc_erase_block(blocks=(%d,%d))\n", block0, block1); +	ret = doc_reset_seq(docg3); +	if (ret) +		return -EIO; + +	doc_set_reliable_mode(docg3); +	doc_flash_sequence(docg3, DOC_SEQ_ERASE); + +	sector = block0 << DOC_ADDR_BLOCK_SHIFT; +	doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR); +	doc_setup_addr_sector(docg3, sector); +	sector = block1 << DOC_ADDR_BLOCK_SHIFT; +	doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR); +	doc_setup_addr_sector(docg3, sector); +	doc_delay(docg3, 1); + +	doc_flash_command(docg3, DOC_CMD_ERASECYCLE2); +	doc_delay(docg3, 2); + +	if (is_prot_seq_error(docg3)) { +		doc_err("Erase blocks %d,%d error\n", block0, block1); +		return -EIO; +	} + +	return doc_write_erase_wait_status(docg3); +} + +/** + * doc_erase - Erase a portion of the chip + * @mtd: the device + * @info: the erase info + * + * Erase a bunch of contiguous blocks, by pairs, as a "mtd" page of 1024 is + * split into 2 pages of 512 bytes on 2 contiguous blocks. + * + * Returns 0 if erase successful, -EINVAL if addressing error, -EIO if erase + * issue + */ +static int doc_erase(struct mtd_info *mtd, struct erase_info *info) +{ +	struct docg3 *docg3 = mtd->priv; +	uint64_t len; +	int block0, block1, page, ret, ofs = 0; + +	doc_dbg("doc_erase(from=%lld, len=%lld\n", info->addr, info->len); + +	info->state = MTD_ERASE_PENDING; +	calc_block_sector(info->addr + info->len, &block0, &block1, &page, +			  &ofs, docg3->reliable); +	ret = -EINVAL; +	if (info->addr + info->len > mtd->size || page || ofs) +		goto reset_err; + +	ret = 0; +	calc_block_sector(info->addr, &block0, &block1, &page, &ofs, +			  docg3->reliable); +	mutex_lock(&docg3->cascade->lock); +	doc_set_device_id(docg3, docg3->device_id); +	doc_set_reliable_mode(docg3); +	for (len = info->len; !ret && len > 0; len -= mtd->erasesize) { +		info->state = MTD_ERASING; +		ret = doc_erase_block(docg3, block0, block1); +		block0 += 2; +		block1 += 2; +	} +	mutex_unlock(&docg3->cascade->lock); + +	if (ret) +		goto reset_err; + +	info->state = MTD_ERASE_DONE; +	return 0; + +reset_err: +	info->state = MTD_ERASE_FAILED; +	return ret; +} + +/** + * doc_write_page - Write a single page to the chip + * @docg3: the device + * @to: the offset from first block and first page, in bytes, aligned on page + *      size + * @buf: buffer to get bytes from + * @oob: buffer to get out of band bytes from (can be NULL if no OOB should be + *       written) + * @autoecc: if 0, all 16 bytes from OOB are taken, regardless of HW Hamming or + *           BCH computations. If 1, only bytes 0-7 and byte 15 are taken, + *           remaining ones are filled with hardware Hamming and BCH + *           computations. Its value is not meaningfull is oob == NULL. + * + * Write one full page (ie. 1 page split on two planes), of 512 bytes, with the + * OOB data. The OOB ECC is automatically computed by the hardware Hamming and + * BCH generator if autoecc is not null. + * + * Returns 0 if write successful, -EIO if write error, -EAGAIN if timeout + */ +static int doc_write_page(struct docg3 *docg3, loff_t to, const u_char *buf, +			  const u_char *oob, int autoecc) +{ +	int block0, block1, page, ret, ofs = 0; +	u8 hwecc[DOC_ECC_BCH_SIZE], hamming; + +	doc_dbg("doc_write_page(to=%lld)\n", to); +	calc_block_sector(to, &block0, &block1, &page, &ofs, docg3->reliable); + +	doc_set_device_id(docg3, docg3->device_id); +	ret = doc_reset_seq(docg3); +	if (ret) +		goto err; + +	/* Program the flash address block and page */ +	ret = doc_write_seek(docg3, block0, block1, page, ofs); +	if (ret) +		goto err; + +	doc_write_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES); +	doc_delay(docg3, 2); +	doc_write_page_putbytes(docg3, DOC_LAYOUT_PAGE_SIZE, buf); + +	if (oob && autoecc) { +		doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_PAGEINFO_SZ, oob); +		doc_delay(docg3, 2); +		oob += DOC_LAYOUT_OOB_UNUSED_OFS; + +		hamming = doc_register_readb(docg3, DOC_HAMMINGPARITY); +		doc_delay(docg3, 2); +		doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_HAMMING_SZ, +					&hamming); +		doc_delay(docg3, 2); + +		doc_get_bch_hw_ecc(docg3, hwecc); +		doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_BCH_SZ, hwecc); +		doc_delay(docg3, 2); + +		doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_UNUSED_SZ, oob); +	} +	if (oob && !autoecc) +		doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_SIZE, oob); + +	doc_delay(docg3, 2); +	doc_page_finish(docg3); +	doc_delay(docg3, 2); +	doc_flash_command(docg3, DOC_CMD_PROG_CYCLE2); +	doc_delay(docg3, 2); + +	/* +	 * The wait status will perform another doc_page_finish() call, but that +	 * seems to please the docg3, so leave it. +	 */ +	ret = doc_write_erase_wait_status(docg3); +	return ret; +err: +	doc_read_page_finish(docg3); +	return ret; +} + +/** + * doc_guess_autoecc - Guess autoecc mode from mbd_oob_ops + * @ops: the oob operations + * + * Returns 0 or 1 if success, -EINVAL if invalid oob mode + */ +static int doc_guess_autoecc(struct mtd_oob_ops *ops) +{ +	int autoecc; + +	switch (ops->mode) { +	case MTD_OPS_PLACE_OOB: +	case MTD_OPS_AUTO_OOB: +		autoecc = 1; +		break; +	case MTD_OPS_RAW: +		autoecc = 0; +		break; +	default: +		autoecc = -EINVAL; +	} +	return autoecc; +} + +/** + * doc_fill_autooob - Fill a 16 bytes OOB from 8 non-ECC bytes + * @dst: the target 16 bytes OOB buffer + * @oobsrc: the source 8 bytes non-ECC OOB buffer + * + */ +static void doc_fill_autooob(u8 *dst, u8 *oobsrc) +{ +	memcpy(dst, oobsrc, DOC_LAYOUT_OOB_PAGEINFO_SZ); +	dst[DOC_LAYOUT_OOB_UNUSED_OFS] = oobsrc[DOC_LAYOUT_OOB_PAGEINFO_SZ]; +} + +/** + * doc_backup_oob - Backup OOB into docg3 structure + * @docg3: the device + * @to: the page offset in the chip + * @ops: the OOB size and buffer + * + * As the docg3 should write a page with its OOB in one pass, and some userland + * applications do write_oob() to setup the OOB and then write(), store the OOB + * into a temporary storage. This is very dangerous, as 2 concurrent + * applications could store an OOB, and then write their pages (which will + * result into one having its OOB corrupted). + * + * The only reliable way would be for userland to call doc_write_oob() with both + * the page data _and_ the OOB area. + * + * Returns 0 if success, -EINVAL if ops content invalid + */ +static int doc_backup_oob(struct docg3 *docg3, loff_t to, +			  struct mtd_oob_ops *ops) +{ +	int ooblen = ops->ooblen, autoecc; + +	if (ooblen != DOC_LAYOUT_OOB_SIZE) +		return -EINVAL; +	autoecc = doc_guess_autoecc(ops); +	if (autoecc < 0) +		return autoecc; + +	docg3->oob_write_ofs = to; +	docg3->oob_autoecc = autoecc; +	if (ops->mode == MTD_OPS_AUTO_OOB) { +		doc_fill_autooob(docg3->oob_write_buf, ops->oobbuf); +		ops->oobretlen = 8; +	} else { +		memcpy(docg3->oob_write_buf, ops->oobbuf, DOC_LAYOUT_OOB_SIZE); +		ops->oobretlen = DOC_LAYOUT_OOB_SIZE; +	} +	return 0; +} + +/** + * doc_write_oob - Write out of band bytes to flash + * @mtd: the device + * @ofs: the offset from first block and first page, in bytes, aligned on page + *       size + * @ops: the mtd oob structure + * + * Either write OOB data into a temporary buffer, for the subsequent write + * page. The provided OOB should be 16 bytes long. If a data buffer is provided + * as well, issue the page write. + * Or provide data without OOB, and then a all zeroed OOB will be used (ECC will + * still be filled in if asked for). + * + * Returns 0 is successful, EINVAL if length is not 14 bytes + */ +static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, +			 struct mtd_oob_ops *ops) +{ +	struct docg3 *docg3 = mtd->priv; +	int ret, autoecc, oobdelta; +	u8 *oobbuf = ops->oobbuf; +	u8 *buf = ops->datbuf; +	size_t len, ooblen; +	u8 oob[DOC_LAYOUT_OOB_SIZE]; + +	if (buf) +		len = ops->len; +	else +		len = 0; +	if (oobbuf) +		ooblen = ops->ooblen; +	else +		ooblen = 0; + +	if (oobbuf && ops->mode == MTD_OPS_PLACE_OOB) +		oobbuf += ops->ooboffs; + +	doc_dbg("doc_write_oob(from=%lld, mode=%d, data=(%p:%zu), oob=(%p:%zu))\n", +		ofs, ops->mode, buf, len, oobbuf, ooblen); +	switch (ops->mode) { +	case MTD_OPS_PLACE_OOB: +	case MTD_OPS_RAW: +		oobdelta = mtd->oobsize; +		break; +	case MTD_OPS_AUTO_OOB: +		oobdelta = mtd->ecclayout->oobavail; +		break; +	default: +		return -EINVAL; +	} +	if ((len % DOC_LAYOUT_PAGE_SIZE) || (ooblen % oobdelta) || +	    (ofs % DOC_LAYOUT_PAGE_SIZE)) +		return -EINVAL; +	if (len && ooblen && +	    (len / DOC_LAYOUT_PAGE_SIZE) != (ooblen / oobdelta)) +		return -EINVAL; +	if (ofs + len > mtd->size) +		return -EINVAL; + +	ops->oobretlen = 0; +	ops->retlen = 0; +	ret = 0; +	if (len == 0 && ooblen == 0) +		return -EINVAL; +	if (len == 0 && ooblen > 0) +		return doc_backup_oob(docg3, ofs, ops); + +	autoecc = doc_guess_autoecc(ops); +	if (autoecc < 0) +		return autoecc; + +	mutex_lock(&docg3->cascade->lock); +	while (!ret && len > 0) { +		memset(oob, 0, sizeof(oob)); +		if (ofs == docg3->oob_write_ofs) +			memcpy(oob, docg3->oob_write_buf, DOC_LAYOUT_OOB_SIZE); +		else if (ooblen > 0 && ops->mode == MTD_OPS_AUTO_OOB) +			doc_fill_autooob(oob, oobbuf); +		else if (ooblen > 0) +			memcpy(oob, oobbuf, DOC_LAYOUT_OOB_SIZE); +		ret = doc_write_page(docg3, ofs, buf, oob, autoecc); + +		ofs += DOC_LAYOUT_PAGE_SIZE; +		len -= DOC_LAYOUT_PAGE_SIZE; +		buf += DOC_LAYOUT_PAGE_SIZE; +		if (ooblen) { +			oobbuf += oobdelta; +			ooblen -= oobdelta; +			ops->oobretlen += oobdelta; +		} +		ops->retlen += DOC_LAYOUT_PAGE_SIZE; +	} + +	doc_set_device_id(docg3, 0); +	mutex_unlock(&docg3->cascade->lock); +	return ret; +} + +/** + * doc_write - Write a buffer to the chip + * @mtd: the device + * @to: the offset from first block and first page, in bytes, aligned on page + *      size + * @len: the number of bytes to write (must be a full page size, ie. 512) + * @retlen: the number of bytes actually written (0 or 512) + * @buf: the buffer to get bytes from + * + * Writes data to the chip. + * + * Returns 0 if write successful, -EIO if write error + */ +static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, +		     size_t *retlen, const u_char *buf) +{ +	struct docg3 *docg3 = mtd->priv; +	int ret; +	struct mtd_oob_ops ops; + +	doc_dbg("doc_write(to=%lld, len=%zu)\n", to, len); +	ops.datbuf = (char *)buf; +	ops.len = len; +	ops.mode = MTD_OPS_PLACE_OOB; +	ops.oobbuf = NULL; +	ops.ooblen = 0; +	ops.ooboffs = 0; + +	ret = doc_write_oob(mtd, to, &ops); +	*retlen = ops.retlen; +	return ret; +} + +static struct docg3 *sysfs_dev2docg3(struct device *dev, +				     struct device_attribute *attr) +{ +	int floor; +	struct platform_device *pdev = to_platform_device(dev); +	struct mtd_info **docg3_floors = platform_get_drvdata(pdev); + +	floor = attr->attr.name[1] - '0'; +	if (floor < 0 || floor >= DOC_MAX_NBFLOORS) +		return NULL; +	else +		return docg3_floors[floor]->priv; +} + +static ssize_t dps0_is_key_locked(struct device *dev, +				  struct device_attribute *attr, char *buf) +{ +	struct docg3 *docg3 = sysfs_dev2docg3(dev, attr); +	int dps0; + +	mutex_lock(&docg3->cascade->lock); +	doc_set_device_id(docg3, docg3->device_id); +	dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS); +	doc_set_device_id(docg3, 0); +	mutex_unlock(&docg3->cascade->lock); + +	return sprintf(buf, "%d\n", !(dps0 & DOC_DPS_KEY_OK)); +} + +static ssize_t dps1_is_key_locked(struct device *dev, +				  struct device_attribute *attr, char *buf) +{ +	struct docg3 *docg3 = sysfs_dev2docg3(dev, attr); +	int dps1; + +	mutex_lock(&docg3->cascade->lock); +	doc_set_device_id(docg3, docg3->device_id); +	dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS); +	doc_set_device_id(docg3, 0); +	mutex_unlock(&docg3->cascade->lock); + +	return sprintf(buf, "%d\n", !(dps1 & DOC_DPS_KEY_OK)); +} + +static ssize_t dps0_insert_key(struct device *dev, +			       struct device_attribute *attr, +			       const char *buf, size_t count) +{ +	struct docg3 *docg3 = sysfs_dev2docg3(dev, attr); +	int i; + +	if (count != DOC_LAYOUT_DPS_KEY_LENGTH) +		return -EINVAL; + +	mutex_lock(&docg3->cascade->lock); +	doc_set_device_id(docg3, docg3->device_id); +	for (i = 0; i < DOC_LAYOUT_DPS_KEY_LENGTH; i++) +		doc_writeb(docg3, buf[i], DOC_DPS0_KEY); +	doc_set_device_id(docg3, 0); +	mutex_unlock(&docg3->cascade->lock); +	return count; +} + +static ssize_t dps1_insert_key(struct device *dev, +			       struct device_attribute *attr, +			       const char *buf, size_t count) +{ +	struct docg3 *docg3 = sysfs_dev2docg3(dev, attr); +	int i; + +	if (count != DOC_LAYOUT_DPS_KEY_LENGTH) +		return -EINVAL; + +	mutex_lock(&docg3->cascade->lock); +	doc_set_device_id(docg3, docg3->device_id); +	for (i = 0; i < DOC_LAYOUT_DPS_KEY_LENGTH; i++) +		doc_writeb(docg3, buf[i], DOC_DPS1_KEY); +	doc_set_device_id(docg3, 0); +	mutex_unlock(&docg3->cascade->lock); +	return count; +} + +#define FLOOR_SYSFS(id) { \ +	__ATTR(f##id##_dps0_is_keylocked, S_IRUGO, dps0_is_key_locked, NULL), \ +	__ATTR(f##id##_dps1_is_keylocked, S_IRUGO, dps1_is_key_locked, NULL), \ +	__ATTR(f##id##_dps0_protection_key, S_IWUSR|S_IWGRP, NULL, dps0_insert_key), \ +	__ATTR(f##id##_dps1_protection_key, S_IWUSR|S_IWGRP, NULL, dps1_insert_key), \ +} + +static struct device_attribute doc_sys_attrs[DOC_MAX_NBFLOORS][4] = { +	FLOOR_SYSFS(0), FLOOR_SYSFS(1), FLOOR_SYSFS(2), FLOOR_SYSFS(3) +}; + +static int doc_register_sysfs(struct platform_device *pdev, +			      struct docg3_cascade *cascade) +{ +	int ret = 0, floor, i = 0; +	struct device *dev = &pdev->dev; + +	for (floor = 0; !ret && floor < DOC_MAX_NBFLOORS && +		     cascade->floors[floor]; floor++) +		for (i = 0; !ret && i < 4; i++) +			ret = device_create_file(dev, &doc_sys_attrs[floor][i]); +	if (!ret) +		return 0; +	do { +		while (--i >= 0) +			device_remove_file(dev, &doc_sys_attrs[floor][i]); +		i = 4; +	} while (--floor >= 0); +	return ret; +} + +static void doc_unregister_sysfs(struct platform_device *pdev, +				 struct docg3_cascade *cascade) +{ +	struct device *dev = &pdev->dev; +	int floor, i; + +	for (floor = 0; floor < DOC_MAX_NBFLOORS && cascade->floors[floor]; +	     floor++) +		for (i = 0; i < 4; i++) +			device_remove_file(dev, &doc_sys_attrs[floor][i]); +} + +/* + * Debug sysfs entries + */ +static int dbg_flashctrl_show(struct seq_file *s, void *p) +{ +	struct docg3 *docg3 = (struct docg3 *)s->private; + +	int pos = 0; +	u8 fctrl; + +	mutex_lock(&docg3->cascade->lock); +	fctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); +	mutex_unlock(&docg3->cascade->lock); + +	pos += seq_printf(s, +		 "FlashControl : 0x%02x (%s,CE# %s,%s,%s,flash %s)\n", +		 fctrl, +		 fctrl & DOC_CTRL_VIOLATION ? "protocol violation" : "-", +		 fctrl & DOC_CTRL_CE ? "active" : "inactive", +		 fctrl & DOC_CTRL_PROTECTION_ERROR ? "protection error" : "-", +		 fctrl & DOC_CTRL_SEQUENCE_ERROR ? "sequence error" : "-", +		 fctrl & DOC_CTRL_FLASHREADY ? "ready" : "not ready"); +	return pos; +} +DEBUGFS_RO_ATTR(flashcontrol, dbg_flashctrl_show); + +static int dbg_asicmode_show(struct seq_file *s, void *p) +{ +	struct docg3 *docg3 = (struct docg3 *)s->private; + +	int pos = 0, pctrl, mode; + +	mutex_lock(&docg3->cascade->lock); +	pctrl = doc_register_readb(docg3, DOC_ASICMODE); +	mode = pctrl & 0x03; +	mutex_unlock(&docg3->cascade->lock); + +	pos += seq_printf(s, +			 "%04x : RAM_WE=%d,RSTIN_RESET=%d,BDETCT_RESET=%d,WRITE_ENABLE=%d,POWERDOWN=%d,MODE=%d%d (", +			 pctrl, +			 pctrl & DOC_ASICMODE_RAM_WE ? 1 : 0, +			 pctrl & DOC_ASICMODE_RSTIN_RESET ? 1 : 0, +			 pctrl & DOC_ASICMODE_BDETCT_RESET ? 1 : 0, +			 pctrl & DOC_ASICMODE_MDWREN ? 1 : 0, +			 pctrl & DOC_ASICMODE_POWERDOWN ? 1 : 0, +			 mode >> 1, mode & 0x1); + +	switch (mode) { +	case DOC_ASICMODE_RESET: +		pos += seq_printf(s, "reset"); +		break; +	case DOC_ASICMODE_NORMAL: +		pos += seq_printf(s, "normal"); +		break; +	case DOC_ASICMODE_POWERDOWN: +		pos += seq_printf(s, "powerdown"); +		break; +	} +	pos += seq_printf(s, ")\n"); +	return pos; +} +DEBUGFS_RO_ATTR(asic_mode, dbg_asicmode_show); + +static int dbg_device_id_show(struct seq_file *s, void *p) +{ +	struct docg3 *docg3 = (struct docg3 *)s->private; +	int pos = 0; +	int id; + +	mutex_lock(&docg3->cascade->lock); +	id = doc_register_readb(docg3, DOC_DEVICESELECT); +	mutex_unlock(&docg3->cascade->lock); + +	pos += seq_printf(s, "DeviceId = %d\n", id); +	return pos; +} +DEBUGFS_RO_ATTR(device_id, dbg_device_id_show); + +static int dbg_protection_show(struct seq_file *s, void *p) +{ +	struct docg3 *docg3 = (struct docg3 *)s->private; +	int pos = 0; +	int protect, dps0, dps0_low, dps0_high, dps1, dps1_low, dps1_high; + +	mutex_lock(&docg3->cascade->lock); +	protect = doc_register_readb(docg3, DOC_PROTECTION); +	dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS); +	dps0_low = doc_register_readw(docg3, DOC_DPS0_ADDRLOW); +	dps0_high = doc_register_readw(docg3, DOC_DPS0_ADDRHIGH); +	dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS); +	dps1_low = doc_register_readw(docg3, DOC_DPS1_ADDRLOW); +	dps1_high = doc_register_readw(docg3, DOC_DPS1_ADDRHIGH); +	mutex_unlock(&docg3->cascade->lock); + +	pos += seq_printf(s, "Protection = 0x%02x (", +			 protect); +	if (protect & DOC_PROTECT_FOUNDRY_OTP_LOCK) +		pos += seq_printf(s, "FOUNDRY_OTP_LOCK,"); +	if (protect & DOC_PROTECT_CUSTOMER_OTP_LOCK) +		pos += seq_printf(s, "CUSTOMER_OTP_LOCK,"); +	if (protect & DOC_PROTECT_LOCK_INPUT) +		pos += seq_printf(s, "LOCK_INPUT,"); +	if (protect & DOC_PROTECT_STICKY_LOCK) +		pos += seq_printf(s, "STICKY_LOCK,"); +	if (protect & DOC_PROTECT_PROTECTION_ENABLED) +		pos += seq_printf(s, "PROTECTION ON,"); +	if (protect & DOC_PROTECT_IPL_DOWNLOAD_LOCK) +		pos += seq_printf(s, "IPL_DOWNLOAD_LOCK,"); +	if (protect & DOC_PROTECT_PROTECTION_ERROR) +		pos += seq_printf(s, "PROTECT_ERR,"); +	else +		pos += seq_printf(s, "NO_PROTECT_ERR"); +	pos += seq_printf(s, ")\n"); + +	pos += seq_printf(s, "DPS0 = 0x%02x : " +			 "Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, " +			 "WRITE=%d, HW_LOCK=%d, KEY_OK=%d\n", +			 dps0, dps0_low, dps0_high, +			 !!(dps0 & DOC_DPS_OTP_PROTECTED), +			 !!(dps0 & DOC_DPS_READ_PROTECTED), +			 !!(dps0 & DOC_DPS_WRITE_PROTECTED), +			 !!(dps0 & DOC_DPS_HW_LOCK_ENABLED), +			 !!(dps0 & DOC_DPS_KEY_OK)); +	pos += seq_printf(s, "DPS1 = 0x%02x : " +			 "Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, " +			 "WRITE=%d, HW_LOCK=%d, KEY_OK=%d\n", +			 dps1, dps1_low, dps1_high, +			 !!(dps1 & DOC_DPS_OTP_PROTECTED), +			 !!(dps1 & DOC_DPS_READ_PROTECTED), +			 !!(dps1 & DOC_DPS_WRITE_PROTECTED), +			 !!(dps1 & DOC_DPS_HW_LOCK_ENABLED), +			 !!(dps1 & DOC_DPS_KEY_OK)); +	return pos; +} +DEBUGFS_RO_ATTR(protection, dbg_protection_show); + +static int __init doc_dbg_register(struct docg3 *docg3) +{ +	struct dentry *root, *entry; + +	root = debugfs_create_dir("docg3", NULL); +	if (!root) +		return -ENOMEM; + +	entry = debugfs_create_file("flashcontrol", S_IRUSR, root, docg3, +				  &flashcontrol_fops); +	if (entry) +		entry = debugfs_create_file("asic_mode", S_IRUSR, root, +					    docg3, &asic_mode_fops); +	if (entry) +		entry = debugfs_create_file("device_id", S_IRUSR, root, +					    docg3, &device_id_fops); +	if (entry) +		entry = debugfs_create_file("protection", S_IRUSR, root, +					    docg3, &protection_fops); +	if (entry) { +		docg3->debugfs_root = root; +		return 0; +	} else { +		debugfs_remove_recursive(root); +		return -ENOMEM; +	} +} + +static void __exit doc_dbg_unregister(struct docg3 *docg3) +{ +	debugfs_remove_recursive(docg3->debugfs_root); +} + +/** + * doc_set_driver_info - Fill the mtd_info structure and docg3 structure + * @chip_id: The chip ID of the supported chip + * @mtd: The structure to fill + */ +static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd) +{ +	struct docg3 *docg3 = mtd->priv; +	int cfg; + +	cfg = doc_register_readb(docg3, DOC_CONFIGURATION); +	docg3->if_cfg = (cfg & DOC_CONF_IF_CFG ? 1 : 0); +	docg3->reliable = reliable_mode; + +	switch (chip_id) { +	case DOC_CHIPID_G3: +		mtd->name = kasprintf(GFP_KERNEL, "docg3.%d", +				      docg3->device_id); +		docg3->max_block = 2047; +		break; +	} +	mtd->type = MTD_NANDFLASH; +	mtd->flags = MTD_CAP_NANDFLASH; +	mtd->size = (docg3->max_block + 1) * DOC_LAYOUT_BLOCK_SIZE; +	if (docg3->reliable == 2) +		mtd->size /= 2; +	mtd->erasesize = DOC_LAYOUT_BLOCK_SIZE * DOC_LAYOUT_NBPLANES; +	if (docg3->reliable == 2) +		mtd->erasesize /= 2; +	mtd->writebufsize = mtd->writesize = DOC_LAYOUT_PAGE_SIZE; +	mtd->oobsize = DOC_LAYOUT_OOB_SIZE; +	mtd->owner = THIS_MODULE; +	mtd->_erase = doc_erase; +	mtd->_read = doc_read; +	mtd->_write = doc_write; +	mtd->_read_oob = doc_read_oob; +	mtd->_write_oob = doc_write_oob; +	mtd->_block_isbad = doc_block_isbad; +	mtd->ecclayout = &docg3_oobinfo; +	mtd->ecc_strength = DOC_ECC_BCH_T; +} + +/** + * doc_probe_device - Check if a device is available + * @base: the io space where the device is probed + * @floor: the floor of the probed device + * @dev: the device + * @cascade: the cascade of chips this devices will belong to + * + * Checks whether a device at the specified IO range, and floor is available. + * + * Returns a mtd_info struct if there is a device, ENODEV if none found, ENOMEM + * if a memory allocation failed. If floor 0 is checked, a reset of the ASIC is + * launched. + */ +static struct mtd_info * __init +doc_probe_device(struct docg3_cascade *cascade, int floor, struct device *dev) +{ +	int ret, bbt_nbpages; +	u16 chip_id, chip_id_inv; +	struct docg3 *docg3; +	struct mtd_info *mtd; + +	ret = -ENOMEM; +	docg3 = kzalloc(sizeof(struct docg3), GFP_KERNEL); +	if (!docg3) +		goto nomem1; +	mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); +	if (!mtd) +		goto nomem2; +	mtd->priv = docg3; +	bbt_nbpages = DIV_ROUND_UP(docg3->max_block + 1, +				   8 * DOC_LAYOUT_PAGE_SIZE); +	docg3->bbt = kzalloc(bbt_nbpages * DOC_LAYOUT_PAGE_SIZE, GFP_KERNEL); +	if (!docg3->bbt) +		goto nomem3; + +	docg3->dev = dev; +	docg3->device_id = floor; +	docg3->cascade = cascade; +	doc_set_device_id(docg3, docg3->device_id); +	if (!floor) +		doc_set_asic_mode(docg3, DOC_ASICMODE_RESET); +	doc_set_asic_mode(docg3, DOC_ASICMODE_NORMAL); + +	chip_id = doc_register_readw(docg3, DOC_CHIPID); +	chip_id_inv = doc_register_readw(docg3, DOC_CHIPID_INV); + +	ret = 0; +	if (chip_id != (u16)(~chip_id_inv)) { +		goto nomem3; +	} + +	switch (chip_id) { +	case DOC_CHIPID_G3: +		doc_info("Found a G3 DiskOnChip at addr %p, floor %d\n", +			 docg3->cascade->base, floor); +		break; +	default: +		doc_err("Chip id %04x is not a DiskOnChip G3 chip\n", chip_id); +		goto nomem3; +	} + +	doc_set_driver_info(chip_id, mtd); + +	doc_hamming_ecc_init(docg3, DOC_LAYOUT_OOB_PAGEINFO_SZ); +	doc_reload_bbt(docg3); +	return mtd; + +nomem3: +	kfree(mtd); +nomem2: +	kfree(docg3); +nomem1: +	return ERR_PTR(ret); +} + +/** + * doc_release_device - Release a docg3 floor + * @mtd: the device + */ +static void doc_release_device(struct mtd_info *mtd) +{ +	struct docg3 *docg3 = mtd->priv; + +	mtd_device_unregister(mtd); +	kfree(docg3->bbt); +	kfree(docg3); +	kfree(mtd->name); +	kfree(mtd); +} + +/** + * docg3_resume - Awakens docg3 floor + * @pdev: platfrom device + * + * Returns 0 (always successful) + */ +static int docg3_resume(struct platform_device *pdev) +{ +	int i; +	struct docg3_cascade *cascade; +	struct mtd_info **docg3_floors, *mtd; +	struct docg3 *docg3; + +	cascade = platform_get_drvdata(pdev); +	docg3_floors = cascade->floors; +	mtd = docg3_floors[0]; +	docg3 = mtd->priv; + +	doc_dbg("docg3_resume()\n"); +	for (i = 0; i < 12; i++) +		doc_readb(docg3, DOC_IOSPACE_IPL); +	return 0; +} + +/** + * docg3_suspend - Put in low power mode the docg3 floor + * @pdev: platform device + * @state: power state + * + * Shuts off most of docg3 circuitery to lower power consumption. + * + * Returns 0 if suspend succeeded, -EIO if chip refused suspend + */ +static int docg3_suspend(struct platform_device *pdev, pm_message_t state) +{ +	int floor, i; +	struct docg3_cascade *cascade; +	struct mtd_info **docg3_floors, *mtd; +	struct docg3 *docg3; +	u8 ctrl, pwr_down; + +	cascade = platform_get_drvdata(pdev); +	docg3_floors = cascade->floors; +	for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) { +		mtd = docg3_floors[floor]; +		if (!mtd) +			continue; +		docg3 = mtd->priv; + +		doc_writeb(docg3, floor, DOC_DEVICESELECT); +		ctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); +		ctrl &= ~DOC_CTRL_VIOLATION & ~DOC_CTRL_CE; +		doc_writeb(docg3, ctrl, DOC_FLASHCONTROL); + +		for (i = 0; i < 10; i++) { +			usleep_range(3000, 4000); +			pwr_down = doc_register_readb(docg3, DOC_POWERMODE); +			if (pwr_down & DOC_POWERDOWN_READY) +				break; +		} +		if (pwr_down & DOC_POWERDOWN_READY) { +			doc_dbg("docg3_suspend(): floor %d powerdown ok\n", +				floor); +		} else { +			doc_err("docg3_suspend(): floor %d powerdown failed\n", +				floor); +			return -EIO; +		} +	} + +	mtd = docg3_floors[0]; +	docg3 = mtd->priv; +	doc_set_asic_mode(docg3, DOC_ASICMODE_POWERDOWN); +	return 0; +} + +/** + * doc_probe - Probe the IO space for a DiskOnChip G3 chip + * @pdev: platform device + * + * Probes for a G3 chip at the specified IO space in the platform data + * ressources. The floor 0 must be available. + * + * Returns 0 on success, -ENOMEM, -ENXIO on error + */ +static int __init docg3_probe(struct platform_device *pdev) +{ +	struct device *dev = &pdev->dev; +	struct mtd_info *mtd; +	struct resource *ress; +	void __iomem *base; +	int ret, floor, found = 0; +	struct docg3_cascade *cascade; + +	ret = -ENXIO; +	ress = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!ress) { +		dev_err(dev, "No I/O memory resource defined\n"); +		return ret; +	} +	base = devm_ioremap(dev, ress->start, DOC_IOSPACE_SIZE); + +	ret = -ENOMEM; +	cascade = devm_kzalloc(dev, sizeof(*cascade) * DOC_MAX_NBFLOORS, +			       GFP_KERNEL); +	if (!cascade) +		return ret; +	cascade->base = base; +	mutex_init(&cascade->lock); +	cascade->bch = init_bch(DOC_ECC_BCH_M, DOC_ECC_BCH_T, +			     DOC_ECC_BCH_PRIMPOLY); +	if (!cascade->bch) +		return ret; + +	for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) { +		mtd = doc_probe_device(cascade, floor, dev); +		if (IS_ERR(mtd)) { +			ret = PTR_ERR(mtd); +			goto err_probe; +		} +		if (!mtd) { +			if (floor == 0) +				goto notfound; +			else +				continue; +		} +		cascade->floors[floor] = mtd; +		ret = mtd_device_parse_register(mtd, part_probes, NULL, NULL, +						0); +		if (ret) +			goto err_probe; +		found++; +	} + +	ret = doc_register_sysfs(pdev, cascade); +	if (ret) +		goto err_probe; +	if (!found) +		goto notfound; + +	platform_set_drvdata(pdev, cascade); +	doc_dbg_register(cascade->floors[0]->priv); +	return 0; + +notfound: +	ret = -ENODEV; +	dev_info(dev, "No supported DiskOnChip found\n"); +err_probe: +	free_bch(cascade->bch); +	for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) +		if (cascade->floors[floor]) +			doc_release_device(cascade->floors[floor]); +	return ret; +} + +/** + * docg3_release - Release the driver + * @pdev: the platform device + * + * Returns 0 + */ +static int __exit docg3_release(struct platform_device *pdev) +{ +	struct docg3_cascade *cascade = platform_get_drvdata(pdev); +	struct docg3 *docg3 = cascade->floors[0]->priv; +	int floor; + +	doc_unregister_sysfs(pdev, cascade); +	doc_dbg_unregister(docg3); +	for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) +		if (cascade->floors[floor]) +			doc_release_device(cascade->floors[floor]); + +	free_bch(docg3->cascade->bch); +	return 0; +} + +static struct platform_driver g3_driver = { +	.driver		= { +		.name	= "docg3", +		.owner	= THIS_MODULE, +	}, +	.suspend	= docg3_suspend, +	.resume		= docg3_resume, +	.remove		= __exit_p(docg3_release), +}; + +module_platform_driver_probe(g3_driver, docg3_probe); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>"); +MODULE_DESCRIPTION("MTD driver for DiskOnChip G3"); diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h new file mode 100644 index 00000000000..19fb93f96a3 --- /dev/null +++ b/drivers/mtd/devices/docg3.h @@ -0,0 +1,370 @@ +/* + * Handles the M-Systems DiskOnChip G3 chip + * + * Copyright (C) 2011 Robert Jarzmik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + */ + +#ifndef _MTD_DOCG3_H +#define _MTD_DOCG3_H + +#include <linux/mtd/mtd.h> + +/* + * Flash memory areas : + *   - 0x0000 .. 0x07ff : IPL + *   - 0x0800 .. 0x0fff : Data area + *   - 0x1000 .. 0x17ff : Registers + *   - 0x1800 .. 0x1fff : Unknown + */ +#define DOC_IOSPACE_IPL			0x0000 +#define DOC_IOSPACE_DATA		0x0800 +#define DOC_IOSPACE_SIZE		0x2000 + +/* + * DOC G3 layout and adressing scheme + *   A page address for the block "b", plane "P" and page "p": + *   address = [bbbb bPpp pppp] + */ + +#define DOC_ADDR_PAGE_MASK		0x3f +#define DOC_ADDR_BLOCK_SHIFT		6 +#define DOC_LAYOUT_NBPLANES		2 +#define DOC_LAYOUT_PAGES_PER_BLOCK	64 +#define DOC_LAYOUT_PAGE_SIZE		512 +#define DOC_LAYOUT_OOB_SIZE		16 +#define DOC_LAYOUT_WEAR_SIZE		8 +#define DOC_LAYOUT_PAGE_OOB_SIZE				\ +	(DOC_LAYOUT_PAGE_SIZE + DOC_LAYOUT_OOB_SIZE) +#define DOC_LAYOUT_WEAR_OFFSET		(DOC_LAYOUT_PAGE_OOB_SIZE * 2) +#define DOC_LAYOUT_BLOCK_SIZE					\ +	(DOC_LAYOUT_PAGES_PER_BLOCK * DOC_LAYOUT_PAGE_SIZE) + +/* + * ECC related constants + */ +#define DOC_ECC_BCH_M			14 +#define DOC_ECC_BCH_T			4 +#define DOC_ECC_BCH_PRIMPOLY		0x4443 +#define DOC_ECC_BCH_SIZE		7 +#define DOC_ECC_BCH_COVERED_BYTES				\ +	(DOC_LAYOUT_PAGE_SIZE + DOC_LAYOUT_OOB_PAGEINFO_SZ +	\ +	 DOC_LAYOUT_OOB_HAMMING_SZ) +#define DOC_ECC_BCH_TOTAL_BYTES					\ +	(DOC_ECC_BCH_COVERED_BYTES + DOC_LAYOUT_OOB_BCH_SZ) + +/* + * Blocks distribution + */ +#define DOC_LAYOUT_BLOCK_BBT		0 +#define DOC_LAYOUT_BLOCK_OTP		0 +#define DOC_LAYOUT_BLOCK_FIRST_DATA	6 + +#define DOC_LAYOUT_PAGE_BBT		4 + +/* + * Extra page OOB (16 bytes wide) layout + */ +#define DOC_LAYOUT_OOB_PAGEINFO_OFS	0 +#define DOC_LAYOUT_OOB_HAMMING_OFS	7 +#define DOC_LAYOUT_OOB_BCH_OFS		8 +#define DOC_LAYOUT_OOB_UNUSED_OFS	15 +#define DOC_LAYOUT_OOB_PAGEINFO_SZ	7 +#define DOC_LAYOUT_OOB_HAMMING_SZ	1 +#define DOC_LAYOUT_OOB_BCH_SZ		7 +#define DOC_LAYOUT_OOB_UNUSED_SZ	1 + + +#define DOC_CHIPID_G3			0x200 +#define DOC_ERASE_MARK			0xaa +#define DOC_MAX_NBFLOORS		4 +/* + * Flash registers + */ +#define DOC_CHIPID			0x1000 +#define DOC_TEST			0x1004 +#define DOC_BUSLOCK			0x1006 +#define DOC_ENDIANCONTROL		0x1008 +#define DOC_DEVICESELECT		0x100a +#define DOC_ASICMODE			0x100c +#define DOC_CONFIGURATION		0x100e +#define DOC_INTERRUPTCONTROL		0x1010 +#define DOC_READADDRESS			0x101a +#define DOC_DATAEND			0x101e +#define DOC_INTERRUPTSTATUS		0x1020 + +#define DOC_FLASHSEQUENCE		0x1032 +#define DOC_FLASHCOMMAND		0x1034 +#define DOC_FLASHADDRESS		0x1036 +#define DOC_FLASHCONTROL		0x1038 +#define DOC_NOP				0x103e + +#define DOC_ECCCONF0			0x1040 +#define DOC_ECCCONF1			0x1042 +#define DOC_ECCPRESET			0x1044 +#define DOC_HAMMINGPARITY		0x1046 +#define DOC_BCH_HW_ECC(idx)		(0x1048 + idx) + +#define DOC_PROTECTION			0x1056 +#define DOC_DPS0_KEY			0x105c +#define DOC_DPS1_KEY			0x105e +#define DOC_DPS0_ADDRLOW		0x1060 +#define DOC_DPS0_ADDRHIGH		0x1062 +#define DOC_DPS1_ADDRLOW		0x1064 +#define DOC_DPS1_ADDRHIGH		0x1066 +#define DOC_DPS0_STATUS			0x106c +#define DOC_DPS1_STATUS			0x106e + +#define DOC_ASICMODECONFIRM		0x1072 +#define DOC_CHIPID_INV			0x1074 +#define DOC_POWERMODE			0x107c + +/* + * Flash sequences + * A sequence is preset before one or more commands are input to the chip. + */ +#define DOC_SEQ_RESET			0x00 +#define DOC_SEQ_PAGE_SIZE_532		0x03 +#define DOC_SEQ_SET_FASTMODE		0x05 +#define DOC_SEQ_SET_RELIABLEMODE	0x09 +#define DOC_SEQ_READ			0x12 +#define DOC_SEQ_SET_PLANE1		0x0e +#define DOC_SEQ_SET_PLANE2		0x10 +#define DOC_SEQ_PAGE_SETUP		0x1d +#define DOC_SEQ_ERASE			0x27 +#define DOC_SEQ_PLANES_STATUS		0x31 + +/* + * Flash commands + */ +#define DOC_CMD_READ_PLANE1		0x00 +#define DOC_CMD_SET_ADDR_READ		0x05 +#define DOC_CMD_READ_ALL_PLANES		0x30 +#define DOC_CMD_READ_PLANE2		0x50 +#define DOC_CMD_READ_FLASH		0xe0 +#define DOC_CMD_PAGE_SIZE_532		0x3c + +#define DOC_CMD_PROG_BLOCK_ADDR		0x60 +#define DOC_CMD_PROG_CYCLE1		0x80 +#define DOC_CMD_PROG_CYCLE2		0x10 +#define DOC_CMD_PROG_CYCLE3		0x11 +#define DOC_CMD_ERASECYCLE2		0xd0 +#define DOC_CMD_READ_STATUS		0x70 +#define DOC_CMD_PLANES_STATUS		0x71 + +#define DOC_CMD_RELIABLE_MODE		0x22 +#define DOC_CMD_FAST_MODE		0xa2 + +#define DOC_CMD_RESET			0xff + +/* + * Flash register : DOC_FLASHCONTROL + */ +#define DOC_CTRL_VIOLATION		0x20 +#define DOC_CTRL_CE			0x10 +#define DOC_CTRL_UNKNOWN_BITS		0x08 +#define DOC_CTRL_PROTECTION_ERROR	0x04 +#define DOC_CTRL_SEQUENCE_ERROR		0x02 +#define DOC_CTRL_FLASHREADY		0x01 + +/* + * Flash register : DOC_ASICMODE + */ +#define DOC_ASICMODE_RESET		0x00 +#define DOC_ASICMODE_NORMAL		0x01 +#define DOC_ASICMODE_POWERDOWN		0x02 +#define DOC_ASICMODE_MDWREN		0x04 +#define DOC_ASICMODE_BDETCT_RESET	0x08 +#define DOC_ASICMODE_RSTIN_RESET	0x10 +#define DOC_ASICMODE_RAM_WE		0x20 + +/* + * Flash register : DOC_ECCCONF0 + */ +#define DOC_ECCCONF0_WRITE_MODE		0x0000 +#define DOC_ECCCONF0_READ_MODE		0x8000 +#define DOC_ECCCONF0_AUTO_ECC_ENABLE	0x4000 +#define DOC_ECCCONF0_HAMMING_ENABLE	0x1000 +#define DOC_ECCCONF0_BCH_ENABLE		0x0800 +#define DOC_ECCCONF0_DATA_BYTES_MASK	0x07ff + +/* + * Flash register : DOC_ECCCONF1 + */ +#define DOC_ECCCONF1_BCH_SYNDROM_ERR	0x80 +#define DOC_ECCCONF1_UNKOWN1		0x40 +#define DOC_ECCCONF1_PAGE_IS_WRITTEN	0x20 +#define DOC_ECCCONF1_UNKOWN3		0x10 +#define DOC_ECCCONF1_HAMMING_BITS_MASK	0x0f + +/* + * Flash register : DOC_PROTECTION + */ +#define DOC_PROTECT_FOUNDRY_OTP_LOCK	0x01 +#define DOC_PROTECT_CUSTOMER_OTP_LOCK	0x02 +#define DOC_PROTECT_LOCK_INPUT		0x04 +#define DOC_PROTECT_STICKY_LOCK		0x08 +#define DOC_PROTECT_PROTECTION_ENABLED	0x10 +#define DOC_PROTECT_IPL_DOWNLOAD_LOCK	0x20 +#define DOC_PROTECT_PROTECTION_ERROR	0x80 + +/* + * Flash register : DOC_DPS0_STATUS and DOC_DPS1_STATUS + */ +#define DOC_DPS_OTP_PROTECTED		0x01 +#define DOC_DPS_READ_PROTECTED		0x02 +#define DOC_DPS_WRITE_PROTECTED		0x04 +#define DOC_DPS_HW_LOCK_ENABLED		0x08 +#define DOC_DPS_KEY_OK			0x80 + +/* + * Flash register : DOC_CONFIGURATION + */ +#define DOC_CONF_IF_CFG			0x80 +#define DOC_CONF_MAX_ID_MASK		0x30 +#define DOC_CONF_VCCQ_3V		0x01 + +/* + * Flash register : DOC_READADDRESS + */ +#define DOC_READADDR_INC		0x8000 +#define DOC_READADDR_ONE_BYTE		0x4000 +#define DOC_READADDR_ADDR_MASK		0x1fff + +/* + * Flash register : DOC_POWERMODE + */ +#define DOC_POWERDOWN_READY		0x80 + +/* + * Status of erase and write operation + */ +#define DOC_PLANES_STATUS_FAIL		0x01 +#define DOC_PLANES_STATUS_PLANE0_KO	0x02 +#define DOC_PLANES_STATUS_PLANE1_KO	0x04 + +/* + * DPS key management + * + * Each floor of docg3 has 2 protection areas: DPS0 and DPS1. These areas span + * across block boundaries, and define whether these blocks can be read or + * written. + * The definition is dynamically stored in page 0 of blocks (2,3) for DPS0, and + * page 0 of blocks (4,5) for DPS1. + */ +#define DOC_LAYOUT_DPS_KEY_LENGTH	8 + +/** + * struct docg3_cascade - Cascade of 1 to 4 docg3 chips + * @floors: floors (ie. one physical docg3 chip is one floor) + * @base: IO space to access all chips in the cascade + * @bch: the BCH correcting control structure + * @lock: lock to protect docg3 IO space from concurrent accesses + */ +struct docg3_cascade { +	struct mtd_info *floors[DOC_MAX_NBFLOORS]; +	void __iomem *base; +	struct bch_control *bch; +	struct mutex lock; +}; + +/** + * struct docg3 - DiskOnChip driver private data + * @dev: the device currently under control + * @cascade: the cascade this device belongs to + * @device_id: number of the cascaded DoCG3 device (0, 1, 2 or 3) + * @if_cfg: if true, reads are on 16bits, else reads are on 8bits + + * @reliable: if 0, docg3 in normal mode, if 1 docg3 in fast mode, if 2 in + *            reliable mode + *            Fast mode implies more errors than normal mode. + *            Reliable mode implies that page 2*n and 2*n+1 are clones. + * @bbt: bad block table cache + * @oob_write_ofs: offset of the MTD where this OOB should belong (ie. in next + *                 page_write) + * @oob_autoecc: if 1, use only bytes 0-7, 15, and fill the others with HW ECC + *               if 0, use all the 16 bytes. + * @oob_write_buf: prepared OOB for next page_write + * @debugfs_root: debugfs root node + */ +struct docg3 { +	struct device *dev; +	struct docg3_cascade *cascade; +	unsigned int device_id:4; +	unsigned int if_cfg:1; +	unsigned int reliable:2; +	int max_block; +	u8 *bbt; +	loff_t oob_write_ofs; +	int oob_autoecc; +	u8 oob_write_buf[DOC_LAYOUT_OOB_SIZE]; +	struct dentry *debugfs_root; +}; + +#define doc_err(fmt, arg...) dev_err(docg3->dev, (fmt), ## arg) +#define doc_info(fmt, arg...) dev_info(docg3->dev, (fmt), ## arg) +#define doc_dbg(fmt, arg...) dev_dbg(docg3->dev, (fmt), ## arg) +#define doc_vdbg(fmt, arg...) dev_vdbg(docg3->dev, (fmt), ## arg) + +#define DEBUGFS_RO_ATTR(name, show_fct) \ +	static int name##_open(struct inode *inode, struct file *file) \ +	{ return single_open(file, show_fct, inode->i_private); }      \ +	static const struct file_operations name##_fops = { \ +		.owner = THIS_MODULE, \ +		.open = name##_open, \ +		.llseek = seq_lseek, \ +		.read = seq_read, \ +		.release = single_release \ +	}; +#endif + +/* + * Trace events part + */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM docg3 + +#if !defined(_MTD_DOCG3_TRACE) || defined(TRACE_HEADER_MULTI_READ) +#define _MTD_DOCG3_TRACE + +#include <linux/tracepoint.h> + +TRACE_EVENT(docg3_io, +	    TP_PROTO(int op, int width, u16 reg, int val), +	    TP_ARGS(op, width, reg, val), +	    TP_STRUCT__entry( +		    __field(int, op) +		    __field(unsigned char, width) +		    __field(u16, reg) +		    __field(int, val)), +	    TP_fast_assign( +		    __entry->op = op; +		    __entry->width = width; +		    __entry->reg = reg; +		    __entry->val = val;), +	    TP_printk("docg3: %s%02d reg=%04x, val=%04x", +		      __entry->op ? "write" : "read", __entry->width, +		      __entry->reg, __entry->val) +	); +#endif + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE docg3 +#include <trace/define_trace.h> diff --git a/drivers/mtd/devices/docprobe.c b/drivers/mtd/devices/docprobe.c deleted file mode 100644 index d374603493a..00000000000 --- a/drivers/mtd/devices/docprobe.c +++ /dev/null @@ -1,337 +0,0 @@ - -/* Linux driver for Disk-On-Chip devices			*/ -/* Probe routines common to all DoC devices			*/ -/* (C) 1999 Machine Vision Holdings, Inc.			*/ -/* (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>		*/ - - -/* DOC_PASSIVE_PROBE: -   In order to ensure that the BIOS checksum is correct at boot time, and -   hence that the onboard BIOS extension gets executed, the DiskOnChip -   goes into reset mode when it is read sequentially: all registers -   return 0xff until the chip is woken up again by writing to the -   DOCControl register. - -   Unfortunately, this means that the probe for the DiskOnChip is unsafe, -   because one of the first things it does is write to where it thinks -   the DOCControl register should be - which may well be shared memory -   for another device. I've had machines which lock up when this is -   attempted. Hence the possibility to do a passive probe, which will fail -   to detect a chip in reset mode, but is at least guaranteed not to lock -   the machine. - -   If you have this problem, uncomment the following line: -#define DOC_PASSIVE_PROBE -*/ - - -/* DOC_SINGLE_DRIVER: -   Millennium driver has been merged into DOC2000 driver. - -   The old Millennium-only driver has been retained just in case there -   are problems with the new code. If the combined driver doesn't work -   for you, you can try the old one by undefining DOC_SINGLE_DRIVER -   below and also enabling it in your configuration. If this fixes the -   problems, please send a report to the MTD mailing list at -   <linux-mtd@lists.infradead.org>. -*/ -#define DOC_SINGLE_DRIVER - -#include <linux/kernel.h> -#include <linux/module.h> -#include <asm/errno.h> -#include <asm/io.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/types.h> - -#include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> -#include <linux/mtd/doc2000.h> - -/* Where to look for the devices? */ -#ifndef CONFIG_MTD_DOCPROBE_ADDRESS -#define CONFIG_MTD_DOCPROBE_ADDRESS 0 -#endif - - -static unsigned long doc_config_location = CONFIG_MTD_DOCPROBE_ADDRESS; -module_param(doc_config_location, ulong, 0); -MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip"); - -static unsigned long __initdata doc_locations[] = { -#if defined (__alpha__) || defined(__i386__) || defined(__x86_64__) -#ifdef CONFIG_MTD_DOCPROBE_HIGH -	0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000, -	0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000, -	0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000, -	0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000, -	0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000, -#else /*  CONFIG_MTD_DOCPROBE_HIGH */ -	0xc8000, 0xca000, 0xcc000, 0xce000, -	0xd0000, 0xd2000, 0xd4000, 0xd6000, -	0xd8000, 0xda000, 0xdc000, 0xde000, -	0xe0000, 0xe2000, 0xe4000, 0xe6000, -	0xe8000, 0xea000, 0xec000, 0xee000, -#endif /*  CONFIG_MTD_DOCPROBE_HIGH */ -#else -#warning Unknown architecture for DiskOnChip. No default probe locations defined -#endif -	0xffffffff }; - -/* doccheck: Probe a given memory window to see if there's a DiskOnChip present */ - -static inline int __init doccheck(void __iomem *potential, unsigned long physadr) -{ -	void __iomem *window=potential; -	unsigned char tmp, tmpb, tmpc, ChipID; -#ifndef DOC_PASSIVE_PROBE -	unsigned char tmp2; -#endif - -	/* Routine copied from the Linux DOC driver */ - -#ifdef CONFIG_MTD_DOCPROBE_55AA -	/* Check for 0x55 0xAA signature at beginning of window, -	   this is no longer true once we remove the IPL (for Millennium */ -	if (ReadDOC(window, Sig1) != 0x55 || ReadDOC(window, Sig2) != 0xaa) -		return 0; -#endif /* CONFIG_MTD_DOCPROBE_55AA */ - -#ifndef DOC_PASSIVE_PROBE -	/* It's not possible to cleanly detect the DiskOnChip - the -	 * bootup procedure will put the device into reset mode, and -	 * it's not possible to talk to it without actually writing -	 * to the DOCControl register. So we store the current contents -	 * of the DOCControl register's location, in case we later decide -	 * that it's not a DiskOnChip, and want to put it back how we -	 * found it. -	 */ -	tmp2 = ReadDOC(window, DOCControl); - -	/* Reset the DiskOnChip ASIC */ -	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, -		 window, DOCControl); -	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, -		 window, DOCControl); - -	/* Enable the DiskOnChip ASIC */ -	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, -		 window, DOCControl); -	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, -		 window, DOCControl); -#endif /* !DOC_PASSIVE_PROBE */ - -	/* We need to read the ChipID register four times. For some -	   newer DiskOnChip 2000 units, the first three reads will -	   return the DiskOnChip Millennium ident. Don't ask. */ -	ChipID = ReadDOC(window, ChipID); - -	switch (ChipID) { -	case DOC_ChipID_Doc2k: -		/* Check the TOGGLE bit in the ECC register */ -		tmp  = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT; -		tmpb = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT; -		tmpc = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT; -		if (tmp != tmpb && tmp == tmpc) -				return ChipID; -		break; - -	case DOC_ChipID_DocMil: -		/* Check for the new 2000 with Millennium ASIC */ -		ReadDOC(window, ChipID); -		ReadDOC(window, ChipID); -		if (ReadDOC(window, ChipID) != DOC_ChipID_DocMil) -			ChipID = DOC_ChipID_Doc2kTSOP; - -		/* Check the TOGGLE bit in the ECC register */ -		tmp  = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT; -		tmpb = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT; -		tmpc = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT; -		if (tmp != tmpb && tmp == tmpc) -				return ChipID; -		break; - -	case DOC_ChipID_DocMilPlus16: -	case DOC_ChipID_DocMilPlus32: -	case 0: -		/* Possible Millennium+, need to do more checks */ -#ifndef DOC_PASSIVE_PROBE -		/* Possibly release from power down mode */ -		for (tmp = 0; (tmp < 4); tmp++) -			ReadDOC(window, Mplus_Power); - -		/* Reset the DiskOnChip ASIC */ -		tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | -			DOC_MODE_BDECT; -		WriteDOC(tmp, window, Mplus_DOCControl); -		WriteDOC(~tmp, window, Mplus_CtrlConfirm); - -		mdelay(1); -		/* Enable the DiskOnChip ASIC */ -		tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | -			DOC_MODE_BDECT; -		WriteDOC(tmp, window, Mplus_DOCControl); -		WriteDOC(~tmp, window, Mplus_CtrlConfirm); -		mdelay(1); -#endif /* !DOC_PASSIVE_PROBE */ - -		ChipID = ReadDOC(window, ChipID); - -		switch (ChipID) { -		case DOC_ChipID_DocMilPlus16: -		case DOC_ChipID_DocMilPlus32: -			/* Check the TOGGLE bit in the toggle register */ -			tmp  = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT; -			tmpb = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT; -			tmpc = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT; -			if (tmp != tmpb && tmp == tmpc) -					return ChipID; -		default: -			break; -		} -		/* FALL TRHU */ - -	default: - -#ifdef CONFIG_MTD_DOCPROBE_55AA -		printk(KERN_DEBUG "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n", -		       ChipID, physadr); -#endif -#ifndef DOC_PASSIVE_PROBE -		/* Put back the contents of the DOCControl register, in case it's not -		 * actually a DiskOnChip. -		 */ -		WriteDOC(tmp2, window, DOCControl); -#endif -		return 0; -	} - -	printk(KERN_WARNING "DiskOnChip failed TOGGLE test, dropping.\n"); - -#ifndef DOC_PASSIVE_PROBE -	/* Put back the contents of the DOCControl register: it's not a DiskOnChip */ -	WriteDOC(tmp2, window, DOCControl); -#endif -	return 0; -} - -static int docfound; - -extern void DoC2k_init(struct mtd_info *); -extern void DoCMil_init(struct mtd_info *); -extern void DoCMilPlus_init(struct mtd_info *); - -static void __init DoC_Probe(unsigned long physadr) -{ -	void __iomem *docptr; -	struct DiskOnChip *this; -	struct mtd_info *mtd; -	int ChipID; -	char namebuf[15]; -	char *name = namebuf; -	void (*initroutine)(struct mtd_info *) = NULL; - -	docptr = ioremap(physadr, DOC_IOREMAP_LEN); - -	if (!docptr) -		return; - -	if ((ChipID = doccheck(docptr, physadr))) { -		if (ChipID == DOC_ChipID_Doc2kTSOP) { -			/* Remove this at your own peril. The hardware driver works but nothing prevents you from erasing bad blocks */ -			printk(KERN_NOTICE "Refusing to drive DiskOnChip 2000 TSOP until Bad Block Table is correctly supported by INFTL\n"); -			iounmap(docptr); -			return; -		} -		docfound = 1; -		mtd = kmalloc(sizeof(struct DiskOnChip) + sizeof(struct mtd_info), GFP_KERNEL); - -		if (!mtd) { -			printk(KERN_WARNING "Cannot allocate memory for data structures. Dropping.\n"); -			iounmap(docptr); -			return; -		} - -		this = (struct DiskOnChip *)(&mtd[1]); - -		memset((char *)mtd,0, sizeof(struct mtd_info)); -		memset((char *)this, 0, sizeof(struct DiskOnChip)); - -		mtd->priv = this; -		this->virtadr = docptr; -		this->physadr = physadr; -		this->ChipID = ChipID; -		sprintf(namebuf, "with ChipID %2.2X", ChipID); - -		switch(ChipID) { -		case DOC_ChipID_Doc2kTSOP: -			name="2000 TSOP"; -			initroutine = symbol_request(DoC2k_init); -			break; - -		case DOC_ChipID_Doc2k: -			name="2000"; -			initroutine = symbol_request(DoC2k_init); -			break; - -		case DOC_ChipID_DocMil: -			name="Millennium"; -#ifdef DOC_SINGLE_DRIVER -			initroutine = symbol_request(DoC2k_init); -#else -			initroutine = symbol_request(DoCMil_init); -#endif /* DOC_SINGLE_DRIVER */ -			break; - -		case DOC_ChipID_DocMilPlus16: -		case DOC_ChipID_DocMilPlus32: -			name="MillenniumPlus"; -			initroutine = symbol_request(DoCMilPlus_init); -			break; -		} - -		if (initroutine) { -			(*initroutine)(mtd); -			symbol_put_addr(initroutine); -			return; -		} -		printk(KERN_NOTICE "Cannot find driver for DiskOnChip %s at 0x%lX\n", name, physadr); -		kfree(mtd); -	} -	iounmap(docptr); -} - - -/**************************************************************************** - * - * Module stuff - * - ****************************************************************************/ - -static int __init init_doc(void) -{ -	int i; - -	if (doc_config_location) { -		printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location); -		DoC_Probe(doc_config_location); -	} else { -		for (i=0; (doc_locations[i] != 0xffffffff); i++) { -			DoC_Probe(doc_locations[i]); -		} -	} -	/* No banner message any more. Print a message if no DiskOnChip -	   found, so the user knows we at least tried. */ -	if (!docfound) -		printk(KERN_INFO "No recognised DiskOnChip devices found\n"); -	return -EAGAIN; -} - -module_init(init_doc); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); -MODULE_DESCRIPTION("Probe code for DiskOnChip 2000 and Millennium devices"); - diff --git a/drivers/mtd/devices/elm.c b/drivers/mtd/devices/elm.c new file mode 100644 index 00000000000..b4f61c7fc16 --- /dev/null +++ b/drivers/mtd/devices/elm.c @@ -0,0 +1,579 @@ +/* + * Error Location Module + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define DRIVER_NAME	"omap-elm" + +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/sched.h> +#include <linux/pm_runtime.h> +#include <linux/platform_data/elm.h> + +#define ELM_SYSCONFIG			0x010 +#define ELM_IRQSTATUS			0x018 +#define ELM_IRQENABLE			0x01c +#define ELM_LOCATION_CONFIG		0x020 +#define ELM_PAGE_CTRL			0x080 +#define ELM_SYNDROME_FRAGMENT_0		0x400 +#define ELM_SYNDROME_FRAGMENT_1		0x404 +#define ELM_SYNDROME_FRAGMENT_2		0x408 +#define ELM_SYNDROME_FRAGMENT_3		0x40c +#define ELM_SYNDROME_FRAGMENT_4		0x410 +#define ELM_SYNDROME_FRAGMENT_5		0x414 +#define ELM_SYNDROME_FRAGMENT_6		0x418 +#define ELM_LOCATION_STATUS		0x800 +#define ELM_ERROR_LOCATION_0		0x880 + +/* ELM Interrupt Status Register */ +#define INTR_STATUS_PAGE_VALID		BIT(8) + +/* ELM Interrupt Enable Register */ +#define INTR_EN_PAGE_MASK		BIT(8) + +/* ELM Location Configuration Register */ +#define ECC_BCH_LEVEL_MASK		0x3 + +/* ELM syndrome */ +#define ELM_SYNDROME_VALID		BIT(16) + +/* ELM_LOCATION_STATUS Register */ +#define ECC_CORRECTABLE_MASK		BIT(8) +#define ECC_NB_ERRORS_MASK		0x1f + +/* ELM_ERROR_LOCATION_0-15 Registers */ +#define ECC_ERROR_LOCATION_MASK		0x1fff + +#define ELM_ECC_SIZE			0x7ff + +#define SYNDROME_FRAGMENT_REG_SIZE	0x40 +#define ERROR_LOCATION_SIZE		0x100 + +struct elm_registers { +	u32 elm_irqenable; +	u32 elm_sysconfig; +	u32 elm_location_config; +	u32 elm_page_ctrl; +	u32 elm_syndrome_fragment_6[ERROR_VECTOR_MAX]; +	u32 elm_syndrome_fragment_5[ERROR_VECTOR_MAX]; +	u32 elm_syndrome_fragment_4[ERROR_VECTOR_MAX]; +	u32 elm_syndrome_fragment_3[ERROR_VECTOR_MAX]; +	u32 elm_syndrome_fragment_2[ERROR_VECTOR_MAX]; +	u32 elm_syndrome_fragment_1[ERROR_VECTOR_MAX]; +	u32 elm_syndrome_fragment_0[ERROR_VECTOR_MAX]; +}; + +struct elm_info { +	struct device *dev; +	void __iomem *elm_base; +	struct completion elm_completion; +	struct list_head list; +	enum bch_ecc bch_type; +	struct elm_registers elm_regs; +	int ecc_steps; +	int ecc_syndrome_size; +}; + +static LIST_HEAD(elm_devices); + +static void elm_write_reg(struct elm_info *info, int offset, u32 val) +{ +	writel(val, info->elm_base + offset); +} + +static u32 elm_read_reg(struct elm_info *info, int offset) +{ +	return readl(info->elm_base + offset); +} + +/** + * elm_config - Configure ELM module + * @dev:	ELM device + * @bch_type:	Type of BCH ecc + */ +int elm_config(struct device *dev, enum bch_ecc bch_type, +	int ecc_steps, int ecc_step_size, int ecc_syndrome_size) +{ +	u32 reg_val; +	struct elm_info *info = dev_get_drvdata(dev); + +	if (!info) { +		dev_err(dev, "Unable to configure elm - device not probed?\n"); +		return -ENODEV; +	} +	/* ELM cannot detect ECC errors for chunks > 1KB */ +	if (ecc_step_size > ((ELM_ECC_SIZE + 1) / 2)) { +		dev_err(dev, "unsupported config ecc-size=%d\n", ecc_step_size); +		return -EINVAL; +	} +	/* ELM support 8 error syndrome process */ +	if (ecc_steps > ERROR_VECTOR_MAX) { +		dev_err(dev, "unsupported config ecc-step=%d\n", ecc_steps); +		return -EINVAL; +	} + +	reg_val = (bch_type & ECC_BCH_LEVEL_MASK) | (ELM_ECC_SIZE << 16); +	elm_write_reg(info, ELM_LOCATION_CONFIG, reg_val); +	info->bch_type		= bch_type; +	info->ecc_steps		= ecc_steps; +	info->ecc_syndrome_size	= ecc_syndrome_size; + +	return 0; +} +EXPORT_SYMBOL(elm_config); + +/** + * elm_configure_page_mode - Enable/Disable page mode + * @info:	elm info + * @index:	index number of syndrome fragment vector + * @enable:	enable/disable flag for page mode + * + * Enable page mode for syndrome fragment index + */ +static void elm_configure_page_mode(struct elm_info *info, int index, +		bool enable) +{ +	u32 reg_val; + +	reg_val = elm_read_reg(info, ELM_PAGE_CTRL); +	if (enable) +		reg_val |= BIT(index);	/* enable page mode */ +	else +		reg_val &= ~BIT(index);	/* disable page mode */ + +	elm_write_reg(info, ELM_PAGE_CTRL, reg_val); +} + +/** + * elm_load_syndrome - Load ELM syndrome reg + * @info:	elm info + * @err_vec:	elm error vectors + * @ecc:	buffer with calculated ecc + * + * Load syndrome fragment registers with calculated ecc in reverse order. + */ +static void elm_load_syndrome(struct elm_info *info, +		struct elm_errorvec *err_vec, u8 *ecc) +{ +	int i, offset; +	u32 val; + +	for (i = 0; i < info->ecc_steps; i++) { + +		/* Check error reported */ +		if (err_vec[i].error_reported) { +			elm_configure_page_mode(info, i, true); +			offset = ELM_SYNDROME_FRAGMENT_0 + +				SYNDROME_FRAGMENT_REG_SIZE * i; +			switch (info->bch_type) { +			case BCH8_ECC: +				/* syndrome fragment 0 = ecc[9-12B] */ +				val = cpu_to_be32(*(u32 *) &ecc[9]); +				elm_write_reg(info, offset, val); + +				/* syndrome fragment 1 = ecc[5-8B] */ +				offset += 4; +				val = cpu_to_be32(*(u32 *) &ecc[5]); +				elm_write_reg(info, offset, val); + +				/* syndrome fragment 2 = ecc[1-4B] */ +				offset += 4; +				val = cpu_to_be32(*(u32 *) &ecc[1]); +				elm_write_reg(info, offset, val); + +				/* syndrome fragment 3 = ecc[0B] */ +				offset += 4; +				val = ecc[0]; +				elm_write_reg(info, offset, val); +				break; +			case BCH4_ECC: +				/* syndrome fragment 0 = ecc[20-52b] bits */ +				val = (cpu_to_be32(*(u32 *) &ecc[3]) >> 4) | +					((ecc[2] & 0xf) << 28); +				elm_write_reg(info, offset, val); + +				/* syndrome fragment 1 = ecc[0-20b] bits */ +				offset += 4; +				val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12; +				elm_write_reg(info, offset, val); +				break; +			case BCH16_ECC: +				val = cpu_to_be32(*(u32 *) &ecc[22]); +				elm_write_reg(info, offset, val); +				offset += 4; +				val = cpu_to_be32(*(u32 *) &ecc[18]); +				elm_write_reg(info, offset, val); +				offset += 4; +				val = cpu_to_be32(*(u32 *) &ecc[14]); +				elm_write_reg(info, offset, val); +				offset += 4; +				val = cpu_to_be32(*(u32 *) &ecc[10]); +				elm_write_reg(info, offset, val); +				offset += 4; +				val = cpu_to_be32(*(u32 *) &ecc[6]); +				elm_write_reg(info, offset, val); +				offset += 4; +				val = cpu_to_be32(*(u32 *) &ecc[2]); +				elm_write_reg(info, offset, val); +				offset += 4; +				val = cpu_to_be32(*(u32 *) &ecc[0]) >> 16; +				elm_write_reg(info, offset, val); +				break; +			default: +				pr_err("invalid config bch_type\n"); +			} +		} + +		/* Update ecc pointer with ecc byte size */ +		ecc += info->ecc_syndrome_size; +	} +} + +/** + * elm_start_processing - start elm syndrome processing + * @info:	elm info + * @err_vec:	elm error vectors + * + * Set syndrome valid bit for syndrome fragment registers for which + * elm syndrome fragment registers are loaded. This enables elm module + * to start processing syndrome vectors. + */ +static void elm_start_processing(struct elm_info *info, +		struct elm_errorvec *err_vec) +{ +	int i, offset; +	u32 reg_val; + +	/* +	 * Set syndrome vector valid, so that ELM module +	 * will process it for vectors error is reported +	 */ +	for (i = 0; i < info->ecc_steps; i++) { +		if (err_vec[i].error_reported) { +			offset = ELM_SYNDROME_FRAGMENT_6 + +				SYNDROME_FRAGMENT_REG_SIZE * i; +			reg_val = elm_read_reg(info, offset); +			reg_val |= ELM_SYNDROME_VALID; +			elm_write_reg(info, offset, reg_val); +		} +	} +} + +/** + * elm_error_correction - locate correctable error position + * @info:	elm info + * @err_vec:	elm error vectors + * + * On completion of processing by elm module, error location status + * register updated with correctable/uncorrectable error information. + * In case of correctable errors, number of errors located from + * elm location status register & read the positions from + * elm error location register. + */ +static void elm_error_correction(struct elm_info *info, +		struct elm_errorvec *err_vec) +{ +	int i, j, errors = 0; +	int offset; +	u32 reg_val; + +	for (i = 0; i < info->ecc_steps; i++) { + +		/* Check error reported */ +		if (err_vec[i].error_reported) { +			offset = ELM_LOCATION_STATUS + ERROR_LOCATION_SIZE * i; +			reg_val = elm_read_reg(info, offset); + +			/* Check correctable error or not */ +			if (reg_val & ECC_CORRECTABLE_MASK) { +				offset = ELM_ERROR_LOCATION_0 + +					ERROR_LOCATION_SIZE * i; + +				/* Read count of correctable errors */ +				err_vec[i].error_count = reg_val & +					ECC_NB_ERRORS_MASK; + +				/* Update the error locations in error vector */ +				for (j = 0; j < err_vec[i].error_count; j++) { + +					reg_val = elm_read_reg(info, offset); +					err_vec[i].error_loc[j] = reg_val & +						ECC_ERROR_LOCATION_MASK; + +					/* Update error location register */ +					offset += 4; +				} + +				errors += err_vec[i].error_count; +			} else { +				err_vec[i].error_uncorrectable = true; +			} + +			/* Clearing interrupts for processed error vectors */ +			elm_write_reg(info, ELM_IRQSTATUS, BIT(i)); + +			/* Disable page mode */ +			elm_configure_page_mode(info, i, false); +		} +	} +} + +/** + * elm_decode_bch_error_page - Locate error position + * @dev:	device pointer + * @ecc_calc:	calculated ECC bytes from GPMC + * @err_vec:	elm error vectors + * + * Called with one or more error reported vectors & vectors with + * error reported is updated in err_vec[].error_reported + */ +void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc, +		struct elm_errorvec *err_vec) +{ +	struct elm_info *info = dev_get_drvdata(dev); +	u32 reg_val; + +	/* Enable page mode interrupt */ +	reg_val = elm_read_reg(info, ELM_IRQSTATUS); +	elm_write_reg(info, ELM_IRQSTATUS, reg_val & INTR_STATUS_PAGE_VALID); +	elm_write_reg(info, ELM_IRQENABLE, INTR_EN_PAGE_MASK); + +	/* Load valid ecc byte to syndrome fragment register */ +	elm_load_syndrome(info, err_vec, ecc_calc); + +	/* Enable syndrome processing for which syndrome fragment is updated */ +	elm_start_processing(info, err_vec); + +	/* Wait for ELM module to finish locating error correction */ +	wait_for_completion(&info->elm_completion); + +	/* Disable page mode interrupt */ +	reg_val = elm_read_reg(info, ELM_IRQENABLE); +	elm_write_reg(info, ELM_IRQENABLE, reg_val & ~INTR_EN_PAGE_MASK); +	elm_error_correction(info, err_vec); +} +EXPORT_SYMBOL(elm_decode_bch_error_page); + +static irqreturn_t elm_isr(int this_irq, void *dev_id) +{ +	u32 reg_val; +	struct elm_info *info = dev_id; + +	reg_val = elm_read_reg(info, ELM_IRQSTATUS); + +	/* All error vectors processed */ +	if (reg_val & INTR_STATUS_PAGE_VALID) { +		elm_write_reg(info, ELM_IRQSTATUS, +				reg_val & INTR_STATUS_PAGE_VALID); +		complete(&info->elm_completion); +		return IRQ_HANDLED; +	} + +	return IRQ_NONE; +} + +static int elm_probe(struct platform_device *pdev) +{ +	int ret = 0; +	struct resource *res, *irq; +	struct elm_info *info; + +	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); +	if (!info) +		return -ENOMEM; + +	info->dev = &pdev->dev; + +	irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); +	if (!irq) { +		dev_err(&pdev->dev, "no irq resource defined\n"); +		return -ENODEV; +	} + +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	info->elm_base = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(info->elm_base)) +		return PTR_ERR(info->elm_base); + +	ret = devm_request_irq(&pdev->dev, irq->start, elm_isr, 0, +			pdev->name, info); +	if (ret) { +		dev_err(&pdev->dev, "failure requesting irq %i\n", irq->start); +		return ret; +	} + +	pm_runtime_enable(&pdev->dev); +	if (pm_runtime_get_sync(&pdev->dev) < 0) { +		ret = -EINVAL; +		pm_runtime_disable(&pdev->dev); +		dev_err(&pdev->dev, "can't enable clock\n"); +		return ret; +	} + +	init_completion(&info->elm_completion); +	INIT_LIST_HEAD(&info->list); +	list_add(&info->list, &elm_devices); +	platform_set_drvdata(pdev, info); +	return ret; +} + +static int elm_remove(struct platform_device *pdev) +{ +	pm_runtime_put_sync(&pdev->dev); +	pm_runtime_disable(&pdev->dev); +	return 0; +} + +#ifdef CONFIG_PM_SLEEP +/** + * elm_context_save + * saves ELM configurations to preserve them across Hardware powered-down + */ +static int elm_context_save(struct elm_info *info) +{ +	struct elm_registers *regs = &info->elm_regs; +	enum bch_ecc bch_type = info->bch_type; +	u32 offset = 0, i; + +	regs->elm_irqenable       = elm_read_reg(info, ELM_IRQENABLE); +	regs->elm_sysconfig       = elm_read_reg(info, ELM_SYSCONFIG); +	regs->elm_location_config = elm_read_reg(info, ELM_LOCATION_CONFIG); +	regs->elm_page_ctrl       = elm_read_reg(info, ELM_PAGE_CTRL); +	for (i = 0; i < ERROR_VECTOR_MAX; i++) { +		offset = i * SYNDROME_FRAGMENT_REG_SIZE; +		switch (bch_type) { +		case BCH16_ECC: +			regs->elm_syndrome_fragment_6[i] = elm_read_reg(info, +					ELM_SYNDROME_FRAGMENT_6 + offset); +			regs->elm_syndrome_fragment_5[i] = elm_read_reg(info, +					ELM_SYNDROME_FRAGMENT_5 + offset); +			regs->elm_syndrome_fragment_4[i] = elm_read_reg(info, +					ELM_SYNDROME_FRAGMENT_4 + offset); +		case BCH8_ECC: +			regs->elm_syndrome_fragment_3[i] = elm_read_reg(info, +					ELM_SYNDROME_FRAGMENT_3 + offset); +			regs->elm_syndrome_fragment_2[i] = elm_read_reg(info, +					ELM_SYNDROME_FRAGMENT_2 + offset); +		case BCH4_ECC: +			regs->elm_syndrome_fragment_1[i] = elm_read_reg(info, +					ELM_SYNDROME_FRAGMENT_1 + offset); +			regs->elm_syndrome_fragment_0[i] = elm_read_reg(info, +					ELM_SYNDROME_FRAGMENT_0 + offset); +			break; +		default: +			return -EINVAL; +		} +		/* ELM SYNDROME_VALID bit in SYNDROME_FRAGMENT_6[] needs +		 * to be saved for all BCH schemes*/ +		regs->elm_syndrome_fragment_6[i] = elm_read_reg(info, +					ELM_SYNDROME_FRAGMENT_6 + offset); +	} +	return 0; +} + +/** + * elm_context_restore + * writes configurations saved duing power-down back into ELM registers + */ +static int elm_context_restore(struct elm_info *info) +{ +	struct elm_registers *regs = &info->elm_regs; +	enum bch_ecc bch_type = info->bch_type; +	u32 offset = 0, i; + +	elm_write_reg(info, ELM_IRQENABLE,	 regs->elm_irqenable); +	elm_write_reg(info, ELM_SYSCONFIG,	 regs->elm_sysconfig); +	elm_write_reg(info, ELM_LOCATION_CONFIG, regs->elm_location_config); +	elm_write_reg(info, ELM_PAGE_CTRL,	 regs->elm_page_ctrl); +	for (i = 0; i < ERROR_VECTOR_MAX; i++) { +		offset = i * SYNDROME_FRAGMENT_REG_SIZE; +		switch (bch_type) { +		case BCH16_ECC: +			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset, +					regs->elm_syndrome_fragment_6[i]); +			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_5 + offset, +					regs->elm_syndrome_fragment_5[i]); +			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_4 + offset, +					regs->elm_syndrome_fragment_4[i]); +		case BCH8_ECC: +			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_3 + offset, +					regs->elm_syndrome_fragment_3[i]); +			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_2 + offset, +					regs->elm_syndrome_fragment_2[i]); +		case BCH4_ECC: +			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_1 + offset, +					regs->elm_syndrome_fragment_1[i]); +			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_0 + offset, +					regs->elm_syndrome_fragment_0[i]); +			break; +		default: +			return -EINVAL; +		} +		/* ELM_SYNDROME_VALID bit to be set in last to trigger FSM */ +		elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset, +					regs->elm_syndrome_fragment_6[i] & +							 ELM_SYNDROME_VALID); +	} +	return 0; +} + +static int elm_suspend(struct device *dev) +{ +	struct elm_info *info = dev_get_drvdata(dev); +	elm_context_save(info); +	pm_runtime_put_sync(dev); +	return 0; +} + +static int elm_resume(struct device *dev) +{ +	struct elm_info *info = dev_get_drvdata(dev); +	pm_runtime_get_sync(dev); +	elm_context_restore(info); +	return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(elm_pm_ops, elm_suspend, elm_resume); + +#ifdef CONFIG_OF +static const struct of_device_id elm_of_match[] = { +	{ .compatible = "ti,am3352-elm" }, +	{}, +}; +MODULE_DEVICE_TABLE(of, elm_of_match); +#endif + +static struct platform_driver elm_driver = { +	.driver	= { +		.name	= DRIVER_NAME, +		.owner	= THIS_MODULE, +		.of_match_table = of_match_ptr(elm_of_match), +		.pm	= &elm_pm_ops, +	}, +	.probe	= elm_probe, +	.remove	= elm_remove, +}; + +module_platform_driver(elm_driver); + +MODULE_DESCRIPTION("ELM driver for BCH error correction"); +MODULE_AUTHOR("Texas Instruments"); +MODULE_ALIAS("platform: elm"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c index caf604167f0..82bd00af5cc 100644 --- a/drivers/mtd/devices/lart.c +++ b/drivers/mtd/devices/lart.c @@ -34,9 +34,6 @@  /* debugging */  //#define LART_DEBUG -/* partition support */ -#define HAVE_PARTITIONS -  #include <linux/kernel.h>  #include <linux/module.h>  #include <linux/types.h> @@ -44,9 +41,7 @@  #include <linux/errno.h>  #include <linux/string.h>  #include <linux/mtd/mtd.h> -#ifdef HAVE_PARTITIONS  #include <linux/mtd/partitions.h> -#endif  #ifndef CONFIG_SA1100_LART  #error This is for LART architecture only @@ -353,7 +348,7 @@ static inline int erase_block (__u32 offset)     /* put the flash back into command mode */     write32 (DATA_TO_FLASH (READ_ARRAY),offset); -   /* was the erase successfull? */ +   /* was the erase successful? */     if ((status & STATUS_ERASE_ERR))  	 {  		printk (KERN_WARNING "%s: erase error at address 0x%.8x.\n",module_name,offset); @@ -372,9 +367,6 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr)     printk (KERN_DEBUG "%s(addr = 0x%.8x, len = %d)\n", __func__, instr->addr, instr->len);  #endif -   /* sanity checks */ -   if (instr->addr + instr->len > mtd->size) return (-EINVAL); -     /*  	* check that both start and end of the requested erase are  	* aligned with the erasesize at the appropriate addresses. @@ -445,10 +437,6 @@ static int flash_read (struct mtd_info *mtd,loff_t from,size_t len,size_t *retle     printk (KERN_DEBUG "%s(from = 0x%.8x, len = %d)\n", __func__, (__u32)from, len);  #endif -   /* sanity checks */ -   if (!len) return (0); -   if (from + len > mtd->size) return (-EINVAL); -     /* we always read len bytes */     *retlen = len; @@ -508,7 +496,7 @@ static inline int write_dword (__u32 offset,__u32 x)     /* put the flash back into command mode */     write32 (DATA_TO_FLASH (READ_ARRAY),offset); -   /* was the write successfull? */ +   /* was the write successful? */     if ((status & STATUS_PGM_ERR) || read32 (offset) != x)  	 {  		printk (KERN_WARNING "%s: write error at address 0x%.8x.\n",module_name,offset); @@ -527,11 +515,8 @@ static int flash_write (struct mtd_info *mtd,loff_t to,size_t len,size_t *retlen     printk (KERN_DEBUG "%s(to = 0x%.8x, len = %d)\n", __func__, (__u32)to, len);  #endif -   *retlen = 0; -     /* sanity checks */     if (!len) return (0); -   if (to + len > mtd->size) return (-EINVAL);     /* first, we write a 0xFF.... padded byte until we reach a dword boundary */     if (to & (BUSWIDTH - 1)) @@ -598,7 +583,6 @@ static struct mtd_erase_region_info erase_regions[] = {  	}  }; -#ifdef HAVE_PARTITIONS  static struct mtd_partition lart_partitions[] = {  	/* blob */  	{ @@ -619,7 +603,7 @@ static struct mtd_partition lart_partitions[] = {  		.size	= INITRD_LEN,		/* MTDPART_SIZ_FULL */  	}  }; -#endif +#define NUM_PARTITIONS ARRAY_SIZE(lart_partitions)  static int __init lart_flash_init (void)  { @@ -636,14 +620,15 @@ static int __init lart_flash_init (void)     mtd.name = module_name;     mtd.type = MTD_NORFLASH;     mtd.writesize = 1; +   mtd.writebufsize = 4;     mtd.flags = MTD_CAP_NORFLASH;     mtd.size = FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM + FLASH_BLOCKSIZE_MAIN * FLASH_NUMBLOCKS_16m_MAIN;     mtd.erasesize = FLASH_BLOCKSIZE_MAIN;     mtd.numeraseregions = ARRAY_SIZE(erase_regions);     mtd.eraseregions = erase_regions; -   mtd.erase = flash_erase; -   mtd.read = flash_read; -   mtd.write = flash_write; +   mtd._erase = flash_erase; +   mtd._read = flash_read; +   mtd._write = flash_write;     mtd.owner = THIS_MODULE;  #ifdef LART_DEBUG @@ -668,7 +653,6 @@ static int __init lart_flash_init (void)  			   result,mtd.eraseregions[result].erasesize,mtd.eraseregions[result].erasesize / 1024,  			   result,mtd.eraseregions[result].numblocks); -#ifdef HAVE_PARTITIONS     printk ("\npartitions = %d\n", ARRAY_SIZE(lart_partitions));     for (result = 0; result < ARRAY_SIZE(lart_partitions); result++) @@ -681,24 +665,16 @@ static int __init lart_flash_init (void)  			 result,lart_partitions[result].offset,  			 result,lart_partitions[result].size,lart_partitions[result].size / 1024);  #endif -#endif -#ifndef HAVE_PARTITIONS -   result = add_mtd_device (&mtd); -#else -   result = add_mtd_partitions (&mtd,lart_partitions, ARRAY_SIZE(lart_partitions)); -#endif +   result = mtd_device_register(&mtd, lart_partitions, +                                ARRAY_SIZE(lart_partitions));     return (result);  }  static void __exit lart_flash_exit (void)  { -#ifndef HAVE_PARTITIONS -   del_mtd_device (&mtd); -#else -   del_mtd_partitions (&mtd); -#endif +   mtd_device_unregister(&mtd);  }  module_init (lart_flash_init); diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index bf5a002209b..ed7e0a1bed3 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -15,974 +15,244 @@   *   */ -#include <linux/init.h>  #include <linux/err.h>  #include <linux/errno.h>  #include <linux/module.h>  #include <linux/device.h> -#include <linux/interrupt.h> -#include <linux/mutex.h> -#include <linux/math64.h> -#include <linux/slab.h> -#include <linux/sched.h> -#include <linux/mod_devicetable.h>  #include <linux/mtd/mtd.h>  #include <linux/mtd/partitions.h>  #include <linux/spi/spi.h>  #include <linux/spi/flash.h> +#include <linux/mtd/spi-nor.h> -/* Flash opcodes. */ -#define	OPCODE_WREN		0x06	/* Write enable */ -#define	OPCODE_RDSR		0x05	/* Read status register */ -#define	OPCODE_WRSR		0x01	/* Write status register 1 byte */ -#define	OPCODE_NORM_READ	0x03	/* Read data bytes (low frequency) */ -#define	OPCODE_FAST_READ	0x0b	/* Read data bytes (high frequency) */ -#define	OPCODE_PP		0x02	/* Page program (up to 256 bytes) */ -#define	OPCODE_BE_4K		0x20	/* Erase 4KiB block */ -#define	OPCODE_BE_32K		0x52	/* Erase 32KiB block */ -#define	OPCODE_CHIP_ERASE	0xc7	/* Erase whole flash chip */ -#define	OPCODE_SE		0xd8	/* Sector erase (usually 64KiB) */ -#define	OPCODE_RDID		0x9f	/* Read JEDEC ID */ - -/* Used for SST flashes only. */ -#define	OPCODE_BP		0x02	/* Byte program */ -#define	OPCODE_WRDI		0x04	/* Write disable */ -#define	OPCODE_AAI_WP		0xad	/* Auto address increment word program */ - -/* Status Register bits. */ -#define	SR_WIP			1	/* Write in progress */ -#define	SR_WEL			2	/* Write enable latch */ -/* meaning of other SR_* bits may differ between vendors */ -#define	SR_BP0			4	/* Block protect 0 */ -#define	SR_BP1			8	/* Block protect 1 */ -#define	SR_BP2			0x10	/* Block protect 2 */ -#define	SR_SRWD			0x80	/* SR write protect */ - -/* Define max times to check status register before we give up. */ -#define	MAX_READY_WAIT_JIFFIES	(40 * HZ)	/* M25P16 specs 40s max chip erase */ -#define	MAX_CMD_SIZE		4 - -#ifdef CONFIG_M25PXX_USE_FAST_READ -#define OPCODE_READ 	OPCODE_FAST_READ -#define FAST_READ_DUMMY_BYTE 1 -#else -#define OPCODE_READ 	OPCODE_NORM_READ -#define FAST_READ_DUMMY_BYTE 0 -#endif - -/****************************************************************************/ - +#define	MAX_CMD_SIZE		6  struct m25p {  	struct spi_device	*spi; -	struct mutex		lock; +	struct spi_nor		spi_nor;  	struct mtd_info		mtd; -	unsigned		partitioned:1; -	u16			page_size; -	u16			addr_width; -	u8			erase_opcode; -	u8			*command; +	u8			command[MAX_CMD_SIZE];  }; -static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd) -{ -	return container_of(mtd, struct m25p, mtd); -} - -/****************************************************************************/ - -/* - * Internal helper functions - */ - -/* - * Read the status register, returning its value in the location - * Return the status register value. - * Returns negative if error occurred. - */ -static int read_sr(struct m25p *flash) -{ -	ssize_t retval; -	u8 code = OPCODE_RDSR; -	u8 val; - -	retval = spi_write_then_read(flash->spi, &code, 1, &val, 1); - -	if (retval < 0) { -		dev_err(&flash->spi->dev, "error %d reading SR\n", -				(int) retval); -		return retval; -	} - -	return val; -} - -/* - * Write status register 1 byte - * Returns negative if error occurred. - */ -static int write_sr(struct m25p *flash, u8 val) -{ -	flash->command[0] = OPCODE_WRSR; -	flash->command[1] = val; - -	return spi_write(flash->spi, flash->command, 2); -} - -/* - * Set write enable latch with Write Enable command. - * Returns negative if error occurred. - */ -static inline int write_enable(struct m25p *flash) -{ -	u8	code = OPCODE_WREN; - -	return spi_write_then_read(flash->spi, &code, 1, NULL, 0); -} - -/* - * Send write disble instruction to the chip. - */ -static inline int write_disable(struct m25p *flash) -{ -	u8	code = OPCODE_WRDI; - -	return spi_write_then_read(flash->spi, &code, 1, NULL, 0); -} - -/* - * Service routine to read status register until ready, or timeout occurs. - * Returns non-zero if error. - */ -static int wait_till_ready(struct m25p *flash) -{ -	unsigned long deadline; -	int sr; - -	deadline = jiffies + MAX_READY_WAIT_JIFFIES; - -	do { -		if ((sr = read_sr(flash)) < 0) -			break; -		else if (!(sr & SR_WIP)) -			return 0; - -		cond_resched(); - -	} while (!time_after_eq(jiffies, deadline)); - -	return 1; -} - -/* - * Erase the whole flash memory - * - * Returns 0 if successful, non-zero otherwise. - */ -static int erase_chip(struct m25p *flash) +static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)  { -	DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %lldKiB\n", -	      dev_name(&flash->spi->dev), __func__, -	      (long long)(flash->mtd.size >> 10)); - -	/* Wait until finished previous write command. */ -	if (wait_till_ready(flash)) -		return 1; - -	/* Send write enable, then erase commands. */ -	write_enable(flash); +	struct m25p *flash = nor->priv; +	struct spi_device *spi = flash->spi; +	int ret; -	/* Set up command buffer. */ -	flash->command[0] = OPCODE_CHIP_ERASE; - -	spi_write(flash->spi, flash->command, 1); +	ret = spi_write_then_read(spi, &code, 1, val, len); +	if (ret < 0) +		dev_err(&spi->dev, "error %d reading %x\n", ret, code); -	return 0; +	return ret;  } -static void m25p_addr2cmd(struct m25p *flash, unsigned int addr, u8 *cmd) +static void m25p_addr2cmd(struct spi_nor *nor, unsigned int addr, u8 *cmd)  {  	/* opcode is in cmd[0] */ -	cmd[1] = addr >> (flash->addr_width * 8 -  8); -	cmd[2] = addr >> (flash->addr_width * 8 - 16); -	cmd[3] = addr >> (flash->addr_width * 8 - 24); +	cmd[1] = addr >> (nor->addr_width * 8 -  8); +	cmd[2] = addr >> (nor->addr_width * 8 - 16); +	cmd[3] = addr >> (nor->addr_width * 8 - 24); +	cmd[4] = addr >> (nor->addr_width * 8 - 32);  } -static int m25p_cmdsz(struct m25p *flash) +static int m25p_cmdsz(struct spi_nor *nor)  { -	return 1 + flash->addr_width; -} - -/* - * Erase one sector of flash memory at offset ``offset'' which is any - * address within the sector which should be erased. - * - * Returns 0 if successful, non-zero otherwise. - */ -static int erase_sector(struct m25p *flash, u32 offset) -{ -	DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB at 0x%08x\n", -			dev_name(&flash->spi->dev), __func__, -			flash->mtd.erasesize / 1024, offset); - -	/* Wait until finished previous write command. */ -	if (wait_till_ready(flash)) -		return 1; - -	/* Send write enable, then erase commands. */ -	write_enable(flash); - -	/* Set up command buffer. */ -	flash->command[0] = flash->erase_opcode; -	m25p_addr2cmd(flash, offset, flash->command); - -	spi_write(flash->spi, flash->command, m25p_cmdsz(flash)); - -	return 0; +	return 1 + nor->addr_width;  } -/****************************************************************************/ - -/* - * MTD implementation - */ - -/* - * Erase an address range on the flash chip.  The address range may extend - * one or more erase sectors.  Return an error is there is a problem erasing. - */ -static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) +static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len, +			int wr_en)  { -	struct m25p *flash = mtd_to_m25p(mtd); -	u32 addr,len; -	uint32_t rem; - -	DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%llx, len %lld\n", -	      dev_name(&flash->spi->dev), __func__, "at", -	      (long long)instr->addr, (long long)instr->len); - -	/* sanity checks */ -	if (instr->addr + instr->len > flash->mtd.size) -		return -EINVAL; -	div_u64_rem(instr->len, mtd->erasesize, &rem); -	if (rem) -		return -EINVAL; - -	addr = instr->addr; -	len = instr->len; - -	mutex_lock(&flash->lock); - -	/* whole-chip erase? */ -	if (len == flash->mtd.size) { -		if (erase_chip(flash)) { -			instr->state = MTD_ERASE_FAILED; -			mutex_unlock(&flash->lock); -			return -EIO; -		} - -	/* REVISIT in some cases we could speed up erasing large regions -	 * by using OPCODE_SE instead of OPCODE_BE_4K.  We may have set up -	 * to use "small sector erase", but that's not always optimal. -	 */ +	struct m25p *flash = nor->priv; +	struct spi_device *spi = flash->spi; -	/* "sector"-at-a-time erase */ -	} else { -		while (len) { -			if (erase_sector(flash, addr)) { -				instr->state = MTD_ERASE_FAILED; -				mutex_unlock(&flash->lock); -				return -EIO; -			} - -			addr += mtd->erasesize; -			len -= mtd->erasesize; -		} -	} - -	mutex_unlock(&flash->lock); +	flash->command[0] = opcode; +	if (buf) +		memcpy(&flash->command[1], buf, len); -	instr->state = MTD_ERASE_DONE; -	mtd_erase_callback(instr); - -	return 0; +	return spi_write(spi, flash->command, len + 1);  } -/* - * Read an address range from the flash chip.  The address range - * may be any size provided it is within the physical boundaries. - */ -static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, -	size_t *retlen, u_char *buf) +static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len, +			size_t *retlen, const u_char *buf)  { -	struct m25p *flash = mtd_to_m25p(mtd); -	struct spi_transfer t[2]; +	struct m25p *flash = nor->priv; +	struct spi_device *spi = flash->spi; +	struct spi_transfer t[2] = {};  	struct spi_message m; +	int cmd_sz = m25p_cmdsz(nor); -	DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n", -			dev_name(&flash->spi->dev), __func__, "from", -			(u32)from, len); - -	/* sanity checks */ -	if (!len) -		return 0; +	spi_message_init(&m); -	if (from + len > flash->mtd.size) -		return -EINVAL; +	if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) +		cmd_sz = 1; -	spi_message_init(&m); -	memset(t, 0, (sizeof t)); +	flash->command[0] = nor->program_opcode; +	m25p_addr2cmd(nor, to, flash->command); -	/* NOTE: -	 * OPCODE_FAST_READ (if available) is faster. -	 * Should add 1 byte DUMMY_BYTE. -	 */  	t[0].tx_buf = flash->command; -	t[0].len = m25p_cmdsz(flash) + FAST_READ_DUMMY_BYTE; +	t[0].len = cmd_sz;  	spi_message_add_tail(&t[0], &m); -	t[1].rx_buf = buf; +	t[1].tx_buf = buf;  	t[1].len = len;  	spi_message_add_tail(&t[1], &m); -	/* Byte count starts at zero. */ -	*retlen = 0; +	spi_sync(spi, &m); -	mutex_lock(&flash->lock); +	*retlen += m.actual_length - cmd_sz; +} -	/* Wait till previous write/erase is done. */ -	if (wait_till_ready(flash)) { -		/* REVISIT status return?? */ -		mutex_unlock(&flash->lock); -		return 1; +static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor) +{ +	switch (nor->flash_read) { +	case SPI_NOR_DUAL: +		return 2; +	case SPI_NOR_QUAD: +		return 4; +	default: +		return 0;  	} - -	/* FIXME switch to OPCODE_FAST_READ.  It's required for higher -	 * clocks; and at this writing, every chip this driver handles -	 * supports that opcode. -	 */ - -	/* Set up the write data buffer. */ -	flash->command[0] = OPCODE_READ; -	m25p_addr2cmd(flash, from, flash->command); - -	spi_sync(flash->spi, &m); - -	*retlen = m.actual_length - m25p_cmdsz(flash) - FAST_READ_DUMMY_BYTE; - -	mutex_unlock(&flash->lock); - -	return 0;  }  /* - * Write an address range to the flash chip.  Data must be written in - * FLASH_PAGESIZE chunks.  The address range may be any size provided - * it is within the physical boundaries. + * Read an address range from the nor chip.  The address range + * may be any size provided it is within the physical boundaries.   */ -static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, -	size_t *retlen, const u_char *buf) +static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len, +			size_t *retlen, u_char *buf)  { -	struct m25p *flash = mtd_to_m25p(mtd); -	u32 page_offset, page_size; +	struct m25p *flash = nor->priv; +	struct spi_device *spi = flash->spi;  	struct spi_transfer t[2];  	struct spi_message m; +	int dummy = nor->read_dummy; +	int ret; -	DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n", -			dev_name(&flash->spi->dev), __func__, "to", -			(u32)to, len); - -	*retlen = 0; - -	/* sanity checks */ -	if (!len) -		return(0); - -	if (to + len > flash->mtd.size) -		return -EINVAL; +	/* Wait till previous write/erase is done. */ +	ret = nor->wait_till_ready(nor); +	if (ret) +		return ret;  	spi_message_init(&m);  	memset(t, 0, (sizeof t)); +	flash->command[0] = nor->read_opcode; +	m25p_addr2cmd(nor, from, flash->command); +  	t[0].tx_buf = flash->command; -	t[0].len = m25p_cmdsz(flash); +	t[0].len = m25p_cmdsz(nor) + dummy;  	spi_message_add_tail(&t[0], &m); -	t[1].tx_buf = buf; +	t[1].rx_buf = buf; +	t[1].rx_nbits = m25p80_rx_nbits(nor); +	t[1].len = len;  	spi_message_add_tail(&t[1], &m); -	mutex_lock(&flash->lock); - -	/* Wait until finished previous write command. */ -	if (wait_till_ready(flash)) { -		mutex_unlock(&flash->lock); -		return 1; -	} - -	write_enable(flash); - -	/* Set up the opcode in the write buffer. */ -	flash->command[0] = OPCODE_PP; -	m25p_addr2cmd(flash, to, flash->command); - -	page_offset = to & (flash->page_size - 1); - -	/* do all the bytes fit onto one page? */ -	if (page_offset + len <= flash->page_size) { -		t[1].len = len; - -		spi_sync(flash->spi, &m); - -		*retlen = m.actual_length - m25p_cmdsz(flash); -	} else { -		u32 i; - -		/* the size of data remaining on the first page */ -		page_size = flash->page_size - page_offset; - -		t[1].len = page_size; -		spi_sync(flash->spi, &m); - -		*retlen = m.actual_length - m25p_cmdsz(flash); - -		/* write everything in flash->page_size chunks */ -		for (i = page_size; i < len; i += page_size) { -			page_size = len - i; -			if (page_size > flash->page_size) -				page_size = flash->page_size; - -			/* write the next page to flash */ -			m25p_addr2cmd(flash, to + i, flash->command); - -			t[1].tx_buf = buf + i; -			t[1].len = page_size; - -			wait_till_ready(flash); - -			write_enable(flash); - -			spi_sync(flash->spi, &m); - -			*retlen += m.actual_length - m25p_cmdsz(flash); -		} -	} - -	mutex_unlock(&flash->lock); +	spi_sync(spi, &m); +	*retlen = m.actual_length - m25p_cmdsz(nor) - dummy;  	return 0;  } -static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, -		size_t *retlen, const u_char *buf) +static int m25p80_erase(struct spi_nor *nor, loff_t offset)  { -	struct m25p *flash = mtd_to_m25p(mtd); -	struct spi_transfer t[2]; -	struct spi_message m; -	size_t actual; -	int cmd_sz, ret; - -	*retlen = 0; - -	/* sanity checks */ -	if (!len) -		return 0; +	struct m25p *flash = nor->priv; +	int ret; -	if (to + len > flash->mtd.size) -		return -EINVAL; - -	spi_message_init(&m); -	memset(t, 0, (sizeof t)); - -	t[0].tx_buf = flash->command; -	t[0].len = m25p_cmdsz(flash); -	spi_message_add_tail(&t[0], &m); - -	t[1].tx_buf = buf; -	spi_message_add_tail(&t[1], &m); - -	mutex_lock(&flash->lock); +	dev_dbg(nor->dev, "%dKiB at 0x%08x\n", +		flash->mtd.erasesize / 1024, (u32)offset);  	/* Wait until finished previous write command. */ -	ret = wait_till_ready(flash); -	if (ret) -		goto time_out; - -	write_enable(flash); - -	actual = to % 2; -	/* Start write from odd address. */ -	if (actual) { -		flash->command[0] = OPCODE_BP; -		m25p_addr2cmd(flash, to, flash->command); - -		/* write one byte. */ -		t[1].len = 1; -		spi_sync(flash->spi, &m); -		ret = wait_till_ready(flash); -		if (ret) -			goto time_out; -		*retlen += m.actual_length - m25p_cmdsz(flash); -	} -	to += actual; - -	flash->command[0] = OPCODE_AAI_WP; -	m25p_addr2cmd(flash, to, flash->command); - -	/* Write out most of the data here. */ -	cmd_sz = m25p_cmdsz(flash); -	for (; actual < len - 1; actual += 2) { -		t[0].len = cmd_sz; -		/* write two bytes. */ -		t[1].len = 2; -		t[1].tx_buf = buf + actual; - -		spi_sync(flash->spi, &m); -		ret = wait_till_ready(flash); -		if (ret) -			goto time_out; -		*retlen += m.actual_length - cmd_sz; -		cmd_sz = 1; -		to += 2; -	} -	write_disable(flash); -	ret = wait_till_ready(flash); +	ret = nor->wait_till_ready(nor);  	if (ret) -		goto time_out; - -	/* Write out trailing byte if it exists. */ -	if (actual != len) { -		write_enable(flash); -		flash->command[0] = OPCODE_BP; -		m25p_addr2cmd(flash, to, flash->command); -		t[0].len = m25p_cmdsz(flash); -		t[1].len = 1; -		t[1].tx_buf = buf + actual; - -		spi_sync(flash->spi, &m); -		ret = wait_till_ready(flash); -		if (ret) -			goto time_out; -		*retlen += m.actual_length - m25p_cmdsz(flash); -		write_disable(flash); -	} - -time_out: -	mutex_unlock(&flash->lock); -	return ret; -} - -/****************************************************************************/ - -/* - * SPI device driver setup and teardown - */ - -struct flash_info { -	/* JEDEC id zero means "no ID" (most older chips); otherwise it has -	 * a high byte of zero plus three data bytes: the manufacturer id, -	 * then a two byte device id. -	 */ -	u32		jedec_id; -	u16             ext_id; - -	/* The size listed here is what works with OPCODE_SE, which isn't -	 * necessarily called a "sector" by the vendor. -	 */ -	unsigned	sector_size; -	u16		n_sectors; +		return ret; -	u16		page_size; -	u16		addr_width; +	/* Send write enable, then erase commands. */ +	ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0); +	if (ret) +		return ret; -	u16		flags; -#define	SECT_4K		0x01		/* OPCODE_BE_4K works uniformly */ -#define	M25P_NO_ERASE	0x02		/* No erase command needed */ -}; +	/* Set up command buffer. */ +	flash->command[0] = nor->erase_opcode; +	m25p_addr2cmd(nor, offset, flash->command); -#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)	\ -	((kernel_ulong_t)&(struct flash_info) {				\ -		.jedec_id = (_jedec_id),				\ -		.ext_id = (_ext_id),					\ -		.sector_size = (_sector_size),				\ -		.n_sectors = (_n_sectors),				\ -		.page_size = 256,					\ -		.addr_width = 3,					\ -		.flags = (_flags),					\ -	}) - -#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width)	\ -	((kernel_ulong_t)&(struct flash_info) {				\ -		.sector_size = (_sector_size),				\ -		.n_sectors = (_n_sectors),				\ -		.page_size = (_page_size),				\ -		.addr_width = (_addr_width),				\ -		.flags = M25P_NO_ERASE,					\ -	}) - -/* NOTE: double check command sets and memory organization when you add - * more flash chips.  This current list focusses on newer chips, which - * have been converging on command sets which including JEDEC ID. - */ -static const struct spi_device_id m25p_ids[] = { -	/* Atmel -- some are (confusingly) marketed as "DataFlash" */ -	{ "at25fs010",  INFO(0x1f6601, 0, 32 * 1024,   4, SECT_4K) }, -	{ "at25fs040",  INFO(0x1f6604, 0, 64 * 1024,   8, SECT_4K) }, - -	{ "at25df041a", INFO(0x1f4401, 0, 64 * 1024,   8, SECT_4K) }, -	{ "at25df641",  INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) }, - -	{ "at26f004",   INFO(0x1f0400, 0, 64 * 1024,  8, SECT_4K) }, -	{ "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) }, -	{ "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) }, -	{ "at26df321",  INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) }, - -	/* EON -- en25pxx */ -	{ "en25p32", INFO(0x1c2016, 0, 64 * 1024,  64, 0) }, -	{ "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, - -	/* Intel/Numonyx -- xxxs33b */ -	{ "160s33b",  INFO(0x898911, 0, 64 * 1024,  32, 0) }, -	{ "320s33b",  INFO(0x898912, 0, 64 * 1024,  64, 0) }, -	{ "640s33b",  INFO(0x898913, 0, 64 * 1024, 128, 0) }, - -	/* Macronix */ -	{ "mx25l4005a",  INFO(0xc22013, 0, 64 * 1024,   8, SECT_4K) }, -	{ "mx25l8005",   INFO(0xc22014, 0, 64 * 1024,  16, 0) }, -	{ "mx25l3205d",  INFO(0xc22016, 0, 64 * 1024,  64, 0) }, -	{ "mx25l6405d",  INFO(0xc22017, 0, 64 * 1024, 128, 0) }, -	{ "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, -	{ "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, - -	/* Spansion -- single (large) sector size only, at least -	 * for the chips listed here (without boot sectors). -	 */ -	{ "s25sl004a",  INFO(0x010212,      0,  64 * 1024,   8, 0) }, -	{ "s25sl008a",  INFO(0x010213,      0,  64 * 1024,  16, 0) }, -	{ "s25sl016a",  INFO(0x010214,      0,  64 * 1024,  32, 0) }, -	{ "s25sl032a",  INFO(0x010215,      0,  64 * 1024,  64, 0) }, -	{ "s25sl032p",  INFO(0x010215, 0x4d00,  64 * 1024,  64, SECT_4K) }, -	{ "s25sl064a",  INFO(0x010216,      0,  64 * 1024, 128, 0) }, -	{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024,  64, 0) }, -	{ "s25sl12801", INFO(0x012018, 0x0301,  64 * 1024, 256, 0) }, -	{ "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024,  64, 0) }, -	{ "s25fl129p1", INFO(0x012018, 0x4d01,  64 * 1024, 256, 0) }, -	{ "s25fl016k",  INFO(0xef4015,      0,  64 * 1024,  32, SECT_4K) }, -	{ "s25fl064k",  INFO(0xef4017,      0,  64 * 1024, 128, SECT_4K) }, - -	/* SST -- large erase sizes are "overlays", "sectors" are 4K */ -	{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024,  8, SECT_4K) }, -	{ "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K) }, -	{ "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K) }, -	{ "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K) }, -	{ "sst25wf512",  INFO(0xbf2501, 0, 64 * 1024,  1, SECT_4K) }, -	{ "sst25wf010",  INFO(0xbf2502, 0, 64 * 1024,  2, SECT_4K) }, -	{ "sst25wf020",  INFO(0xbf2503, 0, 64 * 1024,  4, SECT_4K) }, -	{ "sst25wf040",  INFO(0xbf2504, 0, 64 * 1024,  8, SECT_4K) }, - -	/* ST Microelectronics -- newer production may have feature updates */ -	{ "m25p05",  INFO(0x202010,  0,  32 * 1024,   2, 0) }, -	{ "m25p10",  INFO(0x202011,  0,  32 * 1024,   4, 0) }, -	{ "m25p20",  INFO(0x202012,  0,  64 * 1024,   4, 0) }, -	{ "m25p40",  INFO(0x202013,  0,  64 * 1024,   8, 0) }, -	{ "m25p80",  INFO(0x202014,  0,  64 * 1024,  16, 0) }, -	{ "m25p16",  INFO(0x202015,  0,  64 * 1024,  32, 0) }, -	{ "m25p32",  INFO(0x202016,  0,  64 * 1024,  64, 0) }, -	{ "m25p64",  INFO(0x202017,  0,  64 * 1024, 128, 0) }, -	{ "m25p128", INFO(0x202018,  0, 256 * 1024,  64, 0) }, - -	{ "m25p05-nonjedec",  INFO(0, 0,  32 * 1024,   2, 0) }, -	{ "m25p10-nonjedec",  INFO(0, 0,  32 * 1024,   4, 0) }, -	{ "m25p20-nonjedec",  INFO(0, 0,  64 * 1024,   4, 0) }, -	{ "m25p40-nonjedec",  INFO(0, 0,  64 * 1024,   8, 0) }, -	{ "m25p80-nonjedec",  INFO(0, 0,  64 * 1024,  16, 0) }, -	{ "m25p16-nonjedec",  INFO(0, 0,  64 * 1024,  32, 0) }, -	{ "m25p32-nonjedec",  INFO(0, 0,  64 * 1024,  64, 0) }, -	{ "m25p64-nonjedec",  INFO(0, 0,  64 * 1024, 128, 0) }, -	{ "m25p128-nonjedec", INFO(0, 0, 256 * 1024,  64, 0) }, - -	{ "m45pe10", INFO(0x204011,  0, 64 * 1024,    2, 0) }, -	{ "m45pe80", INFO(0x204014,  0, 64 * 1024,   16, 0) }, -	{ "m45pe16", INFO(0x204015,  0, 64 * 1024,   32, 0) }, - -	{ "m25pe80", INFO(0x208014,  0, 64 * 1024, 16,       0) }, -	{ "m25pe16", INFO(0x208015,  0, 64 * 1024, 32, SECT_4K) }, - -	/* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ -	{ "w25x10", INFO(0xef3011, 0, 64 * 1024,  2,  SECT_4K) }, -	{ "w25x20", INFO(0xef3012, 0, 64 * 1024,  4,  SECT_4K) }, -	{ "w25x40", INFO(0xef3013, 0, 64 * 1024,  8,  SECT_4K) }, -	{ "w25x80", INFO(0xef3014, 0, 64 * 1024,  16, SECT_4K) }, -	{ "w25x16", INFO(0xef3015, 0, 64 * 1024,  32, SECT_4K) }, -	{ "w25x32", INFO(0xef3016, 0, 64 * 1024,  64, SECT_4K) }, -	{ "w25q32", INFO(0xef4016, 0, 64 * 1024,  64, SECT_4K) }, -	{ "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, -	{ "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, - -	/* Catalyst / On Semiconductor -- non-JEDEC */ -	{ "cat25c11", CAT25_INFO(  16, 8, 16, 1) }, -	{ "cat25c03", CAT25_INFO(  32, 8, 16, 2) }, -	{ "cat25c09", CAT25_INFO( 128, 8, 32, 2) }, -	{ "cat25c17", CAT25_INFO( 256, 8, 32, 2) }, -	{ "cat25128", CAT25_INFO(2048, 8, 64, 2) }, -	{ }, -}; -MODULE_DEVICE_TABLE(spi, m25p_ids); +	spi_write(flash->spi, flash->command, m25p_cmdsz(nor)); -static const struct spi_device_id *__devinit jedec_probe(struct spi_device *spi) -{ -	int			tmp; -	u8			code = OPCODE_RDID; -	u8			id[5]; -	u32			jedec; -	u16                     ext_jedec; -	struct flash_info	*info; - -	/* JEDEC also defines an optional "extended device information" -	 * string for after vendor-specific data, after the three bytes -	 * we use here.  Supporting some chips might require using it. -	 */ -	tmp = spi_write_then_read(spi, &code, 1, id, 5); -	if (tmp < 0) { -		DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n", -			dev_name(&spi->dev), tmp); -		return ERR_PTR(tmp); -	} -	jedec = id[0]; -	jedec = jedec << 8; -	jedec |= id[1]; -	jedec = jedec << 8; -	jedec |= id[2]; - -	ext_jedec = id[3] << 8 | id[4]; - -	for (tmp = 0; tmp < ARRAY_SIZE(m25p_ids) - 1; tmp++) { -		info = (void *)m25p_ids[tmp].driver_data; -		if (info->jedec_id == jedec) { -			if (info->ext_id != 0 && info->ext_id != ext_jedec) -				continue; -			return &m25p_ids[tmp]; -		} -	} -	return ERR_PTR(-ENODEV); +	return 0;  } -  /*   * board specific setup should have ensured the SPI clock used here   * matches what the READ command supports, at least until this driver   * understands FAST_READ (for clocks over 25 MHz).   */ -static int __devinit m25p_probe(struct spi_device *spi) +static int m25p_probe(struct spi_device *spi)  { -	const struct spi_device_id	*id = spi_get_device_id(spi); +	struct mtd_part_parser_data	ppdata;  	struct flash_platform_data	*data; -	struct m25p			*flash; -	struct flash_info		*info; -	unsigned			i; - -	/* Platform data helps sort out which chip type we have, as -	 * well as how this board partitions it.  If we don't have -	 * a chip ID, try the JEDEC id commands; they'll work for most -	 * newer chips, even if we don't recognize the particular chip. -	 */ -	data = spi->dev.platform_data; -	if (data && data->type) { -		const struct spi_device_id *plat_id; - -		for (i = 0; i < ARRAY_SIZE(m25p_ids) - 1; i++) { -			plat_id = &m25p_ids[i]; -			if (strcmp(data->type, plat_id->name)) -				continue; -			break; -		} - -		if (i < ARRAY_SIZE(m25p_ids) - 1) -			id = plat_id; -		else -			dev_warn(&spi->dev, "unrecognized id %s\n", data->type); -	} - -	info = (void *)id->driver_data; - -	if (info->jedec_id) { -		const struct spi_device_id *jid; - -		jid = jedec_probe(spi); -		if (IS_ERR(jid)) { -			return PTR_ERR(jid); -		} else if (jid != id) { -			/* -			 * JEDEC knows better, so overwrite platform ID. We -			 * can't trust partitions any longer, but we'll let -			 * mtd apply them anyway, since some partitions may be -			 * marked read-only, and we don't want to lose that -			 * information, even if it's not 100% accurate. -			 */ -			dev_warn(&spi->dev, "found %s, expected %s\n", -				 jid->name, id->name); -			id = jid; -			info = (void *)jid->driver_data; -		} -	} +	struct m25p *flash; +	struct spi_nor *nor; +	enum read_mode mode = SPI_NOR_NORMAL; +	int ret; -	flash = kzalloc(sizeof *flash, GFP_KERNEL); +	flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL);  	if (!flash)  		return -ENOMEM; -	flash->command = kmalloc(MAX_CMD_SIZE + FAST_READ_DUMMY_BYTE, GFP_KERNEL); -	if (!flash->command) { -		kfree(flash); -		return -ENOMEM; -	} -	flash->spi = spi; -	mutex_init(&flash->lock); -	dev_set_drvdata(&spi->dev, flash); +	nor = &flash->spi_nor; -	/* -	 * Atmel, SST and Intel/Numonyx serial flash tend to power -	 * up with the software protection bits set -	 */ +	/* install the hooks */ +	nor->read = m25p80_read; +	nor->write = m25p80_write; +	nor->erase = m25p80_erase; +	nor->write_reg = m25p80_write_reg; +	nor->read_reg = m25p80_read_reg; -	if (info->jedec_id >> 16 == 0x1f || -	    info->jedec_id >> 16 == 0x89 || -	    info->jedec_id >> 16 == 0xbf) { -		write_enable(flash); -		write_sr(flash, 0); -	} +	nor->dev = &spi->dev; +	nor->mtd = &flash->mtd; +	nor->priv = flash; -	if (data && data->name) -		flash->mtd.name = data->name; -	else -		flash->mtd.name = dev_name(&spi->dev); - -	flash->mtd.type = MTD_NORFLASH; -	flash->mtd.writesize = 1; -	flash->mtd.flags = MTD_CAP_NORFLASH; -	flash->mtd.size = info->sector_size * info->n_sectors; -	flash->mtd.erase = m25p80_erase; -	flash->mtd.read = m25p80_read; - -	/* sst flash chips use AAI word program */ -	if (info->jedec_id >> 16 == 0xbf) -		flash->mtd.write = sst_write; -	else -		flash->mtd.write = m25p80_write; - -	/* prefer "small sector" erase if possible */ -	if (info->flags & SECT_4K) { -		flash->erase_opcode = OPCODE_BE_4K; -		flash->mtd.erasesize = 4096; -	} else { -		flash->erase_opcode = OPCODE_SE; -		flash->mtd.erasesize = info->sector_size; -	} +	spi_set_drvdata(spi, flash); +	flash->mtd.priv = nor; +	flash->spi = spi; -	if (info->flags & M25P_NO_ERASE) -		flash->mtd.flags |= MTD_NO_ERASE; - -	flash->mtd.dev.parent = &spi->dev; -	flash->page_size = info->page_size; -	flash->addr_width = info->addr_width; - -	dev_info(&spi->dev, "%s (%lld Kbytes)\n", id->name, -			(long long)flash->mtd.size >> 10); - -	DEBUG(MTD_DEBUG_LEVEL2, -		"mtd .name = %s, .size = 0x%llx (%lldMiB) " -			".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n", -		flash->mtd.name, -		(long long)flash->mtd.size, (long long)(flash->mtd.size >> 20), -		flash->mtd.erasesize, flash->mtd.erasesize / 1024, -		flash->mtd.numeraseregions); - -	if (flash->mtd.numeraseregions) -		for (i = 0; i < flash->mtd.numeraseregions; i++) -			DEBUG(MTD_DEBUG_LEVEL2, -				"mtd.eraseregions[%d] = { .offset = 0x%llx, " -				".erasesize = 0x%.8x (%uKiB), " -				".numblocks = %d }\n", -				i, (long long)flash->mtd.eraseregions[i].offset, -				flash->mtd.eraseregions[i].erasesize, -				flash->mtd.eraseregions[i].erasesize / 1024, -				flash->mtd.eraseregions[i].numblocks); - - -	/* partitions should match sector boundaries; and it may be good to -	 * use readonly partitions for writeprotected sectors (BP2..BP0). -	 */ -	if (mtd_has_partitions()) { -		struct mtd_partition	*parts = NULL; -		int			nr_parts = 0; - -		if (mtd_has_cmdlinepart()) { -			static const char *part_probes[] -					= { "cmdlinepart", NULL, }; - -			nr_parts = parse_mtd_partitions(&flash->mtd, -					part_probes, &parts, 0); -		} - -		if (nr_parts <= 0 && data && data->parts) { -			parts = data->parts; -			nr_parts = data->nr_parts; -		} - -#ifdef CONFIG_MTD_OF_PARTS -		if (nr_parts <= 0 && spi->dev.of_node) { -			nr_parts = of_mtd_parse_partitions(&spi->dev, -					spi->dev.of_node, &parts); -		} -#endif - -		if (nr_parts > 0) { -			for (i = 0; i < nr_parts; i++) { -				DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " -					"{.name = %s, .offset = 0x%llx, " -						".size = 0x%llx (%lldKiB) }\n", -					i, parts[i].name, -					(long long)parts[i].offset, -					(long long)parts[i].size, -					(long long)(parts[i].size >> 10)); -			} -			flash->partitioned = 1; -			return add_mtd_partitions(&flash->mtd, parts, nr_parts); -		} -	} else if (data && data->nr_parts) -		dev_warn(&spi->dev, "ignoring %d default partitions on %s\n", -				data->nr_parts, data->name); - -	return add_mtd_device(&flash->mtd) == 1 ? -ENODEV : 0; +	if (spi->mode & SPI_RX_QUAD) +		mode = SPI_NOR_QUAD; +	else if (spi->mode & SPI_RX_DUAL) +		mode = SPI_NOR_DUAL; +	ret = spi_nor_scan(nor, spi_get_device_id(spi), mode); +	if (ret) +		return ret; + +	data = dev_get_platdata(&spi->dev); +	ppdata.of_node = spi->dev.of_node; + +	return mtd_device_parse_register(&flash->mtd, NULL, &ppdata, +			data ? data->parts : NULL, +			data ? data->nr_parts : 0);  } -static int __devexit m25p_remove(struct spi_device *spi) +static int m25p_remove(struct spi_device *spi)  { -	struct m25p	*flash = dev_get_drvdata(&spi->dev); -	int		status; +	struct m25p	*flash = spi_get_drvdata(spi);  	/* Clean up MTD stuff. */ -	if (mtd_has_partitions() && flash->partitioned) -		status = del_mtd_partitions(&flash->mtd); -	else -		status = del_mtd_device(&flash->mtd); -	if (status == 0) { -		kfree(flash->command); -		kfree(flash); -	} -	return 0; +	return mtd_device_unregister(&flash->mtd);  }  static struct spi_driver m25p80_driver = {  	.driver = {  		.name	= "m25p80", -		.bus	= &spi_bus_type,  		.owner	= THIS_MODULE,  	}, -	.id_table	= m25p_ids, +	.id_table	= spi_nor_ids,  	.probe	= m25p_probe, -	.remove	= __devexit_p(m25p_remove), +	.remove	= m25p_remove,  	/* REVISIT: many of these chips have deep power-down modes, which  	 * should clearly be entered on suspend() to minimize power use. @@ -990,21 +260,7 @@ static struct spi_driver m25p80_driver = {  	 */  }; - -static int __init m25p80_init(void) -{ -	return spi_register_driver(&m25p80_driver); -} - - -static void __exit m25p80_exit(void) -{ -	spi_unregister_driver(&m25p80_driver); -} - - -module_init(m25p80_init); -module_exit(m25p80_exit); +module_spi_driver(m25p80_driver);  MODULE_LICENSE("GPL");  MODULE_AUTHOR("Mike Lavender"); diff --git a/drivers/mtd/devices/ms02-nv.c b/drivers/mtd/devices/ms02-nv.c index 6a9a24a80a6..5c8b322ba90 100644 --- a/drivers/mtd/devices/ms02-nv.c +++ b/drivers/mtd/devices/ms02-nv.c @@ -59,12 +59,8 @@ static int ms02nv_read(struct mtd_info *mtd, loff_t from,  {  	struct ms02nv_private *mp = mtd->priv; -	if (from + len > mtd->size) -		return -EINVAL; -  	memcpy(buf, mp->uaddr + from, len);  	*retlen = len; -  	return 0;  } @@ -73,12 +69,8 @@ static int ms02nv_write(struct mtd_info *mtd, loff_t to,  {  	struct ms02nv_private *mp = mtd->priv; -	if (to + len > mtd->size) -		return -EINVAL; -  	memcpy(mp->uaddr + to, buf, len);  	*retlen = len; -  	return 0;  } @@ -213,14 +205,14 @@ static int __init ms02nv_init_one(ulong addr)  	mtd->type = MTD_RAM;  	mtd->flags = MTD_CAP_RAM;  	mtd->size = fixsize; -	mtd->name = (char *)ms02nv_name; +	mtd->name = ms02nv_name;  	mtd->owner = THIS_MODULE; -	mtd->read = ms02nv_read; -	mtd->write = ms02nv_write; +	mtd->_read = ms02nv_read; +	mtd->_write = ms02nv_write;  	mtd->writesize = 1;  	ret = -EIO; -	if (add_mtd_device(mtd)) { +	if (mtd_device_register(mtd, NULL, 0)) {  		printk(KERN_ERR  			"ms02-nv: Unable to register MTD device, aborting!\n");  		goto err_out_csr_res; @@ -262,7 +254,7 @@ static void __exit ms02nv_remove_one(void)  	root_ms02nv_mtd = mp->next; -	del_mtd_device(mtd); +	mtd_device_unregister(mtd);  	release_resource(mp->resource.csr);  	kfree(mp->resource.csr); diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index c5015cc721d..dd22ce2cc9a 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -10,13 +10,14 @@   * 2 of the License, or (at your option) any later version.  */  #include <linux/module.h> -#include <linux/init.h>  #include <linux/slab.h>  #include <linux/delay.h>  #include <linux/device.h>  #include <linux/mutex.h>  #include <linux/err.h>  #include <linux/math64.h> +#include <linux/of.h> +#include <linux/of_device.h>  #include <linux/spi/spi.h>  #include <linux/spi/flash.h> @@ -24,7 +25,6 @@  #include <linux/mtd/mtd.h>  #include <linux/mtd/partitions.h> -  /*   * DataFlash is a kind of SPI flash.  Most AT45 chips have two buffers in   * each chip, which may be used for double buffered I/O; but this driver @@ -87,8 +87,6 @@ struct dataflash {  	uint8_t			command[4];  	char			name[24]; -	unsigned		partitioned:1; -  	unsigned short		page_offset;	/* offset in flash address */  	unsigned int		page_size;	/* of bytes per page */ @@ -98,6 +96,14 @@ struct dataflash {  	struct mtd_info		mtd;  }; +#ifdef CONFIG_OF +static const struct of_device_id dataflash_dt_ids[] = { +	{ .compatible = "atmel,at45", }, +	{ .compatible = "atmel,dataflash", }, +	{ /* sentinel */ } +}; +#endif +  /* ......................................................................... */  /* @@ -122,7 +128,7 @@ static int dataflash_waitready(struct spi_device *spi)  	for (;;) {  		status = dataflash_status(spi);  		if (status < 0) { -			DEBUG(MTD_DEBUG_LEVEL1, "%s: status %d?\n", +			pr_debug("%s: status %d?\n",  					dev_name(&spi->dev), status);  			status = 0;  		} @@ -149,13 +155,10 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)  	uint8_t			*command;  	uint32_t		rem; -	DEBUG(MTD_DEBUG_LEVEL2, "%s: erase addr=0x%llx len 0x%llx\n", +	pr_debug("%s: erase addr=0x%llx len 0x%llx\n",  	      dev_name(&spi->dev), (long long)instr->addr,  	      (long long)instr->len); -	/* Sanity checks */ -	if (instr->addr + instr->len > mtd->size) -		return -EINVAL;  	div_u64_rem(instr->len, priv->page_size, &rem);  	if (rem)  		return -EINVAL; @@ -187,7 +190,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)  		command[2] = (uint8_t)(pageaddr >> 8);  		command[3] = 0; -		DEBUG(MTD_DEBUG_LEVEL3, "ERASE %s: (%x) %x %x %x [%i]\n", +		pr_debug("ERASE %s: (%x) %x %x %x [%i]\n",  			do_block ? "block" : "page",  			command[0], command[1], command[2], command[3],  			pageaddr); @@ -238,16 +241,8 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,  	uint8_t			*command;  	int			status; -	DEBUG(MTD_DEBUG_LEVEL2, "%s: read 0x%x..0x%x\n", -		dev_name(&priv->spi->dev), (unsigned)from, (unsigned)(from + len)); - -	*retlen = 0; - -	/* Sanity checks */ -	if (!len) -		return 0; -	if (from + len > mtd->size) -		return -EINVAL; +	pr_debug("%s: read 0x%x..0x%x\n", dev_name(&priv->spi->dev), +			(unsigned)from, (unsigned)(from + len));  	/* Calculate flash page/byte address */  	addr = (((unsigned)from / priv->page_size) << priv->page_offset) @@ -255,7 +250,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,  	command = priv->command; -	DEBUG(MTD_DEBUG_LEVEL3, "READ: (%x) %x %x %x\n", +	pr_debug("READ: (%x) %x %x %x\n",  		command[0], command[1], command[2], command[3]);  	spi_message_init(&msg); @@ -287,7 +282,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,  		*retlen = msg.actual_length - 8;  		status = 0;  	} else -		DEBUG(MTD_DEBUG_LEVEL1, "%s: read %x..%x --> %d\n", +		pr_debug("%s: read %x..%x --> %d\n",  			dev_name(&priv->spi->dev),  			(unsigned)from, (unsigned)(from + len),  			status); @@ -314,17 +309,9 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,  	int			status = -EINVAL;  	uint8_t			*command; -	DEBUG(MTD_DEBUG_LEVEL2, "%s: write 0x%x..0x%x\n", +	pr_debug("%s: write 0x%x..0x%x\n",  		dev_name(&spi->dev), (unsigned)to, (unsigned)(to + len)); -	*retlen = 0; - -	/* Sanity checks */ -	if (!len) -		return 0; -	if ((to + len) > mtd->size) -		return -EINVAL; -  	spi_message_init(&msg);  	x[0].tx_buf = command = priv->command; @@ -340,7 +327,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,  	mutex_lock(&priv->lock);  	while (remaining > 0) { -		DEBUG(MTD_DEBUG_LEVEL3, "write @ %i:%i len=%i\n", +		pr_debug("write @ %i:%i len=%i\n",  			pageaddr, offset, writelen);  		/* REVISIT: @@ -368,12 +355,12 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,  			command[2] = (addr & 0x0000FF00) >> 8;  			command[3] = 0; -			DEBUG(MTD_DEBUG_LEVEL3, "TRANSFER: (%x) %x %x %x\n", +			pr_debug("TRANSFER: (%x) %x %x %x\n",  				command[0], command[1], command[2], command[3]);  			status = spi_sync(spi, &msg);  			if (status < 0) -				DEBUG(MTD_DEBUG_LEVEL1, "%s: xfer %u -> %d \n", +				pr_debug("%s: xfer %u -> %d\n",  					dev_name(&spi->dev), addr, status);  			(void) dataflash_waitready(priv->spi); @@ -386,7 +373,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,  		command[2] = (addr & 0x0000FF00) >> 8;  		command[3] = (addr & 0x000000FF); -		DEBUG(MTD_DEBUG_LEVEL3, "PROGRAM: (%x) %x %x %x\n", +		pr_debug("PROGRAM: (%x) %x %x %x\n",  			command[0], command[1], command[2], command[3]);  		x[1].tx_buf = writebuf; @@ -395,7 +382,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,  		status = spi_sync(spi, &msg);  		spi_transfer_del(x + 1);  		if (status < 0) -			DEBUG(MTD_DEBUG_LEVEL1, "%s: pgm %u/%u -> %d \n", +			pr_debug("%s: pgm %u/%u -> %d\n",  				dev_name(&spi->dev), addr, writelen, status);  		(void) dataflash_waitready(priv->spi); @@ -410,12 +397,12 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,  		command[2] = (addr & 0x0000FF00) >> 8;  		command[3] = 0; -		DEBUG(MTD_DEBUG_LEVEL3, "COMPARE: (%x) %x %x %x\n", +		pr_debug("COMPARE: (%x) %x %x %x\n",  			command[0], command[1], command[2], command[3]);  		status = spi_sync(spi, &msg);  		if (status < 0) -			DEBUG(MTD_DEBUG_LEVEL1, "%s: compare %u -> %d \n", +			pr_debug("%s: compare %u -> %d\n",  				dev_name(&spi->dev), addr, status);  		status = dataflash_waitready(priv->spi); @@ -452,8 +439,8 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,  #ifdef CONFIG_MTD_DATAFLASH_OTP -static int dataflash_get_otp_info(struct mtd_info *mtd, -		struct otp_info *info, size_t len) +static int dataflash_get_otp_info(struct mtd_info *mtd, size_t len, +				  size_t *retlen, struct otp_info *info)  {  	/* Report both blocks as identical:  bytes 0..64, locked.  	 * Unless the user block changed from all-ones, we can't @@ -462,7 +449,8 @@ static int dataflash_get_otp_info(struct mtd_info *mtd,  	info->start = 0;  	info->length = 64;  	info->locked = 1; -	return sizeof(*info); +	*retlen = sizeof(*info); +	return 0;  }  static ssize_t otp_read(struct spi_device *spi, unsigned base, @@ -479,8 +467,6 @@ static ssize_t otp_read(struct spi_device *spi, unsigned base,  	if ((off + len) > 64)  		len = 64 - off; -	if (len == 0) -		return len;  	spi_message_init(&m); @@ -556,14 +542,18 @@ static int dataflash_write_user_otp(struct mtd_info *mtd,  	struct dataflash	*priv = mtd->priv;  	int			status; -	if (len > 64) -		return -EINVAL; +	if (from >= 64) { +		/* +		 * Attempting to write beyond the end of OTP memory, +		 * no data can be written. +		 */ +		*retlen = 0; +		return 0; +	} -	/* Strictly speaking, we *could* truncate the write ... but -	 * let's not do that for the only write that's ever possible. -	 */ +	/* Truncate the write to fit into OTP memory. */  	if ((from + len) > 64) -		return -EINVAL; +		len = 64 - from;  	/* OUT: OP_WRITE_SECURITY, 3 zeroes, 64 data-or-zero bytes  	 * IN:  ignore all @@ -600,16 +590,16 @@ static int dataflash_write_user_otp(struct mtd_info *mtd,  static char *otp_setup(struct mtd_info *device, char revision)  { -	device->get_fact_prot_info = dataflash_get_otp_info; -	device->read_fact_prot_reg = dataflash_read_fact_otp; -	device->get_user_prot_info = dataflash_get_otp_info; -	device->read_user_prot_reg = dataflash_read_user_otp; +	device->_get_fact_prot_info = dataflash_get_otp_info; +	device->_read_fact_prot_reg = dataflash_read_fact_otp; +	device->_get_user_prot_info = dataflash_get_otp_info; +	device->_read_user_prot_reg = dataflash_read_user_otp;  	/* rev c parts (at45db321c and at45db1281 only!) use a  	 * different write procedure; not (yet?) implemented.  	 */  	if (revision > 'c') -		device->write_user_prot_reg = dataflash_write_user_otp; +		device->_write_user_prot_reg = dataflash_write_user_otp;  	return ", OTP";  } @@ -628,13 +618,13 @@ static char *otp_setup(struct mtd_info *device, char revision)  /*   * Register DataFlash device with MTD subsystem.   */ -static int __devinit -add_dataflash_otp(struct spi_device *spi, char *name, -		int nr_pages, int pagesize, int pageoffset, char revision) +static int add_dataflash_otp(struct spi_device *spi, char *name, int nr_pages, +			     int pagesize, int pageoffset, char revision)  {  	struct dataflash		*priv;  	struct mtd_info			*device; -	struct flash_platform_data	*pdata = spi->dev.platform_data; +	struct mtd_part_parser_data	ppdata; +	struct flash_platform_data	*pdata = dev_get_platdata(&spi->dev);  	char				*otp_tag = "";  	int				err = 0; @@ -660,9 +650,9 @@ add_dataflash_otp(struct spi_device *spi, char *name,  	device->owner = THIS_MODULE;  	device->type = MTD_DATAFLASH;  	device->flags = MTD_WRITEABLE; -	device->erase = dataflash_erase; -	device->read = dataflash_read; -	device->write = dataflash_write; +	device->_erase = dataflash_erase; +	device->_read = dataflash_read; +	device->_write = dataflash_write;  	device->priv = priv;  	device->dev.parent = &spi->dev; @@ -673,49 +663,22 @@ add_dataflash_otp(struct spi_device *spi, char *name,  	dev_info(&spi->dev, "%s (%lld KBytes) pagesize %d bytes%s\n",  			name, (long long)((device->size + 1023) >> 10),  			pagesize, otp_tag); -	dev_set_drvdata(&spi->dev, priv); - -	if (mtd_has_partitions()) { -		struct mtd_partition	*parts; -		int			nr_parts = 0; - -		if (mtd_has_cmdlinepart()) { -			static const char *part_probes[] -					= { "cmdlinepart", NULL, }; - -			nr_parts = parse_mtd_partitions(device, -					part_probes, &parts, 0); -		} - -		if (nr_parts <= 0 && pdata && pdata->parts) { -			parts = pdata->parts; -			nr_parts = pdata->nr_parts; -		} - -		if (nr_parts > 0) { -			priv->partitioned = 1; -			err = add_mtd_partitions(device, parts, nr_parts); -			goto out; -		} -	} else if (pdata && pdata->nr_parts) -		dev_warn(&spi->dev, "ignoring %d default partitions on %s\n", -				pdata->nr_parts, device->name); +	spi_set_drvdata(spi, priv); -	if (add_mtd_device(device) == 1) -		err = -ENODEV; +	ppdata.of_node = spi->dev.of_node; +	err = mtd_device_parse_register(device, NULL, &ppdata, +			pdata ? pdata->parts : NULL, +			pdata ? pdata->nr_parts : 0); -out:  	if (!err)  		return 0; -	dev_set_drvdata(&spi->dev, NULL);  	kfree(priv);  	return err;  } -static inline int __devinit -add_dataflash(struct spi_device *spi, char *name, -		int nr_pages, int pagesize, int pageoffset) +static inline int add_dataflash(struct spi_device *spi, char *name, +				int nr_pages, int pagesize, int pageoffset)  {  	return add_dataflash_otp(spi, name, nr_pages, pagesize,  			pageoffset, 0); @@ -739,7 +702,7 @@ struct flash_info {  #define IS_POW2PS	0x0001		/* uses 2^N byte pages */  }; -static struct flash_info __devinitdata dataflash_data [] = { +static struct flash_info dataflash_data[] = {  	/*  	 * NOTE:  chips with SUP_POW2PS (rev D and up) need two entries, @@ -774,7 +737,7 @@ static struct flash_info __devinitdata dataflash_data [] = {  	{ "at45db642d",  0x1f2800, 8192, 1024, 10, SUP_POW2PS | IS_POW2PS},  }; -static struct flash_info *__devinit jedec_probe(struct spi_device *spi) +static struct flash_info *jedec_probe(struct spi_device *spi)  {  	int			tmp;  	uint8_t			code = OP_READ_ID; @@ -793,7 +756,7 @@ static struct flash_info *__devinit jedec_probe(struct spi_device *spi)  	 */  	tmp = spi_write_then_read(spi, &code, 1, id, 3);  	if (tmp < 0) { -		DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n", +		pr_debug("%s: error %d reading JEDEC ID\n",  			dev_name(&spi->dev), tmp);  		return ERR_PTR(tmp);  	} @@ -810,7 +773,7 @@ static struct flash_info *__devinit jedec_probe(struct spi_device *spi)  			tmp < ARRAY_SIZE(dataflash_data);  			tmp++, info++) {  		if (info->jedec_id == jedec) { -			DEBUG(MTD_DEBUG_LEVEL1, "%s: OTP, sector protect%s\n", +			pr_debug("%s: OTP, sector protect%s\n",  				dev_name(&spi->dev),  				(info->flags & SUP_POW2PS)  					? ", binary pagesize" : "" @@ -818,8 +781,7 @@ static struct flash_info *__devinit jedec_probe(struct spi_device *spi)  			if (info->flags & SUP_POW2PS) {  				status = dataflash_status(spi);  				if (status < 0) { -					DEBUG(MTD_DEBUG_LEVEL1, -						"%s: status error %d\n", +					pr_debug("%s: status error %d\n",  						dev_name(&spi->dev), status);  					return ERR_PTR(status);  				} @@ -858,7 +820,7 @@ static struct flash_info *__devinit jedec_probe(struct spi_device *spi)   *   AT45DB0642  64Mbit  (8M)    xx111xxx (0x3c)   8192   1056     11   *   AT45DB1282  128Mbit (16M)   xx0100xx (0x10)  16384   1056     11   */ -static int __devinit dataflash_probe(struct spi_device *spi) +static int dataflash_probe(struct spi_device *spi)  {  	int status;  	struct flash_info	*info; @@ -884,7 +846,7 @@ static int __devinit dataflash_probe(struct spi_device *spi)  	 */  	status = dataflash_status(spi);  	if (status <= 0 || status == 0xff) { -		DEBUG(MTD_DEBUG_LEVEL1, "%s: status error %d\n", +		pr_debug("%s: status error %d\n",  				dev_name(&spi->dev), status);  		if (status == 0 || status == 0xff)  			status = -ENODEV; @@ -920,61 +882,45 @@ static int __devinit dataflash_probe(struct spi_device *spi)  		break;  	/* obsolete AT45DB1282 not (yet?) supported */  	default: -		DEBUG(MTD_DEBUG_LEVEL1, "%s: unsupported device (%x)\n", -				dev_name(&spi->dev), status & 0x3c); +		dev_info(&spi->dev, "unsupported device (%x)\n", +				status & 0x3c);  		status = -ENODEV;  	}  	if (status < 0) -		DEBUG(MTD_DEBUG_LEVEL1, "%s: add_dataflash --> %d\n", -				dev_name(&spi->dev), status); +		pr_debug("%s: add_dataflash --> %d\n", dev_name(&spi->dev), +				status);  	return status;  } -static int __devexit dataflash_remove(struct spi_device *spi) +static int dataflash_remove(struct spi_device *spi)  { -	struct dataflash	*flash = dev_get_drvdata(&spi->dev); +	struct dataflash	*flash = spi_get_drvdata(spi);  	int			status; -	DEBUG(MTD_DEBUG_LEVEL1, "%s: remove\n", dev_name(&spi->dev)); +	pr_debug("%s: remove\n", dev_name(&spi->dev)); -	if (mtd_has_partitions() && flash->partitioned) -		status = del_mtd_partitions(&flash->mtd); -	else -		status = del_mtd_device(&flash->mtd); -	if (status == 0) { -		dev_set_drvdata(&spi->dev, NULL); +	status = mtd_device_unregister(&flash->mtd); +	if (status == 0)  		kfree(flash); -	}  	return status;  }  static struct spi_driver dataflash_driver = {  	.driver = {  		.name		= "mtd_dataflash", -		.bus		= &spi_bus_type,  		.owner		= THIS_MODULE, +		.of_match_table = of_match_ptr(dataflash_dt_ids),  	},  	.probe		= dataflash_probe, -	.remove		= __devexit_p(dataflash_remove), +	.remove		= dataflash_remove,  	/* FIXME:  investigate suspend and resume... */  }; -static int __init dataflash_init(void) -{ -	return spi_register_driver(&dataflash_driver); -} -module_init(dataflash_init); - -static void __exit dataflash_exit(void) -{ -	spi_unregister_driver(&dataflash_driver); -} -module_exit(dataflash_exit); - +module_spi_driver(dataflash_driver);  MODULE_LICENSE("GPL");  MODULE_AUTHOR("Andrew Victor, David Brownell"); diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c index 26a6e809013..8e285089229 100644 --- a/drivers/mtd/devices/mtdram.c +++ b/drivers/mtd/devices/mtdram.c @@ -34,34 +34,23 @@ static struct mtd_info *mtd_info;  static int ram_erase(struct mtd_info *mtd, struct erase_info *instr)  { -	if (instr->addr + instr->len > mtd->size) -		return -EINVAL; -  	memset((char *)mtd->priv + instr->addr, 0xff, instr->len); -  	instr->state = MTD_ERASE_DONE;  	mtd_erase_callback(instr); -  	return 0;  }  static int ram_point(struct mtd_info *mtd, loff_t from, size_t len,  		size_t *retlen, void **virt, resource_size_t *phys)  { -	if (from + len > mtd->size) -		return -EINVAL; - -	/* can we return a physical address with this driver? */ -	if (phys) -		return -EINVAL; -  	*virt = mtd->priv + from;  	*retlen = len;  	return 0;  } -static void ram_unpoint(struct mtd_info *mtd, loff_t from, size_t len) +static int ram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)  { +	return 0;  }  /* @@ -80,11 +69,7 @@ static unsigned long ram_get_unmapped_area(struct mtd_info *mtd,  static int ram_read(struct mtd_info *mtd, loff_t from, size_t len,  		size_t *retlen, u_char *buf)  { -	if (from + len > mtd->size) -		return -EINVAL; -  	memcpy(buf, mtd->priv + from, len); -  	*retlen = len;  	return 0;  } @@ -92,11 +77,7 @@ static int ram_read(struct mtd_info *mtd, loff_t from, size_t len,  static int ram_write(struct mtd_info *mtd, loff_t to, size_t len,  		size_t *retlen, const u_char *buf)  { -	if (to + len > mtd->size) -		return -EINVAL; -  	memcpy((char *)mtd->priv + to, buf, len); -  	*retlen = len;  	return 0;  } @@ -104,14 +85,14 @@ static int ram_write(struct mtd_info *mtd, loff_t to, size_t len,  static void __exit cleanup_mtdram(void)  {  	if (mtd_info) { -		del_mtd_device(mtd_info); +		mtd_device_unregister(mtd_info);  		vfree(mtd_info->priv);  		kfree(mtd_info);  	}  }  int mtdram_init_device(struct mtd_info *mtd, void *mapped_address, -		unsigned long size, char *name) +		unsigned long size, const char *name)  {  	memset(mtd, 0, sizeof(*mtd)); @@ -121,20 +102,20 @@ int mtdram_init_device(struct mtd_info *mtd, void *mapped_address,  	mtd->flags = MTD_CAP_RAM;  	mtd->size = size;  	mtd->writesize = 1; +	mtd->writebufsize = 64; /* Mimic CFI NOR flashes */  	mtd->erasesize = MTDRAM_ERASE_SIZE;  	mtd->priv = mapped_address;  	mtd->owner = THIS_MODULE; -	mtd->erase = ram_erase; -	mtd->point = ram_point; -	mtd->unpoint = ram_unpoint; -	mtd->get_unmapped_area = ram_get_unmapped_area; -	mtd->read = ram_read; -	mtd->write = ram_write; - -	if (add_mtd_device(mtd)) { +	mtd->_erase = ram_erase; +	mtd->_point = ram_point; +	mtd->_unpoint = ram_unpoint; +	mtd->_get_unmapped_area = ram_get_unmapped_area; +	mtd->_read = ram_read; +	mtd->_write = ram_write; + +	if (mtd_device_register(mtd, NULL, 0))  		return -EIO; -	}  	return 0;  } diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index 52393282eaf..2cceebfb251 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -33,45 +33,33 @@ struct phram_mtd_list {  static LIST_HEAD(phram_list); -  static int phram_erase(struct mtd_info *mtd, struct erase_info *instr)  {  	u_char *start = mtd->priv; -	if (instr->addr + instr->len > mtd->size) -		return -EINVAL; -  	memset(start + instr->addr, 0xff, instr->len); -	/* This'll catch a few races. Free the thing before returning :) +	/* +	 * This'll catch a few races. Free the thing before returning :)  	 * I don't feel at all ashamed. This kind of thing is possible anyway  	 * with flash, but unlikely.  	 */ -  	instr->state = MTD_ERASE_DONE; -  	mtd_erase_callback(instr); -  	return 0;  }  static int phram_point(struct mtd_info *mtd, loff_t from, size_t len,  		size_t *retlen, void **virt, resource_size_t *phys)  { -	if (from + len > mtd->size) -		return -EINVAL; - -	/* can we return a physical address with this driver? */ -	if (phys) -		return -EINVAL; -  	*virt = mtd->priv + from;  	*retlen = len;  	return 0;  } -static void phram_unpoint(struct mtd_info *mtd, loff_t from, size_t len) +static int phram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)  { +	return 0;  }  static int phram_read(struct mtd_info *mtd, loff_t from, size_t len, @@ -79,14 +67,7 @@ static int phram_read(struct mtd_info *mtd, loff_t from, size_t len,  {  	u_char *start = mtd->priv; -	if (from >= mtd->size) -		return -EINVAL; - -	if (len > mtd->size - from) -		len = mtd->size - from; -  	memcpy(buf, start + from, len); -  	*retlen = len;  	return 0;  } @@ -96,32 +77,24 @@ static int phram_write(struct mtd_info *mtd, loff_t to, size_t len,  {  	u_char *start = mtd->priv; -	if (to >= mtd->size) -		return -EINVAL; - -	if (len > mtd->size - to) -		len = mtd->size - to; -  	memcpy(start + to, buf, len); -  	*retlen = len;  	return 0;  } - -  static void unregister_devices(void)  {  	struct phram_mtd_list *this, *safe;  	list_for_each_entry_safe(this, safe, &phram_list, list) { -		del_mtd_device(&this->mtd); +		mtd_device_unregister(&this->mtd);  		iounmap(this->mtd.priv); +		kfree(this->mtd.name);  		kfree(this);  	}  } -static int register_device(char *name, unsigned long start, unsigned long len) +static int register_device(char *name, phys_addr_t start, size_t len)  {  	struct phram_mtd_list *new;  	int ret = -ENOMEM; @@ -141,18 +114,18 @@ static int register_device(char *name, unsigned long start, unsigned long len)  	new->mtd.name = name;  	new->mtd.size = len;  	new->mtd.flags = MTD_CAP_RAM; -        new->mtd.erase = phram_erase; -	new->mtd.point = phram_point; -	new->mtd.unpoint = phram_unpoint; -	new->mtd.read = phram_read; -	new->mtd.write = phram_write; +	new->mtd._erase = phram_erase; +	new->mtd._point = phram_point; +	new->mtd._unpoint = phram_unpoint; +	new->mtd._read = phram_read; +	new->mtd._write = phram_write;  	new->mtd.owner = THIS_MODULE;  	new->mtd.type = MTD_RAM;  	new->mtd.erasesize = PAGE_SIZE;  	new->mtd.writesize = 1;  	ret = -EAGAIN; -	if (add_mtd_device(&new->mtd)) { +	if (mtd_device_register(&new->mtd, NULL, 0)) {  		pr_err("Failed to register new device\n");  		goto out2;  	} @@ -168,35 +141,35 @@ out0:  	return ret;  } -static int ustrtoul(const char *cp, char **endp, unsigned int base) +static int parse_num64(uint64_t *num64, char *token)  { -	unsigned long result = simple_strtoul(cp, endp, base); - -	switch (**endp) { -	case 'G': -		result *= 1024; -	case 'M': -		result *= 1024; -	case 'k': -		result *= 1024; +	size_t len; +	int shift = 0; +	int ret; + +	len = strlen(token);  	/* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */ -		if ((*endp)[1] == 'i') -			(*endp) += 2; +	if (len > 2) { +		if (token[len - 1] == 'i') { +			switch (token[len - 2]) { +			case 'G': +				shift += 10; +			case 'M': +				shift += 10; +			case 'k': +				shift += 10; +				token[len - 2] = 0; +				break; +			default: +				return -EINVAL; +			} +		}  	} -	return result; -} - -static int parse_num32(uint32_t *num32, const char *token) -{ -	char *endp; -	unsigned long n; -	n = ustrtoul(token, &endp, 0); -	if (*endp) -		return -EINVAL; +	ret = kstrtou64(token, 0, num64); +	*num64 <<= shift; -	*num32 = n; -	return 0; +	return ret;  }  static int parse_name(char **pname, const char *token) @@ -232,13 +205,26 @@ static inline void kill_final_newline(char *str)  	return 1;		\  } while (0) -static int phram_setup(const char *val, struct kernel_param *kp) +#ifndef MODULE +static int phram_init_called; +/* + * This shall contain the module parameter if any. It is of the form: + * - phram=<device>,<address>,<size> for module case + * - phram.phram=<device>,<address>,<size> for built-in case + * We leave 64 bytes for the device name, 20 for the address and 20 for the + * size. + * Example: phram.phram=rootfs,0xa0000000,512Mi + */ +static char phram_paramline[64 + 20 + 20]; +#endif + +static int phram_setup(const char *val)  { -	char buf[64+12+12], *str = buf; +	char buf[64 + 20 + 20], *str = buf;  	char *token[3];  	char *name; -	uint32_t start; -	uint32_t len; +	uint64_t start; +	uint64_t len;  	int i, ret;  	if (strnlen(val, sizeof(buf)) >= sizeof(buf)) @@ -260,13 +246,13 @@ static int phram_setup(const char *val, struct kernel_param *kp)  	if (ret)  		return ret; -	ret = parse_num32(&start, token[1]); +	ret = parse_num64(&start, token[1]);  	if (ret) {  		kfree(name);  		parse_err("illegal start address\n");  	} -	ret = parse_num32(&len, token[2]); +	ret = parse_num64(&len, token[2]);  	if (ret) {  		kfree(name);  		parse_err("illegal device length\n"); @@ -274,18 +260,60 @@ static int phram_setup(const char *val, struct kernel_param *kp)  	ret = register_device(name, start, len);  	if (!ret) -		pr_info("%s device: %#x at %#x\n", name, len, start); +		pr_info("%s device: %#llx at %#llx\n", name, len, start); +	else +		kfree(name);  	return ret;  } -module_param_call(phram, phram_setup, NULL, NULL, 000); +static int phram_param_call(const char *val, struct kernel_param *kp) +{ +#ifdef MODULE +	return phram_setup(val); +#else +	/* +	 * If more parameters are later passed in via +	 * /sys/module/phram/parameters/phram +	 * and init_phram() has already been called, +	 * we can parse the argument now. +	 */ + +	if (phram_init_called) +		return phram_setup(val); + +	/* +	 * During early boot stage, we only save the parameters +	 * here. We must parse them later: if the param passed +	 * from kernel boot command line, phram_param_call() is +	 * called so early that it is not possible to resolve +	 * the device (even kmalloc() fails). Defer that work to +	 * phram_setup(). +	 */ + +	if (strlen(val) >= sizeof(phram_paramline)) +		return -ENOSPC; +	strcpy(phram_paramline, val); + +	return 0; +#endif +} + +module_param_call(phram, phram_param_call, NULL, NULL, 000);  MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>\"");  static int __init init_phram(void)  { -	return 0; +	int ret = 0; + +#ifndef MODULE +	if (phram_paramline[0]) +		ret = phram_setup(phram_paramline); +	phram_init_called = 1; +#endif + +	return ret;  }  static void __exit cleanup_phram(void) diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index ef0aba0ce58..f02603e1bfe 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -93,14 +93,49 @@  #include <linux/fs.h>  #include <linux/ioctl.h>  #include <asm/io.h> -#include <asm/system.h>  #include <linux/pci.h> -  #include <linux/mtd/mtd.h> -#include <linux/mtd/pmc551.h> + +#define PMC551_VERSION \ +	"Ramix PMC551 PCI Mezzanine Ram Driver. (C) 1999,2000 Nortel Networks.\n" + +#define PCI_VENDOR_ID_V3_SEMI 0x11b0 +#define PCI_DEVICE_ID_V3_SEMI_V370PDC 0x0200 + +#define PMC551_PCI_MEM_MAP0 0x50 +#define PMC551_PCI_MEM_MAP1 0x54 +#define PMC551_PCI_MEM_MAP_MAP_ADDR_MASK 0x3ff00000 +#define PMC551_PCI_MEM_MAP_APERTURE_MASK 0x000000f0 +#define PMC551_PCI_MEM_MAP_REG_EN 0x00000002 +#define PMC551_PCI_MEM_MAP_ENABLE 0x00000001 + +#define PMC551_SDRAM_MA  0x60 +#define PMC551_SDRAM_CMD 0x62 +#define PMC551_DRAM_CFG  0x64 +#define PMC551_SYS_CTRL_REG 0x78 + +#define PMC551_DRAM_BLK0 0x68 +#define PMC551_DRAM_BLK1 0x6c +#define PMC551_DRAM_BLK2 0x70 +#define PMC551_DRAM_BLK3 0x74 +#define PMC551_DRAM_BLK_GET_SIZE(x) (524288 << ((x >> 4) & 0x0f)) +#define PMC551_DRAM_BLK_SET_COL_MUX(x, v) (((x) & ~0x00007000) | (((v) & 0x7) << 12)) +#define PMC551_DRAM_BLK_SET_ROW_MUX(x, v) (((x) & ~0x00000f00) | (((v) & 0xf) << 8)) + +struct mypriv { +	struct pci_dev *dev; +	u_char *start; +	u32 base_map0; +	u32 curr_map0; +	u32 asize; +	struct mtd_info *nextpmc551; +};  static struct mtd_info *pmc551list; +static int pmc551_point(struct mtd_info *mtd, loff_t from, size_t len, +			size_t *retlen, void **virt, resource_size_t *phys); +  static int pmc551_erase(struct mtd_info *mtd, struct erase_info *instr)  {  	struct mypriv *priv = mtd->priv; @@ -116,16 +151,6 @@ static int pmc551_erase(struct mtd_info *mtd, struct erase_info *instr)  #endif  	end = instr->addr + instr->len - 1; - -	/* Is it past the end? */ -	if (end > mtd->size) { -#ifdef CONFIG_MTD_PMC551_DEBUG -		printk(KERN_DEBUG "pmc551_erase() out of bounds (%ld > %ld)\n", -			(long)end, (long)mtd->size); -#endif -		return -EINVAL; -	} -  	eoff_hi = end & ~(priv->asize - 1);  	soff_hi = instr->addr & ~(priv->asize - 1);  	eoff_lo = end & (priv->asize - 1); @@ -179,18 +204,6 @@ static int pmc551_point(struct mtd_info *mtd, loff_t from, size_t len,  	printk(KERN_DEBUG "pmc551_point(%ld, %ld)\n", (long)from, (long)len);  #endif -	if (from + len > mtd->size) { -#ifdef CONFIG_MTD_PMC551_DEBUG -		printk(KERN_DEBUG "pmc551_point() out of bounds (%ld > %ld)\n", -			(long)from + len, (long)mtd->size); -#endif -		return -EINVAL; -	} - -	/* can we return a physical address with this driver? */ -	if (phys) -		return -EINVAL; -  	soff_hi = from & ~(priv->asize - 1);  	soff_lo = from & (priv->asize - 1); @@ -206,11 +219,12 @@ static int pmc551_point(struct mtd_info *mtd, loff_t from, size_t len,  	return 0;  } -static void pmc551_unpoint(struct mtd_info *mtd, loff_t from, size_t len) +static int pmc551_unpoint(struct mtd_info *mtd, loff_t from, size_t len)  {  #ifdef CONFIG_MTD_PMC551_DEBUG  	printk(KERN_DEBUG "pmc551_unpoint()\n");  #endif +	return 0;  }  static int pmc551_read(struct mtd_info *mtd, loff_t from, size_t len, @@ -229,16 +243,6 @@ static int pmc551_read(struct mtd_info *mtd, loff_t from, size_t len,  #endif  	end = from + len - 1; - -	/* Is it past the end? */ -	if (end > mtd->size) { -#ifdef CONFIG_MTD_PMC551_DEBUG -		printk(KERN_DEBUG "pmc551_read() out of bounds (%ld > %ld)\n", -			(long)end, (long)mtd->size); -#endif -		return -EINVAL; -	} -  	soff_hi = from & ~(priv->asize - 1);  	eoff_hi = end & ~(priv->asize - 1);  	soff_lo = from & (priv->asize - 1); @@ -296,16 +300,6 @@ static int pmc551_write(struct mtd_info *mtd, loff_t to, size_t len,  #endif  	end = to + len - 1; -	/* Is it past the end?  or did the u32 wrap? */ -	if (end > mtd->size) { -#ifdef CONFIG_MTD_PMC551_DEBUG -		printk(KERN_DEBUG "pmc551_write() out of bounds (end: %ld, " -			"size: %ld, to: %ld)\n", (long)end, (long)mtd->size, -			(long)to); -#endif -		return -EINVAL; -	} -  	soff_hi = to & ~(priv->asize - 1);  	eoff_hi = end & ~(priv->asize - 1);  	soff_lo = to & (priv->asize - 1); @@ -351,7 +345,7 @@ static int pmc551_write(struct mtd_info *mtd, loff_t to, size_t len,   * Fixup routines for the V370PDC   * PCI device ID 0x020011b0   * - * This function basicly kick starts the DRAM oboard the card and gets it + * This function basically kick starts the DRAM oboard the card and gets it   * ready to be used.  Before this is done the device reads VERY erratic, so   * much that it can crash the Linux 2.2.x series kernels when a user cat's   * /proc/pci .. though that is mainly a kernel bug in handling the PCI DEVSEL @@ -359,7 +353,7 @@ static int pmc551_write(struct mtd_info *mtd, loff_t to, size_t len,   * mechanism   * returns the size of the memory region found.   */ -static u32 fixup_pmc551(struct pci_dev *dev) +static int fixup_pmc551(struct pci_dev *dev)  {  #ifdef CONFIG_MTD_PMC551_BUGFIX  	u32 dram_data; @@ -540,7 +534,7 @@ static u32 fixup_pmc551(struct pci_dev *dev)  	/*  	 * Check to make certain the DEVSEL is set correctly, this device -	 * has a tendancy to assert DEVSEL and TRDY when a write is performed +	 * has a tendency to assert DEVSEL and TRDY when a write is performed  	 * to the memory when memory is read-only  	 */  	if ((cmd & PCI_STATUS_DEVSEL_MASK) != 0x0) { @@ -669,7 +663,7 @@ static int __init init_pmc551(void)  	struct mypriv *priv;  	int found = 0;  	struct mtd_info *mtd; -	u32 length = 0; +	int length = 0;  	if (msize) {  		msize = (1 << (ffs(msize) - 1)) << 20; @@ -731,16 +725,11 @@ static int __init init_pmc551(void)  		}  		mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); -		if (!mtd) { -			printk(KERN_NOTICE "pmc551: Cannot allocate new MTD " -				"device.\n"); +		if (!mtd)  			break; -		}  		priv = kzalloc(sizeof(struct mypriv), GFP_KERNEL);  		if (!priv) { -			printk(KERN_NOTICE "pmc551: Cannot allocate new MTD " -				"device.\n");  			kfree(mtd);  			break;  		} @@ -787,18 +776,18 @@ static int __init init_pmc551(void)  		mtd->size = msize;  		mtd->flags = MTD_CAP_RAM; -		mtd->erase = pmc551_erase; -		mtd->read = pmc551_read; -		mtd->write = pmc551_write; -		mtd->point = pmc551_point; -		mtd->unpoint = pmc551_unpoint; +		mtd->_erase = pmc551_erase; +		mtd->_read = pmc551_read; +		mtd->_write = pmc551_write; +		mtd->_point = pmc551_point; +		mtd->_unpoint = pmc551_unpoint;  		mtd->type = MTD_RAM;  		mtd->name = "PMC551 RAM board";  		mtd->erasesize = 0x10000;  		mtd->writesize = 1;  		mtd->owner = THIS_MODULE; -		if (add_mtd_device(mtd)) { +		if (mtd_device_register(mtd, NULL, 0)) {  			printk(KERN_NOTICE "pmc551: Failed to register new device\n");  			pci_iounmap(PCI_Device, priv->start);  			kfree(mtd->priv); @@ -806,7 +795,7 @@ static int __init init_pmc551(void)  			break;  		} -		/* Keep a reference as the add_mtd_device worked */ +		/* Keep a reference as the mtd_device_register worked */  		pci_dev_get(PCI_Device);  		printk(KERN_NOTICE "Registered pmc551 memory device.\n"); @@ -856,7 +845,7 @@ static void __exit cleanup_pmc551(void)  		pci_dev_put(priv->dev);  		kfree(mtd->priv); -		del_mtd_device(mtd); +		mtd_device_unregister(mtd);  		kfree(mtd);  		found++;  	} diff --git a/drivers/mtd/devices/serial_flash_cmds.h b/drivers/mtd/devices/serial_flash_cmds.h new file mode 100644 index 00000000000..f59a125295d --- /dev/null +++ b/drivers/mtd/devices/serial_flash_cmds.h @@ -0,0 +1,61 @@ +/* + * Generic/SFDP Flash Commands and Device Capabilities + * + * Copyright (C) 2013 Lee Jones <lee.jones@lianro.org> + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef _MTD_SERIAL_FLASH_CMDS_H +#define _MTD_SERIAL_FLASH_CMDS_H + +/* Generic Flash Commands/OPCODEs */ +#define SPINOR_OP_RDSR2		0x35 +#define SPINOR_OP_WRVCR		0x81 +#define SPINOR_OP_RDVCR		0x85 + +/* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */ +#define SPINOR_OP_READ_1_2_2	0xbb	/* DUAL I/O READ */ +#define SPINOR_OP_READ_1_4_4	0xeb	/* QUAD I/O READ */ + +#define SPINOR_OP_WRITE		0x02	/* PAGE PROGRAM */ +#define SPINOR_OP_WRITE_1_1_2	0xa2	/* DUAL INPUT PROGRAM */ +#define SPINOR_OP_WRITE_1_2_2	0xd2	/* DUAL INPUT EXT PROGRAM */ +#define SPINOR_OP_WRITE_1_1_4	0x32	/* QUAD INPUT PROGRAM */ +#define SPINOR_OP_WRITE_1_4_4	0x12	/* QUAD INPUT EXT PROGRAM */ + +/* READ commands with 32-bit addressing */ +#define SPINOR_OP_READ4_1_2_2	0xbc +#define SPINOR_OP_READ4_1_4_4	0xec + +/* Configuration flags */ +#define FLASH_FLAG_SINGLE	0x000000ff +#define FLASH_FLAG_READ_WRITE	0x00000001 +#define FLASH_FLAG_READ_FAST	0x00000002 +#define FLASH_FLAG_SE_4K	0x00000004 +#define FLASH_FLAG_SE_32K	0x00000008 +#define FLASH_FLAG_CE		0x00000010 +#define FLASH_FLAG_32BIT_ADDR	0x00000020 +#define FLASH_FLAG_RESET	0x00000040 +#define FLASH_FLAG_DYB_LOCKING	0x00000080 + +#define FLASH_FLAG_DUAL		0x0000ff00 +#define FLASH_FLAG_READ_1_1_2	0x00000100 +#define FLASH_FLAG_READ_1_2_2	0x00000200 +#define FLASH_FLAG_READ_2_2_2	0x00000400 +#define FLASH_FLAG_WRITE_1_1_2	0x00001000 +#define FLASH_FLAG_WRITE_1_2_2	0x00002000 +#define FLASH_FLAG_WRITE_2_2_2	0x00004000 + +#define FLASH_FLAG_QUAD		0x00ff0000 +#define FLASH_FLAG_READ_1_1_4	0x00010000 +#define FLASH_FLAG_READ_1_4_4	0x00020000 +#define FLASH_FLAG_READ_4_4_4	0x00040000 +#define FLASH_FLAG_WRITE_1_1_4	0x00100000 +#define FLASH_FLAG_WRITE_1_4_4	0x00200000 +#define FLASH_FLAG_WRITE_4_4_4	0x00400000 + +#endif /* _MTD_SERIAL_FLASH_CMDS_H */ diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c index 592016a0668..2fc4957cbe7 100644 --- a/drivers/mtd/devices/slram.c +++ b/drivers/mtd/devices/slram.c @@ -42,7 +42,6 @@  #include <linux/ioctl.h>  #include <linux/init.h>  #include <asm/io.h> -#include <asm/system.h>  #include <linux/mtd/mtd.h> @@ -76,7 +75,7 @@ static slram_mtd_list_t *slram_mtdlist = NULL;  static int slram_erase(struct mtd_info *, struct erase_info *);  static int slram_point(struct mtd_info *, loff_t, size_t, size_t *, void **,  		resource_size_t *); -static void slram_unpoint(struct mtd_info *, loff_t, size_t); +static int slram_unpoint(struct mtd_info *, loff_t, size_t);  static int slram_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *);  static int slram_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); @@ -84,21 +83,13 @@ static int slram_erase(struct mtd_info *mtd, struct erase_info *instr)  {  	slram_priv_t *priv = mtd->priv; -	if (instr->addr + instr->len > mtd->size) { -		return(-EINVAL); -	} -  	memset(priv->start + instr->addr, 0xff, instr->len); -  	/* This'll catch a few races. Free the thing before returning :)  	 * I don't feel at all ashamed. This kind of thing is possible anyway  	 * with flash, but unlikely.  	 */ -  	instr->state = MTD_ERASE_DONE; -  	mtd_erase_callback(instr); -  	return(0);  } @@ -107,20 +98,14 @@ static int slram_point(struct mtd_info *mtd, loff_t from, size_t len,  {  	slram_priv_t *priv = mtd->priv; -	/* can we return a physical address with this driver? */ -	if (phys) -		return -EINVAL; - -	if (from + len > mtd->size) -		return -EINVAL; -  	*virt = priv->start + from;  	*retlen = len;  	return(0);  } -static void slram_unpoint(struct mtd_info *mtd, loff_t from, size_t len) +static int slram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)  { +	return 0;  }  static int slram_read(struct mtd_info *mtd, loff_t from, size_t len, @@ -128,14 +113,7 @@ static int slram_read(struct mtd_info *mtd, loff_t from, size_t len,  {  	slram_priv_t *priv = mtd->priv; -	if (from > mtd->size) -		return -EINVAL; - -	if (from + len > mtd->size) -		len = mtd->size - from; -  	memcpy(buf, priv->start + from, len); -  	*retlen = len;  	return(0);  } @@ -145,11 +123,7 @@ static int slram_write(struct mtd_info *mtd, loff_t to, size_t len,  {  	slram_priv_t *priv = mtd->priv; -	if (to + len > mtd->size) -		return -EINVAL; -  	memcpy(priv->start + to, buf, len); -  	*retlen = len;  	return(0);  } @@ -200,17 +174,17 @@ static int register_device(char *name, unsigned long start, unsigned long length  	(*curmtd)->mtdinfo->name = name;  	(*curmtd)->mtdinfo->size = length;  	(*curmtd)->mtdinfo->flags = MTD_CAP_RAM; -        (*curmtd)->mtdinfo->erase = slram_erase; -	(*curmtd)->mtdinfo->point = slram_point; -	(*curmtd)->mtdinfo->unpoint = slram_unpoint; -	(*curmtd)->mtdinfo->read = slram_read; -	(*curmtd)->mtdinfo->write = slram_write; +	(*curmtd)->mtdinfo->_erase = slram_erase; +	(*curmtd)->mtdinfo->_point = slram_point; +	(*curmtd)->mtdinfo->_unpoint = slram_unpoint; +	(*curmtd)->mtdinfo->_read = slram_read; +	(*curmtd)->mtdinfo->_write = slram_write;  	(*curmtd)->mtdinfo->owner = THIS_MODULE;  	(*curmtd)->mtdinfo->type = MTD_RAM;  	(*curmtd)->mtdinfo->erasesize = SLRAM_BLK_SZ;  	(*curmtd)->mtdinfo->writesize = 1; -	if (add_mtd_device((*curmtd)->mtdinfo))	{ +	if (mtd_device_register((*curmtd)->mtdinfo, NULL, 0))	{  		E("slram: Failed to register new device\n");  		iounmap(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start);  		kfree((*curmtd)->mtdinfo->priv); @@ -231,7 +205,7 @@ static void unregister_devices(void)  	while (slram_mtdlist) {  		nextitem = slram_mtdlist->next; -		del_mtd_device(slram_mtdlist->mtdinfo); +		mtd_device_unregister(slram_mtdlist->mtdinfo);  		iounmap(((slram_priv_t *)slram_mtdlist->mtdinfo->priv)->start);  		kfree(slram_mtdlist->mtdinfo->priv);  		kfree(slram_mtdlist->mtdinfo); @@ -266,7 +240,7 @@ static int parse_cmdline(char *devname, char *szstart, char *szlength)  	if (*(szlength) != '+') {  		devlength = simple_strtoul(szlength, &buffer, 0); -		devlength = handle_unit(devlength, buffer) - devstart; +		devlength = handle_unit(devlength, buffer);  		if (devlength < devstart)  			goto err_out; @@ -306,14 +280,11 @@ __setup("slram=", mtd_slram_setup);  static int __init init_slram(void)  {  	char *devname; -	int i;  #ifndef MODULE  	char *devstart;  	char *devlength; -	i = 0; -  	if (!map) {  		E("slram: not enough parameters.\n");  		return(-EINVAL); @@ -340,6 +311,7 @@ static int __init init_slram(void)  	}  #else  	int count; +	int i;  	for (count = 0; count < SLRAM_MAX_DEVICES_PARAMS && map[count];  			count++) { diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c new file mode 100644 index 00000000000..c4176b0f382 --- /dev/null +++ b/drivers/mtd/devices/spear_smi.c @@ -0,0 +1,1093 @@ +/* + * SMI (Serial Memory Controller) device driver for Serial NOR Flash on + * SPEAr platform + * The serial nor interface is largely based on drivers/mtd/m25p80.c, + * however the SPI interface has been replaced by SMI. + * + * Copyright © 2010 STMicroelectronics. + * Ashish Priyadarshi + * Shiraz Hashim <shiraz.linux.kernel@gmail.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/param.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/spear_smi.h> +#include <linux/mutex.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/wait.h> +#include <linux/of.h> +#include <linux/of_address.h> + +/* SMI clock rate */ +#define SMI_MAX_CLOCK_FREQ	50000000 /* 50 MHz */ + +/* MAX time out to safely come out of a erase or write busy conditions */ +#define SMI_PROBE_TIMEOUT	(HZ / 10) +#define SMI_MAX_TIME_OUT	(3 * HZ) + +/* timeout for command completion */ +#define SMI_CMD_TIMEOUT		(HZ / 10) + +/* registers of smi */ +#define SMI_CR1		0x0	/* SMI control register 1 */ +#define SMI_CR2		0x4	/* SMI control register 2 */ +#define SMI_SR		0x8	/* SMI status register */ +#define SMI_TR		0xC	/* SMI transmit register */ +#define SMI_RR		0x10	/* SMI receive register */ + +/* defines for control_reg 1 */ +#define BANK_EN		(0xF << 0)	/* enables all banks */ +#define DSEL_TIME	(0x6 << 4)	/* Deselect time 6 + 1 SMI_CK periods */ +#define SW_MODE		(0x1 << 28)	/* enables SW Mode */ +#define WB_MODE		(0x1 << 29)	/* Write Burst Mode */ +#define FAST_MODE	(0x1 << 15)	/* Fast Mode */ +#define HOLD1		(0x1 << 16)	/* Clock Hold period selection */ + +/* defines for control_reg 2 */ +#define SEND		(0x1 << 7)	/* Send data */ +#define TFIE		(0x1 << 8)	/* Transmission Flag Interrupt Enable */ +#define WCIE		(0x1 << 9)	/* Write Complete Interrupt Enable */ +#define RD_STATUS_REG	(0x1 << 10)	/* reads status reg */ +#define WE		(0x1 << 11)	/* Write Enable */ + +#define TX_LEN_SHIFT	0 +#define RX_LEN_SHIFT	4 +#define BANK_SHIFT	12 + +/* defines for status register */ +#define SR_WIP		0x1	/* Write in progress */ +#define SR_WEL		0x2	/* Write enable latch */ +#define SR_BP0		0x4	/* Block protect 0 */ +#define SR_BP1		0x8	/* Block protect 1 */ +#define SR_BP2		0x10	/* Block protect 2 */ +#define SR_SRWD		0x80	/* SR write protect */ +#define TFF		0x100	/* Transfer Finished Flag */ +#define WCF		0x200	/* Transfer Finished Flag */ +#define ERF1		0x400	/* Forbidden Write Request */ +#define ERF2		0x800	/* Forbidden Access */ + +#define WM_SHIFT	12 + +/* flash opcodes */ +#define OPCODE_RDID	0x9f	/* Read JEDEC ID */ + +/* Flash Device Ids maintenance section */ + +/* data structure to maintain flash ids from different vendors */ +struct flash_device { +	char *name; +	u8 erase_cmd; +	u32 device_id; +	u32 pagesize; +	unsigned long sectorsize; +	unsigned long size_in_bytes; +}; + +#define FLASH_ID(n, es, id, psize, ssize, size)	\ +{				\ +	.name = n,		\ +	.erase_cmd = es,	\ +	.device_id = id,	\ +	.pagesize = psize,	\ +	.sectorsize = ssize,	\ +	.size_in_bytes = size	\ +} + +static struct flash_device flash_devices[] = { +	FLASH_ID("st m25p16"     , 0xd8, 0x00152020, 0x100, 0x10000, 0x200000), +	FLASH_ID("st m25p32"     , 0xd8, 0x00162020, 0x100, 0x10000, 0x400000), +	FLASH_ID("st m25p64"     , 0xd8, 0x00172020, 0x100, 0x10000, 0x800000), +	FLASH_ID("st m25p128"    , 0xd8, 0x00182020, 0x100, 0x40000, 0x1000000), +	FLASH_ID("st m25p05"     , 0xd8, 0x00102020, 0x80 , 0x8000 , 0x10000), +	FLASH_ID("st m25p10"     , 0xd8, 0x00112020, 0x80 , 0x8000 , 0x20000), +	FLASH_ID("st m25p20"     , 0xd8, 0x00122020, 0x100, 0x10000, 0x40000), +	FLASH_ID("st m25p40"     , 0xd8, 0x00132020, 0x100, 0x10000, 0x80000), +	FLASH_ID("st m25p80"     , 0xd8, 0x00142020, 0x100, 0x10000, 0x100000), +	FLASH_ID("st m45pe10"    , 0xd8, 0x00114020, 0x100, 0x10000, 0x20000), +	FLASH_ID("st m45pe20"    , 0xd8, 0x00124020, 0x100, 0x10000, 0x40000), +	FLASH_ID("st m45pe40"    , 0xd8, 0x00134020, 0x100, 0x10000, 0x80000), +	FLASH_ID("st m45pe80"    , 0xd8, 0x00144020, 0x100, 0x10000, 0x100000), +	FLASH_ID("sp s25fl004"   , 0xd8, 0x00120201, 0x100, 0x10000, 0x80000), +	FLASH_ID("sp s25fl008"   , 0xd8, 0x00130201, 0x100, 0x10000, 0x100000), +	FLASH_ID("sp s25fl016"   , 0xd8, 0x00140201, 0x100, 0x10000, 0x200000), +	FLASH_ID("sp s25fl032"   , 0xd8, 0x00150201, 0x100, 0x10000, 0x400000), +	FLASH_ID("sp s25fl064"   , 0xd8, 0x00160201, 0x100, 0x10000, 0x800000), +	FLASH_ID("atmel 25f512"  , 0x52, 0x0065001F, 0x80 , 0x8000 , 0x10000), +	FLASH_ID("atmel 25f1024" , 0x52, 0x0060001F, 0x100, 0x8000 , 0x20000), +	FLASH_ID("atmel 25f2048" , 0x52, 0x0063001F, 0x100, 0x10000, 0x40000), +	FLASH_ID("atmel 25f4096" , 0x52, 0x0064001F, 0x100, 0x10000, 0x80000), +	FLASH_ID("atmel 25fs040" , 0xd7, 0x0004661F, 0x100, 0x10000, 0x80000), +	FLASH_ID("mac 25l512"    , 0xd8, 0x001020C2, 0x010, 0x10000, 0x10000), +	FLASH_ID("mac 25l1005"   , 0xd8, 0x001120C2, 0x010, 0x10000, 0x20000), +	FLASH_ID("mac 25l2005"   , 0xd8, 0x001220C2, 0x010, 0x10000, 0x40000), +	FLASH_ID("mac 25l4005"   , 0xd8, 0x001320C2, 0x010, 0x10000, 0x80000), +	FLASH_ID("mac 25l4005a"  , 0xd8, 0x001320C2, 0x010, 0x10000, 0x80000), +	FLASH_ID("mac 25l8005"   , 0xd8, 0x001420C2, 0x010, 0x10000, 0x100000), +	FLASH_ID("mac 25l1605"   , 0xd8, 0x001520C2, 0x100, 0x10000, 0x200000), +	FLASH_ID("mac 25l1605a"  , 0xd8, 0x001520C2, 0x010, 0x10000, 0x200000), +	FLASH_ID("mac 25l3205"   , 0xd8, 0x001620C2, 0x100, 0x10000, 0x400000), +	FLASH_ID("mac 25l3205a"  , 0xd8, 0x001620C2, 0x100, 0x10000, 0x400000), +	FLASH_ID("mac 25l6405"   , 0xd8, 0x001720C2, 0x100, 0x10000, 0x800000), +}; + +/* Define spear specific structures */ + +struct spear_snor_flash; + +/** + * struct spear_smi - Structure for SMI Device + * + * @clk: functional clock + * @status: current status register of SMI. + * @clk_rate: functional clock rate of SMI (default: SMI_MAX_CLOCK_FREQ) + * @lock: lock to prevent parallel access of SMI. + * @io_base: base address for registers of SMI. + * @pdev: platform device + * @cmd_complete: queue to wait for command completion of NOR-flash. + * @num_flashes: number of flashes actually present on board. + * @flash: separate structure for each Serial NOR-flash attached to SMI. + */ +struct spear_smi { +	struct clk *clk; +	u32 status; +	unsigned long clk_rate; +	struct mutex lock; +	void __iomem *io_base; +	struct platform_device *pdev; +	wait_queue_head_t cmd_complete; +	u32 num_flashes; +	struct spear_snor_flash *flash[MAX_NUM_FLASH_CHIP]; +}; + +/** + * struct spear_snor_flash - Structure for Serial NOR Flash + * + * @bank: Bank number(0, 1, 2, 3) for each NOR-flash. + * @dev_id: Device ID of NOR-flash. + * @lock: lock to manage flash read, write and erase operations + * @mtd: MTD info for each NOR-flash. + * @num_parts: Total number of partition in each bank of NOR-flash. + * @parts: Partition info for each bank of NOR-flash. + * @page_size: Page size of NOR-flash. + * @base_addr: Base address of NOR-flash. + * @erase_cmd: erase command may vary on different flash types + * @fast_mode: flash supports read in fast mode + */ +struct spear_snor_flash { +	u32 bank; +	u32 dev_id; +	struct mutex lock; +	struct mtd_info mtd; +	u32 num_parts; +	struct mtd_partition *parts; +	u32 page_size; +	void __iomem *base_addr; +	u8 erase_cmd; +	u8 fast_mode; +}; + +static inline struct spear_snor_flash *get_flash_data(struct mtd_info *mtd) +{ +	return container_of(mtd, struct spear_snor_flash, mtd); +} + +/** + * spear_smi_read_sr - Read status register of flash through SMI + * @dev: structure of SMI information. + * @bank: bank to which flash is connected + * + * This routine will return the status register of the flash chip present at the + * given bank. + */ +static int spear_smi_read_sr(struct spear_smi *dev, u32 bank) +{ +	int ret; +	u32 ctrlreg1; + +	mutex_lock(&dev->lock); +	dev->status = 0; /* Will be set in interrupt handler */ + +	ctrlreg1 = readl(dev->io_base + SMI_CR1); +	/* program smi in hw mode */ +	writel(ctrlreg1 & ~(SW_MODE | WB_MODE), dev->io_base + SMI_CR1); + +	/* performing a rsr instruction in hw mode */ +	writel((bank << BANK_SHIFT) | RD_STATUS_REG | TFIE, +			dev->io_base + SMI_CR2); + +	/* wait for tff */ +	ret = wait_event_interruptible_timeout(dev->cmd_complete, +			dev->status & TFF, SMI_CMD_TIMEOUT); + +	/* copy dev->status (lower 16 bits) in order to release lock */ +	if (ret > 0) +		ret = dev->status & 0xffff; +	else if (ret == 0) +		ret = -ETIMEDOUT; + +	/* restore the ctrl regs state */ +	writel(ctrlreg1, dev->io_base + SMI_CR1); +	writel(0, dev->io_base + SMI_CR2); +	mutex_unlock(&dev->lock); + +	return ret; +} + +/** + * spear_smi_wait_till_ready - wait till flash is ready + * @dev: structure of SMI information. + * @bank: flash corresponding to this bank + * @timeout: timeout for busy wait condition + * + * This routine checks for WIP (write in progress) bit in Status register + * If successful the routine returns 0 else -EBUSY + */ +static int spear_smi_wait_till_ready(struct spear_smi *dev, u32 bank, +		unsigned long timeout) +{ +	unsigned long finish; +	int status; + +	finish = jiffies + timeout; +	do { +		status = spear_smi_read_sr(dev, bank); +		if (status < 0) { +			if (status == -ETIMEDOUT) +				continue; /* try till finish */ +			return status; +		} else if (!(status & SR_WIP)) { +			return 0; +		} + +		cond_resched(); +	} while (!time_after_eq(jiffies, finish)); + +	dev_err(&dev->pdev->dev, "smi controller is busy, timeout\n"); +	return -EBUSY; +} + +/** + * spear_smi_int_handler - SMI Interrupt Handler. + * @irq: irq number + * @dev_id: structure of SMI device, embedded in dev_id. + * + * The handler clears all interrupt conditions and records the status in + * dev->status which is used by the driver later. + */ +static irqreturn_t spear_smi_int_handler(int irq, void *dev_id) +{ +	u32 status = 0; +	struct spear_smi *dev = dev_id; + +	status = readl(dev->io_base + SMI_SR); + +	if (unlikely(!status)) +		return IRQ_NONE; + +	/* clear all interrupt conditions */ +	writel(0, dev->io_base + SMI_SR); + +	/* copy the status register in dev->status */ +	dev->status |= status; + +	/* send the completion */ +	wake_up_interruptible(&dev->cmd_complete); + +	return IRQ_HANDLED; +} + +/** + * spear_smi_hw_init - initializes the smi controller. + * @dev: structure of smi device + * + * this routine initializes the smi controller wit the default values + */ +static void spear_smi_hw_init(struct spear_smi *dev) +{ +	unsigned long rate = 0; +	u32 prescale = 0; +	u32 val; + +	rate = clk_get_rate(dev->clk); + +	/* functional clock of smi */ +	prescale = DIV_ROUND_UP(rate, dev->clk_rate); + +	/* +	 * setting the standard values, fast mode, prescaler for +	 * SMI_MAX_CLOCK_FREQ (50MHz) operation and bank enable +	 */ +	val = HOLD1 | BANK_EN | DSEL_TIME | (prescale << 8); + +	mutex_lock(&dev->lock); +	/* clear all interrupt conditions */ +	writel(0, dev->io_base + SMI_SR); + +	writel(val, dev->io_base + SMI_CR1); +	mutex_unlock(&dev->lock); +} + +/** + * get_flash_index - match chip id from a flash list. + * @flash_id: a valid nor flash chip id obtained from board. + * + * try to validate the chip id by matching from a list, if not found then simply + * returns negative. In case of success returns index in to the flash devices + * array. + */ +static int get_flash_index(u32 flash_id) +{ +	int index; + +	/* Matches chip-id to entire list of 'serial-nor flash' ids */ +	for (index = 0; index < ARRAY_SIZE(flash_devices); index++) { +		if (flash_devices[index].device_id == flash_id) +			return index; +	} + +	/* Memory chip is not listed and not supported */ +	return -ENODEV; +} + +/** + * spear_smi_write_enable - Enable the flash to do write operation + * @dev: structure of SMI device + * @bank: enable write for flash connected to this bank + * + * Set write enable latch with Write Enable command. + * Returns 0 on success. + */ +static int spear_smi_write_enable(struct spear_smi *dev, u32 bank) +{ +	int ret; +	u32 ctrlreg1; + +	mutex_lock(&dev->lock); +	dev->status = 0; /* Will be set in interrupt handler */ + +	ctrlreg1 = readl(dev->io_base + SMI_CR1); +	/* program smi in h/w mode */ +	writel(ctrlreg1 & ~SW_MODE, dev->io_base + SMI_CR1); + +	/* give the flash, write enable command */ +	writel((bank << BANK_SHIFT) | WE | TFIE, dev->io_base + SMI_CR2); + +	ret = wait_event_interruptible_timeout(dev->cmd_complete, +			dev->status & TFF, SMI_CMD_TIMEOUT); + +	/* restore the ctrl regs state */ +	writel(ctrlreg1, dev->io_base + SMI_CR1); +	writel(0, dev->io_base + SMI_CR2); + +	if (ret == 0) { +		ret = -EIO; +		dev_err(&dev->pdev->dev, +			"smi controller failed on write enable\n"); +	} else if (ret > 0) { +		/* check whether write mode status is set for required bank */ +		if (dev->status & (1 << (bank + WM_SHIFT))) +			ret = 0; +		else { +			dev_err(&dev->pdev->dev, "couldn't enable write\n"); +			ret = -EIO; +		} +	} + +	mutex_unlock(&dev->lock); +	return ret; +} + +static inline u32 +get_sector_erase_cmd(struct spear_snor_flash *flash, u32 offset) +{ +	u32 cmd; +	u8 *x = (u8 *)&cmd; + +	x[0] = flash->erase_cmd; +	x[1] = offset >> 16; +	x[2] = offset >> 8; +	x[3] = offset; + +	return cmd; +} + +/** + * spear_smi_erase_sector - erase one sector of flash + * @dev: structure of SMI information + * @command: erase command to be send + * @bank: bank to which this command needs to be send + * @bytes: size of command + * + * Erase one sector of flash memory at offset ``offset'' which is any + * address within the sector which should be erased. + * Returns 0 if successful, non-zero otherwise. + */ +static int spear_smi_erase_sector(struct spear_smi *dev, +		u32 bank, u32 command, u32 bytes) +{ +	u32 ctrlreg1 = 0; +	int ret; + +	ret = spear_smi_wait_till_ready(dev, bank, SMI_MAX_TIME_OUT); +	if (ret) +		return ret; + +	ret = spear_smi_write_enable(dev, bank); +	if (ret) +		return ret; + +	mutex_lock(&dev->lock); + +	ctrlreg1 = readl(dev->io_base + SMI_CR1); +	writel((ctrlreg1 | SW_MODE) & ~WB_MODE, dev->io_base + SMI_CR1); + +	/* send command in sw mode */ +	writel(command, dev->io_base + SMI_TR); + +	writel((bank << BANK_SHIFT) | SEND | TFIE | (bytes << TX_LEN_SHIFT), +			dev->io_base + SMI_CR2); + +	ret = wait_event_interruptible_timeout(dev->cmd_complete, +			dev->status & TFF, SMI_CMD_TIMEOUT); + +	if (ret == 0) { +		ret = -EIO; +		dev_err(&dev->pdev->dev, "sector erase failed\n"); +	} else if (ret > 0) +		ret = 0; /* success */ + +	/* restore ctrl regs */ +	writel(ctrlreg1, dev->io_base + SMI_CR1); +	writel(0, dev->io_base + SMI_CR2); + +	mutex_unlock(&dev->lock); +	return ret; +} + +/** + * spear_mtd_erase - perform flash erase operation as requested by user + * @mtd: Provides the memory characteristics + * @e_info: Provides the erase information + * + * Erase an address range on the flash chip. The address range may extend + * one or more erase sectors. Return an error is there is a problem erasing. + */ +static int spear_mtd_erase(struct mtd_info *mtd, struct erase_info *e_info) +{ +	struct spear_snor_flash *flash = get_flash_data(mtd); +	struct spear_smi *dev = mtd->priv; +	u32 addr, command, bank; +	int len, ret; + +	if (!flash || !dev) +		return -ENODEV; + +	bank = flash->bank; +	if (bank > dev->num_flashes - 1) { +		dev_err(&dev->pdev->dev, "Invalid Bank Num"); +		return -EINVAL; +	} + +	addr = e_info->addr; +	len = e_info->len; + +	mutex_lock(&flash->lock); + +	/* now erase sectors in loop */ +	while (len) { +		command = get_sector_erase_cmd(flash, addr); +		/* preparing the command for flash */ +		ret = spear_smi_erase_sector(dev, bank, command, 4); +		if (ret) { +			e_info->state = MTD_ERASE_FAILED; +			mutex_unlock(&flash->lock); +			return ret; +		} +		addr += mtd->erasesize; +		len -= mtd->erasesize; +	} + +	mutex_unlock(&flash->lock); +	e_info->state = MTD_ERASE_DONE; +	mtd_erase_callback(e_info); + +	return 0; +} + +/** + * spear_mtd_read - performs flash read operation as requested by the user + * @mtd: MTD information of the memory bank + * @from: Address from which to start read + * @len: Number of bytes to be read + * @retlen: Fills the Number of bytes actually read + * @buf: Fills this after reading + * + * Read an address range from the flash chip. The address range + * may be any size provided it is within the physical boundaries. + * Returns 0 on success, non zero otherwise + */ +static int spear_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, +		size_t *retlen, u8 *buf) +{ +	struct spear_snor_flash *flash = get_flash_data(mtd); +	struct spear_smi *dev = mtd->priv; +	void __iomem *src; +	u32 ctrlreg1, val; +	int ret; + +	if (!flash || !dev) +		return -ENODEV; + +	if (flash->bank > dev->num_flashes - 1) { +		dev_err(&dev->pdev->dev, "Invalid Bank Num"); +		return -EINVAL; +	} + +	/* select address as per bank number */ +	src = flash->base_addr + from; + +	mutex_lock(&flash->lock); + +	/* wait till previous write/erase is done. */ +	ret = spear_smi_wait_till_ready(dev, flash->bank, SMI_MAX_TIME_OUT); +	if (ret) { +		mutex_unlock(&flash->lock); +		return ret; +	} + +	mutex_lock(&dev->lock); +	/* put smi in hw mode not wbt mode */ +	ctrlreg1 = val = readl(dev->io_base + SMI_CR1); +	val &= ~(SW_MODE | WB_MODE); +	if (flash->fast_mode) +		val |= FAST_MODE; + +	writel(val, dev->io_base + SMI_CR1); + +	memcpy_fromio(buf, src, len); + +	/* restore ctrl reg1 */ +	writel(ctrlreg1, dev->io_base + SMI_CR1); +	mutex_unlock(&dev->lock); + +	*retlen = len; +	mutex_unlock(&flash->lock); + +	return 0; +} + +static inline int spear_smi_cpy_toio(struct spear_smi *dev, u32 bank, +		void __iomem *dest, const void *src, size_t len) +{ +	int ret; +	u32 ctrlreg1; + +	/* wait until finished previous write command. */ +	ret = spear_smi_wait_till_ready(dev, bank, SMI_MAX_TIME_OUT); +	if (ret) +		return ret; + +	/* put smi in write enable */ +	ret = spear_smi_write_enable(dev, bank); +	if (ret) +		return ret; + +	/* put smi in hw, write burst mode */ +	mutex_lock(&dev->lock); + +	ctrlreg1 = readl(dev->io_base + SMI_CR1); +	writel((ctrlreg1 | WB_MODE) & ~SW_MODE, dev->io_base + SMI_CR1); + +	memcpy_toio(dest, src, len); + +	writel(ctrlreg1, dev->io_base + SMI_CR1); + +	mutex_unlock(&dev->lock); +	return 0; +} + +/** + * spear_mtd_write - performs write operation as requested by the user. + * @mtd: MTD information of the memory bank. + * @to:	Address to write. + * @len: Number of bytes to be written. + * @retlen: Number of bytes actually wrote. + * @buf: Buffer from which the data to be taken. + * + * Write an address range to the flash chip. Data must be written in + * flash_page_size chunks. The address range may be any size provided + * it is within the physical boundaries. + * Returns 0 on success, non zero otherwise + */ +static int spear_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, +		size_t *retlen, const u8 *buf) +{ +	struct spear_snor_flash *flash = get_flash_data(mtd); +	struct spear_smi *dev = mtd->priv; +	void __iomem *dest; +	u32 page_offset, page_size; +	int ret; + +	if (!flash || !dev) +		return -ENODEV; + +	if (flash->bank > dev->num_flashes - 1) { +		dev_err(&dev->pdev->dev, "Invalid Bank Num"); +		return -EINVAL; +	} + +	/* select address as per bank number */ +	dest = flash->base_addr + to; +	mutex_lock(&flash->lock); + +	page_offset = (u32)to % flash->page_size; + +	/* do if all the bytes fit onto one page */ +	if (page_offset + len <= flash->page_size) { +		ret = spear_smi_cpy_toio(dev, flash->bank, dest, buf, len); +		if (!ret) +			*retlen += len; +	} else { +		u32 i; + +		/* the size of data remaining on the first page */ +		page_size = flash->page_size - page_offset; + +		ret = spear_smi_cpy_toio(dev, flash->bank, dest, buf, +				page_size); +		if (ret) +			goto err_write; +		else +			*retlen += page_size; + +		/* write everything in pagesize chunks */ +		for (i = page_size; i < len; i += page_size) { +			page_size = len - i; +			if (page_size > flash->page_size) +				page_size = flash->page_size; + +			ret = spear_smi_cpy_toio(dev, flash->bank, dest + i, +					buf + i, page_size); +			if (ret) +				break; +			else +				*retlen += page_size; +		} +	} + +err_write: +	mutex_unlock(&flash->lock); + +	return ret; +} + +/** + * spear_smi_probe_flash - Detects the NOR Flash chip. + * @dev: structure of SMI information. + * @bank: bank on which flash must be probed + * + * This routine will check whether there exists a flash chip on a given memory + * bank ID. + * Return index of the probed flash in flash devices structure + */ +static int spear_smi_probe_flash(struct spear_smi *dev, u32 bank) +{ +	int ret; +	u32 val = 0; + +	ret = spear_smi_wait_till_ready(dev, bank, SMI_PROBE_TIMEOUT); +	if (ret) +		return ret; + +	mutex_lock(&dev->lock); + +	dev->status = 0; /* Will be set in interrupt handler */ +	/* put smi in sw mode */ +	val = readl(dev->io_base + SMI_CR1); +	writel(val | SW_MODE, dev->io_base + SMI_CR1); + +	/* send readid command in sw mode */ +	writel(OPCODE_RDID, dev->io_base + SMI_TR); + +	val = (bank << BANK_SHIFT) | SEND | (1 << TX_LEN_SHIFT) | +		(3 << RX_LEN_SHIFT) | TFIE; +	writel(val, dev->io_base + SMI_CR2); + +	/* wait for TFF */ +	ret = wait_event_interruptible_timeout(dev->cmd_complete, +			dev->status & TFF, SMI_CMD_TIMEOUT); +	if (ret <= 0) { +		ret = -ENODEV; +		goto err_probe; +	} + +	/* get memory chip id */ +	val = readl(dev->io_base + SMI_RR); +	val &= 0x00ffffff; +	ret = get_flash_index(val); + +err_probe: +	/* clear sw mode */ +	val = readl(dev->io_base + SMI_CR1); +	writel(val & ~SW_MODE, dev->io_base + SMI_CR1); + +	mutex_unlock(&dev->lock); +	return ret; +} + + +#ifdef CONFIG_OF +static int spear_smi_probe_config_dt(struct platform_device *pdev, +				     struct device_node *np) +{ +	struct spear_smi_plat_data *pdata = dev_get_platdata(&pdev->dev); +	struct device_node *pp = NULL; +	const __be32 *addr; +	u32 val; +	int len; +	int i = 0; + +	if (!np) +		return -ENODEV; + +	of_property_read_u32(np, "clock-rate", &val); +	pdata->clk_rate = val; + +	pdata->board_flash_info = devm_kzalloc(&pdev->dev, +					       sizeof(*pdata->board_flash_info), +					       GFP_KERNEL); + +	/* 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 */ +		addr = of_get_property(pp, "reg", &len); +		pdata->board_flash_info->mem_base = be32_to_cpup(&addr[0]); +		pdata->board_flash_info->size = be32_to_cpup(&addr[1]); + +		if (of_get_property(pp, "st,smi-fast-mode", NULL)) +			pdata->board_flash_info->fast_mode = 1; + +		i++; +	} + +	pdata->num_flashes = i; + +	return 0; +} +#else +static int spear_smi_probe_config_dt(struct platform_device *pdev, +				     struct device_node *np) +{ +	return -ENOSYS; +} +#endif + +static int spear_smi_setup_banks(struct platform_device *pdev, +				 u32 bank, struct device_node *np) +{ +	struct spear_smi *dev = platform_get_drvdata(pdev); +	struct mtd_part_parser_data ppdata = {}; +	struct spear_smi_flash_info *flash_info; +	struct spear_smi_plat_data *pdata; +	struct spear_snor_flash *flash; +	struct mtd_partition *parts = NULL; +	int count = 0; +	int flash_index; +	int ret = 0; + +	pdata = dev_get_platdata(&pdev->dev); +	if (bank > pdata->num_flashes - 1) +		return -EINVAL; + +	flash_info = &pdata->board_flash_info[bank]; +	if (!flash_info) +		return -ENODEV; + +	flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_ATOMIC); +	if (!flash) +		return -ENOMEM; +	flash->bank = bank; +	flash->fast_mode = flash_info->fast_mode ? 1 : 0; +	mutex_init(&flash->lock); + +	/* verify whether nor flash is really present on board */ +	flash_index = spear_smi_probe_flash(dev, bank); +	if (flash_index < 0) { +		dev_info(&dev->pdev->dev, "smi-nor%d not found\n", bank); +		return flash_index; +	} +	/* map the memory for nor flash chip */ +	flash->base_addr = devm_ioremap(&pdev->dev, flash_info->mem_base, +					flash_info->size); +	if (!flash->base_addr) +		return -EIO; + +	dev->flash[bank] = flash; +	flash->mtd.priv = dev; + +	if (flash_info->name) +		flash->mtd.name = flash_info->name; +	else +		flash->mtd.name = flash_devices[flash_index].name; + +	flash->mtd.type = MTD_NORFLASH; +	flash->mtd.writesize = 1; +	flash->mtd.flags = MTD_CAP_NORFLASH; +	flash->mtd.size = flash_info->size; +	flash->mtd.erasesize = flash_devices[flash_index].sectorsize; +	flash->page_size = flash_devices[flash_index].pagesize; +	flash->mtd.writebufsize = flash->page_size; +	flash->erase_cmd = flash_devices[flash_index].erase_cmd; +	flash->mtd._erase = spear_mtd_erase; +	flash->mtd._read = spear_mtd_read; +	flash->mtd._write = spear_mtd_write; +	flash->dev_id = flash_devices[flash_index].device_id; + +	dev_info(&dev->pdev->dev, "mtd .name=%s .size=%llx(%lluM)\n", +			flash->mtd.name, flash->mtd.size, +			flash->mtd.size / (1024 * 1024)); + +	dev_info(&dev->pdev->dev, ".erasesize = 0x%x(%uK)\n", +			flash->mtd.erasesize, flash->mtd.erasesize / 1024); + +#ifndef CONFIG_OF +	if (flash_info->partitions) { +		parts = flash_info->partitions; +		count = flash_info->nr_partitions; +	} +#endif +	ppdata.of_node = np; + +	ret = mtd_device_parse_register(&flash->mtd, NULL, &ppdata, parts, +					count); +	if (ret) { +		dev_err(&dev->pdev->dev, "Err MTD partition=%d\n", ret); +		return ret; +	} + +	return 0; +} + +/** + * spear_smi_probe - Entry routine + * @pdev: platform device structure + * + * This is the first routine which gets invoked during booting and does all + * initialization/allocation work. The routine looks for available memory banks, + * and do proper init for any found one. + * Returns 0 on success, non zero otherwise + */ +static int spear_smi_probe(struct platform_device *pdev) +{ +	struct device_node *np = pdev->dev.of_node; +	struct spear_smi_plat_data *pdata = NULL; +	struct spear_smi *dev; +	struct resource *smi_base; +	int irq, ret = 0; +	int i; + +	if (np) { +		pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); +		if (!pdata) { +			ret = -ENOMEM; +			goto err; +		} +		pdev->dev.platform_data = pdata; +		ret = spear_smi_probe_config_dt(pdev, np); +		if (ret) { +			ret = -ENODEV; +			dev_err(&pdev->dev, "no platform data\n"); +			goto err; +		} +	} else { +		pdata = dev_get_platdata(&pdev->dev); +		if (!pdata) { +			ret = -ENODEV; +			dev_err(&pdev->dev, "no platform data\n"); +			goto err; +		} +	} + +	irq = platform_get_irq(pdev, 0); +	if (irq < 0) { +		ret = -ENODEV; +		dev_err(&pdev->dev, "invalid smi irq\n"); +		goto err; +	} + +	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC); +	if (!dev) { +		ret = -ENOMEM; +		goto err; +	} + +	smi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); + +	dev->io_base = devm_ioremap_resource(&pdev->dev, smi_base); +	if (IS_ERR(dev->io_base)) { +		ret = PTR_ERR(dev->io_base); +		goto err; +	} + +	dev->pdev = pdev; +	dev->clk_rate = pdata->clk_rate; + +	if (dev->clk_rate > SMI_MAX_CLOCK_FREQ) +		dev->clk_rate = SMI_MAX_CLOCK_FREQ; + +	dev->num_flashes = pdata->num_flashes; + +	if (dev->num_flashes > MAX_NUM_FLASH_CHIP) { +		dev_err(&pdev->dev, "exceeding max number of flashes\n"); +		dev->num_flashes = MAX_NUM_FLASH_CHIP; +	} + +	dev->clk = devm_clk_get(&pdev->dev, NULL); +	if (IS_ERR(dev->clk)) { +		ret = PTR_ERR(dev->clk); +		goto err; +	} + +	ret = clk_prepare_enable(dev->clk); +	if (ret) +		goto err; + +	ret = devm_request_irq(&pdev->dev, irq, spear_smi_int_handler, 0, +			       pdev->name, dev); +	if (ret) { +		dev_err(&dev->pdev->dev, "SMI IRQ allocation failed\n"); +		goto err_irq; +	} + +	mutex_init(&dev->lock); +	init_waitqueue_head(&dev->cmd_complete); +	spear_smi_hw_init(dev); +	platform_set_drvdata(pdev, dev); + +	/* loop for each serial nor-flash which is connected to smi */ +	for (i = 0; i < dev->num_flashes; i++) { +		ret = spear_smi_setup_banks(pdev, i, pdata->np[i]); +		if (ret) { +			dev_err(&dev->pdev->dev, "bank setup failed\n"); +			goto err_irq; +		} +	} + +	return 0; + +err_irq: +	clk_disable_unprepare(dev->clk); +err: +	return ret; +} + +/** + * spear_smi_remove - Exit routine + * @pdev: platform device structure + * + * free all allocations and delete the partitions. + */ +static int spear_smi_remove(struct platform_device *pdev) +{ +	struct spear_smi *dev; +	struct spear_snor_flash *flash; +	int ret, i; + +	dev = platform_get_drvdata(pdev); +	if (!dev) { +		dev_err(&pdev->dev, "dev is null\n"); +		return -ENODEV; +	} + +	/* clean up for all nor flash */ +	for (i = 0; i < dev->num_flashes; i++) { +		flash = dev->flash[i]; +		if (!flash) +			continue; + +		/* clean up mtd stuff */ +		ret = mtd_device_unregister(&flash->mtd); +		if (ret) +			dev_err(&pdev->dev, "error removing mtd\n"); +	} + +	clk_disable_unprepare(dev->clk); + +	return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int spear_smi_suspend(struct device *dev) +{ +	struct spear_smi *sdev = dev_get_drvdata(dev); + +	if (sdev && sdev->clk) +		clk_disable_unprepare(sdev->clk); + +	return 0; +} + +static int spear_smi_resume(struct device *dev) +{ +	struct spear_smi *sdev = dev_get_drvdata(dev); +	int ret = -EPERM; + +	if (sdev && sdev->clk) +		ret = clk_prepare_enable(sdev->clk); + +	if (!ret) +		spear_smi_hw_init(sdev); +	return ret; +} +#endif + +static SIMPLE_DEV_PM_OPS(spear_smi_pm_ops, spear_smi_suspend, spear_smi_resume); + +#ifdef CONFIG_OF +static const struct of_device_id spear_smi_id_table[] = { +	{ .compatible = "st,spear600-smi" }, +	{} +}; +MODULE_DEVICE_TABLE(of, spear_smi_id_table); +#endif + +static struct platform_driver spear_smi_driver = { +	.driver = { +		.name = "smi", +		.bus = &platform_bus_type, +		.owner = THIS_MODULE, +		.of_match_table = of_match_ptr(spear_smi_id_table), +		.pm = &spear_smi_pm_ops, +	}, +	.probe = spear_smi_probe, +	.remove = spear_smi_remove, +}; +module_platform_driver(spear_smi_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ashish Priyadarshi, Shiraz Hashim <shiraz.linux.kernel@gmail.com>"); +MODULE_DESCRIPTION("MTD SMI driver for serial nor flash chips"); diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c index 684247a8a5e..c63ecbcad0b 100644 --- a/drivers/mtd/devices/sst25l.c +++ b/drivers/mtd/devices/sst25l.c @@ -5,7 +5,7 @@   *   * Copyright © 2009 Bluewater Systems Ltd   * Author: Andre Renaud <andre@bluewatersys.com> - * Author: Ryan Mallon <ryan@bluewatersys.com> + * Author: Ryan Mallon   *   * Based on m25p80.c   * @@ -15,7 +15,6 @@   *   */ -#include <linux/init.h>  #include <linux/module.h>  #include <linux/device.h>  #include <linux/mutex.h> @@ -52,8 +51,6 @@ struct sst25l_flash {  	struct spi_device	*spi;  	struct mutex		lock;  	struct mtd_info		mtd; - -	int 			partitioned;  };  struct flash_info { @@ -66,7 +63,7 @@ struct flash_info {  #define to_sst25l_flash(x) container_of(x, struct sst25l_flash, mtd) -static struct flash_info __initdata sst25l_flash_info[] = { +static struct flash_info sst25l_flash_info[] = {  	{"sst25lf020a", 0xbf43, 256, 1024, 4096},  	{"sst25lf040a",	0xbf44,	256, 2048, 4096},  }; @@ -177,9 +174,6 @@ static int sst25l_erase(struct mtd_info *mtd, struct erase_info *instr)  	int err;  	/* Sanity checks */ -	if (instr->addr + instr->len > flash->mtd.size) -		return -EINVAL; -  	if ((uint32_t)instr->len % mtd->erasesize)  		return -EINVAL; @@ -225,16 +219,6 @@ static int sst25l_read(struct mtd_info *mtd, loff_t from, size_t len,  	unsigned char command[4];  	int ret; -	/* Sanity checking */ -	if (len == 0) -		return 0; - -	if (from + len > flash->mtd.size) -		return -EINVAL; - -	if (retlen) -		*retlen = 0; -  	spi_message_init(&message);  	memset(&transfer, 0, sizeof(transfer)); @@ -276,13 +260,6 @@ static int sst25l_write(struct mtd_info *mtd, loff_t to, size_t len,  	int i, j, ret, bytes, copied = 0;  	unsigned char command[5]; -	/* Sanity checks */ -	if (!len) -		return 0; - -	if (to + len > flash->mtd.size) -		return -EINVAL; -  	if ((uint32_t)to % mtd->writesize)  		return -EINVAL; @@ -335,7 +312,7 @@ out:  	return ret;  } -static struct flash_info *__init sst25l_match_device(struct spi_device *spi) +static struct flash_info *sst25l_match_device(struct spi_device *spi)  {  	struct flash_info *flash_info = NULL;  	struct spi_message m; @@ -375,26 +352,26 @@ static struct flash_info *__init sst25l_match_device(struct spi_device *spi)  	return flash_info;  } -static int __init sst25l_probe(struct spi_device *spi) +static int sst25l_probe(struct spi_device *spi)  {  	struct flash_info *flash_info;  	struct sst25l_flash *flash;  	struct flash_platform_data *data; -	int ret, i; +	int ret;  	flash_info = sst25l_match_device(spi);  	if (!flash_info)  		return -ENODEV; -	flash = kzalloc(sizeof(struct sst25l_flash), GFP_KERNEL); +	flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL);  	if (!flash)  		return -ENOMEM;  	flash->spi = spi;  	mutex_init(&flash->lock); -	dev_set_drvdata(&spi->dev, flash); +	spi_set_drvdata(spi, flash); -	data = spi->dev.platform_data; +	data = dev_get_platdata(&spi->dev);  	if (data && data->name)  		flash->mtd.name = data->name;  	else @@ -404,109 +381,51 @@ static int __init sst25l_probe(struct spi_device *spi)  	flash->mtd.flags	= MTD_CAP_NORFLASH;  	flash->mtd.erasesize	= flash_info->erase_size;  	flash->mtd.writesize	= flash_info->page_size; +	flash->mtd.writebufsize	= flash_info->page_size;  	flash->mtd.size		= flash_info->page_size * flash_info->nr_pages; -	flash->mtd.erase	= sst25l_erase; -	flash->mtd.read		= sst25l_read; -	flash->mtd.write 	= sst25l_write; +	flash->mtd._erase	= sst25l_erase; +	flash->mtd._read		= sst25l_read; +	flash->mtd._write 	= sst25l_write;  	dev_info(&spi->dev, "%s (%lld KiB)\n", flash_info->name,  		 (long long)flash->mtd.size >> 10); -	DEBUG(MTD_DEBUG_LEVEL2, -	      "mtd .name = %s, .size = 0x%llx (%lldMiB) " +	pr_debug("mtd .name = %s, .size = 0x%llx (%lldMiB) "  	      ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n",  	      flash->mtd.name,  	      (long long)flash->mtd.size, (long long)(flash->mtd.size >> 20),  	      flash->mtd.erasesize, flash->mtd.erasesize / 1024,  	      flash->mtd.numeraseregions); -	if (mtd_has_partitions()) { -		struct mtd_partition *parts = NULL; -		int nr_parts = 0; -		if (mtd_has_cmdlinepart()) { -			static const char *part_probes[] = -				{"cmdlinepart", NULL}; - -			nr_parts = parse_mtd_partitions(&flash->mtd, -							part_probes, -							&parts, 0); -		} - -		if (nr_parts <= 0 && data && data->parts) { -			parts = data->parts; -			nr_parts = data->nr_parts; -		} - -		if (nr_parts > 0) { -			for (i = 0; i < nr_parts; i++) { -				DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " -				      "{.name = %s, .offset = 0x%llx, " -				      ".size = 0x%llx (%lldKiB) }\n", -				      i, parts[i].name, -				      (long long)parts[i].offset, -				      (long long)parts[i].size, -				      (long long)(parts[i].size >> 10)); -			} - -			flash->partitioned = 1; -			return add_mtd_partitions(&flash->mtd, -						  parts, nr_parts); -		} - -	} else if (data && data->nr_parts) { -		dev_warn(&spi->dev, "ignoring %d default partitions on %s\n", -			 data->nr_parts, data->name); -	} - -	ret = add_mtd_device(&flash->mtd); -	if (ret == 1) { -		kfree(flash); -		dev_set_drvdata(&spi->dev, NULL); +	ret = mtd_device_parse_register(&flash->mtd, NULL, NULL, +					data ? data->parts : NULL, +					data ? data->nr_parts : 0); +	if (ret)  		return -ENODEV; -	}  	return 0;  } -static int __exit sst25l_remove(struct spi_device *spi) +static int sst25l_remove(struct spi_device *spi)  { -	struct sst25l_flash *flash = dev_get_drvdata(&spi->dev); -	int ret; +	struct sst25l_flash *flash = spi_get_drvdata(spi); -	if (mtd_has_partitions() && flash->partitioned) -		ret = del_mtd_partitions(&flash->mtd); -	else -		ret = del_mtd_device(&flash->mtd); -	if (ret == 0) -		kfree(flash); -	return ret; +	return mtd_device_unregister(&flash->mtd);  }  static struct spi_driver sst25l_driver = {  	.driver = {  		.name	= "sst25l", -		.bus	= &spi_bus_type,  		.owner	= THIS_MODULE,  	},  	.probe		= sst25l_probe, -	.remove		= __exit_p(sst25l_remove), +	.remove		= sst25l_remove,  }; -static int __init sst25l_init(void) -{ -	return spi_register_driver(&sst25l_driver); -} - -static void __exit sst25l_exit(void) -{ -	spi_unregister_driver(&sst25l_driver); -} - -module_init(sst25l_init); -module_exit(sst25l_exit); +module_spi_driver(sst25l_driver);  MODULE_DESCRIPTION("MTD SPI driver for SST25L Flash chips");  MODULE_AUTHOR("Andre Renaud <andre@bluewatersys.com>, " -	      "Ryan Mallon <ryan@bluewatersys.com>"); +	      "Ryan Mallon");  MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c new file mode 100644 index 00000000000..d252514d3e9 --- /dev/null +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -0,0 +1,2080 @@ +/* + * st_spi_fsm.c	- ST Fast Sequence Mode (FSM) Serial Flash Controller + * + * Author: Angus Clark <angus.clark@st.com> + * + * Copyright (C) 2010-2014 STMicroelectronics Limited + * + * JEDEC probe based on drivers/mtd/devices/m25p80.c + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/platform_device.h> +#include <linux/mfd/syscon.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/spi-nor.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/of.h> + +#include "serial_flash_cmds.h" + +/* + * FSM SPI Controller Registers + */ +#define SPI_CLOCKDIV			0x0010 +#define SPI_MODESELECT			0x0018 +#define SPI_CONFIGDATA			0x0020 +#define SPI_STA_MODE_CHANGE		0x0028 +#define SPI_FAST_SEQ_TRANSFER_SIZE	0x0100 +#define SPI_FAST_SEQ_ADD1		0x0104 +#define SPI_FAST_SEQ_ADD2		0x0108 +#define SPI_FAST_SEQ_ADD_CFG		0x010c +#define SPI_FAST_SEQ_OPC1		0x0110 +#define SPI_FAST_SEQ_OPC2		0x0114 +#define SPI_FAST_SEQ_OPC3		0x0118 +#define SPI_FAST_SEQ_OPC4		0x011c +#define SPI_FAST_SEQ_OPC5		0x0120 +#define SPI_MODE_BITS			0x0124 +#define SPI_DUMMY_BITS			0x0128 +#define SPI_FAST_SEQ_FLASH_STA_DATA	0x012c +#define SPI_FAST_SEQ_1			0x0130 +#define SPI_FAST_SEQ_2			0x0134 +#define SPI_FAST_SEQ_3			0x0138 +#define SPI_FAST_SEQ_4			0x013c +#define SPI_FAST_SEQ_CFG		0x0140 +#define SPI_FAST_SEQ_STA		0x0144 +#define SPI_QUAD_BOOT_SEQ_INIT_1	0x0148 +#define SPI_QUAD_BOOT_SEQ_INIT_2	0x014c +#define SPI_QUAD_BOOT_READ_SEQ_1	0x0150 +#define SPI_QUAD_BOOT_READ_SEQ_2	0x0154 +#define SPI_PROGRAM_ERASE_TIME		0x0158 +#define SPI_MULT_PAGE_REPEAT_SEQ_1	0x015c +#define SPI_MULT_PAGE_REPEAT_SEQ_2	0x0160 +#define SPI_STATUS_WR_TIME_REG		0x0164 +#define SPI_FAST_SEQ_DATA_REG		0x0300 + +/* + * Register: SPI_MODESELECT + */ +#define SPI_MODESELECT_CONTIG		0x01 +#define SPI_MODESELECT_FASTREAD		0x02 +#define SPI_MODESELECT_DUALIO		0x04 +#define SPI_MODESELECT_FSM		0x08 +#define SPI_MODESELECT_QUADBOOT		0x10 + +/* + * Register: SPI_CONFIGDATA + */ +#define SPI_CFG_DEVICE_ST		0x1 +#define SPI_CFG_DEVICE_ATMEL		0x4 +#define SPI_CFG_MIN_CS_HIGH(x)		(((x) & 0xfff) << 4) +#define SPI_CFG_CS_SETUPHOLD(x)		(((x) & 0xff) << 16) +#define SPI_CFG_DATA_HOLD(x)		(((x) & 0xff) << 24) + +#define SPI_CFG_DEFAULT_MIN_CS_HIGH    SPI_CFG_MIN_CS_HIGH(0x0AA) +#define SPI_CFG_DEFAULT_CS_SETUPHOLD   SPI_CFG_CS_SETUPHOLD(0xA0) +#define SPI_CFG_DEFAULT_DATA_HOLD      SPI_CFG_DATA_HOLD(0x00) + +/* + * Register: SPI_FAST_SEQ_TRANSFER_SIZE + */ +#define TRANSFER_SIZE(x)		((x) * 8) + +/* + * Register: SPI_FAST_SEQ_ADD_CFG + */ +#define ADR_CFG_CYCLES_ADD1(x)		((x) << 0) +#define ADR_CFG_PADS_1_ADD1		(0x0 << 6) +#define ADR_CFG_PADS_2_ADD1		(0x1 << 6) +#define ADR_CFG_PADS_4_ADD1		(0x3 << 6) +#define ADR_CFG_CSDEASSERT_ADD1		(1   << 8) +#define ADR_CFG_CYCLES_ADD2(x)		((x) << (0+16)) +#define ADR_CFG_PADS_1_ADD2		(0x0 << (6+16)) +#define ADR_CFG_PADS_2_ADD2		(0x1 << (6+16)) +#define ADR_CFG_PADS_4_ADD2		(0x3 << (6+16)) +#define ADR_CFG_CSDEASSERT_ADD2		(1   << (8+16)) + +/* + * Register: SPI_FAST_SEQ_n + */ +#define SEQ_OPC_OPCODE(x)		((x) << 0) +#define SEQ_OPC_CYCLES(x)		((x) << 8) +#define SEQ_OPC_PADS_1			(0x0 << 14) +#define SEQ_OPC_PADS_2			(0x1 << 14) +#define SEQ_OPC_PADS_4			(0x3 << 14) +#define SEQ_OPC_CSDEASSERT		(1   << 16) + +/* + * Register: SPI_FAST_SEQ_CFG + */ +#define SEQ_CFG_STARTSEQ		(1 << 0) +#define SEQ_CFG_SWRESET			(1 << 5) +#define SEQ_CFG_CSDEASSERT		(1 << 6) +#define SEQ_CFG_READNOTWRITE		(1 << 7) +#define SEQ_CFG_ERASE			(1 << 8) +#define SEQ_CFG_PADS_1			(0x0 << 16) +#define SEQ_CFG_PADS_2			(0x1 << 16) +#define SEQ_CFG_PADS_4			(0x3 << 16) + +/* + * Register: SPI_MODE_BITS + */ +#define MODE_DATA(x)			(x & 0xff) +#define MODE_CYCLES(x)			((x & 0x3f) << 16) +#define MODE_PADS_1			(0x0 << 22) +#define MODE_PADS_2			(0x1 << 22) +#define MODE_PADS_4			(0x3 << 22) +#define DUMMY_CSDEASSERT		(1   << 24) + +/* + * Register: SPI_DUMMY_BITS + */ +#define DUMMY_CYCLES(x)			((x & 0x3f) << 16) +#define DUMMY_PADS_1			(0x0 << 22) +#define DUMMY_PADS_2			(0x1 << 22) +#define DUMMY_PADS_4			(0x3 << 22) +#define DUMMY_CSDEASSERT		(1   << 24) + +/* + * Register: SPI_FAST_SEQ_FLASH_STA_DATA + */ +#define STA_DATA_BYTE1(x)		((x & 0xff) << 0) +#define STA_DATA_BYTE2(x)		((x & 0xff) << 8) +#define STA_PADS_1			(0x0 << 16) +#define STA_PADS_2			(0x1 << 16) +#define STA_PADS_4			(0x3 << 16) +#define STA_CSDEASSERT			(0x1 << 20) +#define STA_RDNOTWR			(0x1 << 21) + +/* + * FSM SPI Instruction Opcodes + */ +#define STFSM_OPC_CMD			0x1 +#define STFSM_OPC_ADD			0x2 +#define STFSM_OPC_STA			0x3 +#define STFSM_OPC_MODE			0x4 +#define STFSM_OPC_DUMMY		0x5 +#define STFSM_OPC_DATA			0x6 +#define STFSM_OPC_WAIT			0x7 +#define STFSM_OPC_JUMP			0x8 +#define STFSM_OPC_GOTO			0x9 +#define STFSM_OPC_STOP			0xF + +/* + * FSM SPI Instructions (== opcode + operand). + */ +#define STFSM_INSTR(cmd, op)		((cmd) | ((op) << 4)) + +#define STFSM_INST_CMD1			STFSM_INSTR(STFSM_OPC_CMD,	1) +#define STFSM_INST_CMD2			STFSM_INSTR(STFSM_OPC_CMD,	2) +#define STFSM_INST_CMD3			STFSM_INSTR(STFSM_OPC_CMD,	3) +#define STFSM_INST_CMD4			STFSM_INSTR(STFSM_OPC_CMD,	4) +#define STFSM_INST_CMD5			STFSM_INSTR(STFSM_OPC_CMD,	5) +#define STFSM_INST_ADD1			STFSM_INSTR(STFSM_OPC_ADD,	1) +#define STFSM_INST_ADD2			STFSM_INSTR(STFSM_OPC_ADD,	2) + +#define STFSM_INST_DATA_WRITE		STFSM_INSTR(STFSM_OPC_DATA,	1) +#define STFSM_INST_DATA_READ		STFSM_INSTR(STFSM_OPC_DATA,	2) + +#define STFSM_INST_STA_RD1		STFSM_INSTR(STFSM_OPC_STA,	0x1) +#define STFSM_INST_STA_WR1		STFSM_INSTR(STFSM_OPC_STA,	0x1) +#define STFSM_INST_STA_RD2		STFSM_INSTR(STFSM_OPC_STA,	0x2) +#define STFSM_INST_STA_WR1_2		STFSM_INSTR(STFSM_OPC_STA,	0x3) + +#define STFSM_INST_MODE			STFSM_INSTR(STFSM_OPC_MODE,	0) +#define STFSM_INST_DUMMY		STFSM_INSTR(STFSM_OPC_DUMMY,	0) +#define STFSM_INST_WAIT			STFSM_INSTR(STFSM_OPC_WAIT,	0) +#define STFSM_INST_STOP			STFSM_INSTR(STFSM_OPC_STOP,	0) + +#define STFSM_DEFAULT_EMI_FREQ 100000000UL                        /* 100 MHz */ +#define STFSM_DEFAULT_WR_TIME  (STFSM_DEFAULT_EMI_FREQ * (15/1000)) /* 15ms */ + +#define STFSM_FLASH_SAFE_FREQ  10000000UL                         /* 10 MHz */ + +#define STFSM_MAX_WAIT_SEQ_MS  1000     /* FSM execution time */ + +/* S25FLxxxS commands */ +#define S25FL_CMD_WRITE4_1_1_4 0x34 +#define S25FL_CMD_SE4          0xdc +#define S25FL_CMD_CLSR         0x30 +#define S25FL_CMD_DYBWR                0xe1 +#define S25FL_CMD_DYBRD                0xe0 +#define S25FL_CMD_WRITE4       0x12    /* Note, opcode clashes with +					* 'SPINOR_OP_WRITE_1_4_4' +					* as found on N25Qxxx devices! */ + +/* Status register */ +#define FLASH_STATUS_BUSY      0x01 +#define FLASH_STATUS_WEL       0x02 +#define FLASH_STATUS_BP0       0x04 +#define FLASH_STATUS_BP1       0x08 +#define FLASH_STATUS_BP2       0x10 +#define FLASH_STATUS_SRWP0     0x80 +#define FLASH_STATUS_TIMEOUT   0xff +/* S25FL Error Flags */ +#define S25FL_STATUS_E_ERR     0x20 +#define S25FL_STATUS_P_ERR     0x40 + +#define N25Q_CMD_WRVCR         0x81 +#define N25Q_CMD_RDVCR         0x85 +#define N25Q_CMD_RDVECR        0x65 +#define N25Q_CMD_RDNVCR        0xb5 +#define N25Q_CMD_WRNVCR        0xb1 + +#define FLASH_PAGESIZE         256			/* In Bytes    */ +#define FLASH_PAGESIZE_32      (FLASH_PAGESIZE / 4)	/* In uint32_t */ +#define FLASH_MAX_BUSY_WAIT    (300 * HZ)	/* Maximum 'CHIPERASE' time */ + +/* + * Flags to tweak operation of default read/write/erase routines + */ +#define CFG_READ_TOGGLE_32BIT_ADDR     0x00000001 +#define CFG_WRITE_TOGGLE_32BIT_ADDR    0x00000002 +#define CFG_ERASESEC_TOGGLE_32BIT_ADDR 0x00000008 +#define CFG_S25FL_CHECK_ERROR_FLAGS    0x00000010 + +struct stfsm_seq { +	uint32_t data_size; +	uint32_t addr1; +	uint32_t addr2; +	uint32_t addr_cfg; +	uint32_t seq_opc[5]; +	uint32_t mode; +	uint32_t dummy; +	uint32_t status; +	uint8_t  seq[16]; +	uint32_t seq_cfg; +} __packed __aligned(4); + +struct stfsm { +	struct device		*dev; +	void __iomem		*base; +	struct resource		*region; +	struct mtd_info		mtd; +	struct mutex		lock; +	struct flash_info       *info; + +	uint32_t                configuration; +	uint32_t                fifo_dir_delay; +	bool                    booted_from_spi; +	bool                    reset_signal; +	bool                    reset_por; + +	struct stfsm_seq stfsm_seq_read; +	struct stfsm_seq stfsm_seq_write; +	struct stfsm_seq stfsm_seq_en_32bit_addr; +}; + +/* Parameters to configure a READ or WRITE FSM sequence */ +struct seq_rw_config { +	uint32_t        flags;          /* flags to support config */ +	uint8_t         cmd;            /* FLASH command */ +	int             write;          /* Write Sequence */ +	uint8_t         addr_pads;      /* No. of addr pads (MODE & DUMMY) */ +	uint8_t         data_pads;      /* No. of data pads */ +	uint8_t         mode_data;      /* MODE data */ +	uint8_t         mode_cycles;    /* No. of MODE cycles */ +	uint8_t         dummy_cycles;   /* No. of DUMMY cycles */ +}; + +/* SPI Flash Device Table */ +struct flash_info { +	char            *name; +	/* +	 * JEDEC id zero means "no ID" (most older chips); otherwise it has +	 * a high byte of zero plus three data bytes: the manufacturer id, +	 * then a two byte device id. +	 */ +	u32             jedec_id; +	u16             ext_id; +	/* +	 * The size listed here is what works with SPINOR_OP_SE, which isn't +	 * necessarily called a "sector" by the vendor. +	 */ +	unsigned        sector_size; +	u16             n_sectors; +	u32             flags; +	/* +	 * Note, where FAST_READ is supported, freq_max specifies the +	 * FAST_READ frequency, not the READ frequency. +	 */ +	u32             max_freq; +	int             (*config)(struct stfsm *); +}; + +static int stfsm_n25q_config(struct stfsm *fsm); +static int stfsm_mx25_config(struct stfsm *fsm); +static int stfsm_s25fl_config(struct stfsm *fsm); +static int stfsm_w25q_config(struct stfsm *fsm); + +static struct flash_info flash_types[] = { +	/* +	 * ST Microelectronics/Numonyx -- +	 * (newer production versions may have feature updates +	 * (eg faster operating frequency) +	 */ +#define M25P_FLAG (FLASH_FLAG_READ_WRITE | FLASH_FLAG_READ_FAST) +	{ "m25p40",  0x202013, 0,  64 * 1024,   8, M25P_FLAG, 25, NULL }, +	{ "m25p80",  0x202014, 0,  64 * 1024,  16, M25P_FLAG, 25, NULL }, +	{ "m25p16",  0x202015, 0,  64 * 1024,  32, M25P_FLAG, 25, NULL }, +	{ "m25p32",  0x202016, 0,  64 * 1024,  64, M25P_FLAG, 50, NULL }, +	{ "m25p64",  0x202017, 0,  64 * 1024, 128, M25P_FLAG, 50, NULL }, +	{ "m25p128", 0x202018, 0, 256 * 1024,  64, M25P_FLAG, 50, NULL }, + +#define M25PX_FLAG (FLASH_FLAG_READ_WRITE      |	\ +		    FLASH_FLAG_READ_FAST        |	\ +		    FLASH_FLAG_READ_1_1_2       |	\ +		    FLASH_FLAG_WRITE_1_1_2) +	{ "m25px32", 0x207116, 0,  64 * 1024,  64, M25PX_FLAG, 75, NULL }, +	{ "m25px64", 0x207117, 0,  64 * 1024, 128, M25PX_FLAG, 75, NULL }, + +	/* Macronix MX25xxx +	 *     - Support for 'FLASH_FLAG_WRITE_1_4_4' is omitted for devices +	 *       where operating frequency must be reduced. +	 */ +#define MX25_FLAG (FLASH_FLAG_READ_WRITE       |	\ +		   FLASH_FLAG_READ_FAST         |	\ +		   FLASH_FLAG_READ_1_1_2        |	\ +		   FLASH_FLAG_READ_1_2_2        |	\ +		   FLASH_FLAG_READ_1_1_4        |	\ +		   FLASH_FLAG_SE_4K             |	\ +		   FLASH_FLAG_SE_32K) +	{ "mx25l3255e",  0xc29e16, 0, 64 * 1024, 64, +	  (MX25_FLAG | FLASH_FLAG_WRITE_1_4_4), 86, +	  stfsm_mx25_config}, +	{ "mx25l25635e", 0xc22019, 0, 64*1024, 512, +	  (MX25_FLAG | FLASH_FLAG_32BIT_ADDR | FLASH_FLAG_RESET), 70, +	  stfsm_mx25_config }, +	{ "mx25l25655e", 0xc22619, 0, 64*1024, 512, +	  (MX25_FLAG | FLASH_FLAG_32BIT_ADDR | FLASH_FLAG_RESET), 70, +	  stfsm_mx25_config}, + +#define N25Q_FLAG (FLASH_FLAG_READ_WRITE       |	\ +		   FLASH_FLAG_READ_FAST         |	\ +		   FLASH_FLAG_READ_1_1_2        |	\ +		   FLASH_FLAG_READ_1_2_2        |	\ +		   FLASH_FLAG_READ_1_1_4        |	\ +		   FLASH_FLAG_READ_1_4_4        |	\ +		   FLASH_FLAG_WRITE_1_1_2       |	\ +		   FLASH_FLAG_WRITE_1_2_2       |	\ +		   FLASH_FLAG_WRITE_1_1_4       |	\ +		   FLASH_FLAG_WRITE_1_4_4) +	{ "n25q128", 0x20ba18, 0, 64 * 1024,  256, N25Q_FLAG, 108, +	  stfsm_n25q_config }, +	{ "n25q256", 0x20ba19, 0, 64 * 1024,  512, +	  N25Q_FLAG | FLASH_FLAG_32BIT_ADDR, 108, stfsm_n25q_config }, + +	/* +	 * Spansion S25FLxxxP +	 *     - 256KiB and 64KiB sector variants (identified by ext. JEDEC) +	 */ +#define S25FLXXXP_FLAG (FLASH_FLAG_READ_WRITE  |	\ +			FLASH_FLAG_READ_1_1_2   |	\ +			FLASH_FLAG_READ_1_2_2   |	\ +			FLASH_FLAG_READ_1_1_4   |	\ +			FLASH_FLAG_READ_1_4_4   |	\ +			FLASH_FLAG_WRITE_1_1_4  |	\ +			FLASH_FLAG_READ_FAST) +	{ "s25fl032p",  0x010215, 0x4d00,  64 * 1024,  64, S25FLXXXP_FLAG, 80, +	  stfsm_s25fl_config}, +	{ "s25fl129p0", 0x012018, 0x4d00, 256 * 1024,  64, S25FLXXXP_FLAG, 80, +	  stfsm_s25fl_config }, +	{ "s25fl129p1", 0x012018, 0x4d01,  64 * 1024, 256, S25FLXXXP_FLAG, 80, +	  stfsm_s25fl_config }, + +	/* +	 * Spansion S25FLxxxS +	 *     - 256KiB and 64KiB sector variants (identified by ext. JEDEC) +	 *     - RESET# signal supported by die but not bristled out on all +	 *       package types.  The package type is a function of board design, +	 *       so this information is captured in the board's flags. +	 *     - Supports 'DYB' sector protection. Depending on variant, sectors +	 *       may default to locked state on power-on. +	 */ +#define S25FLXXXS_FLAG (S25FLXXXP_FLAG         |	\ +			FLASH_FLAG_RESET        |	\ +			FLASH_FLAG_DYB_LOCKING) +	{ "s25fl128s0", 0x012018, 0x0300,  256 * 1024, 64, S25FLXXXS_FLAG, 80, +	  stfsm_s25fl_config }, +	{ "s25fl128s1", 0x012018, 0x0301,  64 * 1024, 256, S25FLXXXS_FLAG, 80, +	  stfsm_s25fl_config }, +	{ "s25fl256s0", 0x010219, 0x4d00, 256 * 1024, 128, +	  S25FLXXXS_FLAG | FLASH_FLAG_32BIT_ADDR, 80, stfsm_s25fl_config }, +	{ "s25fl256s1", 0x010219, 0x4d01,  64 * 1024, 512, +	  S25FLXXXS_FLAG | FLASH_FLAG_32BIT_ADDR, 80, stfsm_s25fl_config }, + +	/* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ +#define W25X_FLAG (FLASH_FLAG_READ_WRITE       |	\ +		   FLASH_FLAG_READ_FAST         |	\ +		   FLASH_FLAG_READ_1_1_2        |	\ +		   FLASH_FLAG_WRITE_1_1_2) +	{ "w25x40",  0xef3013, 0,  64 * 1024,   8, W25X_FLAG, 75, NULL }, +	{ "w25x80",  0xef3014, 0,  64 * 1024,  16, W25X_FLAG, 75, NULL }, +	{ "w25x16",  0xef3015, 0,  64 * 1024,  32, W25X_FLAG, 75, NULL }, +	{ "w25x32",  0xef3016, 0,  64 * 1024,  64, W25X_FLAG, 75, NULL }, +	{ "w25x64",  0xef3017, 0,  64 * 1024, 128, W25X_FLAG, 75, NULL }, + +	/* Winbond -- w25q "blocks" are 64K, "sectors" are 4KiB */ +#define W25Q_FLAG (FLASH_FLAG_READ_WRITE       |	\ +		   FLASH_FLAG_READ_FAST         |	\ +		   FLASH_FLAG_READ_1_1_2        |	\ +		   FLASH_FLAG_READ_1_2_2        |	\ +		   FLASH_FLAG_READ_1_1_4        |	\ +		   FLASH_FLAG_READ_1_4_4        |	\ +		   FLASH_FLAG_WRITE_1_1_4) +	{ "w25q80",  0xef4014, 0,  64 * 1024,  16, W25Q_FLAG, 80, +	  stfsm_w25q_config }, +	{ "w25q16",  0xef4015, 0,  64 * 1024,  32, W25Q_FLAG, 80, +	  stfsm_w25q_config }, +	{ "w25q32",  0xef4016, 0,  64 * 1024,  64, W25Q_FLAG, 80, +	  stfsm_w25q_config }, +	{ "w25q64",  0xef4017, 0,  64 * 1024, 128, W25Q_FLAG, 80, +	  stfsm_w25q_config }, + +	/* Sentinel */ +	{ NULL, 0x000000, 0, 0, 0, 0, 0, NULL }, +}; + +/* + * FSM message sequence configurations: + * + * All configs are presented in order of preference + */ + +/* Default READ configurations, in order of preference */ +static struct seq_rw_config default_read_configs[] = { +	{FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4,	0, 4, 4, 0x00, 2, 4}, +	{FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4,	0, 1, 4, 0x00, 4, 0}, +	{FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2,	0, 2, 2, 0x00, 4, 0}, +	{FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2,	0, 1, 2, 0x00, 0, 8}, +	{FLASH_FLAG_READ_FAST,	SPINOR_OP_READ_FAST,	0, 1, 1, 0x00, 0, 8}, +	{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ,		0, 1, 1, 0x00, 0, 0}, +	{0x00,			0,			0, 0, 0, 0x00, 0, 0}, +}; + +/* Default WRITE configurations */ +static struct seq_rw_config default_write_configs[] = { +	{FLASH_FLAG_WRITE_1_4_4, SPINOR_OP_WRITE_1_4_4, 1, 4, 4, 0x00, 0, 0}, +	{FLASH_FLAG_WRITE_1_1_4, SPINOR_OP_WRITE_1_1_4, 1, 1, 4, 0x00, 0, 0}, +	{FLASH_FLAG_WRITE_1_2_2, SPINOR_OP_WRITE_1_2_2, 1, 2, 2, 0x00, 0, 0}, +	{FLASH_FLAG_WRITE_1_1_2, SPINOR_OP_WRITE_1_1_2, 1, 1, 2, 0x00, 0, 0}, +	{FLASH_FLAG_READ_WRITE,  SPINOR_OP_WRITE,       1, 1, 1, 0x00, 0, 0}, +	{0x00,			 0,			0, 0, 0, 0x00, 0, 0}, +}; + +/* + * [N25Qxxx] Configuration + */ +#define N25Q_VCR_DUMMY_CYCLES(x)	(((x) & 0xf) << 4) +#define N25Q_VCR_XIP_DISABLED		((uint8_t)0x1 << 3) +#define N25Q_VCR_WRAP_CONT		0x3 + +/* N25Q 3-byte Address READ configurations + *	- 'FAST' variants configured for 8 dummy cycles. + * + * Note, the number of dummy cycles used for 'FAST' READ operations is + * configurable and would normally be tuned according to the READ command and + * operating frequency.  However, this applies universally to all 'FAST' READ + * commands, including those used by the SPIBoot controller, and remains in + * force until the device is power-cycled.  Since the SPIBoot controller is + * hard-wired to use 8 dummy cycles, we must configure the device to also use 8 + * cycles. + */ +static struct seq_rw_config n25q_read3_configs[] = { +	{FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4,	0, 4, 4, 0x00, 0, 8}, +	{FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4,	0, 1, 4, 0x00, 0, 8}, +	{FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2,	0, 2, 2, 0x00, 0, 8}, +	{FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2,	0, 1, 2, 0x00, 0, 8}, +	{FLASH_FLAG_READ_FAST,	SPINOR_OP_READ_FAST,	0, 1, 1, 0x00, 0, 8}, +	{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ,	        0, 1, 1, 0x00, 0, 0}, +	{0x00,			0,			0, 0, 0, 0x00, 0, 0}, +}; + +/* N25Q 4-byte Address READ configurations + *	- use special 4-byte address READ commands (reduces overheads, and + *        reduces risk of hitting watchdog reset issues). + *	- 'FAST' variants configured for 8 dummy cycles (see note above.) + */ +static struct seq_rw_config n25q_read4_configs[] = { +	{FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4,	0, 4, 4, 0x00, 0, 8}, +	{FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4,	0, 1, 4, 0x00, 0, 8}, +	{FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2,	0, 2, 2, 0x00, 0, 8}, +	{FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2,	0, 1, 2, 0x00, 0, 8}, +	{FLASH_FLAG_READ_FAST,	SPINOR_OP_READ4_FAST,	0, 1, 1, 0x00, 0, 8}, +	{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4,	0, 1, 1, 0x00, 0, 0}, +	{0x00,			0,			0, 0, 0, 0x00, 0, 0}, +}; + +/* + * [MX25xxx] Configuration + */ +#define MX25_STATUS_QE			(0x1 << 6) + +static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq) +{ +	seq->seq_opc[0] = (SEQ_OPC_PADS_1 | +			   SEQ_OPC_CYCLES(8) | +			   SEQ_OPC_OPCODE(SPINOR_OP_EN4B) | +			   SEQ_OPC_CSDEASSERT); + +	seq->seq[0] = STFSM_INST_CMD1; +	seq->seq[1] = STFSM_INST_WAIT; +	seq->seq[2] = STFSM_INST_STOP; + +	seq->seq_cfg = (SEQ_CFG_PADS_1 | +			SEQ_CFG_ERASE | +			SEQ_CFG_READNOTWRITE | +			SEQ_CFG_CSDEASSERT | +			SEQ_CFG_STARTSEQ); + +	return 0; +} + +/* + * [S25FLxxx] Configuration + */ +#define STFSM_S25FL_CONFIG_QE		(0x1 << 1) + +/* + * S25FLxxxS devices provide three ways of supporting 32-bit addressing: Bank + * Register, Extended Address Modes, and a 32-bit address command set.  The + * 32-bit address command set is used here, since it avoids any problems with + * entering a state that is incompatible with the SPIBoot Controller. + */ +static struct seq_rw_config stfsm_s25fl_read4_configs[] = { +	{FLASH_FLAG_READ_1_4_4,  SPINOR_OP_READ4_1_4_4,  0, 4, 4, 0x00, 2, 4}, +	{FLASH_FLAG_READ_1_1_4,  SPINOR_OP_READ4_1_1_4,  0, 1, 4, 0x00, 0, 8}, +	{FLASH_FLAG_READ_1_2_2,  SPINOR_OP_READ4_1_2_2,  0, 2, 2, 0x00, 4, 0}, +	{FLASH_FLAG_READ_1_1_2,  SPINOR_OP_READ4_1_1_2,  0, 1, 2, 0x00, 0, 8}, +	{FLASH_FLAG_READ_FAST,   SPINOR_OP_READ4_FAST,   0, 1, 1, 0x00, 0, 8}, +	{FLASH_FLAG_READ_WRITE,  SPINOR_OP_READ4,        0, 1, 1, 0x00, 0, 0}, +	{0x00,                   0,                      0, 0, 0, 0x00, 0, 0}, +}; + +static struct seq_rw_config stfsm_s25fl_write4_configs[] = { +	{FLASH_FLAG_WRITE_1_1_4, S25FL_CMD_WRITE4_1_1_4, 1, 1, 4, 0x00, 0, 0}, +	{FLASH_FLAG_READ_WRITE,  S25FL_CMD_WRITE4,       1, 1, 1, 0x00, 0, 0}, +	{0x00,                   0,                      0, 0, 0, 0x00, 0, 0}, +}; + +/* + * [W25Qxxx] Configuration + */ +#define W25Q_STATUS_QE			(0x1 << 1) + +static struct stfsm_seq stfsm_seq_read_jedec = { +	.data_size = TRANSFER_SIZE(8), +	.seq_opc[0] = (SEQ_OPC_PADS_1 | +		       SEQ_OPC_CYCLES(8) | +		       SEQ_OPC_OPCODE(SPINOR_OP_RDID)), +	.seq = { +		STFSM_INST_CMD1, +		STFSM_INST_DATA_READ, +		STFSM_INST_STOP, +	}, +	.seq_cfg = (SEQ_CFG_PADS_1 | +		    SEQ_CFG_READNOTWRITE | +		    SEQ_CFG_CSDEASSERT | +		    SEQ_CFG_STARTSEQ), +}; + +static struct stfsm_seq stfsm_seq_read_status_fifo = { +	.data_size = TRANSFER_SIZE(4), +	.seq_opc[0] = (SEQ_OPC_PADS_1 | +		       SEQ_OPC_CYCLES(8) | +		       SEQ_OPC_OPCODE(SPINOR_OP_RDSR)), +	.seq = { +		STFSM_INST_CMD1, +		STFSM_INST_DATA_READ, +		STFSM_INST_STOP, +	}, +	.seq_cfg = (SEQ_CFG_PADS_1 | +		    SEQ_CFG_READNOTWRITE | +		    SEQ_CFG_CSDEASSERT | +		    SEQ_CFG_STARTSEQ), +}; + +static struct stfsm_seq stfsm_seq_erase_sector = { +	/* 'addr_cfg' configured during initialisation */ +	.seq_opc = { +		(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | +		 SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT), + +		(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | +		 SEQ_OPC_OPCODE(SPINOR_OP_SE)), +	}, +	.seq = { +		STFSM_INST_CMD1, +		STFSM_INST_CMD2, +		STFSM_INST_ADD1, +		STFSM_INST_ADD2, +		STFSM_INST_STOP, +	}, +	.seq_cfg = (SEQ_CFG_PADS_1 | +		    SEQ_CFG_READNOTWRITE | +		    SEQ_CFG_CSDEASSERT | +		    SEQ_CFG_STARTSEQ), +}; + +static struct stfsm_seq stfsm_seq_erase_chip = { +	.seq_opc = { +		(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | +		 SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT), + +		(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | +		 SEQ_OPC_OPCODE(SPINOR_OP_CHIP_ERASE) | SEQ_OPC_CSDEASSERT), +	}, +	.seq = { +		STFSM_INST_CMD1, +		STFSM_INST_CMD2, +		STFSM_INST_WAIT, +		STFSM_INST_STOP, +	}, +	.seq_cfg = (SEQ_CFG_PADS_1 | +		    SEQ_CFG_ERASE | +		    SEQ_CFG_READNOTWRITE | +		    SEQ_CFG_CSDEASSERT | +		    SEQ_CFG_STARTSEQ), +}; + +static struct stfsm_seq stfsm_seq_write_status = { +	.seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | +		       SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT), +	.seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | +		       SEQ_OPC_OPCODE(SPINOR_OP_WRSR)), +	.seq = { +		STFSM_INST_CMD1, +		STFSM_INST_CMD2, +		STFSM_INST_STA_WR1, +		STFSM_INST_STOP, +	}, +	.seq_cfg = (SEQ_CFG_PADS_1 | +		    SEQ_CFG_READNOTWRITE | +		    SEQ_CFG_CSDEASSERT | +		    SEQ_CFG_STARTSEQ), +}; + +static int stfsm_n25q_en_32bit_addr_seq(struct stfsm_seq *seq) +{ +	seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | +			   SEQ_OPC_OPCODE(SPINOR_OP_EN4B)); +	seq->seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | +			   SEQ_OPC_OPCODE(SPINOR_OP_WREN) | +			   SEQ_OPC_CSDEASSERT); + +	seq->seq[0] = STFSM_INST_CMD2; +	seq->seq[1] = STFSM_INST_CMD1; +	seq->seq[2] = STFSM_INST_WAIT; +	seq->seq[3] = STFSM_INST_STOP; + +	seq->seq_cfg = (SEQ_CFG_PADS_1 | +			SEQ_CFG_ERASE | +			SEQ_CFG_READNOTWRITE | +			SEQ_CFG_CSDEASSERT | +			SEQ_CFG_STARTSEQ); + +	return 0; +} + +static inline int stfsm_is_idle(struct stfsm *fsm) +{ +	return readl(fsm->base + SPI_FAST_SEQ_STA) & 0x10; +} + +static inline uint32_t stfsm_fifo_available(struct stfsm *fsm) +{ +	return (readl(fsm->base + SPI_FAST_SEQ_STA) >> 5) & 0x7f; +} + +static void stfsm_clear_fifo(struct stfsm *fsm) +{ +	uint32_t avail; + +	for (;;) { +		avail = stfsm_fifo_available(fsm); +		if (!avail) +			break; + +		while (avail) { +			readl(fsm->base + SPI_FAST_SEQ_DATA_REG); +			avail--; +		} +	} +} + +static inline void stfsm_load_seq(struct stfsm *fsm, +				  const struct stfsm_seq *seq) +{ +	void __iomem *dst = fsm->base + SPI_FAST_SEQ_TRANSFER_SIZE; +	const uint32_t *src = (const uint32_t *)seq; +	int words = sizeof(*seq) / sizeof(*src); + +	BUG_ON(!stfsm_is_idle(fsm)); + +	while (words--) { +		writel(*src, dst); +		src++; +		dst += 4; +	} +} + +static void stfsm_wait_seq(struct stfsm *fsm) +{ +	unsigned long deadline; +	int timeout = 0; + +	deadline = jiffies + msecs_to_jiffies(STFSM_MAX_WAIT_SEQ_MS); + +	while (!timeout) { +		if (time_after_eq(jiffies, deadline)) +			timeout = 1; + +		if (stfsm_is_idle(fsm)) +			return; + +		cond_resched(); +	} + +	dev_err(fsm->dev, "timeout on sequence completion\n"); +} + +static void stfsm_read_fifo(struct stfsm *fsm, uint32_t *buf, uint32_t size) +{ +	uint32_t remaining = size >> 2; +	uint32_t avail; +	uint32_t words; + +	dev_dbg(fsm->dev, "Reading %d bytes from FIFO\n", size); + +	BUG_ON((((uintptr_t)buf) & 0x3) || (size & 0x3)); + +	while (remaining) { +		for (;;) { +			avail = stfsm_fifo_available(fsm); +			if (avail) +				break; +			udelay(1); +		} +		words = min(avail, remaining); +		remaining -= words; + +		readsl(fsm->base + SPI_FAST_SEQ_DATA_REG, buf, words); +		buf += words; +	} +} + +static int stfsm_write_fifo(struct stfsm *fsm, const uint32_t *buf, +			    uint32_t size) +{ +	uint32_t words = size >> 2; + +	dev_dbg(fsm->dev, "writing %d bytes to FIFO\n", size); + +	BUG_ON((((uintptr_t)buf) & 0x3) || (size & 0x3)); + +	writesl(fsm->base + SPI_FAST_SEQ_DATA_REG, buf, words); + +	return size; +} + +static int stfsm_enter_32bit_addr(struct stfsm *fsm, int enter) +{ +	struct stfsm_seq *seq = &fsm->stfsm_seq_en_32bit_addr; +	uint32_t cmd = enter ? SPINOR_OP_EN4B : SPINOR_OP_EX4B; + +	seq->seq_opc[0] = (SEQ_OPC_PADS_1 | +			   SEQ_OPC_CYCLES(8) | +			   SEQ_OPC_OPCODE(cmd) | +			   SEQ_OPC_CSDEASSERT); + +	stfsm_load_seq(fsm, seq); + +	stfsm_wait_seq(fsm); + +	return 0; +} + +static uint8_t stfsm_wait_busy(struct stfsm *fsm) +{ +	struct stfsm_seq *seq = &stfsm_seq_read_status_fifo; +	unsigned long deadline; +	uint32_t status; +	int timeout = 0; + +	/* Use RDRS1 */ +	seq->seq_opc[0] = (SEQ_OPC_PADS_1 | +			   SEQ_OPC_CYCLES(8) | +			   SEQ_OPC_OPCODE(SPINOR_OP_RDSR)); + +	/* Load read_status sequence */ +	stfsm_load_seq(fsm, seq); + +	/* +	 * Repeat until busy bit is deasserted, or timeout, or error (S25FLxxxS) +	 */ +	deadline = jiffies + FLASH_MAX_BUSY_WAIT; +	while (!timeout) { +		if (time_after_eq(jiffies, deadline)) +			timeout = 1; + +		stfsm_wait_seq(fsm); + +		stfsm_read_fifo(fsm, &status, 4); + +		if ((status & FLASH_STATUS_BUSY) == 0) +			return 0; + +		if ((fsm->configuration & CFG_S25FL_CHECK_ERROR_FLAGS) && +		    ((status & S25FL_STATUS_P_ERR) || +		     (status & S25FL_STATUS_E_ERR))) +			return (uint8_t)(status & 0xff); + +		if (!timeout) +			/* Restart */ +			writel(seq->seq_cfg, fsm->base + SPI_FAST_SEQ_CFG); + +		cond_resched(); +	} + +	dev_err(fsm->dev, "timeout on wait_busy\n"); + +	return FLASH_STATUS_TIMEOUT; +} + +static int stfsm_read_status(struct stfsm *fsm, uint8_t cmd, +			     uint8_t *data, int bytes) +{ +	struct stfsm_seq *seq = &stfsm_seq_read_status_fifo; +	uint32_t tmp; +	uint8_t *t = (uint8_t *)&tmp; +	int i; + +	dev_dbg(fsm->dev, "read 'status' register [0x%02x], %d byte(s)\n", +		cmd, bytes); + +	BUG_ON(bytes != 1 && bytes != 2); + +	seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | +			   SEQ_OPC_OPCODE(cmd)), + +	stfsm_load_seq(fsm, seq); + +	stfsm_read_fifo(fsm, &tmp, 4); + +	for (i = 0; i < bytes; i++) +		data[i] = t[i]; + +	stfsm_wait_seq(fsm); + +	return 0; +} + +static int stfsm_write_status(struct stfsm *fsm, uint8_t cmd, +			    uint16_t data, int bytes, int wait_busy) +{ +	struct stfsm_seq *seq = &stfsm_seq_write_status; + +	dev_dbg(fsm->dev, +		"write 'status' register [0x%02x], %d byte(s), 0x%04x\n" +		" %s wait-busy\n", cmd, bytes, data, wait_busy ? "with" : "no"); + +	BUG_ON(bytes != 1 && bytes != 2); + +	seq->seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | +			   SEQ_OPC_OPCODE(cmd)); + +	seq->status = (uint32_t)data | STA_PADS_1 | STA_CSDEASSERT; +	seq->seq[2] = (bytes == 1) ? STFSM_INST_STA_WR1 : STFSM_INST_STA_WR1_2; + +	stfsm_load_seq(fsm, seq); + +	stfsm_wait_seq(fsm); + +	if (wait_busy) +		stfsm_wait_busy(fsm); + +	return 0; +} + +/* + * SoC reset on 'boot-from-spi' systems + * + * Certain modes of operation cause the Flash device to enter a particular state + * for a period of time (e.g. 'Erase Sector', 'Quad Enable', and 'Enter 32-bit + * Addr' commands).  On boot-from-spi systems, it is important to consider what + * happens if a warm reset occurs during this period.  The SPIBoot controller + * assumes that Flash device is in its default reset state, 24-bit address mode, + * and ready to accept commands.  This can be achieved using some form of + * on-board logic/controller to force a device POR in response to a SoC-level + * reset or by making use of the device reset signal if available (limited + * number of devices only). + * + * Failure to take such precautions can cause problems following a warm reset. + * For some operations (e.g. ERASE), there is little that can be done.  For + * other modes of operation (e.g. 32-bit addressing), options are often + * available that can help minimise the window in which a reset could cause a + * problem. + * + */ +static bool stfsm_can_handle_soc_reset(struct stfsm *fsm) +{ +	/* Reset signal is available on the board and supported by the device */ +	if (fsm->reset_signal && fsm->info->flags & FLASH_FLAG_RESET) +		return true; + +	/* Board-level logic forces a power-on-reset */ +	if (fsm->reset_por) +		return true; + +	/* Reset is not properly handled and may result in failure to reboot */ +	return false; +} + +/* Configure 'addr_cfg' according to addressing mode */ +static void stfsm_prepare_erasesec_seq(struct stfsm *fsm, +				       struct stfsm_seq *seq) +{ +	int addr1_cycles = fsm->info->flags & FLASH_FLAG_32BIT_ADDR ? 16 : 8; + +	seq->addr_cfg = (ADR_CFG_CYCLES_ADD1(addr1_cycles) | +			 ADR_CFG_PADS_1_ADD1 | +			 ADR_CFG_CYCLES_ADD2(16) | +			 ADR_CFG_PADS_1_ADD2 | +			 ADR_CFG_CSDEASSERT_ADD2); +} + +/* Search for preferred configuration based on available flags */ +static struct seq_rw_config * +stfsm_search_seq_rw_configs(struct stfsm *fsm, +			    struct seq_rw_config cfgs[]) +{ +	struct seq_rw_config *config; +	int flags = fsm->info->flags; + +	for (config = cfgs; config->cmd != 0; config++) +		if ((config->flags & flags) == config->flags) +			return config; + +	return NULL; +} + +/* Prepare a READ/WRITE sequence according to configuration parameters */ +static void stfsm_prepare_rw_seq(struct stfsm *fsm, +				 struct stfsm_seq *seq, +				 struct seq_rw_config *cfg) +{ +	int addr1_cycles, addr2_cycles; +	int i = 0; + +	memset(seq, 0, sizeof(*seq)); + +	/* Add READ/WRITE OPC  */ +	seq->seq_opc[i++] = (SEQ_OPC_PADS_1 | +			     SEQ_OPC_CYCLES(8) | +			     SEQ_OPC_OPCODE(cfg->cmd)); + +	/* Add WREN OPC for a WRITE sequence */ +	if (cfg->write) +		seq->seq_opc[i++] = (SEQ_OPC_PADS_1 | +				     SEQ_OPC_CYCLES(8) | +				     SEQ_OPC_OPCODE(SPINOR_OP_WREN) | +				     SEQ_OPC_CSDEASSERT); + +	/* Address configuration (24 or 32-bit addresses) */ +	addr1_cycles  = (fsm->info->flags & FLASH_FLAG_32BIT_ADDR) ? 16 : 8; +	addr1_cycles /= cfg->addr_pads; +	addr2_cycles  = 16 / cfg->addr_pads; +	seq->addr_cfg = ((addr1_cycles & 0x3f) << 0 |	/* ADD1 cycles */ +			 (cfg->addr_pads - 1) << 6 |	/* ADD1 pads */ +			 (addr2_cycles & 0x3f) << 16 |	/* ADD2 cycles */ +			 ((cfg->addr_pads - 1) << 22));	/* ADD2 pads */ + +	/* Data/Sequence configuration */ +	seq->seq_cfg = ((cfg->data_pads - 1) << 16 | +			SEQ_CFG_STARTSEQ | +			SEQ_CFG_CSDEASSERT); +	if (!cfg->write) +		seq->seq_cfg |= SEQ_CFG_READNOTWRITE; + +	/* Mode configuration (no. of pads taken from addr cfg) */ +	seq->mode = ((cfg->mode_data & 0xff) << 0 |	/* data */ +		     (cfg->mode_cycles & 0x3f) << 16 |	/* cycles */ +		     (cfg->addr_pads - 1) << 22);	/* pads */ + +	/* Dummy configuration (no. of pads taken from addr cfg) */ +	seq->dummy = ((cfg->dummy_cycles & 0x3f) << 16 |	/* cycles */ +		      (cfg->addr_pads - 1) << 22);		/* pads */ + + +	/* Instruction sequence */ +	i = 0; +	if (cfg->write) +		seq->seq[i++] = STFSM_INST_CMD2; + +	seq->seq[i++] = STFSM_INST_CMD1; + +	seq->seq[i++] = STFSM_INST_ADD1; +	seq->seq[i++] = STFSM_INST_ADD2; + +	if (cfg->mode_cycles) +		seq->seq[i++] = STFSM_INST_MODE; + +	if (cfg->dummy_cycles) +		seq->seq[i++] = STFSM_INST_DUMMY; + +	seq->seq[i++] = +		cfg->write ? STFSM_INST_DATA_WRITE : STFSM_INST_DATA_READ; +	seq->seq[i++] = STFSM_INST_STOP; +} + +static int stfsm_search_prepare_rw_seq(struct stfsm *fsm, +				       struct stfsm_seq *seq, +				       struct seq_rw_config *cfgs) +{ +	struct seq_rw_config *config; + +	config = stfsm_search_seq_rw_configs(fsm, cfgs); +	if (!config) { +		dev_err(fsm->dev, "failed to find suitable config\n"); +		return -EINVAL; +	} + +	stfsm_prepare_rw_seq(fsm, seq, config); + +	return 0; +} + +/* Prepare a READ/WRITE/ERASE 'default' sequences */ +static int stfsm_prepare_rwe_seqs_default(struct stfsm *fsm) +{ +	uint32_t flags = fsm->info->flags; +	int ret; + +	/* Configure 'READ' sequence */ +	ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read, +					  default_read_configs); +	if (ret) { +		dev_err(fsm->dev, +			"failed to prep READ sequence with flags [0x%08x]\n", +			flags); +		return ret; +	} + +	/* Configure 'WRITE' sequence */ +	ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_write, +					  default_write_configs); +	if (ret) { +		dev_err(fsm->dev, +			"failed to prep WRITE sequence with flags [0x%08x]\n", +			flags); +		return ret; +	} + +	/* Configure 'ERASE_SECTOR' sequence */ +	stfsm_prepare_erasesec_seq(fsm, &stfsm_seq_erase_sector); + +	return 0; +} + +static int stfsm_mx25_config(struct stfsm *fsm) +{ +	uint32_t flags = fsm->info->flags; +	uint32_t data_pads; +	uint8_t sta; +	int ret; +	bool soc_reset; + +	/* +	 * Use default READ/WRITE sequences +	 */ +	ret = stfsm_prepare_rwe_seqs_default(fsm); +	if (ret) +		return ret; + +	/* +	 * Configure 32-bit Address Support +	 */ +	if (flags & FLASH_FLAG_32BIT_ADDR) { +		/* Configure 'enter_32bitaddr' FSM sequence */ +		stfsm_mx25_en_32bit_addr_seq(&fsm->stfsm_seq_en_32bit_addr); + +		soc_reset = stfsm_can_handle_soc_reset(fsm); +		if (soc_reset || !fsm->booted_from_spi) +			/* If we can handle SoC resets, we enable 32-bit address +			 * mode pervasively */ +			stfsm_enter_32bit_addr(fsm, 1); + +		else +			/* Else, enable/disable 32-bit addressing before/after +			 * each operation */ +			fsm->configuration = (CFG_READ_TOGGLE_32BIT_ADDR | +					      CFG_WRITE_TOGGLE_32BIT_ADDR | +					      CFG_ERASESEC_TOGGLE_32BIT_ADDR); +	} + +	/* Check status of 'QE' bit, update if required. */ +	stfsm_read_status(fsm, SPINOR_OP_RDSR, &sta, 1); +	data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1; +	if (data_pads == 4) { +		if (!(sta & MX25_STATUS_QE)) { +			/* Set 'QE' */ +			sta |= MX25_STATUS_QE; + +			stfsm_write_status(fsm, SPINOR_OP_WRSR, sta, 1, 1); +		} +	} else { +		if (sta & MX25_STATUS_QE) { +			/* Clear 'QE' */ +			sta &= ~MX25_STATUS_QE; + +			stfsm_write_status(fsm, SPINOR_OP_WRSR, sta, 1, 1); +		} +	} + +	return 0; +} + +static int stfsm_n25q_config(struct stfsm *fsm) +{ +	uint32_t flags = fsm->info->flags; +	uint8_t vcr; +	int ret = 0; +	bool soc_reset; + +	/* Configure 'READ' sequence */ +	if (flags & FLASH_FLAG_32BIT_ADDR) +		ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read, +						  n25q_read4_configs); +	else +		ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read, +						  n25q_read3_configs); +	if (ret) { +		dev_err(fsm->dev, +			"failed to prepare READ sequence with flags [0x%08x]\n", +			flags); +		return ret; +	} + +	/* Configure 'WRITE' sequence (default configs) */ +	ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_write, +					  default_write_configs); +	if (ret) { +		dev_err(fsm->dev, +			"preparing WRITE sequence using flags [0x%08x] failed\n", +			flags); +		return ret; +	} + +	/* * Configure 'ERASE_SECTOR' sequence */ +	stfsm_prepare_erasesec_seq(fsm, &stfsm_seq_erase_sector); + +	/* Configure 32-bit address support */ +	if (flags & FLASH_FLAG_32BIT_ADDR) { +		stfsm_n25q_en_32bit_addr_seq(&fsm->stfsm_seq_en_32bit_addr); + +		soc_reset = stfsm_can_handle_soc_reset(fsm); +		if (soc_reset || !fsm->booted_from_spi) { +			/* +			 * If we can handle SoC resets, we enable 32-bit +			 * address mode pervasively +			 */ +			stfsm_enter_32bit_addr(fsm, 1); +		} else { +			/* +			 * If not, enable/disable for WRITE and ERASE +			 * operations (READ uses special commands) +			 */ +			fsm->configuration = (CFG_WRITE_TOGGLE_32BIT_ADDR | +					      CFG_ERASESEC_TOGGLE_32BIT_ADDR); +		} +	} + +	/* +	 * Configure device to use 8 dummy cycles +	 */ +	vcr = (N25Q_VCR_DUMMY_CYCLES(8) | N25Q_VCR_XIP_DISABLED | +	       N25Q_VCR_WRAP_CONT); +	stfsm_write_status(fsm, N25Q_CMD_WRVCR, vcr, 1, 0); + +	return 0; +} + +static void stfsm_s25fl_prepare_erasesec_seq_32(struct stfsm_seq *seq) +{ +	seq->seq_opc[1] = (SEQ_OPC_PADS_1 | +			   SEQ_OPC_CYCLES(8) | +			   SEQ_OPC_OPCODE(S25FL_CMD_SE4)); + +	seq->addr_cfg = (ADR_CFG_CYCLES_ADD1(16) | +			 ADR_CFG_PADS_1_ADD1 | +			 ADR_CFG_CYCLES_ADD2(16) | +			 ADR_CFG_PADS_1_ADD2 | +			 ADR_CFG_CSDEASSERT_ADD2); +} + +static void stfsm_s25fl_read_dyb(struct stfsm *fsm, uint32_t offs, uint8_t *dby) +{ +	uint32_t tmp; +	struct stfsm_seq seq = { +		.data_size = TRANSFER_SIZE(4), +		.seq_opc[0] = (SEQ_OPC_PADS_1 | +			       SEQ_OPC_CYCLES(8) | +			       SEQ_OPC_OPCODE(S25FL_CMD_DYBRD)), +		.addr_cfg = (ADR_CFG_CYCLES_ADD1(16) | +			     ADR_CFG_PADS_1_ADD1 | +			     ADR_CFG_CYCLES_ADD2(16) | +			     ADR_CFG_PADS_1_ADD2), +		.addr1 = (offs >> 16) & 0xffff, +		.addr2 = offs & 0xffff, +		.seq = { +			STFSM_INST_CMD1, +			STFSM_INST_ADD1, +			STFSM_INST_ADD2, +			STFSM_INST_DATA_READ, +			STFSM_INST_STOP, +		}, +		.seq_cfg = (SEQ_CFG_PADS_1 | +			    SEQ_CFG_READNOTWRITE | +			    SEQ_CFG_CSDEASSERT | +			    SEQ_CFG_STARTSEQ), +	}; + +	stfsm_load_seq(fsm, &seq); + +	stfsm_read_fifo(fsm, &tmp, 4); + +	*dby = (uint8_t)(tmp >> 24); + +	stfsm_wait_seq(fsm); +} + +static void stfsm_s25fl_write_dyb(struct stfsm *fsm, uint32_t offs, uint8_t dby) +{ +	struct stfsm_seq seq = { +		.seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | +			       SEQ_OPC_OPCODE(SPINOR_OP_WREN) | +			       SEQ_OPC_CSDEASSERT), +		.seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | +			       SEQ_OPC_OPCODE(S25FL_CMD_DYBWR)), +		.addr_cfg = (ADR_CFG_CYCLES_ADD1(16) | +			     ADR_CFG_PADS_1_ADD1 | +			     ADR_CFG_CYCLES_ADD2(16) | +			     ADR_CFG_PADS_1_ADD2), +		.status = (uint32_t)dby | STA_PADS_1 | STA_CSDEASSERT, +		.addr1 = (offs >> 16) & 0xffff, +		.addr2 = offs & 0xffff, +		.seq = { +			STFSM_INST_CMD1, +			STFSM_INST_CMD2, +			STFSM_INST_ADD1, +			STFSM_INST_ADD2, +			STFSM_INST_STA_WR1, +			STFSM_INST_STOP, +		}, +		.seq_cfg = (SEQ_CFG_PADS_1 | +			    SEQ_CFG_READNOTWRITE | +			    SEQ_CFG_CSDEASSERT | +			    SEQ_CFG_STARTSEQ), +	}; + +	stfsm_load_seq(fsm, &seq); +	stfsm_wait_seq(fsm); + +	stfsm_wait_busy(fsm); +} + +static int stfsm_s25fl_clear_status_reg(struct stfsm *fsm) +{ +	struct stfsm_seq seq = { +		.seq_opc[0] = (SEQ_OPC_PADS_1 | +			       SEQ_OPC_CYCLES(8) | +			       SEQ_OPC_OPCODE(S25FL_CMD_CLSR) | +			       SEQ_OPC_CSDEASSERT), +		.seq_opc[1] = (SEQ_OPC_PADS_1 | +			       SEQ_OPC_CYCLES(8) | +			       SEQ_OPC_OPCODE(SPINOR_OP_WRDI) | +			       SEQ_OPC_CSDEASSERT), +		.seq = { +			STFSM_INST_CMD1, +			STFSM_INST_CMD2, +			STFSM_INST_WAIT, +			STFSM_INST_STOP, +		}, +		.seq_cfg = (SEQ_CFG_PADS_1 | +			    SEQ_CFG_ERASE | +			    SEQ_CFG_READNOTWRITE | +			    SEQ_CFG_CSDEASSERT | +			    SEQ_CFG_STARTSEQ), +	}; + +	stfsm_load_seq(fsm, &seq); + +	stfsm_wait_seq(fsm); + +	return 0; +} + +static int stfsm_s25fl_config(struct stfsm *fsm) +{ +	struct flash_info *info = fsm->info; +	uint32_t flags = info->flags; +	uint32_t data_pads; +	uint32_t offs; +	uint16_t sta_wr; +	uint8_t sr1, cr1, dyb; +	int update_sr = 0; +	int ret; + +	if (flags & FLASH_FLAG_32BIT_ADDR) { +		/* +		 * Prepare Read/Write/Erase sequences according to S25FLxxx +		 * 32-bit address command set +		 */ +		ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read, +						  stfsm_s25fl_read4_configs); +		if (ret) +			return ret; + +		ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_write, +						  stfsm_s25fl_write4_configs); +		if (ret) +			return ret; + +		stfsm_s25fl_prepare_erasesec_seq_32(&stfsm_seq_erase_sector); + +	} else { +		/* Use default configurations for 24-bit addressing */ +		ret = stfsm_prepare_rwe_seqs_default(fsm); +		if (ret) +			return ret; +	} + +	/* +	 * For devices that support 'DYB' sector locking, check lock status and +	 * unlock sectors if necessary (some variants power-on with sectors +	 * locked by default) +	 */ +	if (flags & FLASH_FLAG_DYB_LOCKING) { +		offs = 0; +		for (offs = 0; offs < info->sector_size * info->n_sectors;) { +			stfsm_s25fl_read_dyb(fsm, offs, &dyb); +			if (dyb == 0x00) +				stfsm_s25fl_write_dyb(fsm, offs, 0xff); + +			/* Handle bottom/top 4KiB parameter sectors */ +			if ((offs < info->sector_size * 2) || +			    (offs >= (info->sector_size - info->n_sectors * 4))) +				offs += 0x1000; +			else +				offs += 0x10000; +		} +	} + +	/* Check status of 'QE' bit, update if required. */ +	stfsm_read_status(fsm, SPINOR_OP_RDSR2, &cr1, 1); +	data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1; +	if (data_pads == 4) { +		if (!(cr1 & STFSM_S25FL_CONFIG_QE)) { +			/* Set 'QE' */ +			cr1 |= STFSM_S25FL_CONFIG_QE; + +			update_sr = 1; +		} +	} else { +		if (cr1 & STFSM_S25FL_CONFIG_QE) { +			/* Clear 'QE' */ +			cr1 &= ~STFSM_S25FL_CONFIG_QE; + +			update_sr = 1; +		} +	} +	if (update_sr) { +		stfsm_read_status(fsm, SPINOR_OP_RDSR, &sr1, 1); +		sta_wr = ((uint16_t)cr1  << 8) | sr1; +		stfsm_write_status(fsm, SPINOR_OP_WRSR, sta_wr, 2, 1); +	} + +	/* +	 * S25FLxxx devices support Program and Error error flags. +	 * Configure driver to check flags and clear if necessary. +	 */ +	fsm->configuration |= CFG_S25FL_CHECK_ERROR_FLAGS; + +	return 0; +} + +static int stfsm_w25q_config(struct stfsm *fsm) +{ +	uint32_t data_pads; +	uint8_t sr1, sr2; +	uint16_t sr_wr; +	int update_sr = 0; +	int ret; + +	ret = stfsm_prepare_rwe_seqs_default(fsm); +	if (ret) +		return ret; + +	/* Check status of 'QE' bit, update if required. */ +	stfsm_read_status(fsm, SPINOR_OP_RDSR2, &sr2, 1); +	data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1; +	if (data_pads == 4) { +		if (!(sr2 & W25Q_STATUS_QE)) { +			/* Set 'QE' */ +			sr2 |= W25Q_STATUS_QE; +			update_sr = 1; +		} +	} else { +		if (sr2 & W25Q_STATUS_QE) { +			/* Clear 'QE' */ +			sr2 &= ~W25Q_STATUS_QE; +			update_sr = 1; +		} +	} +	if (update_sr) { +		/* Write status register */ +		stfsm_read_status(fsm, SPINOR_OP_RDSR, &sr1, 1); +		sr_wr = ((uint16_t)sr2 << 8) | sr1; +		stfsm_write_status(fsm, SPINOR_OP_WRSR, sr_wr, 2, 1); +	} + +	return 0; +} + +static int stfsm_read(struct stfsm *fsm, uint8_t *buf, uint32_t size, +		      uint32_t offset) +{ +	struct stfsm_seq *seq = &fsm->stfsm_seq_read; +	uint32_t data_pads; +	uint32_t read_mask; +	uint32_t size_ub; +	uint32_t size_lb; +	uint32_t size_mop; +	uint32_t tmp[4]; +	uint32_t page_buf[FLASH_PAGESIZE_32]; +	uint8_t *p; + +	dev_dbg(fsm->dev, "reading %d bytes from 0x%08x\n", size, offset); + +	/* Enter 32-bit address mode, if required */ +	if (fsm->configuration & CFG_READ_TOGGLE_32BIT_ADDR) +		stfsm_enter_32bit_addr(fsm, 1); + +	/* Must read in multiples of 32 cycles (or 32*pads/8 Bytes) */ +	data_pads = ((seq->seq_cfg >> 16) & 0x3) + 1; +	read_mask = (data_pads << 2) - 1; + +	/* Handle non-aligned buf */ +	p = ((uintptr_t)buf & 0x3) ? (uint8_t *)page_buf : buf; + +	/* Handle non-aligned size */ +	size_ub = (size + read_mask) & ~read_mask; +	size_lb = size & ~read_mask; +	size_mop = size & read_mask; + +	seq->data_size = TRANSFER_SIZE(size_ub); +	seq->addr1 = (offset >> 16) & 0xffff; +	seq->addr2 = offset & 0xffff; + +	stfsm_load_seq(fsm, seq); + +	if (size_lb) +		stfsm_read_fifo(fsm, (uint32_t *)p, size_lb); + +	if (size_mop) { +		stfsm_read_fifo(fsm, tmp, read_mask + 1); +		memcpy(p + size_lb, &tmp, size_mop); +	} + +	/* Handle non-aligned buf */ +	if ((uintptr_t)buf & 0x3) +		memcpy(buf, page_buf, size); + +	/* Wait for sequence to finish */ +	stfsm_wait_seq(fsm); + +	stfsm_clear_fifo(fsm); + +	/* Exit 32-bit address mode, if required */ +	if (fsm->configuration & CFG_READ_TOGGLE_32BIT_ADDR) +		stfsm_enter_32bit_addr(fsm, 0); + +	return 0; +} + +static int stfsm_write(struct stfsm *fsm, const uint8_t *buf, +		       uint32_t size, uint32_t offset) +{ +	struct stfsm_seq *seq = &fsm->stfsm_seq_write; +	uint32_t data_pads; +	uint32_t write_mask; +	uint32_t size_ub; +	uint32_t size_lb; +	uint32_t size_mop; +	uint32_t tmp[4]; +	uint32_t page_buf[FLASH_PAGESIZE_32]; +	uint8_t *t = (uint8_t *)&tmp; +	const uint8_t *p; +	int ret; +	int i; + +	dev_dbg(fsm->dev, "writing %d bytes to 0x%08x\n", size, offset); + +	/* Enter 32-bit address mode, if required */ +	if (fsm->configuration & CFG_WRITE_TOGGLE_32BIT_ADDR) +		stfsm_enter_32bit_addr(fsm, 1); + +	/* Must write in multiples of 32 cycles (or 32*pads/8 bytes) */ +	data_pads = ((seq->seq_cfg >> 16) & 0x3) + 1; +	write_mask = (data_pads << 2) - 1; + +	/* Handle non-aligned buf */ +	if ((uintptr_t)buf & 0x3) { +		memcpy(page_buf, buf, size); +		p = (uint8_t *)page_buf; +	} else { +		p = buf; +	} + +	/* Handle non-aligned size */ +	size_ub = (size + write_mask) & ~write_mask; +	size_lb = size & ~write_mask; +	size_mop = size & write_mask; + +	seq->data_size = TRANSFER_SIZE(size_ub); +	seq->addr1 = (offset >> 16) & 0xffff; +	seq->addr2 = offset & 0xffff; + +	/* Need to set FIFO to write mode, before writing data to FIFO (see +	 * GNBvb79594) +	 */ +	writel(0x00040000, fsm->base + SPI_FAST_SEQ_CFG); + +	/* +	 * Before writing data to the FIFO, apply a small delay to allow a +	 * potential change of FIFO direction to complete. +	 */ +	if (fsm->fifo_dir_delay == 0) +		readl(fsm->base + SPI_FAST_SEQ_CFG); +	else +		udelay(fsm->fifo_dir_delay); + + +	/* Write data to FIFO, before starting sequence (see GNBvd79593) */ +	if (size_lb) { +		stfsm_write_fifo(fsm, (uint32_t *)p, size_lb); +		p += size_lb; +	} + +	/* Handle non-aligned size */ +	if (size_mop) { +		memset(t, 0xff, write_mask + 1);	/* fill with 0xff's */ +		for (i = 0; i < size_mop; i++) +			t[i] = *p++; + +		stfsm_write_fifo(fsm, tmp, write_mask + 1); +	} + +	/* Start sequence */ +	stfsm_load_seq(fsm, seq); + +	/* Wait for sequence to finish */ +	stfsm_wait_seq(fsm); + +	/* Wait for completion */ +	ret = stfsm_wait_busy(fsm); +	if (ret && fsm->configuration & CFG_S25FL_CHECK_ERROR_FLAGS) +		stfsm_s25fl_clear_status_reg(fsm); + +	/* Exit 32-bit address mode, if required */ +	if (fsm->configuration & CFG_WRITE_TOGGLE_32BIT_ADDR) +		stfsm_enter_32bit_addr(fsm, 0); + +	return 0; +} + +/* + * Read an address range from the flash chip. The address range + * may be any size provided it is within the physical boundaries. + */ +static int stfsm_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, +			  size_t *retlen, u_char *buf) +{ +	struct stfsm *fsm = dev_get_drvdata(mtd->dev.parent); +	uint32_t bytes; + +	dev_dbg(fsm->dev, "%s from 0x%08x, len %zd\n", +		__func__, (u32)from, len); + +	mutex_lock(&fsm->lock); + +	while (len > 0) { +		bytes = min_t(size_t, len, FLASH_PAGESIZE); + +		stfsm_read(fsm, buf, bytes, from); + +		buf += bytes; +		from += bytes; +		len -= bytes; + +		*retlen += bytes; +	} + +	mutex_unlock(&fsm->lock); + +	return 0; +} + +static int stfsm_erase_sector(struct stfsm *fsm, uint32_t offset) +{ +	struct stfsm_seq *seq = &stfsm_seq_erase_sector; +	int ret; + +	dev_dbg(fsm->dev, "erasing sector at 0x%08x\n", offset); + +	/* Enter 32-bit address mode, if required */ +	if (fsm->configuration & CFG_ERASESEC_TOGGLE_32BIT_ADDR) +		stfsm_enter_32bit_addr(fsm, 1); + +	seq->addr1 = (offset >> 16) & 0xffff; +	seq->addr2 = offset & 0xffff; + +	stfsm_load_seq(fsm, seq); + +	stfsm_wait_seq(fsm); + +	/* Wait for completion */ +	ret = stfsm_wait_busy(fsm); +	if (ret && fsm->configuration & CFG_S25FL_CHECK_ERROR_FLAGS) +		stfsm_s25fl_clear_status_reg(fsm); + +	/* Exit 32-bit address mode, if required */ +	if (fsm->configuration & CFG_ERASESEC_TOGGLE_32BIT_ADDR) +		stfsm_enter_32bit_addr(fsm, 0); + +	return ret; +} + +static int stfsm_erase_chip(struct stfsm *fsm) +{ +	const struct stfsm_seq *seq = &stfsm_seq_erase_chip; + +	dev_dbg(fsm->dev, "erasing chip\n"); + +	stfsm_load_seq(fsm, seq); + +	stfsm_wait_seq(fsm); + +	return stfsm_wait_busy(fsm); +} + +/* + * Write an address range to the flash chip.  Data must be written in + * FLASH_PAGESIZE chunks.  The address range may be any size provided + * it is within the physical boundaries. + */ +static int stfsm_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, +			   size_t *retlen, const u_char *buf) +{ +	struct stfsm *fsm = dev_get_drvdata(mtd->dev.parent); + +	u32 page_offs; +	u32 bytes; +	uint8_t *b = (uint8_t *)buf; +	int ret = 0; + +	dev_dbg(fsm->dev, "%s to 0x%08x, len %zd\n", __func__, (u32)to, len); + +	/* Offset within page */ +	page_offs = to % FLASH_PAGESIZE; + +	mutex_lock(&fsm->lock); + +	while (len) { +		/* Write up to page boundary */ +		bytes = min_t(size_t, FLASH_PAGESIZE - page_offs, len); + +		ret = stfsm_write(fsm, b, bytes, to); +		if (ret) +			goto out1; + +		b += bytes; +		len -= bytes; +		to += bytes; + +		/* We are now page-aligned */ +		page_offs = 0; + +		*retlen += bytes; + +	} + +out1: +	mutex_unlock(&fsm->lock); + +	return ret; +} + +/* + * Erase an address range on the flash chip. The address range may extend + * one or more erase sectors.  Return an error is there is a problem erasing. + */ +static int stfsm_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) +{ +	struct stfsm *fsm = dev_get_drvdata(mtd->dev.parent); +	u32 addr, len; +	int ret; + +	dev_dbg(fsm->dev, "%s at 0x%llx, len %lld\n", __func__, +		(long long)instr->addr, (long long)instr->len); + +	addr = instr->addr; +	len = instr->len; + +	mutex_lock(&fsm->lock); + +	/* Whole-chip erase? */ +	if (len == mtd->size) { +		ret = stfsm_erase_chip(fsm); +		if (ret) +			goto out1; +	} else { +		while (len) { +			ret = stfsm_erase_sector(fsm, addr); +			if (ret) +				goto out1; + +			addr += mtd->erasesize; +			len -= mtd->erasesize; +		} +	} + +	mutex_unlock(&fsm->lock); + +	instr->state = MTD_ERASE_DONE; +	mtd_erase_callback(instr); + +	return 0; + +out1: +	instr->state = MTD_ERASE_FAILED; +	mutex_unlock(&fsm->lock); + +	return ret; +} + +static void stfsm_read_jedec(struct stfsm *fsm, uint8_t *jedec) +{ +	const struct stfsm_seq *seq = &stfsm_seq_read_jedec; +	uint32_t tmp[2]; + +	stfsm_load_seq(fsm, seq); + +	stfsm_read_fifo(fsm, tmp, 8); + +	memcpy(jedec, tmp, 5); + +	stfsm_wait_seq(fsm); +} + +static struct flash_info *stfsm_jedec_probe(struct stfsm *fsm) +{ +	struct flash_info	*info; +	u16                     ext_jedec; +	u32			jedec; +	u8			id[5]; + +	stfsm_read_jedec(fsm, id); + +	jedec     = id[0] << 16 | id[1] << 8 | id[2]; +	/* +	 * JEDEC also defines an optional "extended device information" +	 * string for after vendor-specific data, after the three bytes +	 * we use here. Supporting some chips might require using it. +	 */ +	ext_jedec = id[3] << 8  | id[4]; + +	dev_dbg(fsm->dev, "JEDEC =  0x%08x [%02x %02x %02x %02x %02x]\n", +		jedec, id[0], id[1], id[2], id[3], id[4]); + +	for (info = flash_types; info->name; info++) { +		if (info->jedec_id == jedec) { +			if (info->ext_id && info->ext_id != ext_jedec) +				continue; +			return info; +		} +	} +	dev_err(fsm->dev, "Unrecognized JEDEC id %06x\n", jedec); + +	return NULL; +} + +static int stfsm_set_mode(struct stfsm *fsm, uint32_t mode) +{ +	int ret, timeout = 10; + +	/* Wait for controller to accept mode change */ +	while (--timeout) { +		ret = readl(fsm->base + SPI_STA_MODE_CHANGE); +		if (ret & 0x1) +			break; +		udelay(1); +	} + +	if (!timeout) +		return -EBUSY; + +	writel(mode, fsm->base + SPI_MODESELECT); + +	return 0; +} + +static void stfsm_set_freq(struct stfsm *fsm, uint32_t spi_freq) +{ +	uint32_t emi_freq; +	uint32_t clk_div; + +	/* TODO: Make this dynamic */ +	emi_freq = STFSM_DEFAULT_EMI_FREQ; + +	/* +	 * Calculate clk_div - values between 2 and 128 +	 * Multiple of 2, rounded up +	 */ +	clk_div = 2 * DIV_ROUND_UP(emi_freq, 2 * spi_freq); +	if (clk_div < 2) +		clk_div = 2; +	else if (clk_div > 128) +		clk_div = 128; + +	/* +	 * Determine a suitable delay for the IP to complete a change of +	 * direction of the FIFO. The required delay is related to the clock +	 * divider used. The following heuristics are based on empirical tests, +	 * using a 100MHz EMI clock. +	 */ +	if (clk_div <= 4) +		fsm->fifo_dir_delay = 0; +	else if (clk_div <= 10) +		fsm->fifo_dir_delay = 1; +	else +		fsm->fifo_dir_delay = DIV_ROUND_UP(clk_div, 10); + +	dev_dbg(fsm->dev, "emi_clk = %uHZ, spi_freq = %uHZ, clk_div = %u\n", +		emi_freq, spi_freq, clk_div); + +	writel(clk_div, fsm->base + SPI_CLOCKDIV); +} + +static int stfsm_init(struct stfsm *fsm) +{ +	int ret; + +	/* Perform a soft reset of the FSM controller */ +	writel(SEQ_CFG_SWRESET, fsm->base + SPI_FAST_SEQ_CFG); +	udelay(1); +	writel(0, fsm->base + SPI_FAST_SEQ_CFG); + +	/* Set clock to 'safe' frequency initially */ +	stfsm_set_freq(fsm, STFSM_FLASH_SAFE_FREQ); + +	/* Switch to FSM */ +	ret = stfsm_set_mode(fsm, SPI_MODESELECT_FSM); +	if (ret) +		return ret; + +	/* Set timing parameters */ +	writel(SPI_CFG_DEVICE_ST            | +	       SPI_CFG_DEFAULT_MIN_CS_HIGH  | +	       SPI_CFG_DEFAULT_CS_SETUPHOLD | +	       SPI_CFG_DEFAULT_DATA_HOLD, +	       fsm->base + SPI_CONFIGDATA); +	writel(STFSM_DEFAULT_WR_TIME, fsm->base + SPI_STATUS_WR_TIME_REG); + +	/* +	 * Set the FSM 'WAIT' delay to the minimum workable value.  Note, for +	 * our purposes, the WAIT instruction is used purely to achieve +	 * "sequence validity" rather than actually implement a delay. +	 */ +	writel(0x00000001, fsm->base + SPI_PROGRAM_ERASE_TIME); + +	/* Clear FIFO, just in case */ +	stfsm_clear_fifo(fsm); + +	return 0; +} + +static void stfsm_fetch_platform_configs(struct platform_device *pdev) +{ +	struct stfsm *fsm = platform_get_drvdata(pdev); +	struct device_node *np = pdev->dev.of_node; +	struct regmap *regmap; +	uint32_t boot_device_reg; +	uint32_t boot_device_spi; +	uint32_t boot_device;     /* Value we read from *boot_device_reg */ +	int ret; + +	/* Booting from SPI NOR Flash is the default */ +	fsm->booted_from_spi = true; + +	regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); +	if (IS_ERR(regmap)) +		goto boot_device_fail; + +	fsm->reset_signal = of_property_read_bool(np, "st,reset-signal"); + +	fsm->reset_por = of_property_read_bool(np, "st,reset-por"); + +	/* Where in the syscon the boot device information lives */ +	ret = of_property_read_u32(np, "st,boot-device-reg", &boot_device_reg); +	if (ret) +		goto boot_device_fail; + +	/* Boot device value when booted from SPI NOR */ +	ret = of_property_read_u32(np, "st,boot-device-spi", &boot_device_spi); +	if (ret) +		goto boot_device_fail; + +	ret = regmap_read(regmap, boot_device_reg, &boot_device); +	if (ret) +		goto boot_device_fail; + +	if (boot_device != boot_device_spi) +		fsm->booted_from_spi = false; + +	return; + +boot_device_fail: +	dev_warn(&pdev->dev, +		 "failed to fetch boot device, assuming boot from SPI\n"); +} + +static int stfsm_probe(struct platform_device *pdev) +{ +	struct device_node *np = pdev->dev.of_node; +	struct mtd_part_parser_data ppdata; +	struct flash_info *info; +	struct resource *res; +	struct stfsm *fsm; +	int ret; + +	if (!np) { +		dev_err(&pdev->dev, "No DT found\n"); +		return -EINVAL; +	} +	ppdata.of_node = np; + +	fsm = devm_kzalloc(&pdev->dev, sizeof(*fsm), GFP_KERNEL); +	if (!fsm) +		return -ENOMEM; + +	fsm->dev = &pdev->dev; + +	platform_set_drvdata(pdev, fsm); + +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!res) { +		dev_err(&pdev->dev, "Resource not found\n"); +		return -ENODEV; +	} + +	fsm->base = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(fsm->base)) { +		dev_err(&pdev->dev, +			"Failed to reserve memory region %pR\n", res); +		return PTR_ERR(fsm->base); +	} + +	mutex_init(&fsm->lock); + +	ret = stfsm_init(fsm); +	if (ret) { +		dev_err(&pdev->dev, "Failed to initialise FSM Controller\n"); +		return ret; +	} + +	stfsm_fetch_platform_configs(pdev); + +	/* Detect SPI FLASH device */ +	info = stfsm_jedec_probe(fsm); +	if (!info) +		return -ENODEV; +	fsm->info = info; + +	/* Use device size to determine address width */ +	if (info->sector_size * info->n_sectors > 0x1000000) +		info->flags |= FLASH_FLAG_32BIT_ADDR; + +	/* +	 * Configure READ/WRITE/ERASE sequences according to platform and +	 * device flags. +	 */ +	if (info->config) { +		ret = info->config(fsm); +		if (ret) +			return ret; +	} else { +		ret = stfsm_prepare_rwe_seqs_default(fsm); +		if (ret) +			return ret; +	} + +	fsm->mtd.name		= info->name; +	fsm->mtd.dev.parent	= &pdev->dev; +	fsm->mtd.type		= MTD_NORFLASH; +	fsm->mtd.writesize	= 4; +	fsm->mtd.writebufsize	= fsm->mtd.writesize; +	fsm->mtd.flags		= MTD_CAP_NORFLASH; +	fsm->mtd.size		= info->sector_size * info->n_sectors; +	fsm->mtd.erasesize	= info->sector_size; + +	fsm->mtd._read  = stfsm_mtd_read; +	fsm->mtd._write = stfsm_mtd_write; +	fsm->mtd._erase = stfsm_mtd_erase; + +	dev_info(&pdev->dev, +		"Found serial flash device: %s\n" +		" size = %llx (%lldMiB) erasesize = 0x%08x (%uKiB)\n", +		info->name, +		(long long)fsm->mtd.size, (long long)(fsm->mtd.size >> 20), +		fsm->mtd.erasesize, (fsm->mtd.erasesize >> 10)); + +	return mtd_device_parse_register(&fsm->mtd, NULL, &ppdata, NULL, 0); +} + +static int stfsm_remove(struct platform_device *pdev) +{ +	struct stfsm *fsm = platform_get_drvdata(pdev); + +	return mtd_device_unregister(&fsm->mtd); +} + +static const struct of_device_id stfsm_match[] = { +	{ .compatible = "st,spi-fsm", }, +	{}, +}; +MODULE_DEVICE_TABLE(of, stfsm_match); + +static struct platform_driver stfsm_driver = { +	.probe		= stfsm_probe, +	.remove		= stfsm_remove, +	.driver		= { +		.name	= "st-spi-fsm", +		.owner	= THIS_MODULE, +		.of_match_table = stfsm_match, +	}, +}; +module_platform_driver(stfsm_driver); + +MODULE_AUTHOR("Angus Clark <angus.clark@st.com>"); +MODULE_DESCRIPTION("ST SPI FSM driver"); +MODULE_LICENSE("GPL");  | 
