#!/bin/bash

# Copyright (c) 2014 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.

SCRIPT="$(readlink -f "$0")"
SCRIPT_DIR="$(dirname "$SCRIPT")"

EC_DIR="$(readlink -f "${SCRIPT_DIR}/..")"
if [[ "$(basename "${EC_DIR}")" != "ec" ]]; then
	EC_DIR=
fi

# Loads script libraries.
. "/usr/share/misc/shflags" || exit 1

# Redirects tput to stderr, and drop any error messages.
tput2() {
	tput "$@" 1>&2 2>/dev/null || true
}

error() {
	tput2 bold && tput2 setaf 1
	echo "ERROR: $*" >&2
	tput2 sgr0
}


info() {
	tput2 bold && tput2 setaf 2
	echo "INFO: $*" >&2
	tput2 sgr0
}

warn() {
	tput2 bold && tput2 setaf 3
	echo "WARNING: $*" >&2
	tput2 sgr0
}

die() {
	[ -z "$*" ] || error "$@"
	exit 1
}


BOARDS_IT83XX=(
	bip
	glkrvp_ite
	it83xx_evb
	reef_it8320
)

BOARDS_LM4=(
	samus
)

BOARDS_STM32=(
	big
	blaze
	chell_pd
	coffeecake
	elm
	glados_pd
	hammer
	jerry
	kitty
	meowth_fp
	minimuffin
	nocturne_fp
	oak
	oak_pd
	pit
	plankton
	rainier
	samus_pd
	scarlet
	staff
	strago_pd
	wand
	whiskers
	zinger
)
BOARDS_STM32_PROG_EN=(
	plankton
)

BOARDS_STM32_DFU=(
	dingdong
	hoho
	twinkie
	discovery
	servo_v4
	servo_micro
	sweetberry
	polyberry
	stm32f446e-eval
	tigertail
)

BOARDS_NPCX_5M5G_JTAG=(
	npcx_evb
	npcx_evb_arm
)

BOARDS_NPCX_5M6G_JTAG=(
)

BOARDS_NPCX_7M6X_JTAG=(
)

BOARDS_NPCX_7M7X_JTAG=(
	npcx7_evb
)

BOARDS_NPCX_SPI=(
	coral
	eve
	fizz
	glkrvp
	gru
	kahlee
	kevin
        lux
	nami
	nautilus
	poppy
	reef
	soraka
	wheatley
)

BOARDS_NPCX_INT_SPI=(
	atlas
	grunt
	meowth
	nocturne
	phaser
	yorp
	zoombini
)

BOARDS_NPCX_UUT=(
	cheza
)

BOARDS_NRF51=(
	hadoken
)

BOARDS_MEC1322=(
	chell
	glados
	strago
)

BOARDS_SPI_1800MV=(
	coral
	gru
	kevin
	reef
)

BOARDS_RAIDEN=(
	coral
	eve
	fizz
	gru
	kevin
	nami
	nautilus
	poppy
	reef
	scarlet
	soraka
)

# Flags
DEFINE_string board "${DEFAULT_BOARD}" \
	"The board to run debugger on."
DEFINE_string chip "" \
	"The chip to run debugger on."
DEFINE_string image "" \
	"Full pathname of the EC firmware image to flash."
DEFINE_integer timeout 600 \
	"Timeout for flashing the EC, measured in seconds."
DEFINE_string offset "0" \
	"Offset where to program the image from."
DEFINE_integer port 9999 \
	"Port to communicate to servo on."
DEFINE_boolean raiden "${FLAGS_FALSE}" \
	"Use raiden_debug_spi programmer"
DEFINE_boolean ro "${FLAGS_FALSE}" \
	"Write only the read-only partition"
DEFINE_boolean verbose "${FLAGS_FALSE}" \
	"Verbose hw control logging"

# Parse command line
FLAGS_HELP="usage: $0 [flags]"
FLAGS "$@" || exit 1
eval set -- "${FLAGS_ARGV}"
if [[ $# -gt 0 ]]; then
	die "invalid arguments: \"$*\""
fi

set -e

if [ -z "${FLAGS_board}" -a -z "${FLAGS_chip}" ]; then
	die "should specify a board or a chip."
fi

DUT_CONTROL_CMD="dut-control --port=${FLAGS_port}"

function dut_control() {
	if [ "${FLAGS_verbose}" = ${FLAGS_TRUE} ]; then
		echo "$DUT_CONTROL_CMD $@" 1>&2
	fi

	$DUT_CONTROL_CMD "$@" >/dev/null
}

function get_servo_type() {
	if dut_control "servo_type" ; then
		$DUT_CONTROL_CMD servo_type | sed -e s/servo_type://
	fi
}

BOARD=${FLAGS_board}

in_array() {
	local n=$#
	local value=${!n}

	for (( i=1; i<$#; i++ )) do
		if [ "${!i}" == "${value}" ]; then
			return 0
		fi
	done
	return 1
}

if $(in_array "${BOARDS_LM4[@]}" "${BOARD}"); then
	CHIP="lm4"
elif $(in_array "${BOARDS_STM32[@]}" "${BOARD}"); then
	CHIP="stm32"
elif $(in_array "${BOARDS_STM32_DFU[@]}" "${BOARD}"); then
	CHIP="stm32_dfu"
elif $(in_array "${BOARDS_NPCX_5M5G_JTAG[@]}" "${BOARD}"); then
	CHIP="npcx_5m5g_jtag"
elif $(in_array "${BOARDS_NPCX_5M6G_JTAG[@]}" "${BOARD}"); then
	CHIP="npcx_5m6g_jtag"
elif $(in_array "${BOARDS_NPCX_7M6X_JTAG[@]}" "${BOARD}"); then
	CHIP="npcx_7m6x_jtag"
elif $(in_array "${BOARDS_NPCX_7M7X_JTAG[@]}" "${BOARD}"); then
	CHIP="npcx_7m7x_jtag"
elif $(in_array "${BOARDS_NPCX_SPI[@]}" "${BOARD}"); then
	CHIP="npcx_spi"
elif $(in_array "${BOARDS_NPCX_INT_SPI[@]}" "${BOARD}"); then
	CHIP="npcx_spi"
elif $(in_array "${BOARDS_NPCX_UUT[@]}" "${BOARD}"); then
	CHIP="npcx_uut"
elif $(in_array "${BOARDS_NRF51[@]}" "${BOARD}"); then
	CHIP="nrf51"
elif $(in_array "${BOARDS_MEC1322[@]}" "${BOARD}"); then
	CHIP="mec1322"
elif $(in_array "${BOARDS_IT83XX[@]}" "${BOARD}"); then
	CHIP="it83xx"
elif [ -n "${FLAGS_chip}" ]; then
	CHIP="${FLAGS_chip}"
else
	die "board ${BOARD} not supported"
fi

if [ -n "${FLAGS_chip}" -a "${CHIP}" != "${FLAGS_chip}" ]; then
	die "board ${BOARD} doesn't use chip ${FLAGS_chip}"
fi

if [ "${CHIP}" = "stm32_dfu" -o "${CHIP}" = "it83xx" ]; then
	NEED_SERVO="no"
fi

servo_has_warm_reset() {
	dut_control warm_reset >/dev/null 2>&1
}

servo_has_cold_reset() {
	dut_control cold_reset >/dev/null 2>&1
}

# reset the EC
toad_ec_hard_reset() {
	if dut_control cold_reset 2>/dev/null ; then
		dut_control cold_reset:on
		dut_control cold_reset:off
	else
		info "you probably need to hard-reset your EC manually"
	fi
}

servo_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
}

servo_sh_hard_reset() {
	dut_control sh_reset:on
	dut_control sh_reset:off
}

ccd_cr50_ec_hard_reset() {
	servo_ec_hard_reset
}

ec_reset() {
	stype=${SERVO_TYPE}
	if [[ "${SERVO_TYPE}" =~ "servo" ]] ; then
		stype=servo
	fi

	if [[ ! -z "${stype}" ]]; then
		eval ${stype}_${MCU}_hard_reset
	fi
}

# force the EC to boot in serial monitor mode
toad_ec_boot0() {
	dut_control boot_mode:yes
}

ccd_ec_boot0() {
	info "Using CCD $1."
	dut_control ccd_ec_boot_mode_$1:on
}

servo_ec_boot0() {
	dut_control ec_boot_mode:on
}

servo_usbpd_boot0() {
	dut_control usbpd_boot_mode:on
}

servo_sh_boot0() {
	dut_control sh_boot_mode:on
}

ec_enable_boot0() {
	# Enable programming GPIOs
	if $(in_array "${BOARDS_STM32_PROG_EN[@]}" "${BOARD}"); then
		dut_control prog_en:yes
	fi
	if [[ "${SERVO_TYPE}" =~ "ccd_cr50" ]] ; then
		stype=ccd
	elif [[ "${SERVO_TYPE}" =~ "servo" ]] ; then
		stype=servo
	else
		stype=${SERVO_TYPE}
	fi
	eval ${stype}_${MCU}_boot0 $1
}

# Returns 0 on success (if on beaglebone)
on_servov3() {
	grep -qs '^CHROMEOS_RELEASE_BOARD=beaglebone_servo' /etc/lsb-release
}

# Returns 0 on success (if raiden should be used instead of servo)
error_reported=  # Avoid double printing the error message.
on_raiden() {
	if [[ "${SERVO_TYPE}" =~ "servo_v4" ]] || \
	   [[ "${SERVO_TYPE}" =~ "ccd_cr50" ]] || \
	   [[ "${SERVO_TYPE}" =~ "servo_micro" ]]; then
		return 0
	fi
	if [ -z "${BOARD}" ]; then
		[ "${FLAGS_raiden}" = ${FLAGS_TRUE} ] && return 0 || return 1
	fi
	if [ "${FLAGS_raiden}" = ${FLAGS_TRUE} ]; then
		if in_array "${BOARDS_RAIDEN[@]}" "${BOARD}"; then
			return 0
		fi
		if [ -z "${error_reported}" ]; then
			error_reported="y"
			die "raiden mode not supported on ${BOARD}" >&2
		fi
	fi
	return 1
}

# Put back the servo and the system in a clean state at exit
FROZEN_PIDS=""
cleanup() {
	if [ -n "${save}" ]; then
		info "Restoring servo settings..."
		servo_restore "$save"
	fi

	for pid in ${FROZEN_PIDS}; do
		info "Sending SIGCONT to process ${pid}!"
		kill -CONT ${pid}
	done

	if [ "${CHIP}" == "it83xx" ] ; then
		info "Reinitialize ftdi_i2c interface"
		dut_control --ftdii2c init
		dut_control --ftdii2c open
		dut_control --ftdii2c setclock

		# Reset the dut mux if it exists
		if dut_control -i dut_i2c_mux 2>/dev/null; then
			# Ensure servo i2c mux is on so we can talk to dut
			dut_control i2c_mux_en:on
			dut_control i2c_mux:remote_adc

			# Turn off dut i2c mux
			dut_control dut_i2c_mux:none

			# Turn off servo i2c mux
			dut_control i2c_mux_en:off
		fi

	fi
	if [[ "${SERVO_TYPE}" =~ "ccd_cr50" ]] ; then
		dut_control ccd_ec_boot_mode_uut:off
		dut_control ccd_ec_boot_mode_bitbang:off
		dut_control ${MCU}_uart_bitbang_en:off
	fi

	if ! on_raiden || $(servo_has_cold_reset); then
		ec_reset
	fi
}
trap cleanup EXIT

# Possible default EC images
if [ "${FLAGS_ro}" = ${FLAGS_TRUE} ] ; then
	EC_FILE=ec.RO.flat
else
	EC_FILE=ec.bin
fi


LOCAL_BUILD=
if [[ -n "${EC_DIR}" ]]; then
	if [ "${FLAGS_ro}" = ${FLAGS_TRUE} ] ; then
		LOCAL_BUILD="${EC_DIR}/build/${BOARD}/RO/${EC_FILE}"
	else
		LOCAL_BUILD="${EC_DIR}/build/${BOARD}/${EC_FILE}"
	fi
fi

# Get baseboard from build system if present
BASEBOARD=
if [[ -n "${EC_DIR}" ]]; then
	BASEBOARD=$(make --quiet -C ${EC_DIR} BOARD=${BOARD} print-baseboard)
elif [[ -d "${HOME}/trunk/src/platform/ec" ]]; then
	BASEBOARD=$(make --quiet -C ${HOME}/trunk/src/platform/ec \
		BOARD=${BOARD} print-baseboard)
else
	info "Could not find ec build folder to calculate baseboard."
fi

if [[ -n "${BASEBOARD}" ]]; then
	EMERGE_BUILD=/build/${BASEBOARD}/firmware/${BOARD}/${EC_FILE}
else
	EMERGE_BUILD=/build/${BOARD}/firmware/${EC_FILE}
fi

# Find the EC image to use
function ec_image() {
	# No image specified on the command line, try default ones
	if [[ -n "${FLAGS_image}" ]] ; then
		if [ -f "${FLAGS_image}" ] || \
		   [ "${FLAGS_image}" == "-" ]; then
			echo "${FLAGS_image}"
			return
		fi
		die "Invalid image path : ${FLAGS_image}"
	else
		if [ -f "${LOCAL_BUILD}" ]; then
			echo "${LOCAL_BUILD}"
			return
		fi
		if [ -f "${EMERGE_BUILD}" ]; then
			echo "${EMERGE_BUILD}"
			return
		fi
	fi
	die "no EC image found : build one or specify one."
}

# Find the EC UART provided by servo.
function servo_ec_uart() {
	SERVOD_FAIL="Cannot communicate with servod. Is servod running?"
	PTY=$(($DUT_CONTROL_CMD raw_${MCU}_uart_pty ||
	       $DUT_CONTROL_CMD ${MCU}_uart_pty) | cut -d: -f2)
	if [[ -z "${PTY}" ]]; then
	    die "${SERVOD_FAIL}"
	fi
	echo $PTY
}

# Servo variables management
case "${BOARD}" in
	oak_pd|samus_pd|strago_pd ) MCU="usbpd" ;;
	chell_pd|glados_pd ) MCU="usbpd" ;;
	meowth_fp|nocturne_fp ) MCU="usbpd" ;;
	dingdong|hoho|twinkie ) DUT_CONTROL_CMD="true" ; MCU="ec" ;;
	*) MCU="ec" ;;
esac

# Not every control is supported on every servo type.  Therefore, define which
# controls are supported by each servo type.
servo_v2_VARS="jtag_buf_on_flex_en jtag_buf_en cold_reset spi1_vref"
servo_micro_VARS="cold_reset spi1_vref"
servo_v4_with_ccd_cr50_VARS="cold_reset"
# Flashing an STM32 over the UART requires modifying the UART properties along
# with the boot mode pin.
if [ "${CHIP}" = "stm32" ] ; then
	common_stm32_VARS=" ${MCU}_boot_mode ${MCU}_uart_parity"
	common_stm32_VARS+=" ${MCU}_uart_baudrate"
	servo_v2_VARS+=$common_stm32_VARS
	servo_v2_VARS+=" ${MCU}_uart_en"
	servo_micro_VARS+=$common_stm32_VARS
	servo_micro_VARS+=" ${MCU}_uart_en"
	servo_v4_with_ccd_cr50_VARS+=$common_stm32_VARS
	servo_v4_with_ccd_cr50_VARS+=" ec_uart_bitbang_en ccd_ec_boot_mode_uut"
fi
if $(in_array "${BOARDS_STM32_PROG_EN[@]}" "${BOARD}"); then
	servo_v2_VARS+=" prog_en"
fi
if [ "${CHIP}" = "npcx_uut" ] ; then
	servo_v2_VARS+=" ${MCU}_boot_mode"
	servo_micro_VARS+=" ${MCU}_boot_mode"
	servo_v4_with_ccd_cr50_VARS+=" ccd_${MCU}_boot_mode_uut"
	servo_v4_with_ccd_cr50_VARS+=" ccd_keepalive_en"
fi
toad_VARS="${MCU}_uart_parity ${MCU}_uart_baudrate boot_mode"

if $(in_array "${BOARDS_NPCX_INT_SPI[@]}" "${BOARD}"); then
	servo_v2_VARS+=" fw_up"
fi
# Some servo boards use the same controls.
servo_v3_VARS="${servo_v2_VARS}"
servo_v4_with_servo_micro_VARS="${servo_micro_VARS}"

function servo_save() {
	SERVO_VARS_NAME=${SERVO_TYPE}_VARS
	if [[ -n "${!SERVO_VARS_NAME}" ]]; then
		$DUT_CONTROL_CMD ${!SERVO_VARS_NAME}
	fi
}

function servo_restore() {
	echo "$1" | while read line
	do
		dut_control "$line"
	done
}

function claim_pty() {
	if grep -q cros_sdk /proc/1/cmdline; then
		die "You must run this tool in a chroot that was entered with" \
		    "'cros_sdk --no-ns-pid' (see crbug.com/444931 for details)"
	fi

	if [[ -z "$1" ]]; then
		warn "No parameter passed to claim_pty()"
		return
	fi

	# Disconnect the EC-3PO interpreter from the UART since it will
	# interfere with flashing.
	dut_control ${MCU}_ec3po_interp_connect:off || \
	    warn "hdctools cannot disconnect the EC-3PO interpreter from" \
	    "the UART."

	pids=$(lsof -FR 2>/dev/null -- $1 | tr -d 'pR')
	FROZEN_PIDS=""

	# reverse order to SIGSTOP parents before children
	for pid in $(echo ${pids} | tac -s " "); do
		if ps -o cmd= "${pid}" | grep -qE "(servod|/sbin/init)"; then
			info "Skip stopping servod or init: process ${pid}."
		else
			info "Sending SIGSTOP to process ${pid}!"
			FROZEN_PIDS+=" ${pid}"
			sleep 0.02
			kill -STOP ${pid}
		fi
	done
}

function get_serial() {
	if [[ "${SERVO_TYPE}" =~ "servo_v4_with_servo_micro" ]]; then
		if [[ -z "${BOARD}" ]]; then
			sn_ctl="servo_micro_"
		else
			sn_ctl="servo_micro_for_${BOARD}_"
		fi
	elif [[ "${SERVO_TYPE}" =~ "_with_ccd" ]] ; then
		sn_ctl="ccd_"
	else
		# If it's none of the above, the main serialname will do.
		sn_ctl=""
	fi

	SERIALNAME=$(${DUT_CONTROL_CMD} "${sn_ctl}serialname" | cut -d: -f2)
	echo $SERIALNAME
}

# Board specific flashing scripts

# helper function for using servo v2/3 with openocd
function flash_openocd() {
	OCD_CFG="servo.cfg"
	if [[ -z "${EC_DIR}" ]]; then
		# check if we're on beaglebone
		if [[ -e "/usr/bin/lib" ]]; then
			OCD_PATH="/usr/bin/lib"
		else
			die "Cannot locate openocd configs"
		fi
	else
		OCD_PATH="${EC_DIR}/util/openocd"
	fi

	dut_control jtag_buf_on_flex_en:on
	dut_control jtag_buf_en:on

	sudo timeout -k 10 -s 9 "${FLAGS_timeout}" \
		openocd -s "${OCD_PATH}" -f "${OCD_CFG}" -f "${OCD_CHIP_CFG}" \
		-c "${OCD_CMDS}" || \
	die "Failed to program ${IMG}"
}

# helper function for using servo with flashrom
function flash_flashrom() {
	TOOL_PATH="${EC_DIR}/build/${BOARD}/util:/usr/sbin/:$PATH"
	FLASHROM=$(PATH="${TOOL_PATH}" which flashrom)

	if on_servov3; then
		FLASHROM_PARAM="-p linux_spi"
	elif on_raiden; then
	    if [[ "${SERVO_TYPE}" =~ "servo_micro" ]]; then
		# Servo micro doesn't use the "target" parameter.
		FLASHROM_PARAM="-p raiden_debug_spi:"
	    else
		FLASHROM_PARAM="-p raiden_debug_spi:target=EC,"
	    fi
	else
		FLASHROM_PARAM="-p ft2232_spi:type=servo-v2,port=B,"
	fi

	if [ ! -x "$FLASHROM" ]; then
		die "no flashrom util found."
	fi

	# Check whether the SPI flash is internal to the NPCX's EC.
	#
	# If the in_array function return non-zero, the script will exit.
	# Therefore, temporarily ignore errors so we can capture the return
	# value.
	set +e
	local board_is_npcx_internal_spi
	$(in_array "${BOARDS_NPCX_INT_SPI[@]}" "${BOARD}")
	board_is_npcx_internal_spi=$?
	set -e # Okay, don't ignore errors anymore.

	if ! on_servov3; then
		SERIALNAME=$(get_serial)
		if [[ "$SERIALNAME" != "" ]] ; then
			FLASHROM_PARAM+="serial=${SERIALNAME}"
		fi
	fi

	if ! on_raiden || [[ "${SERVO_TYPE}" =~ "servo_micro" ]] ; then
		if $(in_array "${BOARDS_SPI_1800MV[@]}" "${BOARD}"); then
			SPI_VOLTAGE="pp1800"
		else
			SPI_VOLTAGE="pp3300"
		fi

		dut_control cold_reset:on

		# If spi flash is in npcx's ec, enable gang programer mode
		if [ $board_is_npcx_internal_spi -eq 0 ]; then
			# Set GP_SEL# as low then start ec
			dut_control fw_up:on
			sleep 0.1
			dut_control cold_reset:off
		fi

		# Turn on SPI1 interface on servo for SPI Flash Chip
		dut_control spi1_vref:${SPI_VOLTAGE} spi1_buf_en:on
		if [[ ! "${SERVO_TYPE}" =~ "servo_micro" ]]; then
			# Servo micro doesn't support this control.
			dut_control spi1_buf_on_flex_en:on
		fi

		# b/65694390: Zoombini and Meowth take enough power that when
		# flashing without power, the SPI Vref voltage dips for a little
		# bit.  Therefore, wait 1 second to let the voltage stabilize.
		if [[ "${BOARD}" == "zoombini" || \
		      "${BOARD}" == "meowth" ]]; then
			sleep 1
		fi
	else
		if [ $board_is_npcx_internal_spi -eq 0 ]; then
			# Set GP_SEL# as low then start ec
			dut_control cold_reset:on
			dut_control fw_up:on
			# sleep 0.1
			dut_control cold_reset:off
		else
			# Assert EC reset.
			dut_control cold_reset:on
		fi

		# Temp layout
		L=/tmp/flash_spi_layout_$$

		dump_fmap -F "${IMG}" > "${L}"

		FLASHROM_OPTIONS="-i EC_RW -i WP_RO -l "${L}" --ignore-fmap \
			--fast-verify"
	fi

	# flashrom should report the image size at the end of the output.
	FLASHROM_CMDLINE="sudo ${FLASHROM} ${FLASHROM_PARAM} --get-size"
	if [ "${FLAGS_verbose}" = ${FLAGS_TRUE} ]; then
		echo "Running flashrom:" 1>&2
		echo " ${FLASHROM_CMDLINE}" 1>&2
	fi
	SPI_SIZE=$(${FLASHROM_CMDLINE} 2>/dev/null |\
		       grep -oe '[0-9]\+$') || \
		die "Failed to determine chip size!"
	(( SPI_SIZE == 0 )) && die "Chip size is 0!"

	IMG_SIZE=$(stat -c%s "$IMG")
	PATCH_SIZE=$((${SPI_SIZE} - ${IMG_SIZE}))

	# Temp image
	T=/tmp/flash_spi_$$

	if [ "${CHIP}" = "npcx_spi" ] ; then
	{	# Patch temp image up to SPI_SIZE
		cat $IMG
		if [[ ${IMG_SIZE} -lt ${SPI_SIZE} ]] ; then
			dd if=/dev/zero bs=${PATCH_SIZE} count=1 | \
				tr '\0' '\377'
		fi
	} > $T
	else
	{	# Patch temp image up to SPI_SIZE
		if [[ ${IMG_SIZE} -lt ${SPI_SIZE} ]] ; then
			dd if=/dev/zero bs=${PATCH_SIZE} count=1 | \
				tr '\0' '\377'
		fi
		cat $IMG
	} > $T
	fi

	# Generate the correct flashrom command to write the ec.
	FLASHROM_CMDLINE="${FLASHROM} ${FLASHROM_PARAM}"
	FLASHROM_CMDLINE+=" ${FLASHROM_OPTIONS} -w ${T}"

	if [ "${FLAGS_verbose}" = ${FLAGS_TRUE} ]; then
		echo "Running flashrom:" 1>&2
		echo " ${FLASHROM_CMDLINE}" 1>&2
	fi
	sudo timeout -k 10 -s 9 "${FLAGS_timeout}" \
		${FLASHROM_CMDLINE}

	rm $T

	# Set GP_SEL# as default to disable GP mode when ec reboots
	if [ $board_is_npcx_internal_spi -eq 0 ]; then
		dut_control fw_up:off
	fi

	if  ! on_raiden || [[ "${SERVO_TYPE}" =~ "servo_micro" ]] ; then
		# Turn off SPI1 interface on servo
		dut_control spi1_vref:off spi1_buf_en:off
		if [[ ! "${SERVO_TYPE}" =~ "servo_micro" ]] ; then
			dut_control spi1_buf_on_flex_en:off
		fi

	else
		rm $L
	fi

	# Do not save/restore servo settings
	save=
}

function flash_stm32() {
	TOOL_PATH="${EC_DIR}/build/${BOARD}/util:$PATH"
	STM32MON=$(PATH="${TOOL_PATH}" which stm32mon)
	EC_UART="$(servo_ec_uart)"
	if [ ! -x "$STM32MON" ]; then
		die "no stm32mon util found."
	fi

	info "Using serial flasher : ${STM32MON}"
	info "${MCU} UART pty : ${EC_UART}"
	claim_pty ${EC_UART}

	if ! on_raiden && [[ "${SERVO_TYPE}" =~ "servo" ]] ; then
		dut_control ${MCU}_uart_en:on
	fi
	dut_control ${MCU}_uart_parity:even

	if [[ "${SERVO_TYPE}" =~ "ccd_cr50" ]] ; then
		# TODO(b/77965217): remove when cr50 stops clobbering bitbang
		# when it notices the AP/EC turn off.
		dut_control cold_reset:on
		sleep 2
		dut_control ${MCU}_uart_baudrate:9600
		dut_control ${MCU}_uart_bitbang_en:on
	else
		dut_control ${MCU}_uart_baudrate:115200

		if $(servo_has_warm_reset); then
			dut_control warm_reset:on
		fi
	fi

	# Force the EC to boot in serial monitor mode
	ec_enable_boot0 "bitbang"

	# Add a delay long enough for cr50 to update the ccdstate. Cr50 updates
	# ccdstate once a second, so a 2 second delay should be safe.
	if [[ "${SERVO_TYPE}" =~ "ccd_cr50" ]] ; then
		sleep 2
	fi

	# Reset the EC
	if $(servo_has_cold_reset); then
		ec_reset
	fi
	# Unprotect flash, erase, and write
	STM32MON_COMMAND="${STM32MON} -d ${EC_UART} -U -u -e -w"
	if [ "${FLAGS_verbose}" = ${FLAGS_TRUE} ]; then
		echo "${STM32MON_COMMAND}" "${IMG}"
	fi
	timeout -k 10 -s 9 "${FLAGS_timeout}" \
		${STM32MON_COMMAND} "${IMG}"
	# Remove the Application processor reset
	# TODO(crosbug.com/p/30738): we cannot rely on servo_VARS to restore it
	if $(servo_has_warm_reset); then
		dut_control warm_reset:off
	fi

	# Reconnect the EC-3PO interpreter to the UART.
	dut_control ${MCU}_ec3po_interp_connect:on || \
	    warn "hdctools cannot reconnect the EC-3PO interpreter to" \
	    "the UART."
}

function flash_stm32_dfu() {
	DFU_DEVICE=0483:df11
	ADDR=0x08000000
	DFU_UTIL='dfu-util'
	which $DFU_UTIL &> /dev/null || die \
		"no dfu-util util found.  Did you 'sudo emerge dfu-util'"

	info "Using dfu flasher : ${DFU_UTIL}"

	dev_cnt=$(lsusb -d $DFU_DEVICE | wc -l)
	if [ $dev_cnt -eq 0 ] ; then
		die "unable to locate dfu device at $DFU_DEVICE"
	elif [ $dev_cnt -ne 1 ] ; then
		die "too many dfu devices (${dev_cnt}). Disconnect all but one."
	fi

	SIZE=$(wc -c ${IMG} | cut -d' ' -f1)
	# Remove read protection
	sudo timeout -k 10 -s 9 "${FLAGS_timeout}" \
		$DFU_UTIL -a 0 -s ${ADDR}:${SIZE}:force:unprotect -D "${IMG}"
	# Wait for mass-erase and reboot after unprotection
	sleep 1
	# Actual image flashing
	sudo timeout -k 10 -s 9 "${FLAGS_timeout}" \
		$DFU_UTIL -a 0 -s ${ADDR}:${SIZE} -D "${IMG}"
}

function flash_it83xx() {

	TOOL_PATH="${EC_DIR}/build/${BOARD}/util:$PATH"
	ITEFLASH=$(PATH="${TOOL_PATH}" which iteflash)
	if [ ! -x "$ITEFLASH" ]; then
		die "no iteflash util found."
	fi

	dut_control i2c_mux_en:on
	dut_control i2c_mux:remote_adc

	# Now the we have enabled the I2C mux on the servo to talk to the dut,
	# we need to switch the I2C mux on the dut to allow ec programing (if
	# there is a mux on the dut)
	if dut_control -i dut_i2c_mux 2>/dev/null; then
		dut_control dut_i2c_mux:ec_prog
	fi

	info "Close connection to ftdi_i2c interface"
	dut_control --ftdii2c close

	info "Run iteflash..."
	sudo ${ITEFLASH} -w ${IMG}
}

function flash_lm4() {
	OCD_CHIP_CFG="lm4_chip.cfg"
	OCD_CMDS="init; flash_lm4 ${IMG} ${FLAGS_offset}; shutdown;"

	flash_openocd

}

function flash_nrf51() {
	OCD_CHIP_CFG="nrf51_chip.cfg"
	OCD_CMDS="init; flash_nrf51 ${IMG} ${FLAGS_offset}; exit_debug_mode_nrf51; shutdown;"

	flash_openocd

	# waiting 100us for the reset pulse is not necessary, it takes ~2.5ms
	dut_control swd_reset:on swd_reset:off
}

function flash_npcx_jtag() {
	IMG_PATH="${EC_DIR}/build/${BOARD}"
	OCD_CHIP_CFG="npcx_chip.cfg"
	if [ "${FLAGS_ro}" = ${FLAGS_TRUE} ] ; then
		# Program RO region only
		OCD_CMDS="init; flash_npcx_ro ${CHIP} ${IMG_PATH} ${FLAGS_offset}; shutdown;"
	else
		# Program all EC regions
		OCD_CMDS="init; flash_npcx_all ${CHIP} ${IMG_PATH} ${FLAGS_offset}; shutdown;"
	fi

	# Reset the EC
	ec_reset

	flash_openocd
}

function flash_npcx_uut() {
	TOOL_PATH="${EC_DIR}/build/${BOARD}/util:$PATH"
	NPCX_UUT=$(PATH="${TOOL_PATH}" which uartupdatetool)
	EC_UART="$(servo_ec_uart)"

	BUILD_PATH="${EC_DIR}/build/${BOARD}"
	MONITOR_PATH="${BUILD_PATH}/chip/npcx/spiflashfw"
	MON="${MONITOR_PATH}/npcx_monitor.bin"
	# The start address to restore monitor firmware binary
	MON_ADDR="0x200C3020"

	if [ ! -x "$NPCX_UUT" ]; then
		die "no NPCX UART Update Tool found."
	fi

	info "Using: NPCX UART Update Tool"
	info "${MCU} UART pty : ${EC_UART}"
	claim_pty ${EC_UART}

	if [[ "${SERVO_TYPE}" =~ "ccd_cr50" ]] ; then
		dut_control ccd_keepalive_en:on
	fi

	# Force the EC to boot in UART update mode
	ec_enable_boot0 "uut"
	ec_reset

	# Have to wait a bit for EC boot-up
	sleep 0.1

	# Remove the prefix "/dev/" because uartupdatetool will add it.
	EC_UART=${EC_UART#/dev/}
	UUT_PARAMS="--port ${EC_UART} --baudrate 115200"

	# Load monitor binary to address 0x200C3020
	${NPCX_UUT} ${UUT_PARAMS} --opr wr --addr ${MON_ADDR} --file ${MON}
	${NPCX_UUT} ${UUT_PARAMS} --auto --offset ${FLAGS_offset} --file ${IMG}

	# Reconnect the EC-3PO interpreter to the UART.
	dut_control ${MCU}_ec3po_interp_connect:on || \
		warn "hdctools cannot reconnect the EC-3PO interpreter to" \
		"the UART."
}

function flash_npcx_5m5g_jtag() {
	flash_npcx_jtag
}

function flash_npcx_5m6g_jtag() {
	flash_npcx_jtag
}

function flash_npcx_7m6x_jtag() {
	flash_npcx_jtag
}

function flash_npcx_7m7x_jtag() {
	flash_npcx_jtag
}

function flash_npcx_spi() {
	flash_flashrom
}

function flash_mec1322() {
	flash_flashrom
}

SERVO_TYPE="$(get_servo_type)"
if dut_control boot_mode 2>/dev/null ; then
	if [[ "${MCU}" != "ec" ]] ; then
		die "Toad cable can't support non-ec UARTs"
	fi
	SERVO_TYPE=toad
	info "Using a dedicated debug cable"
fi
info "Using ${SERVO_TYPE}."

IMG="$(ec_image)"
info "Using ${MCU} image : ${IMG}"

if [ "${NEED_SERVO}" != "no" ] ; then
	save="$(servo_save)"
fi

info "Flashing chip ${CHIP}."
flash_${CHIP}
info "Flashing done."
