summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 */