diff options
41 files changed, 1949 insertions, 400 deletions
diff --git a/configure.ac b/configure.ac index 83eb676..ba073f1 100644 --- a/configure.ac +++ b/configure.ac @@ -165,6 +165,9 @@ m4_define([LIBFTDI_USB1_ADAPTERS], m4_define([LIBGPIOD_ADAPTERS], [[[linuxgpiod], [Linux GPIO bitbang through libgpiod], [LINUXGPIOD]]]) +m4_define([REMOTE_BITBANG_ADAPTER], + [[[remote_bitbang], [Remote Bitbang driver], [REMOTE_BITBANG]]]) + m4_define([LIBJAYLINK_ADAPTERS], [[[jlink], [SEGGER J-Link Programmer], [JLINK]]]) @@ -179,6 +182,15 @@ m4_define([LINUXSPIDEV_ADAPTER], m4_define([VDEBUG_ADAPTER], [[[vdebug], [Cadence Virtual Debug Interface], [VDEBUG]]]) +m4_define([JTAG_DPI_ADAPTER], + [[[jtag_dpi], [JTAG DPI Adapter], [JTAG_DPI]]]) + +m4_define([JTAG_VPI_ADAPTER], + [[[jtag_vpi], [JTAG VPI Adapter], [JTAG_VPI]]]) + +m4_define([RSHIM_ADAPTER], + [[[rshim], [BlueField SoC via rshim], [RSHIM]]]) + # The word 'Adapter' in "Dummy Adapter" below must begin with a capital letter # because there is an M4 macro called 'adapter'. m4_define([DUMMY_ADAPTER], @@ -277,10 +289,6 @@ AS_IF([test "x$debug_malloc" = "xyes" -a "x$have_glibc" = "xyes"], [ AC_DEFINE([_DEBUG_FREE_SPACE_],[1], [Include malloc free space in logging]) ]) -AC_ARG_ENABLE([rshim], - AS_HELP_STRING([--enable-rshim], [Enable building the rshim driver]), - [build_rshim=$enableval], [build_rshim=no]) - AC_ARG_ENABLE([dmem], AS_HELP_STRING([--enable-dmem], [Enable building the dmem driver]), [build_dmem=$enableval], [build_dmem=no]) @@ -305,10 +313,14 @@ AC_ARG_ADAPTERS([ LIBFTDI_ADAPTERS, LIBFTDI_USB1_ADAPTERS, LIBGPIOD_ADAPTERS, + REMOTE_BITBANG_ADAPTER, LINUXSPIDEV_ADAPTER, SERIAL_PORT_ADAPTERS, DUMMY_ADAPTER, VDEBUG_ADAPTER, + JTAG_DPI_ADAPTER, + JTAG_VPI_ADAPTER, + RSHIM_ADAPTER, PCIE_ADAPTERS, LIBJAYLINK_ADAPTERS ],[auto]) @@ -327,14 +339,6 @@ AC_ARG_ENABLE([parport_giveio], [Enable use of giveio for parport (for CygWin only)]), [parport_use_giveio=$enableval], [parport_use_giveio=]) -AC_ARG_ENABLE([jtag_vpi], - AS_HELP_STRING([--enable-jtag_vpi], [Enable building support for JTAG VPI]), - [build_jtag_vpi=$enableval], [build_jtag_vpi=no]) - -AC_ARG_ENABLE([jtag_dpi], - AS_HELP_STRING([--enable-jtag_dpi], [Enable building support for JTAG DPI]), - [build_jtag_dpi=$enableval], [build_jtag_dpi=no]) - AC_ARG_ENABLE([amtjtagaccel], AS_HELP_STRING([--enable-amtjtagaccel], [Enable building the Amontec JTAG-Accelerator driver]), [build_amtjtagaccel=$enableval], [build_amtjtagaccel=no]) @@ -380,19 +384,24 @@ AC_ARG_ENABLE([sysfsgpio], AS_HELP_STRING([--enable-sysfsgpio], [Enable building support for programming driven via sysfs gpios.]), [build_sysfsgpio=$enableval], [build_sysfsgpio=no]) +can_build_rshim=no + AS_CASE([$host_os], [linux*], [ is_linux=yes + can_build_rshim=yes ], [ AS_IF([test "x$build_sysfsgpio" = "xyes"], [ AC_MSG_ERROR([sysfsgpio is only available on linux]) ]) - AS_CASE([$host_os], [freebsd*], [], + AS_CASE([$host_os], [freebsd*], [ + can_build_rshim=yes + ], [ - AS_IF([test "x$build_rshim" = "xyes"], [ - AC_MSG_ERROR([build_rshim is only available on linux or freebsd]) + AS_IF([test "x$enable_rshim" = "xyes"], [ + AC_MSG_ERROR([rshim is only available on linux or freebsd]) ]) ]) @@ -414,10 +423,6 @@ AC_ARG_ENABLE([internal-libjaylink], [Enable building internal libjaylink]), [use_internal_libjaylink=$enableval], [use_internal_libjaylink=no]) -AC_ARG_ENABLE([remote-bitbang], - AS_HELP_STRING([--enable-remote-bitbang], [Enable building support for the Remote Bitbang driver]), - [build_remote_bitbang=$enableval], [build_remote_bitbang=yes]) - AS_CASE(["${host_cpu}"], [i?86|x86*], [], [ @@ -515,12 +520,6 @@ AS_IF([test "x$build_parport" = "xyes"], [ AC_DEFINE([BUILD_PARPORT], [0], [0 if you don't want parport.]) ]) -AS_IF([test "x$build_rshim" = "xyes"], [ - AC_DEFINE([BUILD_RSHIM], [1], [1 if you want to debug BlueField SoC via rshim.]) -], [ - AC_DEFINE([BUILD_RSHIM], [0], [0 if you don't want to debug BlueField SoC via rshim.]) -]) - AS_IF([test "x$build_dmem" = "xyes"], [ AC_DEFINE([BUILD_DMEM], [1], [1 if you want to debug via Direct Mem.]) ], [ @@ -578,19 +577,6 @@ AS_IF([test "x$parport_use_giveio" = "xyes"], [ AC_DEFINE([PARPORT_USE_GIVEIO], [0], [0 if you don't want parport to use giveio.]) ]) -AS_IF([test "x$build_jtag_vpi" = "xyes"], [ - AC_DEFINE([BUILD_JTAG_VPI], [1], [1 if you want JTAG VPI.]) -], [ - AC_DEFINE([BUILD_JTAG_VPI], [0], [0 if you don't want JTAG VPI.]) -]) - -AS_IF([test "x$build_jtag_dpi" = "xyes"], [ - AC_DEFINE([BUILD_JTAG_DPI], [1], [1 if you want JTAG DPI.]) -], [ - AC_DEFINE([BUILD_JTAG_DPI], [0], [0 if you don't want JTAG DPI.]) -]) - - AS_IF([test "x$build_amtjtagaccel" = "xyes"], [ AC_DEFINE([BUILD_AMTJTAGACCEL], [1], [1 if you want the Amontec JTAG-Accelerator driver.]) ], [ @@ -625,13 +611,6 @@ AS_IF([test "x$use_internal_jimtcl" = "xyes"], [ ]) ]) -AS_IF([test "x$build_remote_bitbang" = "xyes"], [ - build_bitbang=yes - AC_DEFINE([BUILD_REMOTE_BITBANG], [1], [1 if you want the Remote Bitbang driver.]) -], [ - AC_DEFINE([BUILD_REMOTE_BITBANG], [0], [0 if you don't want the Remote Bitbang driver.]) -]) - AS_IF([test "x$build_sysfsgpio" = "xyes"], [ build_bitbang=yes AC_DEFINE([BUILD_SYSFSGPIO], [1], [1 if you want the SysfsGPIO driver.]) @@ -730,18 +709,27 @@ PROCESS_ADAPTERS([HIDAPI_USB1_ADAPTERS], ["x$use_hidapi" = "xyes" -a "x$use_libu PROCESS_ADAPTERS([LIBFTDI_ADAPTERS], ["x$use_libftdi" = "xyes"], [libftdi]) PROCESS_ADAPTERS([LIBFTDI_USB1_ADAPTERS], ["x$use_libftdi" = "xyes" -a "x$use_libusb1" = "xyes"], [libftdi and libusb-1.x]) PROCESS_ADAPTERS([LIBGPIOD_ADAPTERS], ["x$use_libgpiod" = "xyes"], [Linux libgpiod]) +PROCESS_ADAPTERS([REMOTE_BITBANG_ADAPTER], [true], [unused]) PROCESS_ADAPTERS([LIBJAYLINK_ADAPTERS], ["x$use_internal_libjaylink" = "xyes" -o "x$use_libjaylink" = "xyes"], [libjaylink-0.2]) PROCESS_ADAPTERS([PCIE_ADAPTERS], ["x$is_linux" = "xyes"], [Linux build]) PROCESS_ADAPTERS([SERIAL_PORT_ADAPTERS], ["x$can_build_buspirate" = "xyes"], [internal error: validation should happen beforehand]) PROCESS_ADAPTERS([LINUXSPIDEV_ADAPTER], ["x$is_linux" = "xyes"], [Linux spidev]) PROCESS_ADAPTERS([VDEBUG_ADAPTER], [true], [unused]) +PROCESS_ADAPTERS([JTAG_DPI_ADAPTER], [true], [unused]) +PROCESS_ADAPTERS([JTAG_VPI_ADAPTER], [true], [unused]) +PROCESS_ADAPTERS([RSHIM_ADAPTER], ["x$can_build_rshim" = "xyes"], + [internal error: validation should happen beforehand]) PROCESS_ADAPTERS([DUMMY_ADAPTER], [true], [unused]) AS_IF([test "x$enable_linuxgpiod" != "xno"], [ build_bitbang=yes ]) +AS_IF([test "x$enable_remote_bitbang" != "xno"], [ + build_bitbang=yes +]) + AS_IF([test "x$enable_stlink" != "xno" -o "x$enable_ti_icdi" != "xno" -o "x$enable_nulink" != "xno"], [ AC_DEFINE([BUILD_HLADAPTER], [1], [1 if you want the High Level JTAG driver.]) AM_CONDITIONAL([HLADAPTER], [true]) @@ -783,12 +771,9 @@ AM_CONDITIONAL([BCM2835GPIO], [test "x$build_bcm2835gpio" = "xyes"]) AM_CONDITIONAL([IMX_GPIO], [test "x$build_imx_gpio" = "xyes"]) AM_CONDITIONAL([AM335XGPIO], [test "x$build_am335xgpio" = "xyes"]) AM_CONDITIONAL([BITBANG], [test "x$build_bitbang" = "xyes"]) -AM_CONDITIONAL([JTAG_VPI], [test "x$build_jtag_vpi" = "xyes"]) -AM_CONDITIONAL([JTAG_DPI], [test "x$build_jtag_dpi" = "xyes"]) AM_CONDITIONAL([USB_BLASTER_DRIVER], [test "x$enable_usb_blaster" != "xno" -o "x$enable_usb_blaster_2" != "xno"]) AM_CONDITIONAL([AMTJTAGACCEL], [test "x$build_amtjtagaccel" = "xyes"]) AM_CONDITIONAL([GW16012], [test "x$build_gw16012" = "xyes"]) -AM_CONDITIONAL([REMOTE_BITBANG], [test "x$build_remote_bitbang" = "xyes"]) AM_CONDITIONAL([SYSFSGPIO], [test "x$build_sysfsgpio" = "xyes"]) AM_CONDITIONAL([USE_LIBUSB1], [test "x$use_libusb1" = "xyes"]) AM_CONDITIONAL([IS_CYGWIN], [test "x$is_cygwin" = "xyes"]) @@ -800,7 +785,6 @@ AM_CONDITIONAL([USE_LIBFTDI], [test "x$use_libftdi" = "xyes"]) AM_CONDITIONAL([USE_LIBGPIOD], [test "x$use_libgpiod" = "xyes"]) AM_CONDITIONAL([USE_HIDAPI], [test "x$use_hidapi" = "xyes"]) AM_CONDITIONAL([USE_LIBJAYLINK], [test "x$use_libjaylink" = "xyes"]) -AM_CONDITIONAL([RSHIM], [test "x$build_rshim" = "xyes"]) AM_CONDITIONAL([DMEM], [test "x$build_dmem" = "xyes"]) AM_CONDITIONAL([HAVE_CAPSTONE], [test "x$enable_capstone" != "xno"]) @@ -887,9 +871,13 @@ m4_foreach([adapter], [USB1_ADAPTERS, HIDAPI_ADAPTERS, HIDAPI_USB1_ADAPTERS, LIBFTDI_ADAPTERS, LIBFTDI_USB1_ADAPTERS, LIBGPIOD_ADAPTERS, + REMOTE_BITBANG_ADAPTER, LIBJAYLINK_ADAPTERS, PCIE_ADAPTERS, SERIAL_PORT_ADAPTERS, LINUXSPIDEV_ADAPTER, VDEBUG_ADAPTER, + JTAG_DPI_ADAPTER, + JTAG_VPI_ADAPTER, + RSHIM_ADAPTER, DUMMY_ADAPTER, OPTIONAL_LIBRARIES, COVERAGE], diff --git a/doc/openocd.texi b/doc/openocd.texi index 94a8fc7..1dcb7f3 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -2415,12 +2415,6 @@ target. List the debug adapter drivers that have been built into the running copy of OpenOCD. @end deffn -@deffn {Config Command} {adapter transports} transport_name+ -Specifies the transports supported by this debug adapter. -The adapter driver builds-in similar knowledge; use this only -when external configuration (such as jumpering) changes what -the hardware can support. -@end deffn @anchor{adapter gpio} @deffn {Config Command} {adapter gpio [ @ @@ -3845,13 +3839,6 @@ Not all adapters and adapter drivers support SWD multi-drop. Only the following adapter drivers are SWD multi-drop capable: cmsis_dap (use an adapter with CMSIS-DAP version 2.0), ftdi, all bitbang based. -@subsection SPI Transport -@cindex SPI -@cindex Serial Peripheral Interface -The Serial Peripheral Interface (SPI) is a general purpose transport -which uses four wire signaling. Some processors use it as part of a -solution for flash programming. - @anchor{swimtransport} @subsection SWIM Transport @cindex SWIM @@ -7430,6 +7417,76 @@ msp432 bsl lock @end deffn @end deffn +@deffn {Flash Driver} {mspm0} + +All Arm Cortex-M0+ MSPM0 microcontroller versions from Texas Instruments +include internal flash. The mspm0 flash driver automatically recognizes the +specific version's flash parameters and autoconfigures itself. The main +program flash starts at address 0x0. Non-main flash starts at 0x41c00000. +If present on the device, the optional region called "Data" starts at +0x41d00000. + +@b{Warning}: + +@itemize @bullet +@item @b{Reset while MCU operation:} When erasing all of MAIN memory, if the +MCU is still executing from MAIN memory do not reset the device as it could +cause a double hard-fault due to the missing interrupt vector table at the +start of memory. To recover from such scenario reset or power-cycle the MCU. + +@item @b{No explicit protection support:} MSPM0 flash controller auto-protect +themselves after every flash operation. As a result of this, OpenOCD does not +explicitly provide any protection function and automatically un-protects +required sections as flash operations are requested. +@end itemize + +@b{Examples}: + +@itemize @bullet + +@item @b{Flash bank description:} +@example +flash bank $_FLASHNAME mspm0 0 0 0 0 $_TARGETNAME +@end example + +@item @b{To erase and program the MAIN region:} +@example +halt +flash erase_sector 0 0 last +flash write_image MAIN.bin 0x0 +mspm0_board_reset +@end example + +@item @b{To erase and program the NONMAIN region:} +@example +halt +flash erase_sector 1 0 last +flash write_image NONMAIN.bin 0x41C00000 +mspm0_board_reset +@end example + +@end itemize + +@deffn {TCL proc} {mspm0_board_reset} +Performs an nRST toggle on the device. +@end deffn + +@deffn {TCL proc} {mspm0_mass_erase} +Sends the mass erase command to the SEC-AP mailbox and then performs +an nRST toggle. Once the command has been fully processed by the ROM, +all MAIN memory will be erased. NOTE: This command is not supported +on MSPM0C* family of devices. +@end deffn + +@deffn {TCL proc} {mspm0_factory_reset} +Sends the factory reset command to the SEC-AP mailbox and then performs +an nRST toggle. Once the command has been fully processed by the ROM, +all MAIN memory will be erased and NONMAIN will be reset to its default +values. +@end deffn + +@end deffn + @deffn {Flash Driver} {niietcm4} This drivers handles the integrated NOR flash on NIIET Cortex-M4 based controllers. Flash size and sector layout are auto-configured by the driver. diff --git a/src/Makefile.am b/src/Makefile.am index 4d1c1a2..4dbe93f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -34,7 +34,10 @@ if RELEASE else %C%_libopenocd_la_CPPFLAGS += -DRELSTR=\"`$(top_srcdir)/guess-rev.sh $(top_srcdir)`\" %C%_libopenocd_la_CPPFLAGS += -DGITVERSION=\"`cd $(top_srcdir) && git describe`\" -%C%_libopenocd_la_CPPFLAGS += -DPKGBLDDATE=\"`date +%F-%R`\" +%C%_libopenocd_la_CPPFLAGS += -DPKGBLDDATE=\"`DATE_FMT=+%F-%R; \ + SOURCE_DATE_EPOCH="$${SOURCE_DATE_EPOCH:-$$(date +%s)}"; \ + date -u -d "@$$SOURCE_DATE_EPOCH" "$$DATE_FMT" 2>/dev/null || \ + date -u -r "$$SOURCE_DATE_EPOCH" "$$DATE_FMT" 2>/dev/null || date -u "$$DATE_FMT"`\" endif # add default CPPFLAGS diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index 8296877..147807f 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -45,6 +45,7 @@ NOR_DRIVERS = \ %D%/max32xxx.c \ %D%/mdr.c \ %D%/msp432.c \ + %D%/mspm0.c \ %D%/mrvlqspi.c \ %D%/niietcm4.c \ %D%/non_cfi.c \ diff --git a/src/flash/nor/driver.h b/src/flash/nor/driver.h index 852a55a..794566f 100644 --- a/src/flash/nor/driver.h +++ b/src/flash/nor/driver.h @@ -274,6 +274,7 @@ extern const struct flash_driver max32xxx_flash; extern const struct flash_driver mdr_flash; extern const struct flash_driver mrvlqspi_flash; extern const struct flash_driver msp432_flash; +extern const struct flash_driver mspm0_flash; extern const struct flash_driver niietcm4_flash; extern const struct flash_driver npcx_flash; extern const struct flash_driver nrf51_flash; diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index ce97b81..67d8624 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -51,6 +51,7 @@ static const struct flash_driver * const flash_drivers[] = { &mdr_flash, &mrvlqspi_flash, &msp432_flash, + &mspm0_flash, &niietcm4_flash, &npcx_flash, &nrf5_flash, diff --git a/src/flash/nor/mspm0.c b/src/flash/nor/mspm0.c new file mode 100644 index 0000000..4731c89 --- /dev/null +++ b/src/flash/nor/mspm0.c @@ -0,0 +1,1131 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/*************************************************************************** + * Copyright (C) 2023-2025 Texas Instruments Incorporated - https://www.ti.com/ + * + * NOR flash driver for MSPM0L and MSPM0G class of uC from Texas Instruments. + * + * See: + * https://www.ti.com/microcontrollers-mcus-processors/arm-based-microcontrollers/arm-cortex-m0-mcus/overview.html + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include <helper/bits.h> +#include <helper/time_support.h> + +/* MSPM0 Region memory map */ +#define MSPM0_FLASH_BASE_NONMAIN 0x41C00000 +#define MSPM0_FLASH_END_NONMAIN 0x41C00400 +#define MSPM0_FLASH_BASE_MAIN 0x0 +#define MSPM0_FLASH_BASE_DATA 0x41D00000 + +/* MSPM0 FACTORYREGION registers */ +#define MSPM0_FACTORYREGION 0x41C40000 +#define MSPM0_TRACEID (MSPM0_FACTORYREGION + 0x000) +#define MSPM0_DID (MSPM0_FACTORYREGION + 0x004) +#define MSPM0_USERID (MSPM0_FACTORYREGION + 0x008) +#define MSPM0_SRAMFLASH (MSPM0_FACTORYREGION + 0x018) + +/* MSPM0 FCTL registers */ +#define FLASH_CONTROL_BASE 0x400CD000 +#define FCTL_REG_DESC (FLASH_CONTROL_BASE + 0x10FC) +#define FCTL_REG_CMDEXEC (FLASH_CONTROL_BASE + 0x1100) +#define FCTL_REG_CMDTYPE (FLASH_CONTROL_BASE + 0x1104) +#define FCTL_REG_CMDADDR (FLASH_CONTROL_BASE + 0x1120) +#define FCTL_REG_CMDBYTEN (FLASH_CONTROL_BASE + 0x1124) +#define FCTL_REG_CMDDATA0 (FLASH_CONTROL_BASE + 0x1130) +#define FCTL_REG_CMDWEPROTA (FLASH_CONTROL_BASE + 0x11D0) +#define FCTL_REG_CMDWEPROTB (FLASH_CONTROL_BASE + 0x11D4) +#define FCTL_REG_CMDWEPROTNM (FLASH_CONTROL_BASE + 0x1210) +#define FCTL_REG_STATCMD (FLASH_CONTROL_BASE + 0x13D0) + +/* FCTL_STATCMD[CMDDONE] Bits */ +#define FCTL_STATCMD_CMDDONE_MASK 0x00000001 +#define FCTL_STATCMD_CMDDONE_STATDONE 0x00000001 + +/* FCTL_STATCMD[CMDPASS] Bits */ +#define FCTL_STATCMD_CMDPASS_MASK 0x00000002 +#define FCTL_STATCMD_CMDPASS_STATPASS 0x00000002 + +/* + * FCTL_CMDEXEC Bits + * FCTL_CMDEXEC[VAL] Bits + */ +#define FCTL_CMDEXEC_VAL_EXECUTE 0x00000001 + +/* FCTL_CMDTYPE[COMMAND] Bits */ +#define FCTL_CMDTYPE_COMMAND_PROGRAM 0x00000001 +#define FCTL_CMDTYPE_COMMAND_ERASE 0x00000002 + +/* FCTL_CMDTYPE[SIZE] Bits */ +#define FCTL_CMDTYPE_SIZE_ONEWORD 0x00000000 +#define FCTL_CMDTYPE_SIZE_SECTOR 0x00000040 + +/* FCTL_FEATURE_VER_B minimum */ +#define FCTL_FEATURE_VER_B 0xA + +#define MSPM0_MAX_PROTREGS 3 + +#define MSPM0_FLASH_TIMEOUT_MS 8000 +#define ERR_STRING_MAX 255 + +/* SYSCTL BASE */ +#define SYSCTL_BASE 0x400AF000 +#define SYSCTL_SECCFG_SECSTATUS (SYSCTL_BASE + 0x00003048) + +/* TI manufacturer ID */ +#define TI_MANUFACTURER_ID 0x17 + +/* Defines for probe status */ +#define MSPM0_NO_ID_FOUND 0 +#define MSPM0_DEV_ID_FOUND 1 +#define MSPM0_DEV_PART_ID_FOUND 2 + +struct mspm0_flash_bank { + /* chip id register */ + uint32_t did; + /* Device Unique ID register */ + uint32_t traceid; + unsigned char version; + + const char *name; + + /* Decoded flash information */ + unsigned int data_flash_size_kb; + unsigned int main_flash_size_kb; + unsigned int main_flash_num_banks; + unsigned int sector_size; + /* Decoded SRAM information */ + unsigned int sram_size_kb; + + /* Flash word size: 64 bit = 8, 128bit = 16 bytes */ + unsigned char flash_word_size_bytes; + + /* Protection register stuff */ + unsigned int protect_reg_base; + unsigned int protect_reg_count; + + /* Flashctl version: A - CMDWEPROTA/B, B- CMDWEPROTB */ + unsigned char flash_version; +}; + +struct mspm0_part_info { + const char *part_name; + unsigned short part; + unsigned char variant; +}; + +struct mspm0_family_info { + const char *family_name; + unsigned short part_num; + unsigned char part_count; + const struct mspm0_part_info *part_info; +}; + +/* https://www.ti.com/lit/ds/symlink/mspm0l1346.pdf Table 8-13 and so on */ +static const struct mspm0_part_info mspm0l_parts[] = { + { "MSPM0L1105TDGS20R", 0x51DB, 0x16 }, + { "MSPM0L1105TDGS28R", 0x51DB, 0x83 }, + { "MSPM0L1105TDYYR", 0x51DB, 0x54 }, + { "MSPM0L1105TRGER", 0x51DB, 0x86 }, + { "MSPM0L1105TRHBR", 0x51DB, 0x68 }, + { "MSPM0L1106TDGS20R", 0x5552, 0x4B }, + { "MSPM0L1106TDGS28R", 0x5552, 0x98 }, + { "MSPM0L1106TDYYR", 0x5552, 0x9D }, + { "MSPM0L1106TRGER", 0x5552, 0x90 }, + { "MSPM0L1106TRHBR", 0x5552, 0x53 }, + { "MSPM0L1303SRGER", 0xef0, 0x17 }, + { "MSPM0L1303TRGER", 0xef0, 0xe2 }, + { "MSPM0L1304QDGS20R", 0xd717, 0x91 }, + { "MSPM0L1304QDGS28R", 0xd717, 0xb6 }, + { "MSPM0L1304QDYYR", 0xd717, 0xa0 }, + { "MSPM0L1304QRHBR", 0xd717, 0xa9 }, + { "MSPM0L1304SDGS20R", 0xd717, 0xfa }, + { "MSPM0L1304SDGS28R", 0xd717, 0x73 }, + { "MSPM0L1304SDYYR", 0xd717, 0xb7 }, + { "MSPM0L1304SRGER", 0xd717, 0x26 }, + { "MSPM0L1304SRHBR", 0xd717, 0xe4 }, + { "MSPM0L1304TDGS20R", 0xd717, 0x33 }, + { "MSPM0L1304TDGS28R", 0xd717, 0xa8 }, + { "MSPM0L1304TDYYR", 0xd717, 0xf9 }, + { "MSPM0L1304TRGER", 0xd717, 0xb7 }, + { "MSPM0L1304TRHBR", 0xd717, 0x5a }, + { "MSPM0L1305QDGS20R", 0x4d03, 0xb7 }, + { "MSPM0L1305QDGS28R", 0x4d03, 0x74 }, + { "MSPM0L1305QDYYR", 0x4d03, 0xec }, + { "MSPM0L1305QRHBR", 0x4d03, 0x78 }, + { "MSPM0L1305SDGS20R", 0x4d03, 0xc7 }, + { "MSPM0L1305SDGS28R", 0x4d03, 0x64 }, + { "MSPM0L1305SDYYR", 0x4d03, 0x91 }, + { "MSPM0L1305SRGER", 0x4d03, 0x73 }, + { "MSPM0L1305SRHBR", 0x4d03, 0x2d }, + { "MSPM0L1305TDGS20R", 0x4d03, 0xa0 }, + { "MSPM0L1305TDGS28R", 0x4d03, 0xfb }, + { "MSPM0L1305TDYYR", 0x4d03, 0xde }, + { "MSPM0L1305TRGER", 0x4d03, 0xea }, + { "MSPM0L1305TRHBR", 0x4d03, 0x85 }, + { "MSPM0L1306QDGS20R", 0xbb70, 0x59 }, + { "MSPM0L1306QDGS28R", 0xbb70, 0xf7 }, + { "MSPM0L1306QDYYR", 0xbb70, 0x9f }, + { "MSPM0L1306QRHBR", 0xbb70, 0xc2 }, + { "MSPM0L1306SDGS20R", 0xbb70, 0xf4 }, + { "MSPM0L1306SDGS28R", 0xbb70, 0x5 }, + { "MSPM0L1306SDYYR", 0xbb70, 0xe }, + { "MSPM0L1306SRGER", 0xbb70, 0x7f }, + { "MSPM0L1306SRHBR", 0xbb70, 0x3c }, + { "MSPM0L1306TDGS20R", 0xbb70, 0xa }, + { "MSPM0L1306TDGS28R", 0xbb70, 0x63 }, + { "MSPM0L1306TDYYR", 0xbb70, 0x35 }, + { "MSPM0L1306TRGER", 0xbb70, 0xaa }, + { "MSPM0L1306TRHBR", 0xbb70, 0x52 }, + { "MSPM0L1343TDGS20R", 0xb231, 0x2e }, + { "MSPM0L1344TDGS20R", 0x40b0, 0xd0 }, + { "MSPM0L1345TDGS28R", 0x98b4, 0x74 }, + { "MSPM0L1346TDGS28R", 0xf2b5, 0xef }, +}; + +/* https://www.ti.com/lit/ds/symlink/mspm0g3506.pdf Table 8-20 */ +static const struct mspm0_part_info mspm0g_parts[] = { + { "MSPM0G1105TPTR", 0x8934, 0xD }, + { "MSPM0G1105TRGZR", 0x8934, 0xFE }, + { "MSPM0G1106TPMR", 0x477B, 0xD4 }, + { "MSPM0G1106TPTR", 0x477B, 0x71 }, + { "MSPM0G1106TRGZR", 0x477B, 0xBB }, + { "MSPM0G1106TRHBR", 0x477B, 0x0 }, + { "MSPM0G1107TDGS28R", 0x807B, 0x82 }, + { "MSPM0G1107TPMR", 0x807B, 0xB3 }, + { "MSPM0G1107TPTR", 0x807B, 0x32 }, + { "MSPM0G1107TRGER", 0x807B, 0x79 }, + { "MSPM0G1107TRGZR", 0x807B, 0x20 }, + { "MSPM0G1107TRHBR", 0x807B, 0xBC }, + { "MSPM0G1505SDGS28R", 0x13C4, 0x73 }, + { "MSPM0G1505SPMR", 0x13C4, 0x53 }, + { "MSPM0G1505SPTR", 0x13C4, 0x3E }, + { "MSPM0G1505SRGER", 0x13C4, 0x47 }, + { "MSPM0G1505SRGZR", 0x13C4, 0x34 }, + { "MSPM0G1505SRHBR", 0x13C4, 0x30 }, + { "MSPM0G1506SDGS28R", 0x5AE0, 0x3A }, + { "MSPM0G1506SPMR", 0x5AE0, 0xF6 }, + { "MSPM0G1506SRGER", 0x5AE0, 0x67 }, + { "MSPM0G1506SRGZR", 0x5AE0, 0x75 }, + { "MSPM0G1506SRHBR", 0x5AE0, 0x57 }, + { "MSPM0G1507SDGS28R", 0x2655, 0x6D }, + { "MSPM0G1507SPMR", 0x2655, 0x97 }, + { "MSPM0G1507SRGER", 0x2655, 0x83 }, + { "MSPM0G1507SRGZR", 0x2655, 0xD3 }, + { "MSPM0G1507SRHBR", 0x2655, 0x4D }, + { "MSPM0G3105SDGS20R", 0x4749, 0x21 }, + { "MSPM0G3105SDGS28R", 0x4749, 0xDD }, + { "MSPM0G3105SRHBR", 0x4749, 0xBE }, + { "MSPM0G3106SDGS20R", 0x54C7, 0xD2 }, + { "MSPM0G3106SDGS28R", 0x54C7, 0xB9 }, + { "MSPM0G3106SRHBR", 0x54C7, 0x67 }, + { "MSPM0G3107SDGS20R", 0xAB39, 0x5C }, + { "MSPM0G3107SDGS28R", 0xAB39, 0xCC }, + { "MSPM0G3107SRHBR", 0xAB39, 0xB7 }, + { "MSPM0G3505SDGS28R", 0xc504, 0x8e }, + { "MSPM0G3505SPMR", 0xc504, 0x1d }, + { "MSPM0G3505SPTR", 0xc504, 0x93 }, + { "MSPM0G3505SRGZR", 0xc504, 0xc7 }, + { "MSPM0G3505SRHBR", 0xc504, 0xe7 }, + { "MSPM0G3505TDGS28R", 0xc504, 0xdf }, + { "MSPM0G3506SDGS28R", 0x151f, 0x8 }, + { "MSPM0G3506SPMR", 0x151f, 0xd4 }, + { "MSPM0G3506SPTR", 0x151f, 0x39 }, + { "MSPM0G3506SRGZR", 0x151f, 0xfe }, + { "MSPM0G3506SRHBR", 0x151f, 0xb5 }, + { "MSPM0G3507SDGS28R", 0xae2d, 0xca }, + { "MSPM0G3507SPMR", 0xae2d, 0xc7 }, + { "MSPM0G3507SPTR", 0xae2d, 0x3f }, + { "MSPM0G3507SRGZR", 0xae2d, 0xf7 }, + { "MSPM0G3507SRHBR", 0xae2d, 0x4c }, + { "M0G3107QPMRQ1", 0x4e2f, 0x51 }, + { "M0G3107QPTRQ1", 0x4e2f, 0xc7}, + { "M0G3107QRGZRQ1", 0x4e2f, 0x8a }, + { "M0G3107QRHBRQ1", 0x4e2f, 0x9a}, + { "M0G3107QDGS28RQ1", 0x4e2f, 0xd5}, + { "M0G3107QDGS28RQ1", 0x4e2f, 0x67}, + { "M0G3107QDGS20RQ1", 0x4e2f, 0xfd}, + { "M0G3106QPMRQ1", 0x54C7, 0x08}, + { "M0G3105QDGS32RQ1", 0x1349, 0x08}, + { "M0G3106QPTRQ1", 0x54C7, 0x3F}, + { "M0G3105QDGS28RQ1", 0x1349, 0x1B}, + { "M0G3106QRGZRQ1", 0x94AD, 0xE6}, + { "M0G3105QDGS20RQ1", 0x1349, 0xFB}, + { "M0G3106QRHBRQ1", 0x94AD, 0x20}, + { "M0G3106QDGS32RQ1", 0x94AD, 0x8D}, + { "M0G3106QDGS28RQ1", 0x94AD, 0x03}, + { "M0G3106QDGS20RQ1", 0x94AD, 0x6F}, + { "M0G3105QPMRQ1", 0x1349, 0xD0}, + { "M0G3105QPTRQ1", 0x1349, 0xEF}, + { "M0G3105QRGZRQ1", 0x1349, 0x70}, + { "M0G3105QRHBRQ1", 0x1349, 0x01}, +}; + +/* https://www.ti.com/lit/gpn/mspm0c1104 Table 8-12 and so on */ +static const struct mspm0_part_info mspm0c_parts[] = { + { "MSPS003F4SPW20R", 0x57b3, 0x70}, + { "MSPM0C1104SDGS20R", 0x57b3, 0x71}, + { "MSPM0C1104SRUKR", 0x57b3, 0x73}, + { "MSPM0C1104SDYYR", 0x57b3, 0x75}, + { "MSPM0C1104SDDFR", 0x57b3, 0x77}, + { "MSPM0C1104SDSGR", 0x57b3, 0x79}, +}; + +/* https://www.ti.com/lit/gpn/MSPM0L2228 Table 8-16 and so on */ +static const struct mspm0_part_info mspm0lx22x_parts[] = { + { "MSPM0L1227SRGER", 0x7C32, 0xF1}, + { "MSPM0L1227SPTR", 0x7C32, 0xC9}, + { "MSPM0L1227SPMR", 0x7C32, 0x1C}, + { "MSPM0L1227SPNAR", 0x7C32, 0x91}, + { "MSPM0L1227SPNR", 0x7C32, 0x39}, + { "MSPM0L1228SRGER", 0x33F7, 0x13}, + { "MSPM0L1228SRHBR", 0x33F7, 0x3A}, + { "MSPM0L1228SRGZR", 0x33F7, 0xBC}, + { "MSPM0L1228SPTR", 0x33F7, 0xF8}, + { "MSPM0L1228SPMR", 0x33F7, 0xCE}, + { "MSPM0L1228SPNAR", 0x33F7, 0x59}, + { "MSPM0L1228SPNR", 0x33F7, 0x7}, + { "MSPM0L2227SRGZR", 0x5E8F, 0x90}, + { "MSPM0L2227SPTR", 0x5E8F, 0xA}, + { "MSPM0L2227SPMR", 0x5E8F, 0x6D}, + { "MSPM0L2227SPNAR", 0x5E8F, 0x24}, + { "MSPM0L2227SPNR", 0x5E8F, 0x68}, + { "MSPM0L2228SRGZR", 0x2C38, 0xB8}, + { "MSPM0L2228SPTR", 0x2C38, 0x25}, + { "MSPM0L2228SPMR", 0x2C38, 0x6E}, + { "MSPM0L2228SPNAR", 0x2C38, 0x63}, + { "MSPM0L2228SPNR", 0x2C38, 0x3C}, +}; + +static const struct mspm0_family_info mspm0_finf[] = { + { "MSPM0L", 0xbb82, ARRAY_SIZE(mspm0l_parts), mspm0l_parts }, + { "MSPM0Lx22x", 0xbb9f, ARRAY_SIZE(mspm0lx22x_parts), mspm0lx22x_parts }, + { "MSPM0G", 0xbb88, ARRAY_SIZE(mspm0g_parts), mspm0g_parts }, + { "MSPM0C", 0xbba1, ARRAY_SIZE(mspm0c_parts), mspm0c_parts }, +}; + +/* + * OpenOCD command interface + */ + +/* + * flash_bank mspm0 <base> <size> 0 0 <target#> + */ +FLASH_BANK_COMMAND_HANDLER(mspm0_flash_bank_command) +{ + struct mspm0_flash_bank *mspm0_info; + + switch (bank->base) { + case MSPM0_FLASH_BASE_NONMAIN: + case MSPM0_FLASH_BASE_MAIN: + case MSPM0_FLASH_BASE_DATA: + break; + default: + LOG_ERROR("Invalid bank address " TARGET_ADDR_FMT, bank->base); + return ERROR_FAIL; + } + + mspm0_info = calloc(1, sizeof(struct mspm0_flash_bank)); + if (!mspm0_info) { + LOG_ERROR("%s: Out of memory for mspm0_info!", __func__); + return ERROR_FAIL; + } + + bank->driver_priv = mspm0_info; + + mspm0_info->sector_size = 0x400; + + return ERROR_OK; +} + +/* + * Chip identification and status + */ +static int get_mspm0_info(struct flash_bank *bank, struct command_invocation *cmd) +{ + struct mspm0_flash_bank *mspm0_info = bank->driver_priv; + + if (mspm0_info->did == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + command_print_sameline(cmd, + "\nTI MSPM0 information: Chip is " + "%s rev %d Device Unique ID: 0x%" PRIu32 "\n", + mspm0_info->name, mspm0_info->version, + mspm0_info->traceid); + command_print_sameline(cmd, + "main flash: %uKiB in %u bank(s), sram: %uKiB, data flash: %uKiB", + mspm0_info->main_flash_size_kb, + mspm0_info->main_flash_num_banks, mspm0_info->sram_size_kb, + mspm0_info->data_flash_size_kb); + + return ERROR_OK; +} + +/* Extract a bitfield helper */ +static unsigned int mspm0_extract_val(unsigned int var, unsigned char hi, unsigned char lo) +{ + return (var & GENMASK(hi, lo)) >> lo; +} + +static int mspm0_read_part_info(struct flash_bank *bank) +{ + struct mspm0_flash_bank *mspm0_info = bank->driver_priv; + struct target *target = bank->target; + const struct mspm0_family_info *minfo = NULL; + + /* Read and parse chip identification and flash version register */ + uint32_t did; + int retval = target_read_u32(target, MSPM0_DID, &did); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to read device ID"); + return retval; + } + retval = target_read_u32(target, MSPM0_TRACEID, &mspm0_info->traceid); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to read trace ID"); + return retval; + } + uint32_t userid; + retval = target_read_u32(target, MSPM0_USERID, &userid); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to read user ID"); + return retval; + } + uint32_t flashram; + retval = target_read_u32(target, MSPM0_SRAMFLASH, &flashram); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to read sramflash register"); + return retval; + } + uint32_t flashdesc; + retval = target_read_u32(target, FCTL_REG_DESC, &flashdesc); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to read flashctl description register"); + return retval; + } + + unsigned char version = mspm0_extract_val(did, 31, 28); + unsigned short pnum = mspm0_extract_val(did, 27, 12); + unsigned char variant = mspm0_extract_val(userid, 23, 16); + unsigned short part = mspm0_extract_val(userid, 15, 0); + unsigned short manufacturer = mspm0_extract_val(did, 11, 1); + + /* + * Valid DIE and manufacturer ID? + * Check the ALWAYS_1 bit to be 1 and manufacturer to be 0x17. All MSPM0 + * devices within the Device ID field of the factory constants will + * always read 0x17 as it is TI's JEDEC bank and company code. If 1 + * and 0x17 is not read from their respective registers then it truly + * is not a MSPM0 device so we will return an error instead of + * going any further. + */ + if (!(did & BIT(0)) || !(manufacturer & TI_MANUFACTURER_ID)) { + LOG_WARNING("Unknown Device ID[0x%" PRIx32 "], cannot identify target", + did); + LOG_DEBUG("did 0x%" PRIx32 ", traceid 0x%" PRIx32 ", userid 0x%" PRIx32 + ", flashram 0x%" PRIx32 "", did, mspm0_info->traceid, userid, + flashram); + return ERROR_FLASH_OPERATION_FAILED; + } + + /* Initialize master index selector and probe status*/ + unsigned char minfo_idx = 0xff; + unsigned char probe_status = MSPM0_NO_ID_FOUND; + + /* Check if we at least know the family of devices */ + for (unsigned int i = 0; i < ARRAY_SIZE(mspm0_finf); i++) { + if (mspm0_finf[i].part_num == pnum) { + minfo_idx = i; + minfo = &mspm0_finf[i]; + probe_status = MSPM0_DEV_ID_FOUND; + break; + } + } + + /* Initialize part index selector*/ + unsigned char pinfo_idx = 0xff; + + /* + * If we can identify the part number then we will attempt to identify + * the specific chip. Otherwise, if we do not know the part number then + * it would be useless to identify the specific chip. + */ + if (probe_status == MSPM0_DEV_ID_FOUND) { + /* Can we specifically identify the chip */ + for (unsigned int i = 0; i < minfo->part_count; i++) { + if (minfo->part_info[i].part == part + && minfo->part_info[i].variant == variant) { + pinfo_idx = i; + probe_status = MSPM0_DEV_PART_ID_FOUND; + break; + } + } + } + + /* + * We will check the status of our probe within this switch-case statement + * using these three scenarios. + * + * 1) Device, part, and variant ID is unknown. + * 2) Device ID is known but the part/variant ID is unknown. + * 3) Device ID and part/variant ID is known + * + * For scenario 1, we allow the user to continue because if the + * manufacturer matches TI's JEDEC value and ALWAYS_1 from the device ID + * field is correct then the assumption the user is using an MSPM0 device + * can be made. + */ + switch (probe_status) { + case MSPM0_NO_ID_FOUND: + mspm0_info->name = "mspm0x"; + LOG_INFO("Unidentified PART[0x%x]/variant[0x%x" + "], unknown DeviceID[0x%x" + "]. Attempting to proceed as %s.", part, variant, pnum, + mspm0_info->name); + break; + case MSPM0_DEV_ID_FOUND: + mspm0_info->name = mspm0_finf[minfo_idx].family_name; + LOG_INFO("Unidentified PART[0x%x]/variant[0x%x" + "], known DeviceID[0x%x" + "]. Attempting to proceed as %s.", part, variant, pnum, + mspm0_info->name); + break; + case MSPM0_DEV_PART_ID_FOUND: + default: + mspm0_info->name = mspm0_finf[minfo_idx].part_info[pinfo_idx].part_name; + LOG_DEBUG("Part: %s detected", mspm0_info->name); + break; + } + + mspm0_info->did = did; + mspm0_info->version = version; + mspm0_info->data_flash_size_kb = mspm0_extract_val(flashram, 31, 26); + mspm0_info->main_flash_size_kb = mspm0_extract_val(flashram, 11, 0); + mspm0_info->main_flash_num_banks = mspm0_extract_val(flashram, 13, 12) + 1; + mspm0_info->sram_size_kb = mspm0_extract_val(flashram, 25, 16); + mspm0_info->flash_version = mspm0_extract_val(flashdesc, 15, 12); + + /* + * Hardcode flash_word_size unless we find some other pattern + * See section 7.7 (Foot note mentions the flash word size). + * almost all values seem to be 8 bytes, but if there are variance, + * then we should update mspm0_part_info structure with this info. + */ + mspm0_info->flash_word_size_bytes = 8; + + LOG_DEBUG("Detected: main flash: %uKb in %u banks, sram: %uKb, data flash: %uKb", + mspm0_info->main_flash_size_kb, mspm0_info->main_flash_num_banks, + mspm0_info->sram_size_kb, mspm0_info->data_flash_size_kb); + + return ERROR_OK; +} + +/* + * Decode error values + */ +static const struct { + const unsigned char bit_offset; + const char *fail_string; +} mspm0_fctl_fail_decode_strings[] = { + { 2, "CMDINPROGRESS" }, + { 4, "FAILWEPROT" }, + { 5, "FAILVERIFY" }, + { 6, "FAILILLADDR" }, + { 7, "FAILMODE" }, + { 12, "FAILMISC" }, +}; + +static const char *mspm0_fctl_translate_ret_err(unsigned int return_code) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(mspm0_fctl_fail_decode_strings); i++) { + if (return_code & BIT(mspm0_fctl_fail_decode_strings[i].bit_offset)) + return mspm0_fctl_fail_decode_strings[i].fail_string; + } + + /* If unknown error notify the user*/ + return "FAILUNKNOWN"; +} + +static int mspm0_fctl_get_sector_reg(struct flash_bank *bank, unsigned int addr, + unsigned int *reg, unsigned int *sector_mask) +{ + struct mspm0_flash_bank *mspm0_info = bank->driver_priv; + struct target *target = bank->target; + int ret = ERROR_OK; + unsigned int sector_num = (addr >> 10); + unsigned int sector_in_bank = sector_num; + unsigned int phys_sector_num = sector_num; + uint32_t sysctl_sec_status; + unsigned int exec_upper_bank; + + /* + * If the device has dual banks we will need to check if it is configured + * to execute from the upper bank. In the scenario that we are executing + * from upper bank then we will need to protect it using CMDWEPROTA rather + * than CMDWEPROTB. We also need to take into account what sector + * we're using when going between banks. + */ + if (mspm0_info->main_flash_num_banks > 1 && + bank->base == MSPM0_FLASH_BASE_MAIN) { + ret = target_read_u32(target, SYSCTL_SECCFG_SECSTATUS, &sysctl_sec_status); + if (ret != ERROR_OK) + return ret; + exec_upper_bank = mspm0_extract_val(sysctl_sec_status, 12, 12); + if (exec_upper_bank) { + if (sector_num > (mspm0_info->main_flash_size_kb / 2)) { + phys_sector_num = + sector_num - (mspm0_info->main_flash_size_kb / 2); + } else { + phys_sector_num = + sector_num + (mspm0_info->main_flash_size_kb / 2); + } + } + sector_in_bank = + sector_num % (mspm0_info->main_flash_size_kb / + mspm0_info->main_flash_num_banks); + } + + /* + * NOTE: MSPM0 devices of version A will use CMDWEPROTA and CMDWEPROTB + * for MAIN flash. CMDWEPROTC is included in the TRM/DATASHEET but for + * all practical purposes, it is considered reserved. If the flash + * version on the device is version B, then we will only use + * CMDWEPROTB for MAIN and DATA flash if the device has it. + */ + switch (bank->base) { + case MSPM0_FLASH_BASE_MAIN: + case MSPM0_FLASH_BASE_DATA: + if (mspm0_info->flash_version < FCTL_FEATURE_VER_B) { + /* Use CMDWEPROTA */ + if (phys_sector_num < 32) { + *sector_mask = BIT(phys_sector_num); + *reg = FCTL_REG_CMDWEPROTA; + } + + /* Use CMDWEPROTB */ + if (phys_sector_num >= 32 && sector_in_bank < 256) { + /* Dual bank system */ + if (mspm0_info->main_flash_num_banks > 1) + *sector_mask = BIT(sector_in_bank / 8); + else /* Single bank system */ + *sector_mask = BIT((sector_in_bank - 32) / 8); + *reg = FCTL_REG_CMDWEPROTB; + } + } else { + *sector_mask = BIT((sector_in_bank / 8) % 32); + *reg = FCTL_REG_CMDWEPROTB; + } + break; + case MSPM0_FLASH_BASE_NONMAIN: + *sector_mask = BIT(sector_num % 32); + *reg = FCTL_REG_CMDWEPROTNM; + break; + default: + /* + * Not expected to reach here due to check in mspm0_address_check() + * but adding it as another layer of safety. + */ + ret = ERROR_FLASH_DST_OUT_OF_BANK; + break; + } + + if (ret != ERROR_OK) + LOG_ERROR("Unable to map sector protect reg for address 0x%08x", addr); + + return ret; +} + +static int mspm0_address_check(struct flash_bank *bank, unsigned int addr) +{ + struct mspm0_flash_bank *mspm0_info = bank->driver_priv; + unsigned int flash_main_size = mspm0_info->main_flash_size_kb * 1024; + unsigned int flash_data_size = mspm0_info->data_flash_size_kb * 1024; + int ret = ERROR_FLASH_SECTOR_INVALID; + + /* + * Before unprotecting any memory lets make sure that the address and + * bank given is a known bank and whether or not the address falls under + * the proper bank. + */ + switch (bank->base) { + case MSPM0_FLASH_BASE_MAIN: + if (addr <= (MSPM0_FLASH_BASE_MAIN + flash_main_size)) + ret = ERROR_OK; + break; + case MSPM0_FLASH_BASE_NONMAIN: + if (addr >= MSPM0_FLASH_BASE_NONMAIN && addr <= MSPM0_FLASH_END_NONMAIN) + ret = ERROR_OK; + break; + case MSPM0_FLASH_BASE_DATA: + if (addr >= MSPM0_FLASH_BASE_DATA && + addr <= (MSPM0_FLASH_BASE_DATA + flash_data_size)) + ret = ERROR_OK; + break; + default: + ret = ERROR_FLASH_DST_OUT_OF_BANK; + break; + } + + return ret; +} + +static int mspm0_fctl_unprotect_sector(struct flash_bank *bank, unsigned int addr) +{ + struct target *target = bank->target; + unsigned int reg = 0x0; + uint32_t sector_mask = 0x0; + int ret; + + ret = mspm0_address_check(bank, addr); + switch (ret) { + case ERROR_FLASH_SECTOR_INVALID: + LOG_ERROR("Unable to map sector protect reg for address 0x%08x", addr); + break; + case ERROR_FLASH_DST_OUT_OF_BANK: + LOG_ERROR("Unable to determine which bank to use 0x%08x", addr); + break; + default: + mspm0_fctl_get_sector_reg(bank, addr, ®, §or_mask); + ret = target_write_u32(target, reg, ~sector_mask); + break; + } + + return ret; +} + +static int mspm0_fctl_cfg_command(struct flash_bank *bank, + uint32_t addr, + uint32_t cmd, + uint32_t byte_en) +{ + struct target *target = bank->target; + + /* + * Configure the flash operation within the CMDTYPE register, byte_en + * bits if needed, and then set the address where the flash operation + * will execute. + */ + int retval = target_write_u32(target, FCTL_REG_CMDTYPE, cmd); + if (retval != ERROR_OK) + return retval; + if (byte_en != 0) { + retval = target_write_u32(target, FCTL_REG_CMDBYTEN, byte_en); + if (retval != ERROR_OK) + return retval; + } + + return target_write_u32(target, FCTL_REG_CMDADDR, addr); +} + +static int mspm0_fctl_wait_cmd_ok(struct flash_bank *bank) +{ + struct target *target = bank->target; + uint32_t return_code = 0; + int64_t start_ms; + int64_t elapsed_ms; + + start_ms = timeval_ms(); + while ((return_code & FCTL_STATCMD_CMDDONE_MASK) != FCTL_STATCMD_CMDDONE_STATDONE) { + int retval = target_read_u32(target, FCTL_REG_STATCMD, &return_code); + if (retval != ERROR_OK) + return retval; + + elapsed_ms = timeval_ms() - start_ms; + if (elapsed_ms > 500) + keep_alive(); + if (elapsed_ms > MSPM0_FLASH_TIMEOUT_MS) + break; + } + + if ((return_code & FCTL_STATCMD_CMDPASS_MASK) != FCTL_STATCMD_CMDPASS_STATPASS) { + LOG_ERROR("Flash command failed: %s", mspm0_fctl_translate_ret_err(return_code)); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int mspm0_fctl_sector_erase(struct flash_bank *bank, uint32_t addr) +{ + struct target *target = bank->target; + + /* + * TRM Says: + * Note that the CMDWEPROTx registers are reset to a protected state + * at the end of all program and erase operations. These registers + * must be re-configured by software before a new operation is + * initiated. + * + * This means that as we start erasing sector by sector, the protection + * registers are reset and need to be unprotected *again* for the next + * erase operation. Unfortunately, this means that we cannot do a unitary + * unprotect operation independent of flash erase operation + */ + int retval = mspm0_fctl_unprotect_sector(bank, addr); + if (retval != ERROR_OK) { + LOG_ERROR("Unprotecting sector of memory at address 0x%08" PRIx32 + " failed", addr); + return retval; + } + + /* Actual erase operation */ + retval = mspm0_fctl_cfg_command(bank, addr, + (FCTL_CMDTYPE_COMMAND_ERASE | FCTL_CMDTYPE_SIZE_SECTOR), 0); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, FCTL_REG_CMDEXEC, FCTL_CMDEXEC_VAL_EXECUTE); + if (retval != ERROR_OK) + return retval; + + return mspm0_fctl_wait_cmd_ok(bank); +} + +static int mspm0_protect_check(struct flash_bank *bank) +{ + struct mspm0_flash_bank *mspm0_info = bank->driver_priv; + + if (mspm0_info->did == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + /* + * TRM Says: + * Note that the CMDWEPROTx registers are reset to a protected state + * at the end of all program and erase operations. These registers + * must be re-configured by software before a new operation is + * initiated. + * + * This means that when any flash operation is performed at a block level, + * the block is locked back again. This prevents usage where we can set a + * protection level once at the flash level and then do erase / write + * operation without touching the protection register (since it is + * reset by hardware automatically). In effect, we cannot use the hardware + * defined protection scheme in openOCD. + * + * To deal with this protection scheme, the CMDWEPROTx register that + * correlates to the sector is modified at the time of operation and as far + * openOCD is concerned, the flash operates as completely un-protected + * flash. + */ + for (unsigned int i = 0; i < bank->num_sectors; i++) + bank->sectors[i].is_protected = 0; + + return ERROR_OK; +} + +static int mspm0_erase(struct flash_bank *bank, unsigned int first, unsigned int last) +{ + struct target *target = bank->target; + struct mspm0_flash_bank *mspm0_info = bank->driver_priv; + int retval = ERROR_OK; + uint32_t protect_reg_cache[MSPM0_MAX_PROTREGS]; + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Please halt target for erasing flash"); + return ERROR_TARGET_NOT_HALTED; + } + + if (mspm0_info->did == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + /* Pick a copy of the current protection config for later restoration */ + for (unsigned int i = 0; i < mspm0_info->protect_reg_count; i++) { + retval = target_read_u32(target, + mspm0_info->protect_reg_base + (i * 4), + &protect_reg_cache[i]); + if (retval != ERROR_OK) { + LOG_ERROR("Failed saving flashctl protection status"); + return retval; + } + } + + switch (bank->base) { + case MSPM0_FLASH_BASE_MAIN: + for (unsigned int csa = first; csa <= last; csa++) { + unsigned int addr = csa * mspm0_info->sector_size; + retval = mspm0_fctl_sector_erase(bank, addr); + if (retval != ERROR_OK) + LOG_ERROR("Sector erase on MAIN failed at address 0x%08x " + "(sector: %u)", addr, csa); + } + break; + case MSPM0_FLASH_BASE_NONMAIN: + retval = mspm0_fctl_sector_erase(bank, MSPM0_FLASH_BASE_NONMAIN); + if (retval != ERROR_OK) + LOG_ERROR("Sector erase on NONMAIN failed"); + break; + case MSPM0_FLASH_BASE_DATA: + for (unsigned int csa = first; csa <= last; csa++) { + unsigned int addr = (MSPM0_FLASH_BASE_DATA + + (csa * mspm0_info->sector_size)); + retval = mspm0_fctl_sector_erase(bank, addr); + if (retval != ERROR_OK) + LOG_ERROR("Sector erase on DATA bank failed at address 0x%08x " + "(sector: %u)", addr, csa); + } + break; + default: + LOG_ERROR("Invalid memory region access"); + retval = ERROR_FLASH_BANK_INVALID; + break; + } + + /* If there were any issues in our checks, return the error */ + if (retval != ERROR_OK) + return retval; + + /* + * TRM Says: + * Note that the CMDWEPROTx registers are reset to a protected state + * at the end of all program and erase operations. These registers + * must be re-configured by software before a new operation is + * initiated + * Let us just Dump the protection registers back to the system. + * That way we retain the protection status as requested by the user + */ + for (unsigned int i = 0; i < mspm0_info->protect_reg_count; i++) { + retval = target_write_u32(target, mspm0_info->protect_reg_base + (i * 4), + protect_reg_cache[i]); + if (retval != ERROR_OK) { + LOG_ERROR("Failed re-applying protection status of flashctl"); + return retval; + } + } + + return retval; +} + +static int mspm0_write(struct flash_bank *bank, const unsigned char *buffer, + unsigned int offset, unsigned int count) +{ + struct target *target = bank->target; + struct mspm0_flash_bank *mspm0_info = bank->driver_priv; + uint32_t protect_reg_cache[MSPM0_MAX_PROTREGS]; + int retval; + + /* + * XXX: TRM Says: + * The number of program operations applied to a given word line must be + * monitored to ensure that the maximum word line program limit before + * erase is not violated. + * + * There is no reasonable way we can maintain that state in OpenOCD. So, + * Let the manufacturing path figure this out. + */ + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Please halt target for programming flash"); + return ERROR_TARGET_NOT_HALTED; + } + + if (mspm0_info->did == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + /* + * Pick a copy of the current protection config for later restoration + * We need to restore these regs after every write, so instead of trying + * to figure things out on the fly, we just context save and restore + */ + for (unsigned int i = 0; i < mspm0_info->protect_reg_count; i++) { + retval = target_read_u32(target, + mspm0_info->protect_reg_base + (i * 4), + &protect_reg_cache[i]); + if (retval != ERROR_OK) { + LOG_ERROR("Failed saving flashctl protection status"); + return retval; + } + } + + /* Add proper memory offset for bank being written to */ + unsigned int addr = bank->base + offset; + + while (count) { + unsigned int num_bytes_to_write; + uint32_t bytes_en; + + /* + * If count is not 64 bit aligned, we will do byte wise op to keep things simple + * Usually this might mean we need to additional write ops towards + * trailing edge, but that is a tiny penalty for image downloads. + * NOTE: we are going to assume the device does not support multi-word + * programming - there does not seem to be discoverability! + */ + if (count < mspm0_info->flash_word_size_bytes) + num_bytes_to_write = count; + else + num_bytes_to_write = mspm0_info->flash_word_size_bytes; + + /* Data bytes to write */ + bytes_en = (1 << num_bytes_to_write) - 1; + /* ECC chunks to write */ + switch (mspm0_info->flash_word_size_bytes) { + case 8: + bytes_en |= BIT(8); + break; + case 16: + bytes_en |= BIT(16); + bytes_en |= (num_bytes_to_write > 8) ? BIT(17) : 0; + break; + default: + LOG_ERROR("Invalid flash_word_size_bytes %d", + mspm0_info->flash_word_size_bytes); + return ERROR_FAIL; + } + + retval = mspm0_fctl_cfg_command(bank, addr, + (FCTL_CMDTYPE_COMMAND_PROGRAM | FCTL_CMDTYPE_SIZE_ONEWORD), + bytes_en); + if (retval != ERROR_OK) + return retval; + + retval = mspm0_fctl_unprotect_sector(bank, addr); + if (retval != ERROR_OK) + return retval; + + retval = target_write_buffer(target, FCTL_REG_CMDDATA0, num_bytes_to_write, buffer); + if (retval != ERROR_OK) + return retval; + + addr += num_bytes_to_write; + buffer += num_bytes_to_write; + count -= num_bytes_to_write; + + retval = target_write_u32(target, FCTL_REG_CMDEXEC, FCTL_CMDEXEC_VAL_EXECUTE); + if (retval != ERROR_OK) + return retval; + + retval = mspm0_fctl_wait_cmd_ok(bank); + if (retval != ERROR_OK) + return retval; + } + + /* + * TRM Says: + * Note that the CMDWEPROTx registers are reset to a protected state + * at the end of all program and erase operations. These registers + * must be re-configured by software before a new operation is + * initiated + * Let us just Dump the protection registers back to the system. + * That way we retain the protection status as requested by the user + */ + for (unsigned int i = 0; i < mspm0_info->protect_reg_count; i++) { + retval = target_write_u32(target, + mspm0_info->protect_reg_base + (i * 4), + protect_reg_cache[i]); + if (retval != ERROR_OK) { + LOG_ERROR("Failed re-applying protection status of flashctl"); + return retval; + } + } + + return ERROR_OK; +} + +static int mspm0_probe(struct flash_bank *bank) +{ + struct mspm0_flash_bank *mspm0_info = bank->driver_priv; + + /* + * If this is a mspm0 chip, it has flash; probe() is just + * to figure out how much is present. Only do it once. + */ + if (mspm0_info->did != 0) + return ERROR_OK; + + /* + * mspm0_read_part_info() already handled error checking and + * reporting. Note that it doesn't write, so we don't care about + * whether the target is halted or not. + */ + int retval = mspm0_read_part_info(bank); + if (retval != ERROR_OK) + return retval; + + if (bank->sectors) { + free(bank->sectors); + bank->sectors = NULL; + } + + bank->write_start_alignment = 4; + bank->write_end_alignment = 4; + + switch (bank->base) { + case MSPM0_FLASH_BASE_NONMAIN: + bank->size = 1024; + bank->num_sectors = 0x1; + mspm0_info->protect_reg_base = FCTL_REG_CMDWEPROTNM; + mspm0_info->protect_reg_count = 1; + break; + case MSPM0_FLASH_BASE_MAIN: + bank->size = (mspm0_info->main_flash_size_kb * 1024); + bank->num_sectors = bank->size / mspm0_info->sector_size; + /* + * If the feature version bit read from the FCTL_REG_DESC is + * greater than or equal to 0xA then it means that the device + * will exclusively use CMDWEPROTB ONLY for MAIN memory protection + */ + if (mspm0_info->flash_version >= FCTL_FEATURE_VER_B) { + mspm0_info->protect_reg_base = FCTL_REG_CMDWEPROTB; + mspm0_info->protect_reg_count = 1; + } else { + mspm0_info->protect_reg_base = FCTL_REG_CMDWEPROTA; + mspm0_info->protect_reg_count = 3; + } + break; + case MSPM0_FLASH_BASE_DATA: + if (!mspm0_info->data_flash_size_kb) { + LOG_INFO("Data region NOT available!"); + bank->size = 0x0; + bank->num_sectors = 0x0; + return ERROR_OK; + } + /* + * Any MSPM0 device containing data bank will have a flashctl + * feature version of 0xA or higher. Since data bank is treated + * like MAIN memory, it will also exclusively use CMDWEPROTB for + * protection. + */ + bank->size = (mspm0_info->data_flash_size_kb * 1024); + bank->num_sectors = bank->size / mspm0_info->sector_size; + mspm0_info->protect_reg_base = FCTL_REG_CMDWEPROTB; + mspm0_info->protect_reg_count = 1; + break; + default: + LOG_ERROR("Invalid bank address " TARGET_ADDR_FMT, + bank->base); + return ERROR_FAIL; + } + + bank->sectors = calloc(bank->num_sectors, sizeof(struct flash_sector)); + if (!bank->sectors) { + LOG_ERROR("Out of memory for sectors!"); + return ERROR_FAIL; + } + for (unsigned int i = 0; i < bank->num_sectors; i++) { + bank->sectors[i].offset = i * mspm0_info->sector_size; + bank->sectors[i].size = mspm0_info->sector_size; + bank->sectors[i].is_erased = -1; + } + + return ERROR_OK; +} + +const struct flash_driver mspm0_flash = { + .name = "mspm0", + .flash_bank_command = mspm0_flash_bank_command, + .erase = mspm0_erase, + .protect = NULL, + .write = mspm0_write, + .read = default_flash_read, + .probe = mspm0_probe, + .auto_probe = mspm0_probe, + .erase_check = default_flash_blank_check, + .protect_check = mspm0_protect_check, + .info = get_mspm0_info, + .free_driver_priv = default_flash_free_driver_priv, +}; diff --git a/src/flash/nor/stm32l4x.c b/src/flash/nor/stm32l4x.c index 3062fca..fa57db8 100644 --- a/src/flash/nor/stm32l4x.c +++ b/src/flash/nor/stm32l4x.c @@ -370,11 +370,15 @@ static const struct stm32l4_rev stm32u53_u54xx_revs[] = { static const struct stm32l4_rev stm32u57_u58xx_revs[] = { { 0x1000, "A" }, { 0x1001, "Z" }, { 0x1003, "Y" }, { 0x2000, "B" }, - { 0x2001, "X" }, { 0x3000, "C" }, { 0x3001, "W" }, + { 0x2001, "X" }, { 0x3000, "C" }, { 0x3001, "W" }, { 0x3007, "U" }, }; static const struct stm32l4_rev stm32u59_u5axx_revs[] = { - { 0x3001, "X" }, + { 0x3001, "X" }, { 0x3002, "W" }, +}; + +static const struct stm32l4_rev stm32u5f_u5gxx_revs[] = { + { 0x1000, "A" }, { 0x1001, "Z" }, }; static const struct stm32l4_rev stm32wba5x_revs[] = { @@ -675,6 +679,18 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .otp_size = 512, }, { + .id = DEVID_STM32U5F_U5GXX, + .revs = stm32u5f_u5gxx_revs, + .num_revs = ARRAY_SIZE(stm32u5f_u5gxx_revs), + .device_str = "STM32U5F/U5Gxx", + .max_flash_size_kb = 4096, + .flags = F_HAS_DUAL_BANK | F_QUAD_WORD_PROG | F_HAS_TZ | F_HAS_L5_FLASH_REGS, + .flash_regs_base = 0x40022000, + .fsize_addr = 0x0BFA07A0, + .otp_base = 0x0BFA0000, + .otp_size = 512, + }, + { .id = DEVID_STM32WBA5X, .revs = stm32wba5x_revs, .num_revs = ARRAY_SIZE(stm32wba5x_revs), @@ -2095,6 +2111,7 @@ static int stm32l4_probe(struct flash_bank *bank) case DEVID_STM32U53_U54XX: case DEVID_STM32U57_U58XX: case DEVID_STM32U59_U5AXX: + case DEVID_STM32U5F_U5GXX: /* according to RM0456 Rev 4, Chapter 7.3.1 and 7.9.13 * U53x/U54x have 512K max flash size: * 512K variants are always in DUAL BANK mode @@ -2102,7 +2119,7 @@ static int stm32l4_probe(struct flash_bank *bank) * U57x/U58x have 2M max flash size: * 2M variants are always in DUAL BANK mode * 1M variants can be in DUAL BANK mode if FLASH_OPTR:DUALBANK is set - * U59x/U5Ax have 4M max flash size: + * U59x/U5Ax/U5Fx/U5Gx have 4M max flash size: * 4M variants are always in DUAL BANK mode * 2M variants can be in DUAL BANK mode if FLASH_OPTR:DUALBANK is set * Note: flash banks are always contiguous diff --git a/src/flash/nor/stm32l4x.h b/src/flash/nor/stm32l4x.h index f152c9f..3199d4f 100644 --- a/src/flash/nor/stm32l4x.h +++ b/src/flash/nor/stm32l4x.h @@ -103,6 +103,7 @@ #define DEVID_STM32L4R_L4SXX 0x470 #define DEVID_STM32L4P_L4QXX 0x471 #define DEVID_STM32L55_L56XX 0x472 +#define DEVID_STM32U5F_U5GXX 0x476 #define DEVID_STM32G49_G4AXX 0x479 #define DEVID_STM32U59_U5AXX 0x481 #define DEVID_STM32U57_U58XX 0x482 diff --git a/src/helper/command.c b/src/helper/command.c index d90d341..3d4379d 100644 --- a/src/helper/command.c +++ b/src/helper/command.c @@ -41,6 +41,7 @@ static int jim_command_dispatch(Jim_Interp *interp, int argc, Jim_Obj * const *a static int help_add_command(struct command_context *cmd_ctx, const char *cmd_name, const char *help_text, const char *usage_text); static int help_del_command(struct command_context *cmd_ctx, const char *cmd_name); +static enum command_mode get_command_mode(Jim_Interp *interp, const char *cmd_name); /* set of functions to wrap jimtcl internal data */ static inline bool jimcmd_is_proc(Jim_Cmd *cmd) @@ -673,7 +674,7 @@ COMMAND_HANDLER(handle_echo) } if (CMD_ARGC != 1) - return ERROR_FAIL; + return ERROR_COMMAND_SYNTAX_ERROR; LOG_USER("%s", CMD_ARGV[0]); return ERROR_OK; @@ -779,24 +780,7 @@ static COMMAND_HELPER(command_help_show, struct help_entry *c, if (is_match && show_help) { char *msg; - /* TODO: factorize jim_command_mode() to avoid running jim command here */ - char *request = alloc_printf("command mode %s", c->cmd_name); - if (!request) { - LOG_ERROR("Out of memory"); - return ERROR_FAIL; - } - int retval = Jim_Eval(CMD_CTX->interp, request); - free(request); - enum command_mode mode = COMMAND_UNKNOWN; - if (retval != JIM_ERR) { - const char *result = Jim_GetString(Jim_GetResult(CMD_CTX->interp), NULL); - if (!strcmp(result, "any")) - mode = COMMAND_ANY; - else if (!strcmp(result, "config")) - mode = COMMAND_CONFIG; - else if (!strcmp(result, "exec")) - mode = COMMAND_EXEC; - } + enum command_mode mode = get_command_mode(CMD_CTX->interp, c->cmd_name); /* Normal commands are runtime-only; highlight exceptions */ if (mode != COMMAND_EXEC) { @@ -809,6 +793,7 @@ static COMMAND_HELPER(command_help_show, struct help_entry *c, case COMMAND_ANY: stage_msg = " (command valid any time)"; break; + case COMMAND_UNKNOWN: default: stage_msg = " (?mode error?)"; break; @@ -817,11 +802,13 @@ static COMMAND_HELPER(command_help_show, struct help_entry *c, } else msg = alloc_printf("%s", c->help ? c->help : ""); - if (msg) { - command_help_show_wrap(msg, n + 3, n + 3); - free(msg); - } else - return -ENOMEM; + if (!msg) { + LOG_ERROR("Out of memory"); + return ERROR_FAIL; + } + + command_help_show_wrap(msg, n + 3, n + 3); + free(msg); } return ERROR_OK; @@ -936,35 +923,41 @@ static int jim_command_dispatch(Jim_Interp *interp, int argc, Jim_Obj * const *a return retval; } +static enum command_mode get_command_mode(Jim_Interp *interp, const char *cmd_name) +{ + if (!cmd_name) + return COMMAND_UNKNOWN; + + Jim_Obj *s = Jim_NewStringObj(interp, cmd_name, -1); + Jim_IncrRefCount(s); + Jim_Cmd *cmd = Jim_GetCommand(interp, s, JIM_NONE); + Jim_DecrRefCount(interp, s); + + if (!cmd || !(jimcmd_is_proc(cmd) || jimcmd_is_oocd_command(cmd))) + return COMMAND_UNKNOWN; + + /* tcl proc */ + if (jimcmd_is_proc(cmd)) + return COMMAND_ANY; + + struct command *c = jimcmd_privdata(cmd); + return c->mode; +} + static int jim_command_mode(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { struct command_context *cmd_ctx = current_command_context(interp); - enum command_mode mode; + enum command_mode mode = cmd_ctx->mode; if (argc > 1) { char *full_name = alloc_concatenate_strings(argc - 1, argv + 1); if (!full_name) return JIM_ERR; - Jim_Obj *s = Jim_NewStringObj(interp, full_name, -1); - Jim_IncrRefCount(s); - Jim_Cmd *cmd = Jim_GetCommand(interp, s, JIM_NONE); - Jim_DecrRefCount(interp, s); - free(full_name); - if (!cmd || !(jimcmd_is_proc(cmd) || jimcmd_is_oocd_command(cmd))) { - Jim_SetResultString(interp, "unknown", -1); - return JIM_OK; - } - if (jimcmd_is_proc(cmd)) { - /* tcl proc */ - mode = COMMAND_ANY; - } else { - struct command *c = jimcmd_privdata(cmd); + mode = get_command_mode(interp, full_name); - mode = c->mode; - } - } else - mode = cmd_ctx->mode; + free(full_name); + } const char *mode_str; switch (mode) { @@ -977,6 +970,7 @@ static int jim_command_mode(Jim_Interp *interp, int argc, Jim_Obj *const *argv) case COMMAND_EXEC: mode_str = "exec"; break; + case COMMAND_UNKNOWN: default: mode_str = "unknown"; break; diff --git a/src/helper/log.h b/src/helper/log.h index e2bb131..ac24f8e 100644 --- a/src/helper/log.h +++ b/src/helper/log.h @@ -85,7 +85,8 @@ struct log_callback { int log_add_callback(log_callback_fn fn, void *priv); int log_remove_callback(log_callback_fn fn, void *priv); -char *alloc_vprintf(const char *fmt, va_list ap); +char *alloc_vprintf(const char *fmt, va_list ap) + __attribute__ ((format (PRINTF_ATTRIBUTE_FORMAT, 1, 0))); char *alloc_printf(const char *fmt, ...) __attribute__ ((format (PRINTF_ATTRIBUTE_FORMAT, 1, 2))); diff --git a/src/jtag/adapter.c b/src/jtag/adapter.c index 04942f7..2fcbd60 100644 --- a/src/jtag/adapter.c +++ b/src/jtag/adapter.c @@ -392,37 +392,22 @@ COMMAND_HANDLER(handle_adapter_name) return ERROR_OK; } -COMMAND_HANDLER(adapter_transports_command) +COMMAND_HANDLER(dump_adapter_driver_list) { - char **transports; - int retval; - - retval = CALL_COMMAND_HANDLER(transport_list_parse, &transports); - if (retval != ERROR_OK) - return retval; - - retval = allow_transports(CMD_CTX, (const char **)transports); - - if (retval != ERROR_OK) { - for (unsigned int i = 0; transports[i]; i++) - free(transports[i]); - free(transports); + for (unsigned int i = 0; adapter_drivers[i]; i++) { + const char *name = adapter_drivers[i]->name; + command_print(CMD, "%u: %s", i + 1, name); } - return retval; + + return ERROR_OK; } COMMAND_HANDLER(handle_adapter_list_command) { - if (strcmp(CMD_NAME, "list") == 0 && CMD_ARGC > 0) + if (CMD_ARGC) return ERROR_COMMAND_SYNTAX_ERROR; - command_print(CMD, "The following debug adapters are available:"); - for (unsigned int i = 0; adapter_drivers[i]; i++) { - const char *name = adapter_drivers[i]->name; - command_print(CMD, "%u: %s", i + 1, name); - } - - return ERROR_OK; + return CALL_COMMAND_HANDLER(dump_adapter_driver_list); } COMMAND_HANDLER(handle_adapter_driver_command) @@ -459,7 +444,8 @@ COMMAND_HANDLER(handle_adapter_driver_command) */ LOG_ERROR("The specified debug interface was not found (%s)", CMD_ARGV[0]); - CALL_COMMAND_HANDLER(handle_adapter_list_command); + command_print(CMD, "The following adapter drivers are available:"); + CALL_COMMAND_HANDLER(dump_adapter_driver_list); return ERROR_JTAG_INVALID_INTERFACE; } @@ -1138,13 +1124,6 @@ static const struct command_registration adapter_command_handlers[] = { .chain = adapter_srst_command_handlers, }, { - .name = "transports", - .handler = adapter_transports_command, - .mode = COMMAND_CONFIG, - .help = "Declare transports the adapter supports.", - .usage = "transport ...", - }, - { .name = "usb", .mode = COMMAND_ANY, .help = "usb adapter command group", diff --git a/src/jtag/drivers/bcm2835gpio.c b/src/jtag/drivers/bcm2835gpio.c index 095601f..e8689aa 100644 --- a/src/jtag/drivers/bcm2835gpio.c +++ b/src/jtag/drivers/bcm2835gpio.c @@ -427,7 +427,7 @@ static int bcm2835gpio_blink(bool on) return ERROR_OK; } -static struct bitbang_interface bcm2835gpio_bitbang = { +static const struct bitbang_interface bcm2835gpio_bitbang_swd_write_generic = { .read = bcm2835gpio_read, .write = bcm2835gpio_write, .swdio_read = bcm2835_swdio_read, @@ -436,11 +436,19 @@ static struct bitbang_interface bcm2835gpio_bitbang = { .blink = bcm2835gpio_blink, }; +static const struct bitbang_interface bcm2835gpio_bitbang_swd_write_fast = { + .read = bcm2835gpio_read, + .write = bcm2835gpio_write, + .swdio_read = bcm2835_swdio_read, + .swdio_drive = bcm2835_swdio_drive, + .swd_write = bcm2835gpio_swd_write_fast, + .blink = bcm2835gpio_blink, +}; + static int bcm2835gpio_init(void) { LOG_INFO("BCM2835 GPIO JTAG/SWD bitbang driver"); - bitbang_interface = &bcm2835gpio_bitbang; adapter_gpio_config = adapter_gpio_get_config(); if (transport_is_jtag() && !bcm2835gpio_jtag_mode_possible()) { @@ -509,6 +517,8 @@ LOG_INFO("pads conf set to %08x", pads_base[BCM2835_PADS_GPIO_0_27_OFFSET]); initialize_gpio(ADAPTER_GPIO_IDX_TRST); } + const struct bitbang_interface *bcm2835gpio_bitbang = &bcm2835gpio_bitbang_swd_write_generic; + if (transport_is_swd()) { /* swdio and its buffer should be initialized in the order that prevents * two outputs from being connected together. This will occur if the @@ -529,16 +539,18 @@ LOG_INFO("pads conf set to %08x", pads_base[BCM2835_PADS_GPIO_0_27_OFFSET]); if (adapter_gpio_config[ADAPTER_GPIO_IDX_SWCLK].drive == ADAPTER_GPIO_DRIVE_MODE_PUSH_PULL && adapter_gpio_config[ADAPTER_GPIO_IDX_SWDIO].drive == ADAPTER_GPIO_DRIVE_MODE_PUSH_PULL) { LOG_DEBUG("BCM2835 GPIO using fast mode for SWD write"); - bcm2835gpio_bitbang.swd_write = bcm2835gpio_swd_write_fast; + bcm2835gpio_bitbang = &bcm2835gpio_bitbang_swd_write_fast; } else { LOG_DEBUG("BCM2835 GPIO using generic mode for SWD write"); - bcm2835gpio_bitbang.swd_write = bcm2835gpio_swd_write_generic; + assert(bcm2835gpio_bitbang == &bcm2835gpio_bitbang_swd_write_generic); } } initialize_gpio(ADAPTER_GPIO_IDX_SRST); initialize_gpio(ADAPTER_GPIO_IDX_LED); + bitbang_interface = bcm2835gpio_bitbang; + return ERROR_OK; } diff --git a/src/rtos/linux.c b/src/rtos/linux.c index 5467988..5efdc9f 100644 --- a/src/rtos/linux.c +++ b/src/rtos/linux.c @@ -1040,6 +1040,10 @@ static int linux_gdb_thread_packet(struct target *target, return ERROR_TARGET_FAILURE; char *out_str = calloc(MAX_THREADS * 17 + 10, 1); + if (!out_str) { + LOG_ERROR("Out of memory"); + return ERROR_FAIL; + } char *tmp_str = out_str; tmp_str += sprintf(tmp_str, "m"); struct threads *temp = linux_os->thread_list; @@ -1116,23 +1120,13 @@ static int linux_thread_extra_info(struct target *target, while (temp) { if (temp->threadid == threadid) { - char *pid = " PID: "; - char *pid_current = "*PID: "; - char *name = "Name: "; - int str_size = strlen(pid) + strlen(name); - char *tmp_str = calloc(1, str_size + 50); - char *tmp_str_ptr = tmp_str; - - /* discriminate current task */ - if (temp->status == 3) - tmp_str_ptr += sprintf(tmp_str_ptr, "%s", - pid_current); - else - tmp_str_ptr += sprintf(tmp_str_ptr, "%s", pid); - - tmp_str_ptr += sprintf(tmp_str_ptr, "%d, ", (int)temp->pid); - sprintf(tmp_str_ptr, "%s", name); - sprintf(tmp_str_ptr, "%s", temp->name); + char *tmp_str = alloc_printf("%cPID: %" PRIu32 ", Name: %s", + temp->status == 3 ? '*' : ' ', + temp->pid, temp->name); + if (!tmp_str) { + LOG_ERROR("Out of memory"); + return ERROR_FAIL; + } char *hex_str = calloc(1, strlen(tmp_str) * 2 + 1); size_t pkt_len = hexify(hex_str, (const uint8_t *)tmp_str, strlen(tmp_str), strlen(tmp_str) * 2 + 1); diff --git a/src/rtos/rtos.c b/src/rtos/rtos.c index 2bd7d3b..e87e51c 100644 --- a/src/rtos/rtos.c +++ b/src/rtos/rtos.c @@ -370,6 +370,10 @@ int rtos_thread_packet(struct connection *connection, char const *packet, int pa str_size += strlen(detail->extra_info_str); char *tmp_str = calloc(str_size + 9, sizeof(char)); + if (!tmp_str) { + LOG_ERROR("Out of memory"); + return ERROR_FAIL; + } char *tmp_str_ptr = tmp_str; if (detail->thread_name_str) diff --git a/src/rtt/rtt.c b/src/rtt/rtt.c index 42c3ee3..15b9a37 100644 --- a/src/rtt/rtt.c +++ b/src/rtt/rtt.c @@ -140,8 +140,8 @@ int rtt_start(void) addr); rtt.ctrl.address = addr; } else { - LOG_INFO("rtt: No control block found"); - return ERROR_OK; + LOG_ERROR("rtt: No control block found"); + return ERROR_FAIL; } } diff --git a/src/target/aarch64.c b/src/target/aarch64.c index 609965b..ce7808e 100644 --- a/src/target/aarch64.c +++ b/src/target/aarch64.c @@ -2857,6 +2857,14 @@ static void aarch64_deinit_target(struct target *target) struct aarch64_common *aarch64 = target_to_aarch64(target); struct armv8_common *armv8 = &aarch64->armv8_common; struct arm_dpm *dpm = &armv8->dpm; + uint64_t address; + + if (target->state == TARGET_HALTED) { + // Restore the previous state of the target (gp registers, MMU, caches, etc) + int retval = aarch64_restore_one(target, true, &address, false, false); + if (retval != ERROR_OK) + LOG_TARGET_ERROR(target, "Failed to restore target state"); + } if (armv8->debug_ap) dap_put_ap(armv8->debug_ap); diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c index 0c7633b..df897b8 100644 --- a/src/target/arm_adi_v5.c +++ b/src/target/arm_adi_v5.c @@ -2367,7 +2367,9 @@ static int adiv5_jim_spot_configure(struct jim_getopt_info *goi, return e; dap = dap_instance_by_jim_obj(goi->interp, o_t); if (!dap) { - Jim_SetResultString(goi->interp, "DAP name invalid!", -1); + const char *dap_name = Jim_GetString(o_t, NULL); + Jim_SetResultFormatted(goi->interp, "DAP '%s' not found", + dap_name); return JIM_ERR; } if (*dap_p && *dap_p != dap) { diff --git a/src/target/armv7a.c b/src/target/armv7a.c index e22d309..c14155e 100644 --- a/src/target/armv7a.c +++ b/src/target/armv7a.c @@ -101,14 +101,14 @@ static int armv7a_read_midr(struct target *target) armv7a->arch = (midr >> 16) & 0xf; armv7a->variant = (midr >> 20) & 0xf; armv7a->implementor = (midr >> 24) & 0xff; - LOG_DEBUG("%s rev %" PRIx32 ", partnum %" PRIx32 ", arch %" PRIx32 - ", variant %" PRIx32 ", implementor %" PRIx32, - target->cmd_name, - armv7a->rev, - armv7a->partnum, - armv7a->arch, - armv7a->variant, - armv7a->implementor); + LOG_TARGET_DEBUG(target, + "rev %" PRIx32 ", partnum %" PRIx32 ", arch %" PRIx32 + ", variant %" PRIx32 ", implementor %" PRIx32, + armv7a->rev, + armv7a->partnum, + armv7a->arch, + armv7a->variant, + armv7a->implementor); done: dpm->finish(dpm); diff --git a/src/target/armv8.c b/src/target/armv8.c index 50a9f46..4039073 100644 --- a/src/target/armv8.c +++ b/src/target/armv8.c @@ -1686,12 +1686,12 @@ static int armv8_set_core_reg(struct reg *reg, uint8_t *buf) struct arm_reg *armv8_reg = reg->arch_info; struct target *target = armv8_reg->target; struct arm *arm = target_to_arm(target); - uint64_t value = buf_get_u64(buf, 0, reg->size); if (target->state != TARGET_HALTED) return ERROR_TARGET_NOT_HALTED; if (reg->size <= 64) { + uint64_t value = buf_get_u64(buf, 0, reg->size); if (reg == arm->cpsr) armv8_set_cpsr(arm, (uint32_t)value); else { @@ -1699,6 +1699,7 @@ static int armv8_set_core_reg(struct reg *reg, uint8_t *buf) reg->valid = true; } } else if (reg->size <= 128) { + uint64_t value = buf_get_u64(buf, 0, 64); uint64_t hvalue = buf_get_u64(buf + 8, 0, reg->size - 64); buf_set_u64(reg->value, 0, 64, value); diff --git a/src/target/armv8.h b/src/target/armv8.h index 49ab3e5..dba12f9 100644 --- a/src/target/armv8.h +++ b/src/target/armv8.h @@ -162,6 +162,7 @@ struct armv8_cache_common { /* l2 external unified cache if some */ void *l2_cache; int (*flush_all_data_cache)(struct target *target); + int (*invalidate_all_instruction_cache)(struct target *target); int (*display_cache_info)(struct command_invocation *cmd, struct armv8_cache_common *armv8_cache); }; diff --git a/src/target/armv8_cache.c b/src/target/armv8_cache.c index 66d4e00..1c251be 100644 --- a/src/target/armv8_cache.c +++ b/src/target/armv8_cache.c @@ -61,6 +61,7 @@ static int armv8_cache_d_inner_flush_level(struct armv8_common *armv8, struct ar goto done; c_way -= 1; } while (c_way >= 0); + keep_alive(); c_index -= 1; } while (c_index >= 0); @@ -140,6 +141,36 @@ done: return retval; } +static int armv8_cache_i_inner_clean_inval_all(struct armv8_common *armv8) +{ + struct arm_dpm *dpm = armv8->arm.dpm; + int retval; + + retval = armv8_i_cache_sanity_check(armv8); + if (retval != ERROR_OK) + return retval; + + LOG_DEBUG("flushing cache"); + + retval = dpm->prepare(dpm); + if (retval != ERROR_OK) + goto done; + + retval = dpm->instr_execute(dpm, armv8_opcode(armv8, ARMV8_OPC_ICIALLU)); + if (retval != ERROR_OK) + goto done; + + dpm->finish(dpm); + LOG_DEBUG("flushing cache done"); + return retval; + +done: + LOG_ERROR("i-cache invalidate failed"); + dpm->finish(dpm); + + return retval; +} + int armv8_cache_i_inner_inval_virt(struct armv8_common *armv8, target_addr_t va, size_t size) { struct arm_dpm *dpm = armv8->arm.dpm; @@ -252,6 +283,32 @@ static int armv8_flush_all_data(struct target *target) return retval; } +static int armv8_flush_all_instruction(struct target *target) +{ + int retval = ERROR_FAIL; + /* check that armv8_cache is correctly identify */ + struct armv8_common *armv8 = target_to_armv8(target); + if (armv8->armv8_mmu.armv8_cache.info == -1) { + LOG_ERROR("trying to flush un-identified cache"); + return retval; + } + + if (target->smp) { + /* look if all the other target have been flushed in order to flush icache */ + struct target_list *head; + foreach_smp_target(head, target->smp_targets) { + struct target *curr = head->target; + if (curr->state == TARGET_HALTED) { + LOG_TARGET_INFO(curr, "Wait flushing instruction l1."); + retval = armv8_cache_i_inner_clean_inval_all(target_to_armv8(curr)); + } + } + } else { + retval = armv8_cache_i_inner_clean_inval_all(armv8); + } + return retval; +} + static int get_cache_info(struct arm_dpm *dpm, int cl, int ct, uint32_t *cache_reg) { struct armv8_common *armv8 = dpm->arm->arch_info; @@ -411,6 +468,12 @@ int armv8_identify_cache(struct armv8_common *armv8) armv8->armv8_mmu.armv8_cache.flush_all_data_cache = armv8_flush_all_data; } + if (!armv8->armv8_mmu.armv8_cache.invalidate_all_instruction_cache) { + armv8->armv8_mmu.armv8_cache.display_cache_info = + armv8_handle_inner_cache_info_command; + armv8->armv8_mmu.armv8_cache.invalidate_all_instruction_cache = + armv8_flush_all_instruction; + } done: armv8_dpm_modeswitch(dpm, ARM_MODE_ANY); diff --git a/src/target/armv8_opcodes.c b/src/target/armv8_opcodes.c index 2635b3e..0f6c810 100644 --- a/src/target/armv8_opcodes.c +++ b/src/target/armv8_opcodes.c @@ -41,6 +41,7 @@ static const uint32_t a64_opcodes[ARMV8_OPC_NUM] = { [ARMV8_OPC_STRH_IP] = ARMV8_STRH_IP(1, 0), [ARMV8_OPC_STRW_IP] = ARMV8_STRW_IP(1, 0), [ARMV8_OPC_STRD_IP] = ARMV8_STRD_IP(1, 0), + [ARMV8_OPC_ICIALLU] = ARMV8_SYS(SYSTEM_ICIALLU, 0x1F), }; static const uint32_t t32_opcodes[ARMV8_OPC_NUM] = { @@ -68,6 +69,7 @@ static const uint32_t t32_opcodes[ARMV8_OPC_NUM] = { [ARMV8_OPC_STRB_IP] = ARMV8_STRB_IP_T3(1, 0), [ARMV8_OPC_STRH_IP] = ARMV8_STRH_IP_T3(1, 0), [ARMV8_OPC_STRW_IP] = ARMV8_STRW_IP_T3(1, 0), + [ARMV8_OPC_ICIALLU] = ARMV4_5_MCR(15, 0, 0, 7, 5, 0), }; void armv8_select_opcodes(struct armv8_common *armv8, bool state_is_aarch64) diff --git a/src/target/armv8_opcodes.h b/src/target/armv8_opcodes.h index 9200dac..9f18e94 100644 --- a/src/target/armv8_opcodes.h +++ b/src/target/armv8_opcodes.h @@ -72,6 +72,8 @@ #define SYSTEM_DCCISW 0x43F2 #define SYSTEM_DCCSW 0x43D2 #define SYSTEM_ICIVAU 0x5BA9 +/* Attention, SYSTEM_ICIALLU requires rt=0x1f */ +#define SYSTEM_ICIALLU 0x03A8 #define SYSTEM_DCCVAU 0x5BD9 #define SYSTEM_DCCIVAC 0x5BF1 @@ -207,6 +209,7 @@ enum armv8_opcode { ARMV8_OPC_LDRH_IP, ARMV8_OPC_LDRW_IP, ARMV8_OPC_LDRD_IP, + ARMV8_OPC_ICIALLU, ARMV8_OPC_NUM, }; diff --git a/src/target/cortex_a.c b/src/target/cortex_a.c index b32fec2..9c60645 100644 --- a/src/target/cortex_a.c +++ b/src/target/cortex_a.c @@ -2923,14 +2923,12 @@ static int cortex_a_examine_first(struct target *target) armv7a->debug_ap->memaccess_tck = 80; if (!target->dbgbase_set) { - LOG_DEBUG("%s's dbgbase is not set, trying to detect using the ROM table", - target->cmd_name); + LOG_TARGET_DEBUG(target, "dbgbase is not set, trying to detect using the ROM table"); /* Lookup Processor DAP */ retval = dap_lookup_cs_component(armv7a->debug_ap, ARM_CS_C9_DEVTYPE_CORE_DEBUG, &armv7a->debug_base, target->coreid); if (retval != ERROR_OK) { - LOG_ERROR("Can't detect %s's dbgbase from the ROM table; you need to specify it explicitly.", - target->cmd_name); + LOG_TARGET_ERROR(target, "Can't detect dbgbase from the ROM table; you need to specify it explicitly"); return retval; } LOG_DEBUG("Detected core %" PRId32 " dbgbase: " TARGET_ADDR_FMT, @@ -2939,8 +2937,9 @@ static int cortex_a_examine_first(struct target *target) armv7a->debug_base = target->dbgbase; if ((armv7a->debug_base & (1UL<<31)) == 0) - LOG_WARNING("Debug base address for target %s has bit 31 set to 0. Access to debug registers will likely fail!\n" - "Please fix the target configuration.", target_name(target)); + LOG_TARGET_WARNING(target, + "Debug base address has bit 31 set to 0. Access to debug registers will likely fail!\n" + "Please fix the target configuration"); retval = mem_ap_read_atomic_u32(armv7a->debug_ap, armv7a->debug_base + CPUDBG_DIDR, &didr); diff --git a/src/target/espressif/esp32_apptrace.c b/src/target/espressif/esp32_apptrace.c index 3202fd3..3070960 100644 --- a/src/target/espressif/esp32_apptrace.c +++ b/src/target/espressif/esp32_apptrace.c @@ -649,7 +649,7 @@ static int esp32_apptrace_wait4halt(struct esp32_apptrace_cmd_ctx *ctx, struct t if (res != ERROR_OK) return res; if (target->state == TARGET_HALTED) { - LOG_USER("%s: HALTED", target->cmd_name); + LOG_TARGET_USER(target, "HALTED"); break; } alive_sleep(500); diff --git a/src/target/espressif/esp_xtensa_apptrace.c b/src/target/espressif/esp_xtensa_apptrace.c index 5741ab0..313f6ce 100644 --- a/src/target/espressif/esp_xtensa_apptrace.c +++ b/src/target/espressif/esp_xtensa_apptrace.c @@ -277,7 +277,7 @@ static int esp_xtensa_swdbg_activate(struct target *target, int enab) xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod); int res = xtensa_dm_queue_execute(&xtensa->dbg_mod); if (res != ERROR_OK) { - LOG_ERROR("%s: writing DCR failed!", target->cmd_name); + LOG_TARGET_ERROR(target, "writing DCR failed"); return res; } diff --git a/src/target/target.c b/src/target/target.c index b1ec5c2..442e8be 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -31,6 +31,7 @@ #endif #include <helper/align.h> +#include <helper/list.h> #include <helper/nvp.h> #include <helper/time_support.h> #include <jtag/jtag.h> @@ -52,6 +53,13 @@ /* default halt wait timeout (ms) */ #define DEFAULT_HALT_TIMEOUT 5000 +struct target_event_action { + enum target_event event; + Jim_Interp *interp; + Jim_Obj *body; + struct list_head list; +}; + static int target_read_buffer_default(struct target *target, target_addr_t address, uint32_t count, uint8_t *buffer); static int target_write_buffer_default(struct target *target, target_addr_t address, @@ -2200,12 +2208,11 @@ static void target_destroy(struct target *target) jtag_unregister_event_callback(jtag_enable_callback, target); - struct target_event_action *teap = target->event_action; - while (teap) { - struct target_event_action *next = teap->next; + struct target_event_action *teap, *temp; + list_for_each_entry_safe(teap, temp, &target->events_action, list) { + list_del(&teap->list); Jim_DecrRefCount(teap->interp, teap->body); free(teap); - teap = next; } target_free_all_working_areas(target); @@ -4431,15 +4438,17 @@ COMMAND_HANDLER(handle_target_read_memory) return ERROR_COMMAND_ARGUMENT_INVALID; } - const unsigned int width = width_bits / 8; - - if ((addr + (count * width)) < addr) { - command_print(CMD, "read_memory: addr + count wraps to zero"); + if (count > 65536) { + command_print(CMD, "too large read request, exceeds 64K elements"); return ERROR_COMMAND_ARGUMENT_INVALID; } - if (count > 65536) { - command_print(CMD, "read_memory: too large read request, exceeds 64K elements"); + const unsigned int width = width_bits / 8; + /* -1 is needed to handle cases when (addr + count * width) results in zero + * due to overflow. + */ + if ((addr + count * width - 1) < addr) { + command_print(CMD, "memory region wraps over address zero"); return ERROR_COMMAND_ARGUMENT_INVALID; } @@ -4466,13 +4475,13 @@ COMMAND_HANDLER(handle_target_read_memory) retval = target_read_memory(target, addr, width, chunk_len, buffer); if (retval != ERROR_OK) { - LOG_DEBUG("read_memory: read at " TARGET_ADDR_FMT " with width=%u and count=%zu failed", + LOG_DEBUG("read at " TARGET_ADDR_FMT " with width=%u and count=%zu failed", addr, width_bits, chunk_len); /* * FIXME: we append the errmsg to the list of value already read. * Add a way to flush and replace old output, but LOG_DEBUG() it */ - command_print(CMD, "read_memory: failed to read memory"); + command_print(CMD, "failed to read memory"); free(buffer); return retval; } @@ -4508,50 +4517,36 @@ COMMAND_HANDLER(handle_target_read_memory) return ERROR_OK; } -static int target_jim_write_memory(Jim_Interp *interp, int argc, - Jim_Obj * const *argv) +COMMAND_HANDLER(handle_target_write_memory) { /* - * argv[1] = memory address - * argv[2] = desired element width in bits - * argv[3] = list of data to write - * argv[4] = optional "phys" + * CMD_ARGV[0] = memory address + * CMD_ARGV[1] = desired element width in bits + * CMD_ARGV[2] = list of data to write + * CMD_ARGV[3] = optional "phys" */ - if (argc < 4 || argc > 5) { - Jim_WrongNumArgs(interp, 1, argv, "address width data ['phys']"); - return JIM_ERR; - } + if (CMD_ARGC < 3 || CMD_ARGC > 4) + return ERROR_COMMAND_SYNTAX_ERROR; /* Arg 1: Memory address. */ - int e; - jim_wide wide_addr; - e = Jim_GetWide(interp, argv[1], &wide_addr); - - if (e != JIM_OK) - return e; - - target_addr_t addr = (target_addr_t)wide_addr; + target_addr_t addr; + COMMAND_PARSE_NUMBER(u64, CMD_ARGV[0], addr); /* Arg 2: Bit width of one element. */ - long l; - e = Jim_GetLong(interp, argv[2], &l); - - if (e != JIM_OK) - return e; + unsigned int width_bits; + COMMAND_PARSE_NUMBER(uint, CMD_ARGV[1], width_bits); - const unsigned int width_bits = l; - size_t count = Jim_ListLength(interp, argv[3]); + /* Arg 3: Elements to write. */ + size_t count = Jim_ListLength(CMD_CTX->interp, CMD_JIMTCL_ARGV[2]); /* Arg 4: Optional 'phys'. */ bool is_phys = false; - if (argc > 4) { - const char *phys = Jim_GetString(argv[4], NULL); - - if (strcmp(phys, "phys")) { - Jim_SetResultFormatted(interp, "invalid argument '%s', must be 'phys'", phys); - return JIM_ERR; + if (CMD_ARGC == 4) { + if (strcmp(CMD_ARGV[3], "phys")) { + command_print(CMD, "invalid argument '%s', must be 'phys'", CMD_ARGV[3]); + return ERROR_COMMAND_ARGUMENT_INVALID; } is_phys = true; @@ -4564,32 +4559,32 @@ static int target_jim_write_memory(Jim_Interp *interp, int argc, case 64: break; default: - Jim_SetResultString(interp, "invalid width, must be 8, 16, 32 or 64", -1); - return JIM_ERR; + command_print(CMD, "invalid width, must be 8, 16, 32 or 64"); + return ERROR_COMMAND_ARGUMENT_INVALID; } - const unsigned int width = width_bits / 8; - - if ((addr + (count * width)) < addr) { - Jim_SetResultString(interp, "write_memory: addr + len wraps to zero", -1); - return JIM_ERR; + if (count > 65536) { + command_print(CMD, "too large memory write request, exceeds 64K elements"); + return ERROR_COMMAND_ARGUMENT_INVALID; } - if (count > 65536) { - Jim_SetResultString(interp, "write_memory: too large memory write request, exceeds 64K elements", -1); - return JIM_ERR; + const unsigned int width = width_bits / 8; + /* -1 is needed to handle cases when (addr + count * width) results in zero + * due to overflow. + */ + if ((addr + count * width - 1) < addr) { + command_print(CMD, "memory region wraps over address zero"); + return ERROR_COMMAND_ARGUMENT_INVALID; } - struct command_context *cmd_ctx = current_command_context(interp); - assert(cmd_ctx); - struct target *target = get_current_target(cmd_ctx); + struct target *target = get_current_target(CMD_CTX); const size_t buffersize = 4096; uint8_t *buffer = malloc(buffersize); if (!buffer) { LOG_ERROR("Failed to allocate memory"); - return JIM_ERR; + return ERROR_FAIL; } size_t j = 0; @@ -4599,9 +4594,14 @@ static int target_jim_write_memory(Jim_Interp *interp, int argc, const size_t chunk_len = MIN(count, max_chunk_len); for (size_t i = 0; i < chunk_len; i++, j++) { - Jim_Obj *tmp = Jim_ListGetIndex(interp, argv[3], j); + Jim_Obj *tmp = Jim_ListGetIndex(CMD_CTX->interp, CMD_JIMTCL_ARGV[2], j); jim_wide element_wide; - Jim_GetWide(interp, tmp, &element_wide); + int jimretval = Jim_GetWide(CMD_CTX->interp, tmp, &element_wide); + if (jimretval != JIM_OK) { + command_print(CMD, "invalid value \"%s\"", Jim_GetString(tmp, NULL)); + free(buffer); + return ERROR_COMMAND_ARGUMENT_INVALID; + } const uint64_t v = element_wide; @@ -4631,11 +4631,11 @@ static int target_jim_write_memory(Jim_Interp *interp, int argc, retval = target_write_memory(target, addr, width, chunk_len, buffer); if (retval != ERROR_OK) { - LOG_ERROR("write_memory: write at " TARGET_ADDR_FMT " with width=%u and count=%zu failed", + LOG_DEBUG("write at " TARGET_ADDR_FMT " with width=%u and count=%zu failed", addr, width_bits, chunk_len); - Jim_SetResultString(interp, "write_memory: failed to write memory", -1); - e = JIM_ERR; - break; + command_print(CMD, "failed to write memory"); + free(buffer); + return retval; } addr += chunk_len * width; @@ -4643,7 +4643,7 @@ static int target_jim_write_memory(Jim_Interp *interp, int argc, free(buffer); - return e; + return ERROR_OK; } /* FIX? should we propagate errors here rather than printing them @@ -4654,7 +4654,7 @@ void target_handle_event(struct target *target, enum target_event e) struct target_event_action *teap; int retval; - for (teap = target->event_action; teap; teap = teap->next) { + list_for_each_entry(teap, &target->events_action, list) { if (teap->event == e) { LOG_DEBUG("target: %s (%s) event: %d (%s) action: %s", target_name(target), @@ -4693,63 +4693,46 @@ void target_handle_event(struct target *target, enum target_event e) } } -static int target_jim_get_reg(Jim_Interp *interp, int argc, - Jim_Obj * const *argv) +COMMAND_HANDLER(handle_target_get_reg) { - bool force = false; + if (CMD_ARGC < 1 || CMD_ARGC > 2) + return ERROR_COMMAND_SYNTAX_ERROR; - if (argc == 3) { - const char *option = Jim_GetString(argv[1], NULL); + bool force = false; + Jim_Obj *next_argv = CMD_JIMTCL_ARGV[0]; - if (!strcmp(option, "-force")) { - argc--; - argv++; - force = true; - } else { - Jim_SetResultFormatted(interp, "invalid option '%s'", option); - return JIM_ERR; + if (CMD_ARGC == 2) { + if (strcmp(CMD_ARGV[0], "-force")) { + command_print(CMD, "invalid argument '%s', must be '-force'", CMD_ARGV[0]); + return ERROR_COMMAND_ARGUMENT_INVALID; } - } - if (argc != 2) { - Jim_WrongNumArgs(interp, 1, argv, "[-force] list"); - return JIM_ERR; + force = true; + next_argv = CMD_JIMTCL_ARGV[1]; } - const int length = Jim_ListLength(interp, argv[1]); - - Jim_Obj *result_dict = Jim_NewDictObj(interp, NULL, 0); + const int length = Jim_ListLength(CMD_CTX->interp, next_argv); - if (!result_dict) - return JIM_ERR; - - struct command_context *cmd_ctx = current_command_context(interp); - assert(cmd_ctx); - const struct target *target = get_current_target(cmd_ctx); + const struct target *target = get_current_target(CMD_CTX); for (int i = 0; i < length; i++) { - Jim_Obj *elem = Jim_ListGetIndex(interp, argv[1], i); - - if (!elem) - return JIM_ERR; + Jim_Obj *elem = Jim_ListGetIndex(CMD_CTX->interp, next_argv, i); const char *reg_name = Jim_String(elem); - struct reg *reg = register_get_by_name(target->reg_cache, reg_name, - false); + struct reg *reg = register_get_by_name(target->reg_cache, reg_name, false); if (!reg || !reg->exist) { - Jim_SetResultFormatted(interp, "unknown register '%s'", reg_name); - return JIM_ERR; + command_print(CMD, "unknown register '%s'", reg_name); + return ERROR_COMMAND_ARGUMENT_INVALID; } if (force || !reg->valid) { int retval = reg->type->get(reg); if (retval != ERROR_OK) { - Jim_SetResultFormatted(interp, "failed to read register '%s'", - reg_name); - return JIM_ERR; + command_print(CMD, "failed to read register '%s'", reg_name); + return retval; } } @@ -4757,27 +4740,15 @@ static int target_jim_get_reg(Jim_Interp *interp, int argc, if (!reg_value) { LOG_ERROR("Failed to allocate memory"); - return JIM_ERR; + return ERROR_FAIL; } - char *tmp = alloc_printf("0x%s", reg_value); + command_print(CMD, "%s 0x%s", reg_name, reg_value); free(reg_value); - - if (!tmp) { - LOG_ERROR("Failed to allocate memory"); - return JIM_ERR; - } - - Jim_DictAddElement(interp, result_dict, elem, - Jim_NewStringObj(interp, tmp, -1)); - - free(tmp); } - Jim_SetResult(interp, result_dict); - - return JIM_OK; + return ERROR_OK; } COMMAND_HANDLER(handle_set_reg_command) @@ -4846,7 +4817,7 @@ bool target_has_event_action(const struct target *target, enum target_event even { struct target_event_action *teap; - for (teap = target->event_action; teap; teap = teap->next) { + list_for_each_entry(teap, &target->events_action, list) { if (teap->event == event) return true; } @@ -4966,13 +4937,14 @@ no_params: { struct target_event_action *teap; - teap = target->event_action; /* replace existing? */ - while (teap) { + list_for_each_entry(teap, &target->events_action, list) if (teap->event == (enum target_event)n->value) break; - teap = teap->next; - } + + /* not found! */ + if (&teap->list == &target->events_action) + teap = NULL; if (goi->is_configure) { /* START_DEPRECATED_TPIU */ @@ -4980,6 +4952,17 @@ no_params: LOG_INFO("DEPRECATED target event %s; use TPIU events {pre,post}-{enable,disable}", n->name); /* END_DEPRECATED_TPIU */ + jim_getopt_obj(goi, &o); + if (Jim_Length(o) == 0) { + /* empty action, drop existing one */ + if (teap) { + list_del(&teap->list); + Jim_DecrRefCount(teap->interp, teap->body); + free(teap); + } + break; + } + bool replace = true; if (!teap) { /* create new */ @@ -4988,7 +4971,6 @@ no_params: } teap->event = n->value; teap->interp = goi->interp; - jim_getopt_obj(goi, &o); if (teap->body) Jim_DecrRefCount(teap->interp, teap->body); teap->body = Jim_DuplicateObj(goi->interp, o); @@ -5006,8 +4988,7 @@ no_params: if (!replace) { /* add to head of event list */ - teap->next = target->event_action; - target->event_action = teap; + list_add(&teap->list, &target->events_action); } Jim_SetEmptyResult(goi->interp); } else { @@ -5422,19 +5403,19 @@ COMMAND_HANDLER(handle_target_wait_state) COMMAND_HANDLER(handle_target_event_list) { struct target *target = get_current_target(CMD_CTX); - struct target_event_action *teap = target->event_action; + struct target_event_action *teap; command_print(CMD, "Event actions for target %s\n", target_name(target)); command_print(CMD, "%-25s | Body", "Event"); command_print(CMD, "------------------------- | " "----------------------------------------"); - while (teap) { + + list_for_each_entry(teap, &target->events_action, list) command_print(CMD, "%-25s | %s", target_event_name(teap->event), Jim_GetString(teap->body, NULL)); - teap = teap->next; - } + command_print(CMD, "***END***"); return ERROR_OK; } @@ -5569,9 +5550,9 @@ static const struct command_registration target_instance_command_handlers[] = { { .name = "get_reg", .mode = COMMAND_EXEC, - .jim_handler = target_jim_get_reg, + .handler = handle_target_get_reg, .help = "Get register values from the target", - .usage = "list", + .usage = "[-force] list", }, { .name = "set_reg", @@ -5590,7 +5571,7 @@ static const struct command_registration target_instance_command_handlers[] = { { .name = "write_memory", .mode = COMMAND_EXEC, - .jim_handler = target_jim_write_memory, + .handler = handle_target_write_memory, .help = "Write Tcl list of 8/16/32/64 bit numbers to target memory", .usage = "address width data ['phys']", }, @@ -5787,6 +5768,8 @@ static int target_create(struct jim_getopt_info *goi) target->halt_issued = false; + INIT_LIST_HEAD(&target->events_action); + /* initialize trace information */ target->trace_info = calloc(1, sizeof(struct trace)); if (!target->trace_info) { @@ -5807,6 +5790,16 @@ static int target_create(struct jim_getopt_info *goi) target->gdb_port_override = NULL; target->gdb_max_connections = 1; + cp = Jim_GetString(new_cmd, NULL); + target->cmd_name = strdup(cp); + if (!target->cmd_name) { + LOG_ERROR("Out of memory"); + free(target->trace_info); + free(target->type); + free(target); + return JIM_ERR; + } + /* Do the rest as "configure" options */ goi->is_configure = true; e = target_configure(goi, target); @@ -5843,19 +5836,6 @@ static int target_create(struct jim_getopt_info *goi) target->endianness = TARGET_LITTLE_ENDIAN; } - cp = Jim_GetString(new_cmd, NULL); - target->cmd_name = strdup(cp); - if (!target->cmd_name) { - LOG_ERROR("Out of memory"); - rtos_destroy(target); - free(target->gdb_port_override); - free(target->trace_info); - free(target->type); - free(target->private_config); - free(target); - return JIM_ERR; - } - if (target->type->target_create) { e = (*(target->type->target_create))(target, goi->interp); if (e != ERROR_OK) { @@ -6707,9 +6687,9 @@ static const struct command_registration target_exec_command_handlers[] = { { .name = "get_reg", .mode = COMMAND_EXEC, - .jim_handler = target_jim_get_reg, + .handler = handle_target_get_reg, .help = "Get register values from the target", - .usage = "list", + .usage = "[-force] list", }, { .name = "set_reg", @@ -6728,7 +6708,7 @@ static const struct command_registration target_exec_command_handlers[] = { { .name = "write_memory", .mode = COMMAND_EXEC, - .jim_handler = target_jim_write_memory, + .handler = handle_target_write_memory, .help = "Write Tcl list of 8/16/32/64 bit numbers to target memory", .usage = "address width data ['phys']", }, diff --git a/src/target/target.h b/src/target/target.h index 33aba09..d16c3a0 100644 --- a/src/target/target.h +++ b/src/target/target.h @@ -142,7 +142,7 @@ struct target { */ bool running_alg; - struct target_event_action *event_action; + struct list_head events_action; bool reset_halt; /* attempt resetting the CPU into the halted mode? */ target_addr_t working_area; /* working area (initialised RAM). Evaluated @@ -301,13 +301,6 @@ enum target_event { TARGET_EVENT_SEMIHOSTING_USER_CMD_0X107 = 0x107, }; -struct target_event_action { - enum target_event event; - Jim_Interp *interp; - Jim_Obj *body; - struct target_event_action *next; -}; - bool target_has_event_action(const struct target *target, enum target_event event); struct target_event_callback { diff --git a/src/transport/transport.c b/src/transport/transport.c index c7293e7..0af1360 100644 --- a/src/transport/transport.c +++ b/src/transport/transport.c @@ -166,54 +166,6 @@ struct transport *get_current_transport(void) * Infrastructure for Tcl interface to transports. */ -/** - * Makes and stores a copy of a set of transports passed as - * parameters to a command. - * - * @param vector where the resulting copy is stored, as an argv-style - * NULL-terminated vector. - */ -COMMAND_HELPER(transport_list_parse, char ***vector) -{ - char **argv; - unsigned int n = CMD_ARGC; - unsigned int j = 0; - - *vector = NULL; - - if (n < 1) - return ERROR_COMMAND_SYNTAX_ERROR; - - /* our return vector must be NULL terminated */ - argv = calloc(n + 1, sizeof(char *)); - if (!argv) - return ERROR_FAIL; - - for (unsigned int i = 0; i < n; i++) { - struct transport *t; - - for (t = transport_list; t; t = t->next) { - if (strcmp(t->name, CMD_ARGV[i]) != 0) - continue; - argv[j++] = strdup(CMD_ARGV[i]); - break; - } - if (!t) { - LOG_ERROR("no such transport '%s'", CMD_ARGV[i]); - goto fail; - } - } - - *vector = argv; - return ERROR_OK; - -fail: - for (unsigned int i = 0; i < n; i++) - free(argv[i]); - free(argv); - return ERROR_FAIL; -} - COMMAND_HANDLER(handle_transport_init) { LOG_DEBUG("%s", __func__); diff --git a/src/transport/transport.h b/src/transport/transport.h index 00d8b07..2e3dcc6 100644 --- a/src/transport/transport.h +++ b/src/transport/transport.h @@ -77,8 +77,6 @@ struct transport *get_current_transport(void); int transport_register_commands(struct command_context *ctx); -COMMAND_HELPER(transport_list_parse, char ***vector); - int allow_transports(struct command_context *ctx, const char * const *vector); bool transport_is_jtag(void); diff --git a/tcl/board/ti_am261_launchpad.cfg b/tcl/board/ti_am261_launchpad.cfg new file mode 100644 index 0000000..c6c4609 --- /dev/null +++ b/tcl/board/ti_am261_launchpad.cfg @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (C) 2025 Texas Instruments Incorporated - https://www.ti.com/ +# +# Texas Instruments AM261 Launchpad +# https://www.ti.com/tool/LP-AM261 +# + +# AM263 Launchpad has an xds110 onboard. +source [find interface/xds110.cfg] + +transport select jtag + +# default JTAG configuration has only SRST and no TRST +reset_config srst_only srst_push_pull + +# delay after SRST goes inactive +adapter srst delay 20 + +if { ![info exists SOC] } { + set SOC am261 +} + +source [find target/ti_k3.cfg] + +adapter speed 250 diff --git a/tcl/board/ti_am263p_launchpad.cfg b/tcl/board/ti_am263p_launchpad.cfg new file mode 100644 index 0000000..96e06fa --- /dev/null +++ b/tcl/board/ti_am263p_launchpad.cfg @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (C) 2025 Texas Instruments Incorporated - https://www.ti.com/ +# +# Texas Instruments AM263P Launchpad +# https://www.ti.com/tool/LP-AM263P +# + +# AM263P Launchpad has an xds110 onboard. +source [find interface/xds110.cfg] + +transport select jtag + +# default JTAG configuration has only SRST and no TRST +reset_config srst_only srst_push_pull + +# delay after SRST goes inactive +adapter srst delay 20 + +if { ![info exists SOC] } { + set SOC am263p +} + +source [find target/ti_k3.cfg] + +adapter speed 250 diff --git a/tcl/board/ti_am62levm.cfg b/tcl/board/ti_am62levm.cfg new file mode 100644 index 0000000..6debdd4 --- /dev/null +++ b/tcl/board/ti_am62levm.cfg @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (C) 2025 Texas Instruments Incorporated - https://www.ti.com/ +# +# Texas Instruments AM62L EVM: +# Links: https://www.ti.com/tool/TMDS62LEVM +# + +# the AM62L3 EVM/SK has an xds110 onboard. +source [find interface/xds110.cfg] + +transport select jtag + +# default JTAG configuration has only SRST and no TRST +reset_config srst_only srst_push_pull + +# delay after SRST goes inactive +adapter srst delay 20 + +if { ![info exists SOC] } { + set SOC am62l +} + +source [find target/ti_k3.cfg] + +adapter speed 2500 diff --git a/tcl/board/ti_mspm0_launchpad.cfg b/tcl/board/ti_mspm0_launchpad.cfg new file mode 100644 index 0000000..132fdc2 --- /dev/null +++ b/tcl/board/ti_mspm0_launchpad.cfg @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (C) 2023-2025 Texas Instruments Incorporated - https://www.ti.com/ +# +# TI MSPM0L1306 LaunchPad Evaluation Kit +# https://www.ti.com/tool/LP-MSPM0L1306 +# TI MSPM0C1104 LaunchPad Evaluation Kit +# https://www.ti.com/tool/LP-MSPM0C1104 +# TI MSPM0G3507 LaunchPad Evaluation Kit +# https://www.ti.com/tool/LP-MSPM0G3507 +# + +source [find interface/xds110.cfg] +adapter speed 10000 +source [find target/ti_mspm0.cfg] diff --git a/tcl/target/ngultra.cfg b/tcl/target/ngultra.cfg index 956fdbb..9f9814f 100644 --- a/tcl/target/ngultra.cfg +++ b/tcl/target/ngultra.cfg @@ -36,12 +36,11 @@ dap create $_CHIPNAME.coresight.dap -chain-position $_CHIPNAME.coresight.cpu for { set _core 0 } { $_core < $_cores } { incr _core } { cti create cti.$_core -dap $_CHIPNAME.coresight.dap -ap-num 0 \ -baseaddr [lindex $CTIBASE $_core] -# Cores are armv8-r but works with aarch64 (since armv8-r not directly supported by openocd yet). if { $_core == 0} { - target create core.$_core aarch64 -dap $_CHIPNAME.coresight.dap \ + target create core.$_core armv8r -dap $_CHIPNAME.coresight.dap \ -ap-num 0 -dbgbase [lindex $DBGBASE $_core] -cti cti.$_core } else { - target create core.$_core aarch64 -dap $_CHIPNAME.coresight.dap \ + target create core.$_core armv8r -dap $_CHIPNAME.coresight.dap \ -ap-num 0 -dbgbase [lindex $DBGBASE $_core] -cti cti.$_core -defer-examine } } diff --git a/tcl/target/nordic/nrf54l.cfg b/tcl/target/nordic/nrf54l.cfg index 70ead13..3e14055 100644 --- a/tcl/target/nordic/nrf54l.cfg +++ b/tcl/target/nordic/nrf54l.cfg @@ -27,18 +27,35 @@ if { [info exists CPUTAPID] } { set _CPUTAPID 0x6ba02477 } +# Multidrop instance ID should be configurable by FW in TAD TINSTANCE register. +# Writes to the register are ignored due to a silicon erratum. +if { [info exists SWD_INSTANCE_ID] } { + set _SWD_INSTANCE_ID $SWD_INSTANCE_ID +} else { + set _SWD_INSTANCE_ID 0 +} + transport select swd swd newdap $_CHIPNAME cpu -expected-id $_CPUTAPID -dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu + +if { [info exists SWD_MULTIDROP] } { + dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu -dp-id 0x001c0289 -instance-id $_SWD_INSTANCE_ID +} else { + dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu +} set _TARGETNAME $_CHIPNAME.cpu target create $_TARGETNAME cortex_m -dap $_CHIPNAME.dap -ap-num 0 $_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0 -# Create target for the control access port (CTRL-AP). -target create $_CHIPNAME.ctrl mem_ap -dap $_CHIPNAME.dap -ap-num 1 +# Create target for the AUX access port (AUX-AP). +target create $_CHIPNAME.aux mem_ap -dap $_CHIPNAME.dap -ap-num 1 + +# AUX-AP is accessible only if CSW Prot[0] bit (Data Access) is set +$_CHIPNAME.dap apsel 1 +$_CHIPNAME.dap apcsw 0x01000000 0x01000000 adapter speed 1000 diff --git a/tcl/target/renesas_rcar_gen3.cfg b/tcl/target/renesas_rcar_gen3.cfg index 73b3003..f6bc5e4 100644 --- a/tcl/target/renesas_rcar_gen3.cfg +++ b/tcl/target/renesas_rcar_gen3.cfg @@ -90,6 +90,18 @@ switch $_soc { set _num_cr52 1 set _boot_core CA76 } + V4H { + set _CHIPNAME r8a779g0 + set _num_ca76 4 + set _num_cr52 3 + set _boot_core CR52 + } + V4M { + set _CHIPNAME r8a779h0 + set _num_ca76 4 + set _num_cr52 3 + set _boot_core CR52 + } default { error "'$_soc' is invalid!" } @@ -126,8 +138,8 @@ set CA57_DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000} set CA57_CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000} set CA53_DBGBASE {0x80C10000 0x80D10000 0x80E10000 0x80F10000} set CA53_CTIBASE {0x80C20000 0x80D20000 0x80E20000 0x80F20000} -set CR52_DBGBASE 0x80c10000 -set CR52_CTIBASE 0x80c20000 +set CR52_DBGBASE {0x80C10000 0x80D10000 0x80E10000} +set CR52_CTIBASE {0x80C20000 0x80D20000 0x80E20000} set CR7_DBGBASE 0x80910000 set CR7_CTIBASE 0x80918000 @@ -159,24 +171,27 @@ proc setup_a5x {core_name dbgbase ctibase num boot} { proc setup_crx {core_name dbgbase ctibase num boot} { global _CHIPNAME global _DAPNAME + global smp_targets global _targets for { set _core 0 } { $_core < $num } { incr _core } { - set _TARGETNAME $_CHIPNAME.$core_name + set _TARGETNAME $_CHIPNAME.$core_name.$_core set _CTINAME $_TARGETNAME.cti - cti create $_CTINAME -dap $_DAPNAME -ap-num 1 -baseaddr $ctibase + cti create $_CTINAME -dap $_DAPNAME -ap-num 1 -baseaddr [lindex $ctibase $_core] if { $core_name == "r52" } { set _command "target create $_TARGETNAME armv8r -dap $_DAPNAME \ - -ap-num 1 -dbgbase $dbgbase -cti $_CTINAME" + -ap-num 1 -dbgbase [lindex $dbgbase $_core] -cti $_CTINAME" } else { set _command "target create $_TARGETNAME cortex_r4 -dap $_DAPNAME \ - -ap-num 1 -dbgbase $dbgbase" + -ap-num 1 -dbgbase [lindex $dbgbase $_core]" } - if { $boot == 1 } { + if { $_core == 0 && $boot == 1 } { set _targets "$_TARGETNAME" } else { set _command "$_command -defer-examine" } + set smp_targets "$smp_targets $_TARGETNAME" eval $_command + $_TARGETNAME configure -event examine-end { halt } } } diff --git a/tcl/target/ti_k3.cfg b/tcl/target/ti_k3.cfg index 2ae0f75..0dee74e 100644 --- a/tcl/target/ti_k3.cfg +++ b/tcl/target/ti_k3.cfg @@ -4,8 +4,12 @@ # Texas Instruments K3 devices: # * AM243: https://www.ti.com/lit/pdf/spruim2 # Has 4 R5 Cores, M4F and an M3 +# * AM261: https://www.ti.com/lit/pdf/sprujb6 +# Has 2 R5 Cores and an M4F # * AM263: https://www.ti.com/lit/pdf/spruj17 # Has 4 R5 Cores and an M3 +# * AM263P: https://www.ti.com/lit/pdf/spruj55 +# Has 4 R5 Cores and an M4F # * AM273: https://www.ti.com/lit/pdf/spruiu0 # Has 2 R5 Cores and an M3 # * AM625: https://www.ti.com/lit/pdf/spruiv7a @@ -14,6 +18,8 @@ # Has 4 ARMV8 Cores and 2 R5 Cores # * AM62P: https://www.ti.com/lit/pdf/spruj83 # Has 4 ARMV8 Cores and 2 R5 Cores +# * AM62L: https://www.ti.com/lit/pdf/sprujb4 +# Has 2 ARMv8 Cores only # * AM642: https://www.ti.com/lit/pdf/spruim2 # Has 2 ARMV8 Cores and 4 R5 Cores, M4F and an M3 # * AM654x: https://www.ti.com/lit/pdf/spruid7 @@ -77,8 +83,16 @@ set _gp_mcu_ap_unlock_offsets {0xf0 0x60} # Generic mem-ap port number set _mem_ap_num 2 +# Generic AP_SEL PWR Register number +set _power_ap_num 3 + +# Generic SPREC RESET BANK and Field number +set _powerap_sprec_reset 0xf0 + # Set configuration overrides for each SOC switch $_soc { + am261 - + am263p - am263 { set _K3_DAP_TAPID 0x2bb7d02f @@ -94,6 +108,16 @@ switch $_soc { set R5_DBGBASE {0x90030000 0x90032000 0x90050000 0x90052000} set R5_CTIBASE {0x90038000 0x90039000 0x90058000 0x90059000} set _r5_ap_num 5 + + set _power_ap_num 7 + + if { "$_soc" == "am263p" } { + set _K3_DAP_TAPID 0x1bb9502f + } + if { "$_soc" == "am261" } { + set _K3_DAP_TAPID 0x1bba602f + set _r5_cores 2 + } } am273 { set _K3_DAP_TAPID 0x1bb6a02f @@ -233,6 +257,18 @@ switch $_soc { set R5_CTIBASE {0x9d418000 0x9d518000 0x9d818000} } } + am62l { + set _K3_DAP_TAPID 0x0bba702f + + # AM62Lx has 1 cluster of 2 A53 cores. + set _armv8_cpu_name a53 + set _armv8_cores 2 + set ARMV8_DBGBASE {0x90010000 0x90110000} + set ARMV8_CTIBASE {0x90020000 0x90120000} + + # Has no supporting microcontrollers + set _r5_cores 0 + } j721e { set _K3_DAP_TAPID 0x0bb6402f # J721E has 1 cluster of 2 A72 cores. @@ -499,3 +535,10 @@ if { 0 == [string compare [adapter name] dmem ] } { # AXI AP access port for SoC address map target create $_CHIPNAME.axi_ap mem_ap -dap $_CHIPNAME.dap -ap-num $_mem_ap_num } + +# Reset system using (Debug Reset) SPREC Register,SYSTEMRESET bit field via apreg +proc dbg_sys_reset {} { + $::_CHIPNAME.dap apreg $::_power_ap_num $::_powerap_sprec_reset 0x1 +} + +add_help_text dbg_sys_reset "Debugger initiated system reset attempt via Power-AP" diff --git a/tcl/target/ti_mspm0.cfg b/tcl/target/ti_mspm0.cfg new file mode 100644 index 0000000..4e9b89c --- /dev/null +++ b/tcl/target/ti_mspm0.cfg @@ -0,0 +1,199 @@ +# 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 diff --git a/tools/scripts/camelcase.txt b/tools/scripts/camelcase.txt index b787902..6c6c28d 100644 --- a/tools/scripts/camelcase.txt +++ b/tools/scripts/camelcase.txt @@ -122,6 +122,7 @@ Jim_GetWide Jim_IncrRefCount Jim_InitStaticExtensions Jim_Interp +Jim_Length Jim_ListAppendElement Jim_ListGetIndex Jim_ListLength |