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>
This commit is contained in:
Jett Rink 2019-12-13 16:21:57 -07:00 committed by Commit Bot
parent d79ee1cba0
commit e6d61bac16
9 changed files with 1098 additions and 4 deletions

870
board/c2d2/board.c Normal file
View File

@ -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);

122
board/c2d2/board.h Normal file
View File

@ -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 */

13
board/c2d2/build.mk Normal file
View File

@ -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

11
board/c2d2/ec.tasklist Normal file
View File

@ -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)

50
board/c2d2/gpio.inc Normal file
View File

@ -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 */

View File

@ -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"]
}
}

View File

@ -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"):

View File

@ -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.",

View File

@ -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"