summaryrefslogtreecommitdiff
path: root/chip
diff options
context:
space:
mode:
authorBhanu Prakash Maiya <bhanumaiya@google.com>2020-04-30 15:26:52 -0700
committerCommit Bot <commit-bot@chromium.org>2020-06-05 10:06:44 +0000
commit86aacabcd3c2e496a43d291d9dee61e29edcc2d0 (patch)
tree5a87bbbee684ce95de718f654b0086abfa87daac /chip
parentbc96d7077db615d849d398115af76b7b85f63e01 (diff)
downloadchrome-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>
Diffstat (limited to 'chip')
-rw-r--r--chip/stm32/build.mk2
-rw-r--r--chip/stm32/host_command_common.c56
-rw-r--r--chip/stm32/spi.c6
-rw-r--r--chip/stm32/usart-stm32f4.c113
-rw-r--r--chip/stm32/usart-stm32f4.h19
-rw-r--r--chip/stm32/usart.h13
-rw-r--r--chip/stm32/usart_host_command.c616
-rw-r--r--chip/stm32/usart_host_command.h38
-rw-r--r--chip/stm32/usart_rx_dma.c49
-rw-r--r--chip/stm32/usart_rx_dma.h6
-rw-r--r--chip/stm32/usart_rx_interrupt-stm32f4.c52
-rw-r--r--chip/stm32/usart_tx_interrupt.c55
12 files changed, 1008 insertions, 17 deletions
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 */