spi: Prepare for multi i/o and dummy bytes
Multi-i/o commands split SPI transactions into multiple phases that
can be transferred over 1, 2 or 4 wires. For this, we adapt `struct
spi_command` with a new enum, specifying the transfer mode, and ad-
ditional size fields. While we are at it, move everything related
into a new header file `spi_command.h` so we won't further clutter
`flash.h`.
On the master side, we add respective feature flags for the multi-
i/o modes.
See also the comment in `spi_command.h` about multi-i/o commands.
Change-Id: I79debb845f1c8fec77e0556853ffb01735e73ab8
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.sourcearcade.org/c/flashprog/+/44
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
diff --git a/include/flash.h b/include/flash.h
index 3d899c7..850d607 100644
--- a/include/flash.h
+++ b/include/flash.h
@@ -517,16 +517,5 @@
#define msg_cspew(...) print(FLASHPROG_MSG_SPEW, __VA_ARGS__) /* chip debug spew */
void flashprog_progress_add(struct flashprog_flashctx *, size_t progress);
-/* spi.c */
-struct spi_command {
- unsigned int writecnt;
- unsigned int readcnt;
- const unsigned char *writearr;
- unsigned char *readarr;
-};
-#define NULL_SPI_CMD { 0, 0, NULL, NULL, }
-int spi_send_command(const struct flashctx *flash, unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr);
-int spi_send_multicommand(const struct flashctx *flash, struct spi_command *cmds);
-
enum chipbustype get_buses_supported(void);
#endif /* !__FLASH_H__ */
diff --git a/include/programmer.h b/include/programmer.h
index edef52b..fb19c00 100644
--- a/include/programmer.h
+++ b/include/programmer.h
@@ -299,7 +299,23 @@
#define SPI_MASTER_4BA (1U << 0) /**< Can handle 4-byte addresses */
#define SPI_MASTER_NO_4BA_MODES (1U << 1) /**< Compatibility modes (i.e. extended address
register, 4BA mode switch) don't work */
+#define SPI_MASTER_DUAL_IN (1U << 2) /**< Can read two bits at once (bidirectional
+ MOSI and MISO) */
+#define SPI_MASTER_DUAL_IO (1U << 3) /**< Can transfer two bits at once (bidirectional
+ MOSI and MISO) */
+#define SPI_MASTER_QUAD_IN (1U << 4) /**< Can read four bits at once (bidirectional
+ MOSI and MISO + IO2 + IO3) */
+#define SPI_MASTER_QUAD_IO (1U << 5) /**< Can transfer four bits at once (bidirectional
+ MOSI and MISO + IO2 + IO3) */
+#define SPI_MASTER_QPI (1U << 6) /**< Can send commands with quad i/o */
+#define SPI_MASTER_DTR_IN (1U << 7) /**< Double Transfer Rate: Can read two bits
+ per clock cycle per line */
+/* Shorthands: */
+#define SPI_MASTER_DUAL (SPI_MASTER_DUAL_IN | SPI_MASTER_DUAL_IO)
+#define SPI_MASTER_QUAD (SPI_MASTER_QUAD_IN | SPI_MASTER_QUAD_IO)
+
+struct spi_command;
struct spi_master {
uint32_t features;
unsigned int max_data_read; // (Ideally,) maximum data read size in one go (excluding opcode+address).
diff --git a/include/spi_command.h b/include/spi_command.h
new file mode 100644
index 0000000..54dfe48
--- /dev/null
+++ b/include/spi_command.h
@@ -0,0 +1,101 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __SPI_COMMAND_H__
+#define __SPI_COMMAND_H__ 1
+
+#include <stdlib.h>
+#include <stdbool.h>
+
+/*
+ * Modern SPI flashes support dual and quad i/o modes. However, there are
+ * subtle differences about which parts of a transactions are transferred
+ * in which mode. The transaction is generally divided into three phases:
+ * * opcode
+ * * address
+ * * data
+ *
+ * For each phase, the number of concurrently transferred bits is specified,
+ * hence we get a triple like
+ * * 1-1-1
+ * which tells us that all three phases are transferred in single i/o
+ * mode. Or, for instance,
+ * * 1-4-4
+ * which tells us the opcode is transferred in single i/o mode, but
+ * the address and data are transferred in quad i/o mode.
+ *
+ * There are a few common combinations, often chips support all of them:
+ * * 1-1-1 single i/o
+ * * 1-1-2 dual output (for reads, only the flash outputs two bits at once)
+ * * 1-2-2 dual i/o (both controller and flash can transfer two bits at once)
+ * * 1-1-4 quad output (for reads, only the flash outputs four bits at once)
+ * * 1-4-4 quad i/o (both controller and flash can transfer four bits at once)
+ * * 4-4-4 QPI
+ * In all modes that transfer the opcode in single i/o, the opcode tells the
+ * flash what to expect, i.e. how further bytes will be transferred. This
+ * achieves backwards compatibility with simple SPI controllers. The QPI
+ * mode, OTOH, is not backwards compatible and usually needs to be entered
+ * first with a special opcode. In QPI mode, only fast-read instructions
+ * (w/ dummy cycles) are supported; the number of dummy cycles is often
+ * configurable.
+ *
+ * For dual i/o, MOSI and MISO lines are bidirectional. So this can work
+ * without any special setup, if both controller and flash are compatible.
+ *
+ * For quad i/o, usually the flash's /HOLD and /WP pins are re-purposed, and
+ * the controller needs additional pins. The pin muxes inside the flash are
+ * usually controlled by a quad-enable (QE) bit in the status register. This
+ * is *not* to be confused with entering QPI mode. Quad-enable merely says
+ * that the pins are available for data transfer.
+ */
+enum io_mode {
+ SINGLE_IO_1_1_1,
+ DUAL_OUT_1_1_2,
+ DUAL_IO_1_2_2,
+ QUAD_OUT_1_1_4,
+ QUAD_IO_1_4_4,
+ QPI_4_4_4,
+};
+
+struct spi_command {
+ enum io_mode io_mode;
+ size_t opcode_len; /* bytes to write in opcode i/o phase */
+ size_t address_len; /* bytes to write in address i/o phase */
+ size_t write_len; /* bytes to write in data i/o phase */
+ size_t high_z_len; /* dummy bytes to skip in data i/o phase */
+ size_t read_len; /* bytes to read in data i/o phase */
+ const unsigned char *writearr;
+ unsigned char *readarr;
+};
+#define NULL_SPI_CMD { 0, 0, 0, 0, 0, 0, NULL, NULL, }
+
+static inline size_t spi_write_len(const struct spi_command *const cmd)
+{
+ return cmd->opcode_len + cmd->address_len + cmd->write_len;
+}
+
+static inline size_t spi_read_len(const struct spi_command *const cmd)
+{
+ return cmd->high_z_len + cmd->read_len;
+}
+
+static inline bool spi_is_empty(const struct spi_command *const cmd)
+{
+ return !spi_write_len(cmd) && !spi_read_len(cmd);
+}
+
+int spi_send_command(const struct flashctx *, unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr);
+int spi_send_multicommand(const struct flashctx *, struct spi_command *cmds);
+
+#endif /* !__SPI_COMMAND_H__ */