arm_tpiu_swo: add support for independent TPIU and SWO 58/5858/10
authorAntonio Borneo <borneo.antonio@gmail.com>
Sun, 11 Oct 2020 22:11:46 +0000 (00:11 +0200)
committerAntonio Borneo <borneo.antonio@gmail.com>
Wed, 10 Mar 2021 21:33:40 +0000 (21:33 +0000)
This is supposed to replace big part of armv7m_trace.[ch], since
TPIU is not only the one implemented in Cortex-M3 and M4.

Change-Id: I7588d16cbefe9cdb371c52fb0aa5cdfb48518804
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/5858
Tested-by: jenkins
doc/openocd.texi
src/openocd.c
src/target/Makefile.am
src/target/arm_tpiu_swo.c [new file with mode: 0644]
src/target/arm_tpiu_swo.h [new file with mode: 0644]

index 34f1bb6539a169940d8d3883fdaa9f9a0366ff84..bd4380a2bab8d32b616041006d1f798797980e44 100644 (file)
@@ -9503,13 +9503,146 @@ Selects whether interrupts will be processed when single stepping
 @end deffn
 
 
-@subsection ARMv7-M specific commands
+@subsection ARM CoreSight TPIU and SWO specific commands
 @cindex tracing
 @cindex SWO
 @cindex SWV
 @cindex TPIU
-@cindex ITM
-@cindex ETM
+
+ARM CoreSight provides several modules to generate debugging
+information internally (ITM, DWT and ETM). Their output is directed
+through TPIU or SWO modules to be captured externally either on an SWO pin (this
+configuration is called SWV) or on a synchronous parallel trace port.
+
+ARM CoreSight provides independent HW blocks named TPIU and SWO each with its
+own functionality. Embedded in Cortex-M3 and M4, ARM provides an optional HW
+block that includes both TPIU and SWO functionalities and is again named TPIU,
+which causes quite some confusion.
+The registers map of all the TPIU and SWO implementations allows using a single
+driver that detects at runtime the features available.
+
+The @command{tpiu} is used for either TPIU or SWO.
+A convenient alias @command{swo} is available to help distinguish, in scripts,
+the commands for SWO from the commands for TPIU.
+
+@deffn Command {swo} ...
+Alias of @command{tpiu ...}. Can be used in scripts to distinguish the commands
+for SWO from the commands for TPIU.
+@end deffn
+
+@deffn Command {tpiu create} tpiu_name configparams...
+Creates a TPIU or a SWO object. The two commands are equivalent.
+Add the object in a list and add new commands (@command{@var{tpiu_name}})
+which are used for various purposes including additional configuration.
+
+@itemize @bullet
+@item @var{tpiu_name} -- the name of the TPIU or SWO object.
+This name is also used to create the object's command, referred to here
+as @command{$tpiu_name}, and in other places where the TPIU or SWO needs to be identified.
+@item @var{configparams} -- all parameters accepted by @command{$tpiu_name configure} are permitted.
+
+You @emph{must} set here the AP and MEM_AP base_address through @code{-dap @var{dap_name}},
+@code{-ap-num @var{ap_number}} and @code{-baseaddr @var{base_address}}.
+@end itemize
+@end deffn
+
+@deffn Command {tpiu names}
+Lists all the TPIU or SWO objects created so far. The two commands are equivalent.
+@end deffn
+
+@deffn Command {tpiu init}
+Initialize all registered TPIU and SWO. The two commands are equivalent.
+These commands are used internally during initialization. They can be issued
+at any time after the initialization, too.
+@end deffn
+
+@deffn Command {$tpiu_name cget} queryparm
+Each configuration parameter accepted by @command{$tpiu_name configure} can be
+individually queried, to return its current value.
+The @var{queryparm} is a parameter name accepted by that command, such as @code{-dap}.
+@end deffn
+
+@deffn Command {$tpiu_name configure} configparams...
+The options accepted by this command may also be specified as parameters
+to @command{tpiu create}. Their values can later be queried one at a time by
+using the @command{$tpiu_name cget} command.
+
+@itemize @bullet
+@item @code{-dap} @var{dap_name} -- names the DAP used to access this
+TPIU. @xref{dapdeclaration,,DAP declaration}, on how to create and manage DAP instances.
+
+@item @code{-ap-num} @var{ap_number} -- sets DAP access port for TPIU,
+@var{ap_number} is the numeric index of the DAP AP the TPIU is connected to.
+
+@item @code{-baseaddr} @var{base_address} -- sets the TPIU @var{base_address} where
+to access the TPIU in the DAP AP memory space.
+
+@item @code{-protocol} (@option{sync}|@option{uart}|@option{manchester}) -- sets the
+protocol used for trace data:
+@itemize @minus
+@item @option{sync} -- synchronous parallel trace output mode, using @var{port_width}
+ data bits (default);
+@item @option{uart} -- use asynchronous SWO mode with NRZ (same as regular UART 8N1) coding;
+@item @option{manchester} -- use asynchronous SWO mode with Manchester coding.
+@end itemize
+
+@item @code{-event} @var{event_name} @var{event_body} -- assigns an event handler,
+a TCL string which is evaluated when the event is triggered. The events
+@code{pre-enable}, @code{post-enable}, @code{pre-disable} and @code{post-disable}
+are defined for TPIU/SWO.
+A typical use case for the event @code{pre-enable} is to enable the trace clock
+of the TPIU.
+
+@item @code{-output} (@option{external}|@option{:}@var{port}|@var{filename}|@option{-}) -- specifies
+the destination of the trace data:
+@itemize @minus
+@item @option{external} -- configure TPIU/SWO to let user capture trace
+output externally, either with an additional UART or with a logic analyzer (default);
+@item @option{-} -- configure TPIU/SWO and debug adapter to gather trace data
+and forward it to @command{tcl_trace} command;
+@item @option{:}@var{port} -- configure TPIU/SWO and debug adapter to gather
+trace data, open a TCP server at port @var{port} and send the trace data to
+each connected client;
+@item @var{filename} -- configure TPIU/SWO and debug adapter to
+gather trace data and append it to @var{filename}, which can be
+either a regular file or a named pipe.
+@end itemize
+
+@item @code{-traceclk} @var{TRACECLKIN_freq} -- mandatory parameter.
+Specifies the frequency in Hz of the trace clock. For the TPIU embedded in
+Cortex-M3 or M4, this is usually the same frequency as HCLK. For protocol
+@option{sync} this is twice the frequency of the pin data rate.
+
+@item @code{-pin-freq} @var{trace_freq} -- specifies the expected data rate
+in Hz of the SWO pin. Parameter used only on protocols @option{uart} and
+@option{manchester}. Can be omitted to let the adapter driver select the
+maximum supported rate automatically.
+
+@item @code{-port-width} @var{port_width} -- sets to @var{port_width} the width
+of the synchronous parallel port used for trace output. Parameter used only on
+protocol @option{sync}. If not specified, default value is @var{1}.
+
+@item @code{-formatter} (@option{0}|@option{1}) -- specifies if the formatter
+should be enabled. Parameter used only on protocol @option{sync}. If not specified,
+default value is @var{0}.
+@end itemize
+@end deffn
+
+@deffn Command {$tpiu_name enable}
+Uses the parameters specified by the previous @command{$tpiu_name configure}
+to configure and enable the TPIU or the SWO.
+If required, the adapter is also configured and enabled to receive the trace
+data.
+This command can be used before @command{init}, but it will take effect only
+after the @command{init}.
+@end deffn
+
+@deffn Command {$tpiu_name disable}
+Disable the TPIU or the SWO, terminating the receiving of the trace data.
+@end deffn
+
+
+TODO: remove the old tpiu commands
 
 @deffn Command {tpiu config} (@option{disable} | ((@option{external} | @option{internal (@var{filename} | @var{:port} | -)}) @
                (@option{sync @var{port_width}} | ((@option{manchester} | @option{uart}) @var{formatter_enable})) @
@@ -9585,13 +9718,22 @@ baud with our custom divisor to get 12MHz)
 @item OpenOCD invocation line:
 @example
 openocd -f interface/stlink.cfg \
-        -c "transport select hla_swd" \
-        -f target/stm32l1.cfg \
-        -c "tpiu config external uart off 24000000 12000000"
+-c "transport select hla_swd" \
+-f target/stm32l1.cfg \
+-c "stm32l1.tpiu configure -protocol uart" \
+-c "stm32l1.tpiu configure -traceclk 24000000 -pin-freq 12000000" \
+-c "stm32l1.tpiu enable"
 @end example
 @end enumerate
 @end deffn
 
+@subsection ARMv7-M specific commands
+@cindex tracing
+@cindex SWO
+@cindex SWV
+@cindex ITM
+@cindex ETM
+
 @deffn Command {itm port} @var{port} (@option{0}|@option{1}|@option{on}|@option{off})
 Enable or disable trace output for ITM stimulus @var{port} (counting
 from 0). Port 0 is enabled on target creation automatically.
index 83c35458b9ae89a1ad962f25ded32731a3b00de1..4fec563383a3c581a66e2b8b97e6e9ecd4287410 100644 (file)
@@ -38,6 +38,7 @@
 #include <pld/pld.h>
 #include <target/arm_cti.h>
 #include <target/arm_adi_v5.h>
+#include <target/arm_tpiu_swo.h>
 #include <rtt/rtt.h>
 
 #include <server/server.h>
@@ -173,6 +174,10 @@ COMMAND_HANDLER(handle_init_command)
                return ERROR_FAIL;
        command_context_mode(CMD_CTX, COMMAND_EXEC);
 
+       /* in COMMAND_EXEC, after target_examine(), only tpiu or only swo */
+       if (command_run_line(CMD_CTX, "tpiu init") != ERROR_OK)
+               return ERROR_FAIL;
+
        /* initialize telnet subsystem */
        gdb_target_add_all(all_targets);
 
@@ -255,6 +260,7 @@ static struct command_context *setup_command_handler(Jim_Interp *interp)
                &pld_register_commands,
                &cti_register_commands,
                &dap_register_commands,
+               &arm_tpiu_swo_register_commands,
                NULL
        };
        for (unsigned i = 0; NULL != command_registrants[i]; i++) {
@@ -355,6 +361,7 @@ int openocd_main(int argc, char *argv[])
 
        flash_free_all_banks();
        gdb_service_free();
+       arm_tpiu_swo_cleanup_all();
        server_free();
 
        unregister_all_commands(cmd_ctx, NULL);
index 1d30747b6b56c3b8897e89b035c2daeb4fdd20af..6605b678b13507164d4b0ec7f4be358f1895b806 100644 (file)
@@ -113,6 +113,7 @@ ARM_DEBUG_SRC = \
        %D%/etm.c \
        $(OOCD_TRACE_FILES) \
        %D%/etm_dummy.c \
+       %D%/arm_tpiu_swo.c \
        %D%/arm_cti.c
 
 AVR32_SRC = \
@@ -214,6 +215,7 @@ ARC_SRC = \
        %D%/etb.h \
        %D%/etm.h \
        %D%/etm_dummy.h \
+       %D%/arm_tpiu_swo.h \
        %D%/image.h \
        %D%/mips32.h \
        %D%/mips64.h \
diff --git a/src/target/arm_tpiu_swo.c b/src/target/arm_tpiu_swo.c
new file mode 100644 (file)
index 0000000..61891cb
--- /dev/null
@@ -0,0 +1,998 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/**
+ * @file
+ * This file implements support for the ARM CoreSight components Trace Port
+ * Interface Unit (TPIU) and Serial Wire Output (SWO). It also supports the
+ * CoreSight TPIU-Lite and the special TPIU version present with Cortex-M3
+ * and Cortex-M4 (that includes SWO).
+ */
+
+/*
+ * Relevant specifications from ARM include:
+ *
+ * CoreSight(tm) Components Technical Reference Manual           ARM DDI 0314H
+ * CoreSight(tm) TPIU-Lite Technical Reference Manual            ARM DDI 0317A
+ * Cortex(tm)-M3 Technical Reference Manual                      ARM DDI 0337G
+ * Cortex(tm)-M4 Technical Reference Manual                      ARM DDI 0439B
+ * CoreSight(tm) SoC-400 Technical Reference Manual              ARM DDI 0480F
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <jim.h>
+
+#include <helper/bits.h>
+#include <helper/command.h>
+#include <helper/jim-nvp.h>
+#include <helper/list.h>
+#include <helper/log.h>
+#include <helper/types.h>
+#include <jtag/interface.h>
+#include <server/server.h>
+#include <target/arm_adi_v5.h>
+#include <target/target.h>
+#include <transport/transport.h>
+#include "arm_tpiu_swo.h"
+
+#define TCP_SERVICE_NAME                "tpiu_swo_trace"
+
+/* default for Cortex-M3 and Cortex-M4 specific TPIU */
+#define TPIU_SWO_DEFAULT_BASE           0xE0040000
+
+#define TPIU_SSPSR_OFFSET               0x000
+#define TPIU_CSPSR_OFFSET               0x004
+#define TPIU_ACPR_OFFSET                0x010
+#define TPIU_SPPR_OFFSET                0x0F0
+#define TPIU_FFSR_OFFSET                0x300
+#define TPIU_FFCR_OFFSET                0x304
+#define TPIU_FSCR_OFFSET                0x308
+#define TPIU_DEVID_OFFSET               0xfc8
+
+#define TPIU_ACPR_MAX_PRESCALER         0x1fff
+#define TPIU_SPPR_PROTOCOL_SYNC         0x0 /**< synchronous trace output */
+#define TPIU_SPPR_PROTOCOL_MANCHESTER   0x1 /**< asynchronous output with NRZ coding */
+#define TPIU_SPPR_PROTOCOL_UART         0x2 /**< asynchronous output with Manchester coding */
+#define TPIU_DEVID_NOSUPPORT_SYNC       BIT(9)
+#define TPIU_DEVID_SUPPORT_MANCHESTER   BIT(10)
+#define TPIU_DEVID_SUPPORT_UART         BIT(11)
+
+enum arm_tpiu_swo_event {
+       TPIU_SWO_EVENT_PRE_ENABLE,
+       TPIU_SWO_EVENT_POST_ENABLE,
+       TPIU_SWO_EVENT_PRE_DISABLE,
+       TPIU_SWO_EVENT_POST_DISABLE,
+};
+
+static const Jim_Nvp nvp_arm_tpiu_swo_event[] = {
+       { .value = TPIU_SWO_EVENT_PRE_ENABLE,   .name = "pre-enable" },
+       { .value = TPIU_SWO_EVENT_POST_ENABLE,  .name = "post-enable" },
+       { .value = TPIU_SWO_EVENT_PRE_DISABLE,  .name = "pre-disable" },
+       { .value = TPIU_SWO_EVENT_POST_DISABLE, .name = "post-disable" },
+};
+
+struct arm_tpiu_swo_event_action {
+       enum arm_tpiu_swo_event event;
+       Jim_Interp *interp;
+       Jim_Obj *body;
+       struct arm_tpiu_swo_event_action *next;
+};
+
+struct arm_tpiu_swo_object {
+       struct list_head lh;
+       struct adiv5_mem_ap_spot spot;
+       char *name;
+       struct arm_tpiu_swo_event_action *event_action;
+       /* record enable before init */
+       bool deferred_enable;
+       bool enabled;
+       bool en_capture;
+       /** Handle to output trace data in INTERNAL capture mode */
+       /** Synchronous output port width */
+       uint32_t port_width;
+       FILE *file;
+       /** output mode */
+       unsigned int pin_protocol;
+       /** Enable formatter */
+       bool en_formatter;
+       /** frequency of TRACECLKIN (usually matches HCLK) */
+       unsigned int traceclkin_freq;
+       /** SWO pin frequency */
+       unsigned int swo_pin_freq;
+       /** where to dump the captured output trace data */
+       char *out_filename;
+       /** track TCP connections */
+       struct list_head connections;
+};
+
+struct arm_tpiu_swo_connection {
+       struct list_head lh;
+       struct connection *connection;
+};
+
+struct arm_tpiu_swo_priv_connection {
+       struct arm_tpiu_swo_object *obj;
+};
+
+static LIST_HEAD(all_tpiu_swo);
+
+#define ARM_TPIU_SWO_TRACE_BUF_SIZE    4096
+
+static int arm_tpiu_swo_poll_trace(void *priv)
+{
+       struct arm_tpiu_swo_object *obj = priv;
+       uint8_t buf[ARM_TPIU_SWO_TRACE_BUF_SIZE];
+       size_t size = sizeof(buf);
+       struct arm_tpiu_swo_connection *c;
+
+       int retval = adapter_poll_trace(buf, &size);
+       if (retval != ERROR_OK || !size)
+               return retval;
+
+       target_call_trace_callbacks(/*target*/NULL, size, buf);
+
+       if (obj->file) {
+               if (fwrite(buf, 1, size, obj->file) == size) {
+                       fflush(obj->file);
+               } else {
+                       LOG_ERROR("Error writing to the SWO trace destination file");
+                       return ERROR_FAIL;
+               }
+       }
+
+       if (obj->out_filename && obj->out_filename[0] == ':')
+               list_for_each_entry(c, &obj->connections, lh)
+                       if (connection_write(c->connection, buf, size) != (int)size)
+                               retval = ERROR_FAIL;
+
+       return ERROR_OK;
+}
+
+static void arm_tpiu_swo_handle_event(struct arm_tpiu_swo_object *obj, enum arm_tpiu_swo_event event)
+{
+       for (struct arm_tpiu_swo_event_action *ea = obj->event_action; ea; ea = ea->next) {
+               if (ea->event != event)
+                       continue;
+
+               LOG_DEBUG("TPIU/SWO: %s event: %s (%d) action : %s",
+                       obj->name,
+                       Jim_Nvp_value2name_simple(nvp_arm_tpiu_swo_event, event)->name,
+                       event,
+                       Jim_GetString(ea->body, NULL));
+
+               /* prevent event execution to change current target */
+               struct command_context *cmd_ctx = current_command_context(ea->interp);
+               struct target *saved_target = cmd_ctx->current_target;
+               int retval = Jim_EvalObj(ea->interp, ea->body);
+               cmd_ctx->current_target = saved_target;
+
+               if (retval == JIM_RETURN)
+                       retval = ea->interp->returnCode;
+               if (retval == JIM_OK || retval == ERROR_COMMAND_CLOSE_CONNECTION)
+                       return;
+
+               Jim_MakeErrorMessage(ea->interp);
+               LOG_USER("Error executing event %s on TPIU/SWO %s:\n%s",
+                       Jim_Nvp_value2name_simple(nvp_arm_tpiu_swo_event, event)->name,
+                       obj->name,
+                       Jim_GetString(Jim_GetResult(ea->interp), NULL));
+               /* clean both error code and stacktrace before return */
+               Jim_Eval(ea->interp, "error \"\" \"\"");
+               return;
+       }
+}
+
+static void arm_tpiu_swo_close_output(struct arm_tpiu_swo_object *obj)
+{
+       if (obj->file) {
+               fclose(obj->file);
+               obj->file = NULL;
+       }
+       if (obj->out_filename && obj->out_filename[0] == ':')
+               remove_service(TCP_SERVICE_NAME, &obj->out_filename[1]);
+}
+
+int arm_tpiu_swo_cleanup_all(void)
+{
+       struct arm_tpiu_swo_object *obj, *tmp;
+
+       list_for_each_entry_safe(obj, tmp, &all_tpiu_swo, lh) {
+               if (obj->enabled)
+                       arm_tpiu_swo_handle_event(obj, TPIU_SWO_EVENT_PRE_DISABLE);
+
+               arm_tpiu_swo_close_output(obj);
+
+               if (obj->en_capture) {
+                       target_unregister_timer_callback(arm_tpiu_swo_poll_trace, obj);
+
+                       int retval = adapter_config_trace(false, 0, 0, NULL, 0, NULL);
+                       if (retval != ERROR_OK)
+                               LOG_ERROR("Failed to stop adapter's trace");
+               }
+
+               if (obj->enabled)
+                       arm_tpiu_swo_handle_event(obj, TPIU_SWO_EVENT_POST_DISABLE);
+
+               struct arm_tpiu_swo_event_action *ea = obj->event_action;
+               while (ea) {
+                       struct arm_tpiu_swo_event_action *next = ea->next;
+                       Jim_DecrRefCount(ea->interp, ea->body);
+                       free(ea);
+                       ea = next;
+               }
+
+               free(obj->name);
+               free(obj->out_filename);
+               free(obj);
+       }
+
+       return ERROR_OK;
+}
+
+static int arm_tpiu_swo_service_new_connection(struct connection *connection)
+{
+       struct arm_tpiu_swo_priv_connection *priv = connection->service->priv;
+       struct arm_tpiu_swo_object *obj = priv->obj;
+       struct arm_tpiu_swo_connection *c = malloc(sizeof(*c));
+       if (!c) {
+               LOG_ERROR("Out of memory");
+               return ERROR_FAIL;
+       }
+       c->connection = connection;
+       list_add(&c->lh, &obj->connections);
+       return ERROR_OK;
+}
+
+static int arm_tpiu_swo_service_input(struct connection *connection)
+{
+       /* read a dummy buffer to check if the connection is still active */
+       long dummy;
+       int bytes_read = connection_read(connection, &dummy, sizeof(dummy));
+
+       if (bytes_read == 0) {
+               return ERROR_SERVER_REMOTE_CLOSED;
+       } else if (bytes_read == -1) {
+               LOG_ERROR("error during read: %s", strerror(errno));
+               return ERROR_SERVER_REMOTE_CLOSED;
+       }
+
+       return ERROR_OK;
+}
+
+static int arm_tpiu_swo_service_connection_closed(struct connection *connection)
+{
+       struct arm_tpiu_swo_priv_connection *priv = connection->service->priv;
+       struct arm_tpiu_swo_object *obj = priv->obj;
+       struct arm_tpiu_swo_connection *c, *tmp;
+
+       list_for_each_entry_safe(c, tmp, &obj->connections, lh)
+               if (c->connection == connection) {
+                       list_del(&c->lh);
+                       free(c);
+                       return ERROR_OK;
+               }
+       LOG_ERROR("Failed to find connection to close!");
+       return ERROR_FAIL;
+}
+
+COMMAND_HANDLER(handle_arm_tpiu_swo_event_list)
+{
+       struct arm_tpiu_swo_object *obj = CMD_DATA;
+
+       command_print(CMD, "Event actions for TPIU/SWO %s\n", obj->name);
+       command_print(CMD, "%-25s | Body", "Event");
+       command_print(CMD, "------------------------- | "
+                       "----------------------------------------");
+
+       for (struct arm_tpiu_swo_event_action *ea = obj->event_action; ea; ea = ea->next) {
+               Jim_Nvp *opt = Jim_Nvp_value2name_simple(nvp_arm_tpiu_swo_event, ea->event);
+               command_print(CMD, "%-25s | %s",
+                               opt->name, Jim_GetString(ea->body, NULL));
+       }
+       command_print(CMD, "***END***");
+       return ERROR_OK;
+}
+
+enum arm_tpiu_swo_cfg_param {
+       CFG_PORT_WIDTH,
+       CFG_PROTOCOL,
+       CFG_FORMATTER,
+       CFG_TRACECLKIN,
+       CFG_BITRATE,
+       CFG_OUTFILE,
+       CFG_EVENT,
+};
+
+static const Jim_Nvp nvp_arm_tpiu_swo_config_opts[] = {
+       { .name = "-port-width",    .value = CFG_PORT_WIDTH },
+       { .name = "-protocol",      .value = CFG_PROTOCOL },
+       { .name = "-formatter",     .value = CFG_FORMATTER },
+       { .name = "-traceclk",      .value = CFG_TRACECLKIN },
+       { .name = "-pin-freq",      .value = CFG_BITRATE },
+       { .name = "-output",        .value = CFG_OUTFILE },
+       { .name = "-event",         .value = CFG_EVENT },
+       /* handled by mem_ap_spot, added for Jim_GetOpt_NvpUnknown() */
+       { .name = "-dap",           .value = -1 },
+       { .name = "-ap-num",        .value = -1 },
+       { .name = "-baseaddr",      .value = -1 },
+       { .name = NULL,             .value = -1 },
+};
+
+static const Jim_Nvp nvp_arm_tpiu_swo_protocol_opts[] = {
+       { .name = "sync",           .value = TPIU_SPPR_PROTOCOL_SYNC },
+       { .name = "uart",           .value = TPIU_SPPR_PROTOCOL_UART },
+       { .name = "manchester",     .value = TPIU_SPPR_PROTOCOL_MANCHESTER },
+       { .name = NULL,             .value = -1 },
+};
+
+static const Jim_Nvp nvp_arm_tpiu_swo_bool_opts[] = {
+       { .name = "on",             .value = 1 },
+       { .name = "yes",            .value = 1 },
+       { .name = "1",              .value = 1 },
+       { .name = "true",           .value = 1 },
+       { .name = "off",            .value = 0 },
+       { .name = "no",             .value = 0 },
+       { .name = "0",              .value = 0 },
+       { .name = "false",          .value = 0 },
+       { .name = NULL,             .value = -1 },
+};
+
+static int arm_tpiu_swo_configure(Jim_GetOptInfo *goi, struct arm_tpiu_swo_object *obj)
+{
+       assert(obj != NULL);
+
+       if (goi->isconfigure && obj->enabled) {
+               Jim_SetResultFormatted(goi->interp, "Cannot configure TPIU/SWO; %s is enabled!", obj->name);
+               return JIM_ERR;
+       }
+
+       /* parse config or cget options ... */
+       while (goi->argc > 0) {
+               Jim_SetEmptyResult(goi->interp);
+
+               int e = adiv5_jim_mem_ap_spot_configure(&obj->spot, goi);
+               if (e == JIM_OK)
+                       continue;
+               if (e == JIM_ERR)
+                       return e;
+
+               Jim_Nvp *n;
+               e = Jim_GetOpt_Nvp(goi, nvp_arm_tpiu_swo_config_opts, &n);
+               if (e != JIM_OK) {
+                       Jim_GetOpt_NvpUnknown(goi, nvp_arm_tpiu_swo_config_opts, 0);
+                       return e;
+               }
+
+               switch (n->value) {
+               case CFG_PORT_WIDTH:
+                       if (goi->isconfigure) {
+                               jim_wide port_width;
+                               e = Jim_GetOpt_Wide(goi, &port_width);
+                               if (e != JIM_OK)
+                                       return e;
+                               if (port_width < 1 || port_width > 32) {
+                                       Jim_SetResultString(goi->interp, "Invalid port width!", -1);
+                                       return JIM_ERR;
+                               }
+                               obj->port_width = (uint32_t)port_width;
+                       } else {
+                               if (goi->argc)
+                                       goto err_no_params;
+                               Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, obj->port_width));
+                       }
+                       break;
+               case CFG_PROTOCOL:
+                       if (goi->isconfigure) {
+                               Jim_Nvp *p;
+                               e = Jim_GetOpt_Nvp(goi, nvp_arm_tpiu_swo_protocol_opts, &p);
+                               if (e != JIM_OK)
+                                       return e;
+                               obj->pin_protocol = p->value;
+                       } else {
+                               if (goi->argc)
+                                       goto err_no_params;
+                               Jim_Nvp *p;
+                               e = Jim_Nvp_value2name(goi->interp, nvp_arm_tpiu_swo_protocol_opts, obj->pin_protocol, &p);
+                               if (e != JIM_OK) {
+                                       Jim_SetResultString(goi->interp, "protocol error", -1);
+                                       return JIM_ERR;
+                               }
+                               Jim_SetResult(goi->interp, Jim_NewStringObj(goi->interp, p->name, -1));
+                       }
+                       break;
+               case CFG_FORMATTER:
+                       if (goi->isconfigure) {
+                               Jim_Nvp *p;
+                               e = Jim_GetOpt_Nvp(goi, nvp_arm_tpiu_swo_bool_opts, &p);
+                               if (e != JIM_OK)
+                                       return e;
+                               obj->en_formatter = p->value;
+                       } else {
+                               if (goi->argc)
+                                       goto err_no_params;
+                               Jim_Nvp *p;
+                               e = Jim_Nvp_value2name(goi->interp, nvp_arm_tpiu_swo_bool_opts, obj->en_formatter, &p);
+                               if (e != JIM_OK) {
+                                       Jim_SetResultString(goi->interp, "formatter error", -1);
+                                       return JIM_ERR;
+                               }
+                               Jim_SetResult(goi->interp, Jim_NewStringObj(goi->interp, p->name, -1));
+                       }
+                       break;
+               case CFG_TRACECLKIN:
+                       if (goi->isconfigure) {
+                               jim_wide clk;
+                               e = Jim_GetOpt_Wide(goi, &clk);
+                               if (e != JIM_OK)
+                                       return e;
+                               obj->traceclkin_freq = clk;
+                       } else {
+                               if (goi->argc)
+                                       goto err_no_params;
+                               Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, obj->traceclkin_freq));
+                       }
+                       break;
+               case CFG_BITRATE:
+                       if (goi->isconfigure) {
+                               jim_wide clk;
+                               e = Jim_GetOpt_Wide(goi, &clk);
+                               if (e != JIM_OK)
+                                       return e;
+                               obj->swo_pin_freq = clk;
+                       } else {
+                               if (goi->argc)
+                                       goto err_no_params;
+                               Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, obj->swo_pin_freq));
+                       }
+                       break;
+               case CFG_OUTFILE:
+                       if (goi->isconfigure) {
+                               const char *s;
+                               e = Jim_GetOpt_String(goi, &s, NULL);
+                               if (e != JIM_OK)
+                                       return e;
+                               if (s[0] == ':') {
+                                       char *end;
+                                       long port = strtol(s + 1, &end, 0);
+                                       if (port <= 0 || port > UINT16_MAX || *end != '\0') {
+                                               Jim_SetResultFormatted(goi->interp, "Invalid TCP port \'%s\'", s + 1);
+                                               return JIM_ERR;
+                                       }
+                               }
+                               free(obj->out_filename);
+                               obj->out_filename = strdup(s);
+                               if (!obj->out_filename) {
+                                       LOG_ERROR("Out of memory");
+                                       return JIM_ERR;
+                               }
+                       } else {
+                               if (goi->argc)
+                                       goto err_no_params;
+                               if (obj->out_filename)
+                                       Jim_SetResult(goi->interp, Jim_NewStringObj(goi->interp, obj->out_filename, -1));
+                       }
+                       break;
+               case CFG_EVENT:
+                       if (goi->isconfigure) {
+                               if (goi->argc < 2) {
+                                       Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv, "-event ?event-name? ?EVENT-BODY?");
+                                       return JIM_ERR;
+                               }
+                       } else {
+                               if (goi->argc != 1) {
+                                       Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv, "-event ?event-name?");
+                                       return JIM_ERR;
+                               }
+                       }
+
+                       {
+                               Jim_Nvp *p;
+                               Jim_Obj *o;
+                               struct arm_tpiu_swo_event_action *ea = obj->event_action;
+
+                               e = Jim_GetOpt_Nvp(goi, nvp_arm_tpiu_swo_event, &p);
+                               if (e != JIM_OK) {
+                                       Jim_GetOpt_NvpUnknown(goi, nvp_arm_tpiu_swo_event, 1);
+                                       return e;
+                               }
+
+                               while (ea) {
+                                       /* replace existing? */
+                                       if (ea->event == (enum arm_tpiu_swo_event)p->value)
+                                               break;
+                                       ea = ea->next;
+                               }
+
+                               if (goi->isconfigure) {
+                                       if (!ea) {
+                                               ea = calloc(1, sizeof(*ea));
+                                               if (!ea) {
+                                                       LOG_ERROR("Out of memory");
+                                                       return JIM_ERR;
+                                               }
+                                               ea->next = obj->event_action;
+                                               obj->event_action = ea;
+                                       }
+                                       if (ea->body)
+                                               Jim_DecrRefCount(ea->interp, ea->body);
+                                       ea->event = p->value;
+                                       ea->interp = goi->interp;
+                                       Jim_GetOpt_Obj(goi, &o);
+                                       ea->body = Jim_DuplicateObj(goi->interp, o);
+                                       Jim_IncrRefCount(ea->body);
+                               } else {
+                                       if (ea)
+                                               Jim_SetResult(goi->interp, Jim_DuplicateObj(goi->interp, ea->body));
+                               }
+                       }
+                       break;
+               }
+       }
+
+       return JIM_OK;
+
+err_no_params:
+       Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv, "NO PARAMS");
+       return JIM_ERR;
+}
+
+static int jim_arm_tpiu_swo_configure(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
+{
+       Jim_GetOptInfo goi;
+
+       Jim_GetOpt_Setup(&goi, interp, argc - 1, argv + 1);
+       goi.isconfigure = !strcmp(Jim_GetString(argv[0], NULL), "configure");
+       if (goi.argc < 1) {
+               Jim_WrongNumArgs(goi.interp, goi.argc, goi.argv,
+                       "missing: -option ...");
+               return JIM_ERR;
+       }
+       struct arm_tpiu_swo_object *obj = Jim_CmdPrivData(interp);
+       return arm_tpiu_swo_configure(&goi, obj);
+}
+
+static int wrap_write_u32(struct target *target, struct adiv5_ap *tpiu_ap,
+               target_addr_t address, uint32_t value)
+{
+       if (transport_is_hla())
+               return target_write_u32(target, address, value);
+       else
+               return mem_ap_write_atomic_u32(tpiu_ap, address, value);
+}
+
+static int wrap_read_u32(struct target *target, struct adiv5_ap *tpiu_ap,
+               target_addr_t address, uint32_t *value)
+{
+       if (transport_is_hla())
+               return target_read_u32(target, address, value);
+       else
+               return mem_ap_read_atomic_u32(tpiu_ap, address, value);
+}
+
+static int jim_arm_tpiu_swo_enable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+       struct arm_tpiu_swo_object *obj = Jim_CmdPrivData(interp);
+       struct command_context *cmd_ctx = current_command_context(interp);
+       struct adiv5_ap *tpiu_ap = dap_ap(obj->spot.dap, obj->spot.ap_num);
+       uint32_t value;
+       int retval;
+
+       if (argc != 1) {
+               Jim_WrongNumArgs(interp, 1, argv, "Too many parameters");
+               return JIM_ERR;
+       }
+
+       if (cmd_ctx->mode == COMMAND_CONFIG) {
+               LOG_DEBUG("%s: enable deferred", obj->name);
+               obj->deferred_enable = true;
+               return JIM_OK;
+       }
+
+       if (obj->enabled)
+               return JIM_OK;
+
+       if (transport_is_hla() && obj->spot.ap_num > 0) {
+               LOG_ERROR("Invalid access port %d. Only AP#0 allowed with hla transport", obj->spot.ap_num);
+               return JIM_ERR;
+       }
+
+       if (!obj->traceclkin_freq) {
+               LOG_ERROR("Trace clock-in frequency not set");
+               return JIM_ERR;
+       }
+
+       if (obj->pin_protocol == TPIU_SPPR_PROTOCOL_MANCHESTER || obj->pin_protocol == TPIU_SPPR_PROTOCOL_UART)
+               if (!obj->swo_pin_freq) {
+                       LOG_ERROR("SWO pin frequency not set");
+                       return JIM_ERR;
+               }
+
+       struct target *target = get_current_target(cmd_ctx);
+
+       /* trigger the event before any attempt to R/W in the TPIU/SWO */
+       arm_tpiu_swo_handle_event(obj, TPIU_SWO_EVENT_PRE_ENABLE);
+
+       retval = wrap_read_u32(target, tpiu_ap, obj->spot.base + TPIU_DEVID_OFFSET, &value);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Unable to read %s", obj->name);
+               return JIM_ERR;
+       }
+       switch (obj->pin_protocol) {
+       case TPIU_SPPR_PROTOCOL_SYNC:
+               value = !(value & TPIU_DEVID_NOSUPPORT_SYNC);
+               break;
+       case TPIU_SPPR_PROTOCOL_UART:
+               value &= TPIU_DEVID_SUPPORT_UART;
+               break;
+       case TPIU_SPPR_PROTOCOL_MANCHESTER:
+               value &= TPIU_DEVID_SUPPORT_MANCHESTER;
+               break;
+       default:
+               value = 0;
+       }
+       if (!value) {
+               Jim_Nvp *p;
+               Jim_Nvp_value2name(interp, nvp_arm_tpiu_swo_protocol_opts, obj->pin_protocol, &p);
+               LOG_ERROR("%s does not support protocol %s", obj->name, p->name);
+               return JIM_ERR;
+       }
+
+       if (obj->pin_protocol == TPIU_SPPR_PROTOCOL_SYNC) {
+               retval = wrap_read_u32(target, tpiu_ap, obj->spot.base + TPIU_SSPSR_OFFSET, &value);
+               if (!(value & BIT(obj->port_width - 1))) {
+                       LOG_ERROR("TPIU does not support port-width of %d bits", obj->port_width);
+                       return JIM_ERR;
+               }
+       }
+
+       uint16_t prescaler = 1; /* dummy value */
+       unsigned int swo_pin_freq = obj->swo_pin_freq; /* could be replaced */
+
+       if (obj->out_filename && strcmp(obj->out_filename, "external") && obj->out_filename[0]) {
+               if (obj->out_filename[0] == ':') {
+                       struct arm_tpiu_swo_priv_connection *priv = malloc(sizeof(*priv));
+                       if (!priv) {
+                               LOG_ERROR("Out of memory");
+                               return JIM_ERR;
+                       }
+                       priv->obj = obj;
+                       LOG_INFO("starting trace server for %s on %s", obj->name, &obj->out_filename[1]);
+                       retval = add_service("tpiu_swo_trace", &obj->out_filename[1],
+                               CONNECTION_LIMIT_UNLIMITED, arm_tpiu_swo_service_new_connection,
+                               arm_tpiu_swo_service_input, arm_tpiu_swo_service_connection_closed,
+                               priv, NULL);
+                       if (retval != ERROR_OK) {
+                               LOG_ERROR("Can't configure trace TCP port %s", &obj->out_filename[1]);
+                               return JIM_ERR;
+                       }
+               } else if (strcmp(obj->out_filename, "-")) {
+                       obj->file = fopen(obj->out_filename, "ab");
+                       if (!obj->file) {
+                               LOG_ERROR("Can't open trace destination file \"%s\"", obj->out_filename);
+                               return JIM_ERR;
+                       }
+               }
+
+               retval = adapter_config_trace(true, obj->pin_protocol, obj->port_width,
+                       &swo_pin_freq, obj->traceclkin_freq, &prescaler);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("Failed to start adapter's trace");
+                       arm_tpiu_swo_close_output(obj);
+                       return JIM_ERR;
+               }
+
+               if (obj->swo_pin_freq != swo_pin_freq)
+                       LOG_INFO("SWO pin data rate adjusted by adapter to %d Hz", swo_pin_freq);
+               obj->swo_pin_freq = swo_pin_freq;
+
+               target_register_timer_callback(arm_tpiu_swo_poll_trace, 1,
+                       TARGET_TIMER_TYPE_PERIODIC, obj);
+
+               obj->en_capture = true;
+       } else if (obj->pin_protocol == TPIU_SPPR_PROTOCOL_MANCHESTER || obj->pin_protocol == TPIU_SPPR_PROTOCOL_UART) {
+               prescaler = (obj->traceclkin_freq + obj->swo_pin_freq / 2) / obj->swo_pin_freq;
+               if (prescaler > TPIU_ACPR_MAX_PRESCALER)
+                       prescaler = TPIU_ACPR_MAX_PRESCALER;
+               swo_pin_freq = obj->traceclkin_freq / prescaler;
+
+               if (obj->swo_pin_freq != swo_pin_freq)
+                       LOG_INFO("SWO pin data rate adjusted to %d Hz", swo_pin_freq);
+               obj->swo_pin_freq = swo_pin_freq;
+       }
+
+       retval = wrap_write_u32(target, tpiu_ap, obj->spot.base + TPIU_CSPSR_OFFSET, BIT(obj->port_width - 1));
+       if (retval != ERROR_OK)
+               goto error_exit;
+
+       retval = wrap_write_u32(target, tpiu_ap, obj->spot.base + TPIU_ACPR_OFFSET, prescaler - 1);
+       if (retval != ERROR_OK)
+               goto error_exit;
+
+       retval = wrap_write_u32(target, tpiu_ap, obj->spot.base + TPIU_SPPR_OFFSET, obj->pin_protocol);
+       if (retval != ERROR_OK)
+               goto error_exit;
+
+       retval = wrap_read_u32(target, tpiu_ap, obj->spot.base + TPIU_FFCR_OFFSET, &value);
+       if (retval != ERROR_OK)
+               goto error_exit;
+       if (obj->en_formatter)
+               value |= BIT(1);
+       else
+               value &= ~BIT(1);
+       retval = wrap_write_u32(target, tpiu_ap, obj->spot.base + TPIU_FFCR_OFFSET, value);
+       if (retval != ERROR_OK)
+               goto error_exit;
+
+       arm_tpiu_swo_handle_event(obj, TPIU_SWO_EVENT_POST_ENABLE);
+
+       obj->enabled = true;
+       return JIM_OK;
+
+error_exit:
+       LOG_ERROR("Error!");
+
+       if (obj->en_capture) {
+               obj->en_capture = false;
+
+               arm_tpiu_swo_close_output(obj);
+
+               target_unregister_timer_callback(arm_tpiu_swo_poll_trace, obj);
+
+               retval = adapter_config_trace(false, 0, 0, NULL, 0, NULL);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("Failed to stop adapter's trace");
+                       return JIM_ERR;
+               }
+       }
+       return JIM_ERR;
+}
+
+static int jim_arm_tpiu_swo_disable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+       struct arm_tpiu_swo_object *obj = Jim_CmdPrivData(interp);
+
+       if (argc != 1) {
+               Jim_WrongNumArgs(interp, 1, argv, "Too many parameters");
+               return JIM_ERR;
+       }
+
+       if (!obj->enabled)
+               return JIM_OK;
+       obj->enabled = false;
+
+       arm_tpiu_swo_handle_event(obj, TPIU_SWO_EVENT_PRE_DISABLE);
+
+       if (obj->en_capture) {
+               obj->en_capture = false;
+
+               arm_tpiu_swo_close_output(obj);
+
+               target_unregister_timer_callback(arm_tpiu_swo_poll_trace, obj);
+
+               int retval = adapter_config_trace(false, 0, 0, NULL, 0, NULL);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("Failed to stop adapter's trace");
+                       return JIM_ERR;
+               }
+       }
+
+       arm_tpiu_swo_handle_event(obj, TPIU_SWO_EVENT_POST_DISABLE);
+       return JIM_OK;
+}
+
+static const struct command_registration arm_tpiu_swo_instance_command_handlers[] = {
+       {
+               .name = "configure",
+               .mode = COMMAND_ANY,
+               .jim_handler = jim_arm_tpiu_swo_configure,
+               .help  = "configure a new TPIU/SWO for use",
+               .usage = "[attribute value ...]",
+       },
+       {
+               .name = "cget",
+               .mode = COMMAND_ANY,
+               .jim_handler = jim_arm_tpiu_swo_configure,
+               .help  = "returns the specified TPIU/SWO attribute",
+               .usage = "attribute",
+       },
+       {
+               .name = "eventlist",
+               .mode = COMMAND_ANY,
+               .handler = handle_arm_tpiu_swo_event_list,
+               .help = "displays a table of events defined for this TPIU/SWO",
+               .usage = "",
+       },
+       {
+               .name = "enable",
+               .mode = COMMAND_ANY,
+               .jim_handler = jim_arm_tpiu_swo_enable,
+               .usage = "",
+               .help = "Enables the TPIU/SWO output",
+       },
+       {
+               .name = "disable",
+               .mode = COMMAND_EXEC,
+               .jim_handler = jim_arm_tpiu_swo_disable,
+               .usage = "",
+               .help = "Disables the TPIU/SWO output",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+static int arm_tpiu_swo_create(Jim_Interp *interp, struct arm_tpiu_swo_object *obj)
+{
+       struct command_context *cmd_ctx;
+       Jim_Cmd *cmd;
+       int e;
+
+       cmd_ctx = current_command_context(interp);
+       assert(cmd_ctx != NULL);
+
+       /* does this command exist? */
+       cmd = Jim_GetCommand(interp, Jim_NewStringObj(interp, obj->name, -1), JIM_ERRMSG);
+       if (cmd) {
+               Jim_SetResultFormatted(interp, "Command: %s Exists", obj->name);
+               return JIM_ERR;
+       }
+
+       /* now - create the new tpiu/swo name command */
+       const struct command_registration obj_commands[] = {
+               {
+                       .name = obj->name,
+                       .mode = COMMAND_ANY,
+                       .help = "tpiu/swo instance command group",
+                       .usage = "",
+                       .chain = arm_tpiu_swo_instance_command_handlers,
+               },
+               COMMAND_REGISTRATION_DONE
+       };
+       e = register_commands(cmd_ctx, NULL, obj_commands);
+       if (ERROR_OK != e)
+               return JIM_ERR;
+
+       struct command *c = command_find_in_context(cmd_ctx, obj->name);
+       assert(c);
+       command_set_handler_data(c, obj);
+
+       list_add_tail(&obj->lh, &all_tpiu_swo);
+
+       return JIM_OK;
+}
+
+static int jim_arm_tpiu_swo_create(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+       Jim_GetOptInfo goi;
+       Jim_GetOpt_Setup(&goi, interp, argc - 1, argv + 1);
+       if (goi.argc < 1) {
+               Jim_WrongNumArgs(goi.interp, 1, goi.argv, "?name? ..options...");
+               return JIM_ERR;
+       }
+
+       struct arm_tpiu_swo_object *obj = calloc(1, sizeof(struct arm_tpiu_swo_object));
+       if (!obj) {
+               LOG_ERROR("Out of memory");
+               return JIM_ERR;
+       }
+       INIT_LIST_HEAD(&obj->connections);
+       adiv5_mem_ap_spot_init(&obj->spot);
+       obj->spot.base = TPIU_SWO_DEFAULT_BASE;
+       obj->port_width = 1;
+
+       Jim_Obj *n;
+       Jim_GetOpt_Obj(&goi, &n);
+       obj->name = strdup(Jim_GetString(n, NULL));
+       if (!obj->name) {
+               LOG_ERROR("Out of memory");
+               free(obj);
+               return JIM_ERR;
+       }
+
+       /* Do the rest as "configure" options */
+       goi.isconfigure = 1;
+       int e = arm_tpiu_swo_configure(&goi, obj);
+       if (e != JIM_OK)
+               goto err_exit;
+
+       if (!obj->spot.dap || obj->spot.ap_num == DP_APSEL_INVALID) {
+               Jim_SetResultString(goi.interp, "-dap and -ap-num required when creating TPIU", -1);
+               goto err_exit;
+       }
+
+       e = arm_tpiu_swo_create(goi.interp, obj);
+       if (e != JIM_OK)
+               goto err_exit;
+
+       return JIM_OK;
+
+err_exit:
+       free(obj->name);
+       free(obj->out_filename);
+       free(obj);
+       return JIM_ERR;
+}
+
+static int jim_arm_tpiu_swo_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+       struct arm_tpiu_swo_object *obj;
+
+       if (argc != 1) {
+               Jim_WrongNumArgs(interp, 1, argv, "Too many parameters");
+               return JIM_ERR;
+       }
+       Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0));
+       list_for_each_entry(obj, &all_tpiu_swo, lh) {
+               Jim_ListAppendElement(interp, Jim_GetResult(interp),
+                       Jim_NewStringObj(interp, obj->name, -1));
+       }
+       return JIM_OK;
+}
+
+static int jim_arm_tpiu_swo_init(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+       struct command_context *cmd_ctx = current_command_context(interp);
+       struct arm_tpiu_swo_object *obj;
+       int retval = JIM_OK;
+
+       if (argc != 1) {
+               Jim_WrongNumArgs(interp, 1, argv, "Too many parameters");
+               return JIM_ERR;
+       }
+       list_for_each_entry(obj, &all_tpiu_swo, lh) {
+               if (!obj->deferred_enable)
+                       continue;
+               LOG_DEBUG("%s: running enable during init", obj->name);
+               int retval2 = command_run_linef(cmd_ctx, "%s enable", obj->name);
+               if (retval2 != ERROR_OK)
+                       retval = JIM_ERR;
+       }
+       return retval;
+}
+
+static const struct command_registration arm_tpiu_swo_subcommand_handlers[] = {
+       {
+               .name = "create",
+               .mode = COMMAND_ANY,
+               .jim_handler = jim_arm_tpiu_swo_create,
+               .usage = "name [-dap dap] [-ap-num num] [-address baseaddr]",
+               .help = "Creates a new TPIU or SWO object",
+       },
+       {
+               .name = "names",
+               .mode = COMMAND_ANY,
+               .jim_handler = jim_arm_tpiu_swo_names,
+               .usage = "",
+               .help = "Lists all registered TPIU and SWO objects by name",
+       },
+       {
+               .name = "init",
+               .mode = COMMAND_EXEC,
+               .jim_handler = jim_arm_tpiu_swo_init,
+               .usage = "",
+               .help = "Initialize TPIU and SWO",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration arm_tpiu_swo_command_handlers[] = {
+       {
+               .name = "tpiu",
+               .chain = arm_tpiu_swo_subcommand_handlers,
+               .usage = "",
+               .help = "tpiu command group",
+       },
+       {
+               .name = "swo",
+               .chain = arm_tpiu_swo_subcommand_handlers,
+               .usage = "",
+               .help = "swo command group",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+int arm_tpiu_swo_register_commands(struct command_context *cmd_ctx)
+{
+       return register_commands(cmd_ctx, NULL, arm_tpiu_swo_command_handlers);
+}
diff --git a/src/target/arm_tpiu_swo.h b/src/target/arm_tpiu_swo.h
new file mode 100644 (file)
index 0000000..e0aed71
--- /dev/null
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef OPENOCD_TARGET_ARM_TPIU_SWO_H
+#define OPENOCD_TARGET_ARM_TPIU_SWO_H
+
+int arm_tpiu_swo_register_commands(struct command_context *cmd_ctx);
+int arm_tpiu_swo_cleanup_all(void);
+
+#endif /* OPENOCD_TARGET_ARM_TPIU_SWO_H */

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)