[5/6] 4BA: Support for new direct-4BA instructions + W25Q256.V update

Submitted by Cédric Le Goater on June 11, 2016, 4:29 p.m.

Details

Message ID 1465662544-5730-6-git-send-email-clg@kaod.org
State New
Headers show

Commit Message

Cédric Le Goater June 11, 2016, 4:29 p.m.
From: Boris Baykov <dev@borisbaykov.com>

Large flash chips usually support special instructions to work with
4-bytes address directly from 3-bytes addressing mode and without
do switching to 4-bytes mode. There are 13h (4BA Read), 12h (4BA Program)
and 21h,5Ch,DCh (4BA Erase), correspondingly. However not all these
instructions are supported by all large flash chips. Some chips
support 13h only, some 13h,12h,21h and DCh, but not 5Ch. This depends
on the manufacturer of the chip.

This patch provides code to use direct 4-bytes addressing instructions.

This code should work but it tested partially only. My W25Q256FV has
support for 4BA_Read (13h), but doesn't have support 4BA_Program (12h)
and 4BA_Erase instructions. So, direct 4BA program and erase
should be tested after.

Patched files
-------------
chipdrivers.h
+ added functions declarations for spi4ba.c

flash.h
+ feature definitions added

flashchips.c
+ modified definition of Winbond W25Q256BV/W25Q256FV chips

flashrom.c
+ modified switch to 4-bytes addressing for direct-4BA instructions

spi4ba.h
+ definitions for 4-bytes addressing JEDEC commands
+ functions declarations from spi4ba.c (same as in chipdrivers.h, just to see)

spi4ba.c
+ functions for read/write/erase directly with 4-bytes address (from any mode)

Signed-off-by: Boris Baykov <dev@borisbaykov.com>, Russia, Jan 2014
[clg: ported from
      https://www.flashrom.org/pipermail/flashrom/2015-January/013198.html ]
Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 chipdrivers.h |   6 ++
 flash.h       |   4 +
 flashchips.c  |   4 +-
 flashrom.c    |  15 +++-
 spi4ba.c      | 265 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 spi4ba.h      |  40 +++++++++
 6 files changed, 330 insertions(+), 4 deletions(-)

Patch hide | download patch | download mbox

diff --git a/chipdrivers.h b/chipdrivers.h
index a3da86570c42..20529d5be4ce 100644
--- a/chipdrivers.h
+++ b/chipdrivers.h
@@ -210,5 +210,11 @@  int spi_nbyte_read_4ba_ereg(struct flashctx *flash, unsigned int addr, uint8_t *
 int spi_block_erase_20_4ba_ereg(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
 int spi_block_erase_52_4ba_ereg(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
 int spi_block_erase_d8_4ba_ereg(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_byte_program_4ba_direct(struct flashctx *flash, unsigned int addr, uint8_t databyte);
+int spi_nbyte_program_4ba_direct(struct flashctx *flash, unsigned int addr, const uint8_t *bytes, unsigned int len);
+int spi_nbyte_read_4ba_direct(struct flashctx *flash, unsigned int addr, uint8_t *bytes, unsigned int len);
+int spi_block_erase_21_4ba_direct(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_5c_4ba_direct(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_dc_4ba_direct(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
 
 #endif /* !__CHIPDRIVERS_H__ */
diff --git a/flash.h b/flash.h
index cc374b798f07..7f79387a9251 100644
--- a/flash.h
+++ b/flash.h
@@ -127,6 +127,10 @@  enum write_granularity {
 #define FEATURE_4BA_SUPPORT		(1 << 10)
 #define FEATURE_4BA_ONLY 		(1 << 11)
 #define FEATURE_4BA_EXTENDED_ADDR_REG	(1 << 12)
+#define FEATURE_4BA_DIRECT_READ		(1 << 13)
+#define FEATURE_4BA_DIRECT_WRITE	(1 << 14)
+#define FEATURE_4BA_ALL_ERASERS_DIRECT  (1 << 15)
+#define FEATURE_4BA_ALL_DIRECT  (FEATURE_4BA_DIRECT_READ | FEATURE_4BA_DIRECT_WRITE | FEATURE_4BA_ALL_ERASERS_DIRECT)
 
 enum test_state {
 	OK = 0,
diff --git a/flashchips.c b/flashchips.c
index 21055e64cd22..7cd12fadb312 100644
--- a/flashchips.c
+++ b/flashchips.c
@@ -14597,11 +14597,11 @@  const struct flashchip flashchips[] = {
 		/* supports SFDP */
 		/* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44, read ID 0x4B */
 		/* FOUR_BYTE_ADDR: supports 4-bytes addressing mode */
-		.feature_bits	= FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_4BA_SUPPORT,
+		.feature_bits	= FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_4BA_SUPPORT | FEATURE_4BA_DIRECT_READ,
 		.four_bytes_addr_funcs =
 		{
 			.enter_4ba = spi_enter_4ba_b7_we, /* enter 4-bytes addressing mode by CMD B7 + WREN */
-			.read_nbyte = spi_nbyte_read_4ba, /* read from 4-bytes addressing mode */
+			.read_nbyte = spi_nbyte_read_4ba_direct, /* read directly from any mode, no need to enter 4ba */
 			.program_byte = spi_byte_program_4ba, /* write from 4-bytes addressing mode */
 			.program_nbyte = spi_nbyte_program_4ba /* write from 4-bytes addressing mode */
 		},
diff --git a/flashrom.c b/flashrom.c
index 79ddf76ad3d6..6a6f5b6f6af8 100644
--- a/flashrom.c
+++ b/flashrom.c
@@ -2011,8 +2011,14 @@  int doit(struct flashctx *flash, int force, const char *filename, int read_it,
 		else if(flash->chip->feature_bits & FEATURE_4BA_EXTENDED_ADDR_REG) {
 			msg_cdbg("Using 4-bytes addressing with extended address register.\n");
 		}
-		/* Go to 4-Bytes Addressing mode */
-		else {
+		/* Go to 4-Bytes Addressing mode if selected
+		   operation requires 4-Bytes Addressing mode
+		   (no need if functions are direct-4BA) */
+		else if(((read_it || verify_it)
+			&& (!(flash->chip->feature_bits & FEATURE_4BA_DIRECT_READ)))
+		   || ((erase_it || write_it)
+			&& ((flash->chip->feature_bits & FEATURE_4BA_ALL_DIRECT) != FEATURE_4BA_ALL_DIRECT))) {
+
 			if (!flash->chip->four_bytes_addr_funcs.enter_4ba) {
 				msg_cerr("No function for Enter 4-bytes addressing mode for this flash chip.\n"
 					"Please report to flashrom@flashrom.org\n");
@@ -2026,6 +2032,11 @@  int doit(struct flashctx *flash, int force, const char *filename, int read_it,
 
 			msg_cdbg("Switched to 4-bytes addressing mode.\n");
 		}
+		/* Do not switch to 4-Bytes Addressing mode if all instructions are direct-4BA
+		   or if the flash chip is 4-Bytes Addressing Only and always in 4BA-mode */
+		else {
+			msg_cdbg2("No need to switch to 4-bytes addressing mode.\n");
+		}
 	}
 
 	if (read_it) {
diff --git a/spi4ba.c b/spi4ba.c
index db31cd2064ca..6e1cc9b8d511 100644
--- a/spi4ba.c
+++ b/spi4ba.c
@@ -653,3 +653,268 @@  int spi_block_erase_d8_4ba_ereg(struct flashctx *flash, unsigned int addr,
 	/* FIXME: Check the status register for errors. */
 	return 0;
 }
+
+/* Program one flash byte with 4-bytes address from ANY mode (3-bytes or 4-bytes)
+   JEDEC_BYTE_PROGRAM_4BA (12h) instruction is new for 4-bytes addressing flash chips.
+   The presence of this instruction for an exact chip should be checked
+   by its datasheet or from SFDP 4-Bytes Address Instruction Table (JESD216B). */
+int spi_byte_program_4ba_direct(struct flashctx *flash, unsigned int addr,
+				 uint8_t databyte)
+{
+	int result;
+	struct spi_command cmds[] = {
+	{
+		.writecnt	= JEDEC_WREN_OUTSIZE,
+		.writearr	= (const unsigned char[]){ JEDEC_WREN },
+		.readcnt	= 0,
+		.readarr	= NULL,
+	}, {
+		.writecnt	= JEDEC_BYTE_PROGRAM_4BA_OUTSIZE,
+		.writearr	= (const unsigned char[]){
+					JEDEC_BYTE_PROGRAM_4BA,
+					(addr >> 24) & 0xff,
+					(addr >> 16) & 0xff,
+					(addr >> 8) & 0xff,
+					(addr & 0xff),
+					databyte
+				},
+		.readcnt	= 0,
+		.readarr	= NULL,
+	}, {
+		.writecnt	= 0,
+		.writearr	= NULL,
+		.readcnt	= 0,
+		.readarr	= NULL,
+	}};
+
+	msg_trace("-> %s (0x%08X)\n", __func__, addr);
+
+	result = spi_send_multicommand(flash, cmds);
+	if (result) {
+		msg_cerr("%s failed during command execution at address 0x%x\n",
+			__func__, addr);
+	}
+	return result;
+}
+
+/* Program flash bytes with 4-bytes address from ANY mode (3-bytes or 4-bytes)
+   JEDEC_BYTE_PROGRAM_4BA (12h) instruction is new for 4-bytes addressing flash chips.
+   The presence of this instruction for an exact chip should be checked
+   by its datasheet or from SFDP 4-Bytes Address Instruction Table (JESD216B). */
+int spi_nbyte_program_4ba_direct(struct flashctx *flash, unsigned int addr,
+				  const uint8_t *bytes, unsigned int len)
+{
+	int result;
+	unsigned char cmd[JEDEC_BYTE_PROGRAM_4BA_OUTSIZE - 1 + 256] = {
+		JEDEC_BYTE_PROGRAM_4BA,
+		(addr >> 24) & 0xff,
+		(addr >> 16) & 0xff,
+		(addr >> 8) & 0xff,
+		(addr >> 0) & 0xff
+	};
+	struct spi_command cmds[] = {
+	{
+		.writecnt	= JEDEC_WREN_OUTSIZE,
+		.writearr	= (const unsigned char[]){ JEDEC_WREN },
+		.readcnt	= 0,
+		.readarr	= NULL,
+	}, {
+		.writecnt	= JEDEC_BYTE_PROGRAM_4BA_OUTSIZE - 1 + len,
+		.writearr	= cmd,
+		.readcnt	= 0,
+		.readarr	= NULL,
+	}, {
+		.writecnt	= 0,
+		.writearr	= NULL,
+		.readcnt	= 0,
+		.readarr	= NULL,
+	}};
+
+	msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + len - 1);
+
+	if (!len) {
+		msg_cerr("%s called for zero-length write\n", __func__);
+		return 1;
+	}
+	if (len > 256) {
+		msg_cerr("%s called for too long a write\n", __func__);
+		return 1;
+	}
+
+	memcpy(&cmd[JEDEC_BYTE_PROGRAM_4BA_OUTSIZE - 1], bytes, len);
+
+	result = spi_send_multicommand(flash, cmds);
+	if (result) {
+		msg_cerr("%s failed during command execution at address 0x%x\n",
+			__func__, addr);
+	}
+	return result;
+}
+
+/* Read flash bytes with 4-bytes address from ANY mode (3-bytes or 4-bytes)
+   JEDEC_READ_4BA (13h) instruction is new for 4-bytes addressing flash chips.
+   The presence of this instruction for an exact chip should be checked
+   by its datasheet or from SFDP 4-Bytes Address Instruction Table (JESD216B). */
+int spi_nbyte_read_4ba_direct(struct flashctx *flash, unsigned int addr,
+		   uint8_t *bytes, unsigned int len)
+{
+	const unsigned char cmd[JEDEC_READ_4BA_OUTSIZE] = {
+		JEDEC_READ_4BA,
+		(addr >> 24) & 0xff,
+		(addr >> 16) & 0xff,
+		(addr >> 8) & 0xff,
+		(addr >> 0) & 0xff
+	};
+
+	msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + len - 1);
+
+	/* Send Read */
+	return spi_send_command(flash, sizeof(cmd), len, cmd, bytes);
+}
+
+/* Erase 4 KB of flash with 4-bytes address from ANY mode (3-bytes or 4-bytes)
+   JEDEC_SE_4BA (21h) instruction is new for 4-bytes addressing flash chips.
+   The presence of this instruction for an exact chip should be checked
+   by its datasheet or from SFDP 4-Bytes Address Instruction Table (JESD216B). */
+int spi_block_erase_21_4ba_direct(struct flashctx *flash, unsigned int addr,
+				   unsigned int blocklen)
+{
+	int result;
+	struct spi_command cmds[] = {
+	{
+		.writecnt	= JEDEC_WREN_OUTSIZE,
+		.writearr	= (const unsigned char[]){ JEDEC_WREN },
+		.readcnt	= 0,
+		.readarr	= NULL,
+	}, {
+		.writecnt	= JEDEC_SE_4BA_OUTSIZE,
+		.writearr	= (const unsigned char[]){
+					JEDEC_SE_4BA,
+					(addr >> 24) & 0xff,
+					(addr >> 16) & 0xff,
+					(addr >> 8) & 0xff,
+					(addr & 0xff)
+				},
+		.readcnt	= 0,
+		.readarr	= NULL,
+	}, {
+		.writecnt	= 0,
+		.writearr	= NULL,
+		.readcnt	= 0,
+		.readarr	= NULL,
+	}};
+
+	msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + blocklen - 1);
+
+	result = spi_send_multicommand(flash, cmds);
+	if (result) {
+		msg_cerr("%s failed during command execution at address 0x%x\n",
+			__func__, addr);
+		return result;
+	}
+	/* Wait until the Write-In-Progress bit is cleared.
+	 * This usually takes 15-800 ms, so wait in 10 ms steps.
+	 */
+	while (spi_read_status_register(flash) & SPI_SR_WIP)
+		programmer_delay(10 * 1000);
+	/* FIXME: Check the status register for errors. */
+	return 0;
+}
+
+/* Erase 32 KB of flash with 4-bytes address from ANY mode (3-bytes or 4-bytes)
+   JEDEC_BE_5C_4BA (5Ch) instruction is new for 4-bytes addressing flash chips.
+   The presence of this instruction for an exact chip should be checked
+   by its datasheet or from SFDP 4-Bytes Address Instruction Table (JESD216B). */
+int spi_block_erase_5c_4ba_direct(struct flashctx *flash, unsigned int addr,
+		       unsigned int blocklen)
+{
+	int result;
+	struct spi_command cmds[] = {
+	{
+		.writecnt	= JEDEC_WREN_OUTSIZE,
+		.writearr	= (const unsigned char[]){ JEDEC_WREN },
+		.readcnt	= 0,
+		.readarr	= NULL,
+	}, {
+		.writecnt	= JEDEC_BE_5C_4BA_OUTSIZE,
+		.writearr	= (const unsigned char[]){
+					JEDEC_BE_5C_4BA,
+					(addr >> 24) & 0xff,
+					(addr >> 16) & 0xff,
+					(addr >> 8) & 0xff,
+					(addr & 0xff)
+				},
+		.readcnt	= 0,
+		.readarr	= NULL,
+	}, {
+		.writecnt	= 0,
+		.writearr	= NULL,
+		.readcnt	= 0,
+		.readarr	= NULL,
+	}};
+
+	msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + blocklen - 1);
+
+	result = spi_send_multicommand(flash, cmds);
+	if (result) {
+		msg_cerr("%s failed during command execution at address 0x%x\n",
+			__func__, addr);
+		return result;
+	}
+	/* Wait until the Write-In-Progress bit is cleared.
+	 * This usually takes 100-4000 ms, so wait in 100 ms steps.
+	 */
+	while (spi_read_status_register(flash) & SPI_SR_WIP)
+		programmer_delay(100 * 1000);
+	/* FIXME: Check the status register for errors. */
+	return 0;
+}
+
+/* Erase 64 KB of flash with 4-bytes address from ANY mode (3-bytes or 4-bytes)
+   JEDEC_BE_DC_4BA (DCh) instruction is new for 4-bytes addressing flash chips.
+   The presence of this instruction for an exact chip should be checked
+   by its datasheet or from SFDP 4-Bytes Address Instruction Table (JESD216B). */
+int spi_block_erase_dc_4ba_direct(struct flashctx *flash, unsigned int addr,
+				   unsigned int blocklen)
+{
+	int result;
+	struct spi_command cmds[] = {
+	{
+		.writecnt	= JEDEC_WREN_OUTSIZE,
+		.writearr	= (const unsigned char[]){ JEDEC_WREN },
+		.readcnt	= 0,
+		.readarr	= NULL,
+	}, {
+		.writecnt	= JEDEC_BE_DC_4BA_OUTSIZE,
+		.writearr	= (const unsigned char[]){
+					JEDEC_BE_DC_4BA,
+					(addr >> 24) & 0xff,
+					(addr >> 16) & 0xff,
+					(addr >> 8) & 0xff,
+					(addr & 0xff)
+				},
+		.readcnt	= 0,
+		.readarr	= NULL,
+	}, {
+		.writecnt	= 0,
+		.writearr	= NULL,
+		.readcnt	= 0,
+		.readarr	= NULL,
+	}};
+
+	msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + blocklen - 1);
+
+	result = spi_send_multicommand(flash, cmds);
+	if (result) {
+		msg_cerr("%s failed during command execution at address 0x%x\n",
+			__func__, addr);
+		return result;
+	}
+	/* Wait until the Write-In-Progress bit is cleared.
+	 * This usually takes 100-4000 ms, so wait in 100 ms steps.
+	 */
+	while (spi_read_status_register(flash) & SPI_SR_WIP)
+		programmer_delay(100 * 1000);
+	/* FIXME: Check the status register for errors. */
+	return 0;
+}
diff --git a/spi4ba.h b/spi4ba.h
index f2e7bc54c669..8e500d159143 100644
--- a/spi4ba.h
+++ b/spi4ba.h
@@ -46,6 +46,36 @@ 
 #define JEDEC_READ_EXT_ADDR_REG_OUTSIZE		0x01
 #define JEDEC_READ_EXT_ADDR_REG_INSIZE		0x01
 
+/* Read the memory with 4-byte address
+   From ANY mode (3-bytes or 4-bytes) it works with 4-byte address */
+#define JEDEC_READ_4BA		0x13
+#define JEDEC_READ_4BA_OUTSIZE	0x05
+/*      JEDEC_READ_4BA_INSIZE : any length */
+
+/* Write memory byte with 4-byte address
+   From ANY mode (3-bytes or 4-bytes) it works with 4-byte address */
+#define JEDEC_BYTE_PROGRAM_4BA		0x12
+#define JEDEC_BYTE_PROGRAM_4BA_OUTSIZE	0x06
+#define JEDEC_BYTE_PROGRAM_4BA_INSIZE	0x00
+
+/* Sector Erase 0x21 (with 4-byte address), usually 4k size.
+   From ANY mode (3-bytes or 4-bytes) it works with 4-byte address */
+#define JEDEC_SE_4BA		0x21
+#define JEDEC_SE_4BA_OUTSIZE	0x05
+#define JEDEC_SE_4BA_INSIZE	0x00
+
+/* Block Erase 0x5C (with 4-byte address), usually 32k size.
+   From ANY mode (3-bytes or 4-bytes) it works with 4-byte address */
+#define JEDEC_BE_5C_4BA		0x5C
+#define JEDEC_BE_5C_4BA_OUTSIZE	0x05
+#define JEDEC_BE_5C_4BA_INSIZE	0x00
+
+/* Block Erase 0xDC (with 4-byte address), usually 64k size.
+   From ANY mode (3-bytes or 4-bytes) it works with 4-byte address */
+#define JEDEC_BE_DC_4BA		0xdc
+#define JEDEC_BE_DC_4BA_OUTSIZE	0x05
+#define JEDEC_BE_DC_4BA_INSIZE	0x00
+
 /* enter 4-bytes addressing mode */
 int spi_enter_4ba_b7(struct flashctx *flash);
 int spi_enter_4ba_b7_we(struct flashctx *flash);
@@ -70,5 +100,15 @@  int spi_block_erase_20_4ba_ereg(struct flashctx *flash, unsigned int addr, unsig
 int spi_block_erase_52_4ba_ereg(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
 int spi_block_erase_d8_4ba_ereg(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
 
+/* read/write flash bytes with 4-bytes address from any mode (3-byte or 4-byte) */
+int spi_byte_program_4ba_direct(struct flashctx *flash, unsigned int addr, uint8_t databyte);
+int spi_nbyte_program_4ba_direct(struct flashctx *flash, unsigned int addr, const uint8_t *bytes, unsigned int len);
+int spi_nbyte_read_4ba_direct(struct flashctx *flash, unsigned int addr, uint8_t *bytes, unsigned int len);
+
+/* erase flash bytes with 4-bytes address from any mode (3-byte or 4-byte) */
+int spi_block_erase_21_4ba_direct(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_5c_4ba_direct(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_dc_4ba_direct(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+
 
 #endif /* __SPI_4BA_H__ */