arm_tpiu_swo: fix two dead assignments
[openocd.git] / src / target / arm_tpiu_swo.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2
3 /**
4 * @file
5 * This file implements support for the ARM CoreSight components Trace Port
6 * Interface Unit (TPIU) and Serial Wire Output (SWO). It also supports the
7 * CoreSight TPIU-Lite and the special TPIU version present with Cortex-M3
8 * and Cortex-M4 (that includes SWO).
9 */
10
11 /*
12 * Relevant specifications from ARM include:
13 *
14 * CoreSight(tm) Components Technical Reference Manual ARM DDI 0314H
15 * CoreSight(tm) TPIU-Lite Technical Reference Manual ARM DDI 0317A
16 * Cortex(tm)-M3 Technical Reference Manual ARM DDI 0337G
17 * Cortex(tm)-M4 Technical Reference Manual ARM DDI 0439B
18 * CoreSight(tm) SoC-400 Technical Reference Manual ARM DDI 0480F
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <stdlib.h>
26 #include <jim.h>
27
28 #include <helper/bits.h>
29 #include <helper/command.h>
30 #include <helper/jim-nvp.h>
31 #include <helper/list.h>
32 #include <helper/log.h>
33 #include <helper/types.h>
34 #include <jtag/interface.h>
35 #include <server/server.h>
36 #include <target/arm_adi_v5.h>
37 #include <target/target.h>
38 #include <transport/transport.h>
39 #include "arm_tpiu_swo.h"
40
41 /* START_DEPRECATED_TPIU */
42 #include <target/cortex_m.h>
43 #include <target/target_type.h>
44 #define MSG "DEPRECATED \'tpiu config\' command: "
45 /* END_DEPRECATED_TPIU */
46
47 #define TCP_SERVICE_NAME "tpiu_swo_trace"
48
49 /* default for Cortex-M3 and Cortex-M4 specific TPIU */
50 #define TPIU_SWO_DEFAULT_BASE 0xE0040000
51
52 #define TPIU_SSPSR_OFFSET 0x000
53 #define TPIU_CSPSR_OFFSET 0x004
54 #define TPIU_ACPR_OFFSET 0x010
55 #define TPIU_SPPR_OFFSET 0x0F0
56 #define TPIU_FFSR_OFFSET 0x300
57 #define TPIU_FFCR_OFFSET 0x304
58 #define TPIU_FSCR_OFFSET 0x308
59 #define TPIU_DEVID_OFFSET 0xfc8
60
61 #define TPIU_ACPR_MAX_PRESCALER 0x1fff
62 #define TPIU_SPPR_PROTOCOL_SYNC (TPIU_PIN_PROTOCOL_SYNC)
63 #define TPIU_SPPR_PROTOCOL_MANCHESTER (TPIU_PIN_PROTOCOL_ASYNC_MANCHESTER)
64 #define TPIU_SPPR_PROTOCOL_UART (TPIU_PIN_PROTOCOL_ASYNC_UART)
65 #define TPIU_DEVID_NOSUPPORT_SYNC BIT(9)
66 #define TPIU_DEVID_SUPPORT_MANCHESTER BIT(10)
67 #define TPIU_DEVID_SUPPORT_UART BIT(11)
68
69 enum arm_tpiu_swo_event {
70 TPIU_SWO_EVENT_PRE_ENABLE,
71 TPIU_SWO_EVENT_POST_ENABLE,
72 TPIU_SWO_EVENT_PRE_DISABLE,
73 TPIU_SWO_EVENT_POST_DISABLE,
74 };
75
76 static const struct jim_nvp nvp_arm_tpiu_swo_event[] = {
77 { .value = TPIU_SWO_EVENT_PRE_ENABLE, .name = "pre-enable" },
78 { .value = TPIU_SWO_EVENT_POST_ENABLE, .name = "post-enable" },
79 { .value = TPIU_SWO_EVENT_PRE_DISABLE, .name = "pre-disable" },
80 { .value = TPIU_SWO_EVENT_POST_DISABLE, .name = "post-disable" },
81 };
82
83 struct arm_tpiu_swo_event_action {
84 enum arm_tpiu_swo_event event;
85 Jim_Interp *interp;
86 Jim_Obj *body;
87 struct arm_tpiu_swo_event_action *next;
88 };
89
90 struct arm_tpiu_swo_object {
91 struct list_head lh;
92 struct adiv5_mem_ap_spot spot;
93 char *name;
94 struct arm_tpiu_swo_event_action *event_action;
95 /* record enable before init */
96 bool deferred_enable;
97 bool enabled;
98 bool en_capture;
99 /** Handle to output trace data in INTERNAL capture mode */
100 /** Synchronous output port width */
101 uint32_t port_width;
102 FILE *file;
103 /** output mode */
104 unsigned int pin_protocol;
105 /** Enable formatter */
106 bool en_formatter;
107 /** frequency of TRACECLKIN (usually matches HCLK) */
108 unsigned int traceclkin_freq;
109 /** SWO pin frequency */
110 unsigned int swo_pin_freq;
111 /** where to dump the captured output trace data */
112 char *out_filename;
113 /** track TCP connections */
114 struct list_head connections;
115 /* START_DEPRECATED_TPIU */
116 bool recheck_ap_cur_target;
117 /* END_DEPRECATED_TPIU */
118 };
119
120 struct arm_tpiu_swo_connection {
121 struct list_head lh;
122 struct connection *connection;
123 };
124
125 struct arm_tpiu_swo_priv_connection {
126 struct arm_tpiu_swo_object *obj;
127 };
128
129 static LIST_HEAD(all_tpiu_swo);
130
131 #define ARM_TPIU_SWO_TRACE_BUF_SIZE 4096
132
133 static int arm_tpiu_swo_poll_trace(void *priv)
134 {
135 struct arm_tpiu_swo_object *obj = priv;
136 uint8_t buf[ARM_TPIU_SWO_TRACE_BUF_SIZE];
137 size_t size = sizeof(buf);
138 struct arm_tpiu_swo_connection *c;
139
140 int retval = adapter_poll_trace(buf, &size);
141 if (retval != ERROR_OK || !size)
142 return retval;
143
144 target_call_trace_callbacks(/*target*/NULL, size, buf);
145
146 if (obj->file) {
147 if (fwrite(buf, 1, size, obj->file) == size) {
148 fflush(obj->file);
149 } else {
150 LOG_ERROR("Error writing to the SWO trace destination file");
151 return ERROR_FAIL;
152 }
153 }
154
155 if (obj->out_filename && obj->out_filename[0] == ':')
156 list_for_each_entry(c, &obj->connections, lh)
157 if (connection_write(c->connection, buf, size) != (int)size)
158 LOG_ERROR("Error writing to connection"); /* FIXME: which connection? */
159
160 return ERROR_OK;
161 }
162
163 static void arm_tpiu_swo_handle_event(struct arm_tpiu_swo_object *obj, enum arm_tpiu_swo_event event)
164 {
165 for (struct arm_tpiu_swo_event_action *ea = obj->event_action; ea; ea = ea->next) {
166 if (ea->event != event)
167 continue;
168
169 LOG_DEBUG("TPIU/SWO: %s event: %s (%d) action : %s",
170 obj->name,
171 jim_nvp_value2name_simple(nvp_arm_tpiu_swo_event, event)->name,
172 event,
173 Jim_GetString(ea->body, NULL));
174
175 /* prevent event execution to change current target */
176 struct command_context *cmd_ctx = current_command_context(ea->interp);
177 struct target *saved_target = cmd_ctx->current_target;
178 int retval = Jim_EvalObj(ea->interp, ea->body);
179 cmd_ctx->current_target = saved_target;
180
181 if (retval == JIM_RETURN)
182 retval = ea->interp->returnCode;
183 if (retval == JIM_OK || retval == ERROR_COMMAND_CLOSE_CONNECTION)
184 return;
185
186 Jim_MakeErrorMessage(ea->interp);
187 LOG_USER("Error executing event %s on TPIU/SWO %s:\n%s",
188 jim_nvp_value2name_simple(nvp_arm_tpiu_swo_event, event)->name,
189 obj->name,
190 Jim_GetString(Jim_GetResult(ea->interp), NULL));
191 /* clean both error code and stacktrace before return */
192 Jim_Eval(ea->interp, "error \"\" \"\"");
193 return;
194 }
195 }
196
197 static void arm_tpiu_swo_close_output(struct arm_tpiu_swo_object *obj)
198 {
199 if (obj->file) {
200 fclose(obj->file);
201 obj->file = NULL;
202 }
203 if (obj->out_filename && obj->out_filename[0] == ':')
204 remove_service(TCP_SERVICE_NAME, &obj->out_filename[1]);
205 }
206
207 int arm_tpiu_swo_cleanup_all(void)
208 {
209 struct arm_tpiu_swo_object *obj, *tmp;
210
211 list_for_each_entry_safe(obj, tmp, &all_tpiu_swo, lh) {
212 if (obj->enabled)
213 arm_tpiu_swo_handle_event(obj, TPIU_SWO_EVENT_PRE_DISABLE);
214
215 arm_tpiu_swo_close_output(obj);
216
217 if (obj->en_capture) {
218 target_unregister_timer_callback(arm_tpiu_swo_poll_trace, obj);
219
220 int retval = adapter_config_trace(false, 0, 0, NULL, 0, NULL);
221 if (retval != ERROR_OK)
222 LOG_ERROR("Failed to stop adapter's trace");
223 }
224
225 if (obj->enabled)
226 arm_tpiu_swo_handle_event(obj, TPIU_SWO_EVENT_POST_DISABLE);
227
228 struct arm_tpiu_swo_event_action *ea = obj->event_action;
229 while (ea) {
230 struct arm_tpiu_swo_event_action *next = ea->next;
231 Jim_DecrRefCount(ea->interp, ea->body);
232 free(ea);
233 ea = next;
234 }
235
236 free(obj->name);
237 free(obj->out_filename);
238 free(obj);
239 }
240
241 return ERROR_OK;
242 }
243
244 static int arm_tpiu_swo_service_new_connection(struct connection *connection)
245 {
246 struct arm_tpiu_swo_priv_connection *priv = connection->service->priv;
247 struct arm_tpiu_swo_object *obj = priv->obj;
248 struct arm_tpiu_swo_connection *c = malloc(sizeof(*c));
249 if (!c) {
250 LOG_ERROR("Out of memory");
251 return ERROR_FAIL;
252 }
253 c->connection = connection;
254 list_add(&c->lh, &obj->connections);
255 return ERROR_OK;
256 }
257
258 static int arm_tpiu_swo_service_input(struct connection *connection)
259 {
260 /* read a dummy buffer to check if the connection is still active */
261 long dummy;
262 int bytes_read = connection_read(connection, &dummy, sizeof(dummy));
263
264 if (bytes_read == 0) {
265 return ERROR_SERVER_REMOTE_CLOSED;
266 } else if (bytes_read == -1) {
267 LOG_ERROR("error during read: %s", strerror(errno));
268 return ERROR_SERVER_REMOTE_CLOSED;
269 }
270
271 return ERROR_OK;
272 }
273
274 static int arm_tpiu_swo_service_connection_closed(struct connection *connection)
275 {
276 struct arm_tpiu_swo_priv_connection *priv = connection->service->priv;
277 struct arm_tpiu_swo_object *obj = priv->obj;
278 struct arm_tpiu_swo_connection *c, *tmp;
279
280 list_for_each_entry_safe(c, tmp, &obj->connections, lh)
281 if (c->connection == connection) {
282 list_del(&c->lh);
283 free(c);
284 return ERROR_OK;
285 }
286 LOG_ERROR("Failed to find connection to close!");
287 return ERROR_FAIL;
288 }
289
290 COMMAND_HANDLER(handle_arm_tpiu_swo_event_list)
291 {
292 struct arm_tpiu_swo_object *obj = CMD_DATA;
293
294 command_print(CMD, "Event actions for TPIU/SWO %s\n", obj->name);
295 command_print(CMD, "%-25s | Body", "Event");
296 command_print(CMD, "------------------------- | "
297 "----------------------------------------");
298
299 for (struct arm_tpiu_swo_event_action *ea = obj->event_action; ea; ea = ea->next) {
300 struct jim_nvp *opt = jim_nvp_value2name_simple(nvp_arm_tpiu_swo_event, ea->event);
301 command_print(CMD, "%-25s | %s",
302 opt->name, Jim_GetString(ea->body, NULL));
303 }
304 command_print(CMD, "***END***");
305 return ERROR_OK;
306 }
307
308 enum arm_tpiu_swo_cfg_param {
309 CFG_PORT_WIDTH,
310 CFG_PROTOCOL,
311 CFG_FORMATTER,
312 CFG_TRACECLKIN,
313 CFG_BITRATE,
314 CFG_OUTFILE,
315 CFG_EVENT,
316 };
317
318 static const struct jim_nvp nvp_arm_tpiu_swo_config_opts[] = {
319 { .name = "-port-width", .value = CFG_PORT_WIDTH },
320 { .name = "-protocol", .value = CFG_PROTOCOL },
321 { .name = "-formatter", .value = CFG_FORMATTER },
322 { .name = "-traceclk", .value = CFG_TRACECLKIN },
323 { .name = "-pin-freq", .value = CFG_BITRATE },
324 { .name = "-output", .value = CFG_OUTFILE },
325 { .name = "-event", .value = CFG_EVENT },
326 /* handled by mem_ap_spot, added for jim_getopt_nvp_unknown() */
327 { .name = "-dap", .value = -1 },
328 { .name = "-ap-num", .value = -1 },
329 { .name = "-baseaddr", .value = -1 },
330 { .name = NULL, .value = -1 },
331 };
332
333 static const struct jim_nvp nvp_arm_tpiu_swo_protocol_opts[] = {
334 { .name = "sync", .value = TPIU_SPPR_PROTOCOL_SYNC },
335 { .name = "uart", .value = TPIU_SPPR_PROTOCOL_UART },
336 { .name = "manchester", .value = TPIU_SPPR_PROTOCOL_MANCHESTER },
337 { .name = NULL, .value = -1 },
338 };
339
340 static const struct jim_nvp nvp_arm_tpiu_swo_bool_opts[] = {
341 { .name = "on", .value = 1 },
342 { .name = "yes", .value = 1 },
343 { .name = "1", .value = 1 },
344 { .name = "true", .value = 1 },
345 { .name = "off", .value = 0 },
346 { .name = "no", .value = 0 },
347 { .name = "0", .value = 0 },
348 { .name = "false", .value = 0 },
349 { .name = NULL, .value = -1 },
350 };
351
352 static int arm_tpiu_swo_configure(struct jim_getopt_info *goi, struct arm_tpiu_swo_object *obj)
353 {
354 assert(obj);
355
356 if (goi->isconfigure && obj->enabled) {
357 Jim_SetResultFormatted(goi->interp, "Cannot configure TPIU/SWO; %s is enabled!", obj->name);
358 return JIM_ERR;
359 }
360
361 /* parse config or cget options ... */
362 while (goi->argc > 0) {
363 Jim_SetEmptyResult(goi->interp);
364
365 int e = adiv5_jim_mem_ap_spot_configure(&obj->spot, goi);
366 if (e == JIM_OK)
367 continue;
368 if (e == JIM_ERR)
369 return e;
370
371 struct jim_nvp *n;
372 e = jim_getopt_nvp(goi, nvp_arm_tpiu_swo_config_opts, &n);
373 if (e != JIM_OK) {
374 jim_getopt_nvp_unknown(goi, nvp_arm_tpiu_swo_config_opts, 0);
375 return e;
376 }
377
378 switch (n->value) {
379 case CFG_PORT_WIDTH:
380 if (goi->isconfigure) {
381 jim_wide port_width;
382 e = jim_getopt_wide(goi, &port_width);
383 if (e != JIM_OK)
384 return e;
385 if (port_width < 1 || port_width > 32) {
386 Jim_SetResultString(goi->interp, "Invalid port width!", -1);
387 return JIM_ERR;
388 }
389 obj->port_width = (uint32_t)port_width;
390 } else {
391 if (goi->argc)
392 goto err_no_params;
393 Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, obj->port_width));
394 }
395 break;
396 case CFG_PROTOCOL:
397 if (goi->isconfigure) {
398 struct jim_nvp *p;
399 e = jim_getopt_nvp(goi, nvp_arm_tpiu_swo_protocol_opts, &p);
400 if (e != JIM_OK)
401 return e;
402 obj->pin_protocol = p->value;
403 } else {
404 if (goi->argc)
405 goto err_no_params;
406 struct jim_nvp *p;
407 e = jim_nvp_value2name(goi->interp, nvp_arm_tpiu_swo_protocol_opts, obj->pin_protocol, &p);
408 if (e != JIM_OK) {
409 Jim_SetResultString(goi->interp, "protocol error", -1);
410 return JIM_ERR;
411 }
412 Jim_SetResult(goi->interp, Jim_NewStringObj(goi->interp, p->name, -1));
413 }
414 break;
415 case CFG_FORMATTER:
416 if (goi->isconfigure) {
417 struct jim_nvp *p;
418 e = jim_getopt_nvp(goi, nvp_arm_tpiu_swo_bool_opts, &p);
419 if (e != JIM_OK)
420 return e;
421 obj->en_formatter = p->value;
422 } else {
423 if (goi->argc)
424 goto err_no_params;
425 struct jim_nvp *p;
426 e = jim_nvp_value2name(goi->interp, nvp_arm_tpiu_swo_bool_opts, obj->en_formatter, &p);
427 if (e != JIM_OK) {
428 Jim_SetResultString(goi->interp, "formatter error", -1);
429 return JIM_ERR;
430 }
431 Jim_SetResult(goi->interp, Jim_NewStringObj(goi->interp, p->name, -1));
432 }
433 break;
434 case CFG_TRACECLKIN:
435 if (goi->isconfigure) {
436 jim_wide clk;
437 e = jim_getopt_wide(goi, &clk);
438 if (e != JIM_OK)
439 return e;
440 obj->traceclkin_freq = clk;
441 } else {
442 if (goi->argc)
443 goto err_no_params;
444 Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, obj->traceclkin_freq));
445 }
446 break;
447 case CFG_BITRATE:
448 if (goi->isconfigure) {
449 jim_wide clk;
450 e = jim_getopt_wide(goi, &clk);
451 if (e != JIM_OK)
452 return e;
453 obj->swo_pin_freq = clk;
454 } else {
455 if (goi->argc)
456 goto err_no_params;
457 Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, obj->swo_pin_freq));
458 }
459 break;
460 case CFG_OUTFILE:
461 if (goi->isconfigure) {
462 const char *s;
463 e = jim_getopt_string(goi, &s, NULL);
464 if (e != JIM_OK)
465 return e;
466 if (s[0] == ':') {
467 char *end;
468 long port = strtol(s + 1, &end, 0);
469 if (port <= 0 || port > UINT16_MAX || *end != '\0') {
470 Jim_SetResultFormatted(goi->interp, "Invalid TCP port \'%s\'", s + 1);
471 return JIM_ERR;
472 }
473 }
474 free(obj->out_filename);
475 obj->out_filename = strdup(s);
476 if (!obj->out_filename) {
477 LOG_ERROR("Out of memory");
478 return JIM_ERR;
479 }
480 } else {
481 if (goi->argc)
482 goto err_no_params;
483 if (obj->out_filename)
484 Jim_SetResult(goi->interp, Jim_NewStringObj(goi->interp, obj->out_filename, -1));
485 }
486 break;
487 case CFG_EVENT:
488 if (goi->isconfigure) {
489 if (goi->argc < 2) {
490 Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv, "-event ?event-name? ?EVENT-BODY?");
491 return JIM_ERR;
492 }
493 } else {
494 if (goi->argc != 1) {
495 Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv, "-event ?event-name?");
496 return JIM_ERR;
497 }
498 }
499
500 {
501 struct jim_nvp *p;
502 Jim_Obj *o;
503 struct arm_tpiu_swo_event_action *ea = obj->event_action;
504
505 e = jim_getopt_nvp(goi, nvp_arm_tpiu_swo_event, &p);
506 if (e != JIM_OK) {
507 jim_getopt_nvp_unknown(goi, nvp_arm_tpiu_swo_event, 1);
508 return e;
509 }
510
511 while (ea) {
512 /* replace existing? */
513 if (ea->event == (enum arm_tpiu_swo_event)p->value)
514 break;
515 ea = ea->next;
516 }
517
518 if (goi->isconfigure) {
519 if (!ea) {
520 ea = calloc(1, sizeof(*ea));
521 if (!ea) {
522 LOG_ERROR("Out of memory");
523 return JIM_ERR;
524 }
525 ea->next = obj->event_action;
526 obj->event_action = ea;
527 }
528 if (ea->body)
529 Jim_DecrRefCount(ea->interp, ea->body);
530 ea->event = p->value;
531 ea->interp = goi->interp;
532 jim_getopt_obj(goi, &o);
533 ea->body = Jim_DuplicateObj(goi->interp, o);
534 Jim_IncrRefCount(ea->body);
535 } else {
536 if (ea)
537 Jim_SetResult(goi->interp, Jim_DuplicateObj(goi->interp, ea->body));
538 }
539 }
540 break;
541 }
542 }
543
544 return JIM_OK;
545
546 err_no_params:
547 Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv, "NO PARAMS");
548 return JIM_ERR;
549 }
550
551 static int jim_arm_tpiu_swo_configure(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
552 {
553 struct command *c = jim_to_command(interp);
554 struct jim_getopt_info goi;
555
556 jim_getopt_setup(&goi, interp, argc - 1, argv + 1);
557 goi.isconfigure = !strcmp(c->name, "configure");
558 if (goi.argc < 1) {
559 Jim_WrongNumArgs(goi.interp, goi.argc, goi.argv,
560 "missing: -option ...");
561 return JIM_ERR;
562 }
563 struct arm_tpiu_swo_object *obj = c->jim_handler_data;
564 return arm_tpiu_swo_configure(&goi, obj);
565 }
566
567 static int wrap_write_u32(struct target *target, struct adiv5_ap *tpiu_ap,
568 target_addr_t address, uint32_t value)
569 {
570 if (transport_is_hla())
571 return target_write_u32(target, address, value);
572 else
573 return mem_ap_write_atomic_u32(tpiu_ap, address, value);
574 }
575
576 static int wrap_read_u32(struct target *target, struct adiv5_ap *tpiu_ap,
577 target_addr_t address, uint32_t *value)
578 {
579 if (transport_is_hla())
580 return target_read_u32(target, address, value);
581 else
582 return mem_ap_read_atomic_u32(tpiu_ap, address, value);
583 }
584
585 static int jim_arm_tpiu_swo_enable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
586 {
587 struct command *c = jim_to_command(interp);
588 struct arm_tpiu_swo_object *obj = c->jim_handler_data;
589 struct command_context *cmd_ctx = current_command_context(interp);
590 struct adiv5_ap *tpiu_ap = dap_ap(obj->spot.dap, obj->spot.ap_num);
591 uint32_t value;
592 int retval;
593
594 if (argc != 1) {
595 Jim_WrongNumArgs(interp, 1, argv, "Too many parameters");
596 return JIM_ERR;
597 }
598
599 if (cmd_ctx->mode == COMMAND_CONFIG) {
600 LOG_DEBUG("%s: enable deferred", obj->name);
601 obj->deferred_enable = true;
602 return JIM_OK;
603 }
604
605 if (obj->enabled)
606 return JIM_OK;
607
608 if (transport_is_hla() && obj->spot.ap_num > 0) {
609 LOG_ERROR("Invalid access port %d. Only AP#0 allowed with hla transport", obj->spot.ap_num);
610 return JIM_ERR;
611 }
612
613 if (!obj->traceclkin_freq) {
614 LOG_ERROR("Trace clock-in frequency not set");
615 return JIM_ERR;
616 }
617
618 if (obj->pin_protocol == TPIU_SPPR_PROTOCOL_MANCHESTER || obj->pin_protocol == TPIU_SPPR_PROTOCOL_UART)
619 if (!obj->swo_pin_freq) {
620 LOG_ERROR("SWO pin frequency not set");
621 return JIM_ERR;
622 }
623
624 struct target *target = get_current_target(cmd_ctx);
625
626 /* START_DEPRECATED_TPIU */
627 if (obj->recheck_ap_cur_target) {
628 if (strcmp(target->type->name, "cortex_m") &&
629 strcmp(target->type->name, "hla_target")) {
630 LOG_ERROR(MSG "Current target is not a Cortex-M nor a HLA");
631 return JIM_ERR;
632 }
633 if (!target_was_examined(target)) {
634 LOG_ERROR(MSG "Current target not examined yet");
635 return JIM_ERR;
636 }
637 struct cortex_m_common *cm = target_to_cm(target);
638 obj->recheck_ap_cur_target = false;
639 obj->spot.ap_num = cm->armv7m.debug_ap->ap_num;
640 tpiu_ap = dap_ap(obj->spot.dap, obj->spot.ap_num);
641 if (obj->spot.ap_num == 0)
642 LOG_INFO(MSG "Confirmed TPIU %s is on AP 0", obj->name);
643 else
644 LOG_INFO(MSG "Target %s is on AP %d. Revised command is "
645 "\'tpiu create %s -dap %s -ap-num %d\'",
646 target_name(target), obj->spot.ap_num,
647 obj->name, adiv5_dap_name(obj->spot.dap), obj->spot.ap_num);
648 }
649 /* END_DEPRECATED_TPIU */
650
651 /* trigger the event before any attempt to R/W in the TPIU/SWO */
652 arm_tpiu_swo_handle_event(obj, TPIU_SWO_EVENT_PRE_ENABLE);
653
654 retval = wrap_read_u32(target, tpiu_ap, obj->spot.base + TPIU_DEVID_OFFSET, &value);
655 if (retval != ERROR_OK) {
656 LOG_ERROR("Unable to read %s", obj->name);
657 return JIM_ERR;
658 }
659 switch (obj->pin_protocol) {
660 case TPIU_SPPR_PROTOCOL_SYNC:
661 value = !(value & TPIU_DEVID_NOSUPPORT_SYNC);
662 break;
663 case TPIU_SPPR_PROTOCOL_UART:
664 value &= TPIU_DEVID_SUPPORT_UART;
665 break;
666 case TPIU_SPPR_PROTOCOL_MANCHESTER:
667 value &= TPIU_DEVID_SUPPORT_MANCHESTER;
668 break;
669 default:
670 value = 0;
671 }
672 if (!value) {
673 struct jim_nvp *p;
674 jim_nvp_value2name(interp, nvp_arm_tpiu_swo_protocol_opts, obj->pin_protocol, &p);
675 LOG_ERROR("%s does not support protocol %s", obj->name, p->name);
676 return JIM_ERR;
677 }
678
679 if (obj->pin_protocol == TPIU_SPPR_PROTOCOL_SYNC) {
680 retval = wrap_read_u32(target, tpiu_ap, obj->spot.base + TPIU_SSPSR_OFFSET, &value);
681 if (retval != ERROR_OK) {
682 LOG_ERROR("Cannot read TPIU register SSPSR");
683 return JIM_ERR;
684 }
685 if (!(value & BIT(obj->port_width - 1))) {
686 LOG_ERROR("TPIU does not support port-width of %d bits", obj->port_width);
687 return JIM_ERR;
688 }
689 }
690
691 uint16_t prescaler = 1; /* dummy value */
692 unsigned int swo_pin_freq = obj->swo_pin_freq; /* could be replaced */
693
694 if (obj->out_filename && strcmp(obj->out_filename, "external") && obj->out_filename[0]) {
695 if (obj->out_filename[0] == ':') {
696 struct arm_tpiu_swo_priv_connection *priv = malloc(sizeof(*priv));
697 if (!priv) {
698 LOG_ERROR("Out of memory");
699 return JIM_ERR;
700 }
701 priv->obj = obj;
702 LOG_INFO("starting trace server for %s on %s", obj->name, &obj->out_filename[1]);
703 retval = add_service("tpiu_swo_trace", &obj->out_filename[1],
704 CONNECTION_LIMIT_UNLIMITED, arm_tpiu_swo_service_new_connection,
705 arm_tpiu_swo_service_input, arm_tpiu_swo_service_connection_closed,
706 priv);
707 if (retval != ERROR_OK) {
708 LOG_ERROR("Can't configure trace TCP port %s", &obj->out_filename[1]);
709 return JIM_ERR;
710 }
711 } else if (strcmp(obj->out_filename, "-")) {
712 obj->file = fopen(obj->out_filename, "ab");
713 if (!obj->file) {
714 LOG_ERROR("Can't open trace destination file \"%s\"", obj->out_filename);
715 return JIM_ERR;
716 }
717 }
718
719 retval = adapter_config_trace(true, obj->pin_protocol, obj->port_width,
720 &swo_pin_freq, obj->traceclkin_freq, &prescaler);
721 if (retval != ERROR_OK) {
722 LOG_ERROR("Failed to start adapter's trace");
723 arm_tpiu_swo_close_output(obj);
724 return JIM_ERR;
725 }
726
727 if (obj->swo_pin_freq != swo_pin_freq)
728 LOG_INFO("SWO pin data rate adjusted by adapter to %d Hz", swo_pin_freq);
729 obj->swo_pin_freq = swo_pin_freq;
730
731 target_register_timer_callback(arm_tpiu_swo_poll_trace, 1,
732 TARGET_TIMER_TYPE_PERIODIC, obj);
733
734 obj->en_capture = true;
735 } else if (obj->pin_protocol == TPIU_SPPR_PROTOCOL_MANCHESTER || obj->pin_protocol == TPIU_SPPR_PROTOCOL_UART) {
736 prescaler = (obj->traceclkin_freq + obj->swo_pin_freq / 2) / obj->swo_pin_freq;
737 if (prescaler > TPIU_ACPR_MAX_PRESCALER)
738 prescaler = TPIU_ACPR_MAX_PRESCALER;
739 swo_pin_freq = obj->traceclkin_freq / prescaler;
740
741 if (obj->swo_pin_freq != swo_pin_freq)
742 LOG_INFO("SWO pin data rate adjusted to %d Hz", swo_pin_freq);
743 obj->swo_pin_freq = swo_pin_freq;
744 }
745
746 retval = wrap_write_u32(target, tpiu_ap, obj->spot.base + TPIU_CSPSR_OFFSET, BIT(obj->port_width - 1));
747 if (retval != ERROR_OK)
748 goto error_exit;
749
750 retval = wrap_write_u32(target, tpiu_ap, obj->spot.base + TPIU_ACPR_OFFSET, prescaler - 1);
751 if (retval != ERROR_OK)
752 goto error_exit;
753
754 retval = wrap_write_u32(target, tpiu_ap, obj->spot.base + TPIU_SPPR_OFFSET, obj->pin_protocol);
755 if (retval != ERROR_OK)
756 goto error_exit;
757
758 retval = wrap_read_u32(target, tpiu_ap, obj->spot.base + TPIU_FFCR_OFFSET, &value);
759 if (retval != ERROR_OK)
760 goto error_exit;
761 if (obj->en_formatter)
762 value |= BIT(1);
763 else
764 value &= ~BIT(1);
765 retval = wrap_write_u32(target, tpiu_ap, obj->spot.base + TPIU_FFCR_OFFSET, value);
766 if (retval != ERROR_OK)
767 goto error_exit;
768
769 arm_tpiu_swo_handle_event(obj, TPIU_SWO_EVENT_POST_ENABLE);
770
771 /* START_DEPRECATED_TPIU */
772 target_handle_event(target, TARGET_EVENT_TRACE_CONFIG);
773 /* END_DEPRECATED_TPIU */
774
775 obj->enabled = true;
776 return JIM_OK;
777
778 error_exit:
779 LOG_ERROR("Error!");
780
781 if (obj->en_capture) {
782 obj->en_capture = false;
783
784 arm_tpiu_swo_close_output(obj);
785
786 target_unregister_timer_callback(arm_tpiu_swo_poll_trace, obj);
787
788 retval = adapter_config_trace(false, 0, 0, NULL, 0, NULL);
789 if (retval != ERROR_OK) {
790 LOG_ERROR("Failed to stop adapter's trace");
791 return JIM_ERR;
792 }
793 }
794 return JIM_ERR;
795 }
796
797 static int jim_arm_tpiu_swo_disable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
798 {
799 struct command *c = jim_to_command(interp);
800 struct arm_tpiu_swo_object *obj = c->jim_handler_data;
801
802 if (argc != 1) {
803 Jim_WrongNumArgs(interp, 1, argv, "Too many parameters");
804 return JIM_ERR;
805 }
806
807 if (!obj->enabled)
808 return JIM_OK;
809 obj->enabled = false;
810
811 arm_tpiu_swo_handle_event(obj, TPIU_SWO_EVENT_PRE_DISABLE);
812
813 if (obj->en_capture) {
814 obj->en_capture = false;
815
816 arm_tpiu_swo_close_output(obj);
817
818 target_unregister_timer_callback(arm_tpiu_swo_poll_trace, obj);
819
820 int retval = adapter_config_trace(false, 0, 0, NULL, 0, NULL);
821 if (retval != ERROR_OK) {
822 LOG_ERROR("Failed to stop adapter's trace");
823 return JIM_ERR;
824 }
825 }
826
827 arm_tpiu_swo_handle_event(obj, TPIU_SWO_EVENT_POST_DISABLE);
828
829 /* START_DEPRECATED_TPIU */
830 struct command_context *cmd_ctx = current_command_context(interp);
831 struct target *target = get_current_target(cmd_ctx);
832 target_handle_event(target, TARGET_EVENT_TRACE_CONFIG);
833 /* END_DEPRECATED_TPIU */
834
835 return JIM_OK;
836 }
837
838 static const struct command_registration arm_tpiu_swo_instance_command_handlers[] = {
839 {
840 .name = "configure",
841 .mode = COMMAND_ANY,
842 .jim_handler = jim_arm_tpiu_swo_configure,
843 .help = "configure a new TPIU/SWO for use",
844 .usage = "[attribute value ...]",
845 },
846 {
847 .name = "cget",
848 .mode = COMMAND_ANY,
849 .jim_handler = jim_arm_tpiu_swo_configure,
850 .help = "returns the specified TPIU/SWO attribute",
851 .usage = "attribute",
852 },
853 {
854 .name = "eventlist",
855 .mode = COMMAND_ANY,
856 .handler = handle_arm_tpiu_swo_event_list,
857 .help = "displays a table of events defined for this TPIU/SWO",
858 .usage = "",
859 },
860 {
861 .name = "enable",
862 .mode = COMMAND_ANY,
863 .jim_handler = jim_arm_tpiu_swo_enable,
864 .usage = "",
865 .help = "Enables the TPIU/SWO output",
866 },
867 {
868 .name = "disable",
869 .mode = COMMAND_EXEC,
870 .jim_handler = jim_arm_tpiu_swo_disable,
871 .usage = "",
872 .help = "Disables the TPIU/SWO output",
873 },
874 COMMAND_REGISTRATION_DONE
875 };
876
877 static int arm_tpiu_swo_create(Jim_Interp *interp, struct arm_tpiu_swo_object *obj)
878 {
879 struct command_context *cmd_ctx;
880 Jim_Cmd *cmd;
881 int e;
882
883 cmd_ctx = current_command_context(interp);
884 assert(cmd_ctx);
885
886 /* does this command exist? */
887 cmd = Jim_GetCommand(interp, Jim_NewStringObj(interp, obj->name, -1), JIM_NONE);
888 if (cmd) {
889 Jim_SetResultFormatted(interp, "Command: %s Exists", obj->name);
890 return JIM_ERR;
891 }
892
893 /* now - create the new tpiu/swo name command */
894 const struct command_registration obj_commands[] = {
895 {
896 .name = obj->name,
897 .mode = COMMAND_ANY,
898 .help = "tpiu/swo instance command group",
899 .usage = "",
900 .chain = arm_tpiu_swo_instance_command_handlers,
901 },
902 COMMAND_REGISTRATION_DONE
903 };
904 e = register_commands_with_data(cmd_ctx, NULL, obj_commands, obj);
905 if (e != ERROR_OK)
906 return JIM_ERR;
907
908 list_add_tail(&obj->lh, &all_tpiu_swo);
909
910 return JIM_OK;
911 }
912
913 static int jim_arm_tpiu_swo_create(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
914 {
915 struct jim_getopt_info goi;
916 jim_getopt_setup(&goi, interp, argc - 1, argv + 1);
917 if (goi.argc < 1) {
918 Jim_WrongNumArgs(goi.interp, 1, goi.argv, "?name? ..options...");
919 return JIM_ERR;
920 }
921
922 struct arm_tpiu_swo_object *obj = calloc(1, sizeof(struct arm_tpiu_swo_object));
923 if (!obj) {
924 LOG_ERROR("Out of memory");
925 return JIM_ERR;
926 }
927 INIT_LIST_HEAD(&obj->connections);
928 adiv5_mem_ap_spot_init(&obj->spot);
929 obj->spot.base = TPIU_SWO_DEFAULT_BASE;
930 obj->port_width = 1;
931
932 Jim_Obj *n;
933 jim_getopt_obj(&goi, &n);
934 obj->name = strdup(Jim_GetString(n, NULL));
935 if (!obj->name) {
936 LOG_ERROR("Out of memory");
937 free(obj);
938 return JIM_ERR;
939 }
940
941 /* Do the rest as "configure" options */
942 goi.isconfigure = 1;
943 int e = arm_tpiu_swo_configure(&goi, obj);
944 if (e != JIM_OK)
945 goto err_exit;
946
947 if (!obj->spot.dap || obj->spot.ap_num == DP_APSEL_INVALID) {
948 Jim_SetResultString(goi.interp, "-dap and -ap-num required when creating TPIU", -1);
949 goto err_exit;
950 }
951
952 e = arm_tpiu_swo_create(goi.interp, obj);
953 if (e != JIM_OK)
954 goto err_exit;
955
956 return JIM_OK;
957
958 err_exit:
959 free(obj->name);
960 free(obj->out_filename);
961 free(obj);
962 return JIM_ERR;
963 }
964
965 static int jim_arm_tpiu_swo_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
966 {
967 struct arm_tpiu_swo_object *obj;
968
969 if (argc != 1) {
970 Jim_WrongNumArgs(interp, 1, argv, "Too many parameters");
971 return JIM_ERR;
972 }
973 Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0));
974 list_for_each_entry(obj, &all_tpiu_swo, lh) {
975 Jim_ListAppendElement(interp, Jim_GetResult(interp),
976 Jim_NewStringObj(interp, obj->name, -1));
977 }
978 return JIM_OK;
979 }
980
981 static int jim_arm_tpiu_swo_init(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
982 {
983 struct command_context *cmd_ctx = current_command_context(interp);
984 struct arm_tpiu_swo_object *obj;
985 int retval = JIM_OK;
986
987 if (argc != 1) {
988 Jim_WrongNumArgs(interp, 1, argv, "Too many parameters");
989 return JIM_ERR;
990 }
991 list_for_each_entry(obj, &all_tpiu_swo, lh) {
992 if (!obj->deferred_enable)
993 continue;
994 LOG_DEBUG("%s: running enable during init", obj->name);
995 int retval2 = command_run_linef(cmd_ctx, "%s enable", obj->name);
996 if (retval2 != ERROR_OK)
997 retval = JIM_ERR;
998 }
999 return retval;
1000 }
1001
1002 /* START_DEPRECATED_TPIU */
1003 /* DEPRECATED: emulation of old command 'tpiu config' */
1004 COMMAND_HANDLER(handle_tpiu_deprecated_config_command)
1005 {
1006 struct target *target = get_current_target(CMD_CTX);
1007 struct arm_tpiu_swo_object *obj = NULL;
1008 int retval;
1009
1010 if (strcmp(target->type->name, "cortex_m") &&
1011 strcmp(target->type->name, "hla_target")) {
1012 LOG_ERROR(MSG "Current target is not a Cortex-M nor a HLA");
1013 return ERROR_FAIL;
1014 }
1015
1016 if (!list_empty(&all_tpiu_swo)) {
1017 obj = list_first_entry(&all_tpiu_swo, typeof(*obj), lh);
1018 LOG_INFO(MSG "Using %s", obj->name);
1019 } else {
1020 struct cortex_m_common *cm = target_to_cm(target);
1021 struct adiv5_private_config *pc = target->private_config;
1022 struct adiv5_dap *dap = pc->dap;
1023 int ap_num = pc->ap_num;
1024 bool set_recheck_ap_cur_target = false;
1025
1026 LOG_INFO(MSG "Adding a TPIU \'%s.tpiu\' in the configuration", target_name(target));
1027
1028 if (ap_num == DP_APSEL_INVALID && transport_is_hla())
1029 ap_num = 0; /* HLA should only support AP 0 */
1030
1031 if (ap_num == DP_APSEL_INVALID && target_was_examined(target))
1032 ap_num = cm->armv7m.debug_ap->ap_num;
1033
1034 if (ap_num == DP_APSEL_INVALID) {
1035 LOG_INFO(MSG "Target %s uses AP autodetection. Adding TPIU on AP 0; can be revised later",
1036 target_name(target));
1037 ap_num = 0;
1038 set_recheck_ap_cur_target = true;
1039 }
1040
1041 LOG_INFO(MSG "Running: \'tpiu create %s.tpiu -dap %s -ap-num %d\'",
1042 target_name(target), adiv5_dap_name(dap), ap_num);
1043
1044 retval = command_run_linef(CMD_CTX, "tpiu create %s.tpiu -dap %s -ap-num %d",
1045 target_name(target), adiv5_dap_name(dap), ap_num);
1046 if (retval != ERROR_OK)
1047 return retval;
1048
1049 obj = list_first_entry(&all_tpiu_swo, typeof(*obj), lh);
1050 if (set_recheck_ap_cur_target)
1051 obj->recheck_ap_cur_target = true;
1052 }
1053
1054 unsigned int cmd_idx = 0;
1055 if (cmd_idx == CMD_ARGC)
1056 return ERROR_COMMAND_SYNTAX_ERROR;
1057
1058 if (!strcmp(CMD_ARGV[cmd_idx], "disable")) {
1059 if (CMD_ARGC != cmd_idx + 1)
1060 return ERROR_COMMAND_SYNTAX_ERROR;
1061 LOG_INFO(MSG "Running: \'%s disable\'", obj->name);
1062 return command_run_linef(CMD_CTX, "%s disable", obj->name);
1063 }
1064
1065 const char *output = NULL;
1066 const char *protocol;
1067 const char *formatter = NULL;
1068 const char *port_width = NULL;
1069 const char *trace_clk;
1070 const char *pin_clk = NULL;
1071 if (!strcmp(CMD_ARGV[cmd_idx], "internal")) {
1072 cmd_idx++;
1073 if (cmd_idx == CMD_ARGC)
1074 return ERROR_COMMAND_SYNTAX_ERROR;
1075 output = CMD_ARGV[cmd_idx];
1076 } else if (strcmp(CMD_ARGV[cmd_idx], "external"))
1077 return ERROR_COMMAND_SYNTAX_ERROR;
1078 cmd_idx++;
1079 if (cmd_idx == CMD_ARGC)
1080 return ERROR_COMMAND_SYNTAX_ERROR;
1081 if (!strcmp(CMD_ARGV[cmd_idx], "sync")) {
1082 protocol = CMD_ARGV[cmd_idx];
1083 cmd_idx++;
1084 if (cmd_idx == CMD_ARGC)
1085 return ERROR_COMMAND_SYNTAX_ERROR;
1086 port_width = CMD_ARGV[cmd_idx];
1087 } else {
1088 if (strcmp(CMD_ARGV[cmd_idx], "manchester") && strcmp(CMD_ARGV[cmd_idx], "uart"))
1089 return ERROR_COMMAND_SYNTAX_ERROR;
1090 protocol = CMD_ARGV[cmd_idx];
1091 cmd_idx++;
1092 if (cmd_idx == CMD_ARGC)
1093 return ERROR_COMMAND_SYNTAX_ERROR;
1094 formatter = CMD_ARGV[cmd_idx];
1095 }
1096 cmd_idx++;
1097 if (cmd_idx == CMD_ARGC)
1098 return ERROR_COMMAND_SYNTAX_ERROR;
1099 trace_clk = CMD_ARGV[cmd_idx];
1100 cmd_idx++;
1101 if (cmd_idx != CMD_ARGC) {
1102 pin_clk = CMD_ARGV[cmd_idx];
1103 cmd_idx++;
1104 }
1105 if (cmd_idx != CMD_ARGC)
1106 return ERROR_COMMAND_SYNTAX_ERROR;
1107
1108 LOG_INFO(MSG "Running: \'%s configure -protocol %s -traceclk %s" "%s%s" "%s%s" "%s%s" "%s%s\'",
1109 obj->name, protocol, trace_clk,
1110 pin_clk ? " -pin-freq " : "", pin_clk ? pin_clk : "",
1111 output ? " -output " : "", output ? output : "",
1112 formatter ? " -formatter " : "", formatter ? formatter : "",
1113 port_width ? " -port-width " : "", port_width ? port_width : "");
1114
1115 retval = command_run_linef(CMD_CTX,
1116 "%s configure -protocol %s -traceclk %s" "%s%s" "%s%s" "%s%s" "%s%s",
1117 obj->name, protocol, trace_clk,
1118 pin_clk ? " -pin-freq " : "", pin_clk ? pin_clk : "",
1119 output ? " -output " : "", output ? output : "",
1120 formatter ? " -formatter " : "", formatter ? formatter : "",
1121 port_width ? " -port-width " : "", port_width ? port_width : "");
1122 if (retval != ERROR_OK)
1123 return retval;
1124
1125 LOG_INFO(MSG "Running: \'%s enable\'", obj->name);
1126 retval = command_run_linef(CMD_CTX, "%s enable", obj->name);
1127 if (retval != ERROR_OK)
1128 return retval;
1129
1130 return ERROR_OK;
1131 }
1132
1133 static const struct command_registration arm_tpiu_deprecated_subcommand_handlers[] = {
1134 {
1135 .name = "config",
1136 .handler = handle_tpiu_deprecated_config_command,
1137 .mode = COMMAND_ANY,
1138 .help = "Configure TPIU features, DEPRECATED, use \'tpiu create\'",
1139 .usage = "(disable | "
1140 "((external | internal (<filename> | <:port> | -)) "
1141 "(sync <port width> | ((manchester | uart) <formatter enable>)) "
1142 "<TRACECLKIN freq> [<trace freq>]))",
1143 },
1144 COMMAND_REGISTRATION_DONE
1145 };
1146
1147 const struct command_registration arm_tpiu_deprecated_command_handlers[] = {
1148 {
1149 .name = "tpiu",
1150 .chain = arm_tpiu_deprecated_subcommand_handlers,
1151 .usage = "",
1152 .help = "tpiu command group",
1153 },
1154 COMMAND_REGISTRATION_DONE
1155 };
1156 /* END_DEPRECATED_TPIU */
1157
1158 static const struct command_registration arm_tpiu_swo_subcommand_handlers[] = {
1159 {
1160 .name = "create",
1161 .mode = COMMAND_ANY,
1162 .jim_handler = jim_arm_tpiu_swo_create,
1163 .usage = "name [-dap dap] [-ap-num num] [-address baseaddr]",
1164 .help = "Creates a new TPIU or SWO object",
1165 },
1166 {
1167 .name = "names",
1168 .mode = COMMAND_ANY,
1169 .jim_handler = jim_arm_tpiu_swo_names,
1170 .usage = "",
1171 .help = "Lists all registered TPIU and SWO objects by name",
1172 },
1173 {
1174 .name = "init",
1175 .mode = COMMAND_EXEC,
1176 .jim_handler = jim_arm_tpiu_swo_init,
1177 .usage = "",
1178 .help = "Initialize TPIU and SWO",
1179 },
1180 COMMAND_REGISTRATION_DONE
1181 };
1182
1183 static const struct command_registration arm_tpiu_swo_command_handlers[] = {
1184 {
1185 .name = "tpiu",
1186 .chain = arm_tpiu_swo_subcommand_handlers,
1187 .usage = "",
1188 .help = "tpiu command group",
1189 },
1190 {
1191 .name = "swo",
1192 .chain = arm_tpiu_swo_subcommand_handlers,
1193 .usage = "",
1194 .help = "swo command group",
1195 },
1196 COMMAND_REGISTRATION_DONE
1197 };
1198
1199 int arm_tpiu_swo_register_commands(struct command_context *cmd_ctx)
1200 {
1201 return register_commands(cmd_ctx, NULL, arm_tpiu_swo_command_handlers);
1202 }

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)