flashchips: Add Spansion 25FL256S......0

The Spansion 25SFL256S supports 4BA through an extended address register,
a 4BA mode set by bit 7 of that register, or native 4BA instructions.
Enable the former only for now.

Unfortunately the S25SF256S uses another instruction to write the exten-
ded address register. So we add an override for the instruction byte.

Change-Id: I0a95a81dfe86434f049215ebd8477392391b9efc
Signed-off-by: Nico Huber <nico.h@gmx.de>
Tested-by: Michael Fuckner <michael@fuckner.net>
Reviewed-on: https://review.coreboot.org/25132
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: David Hendricks <david.hendricks@gmail.com>
diff --git a/flash.h b/flash.h
index 01ff5cd..8eb8a7b 100644
--- a/flash.h
+++ b/flash.h
@@ -232,6 +232,9 @@
 		uint16_t max;
 	} voltage;
 	enum write_granularity gran;
+
+	/* SPI specific options (TODO: Make it a union in case other bustypes get specific options.) */
+	uint8_t wrea_override; /**< override opcode for write extended address register */
 };
 
 struct flashrom_flashctx {
diff --git a/flashchips.c b/flashchips.c
index 054ca7a..7853849 100644
--- a/flashchips.c
+++ b/flashchips.c
@@ -12516,6 +12516,48 @@
 	},
 
 	{
+		.vendor		= "Spansion",
+		.name		= "S25FL256S......0", /* hybrid: 32 (top or bottom) 4 kB sub-sectors + 64 kB sectors */
+		.bustype	= BUS_SPI,
+		.manufacture_id	= SPANSION_ID,
+		.model_id	= SPANSION_S25FL256,
+		.total_size	= 32768,
+		.page_size	= 256,
+		/* OTP: 1024B total, 32B reserved; read 0x4B; write 0x42 */
+		.feature_bits	= FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_4BA_EXT_ADDR,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	= {
+			{
+				/* This chip supports erasing of the 32 so-called "parameter sectors" with
+				 * opcode 0x20. Trying to access an address outside these 4kB blocks does
+				 * have no effect on the memory contents, but sets a flag in the SR.
+				.eraseblocks = {
+					{4 * 1024, 32},
+					{64 * 1024, 254} // inaccessible
+				},
+				.block_erase = spi_block_erase_20,
+			}, { */
+				.eraseblocks = { { 64 * 1024, 512} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { { 32768 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { { 32768 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_bp2_ep_srwd, /* TODO: SR2 and many others */
+		.unlock		= spi_disable_blockprotect_bp2_srwd, /* TODO: various other locks */
+		.write		= spi_chip_write_256, /* Multi I/O supported */
+		.read		= spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+		.voltage	= {2700, 3600},
+		.wrea_override	= 0x17,
+	},
+
+	{
 		.vendor		= "SST",
 		.name		= "SST25LF020A",
 		.bustype	= BUS_SPI,
diff --git a/spi25.c b/spi25.c
index c403570..3b5e50e 100644
--- a/spi25.c
+++ b/spi25.c
@@ -341,13 +341,14 @@
 
 static int spi_write_extended_address_register(struct flashctx *const flash, const uint8_t regdata)
 {
+	const uint8_t op = flash->chip->wrea_override ? : JEDEC_WRITE_EXT_ADDR_REG;
 	struct spi_command cmds[] = {
 	{
 		.writecnt = 1,
 		.writearr = (const unsigned char[]){ JEDEC_WREN },
 	}, {
 		.writecnt = 2,
-		.writearr = (const unsigned char[]){ JEDEC_WRITE_EXT_ADDR_REG, regdata },
+		.writearr = (const unsigned char[]){ op, regdata },
 	},
 		NULL_SPI_CMD,
 	};