diff options
Diffstat (limited to 'chip/stm32/usb_spi.h')
-rw-r--r-- | chip/stm32/usb_spi.h | 368 |
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); \ |