summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Staaf <robotboy@chromium.org>2014-12-02 10:37:54 -0800
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-12-08 21:51:52 +0000
commit731a2e74872159ecfa910c3174e9d3ae50ff2dae (patch)
tree84de2d9b11eee75f206c0dc008d98813e4e32c03
parent0f4550468fac985711ef10629ad9d5129c9e4539 (diff)
downloadchrome-ec-731a2e74872159ecfa910c3174e9d3ae50ff2dae.tar.gz
USB-SPI: Support board enable/disable functionality
This allows the USB SPI bridge to be controlled from the host at a larger timescale than a single SPI transaction. This allows the host to signal that many transactions will take place and that the device should keep the SPI bridge enabled across them. This allows the device to hold the AP or other possible user of the SPI bus in reset while the bridge is enabled. Signed-off-by: Anton Staaf <robotboy@chromium.org> BRANCH=None BUG=None TEST=make buildall -j Change-Id: Ifd6f96b0ff47f35d853735d44e255a205b0e677a Reviewed-on: https://chromium-review.googlesource.com/232732 Tested-by: Anton Staaf <robotboy@chromium.org> Reviewed-by: Vincent Palatin <vpalatin@chromium.org> Commit-Queue: Anton Staaf <robotboy@chromium.org>
-rw-r--r--board/discovery-stm32f072/board.c34
-rw-r--r--board/discovery-stm32f072/spi.c17
-rw-r--r--chip/stm32/usb_spi.c69
-rw-r--r--chip/stm32/usb_spi.h71
4 files changed, 159 insertions, 32 deletions
diff --git a/board/discovery-stm32f072/board.c b/board/discovery-stm32f072/board.c
index 8beae33e74..99e0af0b1d 100644
--- a/board/discovery-stm32f072/board.c
+++ b/board/discovery-stm32f072/board.c
@@ -9,8 +9,10 @@
#include "gpio.h"
#include "hooks.h"
#include "registers.h"
+#include "spi.h"
#include "task.h"
#include "usb_gpio.h"
+#include "usb_spi.h"
#include "util.h"
void button_event(enum gpio_signal signal);
@@ -53,6 +55,38 @@ const void *const usb_strings[] = {
BUILD_ASSERT(ARRAY_SIZE(usb_strings) == USB_STR_COUNT);
+void usb_spi_board_enable(struct usb_spi_config const *config)
+{
+ /* Remap SPI2 to DMA channels 6 and 7 */
+ STM32_SYSCFG_CFGR1 |= (1 << 24);
+
+ /* Configure SPI GPIOs */
+ gpio_config_module(MODULE_SPI_MASTER, 1);
+
+ /* Set all four SPI pins to high speed */
+ STM32_GPIO_OSPEEDR(GPIO_B) |= 0xff000000;
+
+ /* Enable clocks to SPI2 module */
+ STM32_RCC_APB1ENR |= STM32_RCC_PB1_SPI2;
+
+ /* Reset SPI2 */
+ STM32_RCC_APB1RSTR |= STM32_RCC_PB1_SPI2;
+ STM32_RCC_APB1RSTR &= ~STM32_RCC_PB1_SPI2;
+
+ spi_enable(1);
+}
+
+void usb_spi_board_disable(struct usb_spi_config const *config)
+{
+ spi_enable(0);
+
+ /* Disable clocks to SPI2 module */
+ STM32_RCC_APB1ENR &= ~STM32_RCC_PB1_SPI2;
+
+ /* Release SPI GPIOs */
+ gpio_config_module(MODULE_SPI_MASTER, 0);
+}
+
/* Initialize board. */
static void board_init(void)
{
diff --git a/board/discovery-stm32f072/spi.c b/board/discovery-stm32f072/spi.c
index 225592f20a..b768bb42bc 100644
--- a/board/discovery-stm32f072/spi.c
+++ b/board/discovery-stm32f072/spi.c
@@ -18,22 +18,7 @@ USB_SPI_CONFIG(usb_spi, USB_IFACE_SPI, USB_EP_SPI, usb_spi_ready)
void usb_spi_task(void)
{
- /* Remap SPI2 to DMA channels 6 and 7 */
- STM32_SYSCFG_CFGR1 |= (1 << 24);
-
- gpio_config_module(MODULE_SPI_MASTER, 1);
-
- /* Set all four SPI pins to high speed */
- STM32_GPIO_OSPEEDR(GPIO_B) |= 0xff000000;
-
- /* Enable clocks to SPI2 module */
- STM32_RCC_APB1ENR |= STM32_RCC_PB1_SPI2;
-
- /* Reset SPI2 */
- STM32_RCC_APB1RSTR |= STM32_RCC_PB1_SPI2;
- STM32_RCC_APB1RSTR &= ~STM32_RCC_PB1_SPI2;
-
- spi_enable(1);
+ usb_spi_enable(&usb_spi. 1);
while (1) {
task_wait_event(-1);
diff --git a/chip/stm32/usb_spi.c b/chip/stm32/usb_spi.c
index 6140f4629b..f0e163cb1b 100644
--- a/chip/stm32/usb_spi.c
+++ b/chip/stm32/usb_spi.c
@@ -13,10 +13,10 @@
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);
+ 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);
}
}
@@ -77,11 +77,13 @@ int usb_spi_service_request(struct usb_spi_config const *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;
+ if (config->state->disabled || !config->state->enabled) {
+ config->buffer[0] = USB_SPI_DISABLED;
+ } else 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;
+ config->buffer[0] = USB_SPI_READ_COUNT_INVALID;
} else {
config->buffer[0] = usb_spi_map_error(
spi_transaction((uint8_t *)(config->buffer + 1),
@@ -122,3 +124,54 @@ void usb_spi_reset(struct usb_spi_config const *config)
(0 << 9) | /* Bulk EP */
(3 << 12)); /* RX Valid */
}
+
+int usb_spi_interface(struct usb_spi_config const *config,
+ usb_uint *rx_buf,
+ usb_uint *tx_buf)
+{
+ struct usb_setup_packet setup;
+
+ usb_read_setup_packet(rx_buf, &setup);
+
+ if (setup.bmRequestType != (USB_DIR_OUT |
+ USB_TYPE_VENDOR |
+ USB_RECIP_INTERFACE))
+ return 1;
+
+ if (setup.wValue != 0 ||
+ setup.wIndex != config->interface ||
+ setup.wLength != 0)
+ return 1;
+
+ if (config->state->disabled)
+ return 1;
+
+ switch (setup.bRequest) {
+ case USB_SPI_REQ_ENABLE:
+ usb_spi_board_enable(config);
+ config->state->enabled = 1;
+ break;
+
+ case USB_SPI_REQ_DISABLE:
+ config->state->enabled = 0;
+ usb_spi_board_disable(config);
+ break;
+
+ default: return 1;
+ }
+
+ btable_ep[0].tx_count = 0;
+ STM32_TOGGLE_EP(0, EP_TX_RX_MASK, EP_TX_RX_VALID, EP_STATUS_OUT);
+ return 0;
+}
+
+void usb_spi_enable(struct usb_spi_config const *config, int enabled)
+{
+ config->state->disabled = !enabled;
+
+ if (config->state->disabled &&
+ config->state->enabled) {
+ config->state->enabled = 0;
+ usb_spi_board_disable(config);
+ }
+}
diff --git a/chip/stm32/usb_spi.h b/chip/stm32/usb_spi.h
index 1880540043..4917c16f0b 100644
--- a/chip/stm32/usb_spi.h
+++ b/chip/stm32/usb_spi.h
@@ -36,6 +36,7 @@
* 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)
+ * 0x0005: The SPI bridge is disabled.
* 0x8000: Unknown error mask
* The bottom 15 bits will contain the bottom 15 bits from the EC
* error code.
@@ -45,12 +46,18 @@
*/
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,
+ 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_UNKNOWN_ERROR = 0x8000,
+};
+
+enum usb_spi_request {
+ USB_SPI_REQ_ENABLE = 0x0000,
+ USB_SPI_REQ_DISABLE = 0x0001,
};
#define USB_SPI_MAX_WRITE_COUNT 62
@@ -59,6 +66,17 @@ enum usb_spi_error {
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));
+struct usb_spi_state {
+ /*
+ * The SPI bridge must be both not disabled and enabled to allow access
+ * to the SPI device. The disabled bit is dictated by the caller of
+ * usb_spi_enable. The enabled bit is set by the USB host, most likely
+ * flashrom, by sending a USB_SPI_REQ_ENABLE message to the device.
+ */
+ int disabled;
+ int enabled;
+};
+
/*
* 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
@@ -66,8 +84,14 @@ BUILD_ASSERT(USB_MAX_PACKET_SIZE == (2 + USB_SPI_MAX_READ_COUNT));
*/
struct usb_spi_config {
/*
- * Endpoint index, and pointers to the USB packet RAM buffers.
+ * In RAM state of the USB SPI bridge.
+ */
+ struct usb_spi_state *state;
+
+ /*
+ * Interface and endpoint indicies.
*/
+ int interface;
int endpoint;
/*
@@ -106,7 +130,13 @@ struct usb_spi_config {
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_state CONCAT2(NAME, _state_) = { \
+ .disabled = 1, \
+ .enabled = 0, \
+ }; \
struct usb_spi_config const NAME = { \
+ .state = &CONCAT2(NAME, _state_), \
+ .interface = INTERFACE, \
.endpoint = ENDPOINT, \
.buffer = CONCAT2(NAME, _buffer_), \
.rx_ram = CONCAT2(NAME, _ep_rx_buffer_), \
@@ -149,7 +179,12 @@ struct usb_spi_config {
USB_DECLARE_EP(ENDPOINT, \
CONCAT2(NAME, _ep_tx_), \
CONCAT2(NAME, _ep_rx_), \
- CONCAT2(NAME, _ep_reset_));
+ CONCAT2(NAME, _ep_reset_)); \
+ static int CONCAT2(NAME, _interface_)(usb_uint *rx_buf, \
+ usb_uint *tx_buf) \
+ { return usb_spi_interface(&NAME, rx_buf, tx_buf); } \
+ USB_DECLARE_IFACE(INTERFACE, \
+ CONCAT2(NAME, _interface_));
/*
* Check for a new request and process it synchronously, the SPI transaction
@@ -162,11 +197,31 @@ struct usb_spi_config {
int usb_spi_service_request(struct usb_spi_config const *config);
/*
+ * Set the enable state for the USB-SPI bridge.
+ *
+ * The bridge must be enabled from both the host and device side
+ * before the SPI bus is usable. This allows the bridge to be
+ * available for host tools to use without forcing the device to
+ * disconnect or disable whatever else might be using the SPI bus.
+ */
+void usb_spi_enable(struct usb_spi_config const *config, int enabled);
+
+/*
* 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);
+int usb_spi_interface(struct usb_spi_config const *config,
+ usb_uint *rx_buf,
+ usb_uint *tx_buf);
+
+/*
+ * These functions should be implemented by the board to provide any board
+ * specific operations required to enable or disable access to the SPI device.
+ */
+void usb_spi_board_enable(struct usb_spi_config const *config);
+void usb_spi_board_disable(struct usb_spi_config const *config);
#endif /* CHIP_STM32_USB_SPI_H */