diff options
Diffstat (limited to 'board/c2d2/board.c')
-rw-r--r-- | board/c2d2/board.c | 870 |
1 files changed, 870 insertions, 0 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); |