/* 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_chip.h"
#include "adc.h"
#include "common.h"
#include "console.h"
#include "ec_version.h"
#include "gpio.h"
#include "hooks.h"
#include "i2c.h"
#include "i2c_ite_flash_support.h"
#include "queue_policies.h"
#include "registers.h"
#include "spi.h"
#include "system.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);
static bool is_ec_i2c_enabled(void);

/* Global state tracking current pin configuration and operations */
static struct mutex vref_bus_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)

/*
 * Tracks if bus pins are locked by a function like UART holding, I2C,
 * or SPI.
 */
enum bus_lock {
	BUS_UNLOCKED,	/* Normal UART; pins available for other functions */
	BUS_UART_HELD,	/* UART locked to pins while holding RX low */
	BUS_SPI,	/* SPI locked to pins */
	BUS_I2C,	/* I2C bus locked to pins */
};
/* A0/A1 (H1 UART or SPI) */
enum bus_lock h1_pins;
/* B6/B7 (EC UART, EC I2C, or SPI) */
enum bus_lock ec_pins;
/* B10/B11 (AP UART, AUX I2C) */
enum bus_lock ap_pins;

static const char *lock_to_string(const enum bus_lock val)
{
	static const char *const names[] = {
		[BUS_UNLOCKED] = "UART",
		[BUS_UART_HELD] = "UART HELD",
		[BUS_SPI] = "SPI",
		[BUS_I2C] = "I2C",
	};

	if (val < 0 || val >= ARRAY_SIZE(names))
		return "UNKNOWN";

	return names[val];
}

static int command_bus_status(int argc, char **argv)
{
	if (argc > 1)
		return EC_ERROR_PARAM_COUNT;

	ccprintf("H1 pins: %s\n", lock_to_string(h1_pins));
	ccprintf("EC pins: %s\n", lock_to_string(ec_pins));
	ccprintf("AP pins: %s\n", lock_to_string(ap_pins));

	return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(bus_status, command_bus_status,
			"",
			"Gets the bus state for swappable pins");


/******************************************************************************
 ** Chip-specific board configuration
 */
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);

/******************************************************************************
 * Support I2C bridging over USB.
 */

/* I2C ports */
const struct i2c_port_t i2c_ports[] = {
	{
		.name = "ec",
		.port = I2C_PORT_EC,
		.kbps = 100,
		.scl = GPIO_UART_DBG_TX_EC_RX_SCL,
		.sda =  GPIO_UART_EC_TX_DBG_RX_SDA,
		.flags = I2C_PORT_FLAG_DYNAMIC_SPEED,
	},
	{
		.name = "aux",
		.port = I2C_PORT_AUX,
		.kbps = 100,
		.scl = GPIO_UART_DBG_TX_AP_RX_INA_SCL,
		.sda =  GPIO_UART_AP_TX_DBG_RX_INA_SDA,
		.flags = I2C_PORT_FLAG_DYNAMIC_SPEED,
	},
};
const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports);

/* Configure ITE flash support module */
const struct ite_dfu_config_t ite_dfu_config = {
	.i2c_port = I2C_PORT_EC,
	/* PB6/7 are connected to complement outputs of TIM16/17 */
	.use_complement_timer_channel = true,
	.access_allow = &is_ec_i2c_enabled,
	.scl = GPIO_UART_DBG_TX_EC_RX_SCL,
	.sda = GPIO_UART_EC_TX_DBG_RX_SDA,
};

/*
 * I2C is always enabled, but the i2c pins may not be muxed to DUT. We will
 * let the i2c transactions fail instead of using the USB endpoint disable
 * status.
 */
int usb_i2c_board_is_enabled(void) { return 1; }

/******************************************************************************
 * 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)
{
	enum bus_lock *bus;
	enum gpio_signal rx;

	if (argc > 3 || argc < 2)
		return EC_ERROR_PARAM_COUNT;

	if (!strcasecmp(argv[1], "usart1")) {
		bus = &ec_pins;
		rx = GPIO_UART_EC_TX_DBG_RX_SDA;
	} else if (!strcasecmp(argv[1], "usart3")) {
		bus = &ap_pins;
		rx = GPIO_UART_AP_TX_DBG_RX_INA_SDA;
	} else if (!strcasecmp(argv[1], "usart4")) {
		bus = &h1_pins;
		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_bus_state_mutex);

		if (hold_low && *bus != BUS_UART_HELD) {
			/* Ensure no other use of these pins */
			if (*bus != BUS_UNLOCKED) {
				ccprintf("Cannot hold low! Pins busy: %s.\n",
					 lock_to_string(*bus));
				goto busy_error_unlock;
			}

			/*
			 * 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 */
			*bus = BUS_UART_HELD;
		} else if (!hold_low && *bus == BUS_UART_HELD) {
			/*
			 * Mux the RX pin back to GPIO mode
			 */
			gpio_config_pin(MODULE_USART, rx, 1);

			/* Update global uart state */
			*bus = BUS_UNLOCKED;
		}

		mutex_unlock(&vref_bus_state_mutex);
	}

	/* Print status for get and set case. */
	ccprintf("USART status: %s\n",
			*bus == BUS_UART_HELD ? "held low" : "normal");

	return EC_SUCCESS;

busy_error_unlock:
	mutex_unlock(&vref_bus_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) {
		int i;
		char *e;
		const enum vref spi_vref = strtoi(argv[1], &e, 0);
		const enum gpio_signal uart_pins[] = {
			GPIO_UART_DBG_TX_H1_RX,
			GPIO_UART_H1_TX_DBG_RX,
			GPIO_UART_DBG_TX_EC_RX_SCL,
			GPIO_UART_EC_TX_DBG_RX_SDA,
		};

		if (*e)
			return EC_ERROR_PARAM1;
		if (spi_vref != OFF && spi_vref != PP1800 && spi_vref != PP3300)
			return EC_ERROR_PARAM1;

		mutex_lock(&vref_bus_state_mutex);

		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 we used. */
			for (i = 0; i < ARRAY_SIZE(uart_pins); ++i)
				gpio_config_pin(MODULE_USART, uart_pins[i], 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  */
			h1_pins = BUS_UNLOCKED;
			ec_pins = BUS_UNLOCKED;
			vref_monitor_disable &= ~VREF_MON_DIS_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 {
			/* Ensure no other use of these pins */
			if (h1_pins != BUS_UNLOCKED ||
			    ec_pins != BUS_UNLOCKED) {
				ccprintf(
					"Cannot enter SPI! H1 pins: %s; EC pins: %s.\n",
					lock_to_string(h1_pins),
					lock_to_string(ec_pins));
				goto busy_error_unlock;
			}

			/* 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 we are using to
			 * avoid drive fights with SPI pins.
			 */
			for (i = 0; i < ARRAY_SIZE(uart_pins); ++i)
				gpio_config_pin(MODULE_USART, uart_pins[i], 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);

			h1_pins = BUS_SPI;
			ec_pins = BUS_SPI;
			vref_monitor_disable |= VREF_MON_DIS_SPI_MODE;
		}

		current_spi_vref_state = spi_vref;

		mutex_unlock(&vref_bus_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_bus_state_mutex);
	return EC_ERROR_BUSY;
}
DECLARE_CONSOLE_COMMAND(enable_spi, command_enable_spi,
			"[0|1800|3300]?",
			"Get/set the SPI Vref");

/******************************************************************************
 * Console commands I2c programming mode
 */
static bool is_ec_i2c_enabled(void)
{
	return ec_pins == BUS_I2C;
}

static inline enum i2c_freq to_i2c_freq(int kbps)
{
	switch (kbps) {
	case 400:
		return I2C_FREQ_400KHZ;
	case 1000:
		return I2C_FREQ_1000KHZ;
	default:
		return I2C_FREQ_100KHZ;
	}
}

static inline int to_kbps(enum i2c_freq freq)
{
	switch (freq) {
	case I2C_FREQ_400KHZ:
		return 400;
	case I2C_FREQ_1000KHZ:
		return 1000;
	default:
		return 100;
	}
}

static int command_enable_i2c(int argc, char **argv)
{
	int i2c_index;
	enum bus_lock *bus;
	enum gpio_signal sda, scl;

	if (argc > 3 || argc < 2)
		return EC_ERROR_PARAM_COUNT;

	if (!strcasecmp(argv[1], "ec")) {
		bus = &ec_pins;
		i2c_index = I2C_PORT_EC;
		sda = GPIO_UART_EC_TX_DBG_RX_SDA;
		scl = GPIO_UART_DBG_TX_EC_RX_SCL;

	} else if (!strcasecmp(argv[1], "ap")) {
		bus = &ap_pins;
		i2c_index = I2C_PORT_AUX;
		sda = GPIO_UART_AP_TX_DBG_RX_INA_SDA;
		scl = GPIO_UART_DBG_TX_AP_RX_INA_SCL;
	} else {
		return EC_ERROR_PARAM1;
	}

	/* Updating the state */
	if (argc == 3) {
		char *e;
		const int speed = strtoi(argv[2], &e, 0);

		if (*e)
			return EC_ERROR_PARAM2;
		if (speed != 0 && speed != 100 && speed != 400 && speed != 1000)
			return EC_ERROR_PARAM2;

		mutex_lock(&vref_bus_state_mutex);

		if (speed != 0 && *bus != BUS_I2C) {
			/* Ensure no other use of these pins */
			if (*bus != BUS_UNLOCKED) {
				ccprintf("Cannot enable i2c! Pin busy: %s.\n",
					 lock_to_string(*bus));
				goto busy_error_unlock;
			}

			/* Change alternate mode to I2C */
			gpio_config_pin(MODULE_I2C, sda, 1);
			gpio_config_pin(MODULE_I2C, scl, 1);

			/* Update state */
			*bus = BUS_I2C;
		} else if (speed == 0 && *bus == BUS_I2C) {
			/* Update back to default UART mode */
			gpio_config_pin(MODULE_USART, sda, 1);
			gpio_config_pin(MODULE_USART, scl, 1);

			/* Update state */
			*bus = BUS_UNLOCKED;
		}

		mutex_unlock(&vref_bus_state_mutex);

		/* If we have a non-zero speed, then set frequency */
		if (speed)
			i2c_set_freq(i2c_index, to_i2c_freq(speed));
	}

	/* Print status for get and set case. */
	ccprintf("I2C speed kbps: %d\n",
		 *bus == BUS_I2C ? to_kbps(i2c_get_freq(i2c_index)) : 0);

	return EC_SUCCESS;

busy_error_unlock:
	mutex_unlock(&vref_bus_state_mutex);
	return EC_ERROR_BUSY;
}
DECLARE_CONSOLE_COMMAND(enable_i2c, command_enable_i2c,
			"[ec|ap] [0|100|400|1000]?",
			"Get/set the I2C speed in kbps for EC and AP pins");

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

/* Set by update and read by console command that polls for Vref presence */
static enum vref h1_vref;
static enum vref ec_vref;

static int command_h1_vref_present(int argc, char **argv)
{
	ccprintf("H1 Vref: %s\n", h1_vref ? "on" : "off");

	return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(h1_vref, command_h1_vref_present,
			"",
			"Get if the h1 vref is present");

/* 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_bus_state_mutex);
	if (vref_monitor_disable) {
		mutex_unlock(&vref_bus_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_bus_state_mutex);
	msleep(5);
	mutex_lock(&vref_bus_state_mutex);
	if (vref_monitor_disable) {
		mutex_unlock(&vref_bus_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_bus_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;

	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_bus_state_mutex);
	if (vref_monitor_disable) {
		mutex_unlock(&vref_bus_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_bus_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);
