xref: /linux/drivers/mtd/devices/mtd_dataflash.c (revision 2f4c53349961c8ca480193e47da4d44fdb8335a8)
1*2874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21d6432feSDavid Brownell /*
31d6432feSDavid Brownell  * Atmel AT45xxx DataFlash MTD driver for lightweight SPI framework
41d6432feSDavid Brownell  *
51d6432feSDavid Brownell  * Largely derived from at91_dataflash.c:
61d6432feSDavid Brownell  *  Copyright (C) 2003-2005 SAN People (Pty) Ltd
71d6432feSDavid Brownell */
81d6432feSDavid Brownell #include <linux/module.h>
91d6432feSDavid Brownell #include <linux/slab.h>
101d6432feSDavid Brownell #include <linux/delay.h>
111d6432feSDavid Brownell #include <linux/device.h>
12ec9ce52eSMatthias Kaehlcke #include <linux/mutex.h>
13771999b6Sakpm@linux-foundation.org #include <linux/err.h>
145b7f3a50SArtem Bityutskiy #include <linux/math64.h>
15b94e757cSShawn Guo #include <linux/of.h>
16b94e757cSShawn Guo #include <linux/of_device.h>
17771999b6Sakpm@linux-foundation.org 
181d6432feSDavid Brownell #include <linux/spi/spi.h>
191d6432feSDavid Brownell #include <linux/spi/flash.h>
201d6432feSDavid Brownell 
211d6432feSDavid Brownell #include <linux/mtd/mtd.h>
221d6432feSDavid Brownell #include <linux/mtd/partitions.h>
231d6432feSDavid Brownell 
241d6432feSDavid Brownell /*
251d6432feSDavid Brownell  * DataFlash is a kind of SPI flash.  Most AT45 chips have two buffers in
261d6432feSDavid Brownell  * each chip, which may be used for double buffered I/O; but this driver
271d6432feSDavid Brownell  * doesn't (yet) use these for any kind of i/o overlap or prefetching.
281d6432feSDavid Brownell  *
291d6432feSDavid Brownell  * Sometimes DataFlash is packaged in MMC-format cards, although the
308c64038eSDavid Brownell  * MMC stack can't (yet?) distinguish between MMC and DataFlash
311d6432feSDavid Brownell  * protocols during enumeration.
321d6432feSDavid Brownell  */
331d6432feSDavid Brownell 
341d6432feSDavid Brownell /* reads can bypass the buffers */
351d6432feSDavid Brownell #define OP_READ_CONTINUOUS	0xE8
361d6432feSDavid Brownell #define OP_READ_PAGE		0xD2
371d6432feSDavid Brownell 
381d6432feSDavid Brownell /* group B requests can run even while status reports "busy" */
391d6432feSDavid Brownell #define OP_READ_STATUS		0xD7	/* group B */
401d6432feSDavid Brownell 
411d6432feSDavid Brownell /* move data between host and buffer */
421d6432feSDavid Brownell #define OP_READ_BUFFER1		0xD4	/* group B */
431d6432feSDavid Brownell #define OP_READ_BUFFER2		0xD6	/* group B */
441d6432feSDavid Brownell #define OP_WRITE_BUFFER1	0x84	/* group B */
451d6432feSDavid Brownell #define OP_WRITE_BUFFER2	0x87	/* group B */
461d6432feSDavid Brownell 
471d6432feSDavid Brownell /* erasing flash */
481d6432feSDavid Brownell #define OP_ERASE_PAGE		0x81
491d6432feSDavid Brownell #define OP_ERASE_BLOCK		0x50
501d6432feSDavid Brownell 
511d6432feSDavid Brownell /* move data between buffer and flash */
521d6432feSDavid Brownell #define OP_TRANSFER_BUF1	0x53
531d6432feSDavid Brownell #define OP_TRANSFER_BUF2	0x55
541d6432feSDavid Brownell #define OP_MREAD_BUFFER1	0xD4
551d6432feSDavid Brownell #define OP_MREAD_BUFFER2	0xD6
561d6432feSDavid Brownell #define OP_MWERASE_BUFFER1	0x83
571d6432feSDavid Brownell #define OP_MWERASE_BUFFER2	0x86
581d6432feSDavid Brownell #define OP_MWRITE_BUFFER1	0x88	/* sector must be pre-erased */
591d6432feSDavid Brownell #define OP_MWRITE_BUFFER2	0x89	/* sector must be pre-erased */
601d6432feSDavid Brownell 
611d6432feSDavid Brownell /* write to buffer, then write-erase to flash */
621d6432feSDavid Brownell #define OP_PROGRAM_VIA_BUF1	0x82
631d6432feSDavid Brownell #define OP_PROGRAM_VIA_BUF2	0x85
641d6432feSDavid Brownell 
651d6432feSDavid Brownell /* compare buffer to flash */
661d6432feSDavid Brownell #define OP_COMPARE_BUF1		0x60
671d6432feSDavid Brownell #define OP_COMPARE_BUF2		0x61
681d6432feSDavid Brownell 
691d6432feSDavid Brownell /* read flash to buffer, then write-erase to flash */
701d6432feSDavid Brownell #define OP_REWRITE_VIA_BUF1	0x58
711d6432feSDavid Brownell #define OP_REWRITE_VIA_BUF2	0x59
721d6432feSDavid Brownell 
731d6432feSDavid Brownell /* newer chips report JEDEC manufacturer and device IDs; chip
741d6432feSDavid Brownell  * serial number and OTP bits; and per-sector writeprotect.
751d6432feSDavid Brownell  */
761d6432feSDavid Brownell #define OP_READ_ID		0x9F
771d6432feSDavid Brownell #define OP_READ_SECURITY	0x77
7834a82443SDavid Brownell #define OP_WRITE_SECURITY_REVC	0x9A
7934a82443SDavid Brownell #define OP_WRITE_SECURITY	0x9B	/* revision D */
801d6432feSDavid Brownell 
8141c9c662SAndrey Smirnov #define CFI_MFR_ATMEL		0x1F
821d6432feSDavid Brownell 
831da8869aSAndrey Smirnov #define DATAFLASH_SHIFT_EXTID	24
841da8869aSAndrey Smirnov #define DATAFLASH_SHIFT_ID	40
851da8869aSAndrey Smirnov 
861d6432feSDavid Brownell struct dataflash {
87c41e43c6SAndrey Smirnov 	u8			command[4];
881d6432feSDavid Brownell 	char			name[24];
891d6432feSDavid Brownell 
901d6432feSDavid Brownell 	unsigned short		page_offset;	/* offset in flash address */
911d6432feSDavid Brownell 	unsigned int		page_size;	/* of bytes per page */
921d6432feSDavid Brownell 
93ec9ce52eSMatthias Kaehlcke 	struct mutex		lock;
941d6432feSDavid Brownell 	struct spi_device	*spi;
951d6432feSDavid Brownell 
961d6432feSDavid Brownell 	struct mtd_info		mtd;
971d6432feSDavid Brownell };
981d6432feSDavid Brownell 
99b94e757cSShawn Guo #ifdef CONFIG_OF
100b94e757cSShawn Guo static const struct of_device_id dataflash_dt_ids[] = {
101b94e757cSShawn Guo 	{ .compatible = "atmel,at45", },
102b94e757cSShawn Guo 	{ .compatible = "atmel,dataflash", },
103b94e757cSShawn Guo 	{ /* sentinel */ }
104b94e757cSShawn Guo };
105d1d97b76SJavier Martinez Canillas MODULE_DEVICE_TABLE(of, dataflash_dt_ids);
106b94e757cSShawn Guo #endif
107b94e757cSShawn Guo 
1081d6432feSDavid Brownell /* ......................................................................... */
1091d6432feSDavid Brownell 
1101d6432feSDavid Brownell /*
1111d6432feSDavid Brownell  * Return the status of the DataFlash device.
1121d6432feSDavid Brownell  */
1131d6432feSDavid Brownell static inline int dataflash_status(struct spi_device *spi)
1141d6432feSDavid Brownell {
1151d6432feSDavid Brownell 	/* NOTE:  at45db321c over 25 MHz wants to write
1161d6432feSDavid Brownell 	 * a dummy byte after the opcode...
1171d6432feSDavid Brownell 	 */
1181d6432feSDavid Brownell 	return spi_w8r8(spi, OP_READ_STATUS);
1191d6432feSDavid Brownell }
1201d6432feSDavid Brownell 
1211d6432feSDavid Brownell /*
1221d6432feSDavid Brownell  * Poll the DataFlash device until it is READY.
1231d6432feSDavid Brownell  * This usually takes 5-20 msec or so; more for sector erase.
1241d6432feSDavid Brownell  */
1251d6432feSDavid Brownell static int dataflash_waitready(struct spi_device *spi)
1261d6432feSDavid Brownell {
1271d6432feSDavid Brownell 	int	status;
1281d6432feSDavid Brownell 
1291d6432feSDavid Brownell 	for (;;) {
1301d6432feSDavid Brownell 		status = dataflash_status(spi);
1311d6432feSDavid Brownell 		if (status < 0) {
13202f62864SAndrey Smirnov 			dev_dbg(&spi->dev, "status %d?\n", status);
1331d6432feSDavid Brownell 			status = 0;
1341d6432feSDavid Brownell 		}
1351d6432feSDavid Brownell 
1361d6432feSDavid Brownell 		if (status & (1 << 7))	/* RDY/nBSY */
1371d6432feSDavid Brownell 			return status;
1381d6432feSDavid Brownell 
139c3c9a2c4SLuca Ellero 		usleep_range(3000, 4000);
1401d6432feSDavid Brownell 	}
1411d6432feSDavid Brownell }
1421d6432feSDavid Brownell 
1431d6432feSDavid Brownell /* ......................................................................... */
1441d6432feSDavid Brownell 
1451d6432feSDavid Brownell /*
1461d6432feSDavid Brownell  * Erase pages of flash.
1471d6432feSDavid Brownell  */
1481d6432feSDavid Brownell static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
1491d6432feSDavid Brownell {
15042845d2aSKulikov Vasiliy 	struct dataflash	*priv = mtd->priv;
1511d6432feSDavid Brownell 	struct spi_device	*spi = priv->spi;
1527a95db03SMark Brown 	struct spi_transfer	x = { };
1531d6432feSDavid Brownell 	struct spi_message	msg;
1541d6432feSDavid Brownell 	unsigned		blocksize = priv->page_size << 3;
155c41e43c6SAndrey Smirnov 	u8			*command;
156c41e43c6SAndrey Smirnov 	u32			rem;
1571d6432feSDavid Brownell 
15802f62864SAndrey Smirnov 	dev_dbg(&spi->dev, "erase addr=0x%llx len 0x%llx\n",
15902f62864SAndrey Smirnov 		(long long)instr->addr, (long long)instr->len);
1601d6432feSDavid Brownell 
1615b7f3a50SArtem Bityutskiy 	div_u64_rem(instr->len, priv->page_size, &rem);
1625b7f3a50SArtem Bityutskiy 	if (rem)
1635b7f3a50SArtem Bityutskiy 		return -EINVAL;
1645b7f3a50SArtem Bityutskiy 	div_u64_rem(instr->addr, priv->page_size, &rem);
1655b7f3a50SArtem Bityutskiy 	if (rem)
1661d6432feSDavid Brownell 		return -EINVAL;
1671d6432feSDavid Brownell 
1688275c642SVitaly Wool 	spi_message_init(&msg);
1698275c642SVitaly Wool 
1708275c642SVitaly Wool 	x.tx_buf = command = priv->command;
1718275c642SVitaly Wool 	x.len = 4;
1728275c642SVitaly Wool 	spi_message_add_tail(&x, &msg);
1731d6432feSDavid Brownell 
174ec9ce52eSMatthias Kaehlcke 	mutex_lock(&priv->lock);
1751d6432feSDavid Brownell 	while (instr->len > 0) {
1761d6432feSDavid Brownell 		unsigned int	pageaddr;
1771d6432feSDavid Brownell 		int		status;
1781d6432feSDavid Brownell 		int		do_block;
1791d6432feSDavid Brownell 
1801d6432feSDavid Brownell 		/* Calculate flash page address; use block erase (for speed) if
1811d6432feSDavid Brownell 		 * we're at a block boundary and need to erase the whole block.
1821d6432feSDavid Brownell 		 */
183dbf8c11fSPeter Korsgaard 		pageaddr = div_u64(instr->addr, priv->page_size);
1843cb4f09fSDavid Brownell 		do_block = (pageaddr & 0x7) == 0 && instr->len >= blocksize;
1851d6432feSDavid Brownell 		pageaddr = pageaddr << priv->page_offset;
1861d6432feSDavid Brownell 
1871d6432feSDavid Brownell 		command[0] = do_block ? OP_ERASE_BLOCK : OP_ERASE_PAGE;
188c41e43c6SAndrey Smirnov 		command[1] = (u8)(pageaddr >> 16);
189c41e43c6SAndrey Smirnov 		command[2] = (u8)(pageaddr >> 8);
1901d6432feSDavid Brownell 		command[3] = 0;
1911d6432feSDavid Brownell 
19202f62864SAndrey Smirnov 		dev_dbg(&spi->dev, "ERASE %s: (%x) %x %x %x [%i]\n",
1931d6432feSDavid Brownell 			do_block ? "block" : "page",
1941d6432feSDavid Brownell 			command[0], command[1], command[2], command[3],
1951d6432feSDavid Brownell 			pageaddr);
1961d6432feSDavid Brownell 
1971d6432feSDavid Brownell 		status = spi_sync(spi, &msg);
1981d6432feSDavid Brownell 		(void) dataflash_waitready(spi);
1991d6432feSDavid Brownell 
2001d6432feSDavid Brownell 		if (status < 0) {
20102f62864SAndrey Smirnov 			dev_err(&spi->dev, "erase %x, err %d\n",
20202f62864SAndrey Smirnov 				pageaddr, status);
2031d6432feSDavid Brownell 			/* REVISIT:  can retry instr->retries times; or
2041d6432feSDavid Brownell 			 * giveup and instr->fail_addr = instr->addr;
2051d6432feSDavid Brownell 			 */
2061d6432feSDavid Brownell 			continue;
2071d6432feSDavid Brownell 		}
2081d6432feSDavid Brownell 
2091d6432feSDavid Brownell 		if (do_block) {
2101d6432feSDavid Brownell 			instr->addr += blocksize;
2111d6432feSDavid Brownell 			instr->len -= blocksize;
2121d6432feSDavid Brownell 		} else {
2131d6432feSDavid Brownell 			instr->addr += priv->page_size;
2141d6432feSDavid Brownell 			instr->len -= priv->page_size;
2151d6432feSDavid Brownell 		}
2161d6432feSDavid Brownell 	}
217ec9ce52eSMatthias Kaehlcke 	mutex_unlock(&priv->lock);
2181d6432feSDavid Brownell 
2191d6432feSDavid Brownell 	return 0;
2201d6432feSDavid Brownell }
2211d6432feSDavid Brownell 
2221d6432feSDavid Brownell /*
2231d6432feSDavid Brownell  * Read from the DataFlash device.
2241d6432feSDavid Brownell  *   from   : Start offset in flash device
2251d6432feSDavid Brownell  *   len    : Amount to read
2261d6432feSDavid Brownell  *   retlen : About of data actually read
2271d6432feSDavid Brownell  *   buf    : Buffer containing the data
2281d6432feSDavid Brownell  */
2291d6432feSDavid Brownell static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
2301d6432feSDavid Brownell 			       size_t *retlen, u_char *buf)
2311d6432feSDavid Brownell {
23242845d2aSKulikov Vasiliy 	struct dataflash	*priv = mtd->priv;
2337a95db03SMark Brown 	struct spi_transfer	x[2] = { };
2341d6432feSDavid Brownell 	struct spi_message	msg;
2351d6432feSDavid Brownell 	unsigned int		addr;
236c41e43c6SAndrey Smirnov 	u8			*command;
2371d6432feSDavid Brownell 	int			status;
2381d6432feSDavid Brownell 
23902f62864SAndrey Smirnov 	dev_dbg(&priv->spi->dev, "read 0x%x..0x%x\n",
24002f62864SAndrey Smirnov 		  (unsigned int)from, (unsigned int)(from + len));
2411d6432feSDavid Brownell 
2421d6432feSDavid Brownell 	/* Calculate flash page/byte address */
2431d6432feSDavid Brownell 	addr = (((unsigned)from / priv->page_size) << priv->page_offset)
2441d6432feSDavid Brownell 		+ ((unsigned)from % priv->page_size);
2451d6432feSDavid Brownell 
2461d6432feSDavid Brownell 	command = priv->command;
2471d6432feSDavid Brownell 
24802f62864SAndrey Smirnov 	dev_dbg(&priv->spi->dev, "READ: (%x) %x %x %x\n",
2491d6432feSDavid Brownell 		command[0], command[1], command[2], command[3]);
2501d6432feSDavid Brownell 
2518275c642SVitaly Wool 	spi_message_init(&msg);
2528275c642SVitaly Wool 
2531d6432feSDavid Brownell 	x[0].tx_buf = command;
2541d6432feSDavid Brownell 	x[0].len = 8;
2558275c642SVitaly Wool 	spi_message_add_tail(&x[0], &msg);
2568275c642SVitaly Wool 
2571d6432feSDavid Brownell 	x[1].rx_buf = buf;
2581d6432feSDavid Brownell 	x[1].len = len;
2598275c642SVitaly Wool 	spi_message_add_tail(&x[1], &msg);
2601d6432feSDavid Brownell 
261ec9ce52eSMatthias Kaehlcke 	mutex_lock(&priv->lock);
2621d6432feSDavid Brownell 
2631d6432feSDavid Brownell 	/* Continuous read, max clock = f(car) which may be less than
2641d6432feSDavid Brownell 	 * the peak rate available.  Some chips support commands with
2651d6432feSDavid Brownell 	 * fewer "don't care" bytes.  Both buffers stay unchanged.
2661d6432feSDavid Brownell 	 */
2671d6432feSDavid Brownell 	command[0] = OP_READ_CONTINUOUS;
268c41e43c6SAndrey Smirnov 	command[1] = (u8)(addr >> 16);
269c41e43c6SAndrey Smirnov 	command[2] = (u8)(addr >> 8);
270c41e43c6SAndrey Smirnov 	command[3] = (u8)(addr >> 0);
2711d6432feSDavid Brownell 	/* plus 4 "don't care" bytes */
2721d6432feSDavid Brownell 
2731d6432feSDavid Brownell 	status = spi_sync(priv->spi, &msg);
274ec9ce52eSMatthias Kaehlcke 	mutex_unlock(&priv->lock);
2751d6432feSDavid Brownell 
2761d6432feSDavid Brownell 	if (status >= 0) {
2771d6432feSDavid Brownell 		*retlen = msg.actual_length - 8;
2781d6432feSDavid Brownell 		status = 0;
2791d6432feSDavid Brownell 	} else
28002f62864SAndrey Smirnov 		dev_dbg(&priv->spi->dev, "read %x..%x --> %d\n",
2811d6432feSDavid Brownell 			(unsigned)from, (unsigned)(from + len),
2821d6432feSDavid Brownell 			status);
2831d6432feSDavid Brownell 	return status;
2841d6432feSDavid Brownell }
2851d6432feSDavid Brownell 
2861d6432feSDavid Brownell /*
2871d6432feSDavid Brownell  * Write to the DataFlash device.
2881d6432feSDavid Brownell  *   to     : Start offset in flash device
2891d6432feSDavid Brownell  *   len    : Amount to write
2901d6432feSDavid Brownell  *   retlen : Amount of data actually written
2911d6432feSDavid Brownell  *   buf    : Buffer containing the data
2921d6432feSDavid Brownell  */
2931d6432feSDavid Brownell static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
2941d6432feSDavid Brownell 				size_t * retlen, const u_char * buf)
2951d6432feSDavid Brownell {
29642845d2aSKulikov Vasiliy 	struct dataflash	*priv = mtd->priv;
2971d6432feSDavid Brownell 	struct spi_device	*spi = priv->spi;
2987a95db03SMark Brown 	struct spi_transfer	x[2] = { };
2991d6432feSDavid Brownell 	struct spi_message	msg;
3001d6432feSDavid Brownell 	unsigned int		pageaddr, addr, offset, writelen;
3011d6432feSDavid Brownell 	size_t			remaining = len;
3021d6432feSDavid Brownell 	u_char			*writebuf = (u_char *) buf;
3031d6432feSDavid Brownell 	int			status = -EINVAL;
304c41e43c6SAndrey Smirnov 	u8			*command;
3051d6432feSDavid Brownell 
30602f62864SAndrey Smirnov 	dev_dbg(&spi->dev, "write 0x%x..0x%x\n",
30702f62864SAndrey Smirnov 		(unsigned int)to, (unsigned int)(to + len));
3081d6432feSDavid Brownell 
3098275c642SVitaly Wool 	spi_message_init(&msg);
3108275c642SVitaly Wool 
3111d6432feSDavid Brownell 	x[0].tx_buf = command = priv->command;
3121d6432feSDavid Brownell 	x[0].len = 4;
3138275c642SVitaly Wool 	spi_message_add_tail(&x[0], &msg);
3141d6432feSDavid Brownell 
3151d6432feSDavid Brownell 	pageaddr = ((unsigned)to / priv->page_size);
3161d6432feSDavid Brownell 	offset = ((unsigned)to % priv->page_size);
3171d6432feSDavid Brownell 	if (offset + len > priv->page_size)
3181d6432feSDavid Brownell 		writelen = priv->page_size - offset;
3191d6432feSDavid Brownell 	else
3201d6432feSDavid Brownell 		writelen = len;
3211d6432feSDavid Brownell 
322ec9ce52eSMatthias Kaehlcke 	mutex_lock(&priv->lock);
3231d6432feSDavid Brownell 	while (remaining > 0) {
32402f62864SAndrey Smirnov 		dev_dbg(&spi->dev, "write @ %i:%i len=%i\n",
3251d6432feSDavid Brownell 			pageaddr, offset, writelen);
3261d6432feSDavid Brownell 
3271d6432feSDavid Brownell 		/* REVISIT:
3281d6432feSDavid Brownell 		 * (a) each page in a sector must be rewritten at least
3291d6432feSDavid Brownell 		 *     once every 10K sibling erase/program operations.
3301d6432feSDavid Brownell 		 * (b) for pages that are already erased, we could
3311d6432feSDavid Brownell 		 *     use WRITE+MWRITE not PROGRAM for ~30% speedup.
3321d6432feSDavid Brownell 		 * (c) WRITE to buffer could be done while waiting for
3331d6432feSDavid Brownell 		 *     a previous MWRITE/MWERASE to complete ...
3341d6432feSDavid Brownell 		 * (d) error handling here seems to be mostly missing.
3351d6432feSDavid Brownell 		 *
3361d6432feSDavid Brownell 		 * Two persistent bits per page, plus a per-sector counter,
3371d6432feSDavid Brownell 		 * could support (a) and (b) ... we might consider using
3381d6432feSDavid Brownell 		 * the second half of sector zero, which is just one block,
3391d6432feSDavid Brownell 		 * to track that state.  (On AT91, that sector should also
3401d6432feSDavid Brownell 		 * support boot-from-DataFlash.)
3411d6432feSDavid Brownell 		 */
3421d6432feSDavid Brownell 
3431d6432feSDavid Brownell 		addr = pageaddr << priv->page_offset;
3441d6432feSDavid Brownell 
3451d6432feSDavid Brownell 		/* (1) Maybe transfer partial page to Buffer1 */
3461d6432feSDavid Brownell 		if (writelen != priv->page_size) {
3471d6432feSDavid Brownell 			command[0] = OP_TRANSFER_BUF1;
3481d6432feSDavid Brownell 			command[1] = (addr & 0x00FF0000) >> 16;
3491d6432feSDavid Brownell 			command[2] = (addr & 0x0000FF00) >> 8;
3501d6432feSDavid Brownell 			command[3] = 0;
3511d6432feSDavid Brownell 
35202f62864SAndrey Smirnov 			dev_dbg(&spi->dev, "TRANSFER: (%x) %x %x %x\n",
3531d6432feSDavid Brownell 				command[0], command[1], command[2], command[3]);
3541d6432feSDavid Brownell 
3551d6432feSDavid Brownell 			status = spi_sync(spi, &msg);
3561d6432feSDavid Brownell 			if (status < 0)
35702f62864SAndrey Smirnov 				dev_dbg(&spi->dev, "xfer %u -> %d\n",
35802f62864SAndrey Smirnov 					addr, status);
3591d6432feSDavid Brownell 
3601d6432feSDavid Brownell 			(void) dataflash_waitready(priv->spi);
3611d6432feSDavid Brownell 		}
3621d6432feSDavid Brownell 
3631d6432feSDavid Brownell 		/* (2) Program full page via Buffer1 */
3641d6432feSDavid Brownell 		addr += offset;
3651d6432feSDavid Brownell 		command[0] = OP_PROGRAM_VIA_BUF1;
3661d6432feSDavid Brownell 		command[1] = (addr & 0x00FF0000) >> 16;
3671d6432feSDavid Brownell 		command[2] = (addr & 0x0000FF00) >> 8;
3681d6432feSDavid Brownell 		command[3] = (addr & 0x000000FF);
3691d6432feSDavid Brownell 
37002f62864SAndrey Smirnov 		dev_dbg(&spi->dev, "PROGRAM: (%x) %x %x %x\n",
3711d6432feSDavid Brownell 			command[0], command[1], command[2], command[3]);
3721d6432feSDavid Brownell 
3731d6432feSDavid Brownell 		x[1].tx_buf = writebuf;
3741d6432feSDavid Brownell 		x[1].len = writelen;
3758275c642SVitaly Wool 		spi_message_add_tail(x + 1, &msg);
3761d6432feSDavid Brownell 		status = spi_sync(spi, &msg);
3778275c642SVitaly Wool 		spi_transfer_del(x + 1);
3781d6432feSDavid Brownell 		if (status < 0)
37902f62864SAndrey Smirnov 			dev_dbg(&spi->dev, "pgm %u/%u -> %d\n",
38002f62864SAndrey Smirnov 				addr, writelen, status);
3811d6432feSDavid Brownell 
3821d6432feSDavid Brownell 		(void) dataflash_waitready(priv->spi);
3831d6432feSDavid Brownell 
3848275c642SVitaly Wool 
38505dd1807SRobert P. J. Day #ifdef CONFIG_MTD_DATAFLASH_WRITE_VERIFY
3861d6432feSDavid Brownell 
3871d6432feSDavid Brownell 		/* (3) Compare to Buffer1 */
3881d6432feSDavid Brownell 		addr = pageaddr << priv->page_offset;
3891d6432feSDavid Brownell 		command[0] = OP_COMPARE_BUF1;
3901d6432feSDavid Brownell 		command[1] = (addr & 0x00FF0000) >> 16;
3911d6432feSDavid Brownell 		command[2] = (addr & 0x0000FF00) >> 8;
3921d6432feSDavid Brownell 		command[3] = 0;
3931d6432feSDavid Brownell 
39402f62864SAndrey Smirnov 		dev_dbg(&spi->dev, "COMPARE: (%x) %x %x %x\n",
3951d6432feSDavid Brownell 			command[0], command[1], command[2], command[3]);
3961d6432feSDavid Brownell 
3971d6432feSDavid Brownell 		status = spi_sync(spi, &msg);
3981d6432feSDavid Brownell 		if (status < 0)
39902f62864SAndrey Smirnov 			dev_dbg(&spi->dev, "compare %u -> %d\n",
40002f62864SAndrey Smirnov 				addr, status);
4011d6432feSDavid Brownell 
4021d6432feSDavid Brownell 		status = dataflash_waitready(priv->spi);
4031d6432feSDavid Brownell 
4041d6432feSDavid Brownell 		/* Check result of the compare operation */
405cccb45d4SAndrew Victor 		if (status & (1 << 6)) {
40602f62864SAndrey Smirnov 			dev_err(&spi->dev, "compare page %u, err %d\n",
40702f62864SAndrey Smirnov 				pageaddr, status);
4081d6432feSDavid Brownell 			remaining = 0;
4091d6432feSDavid Brownell 			status = -EIO;
4101d6432feSDavid Brownell 			break;
4111d6432feSDavid Brownell 		} else
4121d6432feSDavid Brownell 			status = 0;
4131d6432feSDavid Brownell 
41405dd1807SRobert P. J. Day #endif	/* CONFIG_MTD_DATAFLASH_WRITE_VERIFY */
4151d6432feSDavid Brownell 
4161d6432feSDavid Brownell 		remaining = remaining - writelen;
4171d6432feSDavid Brownell 		pageaddr++;
4181d6432feSDavid Brownell 		offset = 0;
4191d6432feSDavid Brownell 		writebuf += writelen;
4201d6432feSDavid Brownell 		*retlen += writelen;
4211d6432feSDavid Brownell 
4221d6432feSDavid Brownell 		if (remaining > priv->page_size)
4231d6432feSDavid Brownell 			writelen = priv->page_size;
4241d6432feSDavid Brownell 		else
4251d6432feSDavid Brownell 			writelen = remaining;
4261d6432feSDavid Brownell 	}
427ec9ce52eSMatthias Kaehlcke 	mutex_unlock(&priv->lock);
4281d6432feSDavid Brownell 
4291d6432feSDavid Brownell 	return status;
4301d6432feSDavid Brownell }
4311d6432feSDavid Brownell 
4321d6432feSDavid Brownell /* ......................................................................... */
4331d6432feSDavid Brownell 
43434a82443SDavid Brownell #ifdef CONFIG_MTD_DATAFLASH_OTP
43534a82443SDavid Brownell 
4364b78fc42SChristian Riesch static int dataflash_get_otp_info(struct mtd_info *mtd, size_t len,
4374b78fc42SChristian Riesch 				  size_t *retlen, struct otp_info *info)
43834a82443SDavid Brownell {
43934a82443SDavid Brownell 	/* Report both blocks as identical:  bytes 0..64, locked.
44034a82443SDavid Brownell 	 * Unless the user block changed from all-ones, we can't
44134a82443SDavid Brownell 	 * tell whether it's still writable; so we assume it isn't.
44234a82443SDavid Brownell 	 */
44334a82443SDavid Brownell 	info->start = 0;
44434a82443SDavid Brownell 	info->length = 64;
44534a82443SDavid Brownell 	info->locked = 1;
4464b78fc42SChristian Riesch 	*retlen = sizeof(*info);
4474b78fc42SChristian Riesch 	return 0;
44834a82443SDavid Brownell }
44934a82443SDavid Brownell 
45034a82443SDavid Brownell static ssize_t otp_read(struct spi_device *spi, unsigned base,
451c41e43c6SAndrey Smirnov 		u8 *buf, loff_t off, size_t len)
45234a82443SDavid Brownell {
45334a82443SDavid Brownell 	struct spi_message	m;
45434a82443SDavid Brownell 	size_t			l;
455c41e43c6SAndrey Smirnov 	u8			*scratch;
45634a82443SDavid Brownell 	struct spi_transfer	t;
45734a82443SDavid Brownell 	int			status;
45834a82443SDavid Brownell 
45934a82443SDavid Brownell 	if (off > 64)
46034a82443SDavid Brownell 		return -EINVAL;
46134a82443SDavid Brownell 
46234a82443SDavid Brownell 	if ((off + len) > 64)
46334a82443SDavid Brownell 		len = 64 - off;
46434a82443SDavid Brownell 
46534a82443SDavid Brownell 	spi_message_init(&m);
46634a82443SDavid Brownell 
46734a82443SDavid Brownell 	l = 4 + base + off + len;
46834a82443SDavid Brownell 	scratch = kzalloc(l, GFP_KERNEL);
46934a82443SDavid Brownell 	if (!scratch)
47034a82443SDavid Brownell 		return -ENOMEM;
47134a82443SDavid Brownell 
47234a82443SDavid Brownell 	/* OUT: OP_READ_SECURITY, 3 don't-care bytes, zeroes
47334a82443SDavid Brownell 	 * IN:  ignore 4 bytes, data bytes 0..N (max 127)
47434a82443SDavid Brownell 	 */
47534a82443SDavid Brownell 	scratch[0] = OP_READ_SECURITY;
47634a82443SDavid Brownell 
47734a82443SDavid Brownell 	memset(&t, 0, sizeof t);
47834a82443SDavid Brownell 	t.tx_buf = scratch;
47934a82443SDavid Brownell 	t.rx_buf = scratch;
48034a82443SDavid Brownell 	t.len = l;
48134a82443SDavid Brownell 	spi_message_add_tail(&t, &m);
48234a82443SDavid Brownell 
48334a82443SDavid Brownell 	dataflash_waitready(spi);
48434a82443SDavid Brownell 
48534a82443SDavid Brownell 	status = spi_sync(spi, &m);
48634a82443SDavid Brownell 	if (status >= 0) {
48734a82443SDavid Brownell 		memcpy(buf, scratch + 4 + base + off, len);
48834a82443SDavid Brownell 		status = len;
48934a82443SDavid Brownell 	}
49034a82443SDavid Brownell 
49134a82443SDavid Brownell 	kfree(scratch);
49234a82443SDavid Brownell 	return status;
49334a82443SDavid Brownell }
49434a82443SDavid Brownell 
49534a82443SDavid Brownell static int dataflash_read_fact_otp(struct mtd_info *mtd,
49634a82443SDavid Brownell 		loff_t from, size_t len, size_t *retlen, u_char *buf)
49734a82443SDavid Brownell {
49842845d2aSKulikov Vasiliy 	struct dataflash	*priv = mtd->priv;
49934a82443SDavid Brownell 	int			status;
50034a82443SDavid Brownell 
50134a82443SDavid Brownell 	/* 64 bytes, from 0..63 ... start at 64 on-chip */
50234a82443SDavid Brownell 	mutex_lock(&priv->lock);
50334a82443SDavid Brownell 	status = otp_read(priv->spi, 64, buf, from, len);
50434a82443SDavid Brownell 	mutex_unlock(&priv->lock);
50534a82443SDavid Brownell 
50634a82443SDavid Brownell 	if (status < 0)
50734a82443SDavid Brownell 		return status;
50834a82443SDavid Brownell 	*retlen = status;
50934a82443SDavid Brownell 	return 0;
51034a82443SDavid Brownell }
51134a82443SDavid Brownell 
51234a82443SDavid Brownell static int dataflash_read_user_otp(struct mtd_info *mtd,
51334a82443SDavid Brownell 		loff_t from, size_t len, size_t *retlen, u_char *buf)
51434a82443SDavid Brownell {
51542845d2aSKulikov Vasiliy 	struct dataflash	*priv = mtd->priv;
51634a82443SDavid Brownell 	int			status;
51734a82443SDavid Brownell 
51834a82443SDavid Brownell 	/* 64 bytes, from 0..63 ... start at 0 on-chip */
51934a82443SDavid Brownell 	mutex_lock(&priv->lock);
52034a82443SDavid Brownell 	status = otp_read(priv->spi, 0, buf, from, len);
52134a82443SDavid Brownell 	mutex_unlock(&priv->lock);
52234a82443SDavid Brownell 
52334a82443SDavid Brownell 	if (status < 0)
52434a82443SDavid Brownell 		return status;
52534a82443SDavid Brownell 	*retlen = status;
52634a82443SDavid Brownell 	return 0;
52734a82443SDavid Brownell }
52834a82443SDavid Brownell 
52934a82443SDavid Brownell static int dataflash_write_user_otp(struct mtd_info *mtd,
53034a82443SDavid Brownell 		loff_t from, size_t len, size_t *retlen, u_char *buf)
53134a82443SDavid Brownell {
53234a82443SDavid Brownell 	struct spi_message	m;
53334a82443SDavid Brownell 	const size_t		l = 4 + 64;
534c41e43c6SAndrey Smirnov 	u8			*scratch;
53534a82443SDavid Brownell 	struct spi_transfer	t;
53642845d2aSKulikov Vasiliy 	struct dataflash	*priv = mtd->priv;
53734a82443SDavid Brownell 	int			status;
53834a82443SDavid Brownell 
5399a78bc83SChristian Riesch 	if (from >= 64) {
5409a78bc83SChristian Riesch 		/*
5419a78bc83SChristian Riesch 		 * Attempting to write beyond the end of OTP memory,
5429a78bc83SChristian Riesch 		 * no data can be written.
54334a82443SDavid Brownell 		 */
5449a78bc83SChristian Riesch 		*retlen = 0;
5459a78bc83SChristian Riesch 		return 0;
5469a78bc83SChristian Riesch 	}
5479a78bc83SChristian Riesch 
5489a78bc83SChristian Riesch 	/* Truncate the write to fit into OTP memory. */
54934a82443SDavid Brownell 	if ((from + len) > 64)
5509a78bc83SChristian Riesch 		len = 64 - from;
55134a82443SDavid Brownell 
55234a82443SDavid Brownell 	/* OUT: OP_WRITE_SECURITY, 3 zeroes, 64 data-or-zero bytes
55334a82443SDavid Brownell 	 * IN:  ignore all
55434a82443SDavid Brownell 	 */
55534a82443SDavid Brownell 	scratch = kzalloc(l, GFP_KERNEL);
55634a82443SDavid Brownell 	if (!scratch)
55734a82443SDavid Brownell 		return -ENOMEM;
55834a82443SDavid Brownell 	scratch[0] = OP_WRITE_SECURITY;
55934a82443SDavid Brownell 	memcpy(scratch + 4 + from, buf, len);
56034a82443SDavid Brownell 
56134a82443SDavid Brownell 	spi_message_init(&m);
56234a82443SDavid Brownell 
56334a82443SDavid Brownell 	memset(&t, 0, sizeof t);
56434a82443SDavid Brownell 	t.tx_buf = scratch;
56534a82443SDavid Brownell 	t.len = l;
56634a82443SDavid Brownell 	spi_message_add_tail(&t, &m);
56734a82443SDavid Brownell 
56834a82443SDavid Brownell 	/* Write the OTP bits, if they've not yet been written.
56934a82443SDavid Brownell 	 * This modifies SRAM buffer1.
57034a82443SDavid Brownell 	 */
57134a82443SDavid Brownell 	mutex_lock(&priv->lock);
57234a82443SDavid Brownell 	dataflash_waitready(priv->spi);
57334a82443SDavid Brownell 	status = spi_sync(priv->spi, &m);
57434a82443SDavid Brownell 	mutex_unlock(&priv->lock);
57534a82443SDavid Brownell 
57634a82443SDavid Brownell 	kfree(scratch);
57734a82443SDavid Brownell 
57834a82443SDavid Brownell 	if (status >= 0) {
57934a82443SDavid Brownell 		status = 0;
58034a82443SDavid Brownell 		*retlen = len;
58134a82443SDavid Brownell 	}
58234a82443SDavid Brownell 	return status;
58334a82443SDavid Brownell }
58434a82443SDavid Brownell 
58534a82443SDavid Brownell static char *otp_setup(struct mtd_info *device, char revision)
58634a82443SDavid Brownell {
5873c3c10bbSArtem Bityutskiy 	device->_get_fact_prot_info = dataflash_get_otp_info;
5883c3c10bbSArtem Bityutskiy 	device->_read_fact_prot_reg = dataflash_read_fact_otp;
5893c3c10bbSArtem Bityutskiy 	device->_get_user_prot_info = dataflash_get_otp_info;
5903c3c10bbSArtem Bityutskiy 	device->_read_user_prot_reg = dataflash_read_user_otp;
59134a82443SDavid Brownell 
59234a82443SDavid Brownell 	/* rev c parts (at45db321c and at45db1281 only!) use a
59334a82443SDavid Brownell 	 * different write procedure; not (yet?) implemented.
59434a82443SDavid Brownell 	 */
59534a82443SDavid Brownell 	if (revision > 'c')
5963c3c10bbSArtem Bityutskiy 		device->_write_user_prot_reg = dataflash_write_user_otp;
59734a82443SDavid Brownell 
59834a82443SDavid Brownell 	return ", OTP";
59934a82443SDavid Brownell }
60034a82443SDavid Brownell 
60134a82443SDavid Brownell #else
60234a82443SDavid Brownell 
603cf93ae02SDavid Brownell static char *otp_setup(struct mtd_info *device, char revision)
60434a82443SDavid Brownell {
60534a82443SDavid Brownell 	return " (OTP)";
60634a82443SDavid Brownell }
60734a82443SDavid Brownell 
60834a82443SDavid Brownell #endif
60934a82443SDavid Brownell 
61034a82443SDavid Brownell /* ......................................................................... */
61134a82443SDavid Brownell 
6121d6432feSDavid Brownell /*
6131d6432feSDavid Brownell  * Register DataFlash device with MTD subsystem.
6141d6432feSDavid Brownell  */
615d8929942SGreg Kroah-Hartman static int add_dataflash_otp(struct spi_device *spi, char *name, int nr_pages,
616d8929942SGreg Kroah-Hartman 			     int pagesize, int pageoffset, char revision)
6171d6432feSDavid Brownell {
6181d6432feSDavid Brownell 	struct dataflash		*priv;
6191d6432feSDavid Brownell 	struct mtd_info			*device;
6200278fd3fSJingoo Han 	struct flash_platform_data	*pdata = dev_get_platdata(&spi->dev);
62134a82443SDavid Brownell 	char				*otp_tag = "";
622d4702669SH Hartley Sweeten 	int				err = 0;
6231d6432feSDavid Brownell 
6245cbded58SRobert P. J. Day 	priv = kzalloc(sizeof *priv, GFP_KERNEL);
6251d6432feSDavid Brownell 	if (!priv)
6261d6432feSDavid Brownell 		return -ENOMEM;
6271d6432feSDavid Brownell 
628ec9ce52eSMatthias Kaehlcke 	mutex_init(&priv->lock);
6291d6432feSDavid Brownell 	priv->spi = spi;
6301d6432feSDavid Brownell 	priv->page_size = pagesize;
6311d6432feSDavid Brownell 	priv->page_offset = pageoffset;
6321d6432feSDavid Brownell 
6331d6432feSDavid Brownell 	/* name must be usable with cmdlinepart */
6341d6432feSDavid Brownell 	sprintf(priv->name, "spi%d.%d-%s",
6351d6432feSDavid Brownell 			spi->master->bus_num, spi->chip_select,
6361d6432feSDavid Brownell 			name);
6371d6432feSDavid Brownell 
6381d6432feSDavid Brownell 	device = &priv->mtd;
6391d6432feSDavid Brownell 	device->name = (pdata && pdata->name) ? pdata->name : priv->name;
6401d6432feSDavid Brownell 	device->size = nr_pages * pagesize;
6411d6432feSDavid Brownell 	device->erasesize = pagesize;
64217ffc7baSArtem B. Bityutskiy 	device->writesize = pagesize;
6431d6432feSDavid Brownell 	device->type = MTD_DATAFLASH;
6446c33cafcSHaavard Skinnemoen 	device->flags = MTD_WRITEABLE;
6453c3c10bbSArtem Bityutskiy 	device->_erase = dataflash_erase;
6463c3c10bbSArtem Bityutskiy 	device->_read = dataflash_read;
6473c3c10bbSArtem Bityutskiy 	device->_write = dataflash_write;
6481d6432feSDavid Brownell 	device->priv = priv;
6491d6432feSDavid Brownell 
65087f39f04SDavid Brownell 	device->dev.parent = &spi->dev;
651004b5e60SBrian Norris 	mtd_set_of_node(device, spi->dev.of_node);
65287f39f04SDavid Brownell 
65334a82443SDavid Brownell 	if (revision >= 'c')
65434a82443SDavid Brownell 		otp_tag = otp_setup(device, revision);
65534a82443SDavid Brownell 
6565b7f3a50SArtem Bityutskiy 	dev_info(&spi->dev, "%s (%lld KBytes) pagesize %d bytes%s\n",
6575b7f3a50SArtem Bityutskiy 			name, (long long)((device->size + 1023) >> 10),
65834a82443SDavid Brownell 			pagesize, otp_tag);
6595cacbfa9SJingoo Han 	spi_set_drvdata(spi, priv);
6601d6432feSDavid Brownell 
661004b5e60SBrian Norris 	err = mtd_device_register(device,
6623a8fb12aSDmitry Eremin-Solenikov 			pdata ? pdata->parts : NULL,
6633a8fb12aSDmitry Eremin-Solenikov 			pdata ? pdata->nr_parts : 0);
6641d6432feSDavid Brownell 
665d4702669SH Hartley Sweeten 	if (!err)
666d4702669SH Hartley Sweeten 		return 0;
667d4702669SH Hartley Sweeten 
668d4702669SH Hartley Sweeten 	kfree(priv);
669d4702669SH Hartley Sweeten 	return err;
6701d6432feSDavid Brownell }
6711d6432feSDavid Brownell 
672d8929942SGreg Kroah-Hartman static inline int add_dataflash(struct spi_device *spi, char *name,
67334a82443SDavid Brownell 				int nr_pages, int pagesize, int pageoffset)
67434a82443SDavid Brownell {
67534a82443SDavid Brownell 	return add_dataflash_otp(spi, name, nr_pages, pagesize,
67634a82443SDavid Brownell 			pageoffset, 0);
67734a82443SDavid Brownell }
67834a82443SDavid Brownell 
679e9d42227SMichael Hennerich struct flash_info {
680e9d42227SMichael Hennerich 	char		*name;
681e9d42227SMichael Hennerich 
682771999b6Sakpm@linux-foundation.org 	/* JEDEC id has a high byte of zero plus three data bytes:
683771999b6Sakpm@linux-foundation.org 	 * the manufacturer id, then a two byte device id.
684e9d42227SMichael Hennerich 	 */
6851da8869aSAndrey Smirnov 	u64		jedec_id;
686e9d42227SMichael Hennerich 
687771999b6Sakpm@linux-foundation.org 	/* The size listed here is what works with OP_ERASE_PAGE. */
688e9d42227SMichael Hennerich 	unsigned	nr_pages;
689c41e43c6SAndrey Smirnov 	u16		pagesize;
690c41e43c6SAndrey Smirnov 	u16		pageoffset;
691e9d42227SMichael Hennerich 
692c41e43c6SAndrey Smirnov 	u16		flags;
6931da8869aSAndrey Smirnov #define SUP_EXTID	0x0004		/* supports extended ID data */
694771999b6Sakpm@linux-foundation.org #define SUP_POW2PS	0x0002		/* supports 2^N byte pages */
695771999b6Sakpm@linux-foundation.org #define IS_POW2PS	0x0001		/* uses 2^N byte pages */
696e9d42227SMichael Hennerich };
697e9d42227SMichael Hennerich 
698042a1909SBill Pemberton static struct flash_info dataflash_data[] = {
699e9d42227SMichael Hennerich 
700771999b6Sakpm@linux-foundation.org 	/*
701771999b6Sakpm@linux-foundation.org 	 * NOTE:  chips with SUP_POW2PS (rev D and up) need two entries,
702771999b6Sakpm@linux-foundation.org 	 * one with IS_POW2PS and the other without.  The entry with the
703771999b6Sakpm@linux-foundation.org 	 * non-2^N byte page size can't name exact chip revisions without
704771999b6Sakpm@linux-foundation.org 	 * losing backwards compatibility for cmdlinepart.
705771999b6Sakpm@linux-foundation.org 	 *
706771999b6Sakpm@linux-foundation.org 	 * These newer chips also support 128-byte security registers (with
707771999b6Sakpm@linux-foundation.org 	 * 64 bytes one-time-programmable) and software write-protection.
708771999b6Sakpm@linux-foundation.org 	 */
709771999b6Sakpm@linux-foundation.org 	{ "AT45DB011B",  0x1f2200, 512, 264, 9, SUP_POW2PS},
710e9d42227SMichael Hennerich 	{ "at45db011d",  0x1f2200, 512, 256, 8, SUP_POW2PS | IS_POW2PS},
711e9d42227SMichael Hennerich 
712771999b6Sakpm@linux-foundation.org 	{ "AT45DB021B",  0x1f2300, 1024, 264, 9, SUP_POW2PS},
713e9d42227SMichael Hennerich 	{ "at45db021d",  0x1f2300, 1024, 256, 8, SUP_POW2PS | IS_POW2PS},
714e9d42227SMichael Hennerich 
715771999b6Sakpm@linux-foundation.org 	{ "AT45DB041x",  0x1f2400, 2048, 264, 9, SUP_POW2PS},
716e9d42227SMichael Hennerich 	{ "at45db041d",  0x1f2400, 2048, 256, 8, SUP_POW2PS | IS_POW2PS},
717e9d42227SMichael Hennerich 
718771999b6Sakpm@linux-foundation.org 	{ "AT45DB081B",  0x1f2500, 4096, 264, 9, SUP_POW2PS},
719e9d42227SMichael Hennerich 	{ "at45db081d",  0x1f2500, 4096, 256, 8, SUP_POW2PS | IS_POW2PS},
720e9d42227SMichael Hennerich 
721771999b6Sakpm@linux-foundation.org 	{ "AT45DB161x",  0x1f2600, 4096, 528, 10, SUP_POW2PS},
722e9d42227SMichael Hennerich 	{ "at45db161d",  0x1f2600, 4096, 512, 9, SUP_POW2PS | IS_POW2PS},
723e9d42227SMichael Hennerich 
724771999b6Sakpm@linux-foundation.org 	{ "AT45DB321x",  0x1f2700, 8192, 528, 10, 0},		/* rev C */
725e9d42227SMichael Hennerich 
726771999b6Sakpm@linux-foundation.org 	{ "AT45DB321x",  0x1f2701, 8192, 528, 10, SUP_POW2PS},
727e9d42227SMichael Hennerich 	{ "at45db321d",  0x1f2701, 8192, 512, 9, SUP_POW2PS | IS_POW2PS},
728e9d42227SMichael Hennerich 
729771999b6Sakpm@linux-foundation.org 	{ "AT45DB642x",  0x1f2800, 8192, 1056, 11, SUP_POW2PS},
730771999b6Sakpm@linux-foundation.org 	{ "at45db642d",  0x1f2800, 8192, 1024, 10, SUP_POW2PS | IS_POW2PS},
73167e4145eSAndrey Smirnov 
732cbdceb9bSGeert Uytterhoeven 	{ "AT45DB641E",  0x1f28000100ULL, 32768, 264, 9, SUP_EXTID | SUP_POW2PS},
733cbdceb9bSGeert Uytterhoeven 	{ "at45db641e",  0x1f28000100ULL, 32768, 256, 8, SUP_EXTID | SUP_POW2PS | IS_POW2PS},
734e9d42227SMichael Hennerich };
735e9d42227SMichael Hennerich 
7361da8869aSAndrey Smirnov static struct flash_info *jedec_lookup(struct spi_device *spi,
7371da8869aSAndrey Smirnov 				       u64 jedec, bool use_extid)
738e9d42227SMichael Hennerich {
739e9d42227SMichael Hennerich 	struct flash_info *info;
740e9d42227SMichael Hennerich 	int status;
741e9d42227SMichael Hennerich 
742a296a1bcSAndrey Smirnov 	for (info = dataflash_data;
743a296a1bcSAndrey Smirnov 	     info < dataflash_data + ARRAY_SIZE(dataflash_data);
744a296a1bcSAndrey Smirnov 	     info++) {
7451da8869aSAndrey Smirnov 		if (use_extid && !(info->flags & SUP_EXTID))
7461da8869aSAndrey Smirnov 			continue;
7471da8869aSAndrey Smirnov 
748e9d42227SMichael Hennerich 		if (info->jedec_id == jedec) {
74902f62864SAndrey Smirnov 			dev_dbg(&spi->dev, "OTP, sector protect%s\n",
75002f62864SAndrey Smirnov 				(info->flags & SUP_POW2PS) ?
75102f62864SAndrey Smirnov 				", binary pagesize" : "");
752e9d42227SMichael Hennerich 			if (info->flags & SUP_POW2PS) {
753e9d42227SMichael Hennerich 				status = dataflash_status(spi);
754771999b6Sakpm@linux-foundation.org 				if (status < 0) {
75502f62864SAndrey Smirnov 					dev_dbg(&spi->dev, "status error %d\n",
75602f62864SAndrey Smirnov 						status);
757771999b6Sakpm@linux-foundation.org 					return ERR_PTR(status);
758771999b6Sakpm@linux-foundation.org 				}
759771999b6Sakpm@linux-foundation.org 				if (status & 0x1) {
760771999b6Sakpm@linux-foundation.org 					if (info->flags & IS_POW2PS)
761771999b6Sakpm@linux-foundation.org 						return info;
762771999b6Sakpm@linux-foundation.org 				} else {
763771999b6Sakpm@linux-foundation.org 					if (!(info->flags & IS_POW2PS))
764e9d42227SMichael Hennerich 						return info;
765e9d42227SMichael Hennerich 				}
766229cc58bSWill Newton 			} else
767229cc58bSWill Newton 				return info;
768e9d42227SMichael Hennerich 		}
769e9d42227SMichael Hennerich 	}
770e9d42227SMichael Hennerich 
7711da8869aSAndrey Smirnov 	return ERR_PTR(-ENODEV);
7721da8869aSAndrey Smirnov }
7731da8869aSAndrey Smirnov 
7741da8869aSAndrey Smirnov static struct flash_info *jedec_probe(struct spi_device *spi)
7751da8869aSAndrey Smirnov {
7761da8869aSAndrey Smirnov 	int ret;
7771da8869aSAndrey Smirnov 	u8 code = OP_READ_ID;
7781da8869aSAndrey Smirnov 	u64 jedec;
7791da8869aSAndrey Smirnov 	u8 id[sizeof(jedec)] = {0};
7801da8869aSAndrey Smirnov 	const unsigned int id_size = 5;
7811da8869aSAndrey Smirnov 	struct flash_info *info;
7821da8869aSAndrey Smirnov 
7831da8869aSAndrey Smirnov 	/*
7841da8869aSAndrey Smirnov 	 * JEDEC also defines an optional "extended device information"
7851da8869aSAndrey Smirnov 	 * string for after vendor-specific data, after the three bytes
7861da8869aSAndrey Smirnov 	 * we use here.  Supporting some chips might require using it.
7871da8869aSAndrey Smirnov 	 *
7881da8869aSAndrey Smirnov 	 * If the vendor ID isn't Atmel's (0x1f), assume this call failed.
7891da8869aSAndrey Smirnov 	 * That's not an error; only rev C and newer chips handle it, and
7901da8869aSAndrey Smirnov 	 * only Atmel sells these chips.
7911da8869aSAndrey Smirnov 	 */
7921da8869aSAndrey Smirnov 	ret = spi_write_then_read(spi, &code, 1, id, id_size);
7931da8869aSAndrey Smirnov 	if (ret < 0) {
7941da8869aSAndrey Smirnov 		dev_dbg(&spi->dev, "error %d reading JEDEC ID\n", ret);
7951da8869aSAndrey Smirnov 		return ERR_PTR(ret);
7961da8869aSAndrey Smirnov 	}
7971da8869aSAndrey Smirnov 
7981da8869aSAndrey Smirnov 	if (id[0] != CFI_MFR_ATMEL)
7991da8869aSAndrey Smirnov 		return NULL;
8001da8869aSAndrey Smirnov 
8011da8869aSAndrey Smirnov 	jedec = be64_to_cpup((__be64 *)id);
8021da8869aSAndrey Smirnov 
8031da8869aSAndrey Smirnov 	/*
8041da8869aSAndrey Smirnov 	 * First, try to match device using extended device
8051da8869aSAndrey Smirnov 	 * information
8061da8869aSAndrey Smirnov 	 */
8071da8869aSAndrey Smirnov 	info = jedec_lookup(spi, jedec >> DATAFLASH_SHIFT_EXTID, true);
8081da8869aSAndrey Smirnov 	if (!IS_ERR(info))
8091da8869aSAndrey Smirnov 		return info;
8101da8869aSAndrey Smirnov 	/*
8111da8869aSAndrey Smirnov 	 * If that fails, make another pass using regular ID
8121da8869aSAndrey Smirnov 	 * information
8131da8869aSAndrey Smirnov 	 */
8141da8869aSAndrey Smirnov 	info = jedec_lookup(spi, jedec >> DATAFLASH_SHIFT_ID, false);
8151da8869aSAndrey Smirnov 	if (!IS_ERR(info))
8161da8869aSAndrey Smirnov 		return info;
817771999b6Sakpm@linux-foundation.org 	/*
818771999b6Sakpm@linux-foundation.org 	 * Treat other chips as errors ... we won't know the right page
819771999b6Sakpm@linux-foundation.org 	 * size (it might be binary) even when we can tell which density
820771999b6Sakpm@linux-foundation.org 	 * class is involved (legacy chip id scheme).
821771999b6Sakpm@linux-foundation.org 	 */
8221da8869aSAndrey Smirnov 	dev_warn(&spi->dev, "JEDEC id %016llx not handled\n", jedec);
823771999b6Sakpm@linux-foundation.org 	return ERR_PTR(-ENODEV);
824771999b6Sakpm@linux-foundation.org }
825771999b6Sakpm@linux-foundation.org 
826771999b6Sakpm@linux-foundation.org /*
827771999b6Sakpm@linux-foundation.org  * Detect and initialize DataFlash device, using JEDEC IDs on newer chips
828771999b6Sakpm@linux-foundation.org  * or else the ID code embedded in the status bits:
829771999b6Sakpm@linux-foundation.org  *
830771999b6Sakpm@linux-foundation.org  *   Device      Density         ID code          #Pages PageSize  Offset
831771999b6Sakpm@linux-foundation.org  *   AT45DB011B  1Mbit   (128K)  xx0011xx (0x0c)    512    264      9
832771999b6Sakpm@linux-foundation.org  *   AT45DB021B  2Mbit   (256K)  xx0101xx (0x14)   1024    264      9
833771999b6Sakpm@linux-foundation.org  *   AT45DB041B  4Mbit   (512K)  xx0111xx (0x1c)   2048    264      9
834771999b6Sakpm@linux-foundation.org  *   AT45DB081B  8Mbit   (1M)    xx1001xx (0x24)   4096    264      9
835771999b6Sakpm@linux-foundation.org  *   AT45DB0161B 16Mbit  (2M)    xx1011xx (0x2c)   4096    528     10
836771999b6Sakpm@linux-foundation.org  *   AT45DB0321B 32Mbit  (4M)    xx1101xx (0x34)   8192    528     10
837771999b6Sakpm@linux-foundation.org  *   AT45DB0642  64Mbit  (8M)    xx111xxx (0x3c)   8192   1056     11
838771999b6Sakpm@linux-foundation.org  *   AT45DB1282  128Mbit (16M)   xx0100xx (0x10)  16384   1056     11
839771999b6Sakpm@linux-foundation.org  */
84006f25510SBill Pemberton static int dataflash_probe(struct spi_device *spi)
8411d6432feSDavid Brownell {
8421d6432feSDavid Brownell 	int status;
843e9d42227SMichael Hennerich 	struct flash_info	*info;
844e9d42227SMichael Hennerich 
845e9d42227SMichael Hennerich 	/*
846e9d42227SMichael Hennerich 	 * Try to detect dataflash by JEDEC ID.
847e9d42227SMichael Hennerich 	 * If it succeeds we know we have either a C or D part.
848e9d42227SMichael Hennerich 	 * D will support power of 2 pagesize option.
84934a82443SDavid Brownell 	 * Both support the security register, though with different
85034a82443SDavid Brownell 	 * write procedures.
851e9d42227SMichael Hennerich 	 */
852e9d42227SMichael Hennerich 	info = jedec_probe(spi);
853771999b6Sakpm@linux-foundation.org 	if (IS_ERR(info))
854771999b6Sakpm@linux-foundation.org 		return PTR_ERR(info);
855e9d42227SMichael Hennerich 	if (info != NULL)
85634a82443SDavid Brownell 		return add_dataflash_otp(spi, info->name, info->nr_pages,
85734a82443SDavid Brownell 				info->pagesize, info->pageoffset,
85834a82443SDavid Brownell 				(info->flags & SUP_POW2PS) ? 'd' : 'c');
859e9d42227SMichael Hennerich 
860771999b6Sakpm@linux-foundation.org 	/*
861771999b6Sakpm@linux-foundation.org 	 * Older chips support only legacy commands, identifing
862771999b6Sakpm@linux-foundation.org 	 * capacity using bits in the status byte.
863771999b6Sakpm@linux-foundation.org 	 */
8641d6432feSDavid Brownell 	status = dataflash_status(spi);
8651d6432feSDavid Brownell 	if (status <= 0 || status == 0xff) {
86602f62864SAndrey Smirnov 		dev_dbg(&spi->dev, "status error %d\n", status);
867de4fa992SDavid Brownell 		if (status == 0 || status == 0xff)
8681d6432feSDavid Brownell 			status = -ENODEV;
8691d6432feSDavid Brownell 		return status;
8701d6432feSDavid Brownell 	}
8711d6432feSDavid Brownell 
8721d6432feSDavid Brownell 	/* if there's a device there, assume it's dataflash.
8731d6432feSDavid Brownell 	 * board setup should have set spi->max_speed_max to
8741d6432feSDavid Brownell 	 * match f(car) for continuous reads, mode 0 or 3.
8751d6432feSDavid Brownell 	 */
8761d6432feSDavid Brownell 	switch (status & 0x3c) {
8771d6432feSDavid Brownell 	case 0x0c:	/* 0 0 1 1 x x */
8781d6432feSDavid Brownell 		status = add_dataflash(spi, "AT45DB011B", 512, 264, 9);
8791d6432feSDavid Brownell 		break;
8801d6432feSDavid Brownell 	case 0x14:	/* 0 1 0 1 x x */
881e9d42227SMichael Hennerich 		status = add_dataflash(spi, "AT45DB021B", 1024, 264, 9);
8821d6432feSDavid Brownell 		break;
8831d6432feSDavid Brownell 	case 0x1c:	/* 0 1 1 1 x x */
884771999b6Sakpm@linux-foundation.org 		status = add_dataflash(spi, "AT45DB041x", 2048, 264, 9);
8851d6432feSDavid Brownell 		break;
8861d6432feSDavid Brownell 	case 0x24:	/* 1 0 0 1 x x */
8871d6432feSDavid Brownell 		status = add_dataflash(spi, "AT45DB081B", 4096, 264, 9);
8881d6432feSDavid Brownell 		break;
8891d6432feSDavid Brownell 	case 0x2c:	/* 1 0 1 1 x x */
890771999b6Sakpm@linux-foundation.org 		status = add_dataflash(spi, "AT45DB161x", 4096, 528, 10);
8911d6432feSDavid Brownell 		break;
8921d6432feSDavid Brownell 	case 0x34:	/* 1 1 0 1 x x */
8931d6432feSDavid Brownell 		status = add_dataflash(spi, "AT45DB321x", 8192, 528, 10);
8941d6432feSDavid Brownell 		break;
8951d6432feSDavid Brownell 	case 0x38:	/* 1 1 1 x x x */
8961d6432feSDavid Brownell 	case 0x3c:
8971d6432feSDavid Brownell 		status = add_dataflash(spi, "AT45DB642x", 8192, 1056, 11);
8981d6432feSDavid Brownell 		break;
8991d6432feSDavid Brownell 	/* obsolete AT45DB1282 not (yet?) supported */
9001d6432feSDavid Brownell 	default:
901cb85b7e7SMark Brown 		dev_info(&spi->dev, "unsupported device (%x)\n",
9020a32a102SBrian Norris 				status & 0x3c);
9031d6432feSDavid Brownell 		status = -ENODEV;
9041d6432feSDavid Brownell 	}
9051d6432feSDavid Brownell 
9061d6432feSDavid Brownell 	if (status < 0)
90702f62864SAndrey Smirnov 		dev_dbg(&spi->dev, "add_dataflash --> %d\n", status);
9081d6432feSDavid Brownell 
9091d6432feSDavid Brownell 	return status;
9101d6432feSDavid Brownell }
9111d6432feSDavid Brownell 
912810b7e06SBill Pemberton static int dataflash_remove(struct spi_device *spi)
9131d6432feSDavid Brownell {
9145cacbfa9SJingoo Han 	struct dataflash	*flash = spi_get_drvdata(spi);
9151d6432feSDavid Brownell 	int			status;
9161d6432feSDavid Brownell 
91702f62864SAndrey Smirnov 	dev_dbg(&spi->dev, "remove\n");
9181d6432feSDavid Brownell 
919436c06daSJamie Iles 	status = mtd_device_unregister(&flash->mtd);
9201ba80c9eSJingoo Han 	if (status == 0)
9211d6432feSDavid Brownell 		kfree(flash);
9221d6432feSDavid Brownell 	return status;
9231d6432feSDavid Brownell }
9241d6432feSDavid Brownell 
9251d6432feSDavid Brownell static struct spi_driver dataflash_driver = {
9261d6432feSDavid Brownell 	.driver = {
9271d6432feSDavid Brownell 		.name		= "mtd_dataflash",
92819d69b86SSachin Kamat 		.of_match_table = of_match_ptr(dataflash_dt_ids),
9291d6432feSDavid Brownell 	},
9301d6432feSDavid Brownell 
9311d6432feSDavid Brownell 	.probe		= dataflash_probe,
9325153b88cSBill Pemberton 	.remove		= dataflash_remove,
9331d6432feSDavid Brownell 
9341d6432feSDavid Brownell 	/* FIXME:  investigate suspend and resume... */
9351d6432feSDavid Brownell };
9361d6432feSDavid Brownell 
937c9d1b752SAxel Lin module_spi_driver(dataflash_driver);
9381d6432feSDavid Brownell 
9391d6432feSDavid Brownell MODULE_LICENSE("GPL");
9401d6432feSDavid Brownell MODULE_AUTHOR("Andrew Victor, David Brownell");
9411d6432feSDavid Brownell MODULE_DESCRIPTION("MTD DataFlash driver");
942e0626e38SAnton Vorontsov MODULE_ALIAS("spi:mtd_dataflash");
943