diff options
-rw-r--r-- | board/c2d2/board.c | 870 | ||||
-rw-r--r-- | board/c2d2/board.h | 122 | ||||
-rw-r--r-- | board/c2d2/build.mk | 13 | ||||
-rw-r--r-- | board/c2d2/ec.tasklist | 11 | ||||
-rw-r--r-- | board/c2d2/gpio.inc | 50 | ||||
-rw-r--r-- | extra/usb_updater/c2d2.json | 15 | ||||
-rwxr-xr-x | extra/usb_updater/servo_updater.py | 2 | ||||
-rw-r--r-- | setup.py | 3 | ||||
-rwxr-xr-x | util/flash_ec | 16 |
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"): @@ -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" |