summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJes B. Klinke <jbk@chromium.org>2023-03-16 09:47:33 -0700
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2023-04-18 17:56:34 +0000
commitb70454186cb913e5060b6237bf9c8b16f07ab153 (patch)
tree71af3f3904a03515748304c0ffcd07f3eb7d0395
parent5d7de771e38b98859c843dafa86c135fd6bdd502 (diff)
downloadchrome-ec-b70454186cb913e5060b6237bf9c8b16f07ab153.tar.gz
chip/stm32: Add extensions for flash programming
Add support to usb_spi.c for processing a new "Flash Command Start" packet, as an alternative to the existing "Command Start" packet. The new serial flash version carries additional flags, among others, to request a one-byte "Write enable" transaction to take place before the main transaction, and to request repeated polling of the "busy bit" after the main transaction. These allow the number of USB round trips to be reduced when using Servo Micro or C2D2 for flash programming. Also, flags of the new request allow the caller to ask for dual or quad lane communication, which can be supported by board-specific SPI drivers. This is intended for use on the HyperDebug debugger board. BUG=b:273601311 TEST=c2d2 flashing gets slight performance boost Change-Id: Id4d1ec9f37387b4e083a397ca15300455f7a0ada Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/4346952 Tested-by: Jes Klinke <jbk@chromium.org> Commit-Queue: Jes Klinke <jbk@chromium.org> Reviewed-by: Edward O'Callaghan <quasisec@chromium.org>
-rw-r--r--chip/stm32/usb_spi.c227
-rw-r--r--chip/stm32/usb_spi.h177
-rw-r--r--util/config_allowed.txt1
3 files changed, 369 insertions, 36 deletions
diff --git a/chip/stm32/usb_spi.c b/chip/stm32/usb_spi.c
index c45ea3520d..0ddde1b0d8 100644
--- a/chip/stm32/usb_spi.c
+++ b/chip/stm32/usb_spi.c
@@ -8,11 +8,18 @@
#include "link_defs.h"
#include "registers.h"
#include "spi.h"
+#include "timer.h"
#include "usb_descriptor.h"
#include "usb_hw.h"
#include "usb_spi.h"
#include "util.h"
+/* How long to wait for a flash page programming. */
+#define FLASH_BUSY_POLL_TIMEOUT_USEC (1000 * MSEC)
+
+const uint8_t JEDEC_READ_STATUS = 0x05;
+const uint8_t JEDEC_STATUS_BUSY = 0x01;
+
/* Forward declare platform specific functions. */
static bool usb_spi_received_packet(void);
static bool usb_spi_transmitted_packet(void);
@@ -164,6 +171,9 @@ static void setup_transfer_response(uint16_t status_code)
*/
static void create_spi_config_response(struct usb_spi_packet_ctx *packet)
{
+ const struct spi_device_t *current_device =
+ &spi_devices[usb_spi_state.current_spi_device_idx];
+
/* Construct the response packet. */
packet->rsp_config.packet_id = USB_SPI_PKT_ID_RSP_USB_SPI_CONFIG;
packet->rsp_config.max_write_count = USB_SPI_MAX_WRITE_COUNT;
@@ -174,6 +184,23 @@ static void create_spi_config_response(struct usb_spi_packet_ctx *packet)
packet->rsp_config.feature_bitmap |=
USB_SPI_FEATURE_FULL_DUPLEX_SUPPORTED;
#endif
+#ifdef CONFIG_USB_SPI_FLASH_EXTENSIONS
+ packet->rsp_config.feature_bitmap |= USB_SPI_FEATURE_FLASH_EXTENSIONS;
+ if (current_device->usb_flags & USB_SPI_FLASH_DUAL_SUPPORT)
+ packet->rsp_config.feature_bitmap |=
+ USB_SPI_FEATURE_DUAL_MODE_SUPPORTED;
+ if (current_device->usb_flags & USB_SPI_FLASH_QUAD_SUPPORT)
+ packet->rsp_config.feature_bitmap |=
+ USB_SPI_FEATURE_QUAD_MODE_SUPPORTED;
+ if (current_device->usb_flags & USB_SPI_FLASH_OCTO_SUPPORT)
+ packet->rsp_config.feature_bitmap |=
+ USB_SPI_FEATURE_OCTO_MODE_SUPPORTED;
+ if (current_device->usb_flags & USB_SPI_FLASH_DTR_SUPPORT)
+ packet->rsp_config.feature_bitmap |=
+ USB_SPI_FEATURE_DTR_SUPPORTED;
+#else
+ (void)current_device; /* Avoid warning about unused variable. */
+#endif
packet->packet_size = sizeof(struct usb_spi_response_configuration_v2);
}
@@ -231,6 +258,45 @@ usb_spi_create_spi_transfer_response(struct usb_spi_packet_ctx *transmit_packet)
}
}
+#ifdef CONFIG_USB_SPI_FLASH_EXTENSIONS
+/*
+ * Decodes the header fields of a Flash Command Start Packet, and sets up the
+ * transaction depending on if it is read or write.
+ */
+static void setup_flash_transfer(struct usb_spi_packet_ctx *packet)
+{
+ const uint32_t flags = packet->cmd_flash_start.flags;
+ usb_spi_state.flash_flags = flags;
+ const uint8_t opcode_count = (flags & FLASH_FLAG_OPCODE_LEN_MSK) >>
+ FLASH_FLAG_OPCODE_LEN_POS;
+ const uint8_t addr_count = (flags & FLASH_FLAG_ADDR_LEN_MSK) >>
+ FLASH_FLAG_ADDR_LEN_POS;
+ const bool write_enable = !!(flags & FLASH_FLAG_WRITE_ENABLE);
+ if ((packet->cmd_flash_start.flags & FLASH_FLAG_READ_WRITE_MSK) ==
+ FLASH_FLAG_READ_WRITE_WRITE) {
+ size_t write_count = packet->cmd_flash_start.count;
+ if (write_count > USB_SPI_MAX_WRITE_COUNT) {
+ usb_spi_state.status_code = USB_SPI_WRITE_COUNT_INVALID;
+ return;
+ }
+ usb_spi_setup_transfer(write_enable + opcode_count +
+ addr_count + write_count,
+ 0);
+ } else {
+ size_t read_count = packet->cmd_flash_start.count;
+ if (read_count > USB_SPI_MAX_READ_COUNT) {
+ usb_spi_state.status_code = USB_SPI_WRITE_COUNT_INVALID;
+ return;
+ }
+ usb_spi_setup_transfer(write_enable + opcode_count + addr_count,
+ read_count);
+ }
+ packet->header_size = offsetof(struct usb_spi_flash_command, data);
+ usb_spi_state.status_code =
+ usb_spi_read_usb_packet(&usb_spi_state.spi_write_ctx, packet);
+}
+#endif
+
/*
* Process the rx packet.
*
@@ -266,6 +332,7 @@ static void usb_spi_process_rx_packet(struct usb_spi_packet_ctx *packet)
/* The host started a new USB SPI transfer */
size_t write_count = packet->cmd_start.write_count;
size_t read_count = packet->cmd_start.read_count;
+ usb_spi_state.flash_flags = 0;
if (!usb_spi_state.enabled) {
setup_transfer_response(USB_SPI_DISABLED);
@@ -302,6 +369,30 @@ static void usb_spi_process_rx_packet(struct usb_spi_packet_ctx *packet)
break;
}
+#ifdef CONFIG_USB_SPI_FLASH_EXTENSIONS
+ case USB_SPI_PKT_ID_CMD_FLASH_TRANSFER_START: {
+ /* The host started a new USB serial flash SPI transfer */
+ if (!usb_spi_state.enabled) {
+ setup_transfer_response(USB_SPI_DISABLED);
+ } else {
+ setup_flash_transfer(packet);
+ }
+
+ /* Send responses if we encountered an error. */
+ if (usb_spi_state.status_code != USB_SPI_SUCCESS) {
+ setup_transfer_response(usb_spi_state.status_code);
+ break;
+ }
+
+ /* Start the SPI transfer when we've read all data. */
+ if (usb_spi_state.spi_write_ctx.transfer_index ==
+ usb_spi_state.spi_write_ctx.transfer_size) {
+ usb_spi_state.mode = USB_SPI_MODE_START_SPI;
+ }
+
+ break;
+ }
+#endif
case USB_SPI_PKT_ID_CMD_TRANSFER_CONTINUE: {
/*
* The host has sent a continue packet for the SPI transfer
@@ -355,6 +446,107 @@ static void usb_spi_process_rx_packet(struct usb_spi_packet_ctx *packet)
}
}
+/*
+ * Perform a SPI write-then-read transaction, optionally preceded by a single
+ * byte "write enable" (separated by deasserting chip select), and optionally
+ * followed by polling the "busy bit" until clear.
+ */
+static uint16_t do_spi_transfer(void)
+{
+ const struct spi_device_t *current_device =
+ &spi_devices[usb_spi_state.current_spi_device_idx];
+ bool custom_board_driver = current_device->usb_flags &
+ USB_SPI_CUSTOM_SPI_DEVICE;
+ /*
+ * If CONFIG_USB_SPI_FLASH_EXTENSIONS is not enabled, then the below
+ * value being zero will allow the compiler to optimize away several
+ * large if-blocks in this function.
+ */
+ const uint32_t flash_flags =
+ IS_ENABLED(CONFIG_USB_SPI_FLASH_EXTENSIONS) ?
+ usb_spi_state.flash_flags :
+ 0;
+ uint16_t status_code = EC_SUCCESS;
+ int read_count = usb_spi_state.spi_read_ctx.transfer_size;
+ const char *write_data_ptr = usb_spi_state.spi_write_ctx.buffer;
+ int write_count = usb_spi_state.spi_write_ctx.transfer_size;
+#ifndef CONFIG_SPI_HALFDUPLEX
+ /*
+ * Handle the full duplex mode on supported platforms.
+ * The read count is equal to the write count.
+ */
+ if (read_count == USB_SPI_FULL_DUPLEX_ENABLED) {
+ usb_spi_state.spi_read_ctx.transfer_size =
+ usb_spi_state.spi_write_ctx.transfer_size;
+ read_count = SPI_READBACK_ALL;
+ }
+#endif
+
+ if (!custom_board_driver &&
+ (flash_flags & FLASH_FLAGS_REQUIRING_SUPPORT)) {
+ /*
+ * The standard spi_transaction() does not support
+ * any multi-lane modes.
+ */
+ return USB_SPI_UNSUPPORTED_FLASH_MODE;
+ }
+
+ if (status_code == EC_SUCCESS &&
+ flash_flags & FLASH_FLAG_WRITE_ENABLE) {
+ /* Precede main transaction with one-byte "write enable". */
+ if (custom_board_driver) {
+ status_code = usb_spi_board_transaction(
+ current_device, 0, write_data_ptr, 1, NULL, 0);
+ } else {
+ status_code = spi_transaction(
+ current_device, write_data_ptr, 1, NULL, 0);
+ }
+ write_data_ptr += 1;
+ write_count -= 1;
+ }
+
+ if (status_code == EC_SUCCESS) {
+ if (custom_board_driver) {
+ status_code = usb_spi_board_transaction(
+ current_device, flash_flags, write_data_ptr,
+ write_count, usb_spi_state.spi_read_ctx.buffer,
+ read_count);
+ } else {
+ status_code = spi_transaction(
+ current_device, write_data_ptr, write_count,
+ usb_spi_state.spi_read_ctx.buffer, read_count);
+ }
+ }
+
+ if (flash_flags & FLASH_FLAG_POLL) {
+ /* After main transaction, poll until no longer "busy". */
+ static timestamp_t deadline;
+ deadline.val = get_time().val + FLASH_BUSY_POLL_TIMEOUT_USEC;
+
+ while (status_code == EC_SUCCESS) {
+ timestamp_t now;
+ uint8_t status_byte;
+ if (custom_board_driver) {
+ status_code = usb_spi_board_transaction(
+ current_device, 0, &JEDEC_READ_STATUS,
+ 1, &status_byte, 1);
+ } else {
+ status_code = spi_transaction(
+ current_device, &JEDEC_READ_STATUS, 1,
+ &status_byte, 1);
+ }
+ if ((status_byte & JEDEC_STATUS_BUSY) == 0)
+ break;
+ now = get_time();
+ if (timestamp_expired(deadline, &now)) {
+ status_code = EC_ERROR_TIMEOUT;
+ break;
+ }
+ }
+ }
+ return usb_spi_map_error(status_code);
+}
+
/* Deferred function to handle state changes, process USB SPI packets,
* and construct responses.
*
@@ -412,40 +604,7 @@ void usb_spi_deferred(void)
/* Start a new SPI transfer. */
if (usb_spi_state.mode == USB_SPI_MODE_START_SPI) {
- const struct spi_device_t *current_device =
- &spi_devices[usb_spi_state.current_spi_device_idx];
- bool custom_board_driver = current_device->usb_flags &
- USB_SPI_CUSTOM_SPI_DEVICE;
- uint16_t status_code;
- int read_count = usb_spi_state.spi_read_ctx.transfer_size;
-#ifndef CONFIG_SPI_HALFDUPLEX
- /*
- * Handle the full duplex mode on supported platforms.
- * The read count is equal to the write count.
- */
- if (read_count == USB_SPI_FULL_DUPLEX_ENABLED) {
- usb_spi_state.spi_read_ctx.transfer_size =
- usb_spi_state.spi_write_ctx.transfer_size;
- read_count = SPI_READBACK_ALL;
- }
-#endif
-
- if (custom_board_driver) {
- status_code = usb_spi_board_transaction(
- current_device, 0 /* flash_flags */,
- usb_spi_state.spi_write_ctx.buffer,
- usb_spi_state.spi_write_ctx.transfer_size,
- usb_spi_state.spi_read_ctx.buffer, read_count);
- } else {
- status_code = spi_transaction(
- current_device,
- usb_spi_state.spi_write_ctx.buffer,
- usb_spi_state.spi_write_ctx.transfer_size,
- usb_spi_state.spi_read_ctx.buffer, read_count);
- }
-
- /* Cast the EC status code to USB SPI and start the response. */
- status_code = usb_spi_map_error(status_code);
+ uint16_t status_code = do_spi_transfer();
setup_transfer_response(status_code);
}
diff --git a/chip/stm32/usb_spi.h b/chip/stm32/usb_spi.h
index 90c15f5f65..e0dfd34b18 100644
--- a/chip/stm32/usb_spi.h
+++ b/chip/stm32/usb_spi.h
@@ -146,6 +146,7 @@
* 0x0008: An unexpected packet arrived that the device could not
* process.
* 0x0009: The device does not support full duplex mode.
+ * 0x000A: Requested serial flash mode not supported
* 0x8000: Unknown error mask
* The bottom 15 bits will contain the bottom 15 bits from the EC
* error code.
@@ -218,7 +219,12 @@
*
* feature bitmap: Bitmap of supported features.
* BIT(0): Full duplex SPI mode is supported
- * BIT(1:15): Reserved for future use
+ * BIT(1): Serial flash extensions are supported
+ * BIT(2): Dual mode flash supported
+ * BIT(3): Quad mode flash supported
+ * BIT(4): Octo mode flash supported
+ * BIT(5): Double transfer rate supported
+ * BIT(6:15): Reserved for future use
*
* Command Restart Response Packet (Host to Device):
*
@@ -256,6 +262,64 @@
* 0x0000: Success
* others: Error
*
+ * Flash Command Start Packet (Host to Device):
+ *
+ * Start of the USB serial flash SPI command, contains the number of
+ * bytes to write or read on SPI and up to the first 58 bytes of write
+ * payload. Longer writes will use the continue packets with packet id
+ * USB_SPI_PKT_ID_CMD_TRANSFER_CONTINUE to transmit the remaining data.
+ *
+ * The reading or writing of the "main" data will be preceded by an
+ * short sequence of opcode, optional address, optional "alternate data",
+ * and optional 'dummy cycles" on the SPI bus. Flags indicate how many
+ * bytes of each stage to send, and whether to use advanced features such
+ * as dual or quad signal lanes for each stage of the transfer".
+ *
+ * The indicated number of opcode, address and alternate bytes will be
+ * the first in the "write payload". The "count" field will contain the
+ * number of data bytes to be written/read after the opcode, address and
+ * alternate bytes.
+ *
+ * This request is only supported if bit 1 of the "feature bitmap"
+ * indicates that serial flash extensions are supported. Implementations
+ * will further advertise whether they support dual, quad or octo modes, if
+ * none of these are supported, then support for "dummy cycles" is not
+ * guaranteed either, and callers should use one or two bytes of "extra
+ * address data" for dummy cycles, address length can be up to 7 bytes for
+ * this reason.
+ *
+ * +----------------+------------+------------+---------------+
+ * | packet id : 2B | count : 2B | flags : 4B | w.p. : <= 56B |
+ * +----------------+------------+------------+---------------+
+ *
+ * packet id: 2 byte enum defined by packet_id_type
+ * Valid values packet id =
+ * USB_SPI_PKT_ID_CMD_FLASH_TRANSFER_START
+ *
+ * count: 2 byte, zero based count of bytes to read or write
+ *
+ * flags: 4 byte, flags
+ * bits 0:1 opcode length in bytes (0-3)
+ * bits 2:4 address length in bytes (0-7)
+ * bits 5:6 mode (0: 1-1-1, 1: 1-1-N, 2: 1-N-N, 3: N-N-N)
+ * bits 7:8 width (0: N=1, 1: N=2, 2: N=4, 3: N=8)
+ * bit 9 double transfer rate (in phases marked as N)
+ * bits 10:14 number of dummy cycles (0-31)
+ * bits 15:27 reserved, must be zero
+ * bit 28 write to be preceded by "write enable"
+ * bit 29 write to be followed by polling of JEDEC "busy bit"
+ * bit 30 reserved, must be zero
+ * bit 31 read (0) / write (1)
+ *
+ * write payload: Up to 56 bytes of data to write to SPI, the total length
+ * of all TX packets must match: write enable length (zero or
+ * one, depending on bit 27) + opcode length + address length
+ * + count, (the last one only if bit 31 indicates a write
+ * operation). Due to data alignment constraints, this must
+ * be an even number of bytes unless this is the final
+ * packet.
+ *
+ *
* USB Error Codes:
*
* send_command return codes have the following format:
@@ -278,6 +342,8 @@
#define USB_SPI_PAYLOAD_SIZE_V2_ERROR (60)
+#define USB_SPI_PAYLOAD_SIZE_FLASH_START (56)
+
#define USB_SPI_MIN_PACKET_SIZE (2)
/*
@@ -294,6 +360,8 @@
#define USB_SPI_FLASH_QUAD_SUPPORT BIT(3)
/* This SPI device supports eight lane mode. */
#define USB_SPI_FLASH_OCTO_SUPPORT BIT(4)
+/* This SPI device supports double transfer rate (data on both clock edges). */
+#define USB_SPI_FLASH_DTR_SUPPORT BIT(5)
enum packet_id_type {
/* Request USB SPI configuration data from device. */
@@ -325,11 +393,34 @@ enum packet_id_type {
USB_SPI_PKT_ID_CMD_CHIP_SELECT = 7,
/* Response to above request. */
USB_SPI_PKT_ID_RSP_CHIP_SELECT = 8,
+ /*
+ * Start a USB serial flash SPI transfer.
+ */
+ USB_SPI_PKT_ID_CMD_FLASH_TRANSFER_START = 9,
};
enum feature_bitmap {
/* Indicates the platform supports full duplex mode. */
- USB_SPI_FEATURE_FULL_DUPLEX_SUPPORTED = BIT(0)
+ USB_SPI_FEATURE_FULL_DUPLEX_SUPPORTED = BIT(0),
+ /* Indicates support for USB_SPI_PKT_ID_CMD_FLASH_TRANSFER_START. */
+ USB_SPI_FEATURE_FLASH_EXTENSIONS = BIT(1),
+ /*
+ * Indicates that chip and any MUXes support bidirectional data on the
+ * two SPI data lines.
+ */
+ USB_SPI_FEATURE_DUAL_MODE_SUPPORTED = BIT(2),
+ /*
+ * Indicates that chip and any MUXes support bidirectional data on the
+ * "hold" and "write protect" lines.
+ */
+ USB_SPI_FEATURE_QUAD_MODE_SUPPORTED = BIT(3),
+ /* Indicates support for eight-line bidirectional data. */
+ USB_SPI_FEATURE_OCTO_MODE_SUPPORTED = BIT(4),
+ /*
+ * Indicates support for double transfer rate, i.e. data bit shift on
+ * both rising and falling clock edges.
+ */
+ USB_SPI_FEATURE_DTR_SUPPORTED = BIT(5),
};
struct usb_spi_response_configuration_v2 {
@@ -374,11 +465,36 @@ struct usb_spi_chip_select_response {
uint16_t status_code;
} __packed;
+struct usb_spi_flash_command {
+ uint16_t packet_id;
+ uint16_t count;
+ uint32_t flags;
+ uint8_t data[USB_SPI_PAYLOAD_SIZE_FLASH_START];
+} __packed;
+
+/*
+ * Mask of the flags that are handled by logic in sub_spi.c, and not passed to
+ * SPI drivers through usb_spi_board_transaction().
+ */
+#define FLASH_FLAGS_NONBOARD 0xF0000000UL
+
+#define FLASH_FLAG_WRITE_ENABLE_POS 28U
+#define FLASH_FLAG_WRITE_ENABLE (0x1UL << FLASH_FLAG_WRITE_ENABLE_POS)
+
+#define FLASH_FLAG_POLL_POS 29U
+#define FLASH_FLAG_POLL (0x1UL << FLASH_FLAG_POLL_POS)
+
+#define FLASH_FLAG_READ_WRITE_POS 31U
+#define FLASH_FLAG_READ_WRITE_MSK (0x1UL << FLASH_FLAG_READ_WRITE_POS)
+#define FLASH_FLAG_READ_WRITE_READ 0
+#define FLASH_FLAG_READ_WRITE_WRITE (0x1UL << FLASH_FLAG_READ_WRITE_POS)
+
struct usb_spi_packet_ctx {
union {
uint8_t bytes[USB_MAX_PACKET_SIZE];
uint16_t packet_id;
struct usb_spi_command_v2 cmd_start;
+ struct usb_spi_flash_command cmd_flash_start;
struct usb_spi_continue_v2 cmd_continue;
struct usb_spi_response_configuration_v2 rsp_config;
struct usb_spi_response_v2 rsp_start;
@@ -412,6 +528,8 @@ enum usb_spi_error {
USB_SPI_RX_UNEXPECTED_PACKET = 0x0008,
/* The device does not support full duplex mode. */
USB_SPI_UNSUPPORTED_FULL_DUPLEX = 0x0009,
+ /* The device does not support dual/quad wire mode. */
+ USB_SPI_UNSUPPORTED_FLASH_MODE = 0x000A,
USB_SPI_UNKNOWN_ERROR = 0x8000,
};
@@ -508,6 +626,12 @@ struct usb_spi_state {
struct usb_spi_packet_ctx transmit_packet;
/*
+ * Flags describing if and how multi-lane (dual/quad), double transfer
+ * rate, and other advanced flash protocol features are to be used.
+ */
+ uint32_t flash_flags;
+
+ /*
* Context structures representing the progress receiving the SPI
* write data and transmitting the SPI read data.
*/
@@ -661,4 +785,53 @@ int usb_spi_board_transaction(const struct spi_device_t *spi_device,
uint32_t flash_flags, const uint8_t *txdata,
int txlen, uint8_t *rxdata, int rxlen);
+/*
+ * Flags to use in usb_spi_board_transaction_async() for advanced serial flash
+ * communication, when supported.
+ */
+
+/* Number of bytes of opcode (0-3). */
+#define FLASH_FLAG_OPCODE_LEN_POS 0
+#define FLASH_FLAG_OPCODE_LEN_MSK (0x3U << FLASH_FLAG_OPCODE_LEN_POS)
+
+/* Number of bytes of address plus additional data bytes (0-7). */
+#define FLASH_FLAG_ADDR_LEN_POS 2
+#define FLASH_FLAG_ADDR_LEN_MSK (0x7U << FLASH_FLAG_ADDR_LEN_POS)
+
+/* At what stage to switch to multi-lane mode (if any). */
+#define FLASH_FLAG_MODE_POS 5
+#define FLASH_FLAG_MODE_MSK (0x3U << FLASH_FLAG_MODE_POS)
+#define FLASH_FLAG_MODE_111 (0x0U << FLASH_FLAG_MODE_POS)
+#define FLASH_FLAG_MODE_11N (0x1U << FLASH_FLAG_MODE_POS)
+#define FLASH_FLAG_MODE_1NN (0x2U << FLASH_FLAG_MODE_POS)
+#define FLASH_FLAG_MODE_NNN (0x3U << FLASH_FLAG_MODE_POS)
+
+/* Data width during the later stages (value of N, above). */
+#define FLASH_FLAG_WIDTH_POS 7
+#define FLASH_FLAG_WIDTH_MSK (0x3U << FLASH_FLAG_WIDTH_POS)
+#define FLASH_FLAG_WIDTH_1WIRE (0x0U << FLASH_FLAG_WIDTH_POS)
+#define FLASH_FLAG_WIDTH_2WIRE (0x1U << FLASH_FLAG_WIDTH_POS)
+#define FLASH_FLAG_WIDTH_4WIRE (0x2U << FLASH_FLAG_WIDTH_POS)
+#define FLASH_FLAG_WIDTH_8WIRE (0x3U << FLASH_FLAG_WIDTH_POS)
+
+/* Transmit opcode bits at both clock edges in later stages. */
+#define FLASH_FLAG_DTR_POS 9
+#define FLASH_FLAG_DTR (0x1U << FLASH_FLAG_DTR_POS)
+
+/* Number of dummy clock cycles (0-31). */
+#define FLASH_FLAG_DUMMY_CYCLES_POS 10
+#define FLASH_FLAG_DUMMY_CYCLES_MSK (0x1FU << FLASH_FLAG_DUMMY_CYCLES_POS)
+
+/*
+ * Mask of the flags that cannot be ignored. This is basically any flags
+ * which call for wires to switch direction, or data being clocked on both
+ * rising and falling edges. As long as none of these are present, then the
+ * remaining flags specifying the length of opcode/address can be ignored, as
+ * the entire data buffer can be transmitted as a sequence of bytes, without
+ * the controller knowing which parts are to be interpreted as
+ * opcode/address/data.
+ */
+#define FLASH_FLAGS_REQUIRING_SUPPORT \
+ (FLASH_FLAG_MODE_MSK | FLASH_FLAG_DTR | FLASH_FLAG_DUMMY_CYCLES_MSK)
+
#endif /* __CROS_EC_USB_SPI_H */
diff --git a/util/config_allowed.txt b/util/config_allowed.txt
index 96f7050257..9ca270e42a 100644
--- a/util/config_allowed.txt
+++ b/util/config_allowed.txt
@@ -1005,6 +1005,7 @@ CONFIG_USB_REMOTE_WAKEUP
CONFIG_USB_SERIALNO
CONFIG_USB_SPI
CONFIG_USB_SPI_BUFFER_SIZE
+CONFIG_USB_SPI_FLASH_EXTENSIONS
CONFIG_USB_SPI_IGNORE_HOST_SIDE_ENABLE
CONFIG_USB_SUSPEND
CONFIG_USB_UPDATE