diff options
author | Anton Staaf <robotboy@chromium.org> | 2014-09-17 15:22:31 -0700 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2014-10-22 19:44:03 +0000 |
commit | a12efa99c40a60b72eb4289deb48359b933625fa (patch) | |
tree | 6aa3fd2fb8a78d7a5fb00657c4f206c27c33a54b /chip | |
parent | 42c31912c754370c5af41a8bdfac65f9bfd849f6 (diff) | |
download | chrome-ec-a12efa99c40a60b72eb4289deb48359b933625fa.tar.gz |
stm32-USB: USB SPI tunnel driver
Simple control of SPI for flashing over USB.
This driver is working, and using the discovery board
with a W25Q16 flash chip attached flashrom can read,
erase, write, and verify the whole chip in 45 seconds.
Signed-off-by: Anton Staaf <robotboy@chromium.org>
BRANCH=None
BUG=None
TEST=make buildall -j
Change-Id: I224f1f87cd6adc8b64c17de1df98dae0a9cfa6a5
Reviewed-on: https://chromium-review.googlesource.com/218740
Reviewed-by: Anton Staaf <robotboy@chromium.org>
Tested-by: Anton Staaf <robotboy@chromium.org>
Reviewed-by: Randall Spangler <rspangler@chromium.org>
Commit-Queue: Anton Staaf <robotboy@chromium.org>
Diffstat (limited to 'chip')
-rw-r--r-- | chip/stm32/build.mk | 1 | ||||
-rw-r--r-- | chip/stm32/usb_spi.c | 124 | ||||
-rw-r--r-- | chip/stm32/usb_spi.h | 172 |
3 files changed, 297 insertions, 0 deletions
diff --git a/chip/stm32/build.mk b/chip/stm32/build.mk index 152b1e9b7a..57b711f094 100644 --- a/chip/stm32/build.mk +++ b/chip/stm32/build.mk @@ -51,3 +51,4 @@ chip-$(CONFIG_USB_GPIO)+=usb_gpio.o chip-$(CONFIG_USB_HID)+=usb_hid.o chip-$(CONFIG_USB_MS)+=usb_ms.o usb_ms_scsi.o chip-$(CONFIG_USB_POWER_DELIVERY)+=usb_pd_phy.o +chip-$(CONFIG_USB_SPI)+=usb_spi.o diff --git a/chip/stm32/usb_spi.c b/chip/stm32/usb_spi.c new file mode 100644 index 0000000000..6140f4629b --- /dev/null +++ b/chip/stm32/usb_spi.c @@ -0,0 +1,124 @@ +/* Copyright (c) 2014 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 "common.h" +#include "link_defs.h" +#include "registers.h" +#include "spi.h" +#include "usb.h" +#include "usb_spi.h" + +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 uint8_t usb_spi_read_packet(struct usb_spi_config const *config) +{ + size_t i; + uint8_t count = btable_ep[config->endpoint].rx_count & 0x3ff; + + /* + * The USB peripheral doesn't support DMA access to its packet + * RAM so we have to copy messages out into a bounce buffer. + */ + for (i = 0; i < (count + 1) / 2; ++i) + config->buffer[i] = config->rx_ram[i]; + + /* + * RX packet consumed, mark the packet as VALID. The master + * could queue up the next command while we process this SPI + * transaction and prepare the response. + */ + STM32_TOGGLE_EP(config->endpoint, EP_RX_MASK, EP_RX_VALID, 0); + + return count; +} + +static void usb_spi_write_packet(struct usb_spi_config const *config, + uint8_t count) +{ + size_t i; + + /* + * Copy read bytes and status back out of bounce buffer and + * update TX packet state (mark as VALID for master to read). + */ + for (i = 0; i < (count + 1) / 2; ++i) + config->tx_ram[i] = config->buffer[i]; + + btable_ep[config->endpoint].tx_count = count; + + STM32_TOGGLE_EP(config->endpoint, EP_TX_MASK, EP_TX_VALID, 0); +} + +static int rx_valid(struct usb_spi_config const *config) +{ + return (STM32_USB_EP(config->endpoint) & EP_RX_MASK) == EP_RX_VALID; +} + +int usb_spi_service_request(struct usb_spi_config const *config) +{ + uint8_t count; + uint8_t write_count; + uint8_t read_count; + + if (rx_valid(config)) + return 0; + + count = usb_spi_read_packet(config); + write_count = (config->buffer[0] >> 0) & 0xff; + read_count = (config->buffer[0] >> 8) & 0xff; + + if (write_count > USB_SPI_MAX_WRITE_COUNT || + write_count != (count - 2)) { + config->buffer[0] = usb_spi_write_count_invalid; + } else if (read_count > USB_SPI_MAX_READ_COUNT) { + config->buffer[0] = usb_spi_read_count_invalid; + } else { + config->buffer[0] = usb_spi_map_error( + spi_transaction((uint8_t *)(config->buffer + 1), + write_count, + (uint8_t *)(config->buffer + 1), + read_count)); + } + + usb_spi_write_packet(config, read_count + 2); + + return 1; +} + +void usb_spi_tx(struct usb_spi_config const *config) +{ + STM32_TOGGLE_EP(config->endpoint, EP_TX_MASK, EP_TX_NAK, 0); +} + +void usb_spi_rx(struct usb_spi_config const *config) +{ + STM32_TOGGLE_EP(config->endpoint, EP_RX_MASK, EP_RX_NAK, 0); + config->ready(config); +} + +void usb_spi_reset(struct usb_spi_config const *config) +{ + int endpoint = config->endpoint; + + btable_ep[endpoint].tx_addr = usb_sram_addr(config->tx_ram); + btable_ep[endpoint].tx_count = 0; + + btable_ep[endpoint].rx_addr = usb_sram_addr(config->rx_ram); + btable_ep[endpoint].rx_count = + 0x8000 | ((USB_MAX_PACKET_SIZE / 32 - 1) << 10); + + STM32_USB_EP(endpoint) = ((endpoint << 0) | /* Endpoint Addr*/ + (2 << 4) | /* TX NAK */ + (0 << 9) | /* Bulk EP */ + (3 << 12)); /* RX Valid */ +} diff --git a/chip/stm32/usb_spi.h b/chip/stm32/usb_spi.h new file mode 100644 index 0000000000..a90898578d --- /dev/null +++ b/chip/stm32/usb_spi.h @@ -0,0 +1,172 @@ +/* Copyright (c) 2014 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. + */ +#ifndef CHIP_STM32_USB_SPI_H +#define CHIP_STM32_USB_SPI_H + +/* STM32 USB SPI driver for Chrome EC */ + +#include "compile_time_macros.h" +#include "usb.h" + +/* + * Command: + * +------------------+-----------------+------------------------+ + * | write count : 1B | read count : 1B | write payload : <= 62B | + * +------------------+-----------------+------------------------+ + * + * write count: 1 byte, zero based count of bytes to write + * + * read count: 1 byte, zero based count of bytes to read + * + * write payload: up to 62 bytes of data to write, length must match + * write count + * + * Response: + * +-------------+-----------------------+ + * | status : 2B | read payload : <= 62B | + * +-------------+-----------------------+ + * + * status: 2 byte status + * 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 (> 62 bytes, or mismatch with payload) + * 0x0004: Read count invalid (> 62 bytes) + * 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 + */ + +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_unknown_error = 0x8000, +}; + +#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)); + +/* + * Compile time Per-USB gpio configuration stored in flash. Instances of this + * structure are provided by the user of the USB gpio. This structure binds + * together all information required to operate a USB gpio. + */ +struct usb_spi_config { + /* + * Endpoint index, and pointers to the USB packet RAM buffers. + */ + int endpoint; + + /* + * Pointers to USB packet RAM and bounce buffer. + */ + uint16_t *buffer; + usb_uint *rx_ram; + usb_uint *tx_ram; + + /* + * Callback to notify managing task that a new SPI request has been + * received. This should wake up a task that will eventually call + * the usb_spi_service_request function. + */ + void (*ready)(struct usb_spi_config const *config); +}; + +/* + * Convenience macro for defining a USB SPI bridge driver. + * + * NAME is used to construct the names of the trampoline functions and the + * usb_spi_config struct, the latter is just called NAME. + * + * INTERFACE is the index of the USB interface to associate with this + * SPI driver. + * + * ENDPOINT is the index of the USB bulk endpoint used for receiving and + * transmitting bytes. + * + * READY callback function for command reception notification. + */ +#define USB_SPI_CONFIG(NAME, \ + INTERFACE, \ + ENDPOINT, \ + READY) \ + static uint16_t CONCAT2(NAME, _buffer)[USB_MAX_PACKET_SIZE / 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; \ + struct usb_spi_config const NAME = { \ + .endpoint = ENDPOINT, \ + .buffer = CONCAT2(NAME, _buffer), \ + .rx_ram = CONCAT2(NAME, _ep_rx_buffer), \ + .tx_ram = CONCAT2(NAME, _ep_tx_buffer), \ + .ready = READY, \ + }; \ + const struct usb_interface_descriptor \ + USB_IFACE_DESC(INTERFACE) = { \ + .bLength = USB_DT_INTERFACE_SIZE, \ + .bDescriptorType = USB_DT_INTERFACE, \ + .bInterfaceNumber = INTERFACE, \ + .bAlternateSetting = 0, \ + .bNumEndpoints = 2, \ + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, \ + .bInterfaceSubClass = 0, \ + .bInterfaceProtocol = 0, \ + .iInterface = 0, \ + }; \ + const struct usb_endpoint_descriptor \ + USB_EP_DESC(INTERFACE, 0) = { \ + .bLength = USB_DT_ENDPOINT_SIZE, \ + .bDescriptorType = USB_DT_ENDPOINT, \ + .bEndpointAddress = 0x80 | ENDPOINT, \ + .bmAttributes = 0x02 /* Bulk IN */, \ + .wMaxPacketSize = USB_MAX_PACKET_SIZE, \ + .bInterval = 10, \ + }; \ + const struct usb_endpoint_descriptor \ + USB_EP_DESC(INTERFACE, 1) = { \ + .bLength = USB_DT_ENDPOINT_SIZE, \ + .bDescriptorType = USB_DT_ENDPOINT, \ + .bEndpointAddress = ENDPOINT, \ + .bmAttributes = 0x02 /* Bulk OUT */, \ + .wMaxPacketSize = USB_MAX_PACKET_SIZE, \ + .bInterval = 0, \ + }; \ + static void CONCAT2(NAME, _ep_tx) (void) { usb_spi_tx (&NAME); } \ + static void CONCAT2(NAME, _ep_rx) (void) { usb_spi_rx (&NAME); } \ + static void CONCAT2(NAME, _ep_reset)(void) { usb_spi_reset(&NAME); } \ + USB_DECLARE_EP(ENDPOINT, \ + CONCAT2(NAME, _ep_tx), \ + CONCAT2(NAME, _ep_rx), \ + CONCAT2(NAME, _ep_reset)); + +/* + * Check for a new request and process it synchronously, the SPI transaction + * will complete before this function returns. + * + * Returns: + * 1: request serviced + * 0: no request waiting + */ +int usb_spi_service_request(struct usb_spi_config const *config); + +/* + * These functions are used by the trampoline functions defined above to + * connect USB endpoint events with the generic USB GPIO driver. + */ +void usb_spi_tx(struct usb_spi_config const *config); +void usb_spi_rx(struct usb_spi_config const *config); +void usb_spi_reset(struct usb_spi_config const *config); + +#endif /* CHIP_STM32_USB_SPI_H */ |