# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (C) 2023-2025 Texas Instruments Incorporated - https://www.ti.com/
#
# Texas Instruments MSPM0L/G - ARM Cortex-M0 @ 32MHz
# https://www.ti.com/microcontrollers-mcus-processors/arm-based-microcontrollers/arm-cortex-m0-mcus/overview.html
#

source [find bitsbytes.tcl]

if { [info exists CHIPNAME] } {
	set _CHIPNAME $CHIPNAME
} else {
	# Meant to work with MSPM0L and MSPM0G class of devices.
	set _CHIPNAME mspm0x
}

if { [info exists CPUTAPID] } {
	set _DAP_TAPID $CPUTAPID
} else {
	set _DAP_TAPID 0x4ba00477
}

if { [info exists DAP_SWD_ID] } {
	set _DAP_SWD_ID $DAP_SWD_ID
} else {
	set _DAP_SWD_ID 0x2ba01477
}

source [find target/swj-dp.tcl]

# MSPM0 only supports swd, so set it here and save a line for custom boards
transport select swd

set _DAP_ID $_DAP_SWD_ID

swj_newdap $_CHIPNAME cpu -irlen 4 -expected-id $_DAP_ID
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu

set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME cortex_m -dap $_CHIPNAME.dap

if { [info exists WORKAREABASE] } {
	set _WORKAREABASE $WORKAREABASE
} else {
	set _WORKAREABASE 0x20000000
}
if { [info exists WORKAREASIZE] } {
	set _WORKAREASIZE $WORKAREASIZE
} else {
    # Smallest SRAM size is 1K SRAM.
	set _WORKAREASIZE 0x400
}

#
# MSPM0 Debug SubSystem Mailbox (DSSM) Communication helpers
#

proc _mspm0_wait_for_dssm_response {command} {
	# Wait for SECAP::RCR rx_valid to be set
	set timeout 1000
	while { [expr { [$::_CHIPNAME.dap apreg 2 0xc] & 0x1}] != 0x1 } {
		sleep 1
		set timeout [expr {$timeout - 1}]
		if { $timeout == 0 } {
			set rcr [$::_CHIPNAME.dap apreg 2 0xc]
			return -code error [format "MSPM0 SECAP RCR=0x%08x timeout rx_valid" $rcr]
		}
	}

	# Read SECAP::RXD to clear the RX_VALID bit
	set rxd [$::_CHIPNAME.dap apreg 2 0x8]
	# Read SECAP::RCR
	set rcr [$::_CHIPNAME.dap apreg 2 0xc]

	# Check if we got successful response. This is denoted as:
	# 8 LSBits of $command should matchup with SECAP::RCR
	# and
	# SECAP::RXD should be 0x10003
	if { ([expr { $command & 0xff}] == $rcr) && ($rxd == 0x10003) } {
		return 0
	}

	# Provide some debug log for users to report back if CMD fails.
	return -code error [format "MSPM0 SECAP CMD FAIL! RXD: 0x%08X RCR: 0x%08X" $rxd $rcr]
}

proc _mspm0_dssm_command {command} {
	# SECAP::TCR = command
	$::_CHIPNAME.dap apreg 2 0x4 $command
	# SECAP::TDR = 0x0
	$::_CHIPNAME.dap apreg 2 0x0 0x0
	# Read SECAP::RCR and RXD to clear up any prev pending reads
	set rxd [$::_CHIPNAME.dap apreg 2 0x8]
	set rcr [$::_CHIPNAME.dap apreg 2 0xc]
	# Make sure everything is synced
	sleep 1000
	# Trigger nRST
	mspm0_board_reset

	# Wait for ROM to do it's magic and respond back
	set res [_mspm0_wait_for_dssm_response $command]
	if { $res } {
		return $res
	}
	# Paranoid.. make sure ROM does what it is meant to do
	# RX valid should have been cleared after the operation is
	# complete
	sleep 1000

	# Trigger nRST to get back to sane system
	mspm0_board_reset
	sleep 1000

	return 0
}

# NOTE: Password authentication scheme is NOT supported atm.
# mspm0_factory_reset: Factory reset the board
proc mspm0_factory_reset {} {
	set res	[_mspm0_dssm_command 0x020a]
	if { $res } {
		echo "Factory Reset failed!"
	} else {
		echo "Factory reset success! Halting processor"
		# We need to halt the processor else the WDT fires!
		halt
	}
	return $res
}

add_help_text mspm0_factory_reset "Force Factory reset to recover 'bricked' board"

# NOTE: Password authentication scheme is NOT supported atm.
# mspm0_mass_erase: Mass erase flash
proc mspm0_mass_erase {} {
	set res	[_mspm0_dssm_command 0x020c]
	if { $res } {
		echo "Mass Erase failed!"
	} else {
		echo "Mass Erase success! Halting Processor"
		# We need to halt the processor else the WDT fires!
		halt
	}
	return $res
}

add_help_text mspm0_mass_erase "Mass erase flash"

# mspm0_start_bootloader: Ask explicitly for bootloader startup
proc mspm0_start_bootloader {} {
	set res	[_mspm0_dssm_command 0x0108]
	if { $res } {
		echo "Start BL failed!"
	}
	return $res
}

add_help_text mspm0_start_bootloader "Ask explicitly for bootloader startup"

# MSPM0 requires board level NRST reset to be toggled for
# Factory reset operations to function.
# However this cannot be the default configuration as this
# prevents reset init reset halt to function properly
# since the Debug Subsystem (debugss) logic or coresight
# seems impacted by nRST.
# This can be overridden in board file as required.
#
# mspm0_board_reset: Board level reset
proc mspm0_board_reset {} {
	set user_reset_config [reset_config]
	reset_config srst_only
	set errno [catch {reset}]
	eval reset_config $user_reset_config
	if {$errno} {error}
}

add_help_text mspm0_board_reset "Request a board level reset"

# If the flash is empty or the device is already in low-power state, then
# debug access is not available. to handle this, explicitly control power ap
# to provide access. Refer to Technical Reference Manual for further info.
proc _mspm0_enable_low_power_mode { } {
	# PWR_AP::DPREC <= FRCACT(3)=1, RST_CTL(14:16)=1, IHIB_SLP(20)=1
	$::_CHIPNAME.dap apreg 4 0x00 0x104008
	# PWR_AP::SPREC <= SYSRST=1
	$::_CHIPNAME.dap apreg 4 0xF0 0x01
	# PWR_AP::DPREC <= FRCACT(3)=1, IHIB_SLP(20)=1
	$::_CHIPNAME.dap apreg 4 0x00 0x100008
}

$_TARGETNAME configure -event examine-start { _mspm0_enable_low_power_mode }
$_TARGETNAME configure -work-area-phys $_WORKAREABASE -work-area-size $_WORKAREASIZE -work-area-backup 0

set _FLASHNAME $_CHIPNAME.flash
flash bank $_FLASHNAME.main mspm0 0 0 0 0 $_TARGETNAME
flash bank $_FLASHNAME.nonmain mspm0 0x41c00000 0 0 0 $_TARGETNAME
flash bank $_FLASHNAME.data mspm0 0x41d00000 0 0 0 $_TARGETNAME

cortex_m reset_config sysresetreq
