summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--board/cr50/board.h2
-rw-r--r--board/cr50/build.mk2
-rw-r--r--chip/g/build.mk1
-rw-r--r--chip/g/usb_spi.c6
-rw-r--r--chip/g/usb_spi.h36
-rw-r--r--chip/g/usb_spi_v2.c693
-rw-r--r--include/config.h4
-rw-r--r--include/usb_descriptor.h6
8 files changed, 740 insertions, 10 deletions
diff --git a/board/cr50/board.h b/board/cr50/board.h
index 679646c861..9519306be4 100644
--- a/board/cr50/board.h
+++ b/board/cr50/board.h
@@ -103,7 +103,7 @@
#define CONFIG_USB_CONSOLE_TX_BUF_SIZE 4096
#define CONFIG_USB_I2C
#define CONFIG_USB_INHIBIT_INIT
-#define CONFIG_USB_SPI
+#define CONFIG_USB_SPI_V2
#define CONFIG_USB_SERIALNO
#define DEFAULT_SERIALNO "0"
diff --git a/board/cr50/build.mk b/board/cr50/build.mk
index 07f74f8686..5ff5894cb8 100644
--- a/board/cr50/build.mk
+++ b/board/cr50/build.mk
@@ -49,7 +49,7 @@ board-y += servo_state.o
board-y += ap_uart_state.o
board-y += factory_mode.o
board-${CONFIG_RDD} += rdd.o
-board-${CONFIG_USB_SPI} += usb_spi.o
+board-${CONFIG_USB_SPI_V2} += usb_spi.o
board-${CONFIG_USB_I2C} += usb_i2c.o
board-y += recovery_button.o
diff --git a/chip/g/build.mk b/chip/g/build.mk
index 2fb82e4f4d..80bc997b59 100644
--- a/chip/g/build.mk
+++ b/chip/g/build.mk
@@ -79,6 +79,7 @@ chip-$(CONFIG_USB)+=usb.o usb_endpoints.o
chip-$(CONFIG_USB_CONSOLE)+=usb_console.o
chip-$(CONFIG_USB_BLOB)+=blob.o
chip-$(CONFIG_USB_SPI)+=usb_spi.o
+chip-$(CONFIG_USB_SPI_V2)+=usb_spi_v2.o
chip-$(CONFIG_RDD)+=rdd.o
chip-$(CONFIG_RBOX)+=rbox.o
chip-$(CONFIG_STREAM_USB)+=usb-stream.o
diff --git a/chip/g/usb_spi.c b/chip/g/usb_spi.c
index 5d436a1b83..df30202f2c 100644
--- a/chip/g/usb_spi.c
+++ b/chip/g/usb_spi.c
@@ -19,6 +19,12 @@
#include "signing.h"
#endif
+#define USB_SPI_MAX_WRITE_COUNT 62
+#define USB_SPI_MAX_READ_COUNT 62
+
+BUILD_ASSERT(USB_MAX_PACKET_SIZE == (1 + 1 + USB_SPI_MAX_WRITE_COUNT));
+BUILD_ASSERT(USB_MAX_PACKET_SIZE == (2 + USB_SPI_MAX_READ_COUNT));
+
#define CPUTS(outstr) cputs(CC_USB, outstr)
#define CPRINTS(format, args...) cprints(CC_USB, format, ## args)
diff --git a/chip/g/usb_spi.h b/chip/g/usb_spi.h
index 65be060616..2432dc2cd9 100644
--- a/chip/g/usb_spi.h
+++ b/chip/g/usb_spi.h
@@ -59,6 +59,11 @@ enum usb_spi_error {
USB_SPI_WRITE_COUNT_INVALID = 0x0003,
USB_SPI_READ_COUNT_INVALID = 0x0004,
USB_SPI_DISABLED = 0x0005,
+ USB_SPI_RX_BAD_DATA_INDEX = 0x0006,
+ USB_SPI_RX_DATA_OVERFLOW = 0x0007,
+ USB_SPI_RX_UNEXPECTED_PACKET = 0x0008,
+ USB_SPI_UNSUPPORTED_FULL_DUPLEX = 0x0009,
+ USB_SPI_RUNT_PACKET = 0x000a,
USB_SPI_UNKNOWN_ERROR = 0x8000,
};
@@ -86,11 +91,22 @@ enum usb_spi {
};
-#define USB_SPI_MAX_WRITE_COUNT 62
-#define USB_SPI_MAX_READ_COUNT 62
-
-BUILD_ASSERT(USB_MAX_PACKET_SIZE == (1 + 1 + USB_SPI_MAX_WRITE_COUNT));
-BUILD_ASSERT(USB_MAX_PACKET_SIZE == (2 + USB_SPI_MAX_READ_COUNT));
+#ifdef CONFIG_USB_SPI_V2
+/*
+ * Raiden client can be in one of two states, IDLE, or WRITING.
+ *
+ * When in IDLE state the client accepts commands from the host and can either
+ * perform the required action (report the configuration, or write a full
+ * write transaction (fitting into one USB packet), or write the received
+ * chunk and then repeatedly read the SPI flash and send the contents back in
+ * USB packets until the full required read transaction is completed.
+ *
+ * In case the received chunk is less than the total write transaction size
+ * the client moves into RAIDEN_WRITING state and expects all following
+ * received USB packets to be continuation of the write transaction.
+ */
+enum raiden_state { RAIDEN_IDLE, RAIDEN_WRITING };
+#endif
struct usb_spi_state {
/*
@@ -114,6 +130,13 @@ struct usb_spi_state {
* callback.
*/
int enabled;
+
+#ifdef CONFIG_USB_SPI_V2
+ /* Variable helping to keep track of multi packet write PDUs. */
+ uint16_t total_write_count;
+ uint16_t wrote_so_far;
+ enum raiden_state raiden_state;
+#endif
};
/*
@@ -167,7 +190,8 @@ extern struct consumer_ops const usb_spi_consumer_ops;
INTERFACE, \
ENDPOINT) \
\
- static uint8_t CONCAT2(NAME, _buffer_)[USB_MAX_PACKET_SIZE]; \
+ static uint8_t CONCAT2(NAME, _buffer_)[USB_MAX_PACKET_SIZE] \
+ __aligned(2); \
static void CONCAT2(NAME, _deferred_)(void); \
DECLARE_DEFERRED(CONCAT2(NAME, _deferred_)); \
static struct queue const CONCAT2(NAME, _to_usb_); \
diff --git a/chip/g/usb_spi_v2.c b/chip/g/usb_spi_v2.c
new file mode 100644
index 0000000000..89a492c0c1
--- /dev/null
+++ b/chip/g/usb_spi_v2.c
@@ -0,0 +1,693 @@
+/* Copyright 2021 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "ccd_config.h"
+#include "common.h"
+#include "link_defs.h"
+#include "gpio.h"
+#include "registers.h"
+#include "spi.h"
+#include "spi_flash.h"
+#include "timer.h"
+#include "usb_descriptor.h"
+#include "usb_spi.h"
+#include "util.h"
+
+/*
+ * This module implements support for SPI programming driven by the flashrom
+ * utility over USB.
+ *
+ * Flashrom connects to the specific SPI USB endpoint and sends commands to
+ * this driver to control the external SPI flash chip attached to the H1's SPI
+ * host port.
+ *
+ * Some configuration actions might have to be performed before SPI
+ * programming is possible (say asserting the AP reset, enabling a
+ * multiplexer, etc.), these actions are not controlled/performed by this
+ * module, it expects the 'H2<=>SPI flash' connection provisioned and the
+ * flash chip ready to be programmed.
+ *
+ * The below protocol specification was copied from raiden_debug_spi.c in the
+ * flashrom tree with addition of some clarifications.
+ *
+ * Note that this implementation imposes additional limits on SPI transactions
+ * which include both read and write portions: the write size of such
+ * transactions case can not exceed 60, the size of the payload of the first
+ * USB packet carrying the transaction PDU.
+ *
+ * USB SPI Version 2:
+ *
+ * USB SPI version 2 adds support for larger SPI transfers which reduces
+ * the number of USB packets transferred. This improves performance when
+ * writing or reading large chunks of memory from a device. Larger
+ * quantities are transferred in PDUs split into USB packets.
+ *
+ * A packet ID field is used to distinguish the different USB packet
+ * types. Additional packets have been included to query the device for
+ * its configuration allowing the interface to be used on platforms with
+ * different SPI limitations. It includes validation and a packet to
+ * recover from the situations where USB packets are lost.
+ *
+ * The device advertises the supported protocol version in the
+ * bInterfaceProtocol field of the USB descriptor, this allows the USB SPI
+ * hosts to be backwards compatible with version 1.
+ *
+ *
+ * Example: USB SPI request with 128 byte write and 0 byte read.
+ *
+ * Packet #1 Host to Device:
+ * packet id = USB_SPI_PKT_ID_CMD_TRANSFER_START
+ * write count = 128
+ * read count = 0
+ * payload = First 58 bytes from the write buffer,
+ * starting at byte 0 in the buffer
+ * packet size = 64 bytes
+ *
+ * Packet #2 Host to Device:
+ * packet id = USB_SPI_PKT_ID_CMD_TRANSFER_CONTINUE
+ * data index = 58
+ * payload = Next 60 bytes from the write buffer,
+ * starting at byte 58 in the buffer
+ * packet size = 64 bytes
+ *
+ * Packet #3 Host to Device:
+ * packet id = USB_SPI_PKT_ID_CMD_TRANSFER_CONTINUE
+ * data index = 118
+ * payload = Next 10 bytes from the write buffer,
+ * starting at byte 118 in the buffer
+ * packet size = 14 bytes
+ *
+ * Packet #4 Device to Host:
+ * packet id = USB_SPI_PKT_ID_RSP_TRANSFER_START
+ * status code = status code from device
+ * payload = 0 bytes
+ * packet size = 4 bytes
+ *
+ * Example: USB SPI request with 2 byte write and 100 byte read.
+ *
+ * Packet #1 Host to Device:
+ * packet id = USB_SPI_PKT_ID_CMD_TRANSFER_START
+ * write count = 2
+ * read count = 100
+ * payload = The 2 byte write buffer
+ * packet size = 8 bytes
+ *
+ * Packet #2 Device to Host:
+ * packet id = USB_SPI_PKT_ID_RSP_TRANSFER_START
+ * status code = status code from device
+ * payload = First 60 bytes from the read buffer,
+ * starting at byte 0 in the buffer
+ * packet size = 64 bytes
+ *
+ * Packet #3 Device to Host:
+ * packet id = USB_SPI_PKT_ID_RSP_TRANSFER_CONTINUE
+ * data index = 60
+ * payload = Next 40 bytes from the read buffer,
+ * starting at byte 60 in the buffer
+ * packet size = 44 bytes
+ *
+ *
+ * Message Packets:
+ *
+ * Note that all 16 bit fields are transferred in little endian mode.
+ *
+ * Command Start Packet (Host to Device):
+ *
+ * Start of the USB SPI command, contains the number of bytes to write
+ * and 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 write part of the transaction is executed first; in case the
+ * packet includes both read and write requests, the write portion is
+ * required to fit into the first USB packet.
+ *
+ * +----------------+------------------+-----------------+---------------+
+ * | packet id : 2B | write count : 2B | read count : 2B | w.p. : <= 58B |
+ * +----------------+------------------+-----------------+---------------+
+ *
+ * packet id: 2 byte enum defined by packet_id_type
+ * Valid values packet id = USB_SPI_PKT_ID_CMD_TRANSFER_START
+ *
+ * write count: 2 byte, zero based count of bytes to write
+ *
+ * read count: 2 byte, zero based count of bytes to read
+ * UINT16_MAX indicates full duplex mode with a read count
+ * equal to the write count.
+ *
+ * write payload: Up to 58 bytes of data to write to SPI, the total
+ * length of all TX packets must match write count.
+ * Due to data alignment constraints, this must be an
+ * even number of bytes unless this is the final packet.
+ *
+ *
+ * Response Start Packet (Device to Host):
+ *
+ * Start of the USB SPI response, contains the status code and up to
+ * the first 60 bytes of read payload. Longer reads will use the
+ * continue packets with packet id USB_SPI_PKT_ID_RSP_TRANSFER_CONTINUE
+ * to transmit the remaining data.
+ *
+ * +----------------+------------------+-----------------------+
+ * | packet id : 2B | status code : 2B | read payload : <= 60B |
+ * +----------------+------------------+-----------------------+
+ *
+ * packet id: 2 byte enum defined by packet_id_type
+ * Valid values packet id = USB_SPI_PKT_ID_RSP_TRANSFER_START
+ *
+ * status code: 2 byte status code
+ * 0x0000: Success
+ * 0x0001: SPI timeout
+ * 0x0002: Busy, try again
+ * This can happen if someone else has acquired the shared memory
+ * buffer that the SPI driver uses as /dev/null
+ * 0x0003: Write count invalid. The byte limit is platform specific
+ * and is set during the configure USB SPI response.
+ * 0x0004: Read count invalid. The byte limit is platform specific
+ * and is set during the configure USB SPI response.
+ * 0x0005: The SPI bridge is disabled.
+ * 0x0006: The RX continue packet's data index is invalid. This
+ * can indicate a USB transfer failure to the device.
+ * 0x0007: The RX endpoint has received more data than write count.
+ * This can indicate a USB transfer failure to the device.
+ * 0x0008: An unexpected packet arrived that the device could not
+ * process.
+ * 0x0009: The device does not support full duplex mode.
+ * 0x8000: Unknown error mask
+ * The bottom 15 bits will contain the bottom 15 bits from the EC
+ * error code.
+ *
+ * read payload: Up to 60 bytes of data read from SPI, the total
+ * length of all RX packets must match read count
+ * unless an error status was returned. Due to data
+ * alignment constraints, this must be a even number
+ * of bytes unless this is the final packet.
+ *
+ *
+ * Continue Packet (Bidirectional):
+ *
+ * Continuation packet for the writes and read buffers. Both packets
+ * follow the same format, a data index counts the number of bytes
+ * previously transferred in the USB SPI transfer and a payload of bytes.
+ *
+ * +----------------+-----------------+-------------------------------+
+ * | packet id : 2B | data index : 2B | write / read payload : <= 60B |
+ * +----------------+-----------------+-------------------------------+
+ *
+ * packet id: 2 byte enum defined by packet_id_type
+ * The packet id has 2 values depending on direction:
+ * packet id = USB_SPI_PKT_ID_CMD_TRANSFER_CONTINUE
+ * indicates the packet is being transmitted from the host
+ * to the device and contains SPI write payload.
+ * packet id = USB_SPI_PKT_ID_RSP_TRANSFER_CONTINUE
+ * indicates the packet is being transmitted from the device
+ * to the host and contains SPI read payload.
+ *
+ * data index: The data index indicates the number of bytes in the
+ * read or write buffers that have already been transmitted.
+ * It is used to validate that no packets have been dropped
+ * and that the prior packets have been correctly decoded.
+ * This value corresponds to the offset bytes in the buffer
+ * to start copying the payload into.
+ *
+ * read and write payload:
+ * Contains up to 60 bytes of payload data to transfer to
+ * the SPI write buffer or from the SPI read buffer.
+ *
+ *
+ * Command Get Configuration Packet (Host to Device):
+ *
+ * Query the device to request its USB SPI configuration indicating
+ * the number of bytes it can write and read.
+ *
+ * +----------------+
+ * | packet id : 2B |
+ * +----------------+
+ *
+ * packet id: 2 byte enum USB_SPI_PKT_ID_CMD_GET_USB_SPI_CONFIG
+ *
+ * Response Configuration Packet (Device to Host):
+ *
+ * Response packet form the device to report the maximum write and
+ * read size supported by the device.
+ *
+ * +----------------+----------------+---------------+----------------+
+ * | packet id : 2B | max write : 2B | max read : 2B | feature bitmap |
+ * +----------------+----------------+---------------+----------------+
+ *
+ * packet id: 2 byte enum USB_SPI_PKT_ID_RSP_USB_SPI_CONFIG
+ *
+ * max write count: 2 byte count of the maximum number of bytes
+ * the device can write to SPI in one transaction.
+ *
+ * max read count: 2 byte count of the maximum number of bytes
+ * the device can read from SPI in one transaction.
+ *
+ * feature bitmap: Bitmap of supported features.
+ * BIT(0): Full duplex SPI mode is supported
+ * BIT(1:15): Reserved for future use
+ *
+ * Command Restart Response Packet (Host to Device):
+ *
+ * Command to restart the response transfer from the device. This enables
+ * the host to recover from a lost packet when reading the response
+ * without restarting the SPI transfer.
+ *
+ * +----------------+
+ * | packet id : 2B |
+ * +----------------+
+ *
+ * packet id: 2 byte enum USB_SPI_PKT_ID_CMD_RESTART_RESPONSE
+ *
+ * USB Error Codes:
+ *
+ * send_command return codes have the following format:
+ *
+ * 0x00000: Status code success.
+ * 0x00001-0x0FFFF: Error code returned by the USB SPI device.
+ * 0x10001-0x1FFFF: Error code returned by the USB SPI host.
+ * 0x20001-0x20063 Lower bits store the positive value representation
+ * of the libusb_error enum. See the libusb documentation:
+ * http://libusb.sourceforge.net/api-1.0/group__misc.html
+ *
+ */
+
+
+#define CPUTS(outstr) cputs(CC_USB, outstr)
+#define CPRINTS(format, args...) cprints(CC_USB, format, ## args)
+
+static int16_t usb_spi_map_error(int error)
+{
+ switch (error) {
+ case EC_SUCCESS:
+ return USB_SPI_SUCCESS;
+ case EC_ERROR_TIMEOUT:
+ return USB_SPI_TIMEOUT;
+ case EC_ERROR_BUSY:
+ return USB_SPI_BUSY;
+ default:
+ return USB_SPI_UNKNOWN_ERROR | (error & 0x7fff);
+ }
+}
+
+static uint16_t usb_spi_read_packet(struct usb_spi_config const *config)
+{
+ return QUEUE_REMOVE_UNITS(config->consumer.queue, config->buffer,
+ queue_count(config->consumer.queue));
+}
+
+static void usb_spi_write_packet(struct usb_spi_config const *config,
+ uint8_t count)
+{
+ /*
+ * Experiments show that while reading a 16M flash time spent waiting
+ * is less than 30 ms, which is negligible in this case, as the AP is
+ * held in reset.
+ */
+ while (queue_space(config->tx_queue) < count)
+ msleep(1);
+
+ QUEUE_ADD_UNITS(config->tx_queue, config->buffer, count);
+}
+
+enum packet_id_type {
+ /* Request USB SPI configuration data from device. */
+ USB_SPI_PKT_ID_CMD_GET_USB_SPI_CONFIG = 0,
+ /* USB SPI configuration data from device. */
+ USB_SPI_PKT_ID_RSP_USB_SPI_CONFIG = 1,
+ /*
+ * Start a USB SPI transfer specifying number of bytes to write,
+ * read and deliver first packet of data to write.
+ */
+ USB_SPI_PKT_ID_CMD_TRANSFER_START = 2,
+ /* Additional packets containing write payload. */
+ USB_SPI_PKT_ID_CMD_TRANSFER_CONTINUE = 3,
+ /*
+ * Request the device restart the response enabling us to recover
+ * from packet loss without another SPI transfer.
+ */
+ USB_SPI_PKT_ID_CMD_RESTART_RESPONSE = 4,
+ /*
+ * First packet of USB SPI response with the status code
+ * and read payload if it was successful.
+ */
+ USB_SPI_PKT_ID_RSP_TRANSFER_START = 5,
+ /* Additional packets containing read payload. */
+ USB_SPI_PKT_ID_RSP_TRANSFER_CONTINUE = 6,
+ /* Inform the device that transfer is over. */
+ USB_SPI_PKT_ID_CMD_HOST_DONE = 7,
+ /* Inform the host that processing is finished. */
+ USB_SPI_PKT_ID_RSP_DEVICE_DONE = 8,
+};
+
+enum feature_bitmap {
+ /* Indicates the platform supports full duplex mode. */
+ USB_SPI_FEATURE_FULL_DUPLEX_SUPPORTED = BIT(0)
+};
+
+/* Helper structures defining various PDU headers. */
+struct raiden_response_hdr {
+ uint16_t packet_id;
+ uint16_t value; /* Either offset into the PDU or error code. */
+};
+
+struct raiden_cmd_start_hdr {
+ uint16_t packet_id;
+ uint16_t write_count;
+ uint16_t read_count;
+};
+
+struct raiden_cmd_continue_hdr {
+ uint16_t packet_id;
+ uint16_t offset;
+};
+
+union raiden_cmd_hdr {
+ struct raiden_cmd_start_hdr s;
+ struct raiden_cmd_continue_hdr c;
+};
+
+#define START_PAYLOAD_SIZE \
+ (USB_MAX_PACKET_SIZE - sizeof(struct raiden_cmd_start_hdr))
+#define CONT_PAYLOAD_SIZE \
+ (USB_MAX_PACKET_SIZE - sizeof(struct raiden_cmd_continue_hdr))
+#define RESP_PAYLOAD_SIZE \
+ (USB_MAX_PACKET_SIZE - sizeof(struct raiden_response_hdr))
+
+/*
+ * Hardcoded values used to communicate to the host maximum sizes of read and
+ * write transactions.
+ */
+/*
+ * Six bytes header, one byte command, up to four byte addr, 256 bytes data
+ * page and then some in case there is a chip which uses a multibyte command.
+ */
+#define USB_SPI_MAX_WRITE_COUNT 270
+/*
+ * Flashrom deducts 5 from this value, remaining 2040 bytes size results in 34
+ * USB packets of 4 byte header and 60 bytes data.
+ */
+#define USB_SPI_MAX_READ_COUNT (5 + 34 * RESP_PAYLOAD_SIZE)
+
+/* Hadrdcoded response to the config request packet. */
+static const struct {
+ uint16_t rsp_id;
+ uint16_t max_write;
+ uint16_t max_read;
+ uint16_t features;
+} config_rsp = { USB_SPI_PKT_ID_RSP_USB_SPI_CONFIG, USB_SPI_MAX_WRITE_COUNT,
+ USB_SPI_MAX_READ_COUNT, 0 };
+
+/*
+ * Prepare an error message to be sent back to the host and switch to
+ * RAIDEN_IDLE state.
+ *
+ * The return value is the number of bytes to send to the controller.
+ */
+static uint8_t report_error(void *packet, uint16_t error,
+ struct usb_spi_state *state)
+{
+ /* Packet is guaranteed to be 2 bytes aligned. */
+ struct raiden_response_hdr *rsp = (struct raiden_response_hdr *)packet;
+
+ rsp->packet_id = USB_SPI_PKT_ID_RSP_TRANSFER_START;
+ rsp->value = error;
+
+ state->raiden_state = RAIDEN_IDLE;
+ return sizeof(*rsp);
+}
+
+/* Return length of the last response packet to send back to the host. */
+static uint8_t process_raiden_packet(const struct usb_spi_config *config,
+ size_t count)
+{
+ /* Buffer is guaranteed to be 2 bytes aligned. */
+ union raiden_cmd_hdr *packet = (union raiden_cmd_hdr *)config->buffer;
+ struct raiden_response_hdr *rsp =
+ (struct raiden_response_hdr *)config->buffer;
+ uint16_t total_read_count;
+ uint16_t read_so_far;
+ uint16_t this_read_count = 0;
+ uint16_t this_write_count = 0;
+ bool last_sub_transaction;
+ uint16_t res;
+ uint16_t packet_id;
+ struct usb_spi_state *state = config->state;
+
+ if (count < sizeof(packet->s.packet_id))
+ return report_error(config->buffer, USB_SPI_UNKNOWN_ERROR,
+ state);
+
+ packet_id = packet->s.packet_id;
+ switch (state->raiden_state) {
+ case RAIDEN_IDLE:
+ if ((packet_id == USB_SPI_PKT_ID_CMD_GET_USB_SPI_CONFIG) &&
+ (count == sizeof(uint16_t))) {
+ memcpy(config->buffer, &config_rsp, sizeof(config_rsp));
+ return sizeof(config_rsp);
+ }
+
+ if ((packet_id != USB_SPI_PKT_ID_CMD_TRANSFER_START) ||
+ (count < sizeof(struct raiden_cmd_start_hdr)))
+ return report_error(config->buffer,
+ USB_SPI_RUNT_PACKET, state);
+
+ /* This is request to start a new transaction. */
+ state->total_write_count = packet->s.write_count;
+ total_read_count = packet->s.read_count;
+
+ /* Some basic validity checks. */
+ if (total_read_count > config_rsp.max_read)
+ return report_error(config->buffer,
+ USB_SPI_READ_COUNT_INVALID, state);
+
+ if (state->total_write_count > config_rsp.max_write)
+ return report_error(config->buffer,
+ USB_SPI_WRITE_COUNT_INVALID, state);
+
+ /*
+ * If write count fits into one USB packet, the size of packet
+ * must match.
+ */
+ if ((state->total_write_count <=
+ (USB_MAX_PACKET_SIZE -
+ sizeof(struct raiden_cmd_start_hdr))) &&
+ (state->total_write_count !=
+ (count - sizeof(struct raiden_cmd_start_hdr))))
+ return report_error(config->buffer,
+ USB_SPI_WRITE_COUNT_INVALID, state);
+
+ /*
+ * Transactions with write count exceeding one USB packet AND
+ * requiring a read are not supported.
+ */
+ if (total_read_count &&
+ (state->total_write_count > START_PAYLOAD_SIZE))
+ return report_error(config->buffer,
+ USB_SPI_WRITE_COUNT_INVALID, state);
+
+ read_so_far = 0;
+ state->wrote_so_far = 0;
+
+ if (state->total_write_count > START_PAYLOAD_SIZE)
+ this_write_count = START_PAYLOAD_SIZE;
+ else
+ this_write_count = state->total_write_count;
+
+ while (this_write_count || (read_so_far != total_read_count)) {
+ this_read_count = total_read_count - read_so_far;
+
+ /*
+ * Need to decide if CS needs to be deasserted in this
+ * iteration. It would be the case if both final read
+ * and write counts fit into USB packet.
+ */
+ if ((this_read_count > RESP_PAYLOAD_SIZE) ||
+ (state->total_write_count > START_PAYLOAD_SIZE)) {
+ last_sub_transaction = false;
+
+ /* Limit read count by USB packet capacity. */
+ if (this_read_count > RESP_PAYLOAD_SIZE)
+ this_read_count = RESP_PAYLOAD_SIZE;
+ } else {
+ last_sub_transaction = true;
+ }
+
+ res = usb_spi_map_error(spi_sub_transaction(
+ SPI_FLASH_DEVICE,
+ config->buffer +
+ sizeof(struct raiden_cmd_start_hdr),
+ this_write_count,
+ config->buffer +
+ sizeof(struct raiden_response_hdr),
+ this_read_count, last_sub_transaction));
+ if (res) {
+ rsp->value = res;
+ rsp->packet_id =
+ USB_SPI_PKT_ID_RSP_TRANSFER_START;
+ return sizeof(*rsp);
+ }
+
+ rsp->packet_id =
+ read_so_far ?
+ USB_SPI_PKT_ID_RSP_TRANSFER_CONTINUE :
+ USB_SPI_PKT_ID_RSP_TRANSFER_START;
+ rsp->value = read_so_far;
+
+ state->wrote_so_far += this_write_count;
+ read_so_far += this_read_count;
+
+ if (state->total_write_count > state->wrote_so_far) {
+ state->raiden_state = RAIDEN_WRITING;
+ return 0;
+ }
+
+ if (last_sub_transaction)
+ break;
+
+ usb_spi_write_packet(config,
+ this_read_count + sizeof(*rsp));
+
+ /*
+ * Make sure this does not keep the loop going, we
+ * wrote all there was to write.
+ */
+ this_write_count = 0;
+ }
+
+ if (this_read_count)
+ return this_read_count + sizeof(*rsp);
+
+ if (state->total_write_count &&
+ (state->total_write_count == this_write_count))
+ return sizeof(*rsp);
+ return 0;
+
+ case RAIDEN_WRITING:
+ if ((packet_id != USB_SPI_PKT_ID_CMD_TRANSFER_CONTINUE) ||
+ (count <= sizeof(struct raiden_cmd_continue_hdr)))
+ return report_error(config->buffer,
+ USB_SPI_RX_UNEXPECTED_PACKET,
+ state);
+
+ if (packet->c.offset != state->wrote_so_far)
+ return report_error(config->buffer,
+ USB_SPI_RX_UNEXPECTED_PACKET,
+ state);
+
+ this_write_count =
+ count - sizeof(struct raiden_cmd_continue_hdr);
+
+ /*
+ * This is the last sub transaction if the remainder of data
+ * to write fits into USB packet, CS will have to be
+ * deasserted.
+ */
+ last_sub_transaction =
+ (this_write_count + state->wrote_so_far) ==
+ state->total_write_count;
+
+ res = usb_spi_map_error(spi_sub_transaction(
+ SPI_FLASH_DEVICE,
+ config->buffer + sizeof(struct raiden_response_hdr),
+ this_write_count, config->buffer, 0,
+ last_sub_transaction));
+ if (res) {
+ rsp->value = res;
+ rsp->packet_id = USB_SPI_PKT_ID_RSP_TRANSFER_START;
+ state->raiden_state = RAIDEN_IDLE;
+ return sizeof(*rsp);
+ }
+
+ if (last_sub_transaction) {
+ state->raiden_state = RAIDEN_IDLE;
+ rsp->value = 0;
+ rsp->packet_id = USB_SPI_PKT_ID_RSP_TRANSFER_START;
+ return sizeof(*rsp);
+ }
+ state->wrote_so_far += this_write_count;
+ return 0;
+ }
+
+ return 0;
+}
+
+void usb_spi_deferred(struct usb_spi_config const *config)
+{
+ uint16_t count;
+ int rv = EC_SUCCESS;
+
+ /*
+ * If our overall enabled state has changed we call the board specific
+ * enable or disable routines and save our new state.
+ */
+ int enabled = !!(config->state->enabled_host &
+ config->state->enabled_device);
+
+ if (enabled ^ config->state->enabled) {
+ if (enabled)
+ rv = usb_spi_board_enable(config->state->enabled_host);
+
+ else
+ usb_spi_board_disable();
+
+ /* Only update our state if we were successful. */
+ if (rv == EC_SUCCESS)
+ config->state->enabled = enabled;
+ }
+
+ /*
+ * And if there is a USB packet waiting we process it and generate a
+ * response.
+ */
+ count = usb_spi_read_packet(config);
+ if (count == 0)
+ return;
+
+ if (!config->state->enabled || usb_spi_shortcut_active()) {
+ struct raiden_response_hdr rsp_hdr = {
+ USB_SPI_PKT_ID_RSP_TRANSFER_START, USB_SPI_DISABLED
+ };
+ memcpy(config->buffer, &rsp_hdr, sizeof(rsp_hdr));
+ usb_spi_write_packet(config, sizeof(rsp_hdr));
+ return;
+ }
+
+ /*
+ * Call the protocol handler and send back the last message generated
+ * by the handler.
+ */
+ usb_spi_write_packet(config, process_raiden_packet(config, count));
+}
+
+static void usb_spi_written(struct consumer const *consumer, size_t count)
+{
+ struct usb_spi_config const *config =
+ DOWNCAST(consumer, struct usb_spi_config, consumer);
+
+ hook_call_deferred(config->deferred, 0);
+}
+
+struct consumer_ops const usb_spi_consumer_ops = {
+ .written = usb_spi_written,
+};
+
+void usb_spi_enable(struct usb_spi_config const *config, int enabled)
+{
+ config->state->enabled_device = 0;
+ if (enabled) {
+#ifdef CONFIG_CASE_CLOSED_DEBUG_V1
+ if (ccd_is_cap_enabled(CCD_CAP_AP_FLASH))
+ config->state->enabled_device |= USB_SPI_AP;
+ if (ccd_is_cap_enabled(CCD_CAP_EC_FLASH))
+ config->state->enabled_device |= USB_SPI_EC;
+#else
+ config->state->enabled_device = USB_SPI_ALL;
+#endif
+ }
+
+ hook_call_deferred(config->deferred, 0);
+}
+
diff --git a/include/config.h b/include/config.h
index 5bab6a1c08..7f334bd5dc 100644
--- a/include/config.h
+++ b/include/config.h
@@ -4066,8 +4066,10 @@
#undef CONFIG_USB_GPIO
/*****************************************************************************/
-/* USB SPI config */
+/* USB SPI legacy protocol (V1). */
#undef CONFIG_USB_SPI
+/* USB_SPI protocol V2. */
+#undef CONFIG_USB_SPI_V2
/*****************************************************************************/
/* USB I2C config */
diff --git a/include/usb_descriptor.h b/include/usb_descriptor.h
index 4b7184a311..153c648085 100644
--- a/include/usb_descriptor.h
+++ b/include/usb_descriptor.h
@@ -191,7 +191,11 @@ struct usb_endpoint_descriptor {
#define USB_PROTOCOL_GOOGLE_SERIAL 0x01
#define USB_SUBCLASS_GOOGLE_SPI 0x51
-#define USB_PROTOCOL_GOOGLE_SPI 0x01
+#ifdef CONFIG_USB_SPI_V2
+#define USB_PROTOCOL_GOOGLE_SPI 0x02
+#else
+#define USB_PROTOCOL_GOOGLE_SPI 0x01
+#endif
#define USB_SUBCLASS_GOOGLE_I2C 0x52
#define USB_PROTOCOL_GOOGLE_I2C 0x01