jtag: linuxgpiod: drop extra parenthesis
[openocd.git] / src / flash / nor / str9x.c
1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
4 * *
5 * Copyright (C) 2008 by Spencer Oliver *
6 * spen@spen-soft.co.uk *
7 *
8 * Copyright (C) 2008 by Oyvind Harboe *
9 * oyvind.harboe@zylin.com *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * GNU General Public License for more details. *
20 * *
21 * You should have received a copy of the GNU General Public License *
22 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
23 ***************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include "imp.h"
30 #include <target/arm966e.h>
31 #include <target/algorithm.h>
32
33 /* Flash registers */
34
35 #define FLASH_BBSR 0x54000000 /* Boot Bank Size Register */
36 #define FLASH_NBBSR 0x54000004 /* Non-Boot Bank Size Register */
37 #define FLASH_BBADR 0x5400000C /* Boot Bank Base Address Register */
38 #define FLASH_NBBADR 0x54000010 /* Non-Boot Bank Base Address Register */
39 #define FLASH_CR 0x54000018 /* Control Register */
40 #define FLASH_SR 0x5400001C /* Status Register */
41 #define FLASH_BCE5ADDR 0x54000020 /* BC Fifth Entry Target Address Register */
42
43 struct str9x_flash_bank {
44 uint32_t *sector_bits;
45 int variant;
46 int bank1;
47 };
48
49 enum str9x_status_codes {
50 STR9X_CMD_SUCCESS = 0,
51 STR9X_INVALID_COMMAND = 1,
52 STR9X_SRC_ADDR_ERROR = 2,
53 STR9X_DST_ADDR_ERROR = 3,
54 STR9X_SRC_ADDR_NOT_MAPPED = 4,
55 STR9X_DST_ADDR_NOT_MAPPED = 5,
56 STR9X_COUNT_ERROR = 6,
57 STR9X_INVALID_SECTOR = 7,
58 STR9X_SECTOR_NOT_BLANK = 8,
59 STR9X_SECTOR_NOT_PREPARED = 9,
60 STR9X_COMPARE_ERROR = 10,
61 STR9X_BUSY = 11
62 };
63
64 static uint32_t bank1start = 0x00080000;
65
66 static int str9x_build_block_list(struct flash_bank *bank)
67 {
68 struct str9x_flash_bank *str9x_info = bank->driver_priv;
69
70 int i;
71 unsigned int num_sectors;
72 int b0_sectors = 0, b1_sectors = 0;
73 uint32_t offset = 0;
74
75 /* set if we have large flash str9 */
76 str9x_info->variant = 0;
77 str9x_info->bank1 = 0;
78
79 switch (bank->size) {
80 case (256 * 1024):
81 b0_sectors = 4;
82 break;
83 case (512 * 1024):
84 b0_sectors = 8;
85 break;
86 case (1024 * 1024):
87 bank1start = 0x00100000;
88 str9x_info->variant = 1;
89 b0_sectors = 16;
90 break;
91 case (2048 * 1024):
92 bank1start = 0x00200000;
93 str9x_info->variant = 1;
94 b0_sectors = 32;
95 break;
96 case (128 * 1024):
97 str9x_info->variant = 1;
98 str9x_info->bank1 = 1;
99 b1_sectors = 8;
100 bank1start = bank->base;
101 break;
102 case (32 * 1024):
103 str9x_info->bank1 = 1;
104 b1_sectors = 4;
105 bank1start = bank->base;
106 break;
107 default:
108 LOG_ERROR("BUG: unknown bank->size encountered");
109 exit(-1);
110 }
111
112 num_sectors = b0_sectors + b1_sectors;
113
114 bank->num_sectors = num_sectors;
115 bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors);
116 str9x_info->sector_bits = malloc(sizeof(uint32_t) * num_sectors);
117
118 num_sectors = 0;
119
120 for (i = 0; i < b0_sectors; i++) {
121 bank->sectors[num_sectors].offset = offset;
122 bank->sectors[num_sectors].size = 0x10000;
123 offset += bank->sectors[i].size;
124 bank->sectors[num_sectors].is_erased = -1;
125 bank->sectors[num_sectors].is_protected = 1;
126 str9x_info->sector_bits[num_sectors++] = (1 << i);
127 }
128
129 for (i = 0; i < b1_sectors; i++) {
130 bank->sectors[num_sectors].offset = offset;
131 bank->sectors[num_sectors].size = str9x_info->variant == 0 ? 0x2000 : 0x4000;
132 offset += bank->sectors[i].size;
133 bank->sectors[num_sectors].is_erased = -1;
134 bank->sectors[num_sectors].is_protected = 1;
135 if (str9x_info->variant)
136 str9x_info->sector_bits[num_sectors++] = (1 << i);
137 else
138 str9x_info->sector_bits[num_sectors++] = (1 << (i + 8));
139 }
140
141 return ERROR_OK;
142 }
143
144 /* flash bank str9x <base> <size> 0 0 <target#>
145 */
146 FLASH_BANK_COMMAND_HANDLER(str9x_flash_bank_command)
147 {
148 struct str9x_flash_bank *str9x_info;
149
150 if (CMD_ARGC < 6)
151 return ERROR_COMMAND_SYNTAX_ERROR;
152
153 str9x_info = malloc(sizeof(struct str9x_flash_bank));
154 bank->driver_priv = str9x_info;
155
156 str9x_build_block_list(bank);
157
158 return ERROR_OK;
159 }
160
161 static int str9x_protect_check(struct flash_bank *bank)
162 {
163 int retval;
164 struct str9x_flash_bank *str9x_info = bank->driver_priv;
165 struct target *target = bank->target;
166
167 uint32_t adr;
168 uint32_t status = 0;
169 uint16_t hstatus = 0;
170
171 if (bank->target->state != TARGET_HALTED) {
172 LOG_ERROR("Target not halted");
173 return ERROR_TARGET_NOT_HALTED;
174 }
175
176 /* read level one protection */
177
178 if (str9x_info->variant) {
179 if (str9x_info->bank1) {
180 adr = bank1start + 0x18;
181 retval = target_write_u16(target, adr, 0x90);
182 if (retval != ERROR_OK)
183 return retval;
184 retval = target_read_u16(target, adr, &hstatus);
185 if (retval != ERROR_OK)
186 return retval;
187 status = hstatus;
188 } else {
189 adr = bank1start + 0x14;
190 retval = target_write_u16(target, adr, 0x90);
191 if (retval != ERROR_OK)
192 return retval;
193 retval = target_read_u32(target, adr, &status);
194 if (retval != ERROR_OK)
195 return retval;
196 }
197 } else {
198 adr = bank1start + 0x10;
199 retval = target_write_u16(target, adr, 0x90);
200 if (retval != ERROR_OK)
201 return retval;
202 retval = target_read_u16(target, adr, &hstatus);
203 if (retval != ERROR_OK)
204 return retval;
205 status = hstatus;
206 }
207
208 /* read array command */
209 retval = target_write_u16(target, adr, 0xFF);
210 if (retval != ERROR_OK)
211 return retval;
212
213 for (unsigned int i = 0; i < bank->num_sectors; i++) {
214 if (status & str9x_info->sector_bits[i])
215 bank->sectors[i].is_protected = 1;
216 else
217 bank->sectors[i].is_protected = 0;
218 }
219
220 return ERROR_OK;
221 }
222
223 static int str9x_erase(struct flash_bank *bank, unsigned int first,
224 unsigned int last)
225 {
226 struct target *target = bank->target;
227 uint32_t adr;
228 uint8_t status;
229 uint8_t erase_cmd;
230 int total_timeout;
231
232 if (bank->target->state != TARGET_HALTED) {
233 LOG_ERROR("Target not halted");
234 return ERROR_TARGET_NOT_HALTED;
235 }
236
237 /* Check if we can erase whole bank */
238 if ((first == 0) && (last == (bank->num_sectors - 1))) {
239 /* Optimize to run erase bank command instead of sector */
240 erase_cmd = 0x80;
241 /* Add timeout duration since erase bank takes more time */
242 total_timeout = 1000 * bank->num_sectors;
243 } else {
244 /* Erase sector command */
245 erase_cmd = 0x20;
246 total_timeout = 1000;
247 }
248
249 /* this is so the compiler can *know* */
250 assert(total_timeout > 0);
251
252 for (unsigned int i = first; i <= last; i++) {
253 int retval;
254 adr = bank->base + bank->sectors[i].offset;
255
256 /* erase sectors or block */
257 retval = target_write_u16(target, adr, erase_cmd);
258 if (retval != ERROR_OK)
259 return retval;
260 retval = target_write_u16(target, adr, 0xD0);
261 if (retval != ERROR_OK)
262 return retval;
263
264 /* get status */
265 retval = target_write_u16(target, adr, 0x70);
266 if (retval != ERROR_OK)
267 return retval;
268
269 int timeout;
270 for (timeout = 0; timeout < total_timeout; timeout++) {
271 retval = target_read_u8(target, adr, &status);
272 if (retval != ERROR_OK)
273 return retval;
274 if (status & 0x80)
275 break;
276 alive_sleep(1);
277 }
278 if (timeout == total_timeout) {
279 LOG_ERROR("erase timed out");
280 return ERROR_FAIL;
281 }
282
283 /* clear status, also clear read array */
284 retval = target_write_u16(target, adr, 0x50);
285 if (retval != ERROR_OK)
286 return retval;
287
288 /* read array command */
289 retval = target_write_u16(target, adr, 0xFF);
290 if (retval != ERROR_OK)
291 return retval;
292
293 if (status & 0x22) {
294 LOG_ERROR("error erasing flash bank, status: 0x%x", status);
295 return ERROR_FLASH_OPERATION_FAILED;
296 }
297
298 /* If we ran erase bank command, we are finished */
299 if (erase_cmd == 0x80)
300 break;
301 }
302
303 return ERROR_OK;
304 }
305
306 static int str9x_protect(struct flash_bank *bank, int set, unsigned int first,
307 unsigned int last)
308 {
309 struct target *target = bank->target;
310 uint32_t adr;
311 uint8_t status;
312
313 if (bank->target->state != TARGET_HALTED) {
314 LOG_ERROR("Target not halted");
315 return ERROR_TARGET_NOT_HALTED;
316 }
317
318 for (unsigned int i = first; i <= last; i++) {
319 /* Level One Protection */
320
321 adr = bank->base + bank->sectors[i].offset;
322
323 target_write_u16(target, adr, 0x60);
324 if (set)
325 target_write_u16(target, adr, 0x01);
326 else
327 target_write_u16(target, adr, 0xD0);
328
329 /* query status */
330 target_read_u8(target, adr, &status);
331
332 /* clear status, also clear read array */
333 target_write_u16(target, adr, 0x50);
334
335 /* read array command */
336 target_write_u16(target, adr, 0xFF);
337 }
338
339 return ERROR_OK;
340 }
341
342 static int str9x_write_block(struct flash_bank *bank,
343 const uint8_t *buffer, uint32_t offset, uint32_t count)
344 {
345 struct target *target = bank->target;
346 uint32_t buffer_size = 32768;
347 struct working_area *write_algorithm;
348 struct working_area *source;
349 uint32_t address = bank->base + offset;
350 struct reg_param reg_params[4];
351 struct arm_algorithm arm_algo;
352 int retval = ERROR_OK;
353
354 /* see contrib/loaders/flash/str9x.s for src */
355
356 static const uint32_t str9x_flash_write_code[] = {
357 /* write: */
358 0xe3c14003, /* bic r4, r1, #3 */
359 0xe3a03040, /* mov r3, #0x40 */
360 0xe1c430b0, /* strh r3, [r4, #0] */
361 0xe0d030b2, /* ldrh r3, [r0], #2 */
362 0xe0c130b2, /* strh r3, [r1], #2 */
363 0xe3a03070, /* mov r3, #0x70 */
364 0xe1c430b0, /* strh r3, [r4, #0] */
365 /* busy: */
366 0xe5d43000, /* ldrb r3, [r4, #0] */
367 0xe3130080, /* tst r3, #0x80 */
368 0x0afffffc, /* beq busy */
369 0xe3a05050, /* mov r5, #0x50 */
370 0xe1c450b0, /* strh r5, [r4, #0] */
371 0xe3a050ff, /* mov r5, #0xFF */
372 0xe1c450b0, /* strh r5, [r4, #0] */
373 0xe3130012, /* tst r3, #0x12 */
374 0x1a000001, /* bne exit */
375 0xe2522001, /* subs r2, r2, #1 */
376 0x1affffed, /* bne write */
377 /* exit: */
378 0xe1200070, /* bkpt #0 */
379 };
380
381 /* flash write code */
382 if (target_alloc_working_area(target, sizeof(str9x_flash_write_code),
383 &write_algorithm) != ERROR_OK) {
384 LOG_WARNING("no working area available, can't do block memory writes");
385 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
386 }
387
388 uint8_t code[sizeof(str9x_flash_write_code)];
389 target_buffer_set_u32_array(target, code, ARRAY_SIZE(str9x_flash_write_code),
390 str9x_flash_write_code);
391 target_write_buffer(target, write_algorithm->address, sizeof(code), code);
392
393 /* memory buffer */
394 while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
395 buffer_size /= 2;
396 if (buffer_size <= 256) {
397 /* we already allocated the writing code, but failed to get a
398 * buffer, free the algorithm */
399 target_free_working_area(target, write_algorithm);
400
401 LOG_WARNING("no large enough working area available, can't do block memory writes");
402 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
403 }
404 }
405
406 arm_algo.common_magic = ARM_COMMON_MAGIC;
407 arm_algo.core_mode = ARM_MODE_SVC;
408 arm_algo.core_state = ARM_STATE_ARM;
409
410 init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
411 init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
412 init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
413 init_reg_param(&reg_params[3], "r3", 32, PARAM_IN);
414
415 while (count > 0) {
416 uint32_t thisrun_count = (count > (buffer_size / 2)) ? (buffer_size / 2) : count;
417
418 target_write_buffer(target, source->address, thisrun_count * 2, buffer);
419
420 buf_set_u32(reg_params[0].value, 0, 32, source->address);
421 buf_set_u32(reg_params[1].value, 0, 32, address);
422 buf_set_u32(reg_params[2].value, 0, 32, thisrun_count);
423
424 retval = target_run_algorithm(target, 0, NULL, 4, reg_params,
425 write_algorithm->address,
426 0, 10000, &arm_algo);
427 if (retval != ERROR_OK) {
428 LOG_ERROR("error executing str9x flash write algorithm");
429 retval = ERROR_FLASH_OPERATION_FAILED;
430 break;
431 }
432
433 if (buf_get_u32(reg_params[3].value, 0, 32) != 0x80) {
434 retval = ERROR_FLASH_OPERATION_FAILED;
435 break;
436 }
437
438 buffer += thisrun_count * 2;
439 address += thisrun_count * 2;
440 count -= thisrun_count;
441 }
442
443 target_free_working_area(target, source);
444 target_free_working_area(target, write_algorithm);
445
446 destroy_reg_param(&reg_params[0]);
447 destroy_reg_param(&reg_params[1]);
448 destroy_reg_param(&reg_params[2]);
449 destroy_reg_param(&reg_params[3]);
450
451 return retval;
452 }
453
454 static int str9x_write(struct flash_bank *bank,
455 const uint8_t *buffer, uint32_t offset, uint32_t count)
456 {
457 struct target *target = bank->target;
458 uint32_t words_remaining = (count / 2);
459 uint32_t bytes_remaining = (count & 0x00000001);
460 uint32_t address = bank->base + offset;
461 uint32_t bytes_written = 0;
462 uint8_t status;
463 int retval;
464 uint32_t check_address = offset;
465 uint32_t bank_adr;
466
467 if (bank->target->state != TARGET_HALTED) {
468 LOG_ERROR("Target not halted");
469 return ERROR_TARGET_NOT_HALTED;
470 }
471
472 if (offset & 0x1) {
473 LOG_WARNING("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset);
474 return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
475 }
476
477 for (unsigned int i = 0; i < bank->num_sectors; i++) {
478 uint32_t sec_start = bank->sectors[i].offset;
479 uint32_t sec_end = sec_start + bank->sectors[i].size;
480
481 /* check if destination falls within the current sector */
482 if ((check_address >= sec_start) && (check_address < sec_end)) {
483 /* check if destination ends in the current sector */
484 if (offset + count < sec_end)
485 check_address = offset + count;
486 else
487 check_address = sec_end;
488 }
489 }
490
491 if (check_address != offset + count)
492 return ERROR_FLASH_DST_OUT_OF_BANK;
493
494 /* multiple half words (2-byte) to be programmed? */
495 if (words_remaining > 0) {
496 /* try using a block write */
497 retval = str9x_write_block(bank, buffer, offset, words_remaining);
498 if (retval != ERROR_OK) {
499 if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
500 /* if block write failed (no sufficient working area),
501 * we use normal (slow) single dword accesses */
502 LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
503 } else if (retval == ERROR_FLASH_OPERATION_FAILED) {
504 LOG_ERROR("flash writing failed");
505 return ERROR_FLASH_OPERATION_FAILED;
506 }
507 } else {
508 buffer += words_remaining * 2;
509 address += words_remaining * 2;
510 words_remaining = 0;
511 }
512 }
513
514 while (words_remaining > 0) {
515 bank_adr = address & ~0x03;
516
517 /* write data command */
518 target_write_u16(target, bank_adr, 0x40);
519 target_write_memory(target, address, 2, 1, buffer + bytes_written);
520
521 /* get status command */
522 target_write_u16(target, bank_adr, 0x70);
523
524 int timeout;
525 for (timeout = 0; timeout < 1000; timeout++) {
526 target_read_u8(target, bank_adr, &status);
527 if (status & 0x80)
528 break;
529 alive_sleep(1);
530 }
531 if (timeout == 1000) {
532 LOG_ERROR("write timed out");
533 return ERROR_FAIL;
534 }
535
536 /* clear status reg and read array */
537 target_write_u16(target, bank_adr, 0x50);
538 target_write_u16(target, bank_adr, 0xFF);
539
540 if (status & 0x10)
541 return ERROR_FLASH_OPERATION_FAILED;
542 else if (status & 0x02)
543 return ERROR_FLASH_OPERATION_FAILED;
544
545 bytes_written += 2;
546 words_remaining--;
547 address += 2;
548 }
549
550 if (bytes_remaining) {
551 uint8_t last_halfword[2] = {0xff, 0xff};
552
553 /* copy the last remaining bytes into the write buffer */
554 memcpy(last_halfword, buffer+bytes_written, bytes_remaining);
555
556 bank_adr = address & ~0x03;
557
558 /* write data command */
559 target_write_u16(target, bank_adr, 0x40);
560 target_write_memory(target, address, 2, 1, last_halfword);
561
562 /* query status command */
563 target_write_u16(target, bank_adr, 0x70);
564
565 int timeout;
566 for (timeout = 0; timeout < 1000; timeout++) {
567 target_read_u8(target, bank_adr, &status);
568 if (status & 0x80)
569 break;
570 alive_sleep(1);
571 }
572 if (timeout == 1000) {
573 LOG_ERROR("write timed out");
574 return ERROR_FAIL;
575 }
576
577 /* clear status reg and read array */
578 target_write_u16(target, bank_adr, 0x50);
579 target_write_u16(target, bank_adr, 0xFF);
580
581 if (status & 0x10)
582 return ERROR_FLASH_OPERATION_FAILED;
583 else if (status & 0x02)
584 return ERROR_FLASH_OPERATION_FAILED;
585 }
586
587 return ERROR_OK;
588 }
589
590 static int str9x_probe(struct flash_bank *bank)
591 {
592 return ERROR_OK;
593 }
594
595 #if 0
596 COMMAND_HANDLER(str9x_handle_part_id_command)
597 {
598 return ERROR_OK;
599 }
600 #endif
601
602 COMMAND_HANDLER(str9x_handle_flash_config_command)
603 {
604 struct target *target = NULL;
605
606 if (CMD_ARGC < 5)
607 return ERROR_COMMAND_SYNTAX_ERROR;
608
609 struct flash_bank *bank;
610 int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
611 if (retval != ERROR_OK)
612 return retval;
613
614 uint32_t bbsr, nbbsr, bbadr, nbbadr;
615 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], bbsr);
616 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], nbbsr);
617 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], bbadr);
618 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[4], nbbadr);
619
620 target = bank->target;
621
622 if (bank->target->state != TARGET_HALTED) {
623 LOG_ERROR("Target not halted");
624 return ERROR_TARGET_NOT_HALTED;
625 }
626
627 /* config flash controller */
628 target_write_u32(target, FLASH_BBSR, bbsr);
629 target_write_u32(target, FLASH_NBBSR, nbbsr);
630 target_write_u32(target, FLASH_BBADR, bbadr >> 2);
631 target_write_u32(target, FLASH_NBBADR, nbbadr >> 2);
632
633 /* set bit 18 instruction TCM order as per flash programming manual */
634 arm966e_write_cp15(target, 62, 0x40000);
635
636 /* enable flash bank 1 */
637 target_write_u32(target, FLASH_CR, 0x18);
638 return ERROR_OK;
639 }
640
641 static const struct command_registration str9x_config_command_handlers[] = {
642 {
643 .name = "flash_config",
644 .handler = str9x_handle_flash_config_command,
645 .mode = COMMAND_EXEC,
646 .help = "Configure str9x flash controller, prior to "
647 "programming the flash.",
648 .usage = "bank_id BBSR NBBSR BBADR NBBADR",
649 },
650 COMMAND_REGISTRATION_DONE
651 };
652
653 static const struct command_registration str9x_command_handlers[] = {
654 {
655 .name = "str9x",
656 .mode = COMMAND_ANY,
657 .help = "str9x flash command group",
658 .usage = "",
659 .chain = str9x_config_command_handlers,
660 },
661 COMMAND_REGISTRATION_DONE
662 };
663
664 const struct flash_driver str9x_flash = {
665 .name = "str9x",
666 .commands = str9x_command_handlers,
667 .flash_bank_command = str9x_flash_bank_command,
668 .erase = str9x_erase,
669 .protect = str9x_protect,
670 .write = str9x_write,
671 .read = default_flash_read,
672 .probe = str9x_probe,
673 .auto_probe = str9x_probe,
674 .erase_check = default_flash_blank_check,
675 .protect_check = str9x_protect_check,
676 .free_driver_priv = default_flash_free_driver_priv,
677 };

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)