diff options
Diffstat (limited to 'chip/stm32/usb_spi.c')
-rw-r--r-- | chip/stm32/usb_spi.c | 627 |
1 files changed, 0 insertions, 627 deletions
diff --git a/chip/stm32/usb_spi.c b/chip/stm32/usb_spi.c deleted file mode 100644 index 54caae015e..0000000000 --- a/chip/stm32/usb_spi.c +++ /dev/null @@ -1,627 +0,0 @@ -/* Copyright 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_descriptor.h" -#include "usb_hw.h" -#include "usb_spi.h" -#include "util.h" - -/* Forward declare platform specific functions. */ -static bool usb_spi_received_packet(struct usb_spi_config const *config); -static bool usb_spi_transmitted_packet(struct usb_spi_config const *config); -static void usb_spi_read_packet(struct usb_spi_config const *config, - struct usb_spi_packet_ctx *packet); -static void usb_spi_write_packet(struct usb_spi_config const *config, - struct usb_spi_packet_ctx *packet); - -/* - * Map EC error codes to USB_SPI error codes. - * - * @param error EC error code - * - * @returns USB SPI error code based on the mapping. - */ -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); - } -} - -/* - * Read data into the receive buffer. - * - * @param dst Destination receive context we are writing data to. - * @param src Source packet context we are reading data from. - * - * @returns USB_SPI_RX_DATA_OVERFLOW if the source packet is too large - */ -static int usb_spi_read_usb_packet(struct usb_spi_transfer_ctx *dst, - const struct usb_spi_packet_ctx *src) -{ - size_t max_read_length = dst->transfer_size - dst->transfer_index; - size_t bytes_in_buffer = src->packet_size - src->header_size; - const uint8_t *packet_buffer = src->bytes + src->header_size; - - if (bytes_in_buffer > max_read_length) { - /* - * An error occurred, we should not receive more data than - * the buffer can support. - */ - return USB_SPI_RX_DATA_OVERFLOW; - } - memcpy(dst->buffer + dst->transfer_index, packet_buffer, - bytes_in_buffer); - - dst->transfer_index += bytes_in_buffer; - return USB_SPI_SUCCESS; -} - -/* - * Fill the USB packet with data from the transmit buffer. - * - * @param dst Destination packet context we are writing data to. - * @param src Source transmit context we are reading data from. - */ -static void usb_spi_fill_usb_packet(struct usb_spi_packet_ctx *dst, - struct usb_spi_transfer_ctx *src) -{ - size_t transfer_size = src->transfer_size - src->transfer_index; - size_t max_buffer_size = USB_MAX_PACKET_SIZE - dst->header_size; - uint8_t *packet_buffer = dst->bytes + dst->header_size; - - if (transfer_size > max_buffer_size) - transfer_size = max_buffer_size; - - memcpy(packet_buffer, src->buffer + src->transfer_index, transfer_size); - - dst->packet_size = dst->header_size + transfer_size; - src->transfer_index += transfer_size; -} - -/* - * Setup the USB SPI state to start a new SPI transfer. - * - * @param config USB SPI config - * @param write_count Number of bytes to write in the SPI transfer - * @param read_count Number of bytes to read in the SPI transfer - */ -static void usb_spi_setup_transfer(struct usb_spi_config const *config, - size_t write_count, size_t read_count) -{ - /* Reset any status code. */ - config->state->status_code = USB_SPI_SUCCESS; - - /* Reset the write and read counts. */ - config->state->spi_write_ctx.transfer_size = write_count; - config->state->spi_write_ctx.transfer_index = 0; - config->state->spi_read_ctx.transfer_size = read_count; - config->state->spi_read_ctx.transfer_index = 0; -} - -/* - * Handle USB events that will reset the USB SPI state. - * - * @param config USB SPI config - */ -static void usb_spi_reset_interface(struct usb_spi_config const *config) -{ - /* Setup a 0 byte transfer to clear the contexts. */ - usb_spi_setup_transfer(config, 0, 0); -} - -/* - * Returns if the response transfer is in progress. - * - * @param config USB SPI config - * - * @returns True if a response transfer is in progress. - */ -static bool usb_spi_response_in_progress(struct usb_spi_config const *config) -{ - if ((config->state->mode == USB_SPI_MODE_START_RESPONSE) || - (config->state->mode == USB_SPI_MODE_CONTINUE_RESPONSE)) { - return true; - } - return false; -} - -/* - * Prep the state to construct a new response. This sets the transfer - * contexts, the mode, and status code. If a non-zero status code is - * returned, then no payload will be transmitted. - * - * @param config USB SPI config - * @param status_code status code to set for the response. - */ -static void setup_transfer_response(struct usb_spi_config const *config, - uint16_t status_code) -{ - config->state->status_code = status_code; - config->state->spi_read_ctx.transfer_index = 0; - config->state->mode = USB_SPI_MODE_START_RESPONSE; - - /* If an error occurred, transmit an empty start packet. */ - if (status_code != USB_SPI_SUCCESS) - config->state->spi_read_ctx.transfer_size = 0; -} - -/* - * Constructs the response packet containing the SPI configuration. - * - * @param config USB SPI config - * @param packet Packet buffer we will be transmitting. - */ -static void create_spi_config_response(struct usb_spi_config const *config, - struct usb_spi_packet_ctx *packet) -{ - /* Construct the response packet. */ - packet->rsp_config.packet_id = USB_SPI_PKT_ID_RSP_USB_SPI_CONFIG; - packet->rsp_config.max_write_count = USB_SPI_MAX_WRITE_COUNT; - packet->rsp_config.max_read_count = USB_SPI_MAX_READ_COUNT; - /* Set the feature flags. */ - packet->rsp_config.feature_bitmap = 0; -#ifndef CONFIG_SPI_HALFDUPLEX - packet->rsp_config.feature_bitmap |= - USB_SPI_FEATURE_FULL_DUPLEX_SUPPORTED; -#endif - packet->packet_size = - sizeof(struct usb_spi_response_configuration_v2); -} - -/* - * If we have a transfer response in progress, this will construct the - * next entry. If no transfer is in progress or if we are unable to - * create the next packet, it will not modify tx_packet. - * - * @param config USB SPI config - * @param packet Packet buffer we will be transmitting. - */ -static void usb_spi_create_spi_transfer_response( - struct usb_spi_config const *config, - struct usb_spi_packet_ctx *transmit_packet) -{ - - if (!usb_spi_response_in_progress(config)) - return; - - if (config->state->spi_read_ctx.transfer_index == 0) { - - /* Transmit the first packet with the status code. */ - transmit_packet->header_size = - offsetof(struct usb_spi_response_v2, data); - transmit_packet->rsp_start.packet_id = - USB_SPI_PKT_ID_RSP_TRANSFER_START; - transmit_packet->rsp_start.status_code = - config->state->status_code; - - usb_spi_fill_usb_packet(transmit_packet, - &config->state->spi_read_ctx); - } else if (config->state->spi_read_ctx.transfer_index < - config->state->spi_read_ctx.transfer_size) { - - /* Transmit the continue packets. */ - transmit_packet->header_size = - offsetof(struct usb_spi_continue_v2, data); - transmit_packet->rsp_continue.packet_id = - USB_SPI_PKT_ID_RSP_TRANSFER_CONTINUE; - transmit_packet->rsp_continue.data_index = - config->state->spi_read_ctx.transfer_index; - - usb_spi_fill_usb_packet(transmit_packet, - &config->state->spi_read_ctx); - } - if (config->state->spi_read_ctx.transfer_index < - config->state->spi_read_ctx.transfer_size) { - config->state->mode = USB_SPI_MODE_CONTINUE_RESPONSE; - } else { - config->state->mode = USB_SPI_MODE_IDLE; - } -} - -/* - * Process the rx packet. - * - * @param config USB SPI config - * @param packet Received packet to process. - */ -static void usb_spi_process_rx_packet(struct usb_spi_config const *config, - struct usb_spi_packet_ctx *packet) -{ - if (packet->packet_size < USB_SPI_MIN_PACKET_SIZE) { - /* No valid packet exists smaller than the packet id. */ - setup_transfer_response(config, USB_SPI_RX_UNEXPECTED_PACKET); - return; - } - /* Reset the mode until we've processed the packet. */ - config->state->mode = USB_SPI_MODE_IDLE; - - switch (packet->packet_id) { - case USB_SPI_PKT_ID_CMD_GET_USB_SPI_CONFIG: - { - /* The host requires the SPI configuration. */ - config->state->mode = USB_SPI_MODE_SEND_CONFIGURATION; - break; - } - case USB_SPI_PKT_ID_CMD_RESTART_RESPONSE: - { - /* - * The host has requested the device restart the last response. - * This is used to recover from lost USB packets without - * duplicating SPI transfers. - */ - setup_transfer_response(config, config->state->status_code); - break; - } - case USB_SPI_PKT_ID_CMD_TRANSFER_START: - { - /* The host started a new USB SPI transfer */ - size_t write_count = packet->cmd_start.write_count; - size_t read_count = packet->cmd_start.read_count; - - if (!config->state->enabled) { - setup_transfer_response(config, USB_SPI_DISABLED); - } else if (write_count > USB_SPI_MAX_WRITE_COUNT) { - setup_transfer_response(config, - USB_SPI_WRITE_COUNT_INVALID); - } else if (read_count == USB_SPI_FULL_DUPLEX_ENABLED) { -#ifndef CONFIG_SPI_HALFDUPLEX - /* Full duplex mode is not supported on this device. */ - setup_transfer_response(config, - USB_SPI_UNSUPPORTED_FULL_DUPLEX); -#endif - } else if (read_count > USB_SPI_MAX_READ_COUNT && - read_count != USB_SPI_FULL_DUPLEX_ENABLED) { - setup_transfer_response(config, - USB_SPI_READ_COUNT_INVALID); - } else { - usb_spi_setup_transfer(config, write_count, read_count); - packet->header_size = - offsetof(struct usb_spi_command_v2, data); - config->state->status_code = usb_spi_read_usb_packet( - &config->state->spi_write_ctx, packet); - } - - /* Send responses if we encountered an error. */ - if (config->state->status_code != USB_SPI_SUCCESS) { - setup_transfer_response(config, - config->state->status_code); - break; - } - - /* Start the SPI transfer when we've read all data. */ - if (config->state->spi_write_ctx.transfer_index == - config->state->spi_write_ctx.transfer_size) { - config->state->mode = USB_SPI_MODE_START_SPI; - } - - break; - } - case USB_SPI_PKT_ID_CMD_TRANSFER_CONTINUE: - { - /* - * The host has sent a continue packet for the SPI transfer - * which contains additional data payload. - */ - packet->header_size = - offsetof(struct usb_spi_continue_v2, data); - if (config->state->status_code == USB_SPI_SUCCESS) { - config->state->status_code = usb_spi_read_usb_packet( - &config->state->spi_write_ctx, packet); - } - - /* Send responses if we encountered an error. */ - if (config->state->status_code != USB_SPI_SUCCESS) { - setup_transfer_response(config, - config->state->status_code); - break; - } - - /* Start the SPI transfer when we've read all data. */ - if (config->state->spi_write_ctx.transfer_index == - config->state->spi_write_ctx.transfer_size) { - config->state->mode = USB_SPI_MODE_START_SPI; - } - - break; - } - default: - { - /* An unknown USB packet was delivered. */ - setup_transfer_response(config, USB_SPI_RX_UNEXPECTED_PACKET); - break; - } - } -} - -/* Deferred function to handle state changes, process USB SPI packets, - * and construct responses. - * - * @param config USB SPI config - */ -void usb_spi_deferred(struct usb_spi_config const *config) -{ - int enabled; - struct usb_spi_packet_ctx *receive_packet = - &config->state->receive_packet; - struct usb_spi_packet_ctx *transmit_packet = - &config->state->transmit_packet; - transmit_packet->packet_size = 0; - - if (config->flags & USB_SPI_CONFIG_FLAGS_IGNORE_HOST_SIDE_ENABLE) - enabled = config->state->enabled_device; - else - enabled = config->state->enabled_device && - config->state->enabled_host; - - /* - * If our overall enabled state has changed we call the board specific - * enable or disable routines and save our new state. - */ - if (enabled != config->state->enabled) { - if (enabled) usb_spi_board_enable(config); - else usb_spi_board_disable(config); - - config->state->enabled = enabled; - } - - /* Read any packets from the endpoint. */ - - usb_spi_read_packet(config, receive_packet); - if (receive_packet->packet_size) { - usb_spi_process_rx_packet(config, receive_packet); - } - - /* Need to send the USB SPI configuration */ - if (config->state->mode == USB_SPI_MODE_SEND_CONFIGURATION) { - create_spi_config_response(config, transmit_packet); - usb_spi_write_packet(config, transmit_packet); - config->state->mode = USB_SPI_MODE_IDLE; - return; - } - - /* Start a new SPI transfer. */ - if (config->state->mode == USB_SPI_MODE_START_SPI) { - uint16_t status_code; - int read_count = config->state->spi_read_ctx.transfer_size; -#ifndef CONFIG_SPI_HALFDUPLEX - /* - * Handle the full duplex mode on supported platforms. - * The read count is equal to the write count. - */ - if (read_count == USB_SPI_FULL_DUPLEX_ENABLED) { - config->state->spi_read_ctx.transfer_size = - config->state->spi_write_ctx.transfer_size; - read_count = SPI_READBACK_ALL; - } -#endif - status_code = spi_transaction(SPI_FLASH_DEVICE, - config->state->spi_write_ctx.buffer, - config->state->spi_write_ctx.transfer_size, - config->state->spi_read_ctx.buffer, - read_count); - - /* Cast the EC status code to USB SPI and start the response. */ - status_code = usb_spi_map_error(status_code); - setup_transfer_response(config, status_code); - } - - if (usb_spi_response_in_progress(config) && - usb_spi_transmitted_packet(config)) { - usb_spi_create_spi_transfer_response(config, transmit_packet); - usb_spi_write_packet(config, transmit_packet); - } -} - -/* - * Sets which SPI modes will be enabled - * - * @param config USB SPI config - * @param enabled usb_spi_request indicating which SPI mode is enabled. - */ -void usb_spi_enable(struct usb_spi_config const *config, int enabled) -{ - config->state->enabled_device = enabled; - - hook_call_deferred(config->deferred, 0); -} - -/* - * STM32 Platform: Receive the data from the endpoint into the packet and - * mark the endpoint as ready to accept more data. - * - * @param config USB SPI config - * @param packet Destination packet used to store the endpoint data. - */ -static void usb_spi_read_packet(struct usb_spi_config const *config, - struct usb_spi_packet_ctx *packet) -{ - size_t packet_size; - - if (!usb_spi_received_packet(config)) { - /* No data is present on the endpoint. */ - packet->packet_size = 0; - return; - } - - /* Copy bytes from endpoint memory. */ - packet_size = btable_ep[config->endpoint].rx_count & RX_COUNT_MASK; - memcpy_from_usbram(packet->bytes, - (void *)usb_sram_addr(config->ep_rx_ram), packet_size); - packet->packet_size = packet_size; - /* Set endpoint as valid for accepting new packet. */ - STM32_TOGGLE_EP(config->endpoint, EP_RX_MASK, EP_RX_VALID, 0); -} - -/* - * STM32 Platform: Transmit data from the packet to the endpoint buffer. - * If a packet is written, the endpoint will be marked valid for transmitting. - * - * @param config USB SPI config - * @param packet Source packet we will write to the endpoint data. - */ -static void usb_spi_write_packet(struct usb_spi_config const *config, - struct usb_spi_packet_ctx *packet) -{ - if (packet->packet_size == 0) - return; - - /* Copy bytes to endpoint memory. */ - memcpy_to_usbram((void *)usb_sram_addr(config->ep_tx_ram), - packet->bytes, packet->packet_size); - btable_ep[config->endpoint].tx_count = packet->packet_size; - - /* Mark the packet as having no data. */ - packet->packet_size = 0; - - /* Set endpoint as valid for transmitting new packet. */ - STM32_TOGGLE_EP(config->endpoint, EP_TX_MASK, EP_TX_VALID, 0); -} - -/* - * STM32 Platform: Returns the RX endpoint status - * - * @param config USB SPI config - * - * @returns Returns true when the RX endpoint has a packet. - */ -static bool usb_spi_received_packet(struct usb_spi_config const *config) -{ - return (STM32_USB_EP(config->endpoint) & EP_RX_MASK) != EP_RX_VALID; -} - -/* STM32 Platform: Returns the TX endpoint status - * - * @param config USB SPI config - * - * @returns Returns true when the TX endpoint transmitted - * the packet written. - */ -static bool usb_spi_transmitted_packet(struct usb_spi_config const *config) -{ - return (STM32_USB_EP(config->endpoint) & EP_TX_MASK) != EP_TX_VALID; -} - -/* STM32 Platform: Handle interrupt for USB data received. - * - * @param config USB SPI config - */ -void usb_spi_rx(struct usb_spi_config const *config) -{ - /* - * We need to set both the TX and RX endpoints to NAK to prevent - * transfers. The protocol requires responses to follow a command, but - * the USB host will request the next packet from the TX endpoint - * before the USB SPI has updated the memory in the buffer. By setting - * it to NAK in the ISR, it will not perform a transfer until the - * next packet is ready. - * - * This has a side effect of disabling the endpoint interrupts until - * they are set to valid or a USB reset events occurs. - */ - STM32_TOGGLE_EP(config->endpoint, EP_TX_RX_MASK, EP_TX_RX_NAK, 0); - - hook_call_deferred(config->deferred, 0); -} - -/* - * STM32 Platform: Handle interrupt for USB data transmitted. - * - * @param config USB SPI config - */ -void usb_spi_tx(struct usb_spi_config const *config) -{ - STM32_TOGGLE_EP(config->endpoint, EP_TX_MASK, EP_TX_NAK, 0); - - hook_call_deferred(config->deferred, 0); -} - -/* - * STM32 Platform: Handle interrupt for USB events - * - * @param config USB SPI config - * @param evt USB event - */ -void usb_spi_event(struct usb_spi_config const *config, enum usb_ep_event evt) -{ - int endpoint; - - if (evt != USB_EVENT_RESET) - return; - - endpoint = config->endpoint; - - usb_spi_reset_interface(config); - - btable_ep[endpoint].tx_addr = usb_sram_addr(config->ep_tx_ram); - btable_ep[endpoint].tx_count = 0; - - btable_ep[endpoint].rx_addr = usb_sram_addr(config->ep_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 */ -} - -/* - * STM32 Platform: Handle control transfers. - * - * @param config USB SPI config - * @param rx_buf Contains setup packet - * @param tx_buf unused - */ -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; - - switch (setup.bRequest) { - case USB_SPI_REQ_ENABLE: - config->state->enabled_host = 1; - break; - - case USB_SPI_REQ_DISABLE: - config->state->enabled_host = 0; - break; - - default: return 1; - } - - /* - * Our state has changed, call the deferred function to handle the - * state change. - */ - if (!(config->flags & USB_SPI_CONFIG_FLAGS_IGNORE_HOST_SIDE_ENABLE)) - hook_call_deferred(config->deferred, 0); - - usb_spi_reset_interface(config); - - btable_ep[0].tx_count = 0; - STM32_TOGGLE_EP(0, EP_TX_RX_MASK, EP_TX_RX_VALID, EP_STATUS_OUT); - return 0; -} |