spi25: Implement multi-i/o reads

We describe a read operation in a new  `struct spi_read_op`. It's
comprised of the i/o mode, its opcode, an optional mode byte, and
the number of dummy bytes.

Based on this information  about the various read operations, and
the flash and master feature flags,  we select the read operation
with the highest throughput.

The following assumption is made about 4BA chips: When it supports
native-4BA fast reads  and a multi-i/o version of the regular fast
read, then it should also support the respective native-4BA, multi-
i/o version (yes, JEDEC, there are too many read commands!). So far
this seems to hold for the chips in our database.

Change-Id: I3c93e71d85f769831d637c14d3571f7ddb54d8b2
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.sourcearcade.org/c/flashprog/+/49
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
diff --git a/spi25_prepare.c b/spi25_prepare.c
index 7482c5c..279f2e4 100644
--- a/spi25_prepare.c
+++ b/spi25_prepare.c
@@ -102,6 +102,42 @@
 	return 0;
 }
 
+static const struct spi_read_op *select_spi_fast_read(const struct flashctx *flash)
+{
+	static const struct {
+		unsigned int feature_check;
+		unsigned int master_check;
+		struct spi_read_op op;
+	#define MIO_CHECKS(flash_feature, master_feature) \
+		FEATURE_FAST_READ_##flash_feature, SPI_MASTER_##master_feature
+	} mio[] = { /*       flash  master                     4BA                              mode  dummies */
+		{ MIO_CHECKS(QIO,  QUAD_IO), { QUAD_IO_1_4_4,  true,  JEDEC_FAST_READ_QIO_4BA,  0xff, 3 } },
+		{ MIO_CHECKS(QOUT, QUAD_IN), { QUAD_OUT_1_1_4, true,  JEDEC_FAST_READ_QOUT_4BA, 0x00, 4 } },
+		{ MIO_CHECKS(DIO,  DUAL_IO), { DUAL_IO_1_2_2,  true,  JEDEC_FAST_READ_DIO_4BA,  0xff, 1 } },
+		{ MIO_CHECKS(DOUT, DUAL_IN), { DUAL_OUT_1_1_2, true,  JEDEC_FAST_READ_DOUT_4BA, 0x00, 2 } },
+		{ MIO_CHECKS(QIO,  QUAD_IO), { QUAD_IO_1_4_4,  false, JEDEC_FAST_READ_QIO,      0xff, 3 } },
+		{ MIO_CHECKS(QOUT, QUAD_IN), { QUAD_OUT_1_1_4, false, JEDEC_FAST_READ_QOUT,     0x00, 4 } },
+		{ MIO_CHECKS(DIO,  DUAL_IO), { DUAL_IO_1_2_2,  false, JEDEC_FAST_READ_DIO,      0xff, 1 } },
+		{ MIO_CHECKS(DOUT, DUAL_IN), { DUAL_OUT_1_1_2, false, JEDEC_FAST_READ_DOUT,     0x00, 2 } },
+	};
+
+	unsigned int i;
+	for (i = 0; i < ARRAY_SIZE(mio); ++i) {
+		if (mio[i].op.native_4ba && !(flash->chip->feature_bits & FEATURE_4BA_FAST_READ))
+			continue;
+		if ((flash->chip->feature_bits & mio[i].feature_check) != mio[i].feature_check)
+			continue;
+		if ((flash->mst.spi->features & mio[i].master_check) != mio[i].master_check)
+			continue;
+		if (mio[i].op.native_4ba && !spi_master_4ba(flash))
+			continue;
+		if (flash->mst.spi->probe_opcode(flash, mio[i].op.opcode))
+			return &mio[i].op;
+	}
+
+	return NULL;
+}
+
 int spi_prepare_io(struct flashctx *const flash, const enum preparation_steps prep)
 {
 	if (prep != PREPARE_FULL)
@@ -115,6 +151,8 @@
 	if (ret)
 		return ret;
 
+	flash->spi_fast_read = select_spi_fast_read(flash);
+
 	return 0;
 }