summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJett Rink <jettrink@chromium.org>2019-12-13 16:21:57 -0700
committerCommit Bot <commit-bot@chromium.org>2020-01-30 22:20:49 +0000
commite6d61bac16b9b7b709d20a8e83177afce27d6e4e (patch)
treea2261569f7d55ef7537af70c98453204fbafa3e0
parentd79ee1cba05d36d568ed6f30ba5dadb45fd57681 (diff)
downloadchrome-ec-e6d61bac16b9b7b709d20a8e83177afce27d6e4e.tar.gz
c2d2: initial c2d2 add
C2D2 is a debug board bring that uses an 8-pin debug header that is pin compatible with the em100 flash emulator. BRANCH=none BUG=b:145314772 TEST=UART communication for EC and H1 TEST=UART flashing of EC TEST=SPI reading and writing TEST=Automatic Vref detection for UART upon connect and disconnect Change-Id: I023994ed78942f2307e4adb802b5cc96afdf7e24 Signed-off-by: Jett Rink <jettrink@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1991849 Reviewed-by: Diana Z <dzigterman@chromium.org>
-rw-r--r--board/c2d2/board.c870
-rw-r--r--board/c2d2/board.h122
-rw-r--r--board/c2d2/build.mk13
-rw-r--r--board/c2d2/ec.tasklist11
-rw-r--r--board/c2d2/gpio.inc50
-rw-r--r--extra/usb_updater/c2d2.json15
-rwxr-xr-xextra/usb_updater/servo_updater.py2
-rw-r--r--setup.py3
-rwxr-xr-xutil/flash_ec16
9 files changed, 1098 insertions, 4 deletions
diff --git a/board/c2d2/board.c b/board/c2d2/board.c
new file mode 100644
index 0000000000..8c47cc224d
--- /dev/null
+++ b/board/c2d2/board.c
@@ -0,0 +1,870 @@
+/* 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.
+ */
+/* C2D2 debug device board configuration */
+
+#include "adc.h"
+#include "adc_chip.h"
+#include "common.h"
+#include "console.h"
+#include "ec_version.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "i2c.h"
+#include "queue_policies.h"
+#include "registers.h"
+#include "spi.h"
+#include "task.h"
+#include "timer.h"
+#include "update_fw.h"
+#include "usart_rx_dma.h"
+#include "usart_tx_dma.h"
+#include "usart-stm32f0.h"
+#include "usb_hw.h"
+#include "usb_i2c.h"
+#include "usb_spi.h"
+#include "usb-stream.h"
+#include "util.h"
+
+#include "gpio_list.h"
+
+#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args)
+#define CPRINTF(format, args...) cprintf(CC_SYSTEM, format, ## args)
+
+/* Forward declarations */
+static void update_vrefs_and_shifters(void);
+DECLARE_DEFERRED(update_vrefs_and_shifters);
+
+/* Global state tracking current pin configuration and operations */
+static struct mutex vref_uart_state_mutex;
+static int vref_monitor_disable;
+#define VREF_MON_DIS_H1_RST_HELD BIT(0)
+#define VREF_MON_DIS_EC_PWR_HELD BIT(1)
+#define VREF_MON_DIS_SPI_MODE BIT(2)
+
+static int uart_state;
+#define UART_STATE_HELD BIT(0)
+#define UART_STATE_SPI_MODE BIT(1)
+
+void board_config_pre_init(void)
+{
+ /* enable SYSCFG & COMP clock */
+ STM32_RCC_APB2ENR |= STM32_RCC_SYSCFGEN;
+
+ /* enable DAC for comparator input */
+ STM32_RCC_APB1ENR |= STM32_RCC_DACEN;
+
+ /*
+ * the DMA mapping is :
+ * Chan 3 : USART3_RX
+ * Chan 5 : USART1_RX
+ * Chan 6 : SPI2_RX
+ * Chan 7 : SPI2_TX
+ *
+ * i2c : no dma
+ * tim16/17: no dma
+ */
+ STM32_SYSCFG_CFGR1 |= BIT(24); /* Remap SPI2_RX to channel 6 */
+ STM32_SYSCFG_CFGR1 |= BIT(26); /* Remap USART3 RX/TX DMA */
+ STM32_SYSCFG_CFGR1 |= BIT(10); /* Remap USART1 RX/TX DMA */
+}
+
+/******************************************************************************
+ ** ADC channels
+ */
+const struct adc_t adc_channels[] = {
+ /* Sensing the H1's voltage at the DUT side. Converted to mV. */
+ [ADC_H1_SPI_VREF] = {
+ .name = "H1_VREF",
+ .factor_mul = 3300,
+ .factor_div = 4096,
+ .shift = 0,
+ .channel = STM32_AIN(3),
+ },
+ /* Sensing the EC's voltage at the DUT side. Converted to mV. */
+ [ADC_EC_SPI_VREF] = {
+ .name = "EC_VREF",
+ .factor_mul = 3300,
+ .factor_div = 4096,
+ .shift = 0,
+ .channel = STM32_AIN(4),
+ }
+};
+BUILD_ASSERT(ARRAY_SIZE(adc_channels) == ADC_CH_COUNT);
+
+/******************************************************************************
+ * Define the strings used in our USB descriptors.
+ */
+const void *const usb_strings[] = {
+ [USB_STR_DESC] = usb_string_desc,
+ [USB_STR_VENDOR] = USB_STRING_DESC("Google Inc."),
+ [USB_STR_PRODUCT] = USB_STRING_DESC("C2D2"),
+ [USB_STR_SERIALNO] = 0,
+ [USB_STR_VERSION] = USB_STRING_DESC(CROS_EC_VERSION32),
+ [USB_STR_USART4_STREAM_NAME] = USB_STRING_DESC("CR50"),
+ [USB_STR_UPDATE_NAME] = USB_STRING_DESC("Firmware update"),
+ [USB_STR_CONSOLE_NAME] = USB_STRING_DESC("C2D2 Shell"),
+ [USB_STR_I2C_NAME] = USB_STRING_DESC("I2C"),
+ [USB_STR_USART3_STREAM_NAME] = USB_STRING_DESC("CPU"),
+ [USB_STR_USART1_STREAM_NAME] = USB_STRING_DESC("EC"),
+};
+
+BUILD_ASSERT(ARRAY_SIZE(usb_strings) == USB_STR_COUNT);
+
+
+/******************************************************************************
+ * Forward UARTs as a USB serial interface.
+ */
+
+#define USB_STREAM_RX_SIZE 32
+#define USB_STREAM_TX_SIZE 64
+
+/******************************************************************************
+ * Forward USART1 (EC) as a simple USB serial interface.
+ */
+
+static struct usart_config const usart1;
+struct usb_stream_config const usart1_usb;
+
+static struct queue const usart1_to_usb = QUEUE_DIRECT(128, uint8_t,
+ usart1.producer, usart1_usb.consumer);
+static struct queue const usb_to_usart1 = QUEUE_DIRECT(64, uint8_t,
+ usart1_usb.producer, usart1.consumer);
+
+static struct usart_rx_dma const usart1_rx_dma =
+ USART_RX_DMA(STM32_DMAC_CH5, 32);
+
+static struct usart_config const usart1 =
+ USART_CONFIG(usart1_hw,
+ usart1_rx_dma.usart_rx,
+ usart_tx_interrupt,
+ 115200,
+ 0,
+ usart1_to_usb,
+ usb_to_usart1);
+
+USB_STREAM_CONFIG_USART_IFACE(usart1_usb,
+ USB_IFACE_USART1_STREAM,
+ USB_STR_USART1_STREAM_NAME,
+ USB_EP_USART1_STREAM,
+ USB_STREAM_RX_SIZE,
+ USB_STREAM_TX_SIZE,
+ usb_to_usart1,
+ usart1_to_usb,
+ usart1)
+
+
+/******************************************************************************
+ * Forward USART3 (CPU) as a simple USB serial interface.
+ */
+
+static struct usart_config const usart3;
+struct usb_stream_config const usart3_usb;
+
+static struct queue const usart3_to_usb = QUEUE_DIRECT(1024, uint8_t,
+ usart3.producer, usart3_usb.consumer);
+static struct queue const usb_to_usart3 = QUEUE_DIRECT(64, uint8_t,
+ usart3_usb.producer, usart3.consumer);
+
+static struct usart_rx_dma const usart3_rx_dma =
+ USART_RX_DMA(STM32_DMAC_CH3, 32);
+
+static struct usart_config const usart3 =
+ USART_CONFIG(usart3_hw,
+ usart3_rx_dma.usart_rx,
+ usart_tx_interrupt,
+ 115200,
+ 0,
+ usart3_to_usb,
+ usb_to_usart3);
+
+USB_STREAM_CONFIG_USART_IFACE(usart3_usb,
+ USB_IFACE_USART3_STREAM,
+ USB_STR_USART3_STREAM_NAME,
+ USB_EP_USART3_STREAM,
+ USB_STREAM_RX_SIZE,
+ USB_STREAM_TX_SIZE,
+ usb_to_usart3,
+ usart3_to_usb,
+ usart3)
+
+
+/******************************************************************************
+ * Forward USART4 (cr50) as a simple USB serial interface.
+ *
+ * We do not try to share DMA channel 6 with SPI2, so just use interrupts
+ */
+
+static struct usart_config const usart4;
+struct usb_stream_config const usart4_usb;
+
+static struct queue const usart4_to_usb = QUEUE_DIRECT(1024, uint8_t,
+ usart4.producer, usart4_usb.consumer);
+static struct queue const usb_to_usart4 = QUEUE_DIRECT(64, uint8_t,
+ usart4_usb.producer, usart4.consumer);
+
+static struct usart_config const usart4 =
+ USART_CONFIG(usart4_hw,
+ usart_rx_interrupt,
+ usart_tx_interrupt,
+ 115200,
+ 0,
+ usart4_to_usb,
+ usb_to_usart4);
+
+USB_STREAM_CONFIG_USART_IFACE(usart4_usb,
+ USB_IFACE_USART4_STREAM,
+ USB_STR_USART4_STREAM_NAME,
+ USB_EP_USART4_STREAM,
+ USB_STREAM_RX_SIZE,
+ USB_STREAM_TX_SIZE,
+ usb_to_usart4,
+ usart4_to_usb,
+ usart4)
+
+/******************************************************************************
+ * Set up SPI over USB
+ * Notes DMA Channel 6 is shared and mutually exclusive with USART4 RX
+ */
+
+/* SPI devices */
+const struct spi_device_t spi_devices[] = {
+ { CONFIG_SPI_FLASH_PORT, 1, GPIO_SPI_CSN},
+};
+const unsigned int spi_devices_used = ARRAY_SIZE(spi_devices);
+
+void usb_spi_board_enable(struct usb_spi_config const *config)
+{
+ /* Configure SPI GPIOs */
+ gpio_config_module(MODULE_SPI_FLASH, 1);
+
+ /* Set all four SPI pins to high speed */
+ STM32_GPIO_OSPEEDR(GPIO_B) |= 0xff000000;
+
+ /* Enable clocks to SPI2 module */
+ STM32_RCC_APB1ENR |= STM32_RCC_PB1_SPI2;
+
+ /* Reset SPI2 */
+ STM32_RCC_APB1RSTR |= STM32_RCC_PB1_SPI2;
+ STM32_RCC_APB1RSTR &= ~STM32_RCC_PB1_SPI2;
+
+ spi_enable(CONFIG_SPI_FLASH_PORT, 1);
+}
+
+void usb_spi_board_disable(struct usb_spi_config const *config)
+{
+ spi_enable(CONFIG_SPI_FLASH_PORT, 0);
+
+ /* Disable clocks to SPI2 module */
+ STM32_RCC_APB1ENR &= ~STM32_RCC_PB1_SPI2;
+
+ /* Release SPI GPIOs */
+ gpio_config_module(MODULE_SPI_FLASH, 0);
+
+ /* Reset all four SPI pins to low speed */
+ STM32_GPIO_OSPEEDR(GPIO_B) &= ~0xff000000;
+}
+
+USB_SPI_CONFIG(usb_spi, USB_IFACE_SPI, USB_EP_SPI,
+ USB_SPI_CONFIG_FLAGS_IGNORE_HOST_SIDE_ENABLE);
+
+/******************************************************************************
+ * Check parity setting on usarts.
+ */
+static int command_uart_parity(int argc, char **argv)
+{
+ int parity = 0, newparity;
+ struct usart_config const *usart;
+ char *e;
+
+ if ((argc < 2) || (argc > 3))
+ return EC_ERROR_PARAM_COUNT;
+
+ if (!strcasecmp(argv[1], "usart1"))
+ usart = &usart1;
+ else if (!strcasecmp(argv[1], "usart3"))
+ usart = &usart3;
+ else if (!strcasecmp(argv[1], "usart4"))
+ usart = &usart4;
+ else
+ return EC_ERROR_PARAM1;
+
+ if (argc == 3) {
+ parity = strtoi(argv[2], &e, 0);
+ if (*e || (parity < 0) || (parity > 2))
+ return EC_ERROR_PARAM2;
+
+ usart_set_parity(usart, parity);
+ }
+
+ newparity = usart_get_parity(usart);
+ ccprintf("Parity on %s is %d.\n", argv[1], newparity);
+
+ if ((argc == 3) && (newparity != parity))
+ return EC_ERROR_UNKNOWN;
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(parity, command_uart_parity,
+ "usart[2|3|4] [0|1|2]",
+ "Set parity on uart");
+
+/******************************************************************************
+ * Set baud rate setting on usarts.
+ */
+static int command_uart_baud(int argc, char **argv)
+{
+ int baud = 0;
+ struct usart_config const *usart;
+ char *e;
+
+ if ((argc < 2) || (argc > 3))
+ return EC_ERROR_PARAM_COUNT;
+
+ if (!strcasecmp(argv[1], "usart1"))
+ usart = &usart1;
+ else if (!strcasecmp(argv[1], "usart3"))
+ usart = &usart3;
+ else if (!strcasecmp(argv[1], "usart4"))
+ usart = &usart4;
+ else
+ return EC_ERROR_PARAM1;
+
+ baud = strtoi(argv[2], &e, 0);
+ if (*e || baud < 0)
+ return EC_ERROR_PARAM2;
+
+ usart_set_baud(usart, baud);
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(baud, command_uart_baud,
+ "usart[2|3|4] rate",
+ "Set baud rate on uart");
+
+/******************************************************************************
+ * Hold the usart pins low while disabling it, or return it to normal.
+ */
+static int command_hold_usart_low(int argc, char **argv)
+{
+ /* Each bit represents if that port is being held low */
+ static int usart_status;
+
+ int usart_mask;
+ enum gpio_signal rx;
+
+ if (argc > 3 || argc < 2)
+ return EC_ERROR_PARAM_COUNT;
+
+ if (!strcasecmp(argv[1], "usart1")) {
+ usart_mask = 1 << 1;
+ rx = GPIO_UART_EC_TX_DBG_RX_SDA;
+ } else if (!strcasecmp(argv[1], "usart3")) {
+ usart_mask = 1 << 3;
+ rx = GPIO_UART_AP_TX_DBG_RX_INA_SDA;
+ } else if (!strcasecmp(argv[1], "usart4")) {
+ usart_mask = 1 << 4;
+ rx = GPIO_UART_H1_TX_DBG_RX;
+ } else {
+ return EC_ERROR_PARAM1;
+ }
+
+ /* Updating the status of this port */
+ if (argc == 3) {
+ char *e;
+ const int hold_low = strtoi(argv[2], &e, 0);
+
+ if (*e || (hold_low < 0) || (hold_low > 1))
+ return EC_ERROR_PARAM2;
+
+ mutex_lock(&vref_uart_state_mutex);
+
+ if (uart_state & UART_STATE_SPI_MODE) {
+ ccprintf("Cannot hold USART while in SPI mode\n");
+ goto busy_error_unlock;
+ }
+
+ if (!!(usart_status & usart_mask) == hold_low) {
+ /* Do nothing since there is no change */
+ } else if (hold_low) {
+ /*
+ * No need to shutdown UART, just de-mux the RX pin from
+ * UART and change it to a GPIO temporarily
+ */
+ gpio_config_pin(MODULE_USART, rx, 0);
+ gpio_set_flags(rx, GPIO_OUT_LOW);
+
+ /* Update global uart state */
+ usart_status |= usart_mask;
+ uart_state |= UART_STATE_HELD;
+ } else {
+ /*
+ * Mux the RX pin back to GPIO mode
+ */
+ gpio_config_pin(MODULE_USART, rx, 1);
+
+ /* Update global uart state */
+ usart_status &= ~usart_mask;
+ uart_state &= ~UART_STATE_HELD;
+ }
+
+ mutex_unlock(&vref_uart_state_mutex);
+ }
+
+ /* Print status for get and set case. */
+ ccprintf("USART status: %s\n",
+ usart_status & usart_mask ? "held low" : "normal");
+
+ return EC_SUCCESS;
+
+busy_error_unlock:
+ mutex_unlock(&vref_uart_state_mutex);
+ return EC_ERROR_BUSY;
+}
+DECLARE_CONSOLE_COMMAND(hold_usart_low, command_hold_usart_low,
+ "usart[1|3|4] [0|1]?",
+ "Get/set the hold-low state for usart port");
+
+
+/******************************************************************************
+ * Console commands SPI programming
+ */
+
+
+
+enum vref {
+ OFF = 0,
+ PP1800 = 1800,
+ PP3300 = 3300,
+};
+
+static int command_enable_spi(int argc, char **argv)
+{
+ static enum vref current_spi_vref_state;
+
+ if (argc > 2)
+ return EC_ERROR_PARAM_COUNT;
+
+ /* Updating the state */
+ if (argc == 2) {
+ char *e;
+ const enum vref spi_vref = strtoi(argv[1], &e, 0);
+
+ if (*e)
+ return EC_ERROR_PARAM1;
+ if (spi_vref != OFF && spi_vref != PP1800 && spi_vref != PP3300)
+ return EC_ERROR_PARAM1;
+
+ mutex_lock(&vref_uart_state_mutex);
+
+ if (uart_state & UART_STATE_HELD) {
+ ccprintf("Cannot update SPI with UART held.\n");
+ goto busy_error_unlock;
+ }
+
+ if (vref_monitor_disable & ~VREF_MON_DIS_SPI_MODE) {
+ ccprintf("Cannot update SPI with reset held.\n");
+ goto busy_error_unlock;
+ }
+
+ if (current_spi_vref_state == spi_vref) {
+ /* No change, do nothing */
+ } else if (spi_vref == OFF) {
+ /* We are transitioning from SPI to UART mode: */
+ /* Disable level shifter pass through */
+ gpio_set_level(GPIO_EN_MISO_MOSI_H1_UART, 0);
+ gpio_set_level(GPIO_EN_CLK_CSN_EC_UART, 0);
+
+ /* Disable SPI. Sets SPI pins to inputs. */
+ usb_spi_enable(&usb_spi, 0);
+
+ /* Set default state for chip select */
+ gpio_set_flags(GPIO_SPI_CSN, GPIO_INPUT);
+
+ /* Re-enable all UARTs pins as UART alternate mode. */
+ gpio_config_module(MODULE_USART, 1);
+
+ /* Ensure DUT's muxes are switched to UART mode */
+ gpio_set_level(GPIO_C2D2_MUX_UART_ODL, 0);
+
+ /* Update state and defer Vrefs update */
+ vref_monitor_disable &= ~VREF_MON_DIS_SPI_MODE;
+ uart_state &= ~UART_STATE_SPI_MODE;
+ hook_call_deferred(&update_vrefs_and_shifters_data, 0);
+ } else if (vref_monitor_disable & VREF_MON_DIS_SPI_MODE) {
+ /* We are just changing voltages */
+ gpio_set_level(GPIO_SEL_SPIVREF_H1VREF_3V3,
+ spi_vref == PP3300);
+ gpio_set_level(GPIO_SEL_SPIVREF_ECVREF_3V3,
+ spi_vref == PP3300);
+ } else {
+ /* We are transitioning from UART to SPI mode: */
+ /* Turn off comparator interrupt for Vref detection */
+ STM32_EXTI_IMR &= ~EXTI_COMP2_EVENT;
+
+ /* Disable level shifters to avoid glitching output */
+ gpio_set_level(GPIO_EN_MISO_MOSI_H1_UART, 0);
+ gpio_set_level(GPIO_EN_CLK_CSN_EC_UART, 0);
+
+ /*
+ * De-select UART on all UARTs pins to avoid drive
+ * fights with SPI pins.
+ */
+ gpio_config_module(MODULE_USART, 0);
+
+ /* Set default state for chip select */
+ gpio_set_flags(GPIO_SPI_CSN, GPIO_OUT_HIGH);
+
+ /* Enable SPI. Sets SPI pins to SPI alternate mode. */
+ usb_spi_enable(&usb_spi, 1);
+
+ /* Set requested Vref voltage */
+ gpio_set_level(GPIO_SEL_SPIVREF_H1VREF_3V3,
+ spi_vref == PP3300);
+ gpio_set_level(GPIO_SEL_SPIVREF_ECVREF_3V3,
+ spi_vref == PP3300);
+
+ /* Ensure DUT's muxes are switched to SPI mode */
+ gpio_set_level(GPIO_C2D2_MUX_UART_ODL, 1);
+
+ /* Enable level shifters passthrough */
+ gpio_set_level(GPIO_EN_MISO_MOSI_H1_UART, 1);
+ gpio_set_level(GPIO_EN_CLK_CSN_EC_UART, 1);
+
+ vref_monitor_disable |= VREF_MON_DIS_SPI_MODE;
+ uart_state |= UART_STATE_SPI_MODE;
+ }
+
+ current_spi_vref_state = spi_vref;
+
+ mutex_unlock(&vref_uart_state_mutex);
+ }
+
+ /* Print status for get and set case. */
+ ccprintf("SPI Vref: %d\n", current_spi_vref_state);
+
+ return EC_SUCCESS;
+
+busy_error_unlock:
+ mutex_unlock(&vref_uart_state_mutex);
+ return EC_ERROR_BUSY;
+}
+DECLARE_CONSOLE_COMMAND(enable_spi, command_enable_spi,
+ "[0|1800|3300]?",
+ "Get/set the SPI Vref");
+
+/******************************************************************************
+ * Console commands for asserting H1 reset and EC Power button
+ */
+
+static int command_vref_alternate(int argc, char **argv,
+ const enum gpio_signal vref_signal,
+ const enum gpio_signal en_signal,
+ const int state_flag,
+ const char *const print_name)
+{
+ if (argc > 2)
+ return EC_ERROR_PARAM_COUNT;
+
+ /* Updating the state */
+ if (argc == 2) {
+ char *e;
+ const int hold_low = strtoi(argv[1], &e, 0);
+
+ if (*e || (hold_low < 0) || (hold_low > 1))
+ return EC_ERROR_PARAM1;
+
+ mutex_lock(&vref_uart_state_mutex);
+
+ if (vref_monitor_disable & VREF_MON_DIS_SPI_MODE) {
+ ccprintf("Cannot hold pin while in SPI mode.\n");
+ goto busy_error_unlock;
+ }
+
+ if (!!(vref_monitor_disable & state_flag) == hold_low) {
+ /* No change, do nothing */
+ } else if (hold_low) {
+ /* Turn off comparator interrupt for vref detection */
+ STM32_EXTI_IMR &= ~EXTI_COMP2_EVENT;
+ /* Start holding the ODL signal line low */
+ gpio_set_flags(vref_signal, GPIO_OUT_LOW);
+ /* Ensure the switch is connecting STM to DUT */
+ gpio_set_level(en_signal, 1);
+ vref_monitor_disable |= state_flag;
+ } else {
+ /* Return GPIO back to input for vref detection */
+ gpio_set_flags(vref_signal, GPIO_INPUT);
+ /* Transitioning out of hold, correct vrefs */
+ hook_call_deferred(&update_vrefs_and_shifters_data, 0);
+ vref_monitor_disable &= ~state_flag;
+ }
+
+ mutex_unlock(&vref_uart_state_mutex);
+ }
+
+ /* Print status for both get and set case */
+ ccprintf("%s held: %s\n", print_name,
+ vref_monitor_disable & state_flag ? "yes" : "no");
+
+
+ return EC_SUCCESS;
+
+busy_error_unlock:
+ mutex_unlock(&vref_uart_state_mutex);
+ return EC_ERROR_BUSY;
+}
+
+static int command_pwr_button(int argc, char **argv)
+{
+ return command_vref_alternate(argc, argv,
+ GPIO_SPIVREF_HOLDN_ECVREF_H1_PWRBTN_ODL,
+ GPIO_EN_SPIVREF_HOLDN_ECVREF_H1_PWRBTN,
+ VREF_MON_DIS_EC_PWR_HELD, "Power button");
+}
+DECLARE_CONSOLE_COMMAND(pwr_button, command_pwr_button,
+ "[0|1]?",
+ "Get/set the power button state");
+
+static int command_h1_reset(int argc, char **argv)
+{
+ return command_vref_alternate(argc, argv,
+ GPIO_SPIVREF_RSVD_H1VREF_H1_RST_ODL,
+ GPIO_EN_SPIVREF_RSVD_H1VREF_H1_RST,
+ VREF_MON_DIS_H1_RST_HELD, "H1 reset");
+}
+DECLARE_CONSOLE_COMMAND(h1_reset, command_h1_reset,
+ "[0|1]?",
+ "Get/set the h1 reset state");
+
+/******************************************************************************
+ * Vref detection logic
+ */
+
+/* Voltage thresholds for rail detection */
+#define VREF_3300_MIN_MV 2300
+#define VREF_1800_MIN_MV 1500
+
+static enum vref get_vref(enum adc_channel chan)
+{
+ const int adc = adc_read_channel(chan);
+
+ if (adc == ADC_READ_ERROR)
+ return OFF;
+ else if (adc > VREF_3300_MIN_MV)
+ return PP3300;
+ else if (adc > VREF_1800_MIN_MV)
+ return PP1800;
+ else
+ return OFF;
+}
+
+static inline void drain_vref_lines(void)
+{
+ mutex_lock(&vref_uart_state_mutex);
+ if (vref_monitor_disable) {
+ mutex_unlock(&vref_uart_state_mutex);
+ return;
+ }
+
+ /*
+ * Disconnect level shifters to prevent any leakage on DUT side while we
+ * are draining Vref lines for a proper read.
+ */
+ gpio_set_level(GPIO_EN_MISO_MOSI_H1_UART, 0);
+ gpio_set_level(GPIO_EN_CLK_CSN_EC_UART, 0);
+
+ /* Disconnect Vref switches */
+ gpio_set_level(GPIO_EN_SPIVREF_RSVD_H1VREF_H1_RST, 0);
+ gpio_set_level(GPIO_EN_SPIVREF_HOLDN_ECVREF_H1_PWRBTN, 0);
+
+ /* Actively pull down floating voltage */
+ gpio_set_flags(GPIO_SPIVREF_RSVD_H1VREF_H1_RST_ODL, GPIO_OUT_LOW);
+ gpio_set_flags(GPIO_SPIVREF_HOLDN_ECVREF_H1_PWRBTN_ODL, GPIO_OUT_LOW);
+
+ /* Ensure we have enough time to drain line. Not in mutex */
+ mutex_unlock(&vref_uart_state_mutex);
+ msleep(5);
+ mutex_lock(&vref_uart_state_mutex);
+ if (vref_monitor_disable) {
+ mutex_unlock(&vref_uart_state_mutex);
+ /*
+ * One or both of the Vref signals will still be low. This is
+ * okay since anyone that just took over these signal will
+ * also take over the enabled switch signals appropriately.
+ *
+ * If no one takes over the Vref signal, then the switch will
+ * remain off and we won't pull down the DUT side.
+ */
+ return;
+ }
+
+ /* Reset Vref GPIOs back to input for Vref detection */
+ gpio_set_flags(GPIO_SPIVREF_RSVD_H1VREF_H1_RST_ODL, GPIO_INPUT);
+ gpio_set_flags(GPIO_SPIVREF_HOLDN_ECVREF_H1_PWRBTN_ODL, GPIO_INPUT);
+
+ /* Reconnect Vref switches */
+ gpio_set_level(GPIO_EN_SPIVREF_RSVD_H1VREF_H1_RST, 1);
+ gpio_set_level(GPIO_EN_SPIVREF_HOLDN_ECVREF_H1_PWRBTN, 1);
+
+ mutex_unlock(&vref_uart_state_mutex);
+ /* Ensure we have enough time to charge line up to real voltage */
+ msleep(10);
+}
+
+/* This if forward declared as a deferred function above */
+static void update_vrefs_and_shifters(void)
+{
+ static enum vref prev_h1_vref, prev_ec_vref;
+
+ enum vref h1_vref, ec_vref;
+ int adc_mv;
+
+ /* Disable Vref comparator interrupt before draining and measuring */
+ STM32_EXTI_IMR &= ~EXTI_COMP2_EVENT;
+
+ drain_vref_lines();
+
+ /* Ensure we aren't actively using Vref lines for other purposes */
+ mutex_lock(&vref_uart_state_mutex);
+ if (vref_monitor_disable) {
+ mutex_unlock(&vref_uart_state_mutex);
+ return;
+ }
+
+ /* Only get the EC Vref if H1 Vref is on */
+ h1_vref = get_vref(ADC_H1_SPI_VREF);
+ ec_vref = (h1_vref == OFF) ? OFF : get_vref(ADC_EC_SPI_VREF);
+
+ /*
+ * It is possible that the user is physically holding the power button
+ * while inserting the c2d2 connector on the DUT. In that case the
+ * EC Vref (shared with power button ODL) will be OFF while H1 Vref is
+ * on. We won't get a valid read on the EC Vref, so we just keep trying
+ * to read in the background until we get out of that state.
+ */
+ if (h1_vref != OFF && ec_vref == OFF) {
+ CPRINTS("Looks like DUT power button is held. Will try again.");
+ hook_call_deferred(&update_vrefs_and_shifters_data, 100 * MSEC);
+ }
+
+ /* Update C2D2 Vref and level shifters based on ADC Vref values */
+ gpio_set_level(GPIO_SEL_SPIVREF_H1VREF_3V3, h1_vref == PP3300);
+ gpio_set_level(GPIO_EN_MISO_MOSI_H1_UART, h1_vref != OFF);
+ gpio_set_level(GPIO_SEL_SPIVREF_ECVREF_3V3, ec_vref == PP3300);
+ gpio_set_level(GPIO_EN_CLK_CSN_EC_UART, ec_vref != OFF);
+
+ /* Set up DAC2 for comparison on H1 Vref */
+ adc_mv = (h1_vref == PP3300) ? VREF_3300_MIN_MV : VREF_1800_MIN_MV;
+ /* 8-bit DAC based off of 3.3V rail */
+ STM32_DAC_DHR8R2 = 256 * adc_mv / 3300;
+
+ /* Clear any pending interrupts and enabled H1 Vref comparator */
+ STM32_EXTI_PR = EXTI_COMP2_EVENT;
+ STM32_EXTI_IMR |= EXTI_COMP2_EVENT;
+
+ mutex_unlock(&vref_uart_state_mutex);
+
+ if (prev_h1_vref != h1_vref || prev_ec_vref != ec_vref)
+ CPRINTS("Vref updated. H1: %d -> %d; EC: %d -> %d",
+ prev_h1_vref, h1_vref, prev_ec_vref, ec_vref);
+
+ /*
+ * Transitioning from 3.3V to 1.8V should not happen and most likely
+ * indicates a leakage path on the DUT being backpowered from C2D2 or
+ * something else.
+ */
+ if (prev_h1_vref == PP3300 && h1_vref == PP1800)
+ CPRINTS("Check for H1 Leakage!!!");
+ if (prev_ec_vref == PP3300 && ec_vref == PP1800)
+ CPRINTS("Check for EC Leakage!!!");
+ prev_h1_vref = h1_vref;
+ prev_ec_vref = ec_vref;
+}
+
+void set_up_comparator(void)
+{
+ /* Overwrite any previous values. This is the only comparator usage */
+ STM32_COMP_CSR = STM32_COMP_CMP2HYST_HI |
+ STM32_COMP_CMP2OUTSEL_NONE |
+ STM32_COMP_CMP2INSEL_INM5 | /* Watch DAC_OUT2 (PA5) */
+ STM32_COMP_CMP2MODE_LSPEED |
+ STM32_COMP_CMP2EN;
+
+ /* Set Falling and Rising interrupts for COMP2 */
+ STM32_EXTI_FTSR |= EXTI_COMP2_EVENT;
+ STM32_EXTI_RTSR |= EXTI_COMP2_EVENT;
+
+ /* Interrupt for COMP2 enabled when setting Vrefs */
+
+ /* Ensure IRQ will get called when comp module enables interrupt */
+ task_enable_irq(STM32_IRQ_COMP);
+}
+
+static void h1_vref_change(void)
+{
+ /* Ack the interrupt */
+ STM32_EXTI_PR = EXTI_COMP2_EVENT;
+
+ /* Disable interrupt, setting Vref will enable again */
+ STM32_EXTI_IMR &= ~EXTI_COMP2_EVENT;
+
+ hook_call_deferred(&update_vrefs_and_shifters_data, 0);
+}
+DECLARE_IRQ(STM32_IRQ_COMP, h1_vref_change, 1);
+
+/******************************************************************************
+ * Initialize board.
+ */
+static void board_init(void)
+{
+ /* USB to serial queues */
+ queue_init(&usart1_to_usb);
+ queue_init(&usb_to_usart1);
+ queue_init(&usart3_to_usb);
+ queue_init(&usb_to_usart3);
+ queue_init(&usart4_to_usb);
+ queue_init(&usb_to_usart4);
+
+ /* UART init */
+ usart_init(&usart1);
+ usart_init(&usart3);
+ usart_init(&usart4);
+
+ /* Enabled DAC, when setting Vref, this voltage is adjusted */
+ STM32_DAC_CR = STM32_DAC_CR_EN2;
+
+ /* Set Vrefs and enabled level shifters */
+ set_up_comparator();
+
+ /*
+ * Ensure we set up vrefs at least once. Don't call here because
+ * there are delays in the reads
+ */
+ hook_call_deferred(&update_vrefs_and_shifters_data, 0);
+}
+DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT);
+
+/******************************************************************************
+ * Turn down USART before jumping to RW.
+ */
+static void board_jump(void)
+{
+ /* Put the board into safer state while jumping */
+ gpio_set_level(GPIO_EN_SPIVREF_RSVD_H1VREF_H1_RST, 0);
+ gpio_set_level(GPIO_EN_SPIVREF_HOLDN_ECVREF_H1_PWRBTN, 0);
+ gpio_set_level(GPIO_EN_CLK_CSN_EC_UART, 0);
+ gpio_set_level(GPIO_EN_MISO_MOSI_H1_UART, 0);
+
+ /*
+ * Shutdown all UARTS before jumping to RW. They will be reinitialized
+ * after the jump is successful.
+ */
+ usart_shutdown(&usart1);
+ usart_shutdown(&usart3);
+ usart_shutdown(&usart4);
+
+ /* Ensure SPI2 is disabled as well */
+ usb_spi_enable(&usb_spi, 0);
+}
+DECLARE_HOOK(HOOK_SYSJUMP, board_jump, HOOK_PRIO_DEFAULT);
diff --git a/board/c2d2/board.h b/board/c2d2/board.h
new file mode 100644
index 0000000000..0eac431345
--- /dev/null
+++ b/board/c2d2/board.h
@@ -0,0 +1,122 @@
+/* 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.
+ */
+
+/* C2D2 configuration */
+
+#ifndef __CROS_EC_BOARD_H
+#define __CROS_EC_BOARD_H
+
+/*
+ * Allow dangerous commands all the time, since we don't have a write protect
+ * switch.
+ */
+#define CONFIG_SYSTEM_UNLOCKED
+
+/* 48 MHz SYSCLK clock frequency */
+#define CPU_CLOCK 48000000
+
+#define CONFIG_BOARD_PRE_INIT
+
+/* Enable USART */
+#define CONFIG_STREAM_USART
+#define CONFIG_STREAM_USART1 /* EC USART */
+#define CONFIG_STREAM_USART3 /* AP USART - not connected by default */
+#define CONFIG_STREAM_USART4 /* H1 USART */
+#define CONFIG_STREAM_USB
+#define CONFIG_CMD_USART_INFO
+
+/* The UART console is on USART2 (PA14/PA15) */
+#undef CONFIG_UART_CONSOLE
+#define CONFIG_UART_CONSOLE 2
+#undef CONFIG_UART_TX_DMA
+#undef CONFIG_UART_RX_DMA
+
+/* Optional features */
+#define CONFIG_STM_HWTIMER32
+#define CONFIG_HW_CRC
+
+/* USB Configuration */
+#define CONFIG_USB
+#define CONFIG_USB_CONSOLE
+#define CONFIG_USB_PID 0x5041
+#define CONFIG_USB_SERIALNO
+#define DEFAULT_SERIALNO "Uninitialized"
+#define CONFIG_USB_UPDATE
+
+
+/* USB interface indexes (use define rather than enum to expand them)
+ *
+ * Note these values are used in servo_interface.py for the 'interface' value
+ */
+#define USB_IFACE_USART4_STREAM 0 /* H1 */
+#define USB_IFACE_UPDATE 1
+#define USB_IFACE_SPI 2
+#define USB_IFACE_CONSOLE 3
+#define USB_IFACE_I2C 4
+#define USB_IFACE_USART3_STREAM 5 /* AP (not connected by default) */
+#define USB_IFACE_USART1_STREAM 6 /* EC */
+#define USB_IFACE_COUNT 7
+
+/* USB endpoint indexes (use define rather than enum to expand them) */
+#define USB_EP_CONTROL 0
+#define USB_EP_USART4_STREAM 1
+#define USB_EP_UPDATE 2
+#define USB_EP_SPI 3
+#define USB_EP_CONSOLE 4
+#define USB_EP_I2C 5
+#define USB_EP_USART3_STREAM 6
+#define USB_EP_USART1_STREAM 7
+#define USB_EP_COUNT 8
+
+/* Enable control of SPI over USB */
+#define CONFIG_USB_SPI
+#define CONFIG_SPI_MASTER
+#define CONFIG_SPI_FLASH_PORT 0 /* SPI2 is 0th in stm's SPI_REGS var */
+
+/*
+ * Set all ADC samples to take 239.5 clock cycles. This allows us to measure
+ * weakly driven signals like the H1 Vref.
+ */
+#define CONFIG_ADC_SAMPLE_TIME STM32_ADC_SMPR_239_5_CY
+
+/* Options features */
+#define CONFIG_ADC
+
+/* This is not actually an EC so disable some features. */
+#undef CONFIG_WATCHDOG_HELP
+#undef CONFIG_LID_SWITCH
+
+#ifndef __ASSEMBLER__
+
+/* Timer selection */
+#define TIM_CLOCK32 2
+#define TIM_ADC 3
+
+#include "gpio_signal.h"
+
+/* USB string indexes */
+enum usb_strings {
+ USB_STR_DESC,
+ USB_STR_VENDOR,
+ USB_STR_PRODUCT,
+ USB_STR_SERIALNO,
+ USB_STR_VERSION,
+ USB_STR_USART4_STREAM_NAME,
+ USB_STR_UPDATE_NAME,
+ USB_STR_CONSOLE_NAME,
+ USB_STR_I2C_NAME,
+ USB_STR_USART3_STREAM_NAME,
+ USB_STR_USART1_STREAM_NAME,
+ USB_STR_COUNT
+};
+
+enum adc_channel {
+ ADC_H1_SPI_VREF, /* Either H1 Vref or SPI Vref depending on mode */
+ ADC_EC_SPI_VREF, /* Either EC Vref or SPI Vref depending on mode */
+ ADC_CH_COUNT,
+};
+
+#endif /* !__ASSEMBLER__ */
+#endif /* __CROS_EC_BOARD_H */
diff --git a/board/c2d2/build.mk b/board/c2d2/build.mk
new file mode 100644
index 0000000000..559b6b8e95
--- /dev/null
+++ b/board/c2d2/build.mk
@@ -0,0 +1,13 @@
+# -*- makefile -*-
+# 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.
+#
+# Board specific files build
+
+# the IC is STmicro STM32F072CBU6TR
+CHIP:=stm32
+CHIP_FAMILY:=stm32f0
+CHIP_VARIANT:=stm32f07x
+
+board-y=board.o
diff --git a/board/c2d2/ec.tasklist b/board/c2d2/ec.tasklist
new file mode 100644
index 0000000000..c1fb169118
--- /dev/null
+++ b/board/c2d2/ec.tasklist
@@ -0,0 +1,11 @@
+/* Copyright 2016 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.
+ */
+
+/**
+ * See CONFIG_TASK_LIST in config.h for details.
+ */
+#define CONFIG_TASK_LIST \
+ TASK_ALWAYS(HOOKS, hook_task, NULL, VENTI_TASK_STACK_SIZE) \
+ TASK_ALWAYS(CONSOLE, console_task, NULL, VENTI_TASK_STACK_SIZE)
diff --git a/board/c2d2/gpio.inc b/board/c2d2/gpio.inc
new file mode 100644
index 0000000000..a6533b42b7
--- /dev/null
+++ b/board/c2d2/gpio.inc
@@ -0,0 +1,50 @@
+/* -*- mode:c -*-
+ *
+ * Copyright 2019 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.
+ */
+
+GPIO(UART_DBG_TX_H1_RX, PIN(A, 0), GPIO_INPUT)
+GPIO(UART_H1_TX_DBG_RX, PIN(A, 1), GPIO_INPUT)
+GPIO(EN_MISO_MOSI_H1_UART, PIN(A, 2), GPIO_OUT_LOW)
+GPIO(SPIVREF_RSVD_H1VREF_H1_RST_ODL, PIN(A, 3), GPIO_INPUT)
+GPIO(SPIVREF_HOLDN_ECVREF_H1_PWRBTN_ODL, PIN(A, 4), GPIO_INPUT)
+GPIO(EN_CLK_CSN_EC_UART, PIN(A, 7), GPIO_OUT_LOW)
+
+GPIO(EN_SPIVREF_RSVD_H1VREF_H1_RST, PIN(B, 2), GPIO_OUT_LOW)
+GPIO(EN_SPIVREF_HOLDN_ECVREF_H1_PWRBTN, PIN(B, 3), GPIO_OUT_LOW)
+GPIO(SEL_SPIVREF_H1VREF_3V3, PIN(B, 4), GPIO_OUT_LOW)
+GPIO(SEL_SPIVREF_ECVREF_3V3, PIN(B, 5), GPIO_OUT_LOW)
+GPIO(UART_DBG_TX_EC_RX_SCL, PIN(B, 6), GPIO_INPUT)
+GPIO(UART_EC_TX_DBG_RX_SDA, PIN(B, 7), GPIO_INPUT)
+/* Start C2D2 in UART mode */
+GPIO(C2D2_MUX_UART_ODL, PIN(B, 8), GPIO_ODR_LOW)
+
+/* I2C pins should be configured as inputs until I2C module is */
+/* initialized. This will avoid driving the lines unintentionally.*/
+GPIO(UART_DBG_TX_AP_RX_INA_SCL, PIN(B, 10), GPIO_INPUT)
+GPIO(UART_AP_TX_DBG_RX_INA_SDA, PIN(B, 11), GPIO_INPUT)
+
+/* Flash SPI interface */
+GPIO(SPI_CSN, PIN(B, 12), GPIO_INPUT)
+GPIO(SPI_CLK, PIN(B, 13), GPIO_INPUT)
+GPIO(SPI_MISO, PIN(B, 14), GPIO_INPUT)
+GPIO(SPI_MOSI, PIN(B, 15), GPIO_INPUT)
+
+/* Unimplemented signals since we are not an EC */
+UNIMPLEMENTED(ENTERING_RW)
+UNIMPLEMENTED(WP_L)
+
+/* Default alternate mode pins */
+ALTERNATE(PIN_MASK(A, GENMASK(15, 14)), 1, MODULE_UART, 0) /* USART2: PA14/PA15 - Servo stm32 console UART*/
+
+ALTERNATE(PIN_MASK(B, GENMASK( 7, 6)), 0, MODULE_USART, 0) /* USART1: PB6/PB7 - Servo UART1 (EC) */
+ALTERNATE(PIN_MASK(B, GENMASK(11, 10)), 4, MODULE_USART, 0) /* USART3: PB10/PB11 - Servo UART2 (AP) */
+ALTERNATE(PIN_MASK(A, GENMASK( 1, 0)), 4, MODULE_USART, 0) /* USART4: PA0/PA1 - Servo UART3 (H1) */
+
+/*
+ * Note BIT(12) is intentionally not marked for alternate mode since it is
+ * directly controlled with gpio_set_level and configured in the spi driver.
+ */
+ALTERNATE(PIN_MASK(B, GENMASK(15, 13)), 0, MODULE_SPI_FLASH, 0) /* SPI: PB15 - PB12 MOSI, MISO, CLK, CS */
diff --git a/extra/usb_updater/c2d2.json b/extra/usb_updater/c2d2.json
new file mode 100644
index 0000000000..79fc6f0992
--- /dev/null
+++ b/extra/usb_updater/c2d2.json
@@ -0,0 +1,15 @@
+{
+ "Comment": "This file describes the updateable sections of the flash.",
+ "board": "c2d2",
+ "vid": "0x18d1",
+ "pid": "0x5041",
+ "console": "3",
+ "Comment on flash": "This is the base address of writeable flash",
+ "flash": "0x8000000",
+ "Comment on region format": "name: [baseoffset, length]",
+ "regions": {
+ "RW": ["0x10000", "0x10000"],
+ "PSTATE": ["0xf000", "0x1000"],
+ "RO": ["0x0000", "0xf000"]
+ }
+}
diff --git a/extra/usb_updater/servo_updater.py b/extra/usb_updater/servo_updater.py
index c819230f21..7ef16658fd 100755
--- a/extra/usb_updater/servo_updater.py
+++ b/extra/usb_updater/servo_updater.py
@@ -200,7 +200,7 @@ def findfiles(cname, fname):
if not fname:
# If None, try defaults.
dev = None
- for default_f in ['servo_v4', 'servo_micro', 'sweetberry']:
+ for default_f in ['c2d2', 'servo_micro', 'servo_v4', 'sweetberry']:
if default_f in cname:
dev = default_f
if os.path.isfile(FIRMWARE_PATH + dev + ".bin"):
diff --git a/setup.py b/setup.py
index cf45267f37..cb75210b22 100644
--- a/setup.py
+++ b/setup.py
@@ -39,7 +39,8 @@ setup(
"console_scripts": ["servo_updater=servo_updater:main"],
},
data_files=[("share/servo_updater/configs",
- ["extra/usb_updater/servo_v4.json",
+ ["extra/usb_updater/c2d2.json",
+ "extra/usb_updater/servo_v4.json",
"extra/usb_updater/servo_micro.json",
"extra/usb_updater/sweetberry.json"])],
description="Servo usb updater.",
diff --git a/util/flash_ec b/util/flash_ec
index 70de72000a..1cead158a9 100755
--- a/util/flash_ec
+++ b/util/flash_ec
@@ -88,6 +88,7 @@ BOARDS_STM32_PROG_EN=(
)
BOARDS_STM32_DFU=(
+ c2d2
dingdong
hoho
twinkie
@@ -400,6 +401,11 @@ servo_ec_hard_reset() {
dut_control cold_reset:off
}
+c2d2_ec_hard_reset() {
+ dut_control cold_reset:on
+ dut_control cold_reset:off
+}
+
servo_usbpd_hard_reset() {
dut_control usbpd_reset:on sleep:0.5 usbpd_reset:off
}
@@ -461,6 +467,10 @@ servo_ec_boot0() {
dut_control ec_boot_mode:$1
}
+c2d2_ec_boot0() {
+ dut_control ec_boot_mode_uut:$1
+}
+
servo_usbpd_boot0() {
dut_control usbpd_boot_mode:$1
}
@@ -676,6 +686,7 @@ function servo_ec_uart() {
servo_v2_VARS=( "cold_reset" )
servo_micro_VARS=( "cold_reset" )
servo_v4_with_ccd_cr50_VARS=( "cold_reset" )
+c2d2_VARS=( "cold_reset" )
# Some servo boards use the same controls.
servo_v3_VARS=( "${servo_v2_VARS[@]}" )
@@ -1313,8 +1324,9 @@ function flash_npcx_uut() {
ec_enable_boot0 "uut"
ec_reset
- # Have to wait a bit for EC boot-up
- sleep 0.1
+ # Have to wait a bit for EC boot-up (twice in some cases when the cold
+ # reset is indirect through h1 reset).
+ sleep 0.2
# For CCD, disable the trigger pin for normal UART operation
ec_disable_boot0 "uut"