[U-Boot] [PATCH 14/18] sf: add 4byte address opcodes

Cyrille Pitchen cyrille.pitchen at atmel.com
Tue Mar 15 19:12:36 CET 2016


This patch provides an alternative to support memory >16MiB (>128Mib).
Indeed using the Base Address Register changes the internal state of
the SPI flash memory. However some early boot loaders expect to access
the first memory bank of the SPI flash. Then when another bank has been
selected, those boot loader will fail to read the right data.

Using 4byte address opcodes doesn't change the internal state of the SPI
flash memory but sill allows to access data above 16MiB.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen at atmel.com>
---
 drivers/mtd/spi/Kconfig       | 18 ++++++++-
 drivers/mtd/spi/sf_internal.h | 17 +++++++-
 drivers/mtd/spi/spi_flash.c   | 93 +++++++++++++++++++++++++++++++++++++------
 include/spi_flash.h           |  2 +
 4 files changed, 114 insertions(+), 16 deletions(-)

diff --git a/drivers/mtd/spi/Kconfig b/drivers/mtd/spi/Kconfig
index 3f7433cbc214..999c3176adee 100644
--- a/drivers/mtd/spi/Kconfig
+++ b/drivers/mtd/spi/Kconfig
@@ -34,14 +34,30 @@ config SPI_FLASH
 
 	  If unsure, say N
 
+config SPI_FLASH_ABOVE_16MB
+	bool "Support of >16MB memories"
+	depends on SPI_FLASH
+	default n
+
+choice
+	prompt "Select >16MB method"
+	depends on SPI_FLASH_ABOVE_16MB
+
 config SPI_FLASH_BAR
 	bool "SPI flash Bank/Extended address register support"
-	depends on SPI_FLASH
 	help
 	  Enable the SPI flash Bank/Extended address register support.
 	  Bank/Extended address registers are used to access the flash
 	  which has size > 16MiB in 3-byte addressing.
 
+config SPI_FLASH_4B_OPCODES
+	bool "4-byte address op codes support"
+	help
+	  Replace regular 3-byte address op codes by thier 4-byte address
+	  version when the size of the memory if greater than 16MB.
+
+endchoice
+
 if SPI_FLASH
 
 config SPI_FLASH_ATMEL
diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h
index 86a0276f8518..a98c011218ce 100644
--- a/drivers/mtd/spi/sf_internal.h
+++ b/drivers/mtd/spi/sf_internal.h
@@ -62,8 +62,8 @@ enum spi_nor_option_flags {
 	SNOR_F_USE_FSR		= BIT(2),
 };
 
-#define SPI_FLASH_3B_ADDR_LEN		3
-#define SPI_FLASH_CMD_LEN		(1 + SPI_FLASH_3B_ADDR_LEN)
+#define SPI_FLASH_4B_ADDR_LEN		4
+#define SPI_FLASH_CMD_LEN		(1 + SPI_FLASH_4B_ADDR_LEN)
 #define SPI_FLASH_16MB_BOUN		0x1000000
 
 /* CFI Manufacture ID's */
@@ -79,6 +79,9 @@ enum spi_nor_option_flags {
 #define CMD_ERASE_32K			0x52
 #define CMD_ERASE_CHIP			0xc7
 #define CMD_ERASE_64K			0xd8
+#define CMD_ERASE_4K_4B			0x21
+#define CMD_ERASE_32K_4B		0x5c
+#define CMD_ERASE_64K_4B		0xdc
 
 /* Write commands */
 #define CMD_WRITE_STATUS		0x01
@@ -86,6 +89,10 @@ enum spi_nor_option_flags {
 #define CMD_WRITE_DISABLE		0x04
 #define CMD_WRITE_ENABLE		0x06
 #define CMD_QUAD_PAGE_PROGRAM		0x32
+#define CMD_QUAD_PAGE_PROGRAM_MXIC	0x38
+#define CMD_PAGE_PROGRAM_4B		0x12
+#define CMD_QUAD_PAGE_PROGRAM_4B	0x34
+#define CMD_QUAD_PAGE_PROGRAM_MXIC_4B	0x3e
 
 /* Read commands */
 #define CMD_READ_ARRAY_SLOW		0x03
@@ -94,6 +101,12 @@ enum spi_nor_option_flags {
 #define CMD_READ_DUAL_IO_FAST		0xbb
 #define CMD_READ_QUAD_OUTPUT_FAST	0x6b
 #define CMD_READ_QUAD_IO_FAST		0xeb
+#define CMD_READ_ARRAY_SLOW_4B		0x13
+#define CMD_READ_ARRAY_FAST_4B		0x0c
+#define CMD_READ_DUAL_OUTPUT_FAST_4B	0x3c
+#define CMD_READ_DUAL_IO_FAST_4B	0xbc
+#define CMD_READ_QUAD_OUTPUT_FAST_4B	0x6c
+#define CMD_READ_QUAD_IO_FAST_4B	0xec
 #define CMD_READ_ID			0x9f
 #define CMD_READ_STATUS			0x05
 #define CMD_READ_STATUS1		0x35
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
index 01a073d81eb9..b4a44e25f190 100644
--- a/drivers/mtd/spi/spi_flash.c
+++ b/drivers/mtd/spi/spi_flash.c
@@ -22,12 +22,15 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
-static void spi_flash_addr(u32 addr, u8 *cmd)
+static void spi_flash_addr(const struct spi_flash *flash, u32 addr, u8 *cmd)
 {
+	u8 shift = flash->addr_width * 8;
+
 	/* cmd[0] is actual command */
-	cmd[1] = addr >> 16;
-	cmd[2] = addr >> 8;
-	cmd[3] = addr >> 0;
+	cmd[1] = addr >> (shift -  8);
+	cmd[2] = addr >> (shift - 16);
+	cmd[3] = addr >> (shift - 24);
+	cmd[4] = addr >> (shift - 32);
 }
 
 static int read_sr(struct spi_flash *flash, u8 *rs)
@@ -154,6 +157,62 @@ bar_end:
 }
 #endif
 
+#ifdef CONFIG_SPI_FLASH_4B_OPCODES
+struct address_entry {
+	u8	src_opcode;
+	u8	dst_opcode;
+};
+
+static u8 spi_flash_convert_opcode(u8 opcode,
+				   const struct address_entry *entries,
+				   size_t num_entries)
+{
+	int b, e;
+
+	b = 0;
+	e = num_entries - 1;
+	while (b <= e) {
+		int m = (b + e) >> 1;
+		const struct address_entry *entry = &entries[m];
+
+		if (opcode == entry->src_opcode)
+			return entry->dst_opcode;
+
+		if (opcode < entry->src_opcode)
+			e = m - 1;
+		else
+			b = m + 1;
+	}
+
+	/* No convertion found */
+	return opcode;
+}
+
+static u8 spi_flash_3to4_opcode(u8 opcode)
+{
+	/* MUST be sorted by 3byte opcode */
+#define ENTRY_3TO4(_opcode)	{ _opcode, _opcode##_4B }
+	static const struct address_entry spi_flash_3to4_table[] = {
+		ENTRY_3TO4(CMD_READ_ARRAY_SLOW),	/* 0x03 */
+		ENTRY_3TO4(CMD_READ_ARRAY_FAST),	/* 0x0b */
+		ENTRY_3TO4(CMD_ERASE_4K),		/* 0x20 */
+		ENTRY_3TO4(CMD_QUAD_PAGE_PROGRAM),	/* 0x32 */
+		ENTRY_3TO4(CMD_QUAD_PAGE_PROGRAM_MXIC),	/* 0x38 */
+		ENTRY_3TO4(CMD_READ_DUAL_OUTPUT_FAST),	/* 0x3b */
+		ENTRY_3TO4(CMD_ERASE_32K),		/* 0x52 */
+		ENTRY_3TO4(CMD_READ_QUAD_OUTPUT_FAST),	/* 0x6b */
+		ENTRY_3TO4(CMD_READ_DUAL_IO_FAST),	/* 0xbb */
+		ENTRY_3TO4(CMD_ERASE_64K),		/* 0xd8 */
+		ENTRY_3TO4(CMD_READ_QUAD_IO_FAST),	/* 0xeb */
+	};
+#undef ENTRY_3TO4
+
+	return spi_flash_convert_opcode(opcode,
+					spi_flash_3to4_table,
+					ARRAY_SIZE(spi_flash_3to4_table));
+}
+#endif /* CONFIG_SPI_FLASH_4B_OPCODES */
+
 #ifdef CONFIG_SF_DUAL_FLASH
 static void spi_flash_dual(struct spi_flash *flash, u32 *addr)
 {
@@ -353,10 +412,11 @@ static int spi_flash_erase_impl(struct spi_flash *flash, u32 offset)
 {
 	struct spi_slave *spi = flash->spi;
 	u8 cmd[SPI_FLASH_CMD_LEN];
+	size_t cmdsz = 1 + flash->addr_width;
 
 	cmd[0] = flash->erase_cmd;
-	spi_flash_addr(offset, cmd);
-	return spi_flash_cmd_write(spi, cmd, sizeof(cmd), NULL, 0);
+	spi_flash_addr(flash, offset, cmd);
+	return spi_flash_cmd_write(spi, cmd, cmdsz, NULL, 0);
 }
 
 int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len)
@@ -461,10 +521,10 @@ static int spi_flash_write_impl(struct spi_flash *flash, u32 offset,
 {
 	struct spi_slave *spi = flash->spi;
 	u8 cmd[SPI_FLASH_CMD_LEN];
-	size_t cmdsz = sizeof(cmd);
+	size_t cmdsz = 1 + flash->addr_width;
 
 	cmd[0] = flash->write_cmd;
-	spi_flash_addr(offset, cmd);
+	spi_flash_addr(flash, offset, cmd);
 #ifdef CONFIG_SPI_FLASH_SST
 	if (flash->flags & SNOR_F_SST_WR_2ND)
 		cmdsz = 1;
@@ -567,7 +627,7 @@ static int spi_flash_read_impl(struct spi_flash *flash, u32 offset,
 	size_t cmdsz;
 	int ret;
 
-	cmdsz = SPI_FLASH_CMD_LEN + flash->dummy_byte;
+	cmdsz = 1 + flash->addr_width + flash->dummy_byte;
 	cmd = calloc(1, cmdsz);
 	if (!cmd) {
 		debug("SF: Failed to allocate cmd\n");
@@ -575,7 +635,7 @@ static int spi_flash_read_impl(struct spi_flash *flash, u32 offset,
 	}
 
 	cmd[0] = flash->read_cmd;
-	spi_flash_addr(offset, cmd);
+	spi_flash_addr(flash, offset, cmd);
 	ret = spi_flash_cmd_read(spi, cmd, cmdsz, buf, len);
 	free(cmd);
 	return ret;
@@ -1255,15 +1315,22 @@ int spi_flash_scan(struct spi_flash *flash, u8 e_rd_cmd)
 	puts("\n");
 #endif
 
-#ifndef CONFIG_SPI_FLASH_BAR
+	/* Set number of bytes for address and convert opcodes if needed */
+	flash->addr_width = 3;
 	if (((flash->dual_flash == SF_SINGLE_FLASH) &&
 	     (flash->size > SPI_FLASH_16MB_BOUN)) ||
 	     ((flash->dual_flash > SF_SINGLE_FLASH) &&
 	     (flash->size > SPI_FLASH_16MB_BOUN << 1))) {
+#if defined(CONFIG_SPI_FLASH_4B_OPCODES)
+		flash->addr_width = 4;
+		flash->read_cmd  = spi_flash_3to4_opcode(flash->read_cmd);
+		flash->write_cmd = spi_flash_3to4_opcode(flash->write_cmd);
+		flash->erase_cmd = spi_flash_3to4_opcode(flash->erase_cmd);
+#elif !defined(CONFIG_SPI_FLASH_BAR)
 		puts("SF: Warning - Only lower 16MiB accessible,");
-		puts(" Full access #define CONFIG_SPI_FLASH_BAR\n");
-	}
+		puts(" Full access #define CONFIG_SPI_FLASH_ABOVE_16MB\n");
 #endif
+	}
 
 	return ret;
 }
diff --git a/include/spi_flash.h b/include/spi_flash.h
index 945cc07ee8b2..5db06e5133f3 100644
--- a/include/spi_flash.h
+++ b/include/spi_flash.h
@@ -88,6 +88,7 @@ enum spi_flash_protocol {
  * @read_proto		SPI protocol to be used by &read ops
  * @write_proto		SPI protocol to be used by &write ops
  * @erase_proto		SPI protocol to be used by &erase ops
+ * @addr_width		Number of address bytes (typically 3 or 4)
  * @memory_map:		Address of read-only SPI flash access
  * @flash_lock:		lock a region of the SPI Flash
  * @flash_unlock:	unlock a region of the SPI Flash
@@ -117,6 +118,7 @@ struct spi_flash {
 	u32 page_size;
 	u32 sector_size;
 	u32 erase_size;
+	u32 addr_width;
 #ifdef CONFIG_SPI_FLASH_BAR
 	u8 bank_read_cmd;
 	u8 bank_write_cmd;
-- 
1.8.2.2



More information about the U-Boot mailing list