diff options
author | Bhanu Prakash Maiya <bhanumaiya@google.com> | 2020-04-30 15:26:52 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-06-05 10:06:44 +0000 |
commit | 86aacabcd3c2e496a43d291d9dee61e29edcc2d0 (patch) | |
tree | 5a87bbbee684ce95de718f654b0086abfa87daac | |
parent | bc96d7077db615d849d398115af76b7b85f63e01 (diff) | |
download | chrome-ec-86aacabcd3c2e496a43d291d9dee61e29edcc2d0.tar.gz |
Bloonchipper: USART based transport layer for host command
1. USART host command layer in chip/stm32
2. Fix usart implementation in stm32
BUG=b:147849609
BRANCH=none
TEST=1. make BOARD=bloonchipper -j
2. usart request and response works on dragonclaw
Change-Id: Idd89d3e490f23aa528ecaf6510c13d16b405de13
Signed-off-by: Bhanu Prakash Maiya <bhanumaiya@google.com>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2190531
Tested-by: Bhanu Prakash Maiya <bhanumaiya@chromium.org>
Reviewed-by: Jett Rink <jettrink@chromium.org>
Reviewed-by: Tom Hughes <tomhughes@chromium.org>
Reviewed-by: Craig Hesling <hesling@chromium.org>
Commit-Queue: Bhanu Prakash Maiya <bhanumaiya@chromium.org>
Auto-Submit: Bhanu Prakash Maiya <bhanumaiya@chromium.org>
-rw-r--r-- | board/hatch_fp/board.c | 42 | ||||
-rw-r--r-- | board/hatch_fp/board.h | 15 | ||||
-rw-r--r-- | chip/stm32/build.mk | 2 | ||||
-rw-r--r-- | chip/stm32/host_command_common.c | 56 | ||||
-rw-r--r-- | chip/stm32/spi.c | 6 | ||||
-rw-r--r-- | chip/stm32/usart-stm32f4.c | 113 | ||||
-rw-r--r-- | chip/stm32/usart-stm32f4.h | 19 | ||||
-rw-r--r-- | chip/stm32/usart.h | 13 | ||||
-rw-r--r-- | chip/stm32/usart_host_command.c | 616 | ||||
-rw-r--r-- | chip/stm32/usart_host_command.h | 38 | ||||
-rw-r--r-- | chip/stm32/usart_rx_dma.c | 49 | ||||
-rw-r--r-- | chip/stm32/usart_rx_dma.h | 6 | ||||
-rw-r--r-- | chip/stm32/usart_rx_interrupt-stm32f4.c | 52 | ||||
-rw-r--r-- | chip/stm32/usart_tx_interrupt.c | 55 | ||||
-rw-r--r-- | common/fpsensor/build.mk | 2 | ||||
-rw-r--r-- | common/fpsensor/fpsensor_detect_strings.c (renamed from common/fpsensor/fpsensor_detect_common.c) | 0 | ||||
-rw-r--r-- | include/config.h | 16 | ||||
-rw-r--r-- | include/spi.h | 14 |
18 files changed, 1088 insertions, 26 deletions
diff --git a/board/hatch_fp/board.c b/board/hatch_fp/board.c index d0afce3414..e42f576ad8 100644 --- a/board/hatch_fp/board.c +++ b/board/hatch_fp/board.c @@ -13,6 +13,7 @@ #include "spi.h" #include "system.h" #include "task.h" +#include "usart_host_command.h" #include "util.h" /** @@ -67,15 +68,14 @@ const struct spi_device_t spi_devices[] = { }; const unsigned int spi_devices_used = ARRAY_SIZE(spi_devices); -static void spi_configure(void) +static void configure_fp_sensor_spi(void) { /* Configure SPI GPIOs */ gpio_config_module(MODULE_SPI_MASTER, 1); - /* - * Set all SPI master signal pins to very high speed: - * pins B12/13/14/15 - */ + + /* Set all SPI master signal pins to very high speed: B12/13/14/15 */ STM32_GPIO_OSPEEDR(GPIO_B) |= 0xff000000; + /* Enable clocks to SPI2 module (master) */ STM32_RCC_APB1ENR |= STM32_RCC_PB1_SPI2; @@ -85,7 +85,36 @@ static void spi_configure(void) /* Initialize board. */ static void board_init(void) { - spi_configure(); + enum fp_transport_type ret_transport = get_fp_transport_type(); + + /* Configure and enable SPI as master for FP sensor */ + configure_fp_sensor_spi(); + + ccprints("TRANSPORT_SEL: %s", + fp_transport_type_to_str(ret_transport)); + + /* Initialize transport based on bootstrap */ + switch (ret_transport) { + + case FP_TRANSPORT_TYPE_UART: + /* Check if CONFIG_USART_HOST_COMMAND is enabled. */ + if (IS_ENABLED(CONFIG_USART_HOST_COMMAND)) + usart_host_command_init(); + else + ccprints("ERROR: UART not supported in fw build."); + + /* Disable SPI interrupt to disable SPI transport layer */ + gpio_disable_interrupt(GPIO_SPI1_NSS); + break; + + case FP_TRANSPORT_TYPE_SPI: + /* SPI transport is enabled. SPI1_NSS interrupt will process + * incoming request/ + */ + break; + default: + ccprints("ERROR: Selected transport is not valid."); + } ccprints("TRANSPORT_SEL: %s", fp_transport_type_to_str(get_fp_transport_type())); @@ -93,6 +122,7 @@ static void board_init(void) /* Enable interrupt on PCH power signals */ gpio_enable_interrupt(GPIO_PCH_SLP_S3_L); gpio_enable_interrupt(GPIO_PCH_SLP_S0_L); + /* enable the SPI slave interface if the PCH is up */ hook_call_deferred(&ap_deferred_data, 0); } diff --git a/board/hatch_fp/board.h b/board/hatch_fp/board.h index 164abbf334..001f437b86 100644 --- a/board/hatch_fp/board.h +++ b/board/hatch_fp/board.h @@ -89,6 +89,21 @@ #define CONFIG_FLASH_READOUT_PROTECTION_AS_PSTATE /*-------------------------------------------------------------------------* + * USART Transport Setup + *-------------------------------------------------------------------------*/ +/* Enable USART host commands */ +#define CONFIG_USART_HOST_COMMAND +/* Enable USART and USART1 stream */ +#define CONFIG_STREAM_USART +#define CONFIG_STREAM_USART1 +/* Allocate and configure hw instance of USART */ +#undef CONFIG_UART_HOST_COMMAND_HW +#define CONFIG_UART_HOST_COMMAND_HW usart1_hw +/* Set baud rate of USART */ +#undef CONFIG_UART_HOST_COMMAND_BAUD_RATE +#define CONFIG_UART_HOST_COMMAND_BAUD_RATE 3000000 + +/*-------------------------------------------------------------------------* * UART Console Setup *-------------------------------------------------------------------------*/ diff --git a/chip/stm32/build.mk b/chip/stm32/build.mk index 8949349aed..904525a8da 100644 --- a/chip/stm32/build.mk +++ b/chip/stm32/build.mk @@ -53,7 +53,9 @@ chip-$(CONFIG_STREAM_USART)+=usart.o usart-$(CHIP_FAMILY).o chip-$(CONFIG_STREAM_USART)+=usart_rx_interrupt-$(CHIP_FAMILY).o chip-$(CONFIG_STREAM_USART)+=usart_tx_interrupt.o chip-$(CONFIG_STREAM_USART)+=usart_rx_dma.o usart_tx_dma.o +chip-$(CONFIG_USART_HOST_COMMAND)+=usart_host_command.o chip-$(CONFIG_CMD_USART_INFO)+=usart_info_command.o +chip-$(HAS_TASK_CONSOLE)+=host_command_common.o chip-$(CONFIG_WATCHDOG)+=watchdog.o chip-$(HAS_TASK_CONSOLE)+=uart.o ifndef CONFIG_KEYBOARD_NOT_RAW diff --git a/chip/stm32/host_command_common.c b/chip/stm32/host_command_common.c new file mode 100644 index 0000000000..f1d5a0f103 --- /dev/null +++ b/chip/stm32/host_command_common.c @@ -0,0 +1,56 @@ +/* Copyright 2020 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 "fpsensor_detect.h" +#include "host_command.h" +#include "spi.h" +#include "usart_host_command.h" + +#ifndef CONFIG_I2C_SLAVE + +/* Store current transport type */ +static enum fp_transport_type curr_transport_type = FP_TRANSPORT_TYPE_UNKNOWN; + +/* + * Get protocol information + */ +static enum ec_status host_command_protocol_info(struct host_cmd_handler_args + *args) +{ + enum ec_status ret_status = EC_RES_INVALID_COMMAND; + + /* + * If FP sensor task is enabled, read transport type from TRANSPORT_SEL + * bootstrap pin for the first time this function is called. + */ + if ((IS_ENABLED(HAS_TASK_FPSENSOR)) && + (curr_transport_type == FP_TRANSPORT_TYPE_UNKNOWN)) { + curr_transport_type = get_fp_transport_type(); + } + + /* + * Transport select is only enabled on boards with fp sensor tasks. + * If fp sensor task is enabled, transport is USART and + * host command layer is present, call usart_get_protocol. + * If fp sensor task is enabled and transport is SPI or else if only + * spi layer is enabled on non fp boards, call spi_get_protocol_info. + */ + if (IS_ENABLED(HAS_TASK_FPSENSOR) && + IS_ENABLED(CONFIG_USART_HOST_COMMAND) && + curr_transport_type == FP_TRANSPORT_TYPE_UART) + ret_status = usart_get_protocol_info(args); + else if (IS_ENABLED(CONFIG_SPI) || + (IS_ENABLED(HAS_TASK_FPSENSOR) && + curr_transport_type == FP_TRANSPORT_TYPE_SPI)) + ret_status = spi_get_protocol_info(args); + + return ret_status; +} +DECLARE_HOST_COMMAND(EC_CMD_GET_PROTOCOL_INFO, + host_command_protocol_info, + EC_VER_MASK(0)); + +#endif /* CONFIG_I2C_SLAVE */ diff --git a/chip/stm32/spi.c b/chip/stm32/spi.c index 78ff064dbd..6a38c2e262 100644 --- a/chip/stm32/spi.c +++ b/chip/stm32/spi.c @@ -14,7 +14,6 @@ #include "dma.h" #include "gpio.h" #include "hooks.h" -#include "host_command.h" #include "link_defs.h" #include "registers.h" #include "spi.h" @@ -720,7 +719,7 @@ DECLARE_HOOK(HOOK_INIT, spi_init, HOOK_PRIO_INIT_SPI); /** * Get protocol information */ -static enum ec_status spi_get_protocol_info(struct host_cmd_handler_args *args) +enum ec_status spi_get_protocol_info(struct host_cmd_handler_args *args) { struct ec_response_get_protocol_info *r = args->response; @@ -737,6 +736,3 @@ static enum ec_status spi_get_protocol_info(struct host_cmd_handler_args *args) return EC_RES_SUCCESS; } -DECLARE_HOST_COMMAND(EC_CMD_GET_PROTOCOL_INFO, - spi_get_protocol_info, - EC_VER_MASK(0)); diff --git a/chip/stm32/usart-stm32f4.c b/chip/stm32/usart-stm32f4.c new file mode 100644 index 0000000000..a554da147a --- /dev/null +++ b/chip/stm32/usart-stm32f4.c @@ -0,0 +1,113 @@ +/* Copyright 2020 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 "usart-stm32f4.h" + +#include "clock.h" +#include "common.h" +#include "compile_time_macros.h" +#include "hooks.h" +#include "registers.h" +#include "task.h" +#include "util.h" + +/* + * This configs array stores the currently active usart_config structure for + * each USART, an entry will be NULL if no USART driver is initialized for the + * corresponding hardware instance. + */ +#define STM32_USARTS_MAX 3 + +static struct usart_config const *configs[STM32_USARTS_MAX]; + +struct usart_configs usart_get_configs(void) +{ + return (struct usart_configs) {configs, ARRAY_SIZE(configs)}; +} + +static void usart_variant_enable(struct usart_config const *config) +{ + configs[config->hw->index] = config; + + + /* Use single-bit sampling */ + STM32_USART_CR3(config->hw->base) |= STM32_USART_CR3_ONEBIT; + + usart_set_baud_f0_l(config, config->baud, clock_get_freq()); + + task_enable_irq(config->hw->irq); +} + +static void usart_variant_disable(struct usart_config const *config) +{ + task_disable_irq(config->hw->irq); + + configs[config->hw->index] = NULL; +} + +static struct usart_hw_ops const usart_variant_hw_ops = { + .enable = usart_variant_enable, + .disable = usart_variant_disable, +}; + +/* + * USART interrupt bindings. These functions can not be defined as static or + * they will be removed by the linker because of the way that DECLARE_IRQ works. + */ +#if defined(CONFIG_STREAM_USART1) +struct usart_hw_config const usart1_hw = { + .index = 0, + .base = STM32_USART1_BASE, + .irq = STM32_IRQ_USART1, + .clock_register = &STM32_RCC_APB2ENR, + .clock_enable = STM32_RCC_PB2_USART1, + .ops = &usart_variant_hw_ops, +}; + +void usart1_interrupt(void) +{ + usart_interrupt(configs[0]); +} + +DECLARE_IRQ(STM32_IRQ_USART1, usart1_interrupt, 2); +#endif + +#if defined(CONFIG_STREAM_USART2) +struct usart_hw_config const usart2_hw = { + .index = 1, + .base = STM32_USART2_BASE, + .irq = STM32_IRQ_USART2, + .clock_register = &STM32_RCC_APB1ENR, + .clock_enable = STM32_RCC_PB1_USART2, + .ops = &usart_variant_hw_ops, +}; + +void usart2_interrupt(void) +{ + usart_interrupt(configs[1]); +} + +DECLARE_IRQ(STM32_IRQ_USART2, usart2_interrupt, 2); +#endif + +#if defined(CONFIG_STREAM_USART3) +struct usart_hw_config const usart3_hw = { + .index = 2, + .base = STM32_USART3_BASE, + .irq = STM32_IRQ_USART3, + .clock_register = &STM32_RCC_APB1ENR, + .clock_enable = STM32_RCC_PB1_USART3, + .ops = &usart_variant_hw_ops, +}; +#endif + +#if defined(CONFIG_STREAM_USART3) +void usart3_interrupt(void) +{ + usart_interrupt(configs[2]); +} + +DECLARE_IRQ(STM32_IRQ_USART3, usart3_interrupt, 2); +#endif diff --git a/chip/stm32/usart-stm32f4.h b/chip/stm32/usart-stm32f4.h new file mode 100644 index 0000000000..49af2af405 --- /dev/null +++ b/chip/stm32/usart-stm32f4.h @@ -0,0 +1,19 @@ +/* Copyright 2020 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 __CROS_EC_USART_STM32F4_H +#define __CROS_EC_USART_STM32F4_H + +#include "usart.h" + +/* + * The STM32F4 series can have as many as three UARTS. These are the HW configs + * for those UARTS. They can be used to initialize STM32 generic UART configs. + * CONFIG_STREAM_USART<X> enables the corresponding hardware instance. + */ +extern struct usart_hw_config const usart1_hw; +extern struct usart_hw_config const usart2_hw; +extern struct usart_hw_config const usart3_hw; + +#endif /* __CROS_EC_USART_STM32F4_H */ diff --git a/chip/stm32/usart.h b/chip/stm32/usart.h index 3e6f658e26..491bd66a04 100644 --- a/chip/stm32/usart.h +++ b/chip/stm32/usart.h @@ -197,6 +197,12 @@ void usart_shutdown(struct usart_config const *config); void usart_interrupt(struct usart_config const *config); /* + * Trigger tx interrupt to process tx data. Calling this function will set + * TXIEIE of USART HW instance and trigger associated IRQ. + */ +void usart_tx_start(struct usart_config const *config); + +/* * These are HW specific baud rate calculation and setting functions that the * peripheral variant code uses during initialization and clock frequency * change. The baud rate divisor input frequency is passed in Hertz. @@ -255,4 +261,11 @@ struct usart_configs { struct usart_configs usart_get_configs(void); +/* + * This usart_tx structure contains function pointer to interrupt + * handler implemented to send host response. Generic queue based + * interrupt handler is not used for usart host transport. + */ +extern struct usart_tx const usart_host_command_tx_interrupt; + #endif /* __CROS_EC_USART_H */ diff --git a/chip/stm32/usart_host_command.c b/chip/stm32/usart_host_command.c new file mode 100644 index 0000000000..c5b6e2990f --- /dev/null +++ b/chip/stm32/usart_host_command.c @@ -0,0 +1,616 @@ +/* Copyright 2020 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 "clock.h" +#include "dma.h" +#include "gpio.h" +#include "hooks.h" +#include "host_command.h" +#include "queue_policies.h" +#include "registers.h" +#include "system.h" +#include "task.h" +#include "usart_rx_dma.h" +#include "usart_host_command.h" +#include "usart-stm32f4.h" +#include "util.h" + +/* Console output macros */ +#define CPRINTS(format, args...) cprints(CC_HOSTCMD, format, ## args) +#define CPRINTF(format, args...) cprintf(CC_HOSTCMD, format, ## args) + +/* + * Timeout to wait for complete request packet + * + * This value determines how long we should wait for entire packet to arrive. + * USART host command handler should wait for at least 75% of + * EC_MSG_DEADLINE_MS, before declaring timeout and dropping the packet. + * + * This timeout should be less than host's driver timeout to make sure that + * last packet can be successfully discarded before AP attempts to resend + * request. AP driver waits for EC_MSG_DEADLINE_MS = 200 before attempting a + * retry. + */ +#define USART_REQ_RX_TIMEOUT (150 * MSEC) + +/* + * Timeout to wait for overrun bytes on USART + * + * This values determines how long call to process_request should be deferred + * in case host is sending extra bytes. This value is based on DMA buffer size. + * + * There is no guarantee that AP will send continuous bytes on usart. Wait + * for USART_DEFERRED_PROCESS_REQ_TIMEOUT_US to check if host is sending + * extra bytes. + * Note: This value affects the response latency. + */ +#define USART_DEFERRED_PROCESS_REQ_TIMEOUT 300 + +/* + * Max data size for a version 3 request/response packet. This is big enough + * to handle a request/response header, flash write offset/size, and 512 bytes + * of flash data. + */ +#define USART_MAX_REQUEST_SIZE 0x220 +#define USART_MAX_RESPONSE_SIZE 0x220 + +/* + * FIFO size for USART DMA. Should be big enough to handle worst case + * data processing + */ +#define USART_DMA_FIFO_SIZE 0x110 + +/* Local definitions */ + +/* + * Raw USART RX/TX byte buffers. + */ +static uint8_t usart_in_buffer[USART_MAX_REQUEST_SIZE] __aligned(4); +static uint8_t usart_out_buffer[USART_MAX_RESPONSE_SIZE] __aligned(4); + +/* + * Maintain head position of in buffer + * Head always starts with zero and goes up to max bytes. + * Once the buffer contents are read, it should go back to zero. + */ +static uint16_t usart_in_head; + +/* + * Maintain head position of out buffer + * Head always starts from zero and goes up to max bytes. + * Head is moved by tx interrupt handler to response size sent by host command + * task. Once all the bytes are sent (head == tail) both should go back to 0. + */ +static uint16_t usart_out_head; + +/* + * Once the response is ready, get the datalen + */ +static uint16_t usart_out_datalen; + +/* + * Enumeration to maintain different states of incoming request from + * host + */ +static enum uart_host_command_state { + /* + * USART host command handler not enabled. + */ + USART_HOST_CMD_STATE_DISABLED, + + /* + * Ready to receive next request + * This state represents USART layer is initialized and ready to + * receive host request. Once the response is sent, current_state is + * reset to this state to accept next packet. + */ + USART_HOST_CMD_READY_TO_RX, + + /* + * Receiving request + * After first byte is received current_state is moved to receiving + * state until all the header bytes + datalen bytes are received. + * If host_request_timeout was called in this state, it would be + * because of an underrun situation. + */ + USART_HOST_CMD_RECEIVING, + + /* + * Receiving complete + * Once all the header bytes + datalen bytes are received, current_state + * is moved to complete. Ideally, host should wait for response or retry + * timeout before sending anymore bytes, otherwise current_state will + * be moved to overrun to represent extra bytes sent by host. + */ + USART_HOST_CMD_COMPLETE, + + /* + * Processing request + * Once the process_request starts processing usart_in_buffer, + * current_state is moved to processing state. Host should not send + * any bytes in this state as it would be considered contiguous + * request. + */ + USART_HOST_CMD_PROCESSING, + + /* + * Sending response + * Once host task is ready with the response bytes, current_state is + * moved to sending state. + */ + USART_HOST_CMD_SENDING, + + /* + * Received bad data + * If bad packet header is received, current_state is moved to rx_bad + * state and after rx_timeout all the bytes are dropped. + */ + USART_HOST_CMD_RX_BAD, + + /* + * Receiving data overrun bytes + * If extra bytes are received after current_state is in complete, + * host is sending extra bytes which indicates data overrun. + */ + USART_HOST_CMD_RX_OVERRUN, + +} current_state __aligned(4); + +/* + * This diagram is the state machine representation of USART host + * command layer. + * + * This layer is responsible for checking packet integrity of incoming bytes + * on usart transceiver. It will only process packet header to check version, + * data_len. This layer will not process payload bytes. + * + * STATE = USART_HOST_CMD_STATE_DISABLED + * + * Initialize USART and local variables + * + * STATE = USART_HOST_CMD_READY_TO_RX + * + * |<---------- HOST RETRY TIMEOUT = 200 ms ---------->| + * | + * |--------------USART_REQ_RX_TIMEOUT------>| + * | Underrun if request not complete -->| + * | |<-- USART ready to rx + * |____REQUEST____ ____REQUEST____ + * | | | | | | + * | HDR | DATA | | HDR | DATA | + * |_____|_________| |_____|_________| + * | + * |<-- Request packet start + * | + * STATE = USART_HOST_CMD_RECEIVING + * | + * |<-- HDR received, now we will wait for data len bytes + * | + * If bad packet is received, move state to rx_bad + * STATE = USART_HOST_CMD_RX_BAD + * Ignore data processing, print status on console and reset layer ----------- + * | | + * |<-- Request packet end (data rx complete) | + * | | + * If request_timeout is called, it represents packet underrun | + * Ignore data processing, print status on console and reset layer ----------- + * | | + * STATE = USART_HOST_CMD_COMPLETE | + * | | + * |<-- Deferred call to process request | + * | | + * If extra byte is received, move state to overrun | + * STATE = USART_HOST_CMD_RX_OVERRUN | + * Ignore data processing, print status on console and reset layer ----------- + * | | + * -->| |<-- USART_DEFERRED_PROCESS_REQ_TIMEOUT | + * | Start process request | + * | | + * STATE = USART_HOST_CMD_PROCESSING | + * | | + * Send ec_host_request to host command task | + * |<-- Packet sent to host command task | + * >| |<-- host command task process time | + * |<-- host command task ready for response | + * | | + * STATE = USART_HOST_CMD_SENDING | + * | | + * |____RESPONSE____ | + * | | | | + * | HDR | DATA | | + * |_____|__________| | + * | | + * |<-- Response send complete | + * | + * STATE = USART_HOST_CMD_READY_TO_RX <------------------------------ + */ + +/* + * Local function definition + */ +static void usart_host_command_reset(void); +static void usart_host_command_request_timeout(void); +static void usart_host_command_process_request(void); +static void usart_host_command_process_response(struct host_packet *pkt); +/* + * Local variable declaration + */ + +/* + * Configure dma instance for rx + * + * STM32_DMAS_USART1_RX is the DMA channel to be used for reception. This DMA + * channel is for the USART peripheral. + * + * A unnamed, valid, empty usart_rx_dma_state structure is required to manage + * DMA based transmission. + * + * USART_DMA_FIFO_SIZE is size of the valid, unnamed DMA circular buffer. + * This buffer is large enough to process worst case interrupt latency this + * layer can encounter. + */ +static struct usart_rx_dma const usart_host_command_rx_dma = { + .usart_rx = { + .producer_ops = { + .read = NULL, + }, + .init = usart_rx_dma_init, + .interrupt = usart_host_command_rx_dma_interrupt, + .info = USART_RX_DMA_INFO, + }, + .state = &((struct usart_rx_dma_state) {}), + .fifo_buffer = ((uint8_t[USART_DMA_FIFO_SIZE]) {}), + .fifo_size = USART_DMA_FIFO_SIZE, + .channel = STM32_DMAS_USART1_RX, +}; + +/* + * Configure USART structure with hardware, interrupt handlers, baudrate. + */ +static struct usart_config const tl_usart = { + .hw = &CONFIG_UART_HOST_COMMAND_HW, + .rx = &usart_host_command_rx_dma.usart_rx, + .tx = &usart_host_command_tx_interrupt, + .state = &((struct usart_state){}), + .baud = CONFIG_UART_HOST_COMMAND_BAUD_RATE, + .flags = 0, +}; + +/* + * Local function declaration + */ + +/* + * This function will be called only if request rx timed out. + * Drop the packet and put tl state into RX_READY + */ +static void usart_host_command_request_timeout(void) +{ + switch (current_state) { + case USART_HOST_CMD_RECEIVING: + /* If state is receiving then timeout was hit due to underrun */ + CPRINTS("USART HOST CMD ERROR: Request underrun detected."); + break; + + case USART_HOST_CMD_RX_OVERRUN: + /* If state is rx_overrun then timeout was hit because + * process request was cancelled and extra rx bytes were + * dropped + */ + CPRINTS("USART HOST CMD ERROR: Request overrun detected."); + break; + + case USART_HOST_CMD_RX_BAD: + /* If state is rx_bad then packet header was bad and process + * request was cancelled to drop all incoming bytes. + */ + CPRINTS("USART HOST CMD ERROR: Bad packet header detected."); + break; + + default: + CPRINTS("USART HOST CMD ERROR: Request timeout mishandled"); + } + + /* Reset host command layer to accept new request */ + usart_host_command_reset(); +} +DECLARE_DEFERRED(usart_host_command_request_timeout); + +/* + * This function is called from interrupt handler after entire packet is + * received. + */ +static void usart_host_command_process_request(void) +{ + /* Handle usart_in_buffer as ec_host_request */ + struct ec_host_request *ec_request = + (struct ec_host_request *)usart_in_buffer; + + /* Prepare host_packet for host command task */ + static struct host_packet uart_packet; + + /* + * Disable interrupts before processing request to be sent + * to host command task. + */ + interrupt_disable(); + + /* + * In case rx interrupt handler was called in this function's prologue, + * host was trying to send extra byte(s) exactly when + * USART_DEFERRED_PROCESS_REQ_TIMEOUT expired. If state is + * not USART_HOST_CMD_COMPLETE, overrun condition is already + * handled. + */ + if (current_state != USART_HOST_CMD_COMPLETE) { + /* Enable interrupts before exiting this function. */ + interrupt_enable(); + + return; + } + + /* Move current_state to USART_HOST_CMD_PROCESSING */ + current_state = USART_HOST_CMD_PROCESSING; + + /* Enable interrupts as current_state is safely handled. */ + interrupt_enable(); + + /* + * Cancel deferred call to timeout handler as request + * received was good. + */ + hook_call_deferred( + &usart_host_command_request_timeout_data, + -1); + + uart_packet.send_response = usart_host_command_process_response; + uart_packet.request = usart_in_buffer; + uart_packet.request_temp = NULL; + uart_packet.request_max = sizeof(usart_in_buffer); + uart_packet.request_size = + host_request_expected_size(ec_request); + uart_packet.response = usart_out_buffer; + uart_packet.response_max = sizeof(usart_out_buffer); + uart_packet.response_size = 0; + uart_packet.driver_result = EC_RES_SUCCESS; + + /* Process usart_packet */ + host_packet_receive(&uart_packet); +} +DECLARE_DEFERRED(usart_host_command_process_request); + +/* + * This function is called from host command task after it is ready with a + * response. + */ +static void usart_host_command_process_response(struct host_packet *pkt) +{ + /* Disable interrupts before entering critical section. */ + interrupt_disable(); + + /* + * Send host command response in usart_out_buffer via + * tx_interrupt_handler. + * + * Send response if current state is USART_HOST_CMD_PROCESSING + * state. If this layer is in any other state drop response and + * let request timeout handler handle state transitions. + */ + if (current_state != USART_HOST_CMD_PROCESSING) { + /* Enable interrupts before exiting critical section. */ + interrupt_enable(); + + return; + } + + /* Move to sending state. */ + current_state = USART_HOST_CMD_SENDING; + + /* Enable interrupts before exiting critical section. */ + interrupt_enable(); + + usart_out_datalen = pkt->response_size; + usart_out_head = 0; + + /* Start sending response to host via usart tx by + * triggering tx interrupt. + */ + usart_tx_start(&tl_usart); +} + +/* + * This function will drop current request, clear buffers. + */ +static void usart_host_command_reset(void) +{ + /* Cancel deferred call to process_request. */ + hook_call_deferred( + &usart_host_command_process_request_data, + -1); + + /* Cancel deferred call to timeout handler. */ + hook_call_deferred( + &usart_host_command_request_timeout_data, + -1); + + /* + * Disable interrupts before entering critical region + * Operations in this section should be minimum to avoid + * harming the real-time characteristics of the runtime. + */ + interrupt_disable(); + + /* Clear in buffer, head and datalen */ + usart_in_head = 0; + + /* Clear out buffer, head and datalen */ + usart_out_datalen = 0; + usart_out_head = 0; + + /* Move to ready state*/ + current_state = USART_HOST_CMD_READY_TO_RX; + + /* Enable interrupts before exiting critical region + */ + interrupt_enable(); +} + +/* + * Exported functions + */ + +/* + * Initialize USART host command layer. + */ +void usart_host_command_init(void) +{ + /* USART host command layer starts in DISABLED state */ + current_state = USART_HOST_CMD_STATE_DISABLED; + + /* Initialize transport uart */ + usart_init(&tl_usart); + + /* Initialize local variables */ + usart_in_head = 0; + usart_out_head = 0; + usart_out_datalen = 0; + + /* Move to ready state */ + current_state = USART_HOST_CMD_READY_TO_RX; +} + +/* + * Function to handle incoming bytes from DMA interrupt handler + * + */ +size_t usart_host_command_rx_append_data(struct usart_config const *config, + const uint8_t *src, size_t count) +{ + /* Define ec_host_request pointer to process in bytes later*/ + struct ec_host_request *ec_request = + (struct ec_host_request *) usart_in_buffer; + + /* Once the header is received, store the datalen */ + static int usart_in_datalen; + + /* + * Host can send extra bytes than in header data_len + * Only copy valid bytes in buffer + */ + if (current_state == USART_HOST_CMD_READY_TO_RX || + current_state == USART_HOST_CMD_RECEIVING || + (usart_in_head + count) < USART_MAX_REQUEST_SIZE) { + /* Copy all the bytes from DMA FIFO */ + memcpy(usart_in_buffer + usart_in_head, + src, count); + } + + /* + * Add incoming byte count to usart_in_head. + * Even if overflow bytes are not copied in buffer, maintain + * the overflow count so that packet can be dropped later in this + * function. + */ + usart_in_head += count; + + if (current_state == USART_HOST_CMD_READY_TO_RX) { + /* Kick deferred call to request timeout handler */ + hook_call_deferred(&usart_host_command_request_timeout_data, + USART_REQ_RX_TIMEOUT); + + /* Move current state to receiving */ + current_state = USART_HOST_CMD_RECEIVING; + } + + if (usart_in_head >= sizeof(struct ec_host_request)) { + /* Buffer has request header. Check header and get data_len */ + usart_in_datalen = host_request_expected_size(ec_request); + + if (usart_in_datalen == 0 || + usart_in_datalen > USART_MAX_REQUEST_SIZE) { + /* EC host request version not compatible or + * reserved byte is not zero. + */ + current_state = USART_HOST_CMD_RX_BAD; + } else if (usart_in_head == usart_in_datalen) { + /* + * Once all the datalen bytes are received, wait for + * USART_DEFERRED_PROCESS_REQ_TIMEOUT to call + * process_request function. This is to catch overrun + * bytes before processing the packet. + */ + hook_call_deferred( + &usart_host_command_process_request_data, + USART_DEFERRED_PROCESS_REQ_TIMEOUT); + + /* If no data in request, packet is complete */ + current_state = USART_HOST_CMD_COMPLETE; + } else if (usart_in_head > usart_in_datalen) { + /* Cancel deferred call to process_request */ + hook_call_deferred( + &usart_host_command_process_request_data, + -1); + + /* Move state to overrun*/ + current_state = USART_HOST_CMD_RX_OVERRUN; + } + } + + if (current_state == USART_HOST_CMD_PROCESSING) + /* Host should not send data before receiving a response. + * Since the request was already sent to host command task, + * just notify console about this. After response is sent + * dma will be cleared to handle next packet + */ + CPRINTS("USART HOST CMD ERROR: Contiguous packets detected."); + + /* Return count to show all incoming bytes were processed */ + return count; +} + +/* + * This function processes the outgoing bytes from tl usart. + */ +size_t usart_host_command_tx_remove_data(struct usart_config const *config, + uint8_t *dest) +{ + size_t bytes_remaining = 0; + + if (current_state == USART_HOST_CMD_SENDING && + usart_out_datalen != 0) { + /* Calculate byte_remaining in out_buffer */ + bytes_remaining = usart_out_datalen - usart_out_head; + + /* Get char on the head */ + *((uint8_t *) dest) = usart_out_buffer[usart_out_head++]; + + /* If no bytes remaining, reset layer to accept next + * request. + */ + if (bytes_remaining == 0) + usart_host_command_reset(); + } + + /* Return count of bytes remaining in out buffer */ + return bytes_remaining; +} + +/* + * Get protocol information + */ +enum ec_status usart_get_protocol_info(struct host_cmd_handler_args *args) +{ + struct ec_response_get_protocol_info *r = args->response; + + memset(r, 0, sizeof(*r)); + r->protocol_versions |= BIT(3); + r->max_request_packet_size = USART_MAX_REQUEST_SIZE; + r->max_response_packet_size = USART_MAX_RESPONSE_SIZE; + r->flags = EC_PROTOCOL_INFO_IN_PROGRESS_SUPPORTED; + args->response_size = sizeof(*r); + + return EC_RES_SUCCESS; +} diff --git a/chip/stm32/usart_host_command.h b/chip/stm32/usart_host_command.h new file mode 100644 index 0000000000..ee41d8a59b --- /dev/null +++ b/chip/stm32/usart_host_command.h @@ -0,0 +1,38 @@ +/* Copyright 2020 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 __CROS_EC_USART_HOST_COMMAND_H +#define __CROS_EC_USART_HOST_COMMAND_H + +#include <stdarg.h> /* For va_list */ +#include "common.h" +#include "gpio.h" +#include "host_command.h" +#include "usart.h" + +/* + * Add data to host command layer buffer. + */ +size_t usart_host_command_rx_append_data(struct usart_config const *config, + const uint8_t *src, size_t count); + +/* + * Remove data from the host command layer buffer. + */ +size_t usart_host_command_tx_remove_data(struct usart_config const *config, + uint8_t *dest); + +/* + * Get USART protocol information. This function is called in runtime if + * board's host command transport is USART. + */ +enum ec_status usart_get_protocol_info(struct host_cmd_handler_args *args); + +/* + * Initialize USART host command layer. + */ +void usart_host_command_init(void); + +#endif /* __CROS_EC_USART_HOST_COMMAND_H */ diff --git a/chip/stm32/usart_rx_dma.c b/chip/stm32/usart_rx_dma.c index 0e26763de6..8665e304e7 100644 --- a/chip/stm32/usart_rx_dma.c +++ b/chip/stm32/usart_rx_dma.c @@ -10,8 +10,12 @@ #include "console.h" #include "registers.h" #include "system.h" +#include "usart_host_command.h" #include "util.h" +typedef size_t (*add_data_t)(struct usart_config const *config, + const uint8_t *src, size_t count); + void usart_rx_dma_init(struct usart_config const *config) { struct usart_rx_dma const *dma_config = @@ -27,6 +31,9 @@ void usart_rx_dma_init(struct usart_config const *config) STM32_DMA_CCR_CIRC), }; + if (IS_ENABLED(CHIP_FAMILY_STM32F4)) + options.flags |= STM32_DMA_CCR_CHANNEL(STM32_REQ_USART1_RX); + STM32_USART_CR1(base) |= STM32_USART_CR1_RXNEIE; STM32_USART_CR1(base) |= STM32_USART_CR1_RE; STM32_USART_CR3(base) |= STM32_USART_CR3_DMAR; @@ -37,7 +44,9 @@ void usart_rx_dma_init(struct usart_config const *config) dma_start_rx(&options, dma_config->fifo_size, dma_config->fifo_buffer); } -void usart_rx_dma_interrupt(struct usart_config const *config) +static void usart_rx_dma_interrupt_common( + struct usart_config const *config, + add_data_t add_data) { struct usart_rx_dma const *dma_config = DOWNCAST(config->rx, struct usart_rx_dma const, usart_rx); @@ -51,9 +60,9 @@ void usart_rx_dma_interrupt(struct usart_config const *config) if (new_index > old_index) { new_bytes = new_index - old_index; - added = queue_add_units(config->producer.queue, - dma_config->fifo_buffer + old_index, - new_bytes); + added = add_data(config, + dma_config->fifo_buffer + old_index, + new_bytes); } else if (new_index < old_index) { /* * Handle the case where the received bytes are not contiguous @@ -62,12 +71,12 @@ void usart_rx_dma_interrupt(struct usart_config const *config) */ new_bytes = dma_config->fifo_size - (old_index - new_index); - added = queue_add_units(config->producer.queue, - dma_config->fifo_buffer + old_index, - dma_config->fifo_size - old_index) + - queue_add_units(config->producer.queue, - dma_config->fifo_buffer, - new_index); + added = add_data(config, + dma_config->fifo_buffer + old_index, + dma_config->fifo_size - old_index) + + add_data(config, + dma_config->fifo_buffer, + new_index); } else { /* (new_index == old_index): nothing to add to the queue. */ } @@ -80,6 +89,26 @@ void usart_rx_dma_interrupt(struct usart_config const *config) dma_config->state->index = new_index; } +static size_t queue_add(struct usart_config const *config, + const uint8_t *src, size_t count) +{ + return queue_add_units(config->producer.queue, (void *)src, count); +} + +void usart_rx_dma_interrupt(struct usart_config const *config) +{ + usart_rx_dma_interrupt_common(config, &queue_add); +} + + +#if defined(CONFIG_USART_HOST_COMMAND) +void usart_host_command_rx_dma_interrupt(struct usart_config const *config) +{ + usart_rx_dma_interrupt_common(config, + &usart_host_command_rx_append_data); +} +#endif /* CONFIG_USART_HOST_COMMAND */ + void usart_rx_dma_info(struct usart_config const *config) { struct usart_rx_dma const *dma_config = diff --git a/chip/stm32/usart_rx_dma.h b/chip/stm32/usart_rx_dma.h index a5a04e2829..064ab8046c 100644 --- a/chip/stm32/usart_rx_dma.h +++ b/chip/stm32/usart_rx_dma.h @@ -102,6 +102,12 @@ void usart_rx_dma_init(struct usart_config const *config); void usart_rx_dma_interrupt(struct usart_config const *config); /* + * Function pointers needed to initialize host command rx dma interrupt. + * This should be only called from usart host command layer. + */ +void usart_host_command_rx_dma_interrupt(struct usart_config const *config); + +/* * Debug function, used to print DMA RX statistics to the console. */ void usart_rx_dma_info(struct usart_config const *config); diff --git a/chip/stm32/usart_rx_interrupt-stm32f4.c b/chip/stm32/usart_rx_interrupt-stm32f4.c new file mode 100644 index 0000000000..bfbee469a6 --- /dev/null +++ b/chip/stm32/usart_rx_interrupt-stm32f4.c @@ -0,0 +1,52 @@ +/* Copyright 2020 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. + */ + +/* Interrupt based USART RX driver for STM32F0 and STM32F4 */ + +#include "usart.h" + +#include "atomic.h" +#include "common.h" +#include "queue.h" +#include "registers.h" + +static void usart_rx_init(struct usart_config const *config) +{ + intptr_t base = config->hw->base; + + STM32_USART_CR1(base) |= STM32_USART_CR1_RXNEIE; + STM32_USART_CR1(base) |= STM32_USART_CR1_RE; +#if defined(CHIP_FAMILY_STM32F0) || defined(CHIP_FAMILY_STM32F3) || \ + defined(CHIP_FAMILY_STM32L4) + STM32_USART_CR3(base) |= STM32_USART_CR3_OVRDIS; +#endif +} + +static void usart_rx_interrupt_handler(struct usart_config const *config) +{ + intptr_t base = config->hw->base; + int32_t status = STM32_USART_SR(base); + + if (status & STM32_USART_SR_RXNE) { + uint8_t byte = STM32_USART_RDR(base); + + if (!queue_add_unit(config->producer.queue, &byte)) + atomic_add(&config->state->rx_dropped, 1); + } +} + +struct usart_rx const usart_rx_interrupt = { + .producer_ops = { + /* + * Nothing to do here, we either had enough space in the queue + * when a character came in or we dropped it already. + */ + .read = NULL, + }, + + .init = usart_rx_init, + .interrupt = usart_rx_interrupt_handler, + .info = NULL, +}; diff --git a/chip/stm32/usart_tx_interrupt.c b/chip/stm32/usart_tx_interrupt.c index 7c7f1f84de..6a6aaf65f7 100644 --- a/chip/stm32/usart_tx_interrupt.c +++ b/chip/stm32/usart_tx_interrupt.c @@ -10,8 +10,13 @@ #include "common.h" #include "registers.h" #include "system.h" +#include "task.h" +#include "usart_host_command.h" #include "util.h" +typedef size_t (*remove_data_t)(struct usart_config const *config, + uint8_t *dest); + static void usart_tx_init(struct usart_config const *config) { intptr_t base = config->hw->base; @@ -32,7 +37,9 @@ static void usart_written(struct consumer const *consumer, size_t count) STM32_USART_CR1(config->hw->base) |= STM32_USART_CR1_TXEIE; } -static void usart_tx_interrupt_handler(struct usart_config const *config) +static void usart_tx_interrupt_handler_common( + struct usart_config const *config, + remove_data_t remove_data) { intptr_t base = config->hw->base; uint8_t byte; @@ -40,7 +47,7 @@ static void usart_tx_interrupt_handler(struct usart_config const *config) if (!(STM32_USART_SR(base) & STM32_USART_SR_TXE)) return; - if (queue_remove_unit(config->consumer.queue, &byte)) { + if (remove_data(config, &byte)) { STM32_USART_TDR(base) = byte; /* @@ -64,6 +71,30 @@ static void usart_tx_interrupt_handler(struct usart_config const *config) } } +static size_t queue_remove(struct usart_config const *config, uint8_t *dest) +{ + return queue_remove_unit(config->producer.queue, (void *) dest); +} + +static void usart_tx_interrupt_handler(struct usart_config const *config) +{ + usart_tx_interrupt_handler_common(config, &queue_remove); +} + +void usart_tx_start(struct usart_config const *config) +{ + intptr_t base = config->hw->base; + + /* If interrupt is already enabled, nothing to do */ + if (STM32_USART_CR1(base) & STM32_USART_CR1_TXEIE) + return; + + disable_sleep(SLEEP_MASK_UART); + STM32_USART_CR1(base) |= (STM32_USART_CR1_TXEIE); + + task_trigger_irq(config->hw->irq); +} + struct usart_tx const usart_tx_interrupt = { .consumer_ops = { .written = usart_written, @@ -73,3 +104,23 @@ struct usart_tx const usart_tx_interrupt = { .interrupt = usart_tx_interrupt_handler, .info = NULL, }; + +#if defined(CONFIG_USART_HOST_COMMAND) + +static void usart_host_command_tx_interrupt_handler( + struct usart_config const *config) +{ + usart_tx_interrupt_handler_common(config, + &usart_host_command_tx_remove_data); +} + +struct usart_tx const usart_host_command_tx_interrupt = { + .consumer_ops = { + .written = usart_written, + }, + + .init = usart_tx_init, + .interrupt = usart_host_command_tx_interrupt_handler, + .info = NULL, +}; +#endif /* CONFIG_USART_HOST_COMMAND */ diff --git a/common/fpsensor/build.mk b/common/fpsensor/build.mk index 8ea6569ddd..d7e0e72b63 100644 --- a/common/fpsensor/build.mk +++ b/common/fpsensor/build.mk @@ -10,4 +10,4 @@ _fpsensor_dir:=$(dir $(lastword $(MAKEFILE_LIST))) all-obj-$(HAS_TASK_FPSENSOR)+=$(_fpsensor_dir)fpsensor_state.o all-obj-$(HAS_TASK_FPSENSOR)+=$(_fpsensor_dir)fpsensor_crypto.o all-obj-$(HAS_TASK_FPSENSOR)+=$(_fpsensor_dir)fpsensor.o -all-obj-$(HAS_TASK_CONSOLE)+=$(_fpsensor_dir)fpsensor_detect_common.o +all-obj-$(HAS_TASK_CONSOLE)+=$(_fpsensor_dir)fpsensor_detect_strings.o diff --git a/common/fpsensor/fpsensor_detect_common.c b/common/fpsensor/fpsensor_detect_strings.c index b9bcf9bb3b..b9bcf9bb3b 100644 --- a/common/fpsensor/fpsensor_detect_common.c +++ b/common/fpsensor/fpsensor_detect_strings.c diff --git a/include/config.h b/include/config.h index 108edc89a5..bd86cae579 100644 --- a/include/config.h +++ b/include/config.h @@ -3575,6 +3575,22 @@ #undef CONFIG_STREAM_USB /*****************************************************************************/ +/* UART HOST COMMAND config */ + +/* Includes USART as host command interface */ +#undef CONFIG_USART_HOST_COMMAND + +/* Pointer to USART HW config of physical instance */ +#undef CONFIG_UART_HOST_COMMAND_HW + +/* + * USART baudrate for host command interface. + * Typically configured at 3000000 to handle use cases + * like firmware download and big packets in a reasonable time. + */ +#undef CONFIG_UART_HOST_COMMAND_BAUD_RATE + +/*****************************************************************************/ /* UART config */ /* Baud rate for UARTs */ diff --git a/include/spi.h b/include/spi.h index c42b7ab757..ad087a6ffc 100644 --- a/include/spi.h +++ b/include/spi.h @@ -8,6 +8,8 @@ #ifndef __CROS_EC_SPI_H #define __CROS_EC_SPI_H +#include "host_command.h" + /* * SPI Clock polarity and phase mode (0 - 3) * @code @@ -66,7 +68,8 @@ int spi_enable(int port, int enable); #define SPI_READBACK_ALL (-1) -/* Issue a SPI transaction. Assumes SPI port has already been enabled. +/* + * Issue a SPI transaction. Assumes SPI port has already been enabled. * * Transmits <txlen> bytes from <txdata>, throwing away the corresponding * received data, then transmits <rxlen> dummy bytes, saving the received data @@ -85,7 +88,8 @@ int spi_transaction(const struct spi_device_t *spi_device, const uint8_t *txdata, int txlen, uint8_t *rxdata, int rxlen); -/* Similar to spi_transaction(), but hands over to DMA for reading response. +/* + * Similar to spi_transaction(), but hands over to DMA for reading response. * Must call spi_transaction_flush() after this to make sure the response is * received. * Contrary the regular spi_transaction(), this function does NOT lock the @@ -101,6 +105,12 @@ int spi_transaction_flush(const struct spi_device_t *spi_device); /* Wait for async response received but do not de-assert chip select */ int spi_transaction_wait(const struct spi_device_t *spi_device); +/* + * Get SPI protocol information. This function is called in runtime if board's + * host command transport is SPI. + */ +enum ec_status spi_get_protocol_info(struct host_cmd_handler_args *args); + #ifdef CONFIG_SPI /** * Called when the NSS level changes, signalling the start or end of a SPI |