jtag: linuxgpiod: drop extra parenthesis
[openocd.git] / src / target / adi_v5_swd.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2
3 /***************************************************************************
4 *
5 * Copyright (C) 2010 by David Brownell
6 ***************************************************************************/
7
8 /**
9 * @file
10 * Utilities to support ARM "Serial Wire Debug" (SWD), a low pin-count debug
11 * link protocol used in cases where JTAG is not wanted. This is coupled to
12 * recent versions of ARM's "CoreSight" debug framework. This specific code
13 * is a transport level interface, with "target/arm_adi_v5.[hc]" code
14 * understanding operation semantics, shared with the JTAG transport.
15 *
16 * Single-DAP support only.
17 *
18 * for details, see "ARM IHI 0031A"
19 * ARM Debug Interface v5 Architecture Specification
20 * especially section 5.3 for SWD protocol
21 * and "ARM IHI 0074C" ARM Debug Interface Architecture Specification ADIv6.0
22 *
23 * On many chips (most current Cortex-M3 parts) SWD is a run-time alternative
24 * to JTAG. Boards may support one or both. There are also SWD-only chips,
25 * (using SW-DP not SWJ-DP).
26 *
27 * Even boards that also support JTAG can benefit from SWD support, because
28 * usually there's no way to access the SWO trace view mechanism in JTAG mode.
29 * That is, trace access may require SWD support.
30 *
31 */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include "arm.h"
38 #include "arm_adi_v5.h"
39 #include <helper/time_support.h>
40
41 #include <transport/transport.h>
42 #include <jtag/interface.h>
43
44 #include <jtag/swd.h>
45
46 /* for debug, set do_sync to true to force synchronous transfers */
47 static bool do_sync;
48
49 static struct adiv5_dap *swd_multidrop_selected_dap;
50
51
52 static int swd_queue_dp_write_inner(struct adiv5_dap *dap, unsigned int reg,
53 uint32_t data);
54
55
56 static int swd_send_sequence(struct adiv5_dap *dap, enum swd_special_seq seq)
57 {
58 const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
59 assert(swd);
60
61 return swd->switch_seq(seq);
62 }
63
64 static void swd_finish_read(struct adiv5_dap *dap)
65 {
66 const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
67 if (dap->last_read) {
68 swd->read_reg(swd_cmd(true, false, DP_RDBUFF), dap->last_read, 0);
69 dap->last_read = NULL;
70 }
71 }
72
73 static void swd_clear_sticky_errors(struct adiv5_dap *dap)
74 {
75 const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
76 assert(swd);
77
78 swd->write_reg(swd_cmd(false, false, DP_ABORT),
79 STKCMPCLR | STKERRCLR | WDERRCLR | ORUNERRCLR, 0);
80 }
81
82 static int swd_run_inner(struct adiv5_dap *dap)
83 {
84 const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
85 int retval;
86
87 retval = swd->run();
88
89 if (retval != ERROR_OK) {
90 /* fault response */
91 dap->do_reconnect = true;
92 }
93
94 return retval;
95 }
96
97 static inline int check_sync(struct adiv5_dap *dap)
98 {
99 return do_sync ? swd_run_inner(dap) : ERROR_OK;
100 }
101
102 /** Select the DP register bank matching bits 7:4 of reg. */
103 static int swd_queue_dp_bankselect(struct adiv5_dap *dap, unsigned int reg)
104 {
105 /* Only register address 0 and 4 are banked. */
106 if ((reg & 0xf) > 4)
107 return ERROR_OK;
108
109 uint64_t sel = (reg & 0x000000F0) >> 4;
110 if (dap->select != DP_SELECT_INVALID)
111 sel |= dap->select & ~0xfULL;
112
113 if (sel == dap->select)
114 return ERROR_OK;
115
116 dap->select = sel;
117
118 int retval = swd_queue_dp_write_inner(dap, DP_SELECT, (uint32_t)sel);
119 if (retval != ERROR_OK)
120 dap->select = DP_SELECT_INVALID;
121
122 return retval;
123 }
124
125 static int swd_queue_dp_read_inner(struct adiv5_dap *dap, unsigned int reg,
126 uint32_t *data)
127 {
128 const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
129 assert(swd);
130
131 int retval = swd_queue_dp_bankselect(dap, reg);
132 if (retval != ERROR_OK)
133 return retval;
134
135 swd->read_reg(swd_cmd(true, false, reg), data, 0);
136
137 return check_sync(dap);
138 }
139
140 static int swd_queue_dp_write_inner(struct adiv5_dap *dap, unsigned int reg,
141 uint32_t data)
142 {
143 int retval;
144 const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
145 assert(swd);
146
147 swd_finish_read(dap);
148
149 if (reg == DP_SELECT) {
150 dap->select = data & (DP_SELECT_APSEL | DP_SELECT_APBANK | DP_SELECT_DPBANK);
151
152 swd->write_reg(swd_cmd(false, false, reg), data, 0);
153
154 retval = check_sync(dap);
155 if (retval != ERROR_OK)
156 dap->select = DP_SELECT_INVALID;
157
158 return retval;
159 }
160
161 retval = swd_queue_dp_bankselect(dap, reg);
162 if (retval != ERROR_OK)
163 return retval;
164
165 swd->write_reg(swd_cmd(false, false, reg), data, 0);
166
167 return check_sync(dap);
168 }
169
170
171 static int swd_multidrop_select_inner(struct adiv5_dap *dap, uint32_t *dpidr_ptr,
172 uint32_t *dlpidr_ptr, bool clear_sticky)
173 {
174 int retval;
175 uint32_t dpidr, dlpidr;
176
177 assert(dap_is_multidrop(dap));
178
179 swd_send_sequence(dap, LINE_RESET);
180 /* From ARM IHI 0074C ADIv6.0, chapter B4.3.3 "Connection and line reset
181 * sequence":
182 * - line reset sets DP_SELECT_DPBANK to zero;
183 * - read of DP_DPIDR takes the connection out of reset;
184 * - write of DP_TARGETSEL keeps the connection in reset;
185 * - other accesses return protocol error (SWDIO not driven by target).
186 *
187 * Read DP_DPIDR to get out of reset. Initialize dap->select to zero to
188 * skip the write to DP_SELECT, avoiding the protocol error. Set again
189 * dap->select to DP_SELECT_INVALID because the rest of the register is
190 * unknown after line reset.
191 */
192 dap->select = 0;
193
194 retval = swd_queue_dp_write_inner(dap, DP_TARGETSEL, dap->multidrop_targetsel);
195 if (retval != ERROR_OK)
196 return retval;
197
198 retval = swd_queue_dp_read_inner(dap, DP_DPIDR, &dpidr);
199 if (retval != ERROR_OK)
200 return retval;
201
202 if (clear_sticky) {
203 /* Clear all sticky errors (including ORUN) */
204 swd_clear_sticky_errors(dap);
205 } else {
206 /* Ideally just clear ORUN flag which is set by reset */
207 retval = swd_queue_dp_write_inner(dap, DP_ABORT, ORUNERRCLR);
208 if (retval != ERROR_OK)
209 return retval;
210 }
211
212 dap->select = DP_SELECT_INVALID;
213
214 retval = swd_queue_dp_read_inner(dap, DP_DLPIDR, &dlpidr);
215 if (retval != ERROR_OK)
216 return retval;
217
218 retval = swd_run_inner(dap);
219 if (retval != ERROR_OK)
220 return retval;
221
222 if ((dpidr & DP_DPIDR_VERSION_MASK) < (2UL << DP_DPIDR_VERSION_SHIFT)) {
223 LOG_INFO("Read DPIDR 0x%08" PRIx32
224 " has version < 2. A non multidrop capable device connected?",
225 dpidr);
226 return ERROR_FAIL;
227 }
228
229 /* TODO: check TARGETID if DLIPDR is same for more than one DP */
230 uint32_t expected_dlpidr = DP_DLPIDR_PROTVSN |
231 (dap->multidrop_targetsel & DP_TARGETSEL_INSTANCEID_MASK);
232 if (dlpidr != expected_dlpidr) {
233 LOG_INFO("Read incorrect DLPIDR 0x%08" PRIx32
234 " (possibly CTRL/STAT value)",
235 dlpidr);
236 return ERROR_FAIL;
237 }
238
239 LOG_DEBUG_IO("Selected DP_TARGETSEL 0x%08" PRIx32, dap->multidrop_targetsel);
240 swd_multidrop_selected_dap = dap;
241
242 if (dpidr_ptr)
243 *dpidr_ptr = dpidr;
244
245 if (dlpidr_ptr)
246 *dlpidr_ptr = dlpidr;
247
248 return retval;
249 }
250
251 static int swd_multidrop_select(struct adiv5_dap *dap)
252 {
253 if (!dap_is_multidrop(dap))
254 return ERROR_OK;
255
256 if (swd_multidrop_selected_dap == dap)
257 return ERROR_OK;
258
259 int retval = ERROR_OK;
260 for (unsigned int retry = 0; ; retry++) {
261 bool clear_sticky = retry > 0;
262
263 retval = swd_multidrop_select_inner(dap, NULL, NULL, clear_sticky);
264 if (retval == ERROR_OK)
265 break;
266
267 swd_multidrop_selected_dap = NULL;
268 if (retry > 3) {
269 LOG_ERROR("Failed to select multidrop %s", adiv5_dap_name(dap));
270 return retval;
271 }
272
273 LOG_DEBUG("Failed to select multidrop %s, retrying...",
274 adiv5_dap_name(dap));
275 /* we going to retry localy, do not ask for full reconnect */
276 dap->do_reconnect = false;
277 }
278
279 return retval;
280 }
281
282 static int swd_connect_multidrop(struct adiv5_dap *dap)
283 {
284 int retval;
285 uint32_t dpidr = 0xdeadbeef;
286 uint32_t dlpidr = 0xdeadbeef;
287 int64_t timeout = timeval_ms() + 500;
288
289 do {
290 swd_send_sequence(dap, JTAG_TO_DORMANT);
291 swd_send_sequence(dap, DORMANT_TO_SWD);
292
293 /* Clear link state, including the SELECT cache. */
294 dap->do_reconnect = false;
295 dap_invalidate_cache(dap);
296 swd_multidrop_selected_dap = NULL;
297
298 retval = swd_multidrop_select_inner(dap, &dpidr, &dlpidr, true);
299 if (retval == ERROR_OK)
300 break;
301
302 alive_sleep(1);
303
304 } while (timeval_ms() < timeout);
305
306 if (retval != ERROR_OK) {
307 swd_multidrop_selected_dap = NULL;
308 LOG_ERROR("Failed to connect multidrop %s", adiv5_dap_name(dap));
309 return retval;
310 }
311
312 LOG_INFO("SWD DPIDR 0x%08" PRIx32 ", DLPIDR 0x%08" PRIx32,
313 dpidr, dlpidr);
314
315 return retval;
316 }
317
318 static int swd_connect_single(struct adiv5_dap *dap)
319 {
320 int retval;
321 uint32_t dpidr = 0xdeadbeef;
322 int64_t timeout = timeval_ms() + 500;
323
324 do {
325 if (dap->switch_through_dormant) {
326 swd_send_sequence(dap, JTAG_TO_DORMANT);
327 swd_send_sequence(dap, DORMANT_TO_SWD);
328 } else {
329 swd_send_sequence(dap, JTAG_TO_SWD);
330 }
331
332 /* Clear link state, including the SELECT cache. */
333 dap->do_reconnect = false;
334 dap_invalidate_cache(dap);
335
336 /* The sequences to enter in SWD (JTAG_TO_SWD and DORMANT_TO_SWD) end
337 * with a SWD line reset sequence (50 clk with SWDIO high).
338 * From ARM IHI 0074C ADIv6.0, chapter B4.3.3 "Connection and line reset
339 * sequence":
340 * - line reset sets DP_SELECT_DPBANK to zero;
341 * - read of DP_DPIDR takes the connection out of reset;
342 * - write of DP_TARGETSEL keeps the connection in reset;
343 * - other accesses return protocol error (SWDIO not driven by target).
344 *
345 * Read DP_DPIDR to get out of reset. Initialize dap->select to zero to
346 * skip the write to DP_SELECT, avoiding the protocol error. Set again
347 * dap->select to DP_SELECT_INVALID because the rest of the register is
348 * unknown after line reset.
349 */
350 dap->select = 0;
351 retval = swd_queue_dp_read_inner(dap, DP_DPIDR, &dpidr);
352 if (retval == ERROR_OK) {
353 retval = swd_run_inner(dap);
354 if (retval == ERROR_OK)
355 break;
356 }
357
358 alive_sleep(1);
359
360 dap->switch_through_dormant = !dap->switch_through_dormant;
361 } while (timeval_ms() < timeout);
362
363 dap->select = DP_SELECT_INVALID;
364
365 if (retval != ERROR_OK) {
366 LOG_ERROR("Error connecting DP: cannot read IDR");
367 return retval;
368 }
369
370 LOG_INFO("SWD DPIDR 0x%08" PRIx32, dpidr);
371
372 do {
373 dap->do_reconnect = false;
374
375 /* force clear all sticky faults */
376 swd_clear_sticky_errors(dap);
377
378 retval = swd_run_inner(dap);
379 if (retval != ERROR_WAIT)
380 break;
381
382 alive_sleep(10);
383
384 } while (timeval_ms() < timeout);
385
386 return retval;
387 }
388
389 static int swd_connect(struct adiv5_dap *dap)
390 {
391 int status;
392
393 /* FIXME validate transport config ... is the
394 * configured DAP present (check IDCODE)?
395 */
396
397 /* Check if we should reset srst already when connecting, but not if reconnecting. */
398 if (!dap->do_reconnect) {
399 enum reset_types jtag_reset_config = jtag_get_reset_config();
400
401 if (jtag_reset_config & RESET_CNCT_UNDER_SRST) {
402 if (jtag_reset_config & RESET_SRST_NO_GATING)
403 adapter_assert_reset();
404 else
405 LOG_WARNING("\'srst_nogate\' reset_config option is required");
406 }
407 }
408
409 if (dap_is_multidrop(dap))
410 status = swd_connect_multidrop(dap);
411 else
412 status = swd_connect_single(dap);
413
414 /* IHI 0031E B4.3.2:
415 * "A WAIT response must not be issued to the ...
416 * ... writes to the ABORT register"
417 * swd_clear_sticky_errors() writes to the ABORT register only.
418 *
419 * Unfortunately at least Microchip SAMD51/E53/E54 returns WAIT
420 * in a corner case. Just try if ABORT resolves the problem.
421 */
422 if (status == ERROR_WAIT) {
423 LOG_WARNING("Connecting DP: stalled AP operation, issuing ABORT");
424
425 dap->do_reconnect = false;
426
427 status = swd_queue_dp_write_inner(dap, DP_ABORT,
428 DAPABORT | STKCMPCLR | STKERRCLR | WDERRCLR | ORUNERRCLR);
429
430 if (status == ERROR_OK)
431 status = swd_run_inner(dap);
432 }
433
434 if (status == ERROR_OK)
435 status = dap_dp_init(dap);
436
437 return status;
438 }
439
440 static int swd_check_reconnect(struct adiv5_dap *dap)
441 {
442 if (dap->do_reconnect)
443 return swd_connect(dap);
444
445 return ERROR_OK;
446 }
447
448 static int swd_queue_ap_abort(struct adiv5_dap *dap, uint8_t *ack)
449 {
450 const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
451 assert(swd);
452
453 /* TODO: Send DAPABORT in swd_multidrop_select_inner()
454 * in the case the multidrop dap is not selected?
455 * swd_queue_ap_abort() is not currently used anyway...
456 */
457 int retval = swd_multidrop_select(dap);
458 if (retval != ERROR_OK)
459 return retval;
460
461 swd->write_reg(swd_cmd(false, false, DP_ABORT),
462 DAPABORT | STKCMPCLR | STKERRCLR | WDERRCLR | ORUNERRCLR, 0);
463 return check_sync(dap);
464 }
465
466 static int swd_queue_dp_read(struct adiv5_dap *dap, unsigned reg,
467 uint32_t *data)
468 {
469 int retval = swd_check_reconnect(dap);
470 if (retval != ERROR_OK)
471 return retval;
472
473 retval = swd_multidrop_select(dap);
474 if (retval != ERROR_OK)
475 return retval;
476
477 return swd_queue_dp_read_inner(dap, reg, data);
478 }
479
480 static int swd_queue_dp_write(struct adiv5_dap *dap, unsigned reg,
481 uint32_t data)
482 {
483 const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
484 assert(swd);
485
486 int retval = swd_check_reconnect(dap);
487 if (retval != ERROR_OK)
488 return retval;
489
490 retval = swd_multidrop_select(dap);
491 if (retval != ERROR_OK)
492 return retval;
493
494 return swd_queue_dp_write_inner(dap, reg, data);
495 }
496
497 /** Select the AP register bank matching bits 7:4 of reg. */
498 static int swd_queue_ap_bankselect(struct adiv5_ap *ap, unsigned reg)
499 {
500 int retval;
501 struct adiv5_dap *dap = ap->dap;
502 uint64_t sel;
503
504 if (is_adiv6(dap)) {
505 sel = ap->ap_num | (reg & 0x00000FF0);
506 if (sel == (dap->select & ~0xfULL))
507 return ERROR_OK;
508
509 if (dap->select != DP_SELECT_INVALID)
510 sel |= dap->select & 0xf;
511 dap->select = sel;
512 LOG_DEBUG("AP BANKSEL: %" PRIx64, sel);
513
514 retval = swd_queue_dp_write(dap, DP_SELECT, (uint32_t)sel);
515
516 if (retval == ERROR_OK && dap->asize > 32)
517 retval = swd_queue_dp_write(dap, DP_SELECT1, (uint32_t)(sel >> 32));
518
519 if (retval != ERROR_OK)
520 dap->select = DP_SELECT_INVALID;
521
522 return retval;
523 }
524
525 /* ADIv5 */
526 sel = (ap->ap_num << 24) | (reg & 0x000000F0);
527 if (dap->select != DP_SELECT_INVALID)
528 sel |= dap->select & DP_SELECT_DPBANK;
529
530 if (sel == dap->select)
531 return ERROR_OK;
532
533 dap->select = sel;
534
535 retval = swd_queue_dp_write_inner(dap, DP_SELECT, sel);
536 if (retval != ERROR_OK)
537 dap->select = DP_SELECT_INVALID;
538
539 return retval;
540 }
541
542 static int swd_queue_ap_read(struct adiv5_ap *ap, unsigned reg,
543 uint32_t *data)
544 {
545 struct adiv5_dap *dap = ap->dap;
546 const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
547 assert(swd);
548
549 int retval = swd_check_reconnect(dap);
550 if (retval != ERROR_OK)
551 return retval;
552
553 retval = swd_multidrop_select(dap);
554 if (retval != ERROR_OK)
555 return retval;
556
557 retval = swd_queue_ap_bankselect(ap, reg);
558 if (retval != ERROR_OK)
559 return retval;
560
561 swd->read_reg(swd_cmd(true, true, reg), dap->last_read, ap->memaccess_tck);
562 dap->last_read = data;
563
564 return check_sync(dap);
565 }
566
567 static int swd_queue_ap_write(struct adiv5_ap *ap, unsigned reg,
568 uint32_t data)
569 {
570 struct adiv5_dap *dap = ap->dap;
571 const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
572 assert(swd);
573
574 int retval = swd_check_reconnect(dap);
575 if (retval != ERROR_OK)
576 return retval;
577
578 retval = swd_multidrop_select(dap);
579 if (retval != ERROR_OK)
580 return retval;
581
582 swd_finish_read(dap);
583
584 retval = swd_queue_ap_bankselect(ap, reg);
585 if (retval != ERROR_OK)
586 return retval;
587
588 swd->write_reg(swd_cmd(false, true, reg), data, ap->memaccess_tck);
589
590 return check_sync(dap);
591 }
592
593 /** Executes all queued DAP operations. */
594 static int swd_run(struct adiv5_dap *dap)
595 {
596 int retval = swd_multidrop_select(dap);
597 if (retval != ERROR_OK)
598 return retval;
599
600 swd_finish_read(dap);
601
602 return swd_run_inner(dap);
603 }
604
605 /** Put the SWJ-DP back to JTAG mode */
606 static void swd_quit(struct adiv5_dap *dap)
607 {
608 const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
609 static bool done;
610
611 /* There is no difference if the sequence is sent at the last
612 * or the first swd_quit() call, send it just once */
613 if (done)
614 return;
615
616 done = true;
617 if (dap_is_multidrop(dap)) {
618 swd->switch_seq(SWD_TO_DORMANT);
619 /* Revisit!
620 * Leaving DPs in dormant state was tested and offers some safety
621 * against DPs mismatch in case of unintentional use of non-multidrop SWD.
622 * To put SWJ-DPs to power-on state issue
623 * swd->switch_seq(DORMANT_TO_JTAG);
624 */
625 } else {
626 if (dap->switch_through_dormant) {
627 swd->switch_seq(SWD_TO_DORMANT);
628 swd->switch_seq(DORMANT_TO_JTAG);
629 } else {
630 swd->switch_seq(SWD_TO_JTAG);
631 }
632 }
633
634 /* flush the queue to shift out the sequence before exit */
635 swd->run();
636 }
637
638 const struct dap_ops swd_dap_ops = {
639 .connect = swd_connect,
640 .send_sequence = swd_send_sequence,
641 .queue_dp_read = swd_queue_dp_read,
642 .queue_dp_write = swd_queue_dp_write,
643 .queue_ap_read = swd_queue_ap_read,
644 .queue_ap_write = swd_queue_ap_write,
645 .queue_ap_abort = swd_queue_ap_abort,
646 .run = swd_run,
647 .quit = swd_quit,
648 };
649
650 static const struct command_registration swd_commands[] = {
651 {
652 /*
653 * Set up SWD and JTAG targets identically, unless/until
654 * infrastructure improves ... meanwhile, ignore all
655 * JTAG-specific stuff like IR length for SWD.
656 *
657 * REVISIT can we verify "just one SWD DAP" here/early?
658 */
659 .name = "newdap",
660 .jim_handler = jim_jtag_newtap,
661 .mode = COMMAND_CONFIG,
662 .help = "declare a new SWD DAP"
663 },
664 COMMAND_REGISTRATION_DONE
665 };
666
667 static const struct command_registration swd_handlers[] = {
668 {
669 .name = "swd",
670 .mode = COMMAND_ANY,
671 .help = "SWD command group",
672 .chain = swd_commands,
673 .usage = "",
674 },
675 COMMAND_REGISTRATION_DONE
676 };
677
678 static int swd_select(struct command_context *ctx)
679 {
680 /* FIXME: only place where global 'adapter_driver' is still needed */
681 extern struct adapter_driver *adapter_driver;
682 const struct swd_driver *swd = adapter_driver->swd_ops;
683 int retval;
684
685 retval = register_commands(ctx, NULL, swd_handlers);
686 if (retval != ERROR_OK)
687 return retval;
688
689 /* be sure driver is in SWD mode; start
690 * with hardware default TRN (1), it can be changed later
691 */
692 if (!swd || !swd->read_reg || !swd->write_reg || !swd->init) {
693 LOG_DEBUG("no SWD driver?");
694 return ERROR_FAIL;
695 }
696
697 retval = swd->init();
698 if (retval != ERROR_OK) {
699 LOG_DEBUG("can't init SWD driver");
700 return retval;
701 }
702
703 return retval;
704 }
705
706 static int swd_init(struct command_context *ctx)
707 {
708 /* nothing done here, SWD is initialized
709 * together with the DAP */
710 return ERROR_OK;
711 }
712
713 static struct transport swd_transport = {
714 .name = "swd",
715 .select = swd_select,
716 .init = swd_init,
717 };
718
719 static void swd_constructor(void) __attribute__((constructor));
720 static void swd_constructor(void)
721 {
722 transport_register(&swd_transport);
723 }
724
725 /** Returns true if the current debug session
726 * is using SWD as its transport.
727 */
728 bool transport_is_swd(void)
729 {
730 return get_current_transport() == &swd_transport;
731 }

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)