summaryrefslogtreecommitdiff
path: root/chip/stm32/usb_spi.h
diff options
context:
space:
mode:
Diffstat (limited to 'chip/stm32/usb_spi.h')
-rw-r--r--chip/stm32/usb_spi.h368
1 files changed, 321 insertions, 47 deletions
diff --git a/chip/stm32/usb_spi.h b/chip/stm32/usb_spi.h
index f4c34bbd85..591975234d 100644
--- a/chip/stm32/usb_spi.h
+++ b/chip/stm32/usb_spi.h
@@ -13,22 +13,120 @@
#include "usb_hw.h"
/*
- * Command:
- * +------------------+-----------------+------------------------+
- * | write count : 1B | read count : 1B | write payload : <= 62B |
- * +------------------+-----------------+------------------------+
+ * This SPI flash programming interface is designed to talk to a Chromium OS
+ * device over a Raiden USB connection.
*
- * write count: 1 byte, zero based count of bytes to write
+ * USB SPI Version 2:
*
- * read count: 1 byte, zero based count of bytes to read
+ * USB SPI version 2 adds support for larger SPI transfers and reduces the
+ * number of USB packets transferred. This improves performance when
+ * writing or reading large chunks of memory from a device. A packet ID
+ * field is used to distinguish the different 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.
*
- * write payload: up to 62 bytes of data to write, length must match
- * write count
+ * The USB SPI hosts which support packet version 2 are backwards compatible
+ * and use the bInterfaceProtocol field to identify which type of target
+ * they are connected to.
*
- * Response:
- * +-------------+-----------------------+
- * | status : 2B | read payload : <= 62B |
- * +-------------+-----------------------+
+ *
+ * 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:
+ *
+ * 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.
+ *
+ * +----------------+------------------+-----------------+---------------+
+ * | 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
@@ -47,33 +145,184 @@
* 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 62 bytes of data read from SPI, length will match
- * requested read count
+ * 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 it's 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: USB SPI Host error codes
+ * 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 PAYLOAD_SIZE_V1 (62)
+#define USB_SPI_FULL_DUPLEX_ENABLED (UINT16_MAX)
+
+#define USB_SPI_PAYLOAD_SIZE_V2_START (58)
+
+#define USB_SPI_PAYLOAD_SIZE_V2_RESPONSE (60)
+
+#define USB_SPI_PAYLOAD_SIZE_V2_CONTINUE (60)
+
+#define USB_SPI_PAYLOAD_SIZE_V2_ERROR (60)
-struct usb_spi_command_v1_t {
- int8_t write_count;
- /* -1 Indicates readback all on halfduplex compliant devices. */
- int8_t read_count;
- uint8_t data[PAYLOAD_SIZE_V1];
+#define USB_SPI_MIN_PACKET_SIZE (2)
+
+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,
+};
+
+enum feature_bitmap {
+ /* Indicates the platform supports full duplex mode. */
+ USB_SPI_FEATURE_FULL_DUPLEX_SUPPORTED = BIT(0)
+};
+
+struct usb_spi_response_configuration_v2 {
+ uint16_t packet_id;
+ uint16_t max_write_count;
+ uint16_t max_read_count;
+ uint16_t feature_bitmap;
+} __packed;
+
+struct usb_spi_command_v2 {
+ uint16_t packet_id;
+ uint16_t write_count;
+ /* UINT16_MAX Indicates readback all on halfduplex compliant devices. */
+ uint16_t read_count;
+ uint8_t data[USB_SPI_PAYLOAD_SIZE_V2_START];
} __packed;
-struct usb_spi_response_v1_t {
+struct usb_spi_response_v2 {
+ uint16_t packet_id;
uint16_t status_code;
- uint8_t data[PAYLOAD_SIZE_V1];
+ uint8_t data[USB_SPI_PAYLOAD_SIZE_V2_RESPONSE];
} __packed;
-struct usb_spi_packet_ctx_t {
+struct usb_spi_continue_v2 {
+ uint16_t packet_id;
+ uint16_t data_index;
+ uint8_t data[USB_SPI_PAYLOAD_SIZE_V2_CONTINUE];
+} __packed;
+
+struct usb_spi_packet_ctx {
union {
uint8_t bytes[USB_MAX_PACKET_SIZE];
- struct usb_spi_command_v1_t command;
- struct usb_spi_response_v1_t response;
+ uint16_t packet_id;
+ struct usb_spi_command_v2 cmd_start;
+ struct usb_spi_continue_v2 cmd_continue;
+ struct usb_spi_response_configuration_v2 rsp_config;
+ struct usb_spi_response_v2 rsp_start;
+ struct usb_spi_continue_v2 rsp_continue;
} __packed;
/*
* By storing the number of bytes in the header and knowing that the
@@ -82,24 +331,26 @@ struct usb_spi_packet_ctx_t {
* duplicating variables that can go out of sync.
*/
size_t header_size;
- /* Number of bytes in the packet.*/
+ /* Number of bytes in the packet. */
size_t packet_size;
};
enum usb_spi_error {
- USB_SPI_SUCCESS = 0x0000,
- USB_SPI_TIMEOUT = 0x0001,
- USB_SPI_BUSY = 0x0002,
- USB_SPI_WRITE_COUNT_INVALID = 0x0003,
- USB_SPI_READ_COUNT_INVALID = 0x0004,
- USB_SPI_DISABLED = 0x0005,
+ USB_SPI_SUCCESS = 0x0000,
+ USB_SPI_TIMEOUT = 0x0001,
+ USB_SPI_BUSY = 0x0002,
+ USB_SPI_WRITE_COUNT_INVALID = 0x0003,
+ USB_SPI_READ_COUNT_INVALID = 0x0004,
+ USB_SPI_DISABLED = 0x0005,
/* The RX continue packet's data index is invalid. */
- USB_SPI_RX_BAD_DATA_INDEX = 0x0006,
+ USB_SPI_RX_BAD_DATA_INDEX = 0x0006,
/* The RX endpoint has received more data than write count. */
- USB_SPI_RX_DATA_OVERFLOW = 0x0007,
+ USB_SPI_RX_DATA_OVERFLOW = 0x0007,
/* An unexpected packet arrived on the device. */
- USB_SPI_RX_UNEXPECTED_PACKET = 0x0008,
- USB_SPI_UNKNOWN_ERROR = 0x8000,
+ USB_SPI_RX_UNEXPECTED_PACKET = 0x0008,
+ /* The device does not support full duplex mode. */
+ USB_SPI_UNSUPPORTED_FULL_DUPLEX = 0x0009,
+ USB_SPI_UNKNOWN_ERROR = 0x8000,
};
enum usb_spi_request {
@@ -107,13 +358,20 @@ enum usb_spi_request {
USB_SPI_REQ_DISABLE = 0x0001,
};
-#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));
+/*
+ * To optimize for speed, we want to fill whole packets for each transfer
+ * This is done by setting the read and write counts to the payload sizes
+ * of the smaller start packet + N * continue packets.
+ *
+ * If a platform has a small maximum SPI transfer size, it can be optimized
+ * by setting these limits to the maximum transfer size.
+ */
+#define USB_SPI_BUFFER_SIZE (USB_SPI_PAYLOAD_SIZE_V2_START + \
+ (4 * USB_SPI_PAYLOAD_SIZE_V2_CONTINUE))
+#define USB_SPI_MAX_WRITE_COUNT USB_SPI_BUFFER_SIZE
+#define USB_SPI_MAX_READ_COUNT USB_SPI_BUFFER_SIZE
-struct usb_spi_transfer_ctx_t {
+struct usb_spi_transfer_ctx {
/* Address of transfer buffer. */
uint8_t *buffer;
/* Number of bytes in the transfer. */
@@ -122,6 +380,19 @@ struct usb_spi_transfer_ctx_t {
size_t transfer_index;
};
+enum usb_spi_mode {
+ /* No tasks are required. */
+ USB_SPI_MODE_IDLE = 0,
+ /* Indicates the device needs to send it's USB SPI configuration.*/
+ USB_SPI_MODE_SEND_CONFIGURATION,
+ /* Indicates we device needs start the SPI transfer. */
+ USB_SPI_MODE_START_SPI,
+ /* Indicates we should start a transfer response. */
+ USB_SPI_MODE_START_RESPONSE,
+ /* Indicates we need to continue a transfer response. */
+ USB_SPI_MODE_CONTINUE_RESPONSE,
+};
+
struct usb_spi_state {
/*
* The SPI bridge must be enabled both locally and by the host to allow
@@ -145,6 +416,9 @@ struct usb_spi_state {
*/
int enabled;
+ /* Mark the current operating mode. */
+ enum usb_spi_mode mode;
+
/*
* Stores the status code response for the transfer, delivered in the
* header for the first response packet. Error code is cleared during
@@ -153,15 +427,15 @@ struct usb_spi_state {
uint16_t status_code;
/* Stores the content from the USB packets */
- struct usb_spi_packet_ctx_t receive_packet;
- struct usb_spi_packet_ctx_t transmit_packet;
+ struct usb_spi_packet_ctx receive_packet;
+ struct usb_spi_packet_ctx transmit_packet;
/*
* Context structures representing the progress receiving the SPI
* write data and transmitting the SPI read data.
*/
- struct usb_spi_transfer_ctx_t spi_write_ctx;
- struct usb_spi_transfer_ctx_t spi_read_ctx;
+ struct usb_spi_transfer_ctx spi_write_ctx;
+ struct usb_spi_transfer_ctx spi_read_ctx;
};
/*
@@ -215,7 +489,7 @@ struct usb_spi_config {
INTERFACE, \
ENDPOINT, \
FLAGS) \
- static uint16_t CONCAT2(NAME, _buffer_)[USB_MAX_PACKET_SIZE / 2]; \
+ static uint16_t CONCAT2(NAME, _buffer_)[(USB_SPI_BUFFER_SIZE + 1) / 2];\
static usb_uint CONCAT2(NAME, _ep_rx_buffer_)[USB_MAX_PACKET_SIZE / 2] __usb_ram; \
static usb_uint CONCAT2(NAME, _ep_tx_buffer_)[USB_MAX_PACKET_SIZE / 2] __usb_ram; \
static void CONCAT2(NAME, _deferred_)(void); \