# SPDX-License-Identifier: GPL-2.0-or-later
#

set CPU_MAX_ADDRESS 0xFFFFFFFF
source [find bitsbytes.tcl]
source [find memory.tcl]
source [find mmr_helpers.tcl]

# Riscv Debug Module Registers which are used around esp configuration files.
set _RISCV_ABS_DATA0	0x04
set _RISCV_DMCONTROL	0x10
set _RISCV_ABS_CMD		0x17
set _RISCV_SB_CS		0x38
set _RISCV_SB_ADDR0		0x39
set _RISCV_SB_DATA0		0x3C

# Common ESP chips definitions

# Espressif supports only NuttX in the upstream.
# FreeRTOS support is not upstreamed yet.
set _RTOS "hwthread"
if { [info exists ESP_RTOS] } {
	set _RTOS "$ESP_RTOS"
}

# by default current dir (when OOCD has been started)
set _SEMIHOST_BASEDIR "."
if { [info exists ESP_SEMIHOST_BASEDIR] } {
	set _SEMIHOST_BASEDIR $ESP_SEMIHOST_BASEDIR
}

proc set_esp_common_variables { } {
	global _CHIPNAME _ONLYCPU _ESP_SMP_TARGET
	global _CPUNAME_0 _CPUNAME_1 _TARGETNAME_0 _TARGETNAME_1 _TAPNAME_0 _TAPNAME_1
	global _ESP_WDT_DISABLE _ESP_SOC_RESET _ESP_MEMPROT_IS_ENABLED

	# For now we support dual core at most.
	if { $_ONLYCPU == 1 && $_ESP_SMP_TARGET == 0} {
		set _TARGETNAME_0 				$_CHIPNAME
		set _CPUNAME_0					cpu
		set _TAPNAME_0 					$_CHIPNAME.$_CPUNAME_0
	} else {
		set _CPUNAME_0 					cpu0
		set _CPUNAME_1 					cpu1
		set _TARGETNAME_0 				$_CHIPNAME.$_CPUNAME_0
		set _TARGETNAME_1 				$_CHIPNAME.$_CPUNAME_1
		set _TAPNAME_0 					$_TARGETNAME_0
		set _TAPNAME_1 					$_TARGETNAME_1
	}

	set _ESP_WDT_DISABLE 			"${_CHIPNAME}_wdt_disable"
	set _ESP_SOC_RESET 				"${_CHIPNAME}_soc_reset"
	set _ESP_MEMPROT_IS_ENABLED 	"${_CHIPNAME}_memprot_is_enabled"
}

proc create_esp_jtag { } {
	global _CHIPNAME _CPUNAME_0 _CPUNAME_1 _CPUTAPID _ONLYCPU
	jtag newtap $_CHIPNAME $_CPUNAME_0 -irlen 5 -expected-id $_CPUTAPID
	if { $_ONLYCPU != 1 } {
		jtag newtap $_CHIPNAME $_CPUNAME_1 -irlen 5 -expected-id $_CPUTAPID
	} elseif [info exists _CPUNAME_1] {
		jtag newtap $_CHIPNAME $_CPUNAME_1 -irlen 5 -disable -expected-id $_CPUTAPID
	}
}

proc create_openocd_targets  { } {
	global _TARGETNAME_0 _TARGETNAME_1 _TAPNAME_0 _TAPNAME_1 _RTOS _CHIPNAME _ONLYCPU

	target create $_TARGETNAME_0 $_CHIPNAME -chain-position $_TAPNAME_0 -coreid 0 -rtos $_RTOS
	if { $_ONLYCPU != 1 } {
		target create $_TARGETNAME_1 $_CHIPNAME -chain-position $_TAPNAME_1 -coreid 1 -rtos $_RTOS
		target smp $_TARGETNAME_0 $_TARGETNAME_1
	}
}

proc create_esp_target { ARCH } {
	set_esp_common_variables
	create_esp_jtag
	create_openocd_targets
	configure_openocd_events $ARCH

	if { $ARCH == "xtensa"} {
		configure_esp_xtensa_default_settings
	} else {
		configure_esp_riscv_default_settings
	}
}

#################### Set event handlers and default settings  ####################

proc configure_event_examine_end { } {
	global _TARGETNAME_0 _TARGETNAME_1 _ONLYCPU

	$_TARGETNAME_0 configure -event examine-end {
		# Need to enable to set 'semihosting_basedir'
		arm semihosting enable
		arm semihosting_resexit enable
		if { [info exists _SEMIHOST_BASEDIR] } {
			if { $_SEMIHOST_BASEDIR != "" } {
				arm semihosting_basedir $_SEMIHOST_BASEDIR
			}
		}
	}

	if { $_ONLYCPU != 1 } {
		$_TARGETNAME_1 configure -event examine-end {
			# Need to enable to set 'semihosting_basedir'
			arm semihosting enable
			arm semihosting_resexit enable
			if { [info exists _SEMIHOST_BASEDIR] } {
				if { $_SEMIHOST_BASEDIR != "" } {
					arm semihosting_basedir $_SEMIHOST_BASEDIR
				}
			}
		}
	}
}

proc configure_event_reset_assert_post { } {
	global _TARGETNAME_0 _TARGETNAME_1 _ONLYCPU

	$_TARGETNAME_0 configure -event reset-assert-post {
		global _ESP_SOC_RESET
		$_ESP_SOC_RESET
	}

	if { $_ONLYCPU != 1 } {
		$_TARGETNAME_1 configure -event reset-assert-post {
			global _ESP_SOC_RESET
			$_ESP_SOC_RESET
		}
	}
}

proc configure_event_halted { } {
	global _TARGETNAME_0

	$_TARGETNAME_0 configure -event halted {
		global _ESP_WDT_DISABLE
	    $_ESP_WDT_DISABLE
	}
}

proc configure_event_gdb_attach { } {
	global _TARGETNAME_0 _TARGETNAME_1 _ONLYCPU

	$_TARGETNAME_0 configure -event gdb-attach {
		if { $_ESP_SMP_BREAK != 0 } {
			$_TARGETNAME_0 xtensa smpbreak BreakIn BreakOut
		}
		# necessary to auto-probe flash bank when GDB is connected and generate proper memory map
		halt 1000
		if { [$_ESP_MEMPROT_IS_ENABLED] } {
			# 'reset halt' to disable memory protection and allow flasher to work correctly
			echo "Memory protection is enabled. Reset target to disable it..."
			reset halt
		}
	}

	if { $_ONLYCPU != 1 } {
		$_TARGETNAME_1 configure -event gdb-attach {
			if { $_ESP_SMP_BREAK != 0 } {
				$_TARGETNAME_1 xtensa smpbreak BreakIn BreakOut
			}
			# necessary to auto-probe flash bank when GDB is connected
			halt 1000
			if { [$_ESP_MEMPROT_IS_ENABLED] } {
				# 'reset halt' to disable memory protection and allow flasher to work correctly
				echo "Memory protection is enabled. Reset target to disable it..."
				reset halt
			}
		}
	}
}

proc configure_openocd_events { ARCH } {
	if { $ARCH == "riscv" } {
		configure_event_halted
	}
	configure_event_examine_end
	configure_event_reset_assert_post
	configure_event_gdb_attach
}

proc configure_esp_riscv_default_settings { } {
	gdb_breakpoint_override hard
	riscv set_reset_timeout_sec 2
	riscv set_command_timeout_sec 5
	riscv set_mem_access sysbus progbuf abstract
	riscv set_ebreakm on
	riscv set_ebreaks on
	riscv set_ebreaku on
}

proc configure_esp_xtensa_default_settings { } {
	global _TARGETNAME_0 _ESP_SMP_BREAK _FLASH_VOLTAGE _CHIPNAME

	$_TARGETNAME_0 xtensa maskisr on
	if { $_ESP_SMP_BREAK != 0 } {
		$_TARGETNAME_0 xtensa smpbreak BreakIn BreakOut
	}

	gdb_breakpoint_override hard

	if { [info exists _FLASH_VOLTAGE] } {
		$_TARGETNAME_0 $_CHIPNAME flashbootstrap $_FLASH_VOLTAGE
	}
}