diff options
-rw-r--r-- | board/discovery-stm32f072/board.c | 34 | ||||
-rw-r--r-- | board/discovery-stm32f072/spi.c | 17 | ||||
-rw-r--r-- | chip/stm32/usb_spi.c | 69 | ||||
-rw-r--r-- | chip/stm32/usb_spi.h | 71 |
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 */ |