[PATCH] spi-nor: Add support for Atmel Dataflash memories

Radu Pirea radu.pirea at microchip.com
Wed Feb 28 03:55:01 PST 2018


This patch add support in spi-nor for allmost all dataflash memories
supported by old mtd_dataflash driver.

Signed-off-by: Radu Pirea <radu.pirea at microchip.com>
---
 drivers/mtd/spi-nor/spi-nor.c | 150 +++++++++++++++++++++++++++++++++++++++++-
 include/linux/mtd/spi-nor.h   |  10 +++
 2 files changed, 158 insertions(+), 2 deletions(-)

diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index d445a4d..4cb3cf7 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -89,6 +89,7 @@ struct flash_info {
 #define NO_CHIP_ERASE		BIT(12) /* Chip does not support chip erase */
 #define SPI_NOR_SKIP_SFDP	BIT(13)	/* Skip parsing of SFDP tables */
 #define USE_CLSR		BIT(14)	/* use CLSR command */
+#define SPI_DATAFLASH		BIT(15) /* Atmel Dataflash memory */
 
 	int	(*quad_enable)(struct spi_nor *nor);
 };
@@ -306,6 +307,20 @@ static int s3an_sr_ready(struct spi_nor *nor)
 	return !!(val & XSR_RDY);
 }
 
+static int dataflash_sr_ready(struct spi_nor *nor)
+{
+	int ret;
+	u8 val;
+
+	ret = nor->read_reg(nor, SPINOR_OP_DFRDSR, &val, 1);
+	if (ret < 0) {
+		dev_err(nor->dev, "error %d reading DFSR\n", ret);
+		return ret;
+	}
+
+	return !!(val & DFSR_RDY);
+}
+
 static inline int spi_nor_sr_ready(struct spi_nor *nor)
 {
 	int sr = read_sr(nor);
@@ -354,6 +369,8 @@ static int spi_nor_ready(struct spi_nor *nor)
 
 	if (nor->flags & SNOR_F_READY_XSR_RDY)
 		sr = s3an_sr_ready(nor);
+	else if (nor->flags & SPI_DATAFLASH)
+		sr = dataflash_sr_ready(nor);
 	else
 		sr = spi_nor_sr_ready(nor);
 	if (sr < 0)
@@ -457,6 +474,44 @@ static loff_t spi_nor_s3an_addr_convert(struct spi_nor *nor, unsigned int addr)
 	return page | offset;
 }
 
+static loff_t spi_nor_dataflash_addr_convert(struct spi_nor *nor,
+					     unsigned int addr)
+{
+	unsigned int offset;
+	unsigned int page;
+	unsigned int page_offset;
+
+	page = addr / nor->page_size;
+	offset = addr % nor->page_size;
+	page_offset = fls(nor->page_size);
+	if (is_power_of_2(nor->page_size))
+		page_offset--;
+
+	return (page << page_offset) | offset;
+}
+
+static int spi_nor_dataflash_erase_sector(struct spi_nor *nor, u32 addr)
+{
+	u32 block_size = 8 * nor->page_size;
+	u32 blocks = nor->mtd.erasesize / block_size;
+	u32 addr_local;
+	u8 buf[SPI_NOR_MAX_ADDR_WIDTH];
+	int i, j;
+
+	for (j = 0; j < blocks; j++) {
+		addr_local = spi_nor_dataflash_addr_convert(nor, addr);
+		for (i = nor->addr_width - 1; i >= 0; i--) {
+			buf[i] = addr_local & 0xff;
+			addr_local >>= 8;
+		}
+		nor->write_reg(nor, nor->erase_opcode, buf, nor->addr_width);
+		addr += block_size;
+		spi_nor_wait_till_ready(nor);
+	}
+
+	return 0;
+}
+
 /*
  * Initiate the erasure of a single sector
  */
@@ -542,7 +597,11 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
 		while (len) {
 			write_enable(nor);
 
-			ret = spi_nor_erase_sector(nor, addr);
+			if (nor->flags & SPI_DATAFLASH)
+				ret = spi_nor_dataflash_erase_sector(nor, addr);
+			else
+				ret = spi_nor_erase_sector(nor, addr);
+
 			if (ret)
 				goto erase_err;
 
@@ -914,6 +973,20 @@ static int macronix_quad_enable(struct spi_nor *nor);
 		.page_size = 256,					\
 		.flags = (_flags),
 
+#define INFOP(_jedec_id, _ext_id, _sector_size, _n_sectors, _page_size, _flags)	\
+		.id = {							\
+			((_jedec_id) >> 16) & 0xff,			\
+			((_jedec_id) >> 8) & 0xff,			\
+			(_jedec_id) & 0xff,				\
+			((_ext_id) >> 8) & 0xff,			\
+			(_ext_id) & 0xff,				\
+			},						\
+		.id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))),	\
+		.sector_size = (_sector_size),				\
+		.n_sectors = (_n_sectors),				\
+		.page_size = _page_size,				\
+		.flags = (_flags),
+
 #define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)	\
 		.id = {							\
 			((_jedec_id) >> 16) & 0xff,			\
@@ -975,7 +1048,14 @@ static const struct flash_info spi_nor_ids[] = {
 	{ "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) },
 	{ "at26df321",  INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) },
 
-	{ "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) },
+	{ "at45db021d", INFOP(0x1f2300, 0, 512 * 264,  2, 264, SPI_DATAFLASH | NO_CHIP_ERASE) },
+	{ "at45db041d", INFOP(0x1f2400, 0, 256 * 264,  8, 264, SPI_DATAFLASH | NO_CHIP_ERASE) },
+	{ "at45db081d", INFOP(0x1f2500, 0, 256 * 264, 16, 264, SPI_DATAFLASH | NO_CHIP_ERASE) },
+	{ "at45db161d", INFOP(0x1f2600, 0, 256 * 528, 16, 528, SPI_DATAFLASH | NO_CHIP_ERASE) },
+	{ "at45db321d", INFOP(0x1f2700, 0, 128 * 528, 64, 528, SPI_DATAFLASH | NO_CHIP_ERASE) },
+	{ "at45db321d", INFOP(0x1f2701, 0, 128 * 528, 64, 528, SPI_DATAFLASH | NO_CHIP_ERASE) },
+	{ "at45db642d", INFOP(0x1f2800, 0, 256 * 1056, 32, 1056, SPI_DATAFLASH | NO_CHIP_ERASE) },
+	{ "at45db641e", INFOP(0x1f2800, 0x0100, 1024 * 264, 32, 264, SPI_DATAFLASH | NO_CHIP_ERASE) },
 
 	/* EON -- en25xxx */
 	{ "en25f32",    INFO(0x1c3116, 0, 64 * 1024,   64, SECT_4K) },
@@ -1278,6 +1358,9 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
 		if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
 			addr = spi_nor_s3an_addr_convert(nor, addr);
 
+		if (nor->flags & SPI_DATAFLASH)
+			addr = spi_nor_dataflash_addr_convert(nor, addr);
+
 		ret = nor->read(nor, addr, len, buf);
 		if (ret == 0) {
 			/* We shouldn't see 0-length reads */
@@ -1379,6 +1462,21 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
 	return ret;
 }
 
+static int dataflash_memtobuf(struct spi_nor *nor, u32 addr)
+{
+	u8 buf[SPI_NOR_MAX_ADDR_WIDTH];
+	int i;
+
+	addr = spi_nor_dataflash_addr_convert(nor, addr);
+
+	for (i = nor->addr_width - 1; i >= 0; i--) {
+		buf[i] = addr & 0xff;
+		addr >>= 8;
+	}
+
+	return nor->write_reg(nor, SPINOR_OP_DFMTB, buf, nor->addr_width);
+}
+
 /*
  * Write an address range to the nor chip.  Data must be written in
  * FLASH_PAGESIZE chunks.  The address range may be any size provided
@@ -1420,9 +1518,17 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
 		page_remain = min_t(size_t,
 				    nor->page_size - page_offset, len - i);
 
+		if (nor->flags & SPI_DATAFLASH) {
+			dataflash_memtobuf(nor, addr);
+			spi_nor_wait_till_ready(nor);
+		}
+
 		if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
 			addr = spi_nor_s3an_addr_convert(nor, addr);
 
+		if (nor->flags & SPI_DATAFLASH)
+			addr = spi_nor_dataflash_addr_convert(nor, addr);
+
 		write_enable(nor);
 		ret = nor->write(nor, addr, page_remain, buf + i);
 		if (ret < 0)
@@ -1742,6 +1848,37 @@ static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor)
 	return 0;
 }
 
+static int dataflash_nor_scan(const struct flash_info *info,
+			      struct spi_nor *nor)
+{
+	int ret;
+	u8 val;
+
+	ret = nor->read_reg(nor, SPINOR_OP_DFRDSR, &val, 1);
+	if (ret < 0) {
+		dev_err(nor->dev, "error reading status register\n");
+		return ret;
+	}
+
+	nor->erase_opcode = SPINOR_OP_DFBE_8k;
+	nor->program_opcode = SPINOR_OP_DFBTOM;
+	nor->read_opcode = SPINOR_OP_DFRD;
+	nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
+
+	if (val & DFSR_PAGESIZE) {
+		/* Flash in Power of 2 mode */
+		nor->mtd.erasesize = nor->mtd.erasesize / nor->page_size *
+		(1 << (fls(nor->page_size) - 1));
+		nor->mtd.size = nor->mtd.erasesize * info->n_sectors;
+		nor->page_size = 1 << (fls(nor->page_size) - 1);
+		nor->mtd.writebufsize = nor->page_size;
+	}
+
+	nor->read_dummy = 32;
+
+	return 0;
+}
+
 struct spi_nor_read_command {
 	u8			num_mode_clocks;
 	u8			num_wait_states;
@@ -2818,6 +2955,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
 	if (info->flags & SPI_S3AN)
 		nor->flags |=  SNOR_F_READY_XSR_RDY;
 
+	if (info->flags & SPI_DATAFLASH)
+		nor->flags |= SPI_DATAFLASH;
+
 	/* Parse the Serial Flash Discoverable Parameters table. */
 	ret = spi_nor_init_params(nor, info, &params);
 	if (ret)
@@ -2922,6 +3062,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
 			return ret;
 	}
 
+	if (info->flags & SPI_DATAFLASH) {
+		ret = dataflash_nor_scan(info, nor);
+		if (ret)
+			return ret;
+	}
+
 	/* Send all the required SPI flash commands to initialize device */
 	nor->info = info;
 	ret = spi_nor_init(nor);
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index de36969..843edf9 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -91,6 +91,16 @@
 #define SPINOR_OP_WRDI		0x04	/* Write disable */
 #define SPINOR_OP_AAI_WP	0xad	/* Auto address increment word program */
 
+/* Used for Atmel Dataflashes only. */
+#define SPINOR_OP_DFRDSR	0xd7
+#define SPINOR_OP_DFBE_8k	0x50
+#define SPINOR_OP_DFMTB		0x53
+#define SPINOR_OP_DFBTOM	0x82
+#define SPINOR_OP_DFRD		0xe8
+
+#define DFSR_PAGESIZE		BIT(0)	/* Page size in Po2 or Linear */
+#define DFSR_RDY		BIT(7)	/* Ready */
+
 /* Used for S3AN flashes only */
 #define SPINOR_OP_XSE		0x50	/* Sector erase */
 #define SPINOR_OP_XPP		0x82	/* Page program */
-- 
2.7.4




More information about the linux-mtd mailing list