11d9a8020ce7e953a03b9662e98c7ab7a4b50ae7
[openocd.git] / src / jtag / drivers / bcm2835gpio.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2
3 /***************************************************************************
4 * Copyright (C) 2013 by Paul Fertser, fercerpav@gmail.com *
5 * *
6 * Copyright (C) 2012 by Creative Product Design, marc @ cpdesign.com.au *
7 * Based on at91rm9200.c (c) Anders Larsen *
8 * and RPi GPIO examples by Gert van Loo & Dom *
9 ***************************************************************************/
10
11 #ifdef HAVE_CONFIG_H
12 #include "config.h"
13 #endif
14
15 #include <jtag/interface.h>
16 #include <transport/transport.h>
17 #include "bitbang.h"
18
19 #include <sys/mman.h>
20
21 uint32_t bcm2835_peri_base = 0x20000000;
22 #define BCM2835_GPIO_BASE (bcm2835_peri_base + 0x200000) /* GPIO controller */
23
24 #define BCM2835_PADS_GPIO_0_27 (bcm2835_peri_base + 0x100000)
25 #define BCM2835_PADS_GPIO_0_27_OFFSET (0x2c / 4)
26
27 /* GPIO setup macros */
28 #define MODE_GPIO(g) (*(pio_base+((g)/10))>>(((g)%10)*3) & 7)
29 #define INP_GPIO(g) do { *(pio_base+((g)/10)) &= ~(7<<(((g)%10)*3)); } while (0)
30 #define SET_MODE_GPIO(g, m) do { /* clear the mode bits first, then set as necessary */ \
31 INP_GPIO(g); \
32 *(pio_base+((g)/10)) |= ((m)<<(((g)%10)*3)); } while (0)
33 #define OUT_GPIO(g) SET_MODE_GPIO(g, 1)
34
35 #define GPIO_SET (*(pio_base+7)) /* sets bits which are 1, ignores bits which are 0 */
36 #define GPIO_CLR (*(pio_base+10)) /* clears bits which are 1, ignores bits which are 0 */
37 #define GPIO_LEV (*(pio_base+13)) /* current level of the pin */
38
39 static int dev_mem_fd;
40 static volatile uint32_t *pio_base;
41
42 static bb_value_t bcm2835gpio_read(void);
43 static int bcm2835gpio_write(int tck, int tms, int tdi);
44
45 static int bcm2835_swdio_read(void);
46 static void bcm2835_swdio_drive(bool is_output);
47 static int bcm2835gpio_swd_write(int swclk, int swdio);
48
49 static int bcm2835gpio_init(void);
50 static int bcm2835gpio_quit(void);
51
52 static struct bitbang_interface bcm2835gpio_bitbang = {
53 .read = bcm2835gpio_read,
54 .write = bcm2835gpio_write,
55 .swdio_read = bcm2835_swdio_read,
56 .swdio_drive = bcm2835_swdio_drive,
57 .swd_write = bcm2835gpio_swd_write,
58 .blink = NULL
59 };
60
61 /* GPIO numbers for each signal. Negative values are invalid */
62 static int tck_gpio = -1;
63 static int tck_gpio_mode;
64 static int tms_gpio = -1;
65 static int tms_gpio_mode;
66 static int tdi_gpio = -1;
67 static int tdi_gpio_mode;
68 static int tdo_gpio = -1;
69 static int tdo_gpio_mode;
70 static int trst_gpio = -1;
71 static int trst_gpio_mode;
72 static int srst_gpio = -1;
73 static int srst_gpio_mode;
74 static int swclk_gpio = -1;
75 static int swclk_gpio_mode;
76 static int swdio_gpio = -1;
77 static int swdio_gpio_mode;
78 static int swdio_dir_gpio = -1;
79 static int swdio_dir_gpio_mode;
80
81 /* Transition delay coefficients */
82 static int speed_coeff = 113714;
83 static int speed_offset = 28;
84 static unsigned int jtag_delay;
85
86 static int is_gpio_valid(int gpio)
87 {
88 return gpio >= 0 && gpio <= 31;
89 }
90
91 static bb_value_t bcm2835gpio_read(void)
92 {
93 return (GPIO_LEV & 1<<tdo_gpio) ? BB_HIGH : BB_LOW;
94 }
95
96 static int bcm2835gpio_write(int tck, int tms, int tdi)
97 {
98 uint32_t set = tck<<tck_gpio | tms<<tms_gpio | tdi<<tdi_gpio;
99 uint32_t clear = !tck<<tck_gpio | !tms<<tms_gpio | !tdi<<tdi_gpio;
100
101 GPIO_SET = set;
102 GPIO_CLR = clear;
103
104 for (unsigned int i = 0; i < jtag_delay; i++)
105 asm volatile ("");
106
107 return ERROR_OK;
108 }
109
110 static int bcm2835gpio_swd_write(int swclk, int swdio)
111 {
112 uint32_t set = swclk << swclk_gpio | swdio << swdio_gpio;
113 uint32_t clear = !swclk << swclk_gpio | !swdio << swdio_gpio;
114
115 GPIO_SET = set;
116 GPIO_CLR = clear;
117
118 for (unsigned int i = 0; i < jtag_delay; i++)
119 asm volatile ("");
120
121 return ERROR_OK;
122 }
123
124 /* (1) assert or (0) deassert reset lines */
125 static int bcm2835gpio_reset(int trst, int srst)
126 {
127 uint32_t set = 0;
128 uint32_t clear = 0;
129
130 if (is_gpio_valid(trst_gpio)) {
131 set |= !trst<<trst_gpio;
132 clear |= trst<<trst_gpio;
133 }
134
135 if (is_gpio_valid(srst_gpio)) {
136 set |= !srst<<srst_gpio;
137 clear |= srst<<srst_gpio;
138 }
139
140 GPIO_SET = set;
141 GPIO_CLR = clear;
142
143 return ERROR_OK;
144 }
145
146 static void bcm2835_swdio_drive(bool is_output)
147 {
148 if (is_gpio_valid(swdio_dir_gpio)) {
149 if (is_output) {
150 GPIO_SET = 1 << swdio_dir_gpio;
151 OUT_GPIO(swdio_gpio);
152 } else {
153 INP_GPIO(swdio_gpio);
154 GPIO_CLR = 1 << swdio_dir_gpio;
155 }
156 } else {
157 if (is_output)
158 OUT_GPIO(swdio_gpio);
159 else
160 INP_GPIO(swdio_gpio);
161 }
162 }
163
164 static int bcm2835_swdio_read(void)
165 {
166 return !!(GPIO_LEV & 1 << swdio_gpio);
167 }
168
169 static int bcm2835gpio_khz(int khz, int *jtag_speed)
170 {
171 if (!khz) {
172 LOG_DEBUG("RCLK not supported");
173 return ERROR_FAIL;
174 }
175 *jtag_speed = speed_coeff/khz - speed_offset;
176 if (*jtag_speed < 0)
177 *jtag_speed = 0;
178 return ERROR_OK;
179 }
180
181 static int bcm2835gpio_speed_div(int speed, int *khz)
182 {
183 *khz = speed_coeff/(speed + speed_offset);
184 return ERROR_OK;
185 }
186
187 static int bcm2835gpio_speed(int speed)
188 {
189 jtag_delay = speed;
190 return ERROR_OK;
191 }
192
193 COMMAND_HANDLER(bcm2835gpio_handle_jtag_gpionums)
194 {
195 if (CMD_ARGC == 4) {
196 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tck_gpio);
197 COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], tms_gpio);
198 COMMAND_PARSE_NUMBER(int, CMD_ARGV[2], tdi_gpio);
199 COMMAND_PARSE_NUMBER(int, CMD_ARGV[3], tdo_gpio);
200 } else if (CMD_ARGC != 0) {
201 return ERROR_COMMAND_SYNTAX_ERROR;
202 }
203
204 command_print(CMD,
205 "BCM2835 GPIO config: tck = %d, tms = %d, tdi = %d, tdo = %d",
206 tck_gpio, tms_gpio, tdi_gpio, tdo_gpio);
207
208 return ERROR_OK;
209 }
210
211 COMMAND_HANDLER(bcm2835gpio_handle_jtag_gpionum_tck)
212 {
213 if (CMD_ARGC == 1)
214 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tck_gpio);
215
216 command_print(CMD, "BCM2835 GPIO config: tck = %d", tck_gpio);
217 return ERROR_OK;
218 }
219
220 COMMAND_HANDLER(bcm2835gpio_handle_jtag_gpionum_tms)
221 {
222 if (CMD_ARGC == 1)
223 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tms_gpio);
224
225 command_print(CMD, "BCM2835 GPIO config: tms = %d", tms_gpio);
226 return ERROR_OK;
227 }
228
229 COMMAND_HANDLER(bcm2835gpio_handle_jtag_gpionum_tdo)
230 {
231 if (CMD_ARGC == 1)
232 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tdo_gpio);
233
234 command_print(CMD, "BCM2835 GPIO config: tdo = %d", tdo_gpio);
235 return ERROR_OK;
236 }
237
238 COMMAND_HANDLER(bcm2835gpio_handle_jtag_gpionum_tdi)
239 {
240 if (CMD_ARGC == 1)
241 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tdi_gpio);
242
243 command_print(CMD, "BCM2835 GPIO config: tdi = %d", tdi_gpio);
244 return ERROR_OK;
245 }
246
247 COMMAND_HANDLER(bcm2835gpio_handle_jtag_gpionum_srst)
248 {
249 if (CMD_ARGC == 1)
250 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], srst_gpio);
251
252 command_print(CMD, "BCM2835 GPIO config: srst = %d", srst_gpio);
253 return ERROR_OK;
254 }
255
256 COMMAND_HANDLER(bcm2835gpio_handle_jtag_gpionum_trst)
257 {
258 if (CMD_ARGC == 1)
259 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], trst_gpio);
260
261 command_print(CMD, "BCM2835 GPIO config: trst = %d", trst_gpio);
262 return ERROR_OK;
263 }
264
265 COMMAND_HANDLER(bcm2835gpio_handle_swd_gpionums)
266 {
267 if (CMD_ARGC == 2) {
268 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swclk_gpio);
269 COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], swdio_gpio);
270 } else if (CMD_ARGC != 0) {
271 return ERROR_COMMAND_SYNTAX_ERROR;
272 }
273
274 command_print(CMD,
275 "BCM2835 GPIO nums: swclk = %d, swdio = %d",
276 swclk_gpio, swdio_gpio);
277
278 return ERROR_OK;
279 }
280
281 COMMAND_HANDLER(bcm2835gpio_handle_swd_gpionum_swclk)
282 {
283 if (CMD_ARGC == 1)
284 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swclk_gpio);
285
286 command_print(CMD, "BCM2835 num: swclk = %d", swclk_gpio);
287 return ERROR_OK;
288 }
289
290 COMMAND_HANDLER(bcm2835gpio_handle_swd_gpionum_swdio)
291 {
292 if (CMD_ARGC == 1)
293 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swdio_gpio);
294
295 command_print(CMD, "BCM2835 num: swdio = %d", swdio_gpio);
296 return ERROR_OK;
297 }
298
299 COMMAND_HANDLER(bcm2835gpio_handle_swd_dir_gpionum_swdio)
300 {
301 if (CMD_ARGC == 1)
302 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swdio_dir_gpio);
303
304 command_print(CMD, "BCM2835 num: swdio_dir = %d", swdio_dir_gpio);
305 return ERROR_OK;
306 }
307
308 COMMAND_HANDLER(bcm2835gpio_handle_speed_coeffs)
309 {
310 if (CMD_ARGC == 2) {
311 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], speed_coeff);
312 COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], speed_offset);
313 }
314
315 command_print(CMD, "BCM2835 GPIO: speed_coeffs = %d, speed_offset = %d",
316 speed_coeff, speed_offset);
317 return ERROR_OK;
318 }
319
320 COMMAND_HANDLER(bcm2835gpio_handle_peripheral_base)
321 {
322 if (CMD_ARGC == 1)
323 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], bcm2835_peri_base);
324
325 command_print(CMD, "BCM2835 GPIO: peripheral_base = 0x%08x",
326 bcm2835_peri_base);
327 return ERROR_OK;
328 }
329
330 static const struct command_registration bcm2835gpio_subcommand_handlers[] = {
331 {
332 .name = "jtag_nums",
333 .handler = &bcm2835gpio_handle_jtag_gpionums,
334 .mode = COMMAND_CONFIG,
335 .help = "gpio numbers for tck, tms, tdi, tdo. (in that order)",
336 .usage = "[tck tms tdi tdo]",
337 },
338 {
339 .name = "tck_num",
340 .handler = &bcm2835gpio_handle_jtag_gpionum_tck,
341 .mode = COMMAND_CONFIG,
342 .help = "gpio number for tck.",
343 .usage = "[tck]",
344 },
345 {
346 .name = "tms_num",
347 .handler = &bcm2835gpio_handle_jtag_gpionum_tms,
348 .mode = COMMAND_CONFIG,
349 .help = "gpio number for tms.",
350 .usage = "[tms]",
351 },
352 {
353 .name = "tdo_num",
354 .handler = &bcm2835gpio_handle_jtag_gpionum_tdo,
355 .mode = COMMAND_CONFIG,
356 .help = "gpio number for tdo.",
357 .usage = "[tdo]",
358 },
359 {
360 .name = "tdi_num",
361 .handler = &bcm2835gpio_handle_jtag_gpionum_tdi,
362 .mode = COMMAND_CONFIG,
363 .help = "gpio number for tdi.",
364 .usage = "[tdi]",
365 },
366 {
367 .name = "swd_nums",
368 .handler = &bcm2835gpio_handle_swd_gpionums,
369 .mode = COMMAND_CONFIG,
370 .help = "gpio numbers for swclk, swdio. (in that order)",
371 .usage = "[swclk swdio]",
372 },
373 {
374 .name = "swclk_num",
375 .handler = &bcm2835gpio_handle_swd_gpionum_swclk,
376 .mode = COMMAND_CONFIG,
377 .help = "gpio number for swclk.",
378 .usage = "[swclk]",
379 },
380 {
381 .name = "swdio_num",
382 .handler = &bcm2835gpio_handle_swd_gpionum_swdio,
383 .mode = COMMAND_CONFIG,
384 .help = "gpio number for swdio.",
385 .usage = "[swdio]",
386 },
387 {
388 .name = "swdio_dir_num",
389 .handler = &bcm2835gpio_handle_swd_dir_gpionum_swdio,
390 .mode = COMMAND_CONFIG,
391 .help = "gpio number for swdio direction control pin (set=output mode, clear=input mode)",
392 .usage = "[swdio_dir]",
393 },
394 {
395 .name = "srst_num",
396 .handler = &bcm2835gpio_handle_jtag_gpionum_srst,
397 .mode = COMMAND_CONFIG,
398 .help = "gpio number for srst.",
399 .usage = "[srst]",
400 },
401 {
402 .name = "trst_num",
403 .handler = &bcm2835gpio_handle_jtag_gpionum_trst,
404 .mode = COMMAND_CONFIG,
405 .help = "gpio number for trst.",
406 .usage = "[trst]",
407 },
408 {
409 .name = "speed_coeffs",
410 .handler = &bcm2835gpio_handle_speed_coeffs,
411 .mode = COMMAND_CONFIG,
412 .help = "SPEED_COEFF and SPEED_OFFSET for delay calculations.",
413 .usage = "[SPEED_COEFF SPEED_OFFSET]",
414 },
415 {
416 .name = "peripheral_base",
417 .handler = &bcm2835gpio_handle_peripheral_base,
418 .mode = COMMAND_CONFIG,
419 .help = "peripheral base to access GPIOs (RPi1 0x20000000, RPi2 0x3F000000).",
420 .usage = "[base]",
421 },
422
423 COMMAND_REGISTRATION_DONE
424 };
425
426 static const struct command_registration bcm2835gpio_command_handlers[] = {
427 {
428 .name = "bcm2835gpio",
429 .mode = COMMAND_ANY,
430 .help = "perform bcm2835gpio management",
431 .chain = bcm2835gpio_subcommand_handlers,
432 .usage = "",
433 },
434 COMMAND_REGISTRATION_DONE
435 };
436
437 static const char * const bcm2835_transports[] = { "jtag", "swd", NULL };
438
439 static struct jtag_interface bcm2835gpio_interface = {
440 .supported = DEBUG_CAP_TMS_SEQ,
441 .execute_queue = bitbang_execute_queue,
442 };
443
444 struct adapter_driver bcm2835gpio_adapter_driver = {
445 .name = "bcm2835gpio",
446 .transports = bcm2835_transports,
447 .commands = bcm2835gpio_command_handlers,
448
449 .init = bcm2835gpio_init,
450 .quit = bcm2835gpio_quit,
451 .reset = bcm2835gpio_reset,
452 .speed = bcm2835gpio_speed,
453 .khz = bcm2835gpio_khz,
454 .speed_div = bcm2835gpio_speed_div,
455
456 .jtag_ops = &bcm2835gpio_interface,
457 .swd_ops = &bitbang_swd,
458 };
459
460 static bool bcm2835gpio_jtag_mode_possible(void)
461 {
462 if (!is_gpio_valid(tck_gpio))
463 return 0;
464 if (!is_gpio_valid(tms_gpio))
465 return 0;
466 if (!is_gpio_valid(tdi_gpio))
467 return 0;
468 if (!is_gpio_valid(tdo_gpio))
469 return 0;
470 return 1;
471 }
472
473 static bool bcm2835gpio_swd_mode_possible(void)
474 {
475 if (!is_gpio_valid(swclk_gpio))
476 return 0;
477 if (!is_gpio_valid(swdio_gpio))
478 return 0;
479 return 1;
480 }
481
482 static int bcm2835gpio_init(void)
483 {
484 bitbang_interface = &bcm2835gpio_bitbang;
485
486 LOG_INFO("BCM2835 GPIO JTAG/SWD bitbang driver");
487
488 if (transport_is_jtag() && !bcm2835gpio_jtag_mode_possible()) {
489 LOG_ERROR("Require tck, tms, tdi and tdo gpios for JTAG mode");
490 return ERROR_JTAG_INIT_FAILED;
491 }
492
493 if (transport_is_swd() && !bcm2835gpio_swd_mode_possible()) {
494 LOG_ERROR("Require swclk and swdio gpio for SWD mode");
495 return ERROR_JTAG_INIT_FAILED;
496 }
497
498 dev_mem_fd = open("/dev/gpiomem", O_RDWR | O_SYNC);
499 if (dev_mem_fd < 0) {
500 LOG_DEBUG("Cannot open /dev/gpiomem, fallback to /dev/mem");
501 dev_mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
502 }
503 if (dev_mem_fd < 0) {
504 LOG_ERROR("open: %s", strerror(errno));
505 return ERROR_JTAG_INIT_FAILED;
506 }
507
508 pio_base = mmap(NULL, sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_WRITE,
509 MAP_SHARED, dev_mem_fd, BCM2835_GPIO_BASE);
510
511 if (pio_base == MAP_FAILED) {
512 LOG_ERROR("mmap: %s", strerror(errno));
513 close(dev_mem_fd);
514 return ERROR_JTAG_INIT_FAILED;
515 }
516
517 static volatile uint32_t *pads_base;
518 pads_base = mmap(NULL, sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_WRITE,
519 MAP_SHARED, dev_mem_fd, BCM2835_PADS_GPIO_0_27);
520
521 if (pads_base == MAP_FAILED) {
522 LOG_ERROR("mmap: %s", strerror(errno));
523 close(dev_mem_fd);
524 return ERROR_JTAG_INIT_FAILED;
525 }
526
527 /* set 4mA drive strength, slew rate limited, hysteresis on */
528 pads_base[BCM2835_PADS_GPIO_0_27_OFFSET] = 0x5a000008 + 1;
529
530 /*
531 * Configure TDO as an input, and TDI, TCK, TMS, TRST, SRST
532 * as outputs. Drive TDI and TCK low, and TMS/TRST/SRST high.
533 */
534 if (transport_is_jtag()) {
535 tdo_gpio_mode = MODE_GPIO(tdo_gpio);
536 tdi_gpio_mode = MODE_GPIO(tdi_gpio);
537 tck_gpio_mode = MODE_GPIO(tck_gpio);
538 tms_gpio_mode = MODE_GPIO(tms_gpio);
539
540 INP_GPIO(tdo_gpio);
541
542 GPIO_CLR = 1<<tdi_gpio | 1<<tck_gpio;
543 GPIO_SET = 1<<tms_gpio;
544
545 OUT_GPIO(tdi_gpio);
546 OUT_GPIO(tck_gpio);
547 OUT_GPIO(tms_gpio);
548
549 if (is_gpio_valid(trst_gpio)) {
550 trst_gpio_mode = MODE_GPIO(trst_gpio);
551 GPIO_SET = 1 << trst_gpio;
552 OUT_GPIO(trst_gpio);
553 }
554 }
555
556 if (transport_is_swd()) {
557 /* Make buffer an output before the GPIO connected to it */
558 if (is_gpio_valid(swdio_dir_gpio)) {
559 swdio_dir_gpio_mode = MODE_GPIO(swdio_dir_gpio);
560 GPIO_SET = 1 << swdio_dir_gpio;
561 OUT_GPIO(swdio_dir_gpio);
562 }
563
564 swclk_gpio_mode = MODE_GPIO(swclk_gpio);
565 swdio_gpio_mode = MODE_GPIO(swdio_gpio);
566
567 GPIO_CLR = 1<<swdio_gpio | 1<<swclk_gpio;
568
569 OUT_GPIO(swclk_gpio);
570 OUT_GPIO(swdio_gpio);
571 }
572
573 if (is_gpio_valid(srst_gpio)) {
574 srst_gpio_mode = MODE_GPIO(srst_gpio);
575 GPIO_SET = 1 << srst_gpio;
576 OUT_GPIO(srst_gpio);
577 }
578
579 LOG_DEBUG("saved pinmux settings: tck %d tms %d tdi %d "
580 "tdo %d trst %d srst %d", tck_gpio_mode, tms_gpio_mode,
581 tdi_gpio_mode, tdo_gpio_mode, trst_gpio_mode, srst_gpio_mode);
582
583 return ERROR_OK;
584 }
585
586 static int bcm2835gpio_quit(void)
587 {
588 if (transport_is_jtag()) {
589 SET_MODE_GPIO(tdo_gpio, tdo_gpio_mode);
590 SET_MODE_GPIO(tdi_gpio, tdi_gpio_mode);
591 SET_MODE_GPIO(tck_gpio, tck_gpio_mode);
592 SET_MODE_GPIO(tms_gpio, tms_gpio_mode);
593 if (is_gpio_valid(trst_gpio))
594 SET_MODE_GPIO(trst_gpio, trst_gpio_mode);
595 }
596
597 if (transport_is_swd()) {
598 SET_MODE_GPIO(swclk_gpio, swclk_gpio_mode);
599 SET_MODE_GPIO(swdio_gpio, swdio_gpio_mode);
600 }
601
602 if (is_gpio_valid(srst_gpio))
603 SET_MODE_GPIO(srst_gpio, srst_gpio_mode);
604
605 if (is_gpio_valid(swdio_dir_gpio))
606 SET_MODE_GPIO(swdio_dir_gpio, swdio_dir_gpio_mode);
607
608 return ERROR_OK;
609 }

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)