From eb179907834bd936a5a4a48d259a203a1fe350e9 Mon Sep 17 00:00:00 2001
From: Troy Kisky <troy.kisky@boundarydevices.com>
Date: Fri, 26 Jan 2018 18:04:25 -0800
Subject: [PATCH] mtd: spi: fix at45db041d

Signed-off-by: Troy Kisky <troy.kisky@boundarydevices.com>
---
 drivers/mtd/spi/sf_internal.h   |  9 ++++
 drivers/mtd/spi/spi_flash.c     | 74 ++++++++++++++++++++++++++++++---
 drivers/mtd/spi/spi_flash_ids.c |  2 +-
 include/spi_flash.h             |  5 +++
 4 files changed, 83 insertions(+), 7 deletions(-)

diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h
index 4f63cacc642..9a3d3350f9b 100644
--- a/drivers/mtd/spi/sf_internal.h
+++ b/drivers/mtd/spi/sf_internal.h
@@ -38,6 +38,7 @@ enum spi_nor_option_flags {
 #define SPI_FLASH_CFI_MFR_ATMEL		0x1f
 
 /* Erase commands */
+#define CMD_ERASE_2K			0x50
 #define CMD_ERASE_4K			0x20
 #define CMD_ERASE_CHIP			0xc7
 #define CMD_ERASE_64K			0xd8
@@ -45,6 +46,10 @@ enum spi_nor_option_flags {
 /* Write commands */
 #define CMD_WRITE_STATUS		0x01
 #define CMD_PAGE_PROGRAM		0x02
+/* 0x84 followed by 3byte offset into buffer */
+#define CMD_BUFFER1_WRITE		0x84
+/* 0x88 followed by 2 byte page #, and 1 dummy byte */
+#define CMD_BUFFER1_PROGRAM		0x88
 #define CMD_WRITE_DISABLE		0x04
 #define CMD_WRITE_ENABLE		0x06
 #define CMD_QUAD_PAGE_PROGRAM		0x32
@@ -59,6 +64,7 @@ enum spi_nor_option_flags {
 #define CMD_READ_ID			0x9f
 #define CMD_READ_STATUS			0x05
 #define CMD_READ_STATUS1		0x35
+#define CMD_READ_STATUS2		0xd7
 #define CMD_READ_CONFIG			0x35
 #define CMD_FLAG_STATUS			0x70
 
@@ -75,6 +81,7 @@ enum spi_nor_option_flags {
 #define STATUS_QEB_WINSPAN		BIT(1)
 #define STATUS_QEB_MXIC			BIT(6)
 #define STATUS_PEC			BIT(7)
+#define STATUS2_READY			BIT(7)
 #define SR_BP0				BIT(2)  /* Block protect 0 */
 #define SR_BP1				BIT(3)  /* Block protect 1 */
 #define SR_BP2				BIT(4)  /* Block protect 2 */
@@ -143,6 +150,8 @@ struct spi_flash_info {
 #define RD_DUAL			BIT(5)	/* use Dual Read */
 #define RD_QUADIO		BIT(6)	/* use Quad IO Read */
 #define RD_DUALIO		BIT(7)	/* use Dual IO Read */
+#define ATMEL_REGS		BIT(8)
+#define SECT_2K			BIT(9)	/* CMD_ERASE_2K works */
 #define RD_FULL			(RD_QUAD | RD_DUAL | RD_QUADIO | RD_DUALIO)
 };
 
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
index dd93ff61d85..14c16a38580 100644
--- a/drivers/mtd/spi/spi_flash.c
+++ b/drivers/mtd/spi/spi_flash.c
@@ -31,15 +31,12 @@ static void spi_flash_addr(u32 addr, u8 *cmd)
 static int read_sr(struct spi_flash *flash, u8 *rs)
 {
 	int ret;
-	u8 cmd;
 
-	cmd = CMD_READ_STATUS;
-	ret = spi_flash_read_common(flash, &cmd, 1, rs, 1);
+	ret = spi_flash_read_common(flash, &flash->status_cmd, 1, rs, 1);
 	if (ret < 0) {
 		debug("SF: fail to read status register\n");
 		return ret;
 	}
-
 	return 0;
 }
 
@@ -215,8 +212,7 @@ static int spi_flash_sr_ready(struct spi_flash *flash)
 	ret = read_sr(flash, &sr);
 	if (ret < 0)
 		return ret;
-
-	return !(sr & STATUS_WIP);
+	return (sr & flash->status_ready_mask) == flash->status_ready_level;
 }
 
 static int spi_flash_fsr_ready(struct spi_flash *flash)
@@ -428,6 +424,50 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
 	return ret;
 }
 
+#ifdef CONFIG_SPI_FLASH_ATMEL
+int spi_flash_cmd_write_ops_atmel(struct spi_flash *flash, u32 offset,
+		size_t len, const void *buf)
+{
+	unsigned long byte_addr, page_size;
+	size_t actual;
+	u8 cmd[4];
+	int ret = -1;
+
+	page_size = flash->page_size;
+
+	byte_addr = offset % page_size;
+	if (byte_addr)
+		return -EINVAL;
+
+	for (actual = 0; actual < len; actual += page_size) {
+		cmd[0] = CMD_BUFFER1_WRITE;
+		cmd[1] = 0;
+		cmd[2] = 0;
+		cmd[3] = 0;
+		ret = spi_flash_write_common(flash, cmd, sizeof(cmd),
+					buf + actual, page_size);
+		if (ret < 0) {
+			debug("SF: write failed\n");
+			break;
+		}
+
+		cmd[0] = CMD_BUFFER1_PROGRAM;
+		spi_flash_addr(offset, cmd);
+
+		ret = spi_flash_write_common(flash, cmd, sizeof(cmd),
+					buf + actual, page_size);
+		if (ret < 0) {
+			debug("SF: write failed\n");
+			break;
+		}
+
+		offset += page_size;
+	}
+
+	return ret;
+}
+#endif
+
 int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,
 		size_t cmd_len, void *data, size_t data_len)
 {
@@ -1176,6 +1216,19 @@ int spi_flash_scan(struct spi_flash *flash)
 			flash->write = sst_write_wp;
 	}
 #endif
+
+	flash->status_cmd = CMD_READ_STATUS;
+	flash->status_ready_mask = STATUS_WIP;
+	flash->status_ready_level = 0;
+
+#ifdef CONFIG_SPI_FLASH_ATMEL
+	if (info->flags & ATMEL_REGS) {
+		flash->write = spi_flash_cmd_write_ops_atmel;
+		flash->status_cmd = CMD_READ_STATUS2;
+		flash->status_ready_mask = STATUS2_READY;
+		flash->status_ready_level = STATUS2_READY;
+	}
+#endif
 	flash->erase = spi_flash_cmd_erase_ops;
 	flash->read = spi_flash_cmd_read_ops;
 #endif
@@ -1227,6 +1280,9 @@ int spi_flash_scan(struct spi_flash *flash)
 	if (info->flags & SECT_4K) {
 		flash->erase_cmd = CMD_ERASE_4K;
 		flash->erase_size = 4096 << flash->shift;
+	} else if (info->flags & SECT_2K) {
+		flash->erase_cmd = CMD_ERASE_2K;
+		flash->erase_size = 2048 << flash->shift;
 	} else
 #endif
 	{
@@ -1253,6 +1309,12 @@ int spi_flash_scan(struct spi_flash *flash)
 		/* Go for default supported write cmd */
 		flash->write_cmd = CMD_PAGE_PROGRAM;
 
+	/* Flash powers up read-only, so clear BP# bits */
+	if (JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_ATMEL ||
+	    JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_MACRONIX ||
+	    JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_SST)
+		write_sr(flash, 0);
+
 	/* Set the quad enable bit - only for quad commands */
 	if ((flash->read_cmd == CMD_READ_QUAD_OUTPUT_FAST) ||
 	    (flash->read_cmd == CMD_READ_QUAD_IO_FAST) ||
diff --git a/drivers/mtd/spi/spi_flash_ids.c b/drivers/mtd/spi/spi_flash_ids.c
index 2d1f9ea8a12..4e77dd2f643 100644
--- a/drivers/mtd/spi/spi_flash_ids.c
+++ b/drivers/mtd/spi/spi_flash_ids.c
@@ -46,7 +46,7 @@ const struct spi_flash_info spi_flash_ids[] = {
 #ifdef CONFIG_SPI_FLASH_ATMEL		/* ATMEL */
 	{"at45db011d",	   INFO(0x1f2200, 0x0, 64 * 1024,     4, SECT_4K) },
 	{"at45db021d",	   INFO(0x1f2300, 0x0, 64 * 1024,     8, SECT_4K) },
-	{"at45db041d",	   INFO(0x1f2400, 0x0, 64 * 1024,     8, SECT_4K) },
+	{"at45db041d",	   INFO(0x1f2400, 0x0, 64 * 1024,     8, SECT_2K | ATMEL_REGS) },
 	{"at45db081d",	   INFO(0x1f2500, 0x0, 64 * 1024,    16, SECT_4K) },
 	{"at45db161d",	   INFO(0x1f2600, 0x0, 64 * 1024,    32, SECT_4K) },
 	{"at45db321d",	   INFO(0x1f2700, 0x0, 64 * 1024,    64, SECT_4K) },
diff --git a/include/spi_flash.h b/include/spi_flash.h
index 22533311c54..5edf4727926 100644
--- a/include/spi_flash.h
+++ b/include/spi_flash.h
@@ -83,6 +83,11 @@ struct spi_flash {
 	u8 write_cmd;
 	u8 dummy_byte;
 
+	u8 status_cmd;
+	u8 status_ready_mask;
+	u8 status_ready_level;
+	u8 spare1;
+
 	void *memory_map;
 
 	int (*flash_lock)(struct spi_flash *flash, u32 ofs, size_t len);
-- 
GitLab