spi25: Introduce spi_simple_write_cmd()

spi_simple_write_cmd() executes WREN plus a single byte write and polls
WIP afterwards. It's used to replace current spi_erase_chip_*() imple-
mentations.

Change-Id: Ib244356fa471e15863b52e6037899d19113cb4a9
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.coreboot.org/22382
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 62f2e34..b18d698 100644
--- a/flash.h
+++ b/flash.h
@@ -369,6 +369,7 @@
 	const unsigned char *writearr;
 	unsigned char *readarr;
 };
+#define NULL_SPI_CMD { 0, 0, NULL, NULL, }
 int spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr);
 int spi_send_multicommand(struct flashctx *flash, struct spi_command *cmds);
 uint32_t spi_get_valid_read_addr(struct flashctx *flash);
diff --git a/spi25.c b/spi25.c
index 30b862f..da04d2e 100644
--- a/spi25.c
+++ b/spi25.c
@@ -22,6 +22,7 @@
  * Contains the common SPI chip driver functions
  */
 
+#include <stddef.h>
 #include <string.h>
 #include "flash.h"
 #include "flashchips.h"
@@ -322,114 +323,56 @@
 	return 0;
 }
 
-int spi_chip_erase_60(struct flashctx *flash)
+/**
+ * Execute WREN plus another one byte `op`, optionally poll WIP afterwards.
+ *
+ * @param flash       the flash chip's context
+ * @param op          the operation to execute
+ * @param poll_delay  interval in us for polling WIP, don't poll if zero
+ * @return 0 on success, non-zero otherwise
+ */
+static int spi_simple_write_cmd(struct flashctx *const flash, const uint8_t op, const unsigned int poll_delay)
 {
-	int result;
 	struct spi_command cmds[] = {
 	{
-		.writecnt	= JEDEC_WREN_OUTSIZE,
-		.writearr	= (const unsigned char[]){ JEDEC_WREN },
-		.readcnt	= 0,
-		.readarr	= NULL,
+		.writecnt = 1,
+		.writearr = (const unsigned char[]){ JEDEC_WREN },
 	}, {
-		.writecnt	= JEDEC_CE_60_OUTSIZE,
-		.writearr	= (const unsigned char[]){ JEDEC_CE_60 },
-		.readcnt	= 0,
-		.readarr	= NULL,
-	}, {
-		.writecnt	= 0,
-		.writearr	= NULL,
-		.readcnt	= 0,
-		.readarr	= NULL,
-	}};
-	
-	result = spi_send_multicommand(flash, cmds);
-	if (result) {
-		msg_cerr("%s failed during command execution\n",
-			__func__);
-		return result;
-	}
-	/* Wait until the Write-In-Progress bit is cleared.
-	 * This usually takes 1-85 s, so wait in 1 s steps.
-	 */
-	/* FIXME: We assume spi_read_status_register will never fail. */
-	while (spi_read_status_register(flash) & SPI_SR_WIP)
-		programmer_delay(1000 * 1000);
+		.writecnt = 1,
+		.writearr = (const unsigned char[]){ op },
+	},
+		NULL_SPI_CMD,
+	};
+
+	const int result = spi_send_multicommand(flash, cmds);
+	if (result)
+		msg_cerr("%s failed during command execution\n", __func__);
+
+	/* FIXME: We can't tell if spi_read_status_register() failed. */
+	/* FIXME: We don't time out. */
+	while (poll_delay && spi_read_status_register(flash) & SPI_SR_WIP)
+		programmer_delay(poll_delay);
 	/* FIXME: Check the status register for errors. */
-	return 0;
+
+	return result;
+}
+
+int spi_chip_erase_60(struct flashctx *flash)
+{
+	/* This usually takes 1-85s, so wait in 1s steps. */
+	return spi_simple_write_cmd(flash, 0x60, 1000 * 1000);
 }
 
 int spi_chip_erase_62(struct flashctx *flash)
 {
-	int result;
-	struct spi_command cmds[] = {
-	{
-		.writecnt	= JEDEC_WREN_OUTSIZE,
-		.writearr	= (const unsigned char[]){ JEDEC_WREN },
-		.readcnt	= 0,
-		.readarr	= NULL,
-	}, {
-		.writecnt	= JEDEC_CE_62_OUTSIZE,
-		.writearr	= (const unsigned char[]){ JEDEC_CE_62 },
-		.readcnt	= 0,
-		.readarr	= NULL,
-	}, {
-		.writecnt	= 0,
-		.writearr	= NULL,
-		.readcnt	= 0,
-		.readarr	= NULL,
-	}};
-	
-	result = spi_send_multicommand(flash, cmds);
-	if (result) {
-		msg_cerr("%s failed during command execution\n",
-			__func__);
-		return result;
-	}
-	/* Wait until the Write-In-Progress bit is cleared.
-	 * This usually takes 2-5 s, so wait in 100 ms steps.
-	 */
-	/* FIXME: We assume spi_read_status_register will never fail. */
-	while (spi_read_status_register(flash) & SPI_SR_WIP)
-		programmer_delay(100 * 1000);
-	/* FIXME: Check the status register for errors. */
-	return 0;
+	/* This usually takes 2-5s, so wait in 100ms steps. */
+	return spi_simple_write_cmd(flash, 0x62, 100 * 1000);
 }
 
 int spi_chip_erase_c7(struct flashctx *flash)
 {
-	int result;
-	struct spi_command cmds[] = {
-	{
-		.writecnt	= JEDEC_WREN_OUTSIZE,
-		.writearr	= (const unsigned char[]){ JEDEC_WREN },
-		.readcnt	= 0,
-		.readarr	= NULL,
-	}, {
-		.writecnt	= JEDEC_CE_C7_OUTSIZE,
-		.writearr	= (const unsigned char[]){ JEDEC_CE_C7 },
-		.readcnt	= 0,
-		.readarr	= NULL,
-	}, {
-		.writecnt	= 0,
-		.writearr	= NULL,
-		.readcnt	= 0,
-		.readarr	= NULL,
-	}};
-
-	result = spi_send_multicommand(flash, cmds);
-	if (result) {
-		msg_cerr("%s failed during command execution\n", __func__);
-		return result;
-	}
-	/* Wait until the Write-In-Progress bit is cleared.
-	 * This usually takes 1-85 s, so wait in 1 s steps.
-	 */
-	/* FIXME: We assume spi_read_status_register will never fail. */
-	while (spi_read_status_register(flash) & SPI_SR_WIP)
-		programmer_delay(1000 * 1000);
-	/* FIXME: Check the status register for errors. */
-	return 0;
+	/* This usually takes 1-85s, so wait in 1s steps. */
+	return spi_simple_write_cmd(flash, 0xc7, 1000 * 1000);
 }
 
 int spi_block_erase_52(struct flashctx *flash, unsigned int addr,