flash/nor: use command_print() in command "flash banks"
[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 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 int i;
168 uint32_t adr;
169 uint32_t status = 0;
170 uint16_t hstatus = 0;
171
172 if (bank->target->state != TARGET_HALTED) {
173 LOG_ERROR("Target not halted");
174 return ERROR_TARGET_NOT_HALTED;
175 }
176
177 /* read level one protection */
178
179 if (str9x_info->variant) {
180 if (str9x_info->bank1) {
181 adr = bank1start + 0x18;
182 retval = target_write_u16(target, adr, 0x90);
183 if (retval != ERROR_OK)
184 return retval;
185 retval = target_read_u16(target, adr, &hstatus);
186 if (retval != ERROR_OK)
187 return retval;
188 status = hstatus;
189 } else {
190 adr = bank1start + 0x14;
191 retval = target_write_u16(target, adr, 0x90);
192 if (retval != ERROR_OK)
193 return retval;
194 retval = target_read_u32(target, adr, &status);
195 if (retval != ERROR_OK)
196 return retval;
197 }
198 } else {
199 adr = bank1start + 0x10;
200 retval = target_write_u16(target, adr, 0x90);
201 if (retval != ERROR_OK)
202 return retval;
203 retval = target_read_u16(target, adr, &hstatus);
204 if (retval != ERROR_OK)
205 return retval;
206 status = hstatus;
207 }
208
209 /* read array command */
210 retval = target_write_u16(target, adr, 0xFF);
211 if (retval != ERROR_OK)
212 return retval;
213
214 for (i = 0; i < bank->num_sectors; i++) {
215 if (status & str9x_info->sector_bits[i])
216 bank->sectors[i].is_protected = 1;
217 else
218 bank->sectors[i].is_protected = 0;
219 }
220
221 return ERROR_OK;
222 }
223
224 static int str9x_erase(struct flash_bank *bank, int first, int last)
225 {
226 struct target *target = bank->target;
227 int i;
228 uint32_t adr;
229 uint8_t status;
230 uint8_t erase_cmd;
231 int total_timeout;
232
233 if (bank->target->state != TARGET_HALTED) {
234 LOG_ERROR("Target not halted");
235 return ERROR_TARGET_NOT_HALTED;
236 }
237
238 /* Check if we can erase whole bank */
239 if ((first == 0) && (last == (bank->num_sectors - 1))) {
240 /* Optimize to run erase bank command instead of sector */
241 erase_cmd = 0x80;
242 /* Add timeout duration since erase bank takes more time */
243 total_timeout = 1000 * bank->num_sectors;
244 } else {
245 /* Erase sector command */
246 erase_cmd = 0x20;
247 total_timeout = 1000;
248 }
249
250 /* this is so the compiler can *know* */
251 assert(total_timeout > 0);
252
253 for (i = first; i <= last; i++) {
254 int retval;
255 adr = bank->base + bank->sectors[i].offset;
256
257 /* erase sectors or block */
258 retval = target_write_u16(target, adr, erase_cmd);
259 if (retval != ERROR_OK)
260 return retval;
261 retval = target_write_u16(target, adr, 0xD0);
262 if (retval != ERROR_OK)
263 return retval;
264
265 /* get status */
266 retval = target_write_u16(target, adr, 0x70);
267 if (retval != ERROR_OK)
268 return retval;
269
270 int timeout;
271 for (timeout = 0; timeout < total_timeout; timeout++) {
272 retval = target_read_u8(target, adr, &status);
273 if (retval != ERROR_OK)
274 return retval;
275 if (status & 0x80)
276 break;
277 alive_sleep(1);
278 }
279 if (timeout == total_timeout) {
280 LOG_ERROR("erase timed out");
281 return ERROR_FAIL;
282 }
283
284 /* clear status, also clear read array */
285 retval = target_write_u16(target, adr, 0x50);
286 if (retval != ERROR_OK)
287 return retval;
288
289 /* read array command */
290 retval = target_write_u16(target, adr, 0xFF);
291 if (retval != ERROR_OK)
292 return retval;
293
294 if (status & 0x22) {
295 LOG_ERROR("error erasing flash bank, status: 0x%x", status);
296 return ERROR_FLASH_OPERATION_FAILED;
297 }
298
299 /* If we ran erase bank command, we are finished */
300 if (erase_cmd == 0x80)
301 break;
302 }
303
304 for (i = first; i <= last; i++)
305 bank->sectors[i].is_erased = 1;
306
307 return ERROR_OK;
308 }
309
310 static int str9x_protect(struct flash_bank *bank,
311 int set, int first, int last)
312 {
313 struct target *target = bank->target;
314 int i;
315 uint32_t adr;
316 uint8_t status;
317
318 if (bank->target->state != TARGET_HALTED) {
319 LOG_ERROR("Target not halted");
320 return ERROR_TARGET_NOT_HALTED;
321 }
322
323 for (i = first; i <= last; i++) {
324 /* Level One Protection */
325
326 adr = bank->base + bank->sectors[i].offset;
327
328 target_write_u16(target, adr, 0x60);
329 if (set)
330 target_write_u16(target, adr, 0x01);
331 else
332 target_write_u16(target, adr, 0xD0);
333
334 /* query status */
335 target_read_u8(target, adr, &status);
336
337 /* clear status, also clear read array */
338 target_write_u16(target, adr, 0x50);
339
340 /* read array command */
341 target_write_u16(target, adr, 0xFF);
342 }
343
344 return ERROR_OK;
345 }
346
347 static int str9x_write_block(struct flash_bank *bank,
348 const uint8_t *buffer, uint32_t offset, uint32_t count)
349 {
350 struct target *target = bank->target;
351 uint32_t buffer_size = 32768;
352 struct working_area *write_algorithm;
353 struct working_area *source;
354 uint32_t address = bank->base + offset;
355 struct reg_param reg_params[4];
356 struct arm_algorithm arm_algo;
357 int retval = ERROR_OK;
358
359 /* see contib/loaders/flash/str9x.s for src */
360
361 static const uint32_t str9x_flash_write_code[] = {
362 /* write: */
363 0xe3c14003, /* bic r4, r1, #3 */
364 0xe3a03040, /* mov r3, #0x40 */
365 0xe1c430b0, /* strh r3, [r4, #0] */
366 0xe0d030b2, /* ldrh r3, [r0], #2 */
367 0xe0c130b2, /* strh r3, [r1], #2 */
368 0xe3a03070, /* mov r3, #0x70 */
369 0xe1c430b0, /* strh r3, [r4, #0] */
370 /* busy: */
371 0xe5d43000, /* ldrb r3, [r4, #0] */
372 0xe3130080, /* tst r3, #0x80 */
373 0x0afffffc, /* beq busy */
374 0xe3a05050, /* mov r5, #0x50 */
375 0xe1c450b0, /* strh r5, [r4, #0] */
376 0xe3a050ff, /* mov r5, #0xFF */
377 0xe1c450b0, /* strh r5, [r4, #0] */
378 0xe3130012, /* tst r3, #0x12 */
379 0x1a000001, /* bne exit */
380 0xe2522001, /* subs r2, r2, #1 */
381 0x1affffed, /* bne write */
382 /* exit: */
383 0xe1200070, /* bkpt #0 */
384 };
385
386 /* flash write code */
387 if (target_alloc_working_area(target, sizeof(str9x_flash_write_code),
388 &write_algorithm) != ERROR_OK) {
389 LOG_WARNING("no working area available, can't do block memory writes");
390 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
391 }
392
393 uint8_t code[sizeof(str9x_flash_write_code)];
394 target_buffer_set_u32_array(target, code, ARRAY_SIZE(str9x_flash_write_code),
395 str9x_flash_write_code);
396 target_write_buffer(target, write_algorithm->address, sizeof(code), code);
397
398 /* memory buffer */
399 while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
400 buffer_size /= 2;
401 if (buffer_size <= 256) {
402 /* we already allocated the writing code, but failed to get a
403 * buffer, free the algorithm */
404 target_free_working_area(target, write_algorithm);
405
406 LOG_WARNING("no large enough working area available, can't do block memory writes");
407 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
408 }
409 }
410
411 arm_algo.common_magic = ARM_COMMON_MAGIC;
412 arm_algo.core_mode = ARM_MODE_SVC;
413 arm_algo.core_state = ARM_STATE_ARM;
414
415 init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
416 init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
417 init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
418 init_reg_param(&reg_params[3], "r3", 32, PARAM_IN);
419
420 while (count > 0) {
421 uint32_t thisrun_count = (count > (buffer_size / 2)) ? (buffer_size / 2) : count;
422
423 target_write_buffer(target, source->address, thisrun_count * 2, buffer);
424
425 buf_set_u32(reg_params[0].value, 0, 32, source->address);
426 buf_set_u32(reg_params[1].value, 0, 32, address);
427 buf_set_u32(reg_params[2].value, 0, 32, thisrun_count);
428
429 retval = target_run_algorithm(target, 0, NULL, 4, reg_params,
430 write_algorithm->address,
431 0, 10000, &arm_algo);
432 if (retval != ERROR_OK) {
433 LOG_ERROR("error executing str9x flash write algorithm");
434 retval = ERROR_FLASH_OPERATION_FAILED;
435 break;
436 }
437
438 if (buf_get_u32(reg_params[3].value, 0, 32) != 0x80) {
439 retval = ERROR_FLASH_OPERATION_FAILED;
440 break;
441 }
442
443 buffer += thisrun_count * 2;
444 address += thisrun_count * 2;
445 count -= thisrun_count;
446 }
447
448 target_free_working_area(target, source);
449 target_free_working_area(target, write_algorithm);
450
451 destroy_reg_param(&reg_params[0]);
452 destroy_reg_param(&reg_params[1]);
453 destroy_reg_param(&reg_params[2]);
454 destroy_reg_param(&reg_params[3]);
455
456 return retval;
457 }
458
459 static int str9x_write(struct flash_bank *bank,
460 const uint8_t *buffer, uint32_t offset, uint32_t count)
461 {
462 struct target *target = bank->target;
463 uint32_t words_remaining = (count / 2);
464 uint32_t bytes_remaining = (count & 0x00000001);
465 uint32_t address = bank->base + offset;
466 uint32_t bytes_written = 0;
467 uint8_t status;
468 int retval;
469 uint32_t check_address = offset;
470 uint32_t bank_adr;
471 int i;
472
473 if (bank->target->state != TARGET_HALTED) {
474 LOG_ERROR("Target not halted");
475 return ERROR_TARGET_NOT_HALTED;
476 }
477
478 if (offset & 0x1) {
479 LOG_WARNING("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset);
480 return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
481 }
482
483 for (i = 0; i < bank->num_sectors; i++) {
484 uint32_t sec_start = bank->sectors[i].offset;
485 uint32_t sec_end = sec_start + bank->sectors[i].size;
486
487 /* check if destination falls within the current sector */
488 if ((check_address >= sec_start) && (check_address < sec_end)) {
489 /* check if destination ends in the current sector */
490 if (offset + count < sec_end)
491 check_address = offset + count;
492 else
493 check_address = sec_end;
494 }
495 }
496
497 if (check_address != offset + count)
498 return ERROR_FLASH_DST_OUT_OF_BANK;
499
500 /* multiple half words (2-byte) to be programmed? */
501 if (words_remaining > 0) {
502 /* try using a block write */
503 retval = str9x_write_block(bank, buffer, offset, words_remaining);
504 if (retval != ERROR_OK) {
505 if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
506 /* if block write failed (no sufficient working area),
507 * we use normal (slow) single dword accesses */
508 LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
509 } else if (retval == ERROR_FLASH_OPERATION_FAILED) {
510 LOG_ERROR("flash writing failed");
511 return ERROR_FLASH_OPERATION_FAILED;
512 }
513 } else {
514 buffer += words_remaining * 2;
515 address += words_remaining * 2;
516 words_remaining = 0;
517 }
518 }
519
520 while (words_remaining > 0) {
521 bank_adr = address & ~0x03;
522
523 /* write data command */
524 target_write_u16(target, bank_adr, 0x40);
525 target_write_memory(target, address, 2, 1, buffer + bytes_written);
526
527 /* get status command */
528 target_write_u16(target, bank_adr, 0x70);
529
530 int timeout;
531 for (timeout = 0; timeout < 1000; timeout++) {
532 target_read_u8(target, bank_adr, &status);
533 if (status & 0x80)
534 break;
535 alive_sleep(1);
536 }
537 if (timeout == 1000) {
538 LOG_ERROR("write timed out");
539 return ERROR_FAIL;
540 }
541
542 /* clear status reg and read array */
543 target_write_u16(target, bank_adr, 0x50);
544 target_write_u16(target, bank_adr, 0xFF);
545
546 if (status & 0x10)
547 return ERROR_FLASH_OPERATION_FAILED;
548 else if (status & 0x02)
549 return ERROR_FLASH_OPERATION_FAILED;
550
551 bytes_written += 2;
552 words_remaining--;
553 address += 2;
554 }
555
556 if (bytes_remaining) {
557 uint8_t last_halfword[2] = {0xff, 0xff};
558
559 /* copy the last remaining bytes into the write buffer */
560 memcpy(last_halfword, buffer+bytes_written, bytes_remaining);
561
562 bank_adr = address & ~0x03;
563
564 /* write data command */
565 target_write_u16(target, bank_adr, 0x40);
566 target_write_memory(target, address, 2, 1, last_halfword);
567
568 /* query status command */
569 target_write_u16(target, bank_adr, 0x70);
570
571 int timeout;
572 for (timeout = 0; timeout < 1000; timeout++) {
573 target_read_u8(target, bank_adr, &status);
574 if (status & 0x80)
575 break;
576 alive_sleep(1);
577 }
578 if (timeout == 1000) {
579 LOG_ERROR("write timed out");
580 return ERROR_FAIL;
581 }
582
583 /* clear status reg and read array */
584 target_write_u16(target, bank_adr, 0x50);
585 target_write_u16(target, bank_adr, 0xFF);
586
587 if (status & 0x10)
588 return ERROR_FLASH_OPERATION_FAILED;
589 else if (status & 0x02)
590 return ERROR_FLASH_OPERATION_FAILED;
591 }
592
593 return ERROR_OK;
594 }
595
596 static int str9x_probe(struct flash_bank *bank)
597 {
598 return ERROR_OK;
599 }
600
601 #if 0
602 COMMAND_HANDLER(str9x_handle_part_id_command)
603 {
604 return ERROR_OK;
605 }
606 #endif
607
608 COMMAND_HANDLER(str9x_handle_flash_config_command)
609 {
610 struct target *target = NULL;
611
612 if (CMD_ARGC < 5)
613 return ERROR_COMMAND_SYNTAX_ERROR;
614
615 struct flash_bank *bank;
616 int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
617 if (ERROR_OK != retval)
618 return retval;
619
620 uint32_t bbsr, nbbsr, bbadr, nbbadr;
621 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], bbsr);
622 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], nbbsr);
623 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], bbadr);
624 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[4], nbbadr);
625
626 target = bank->target;
627
628 if (bank->target->state != TARGET_HALTED) {
629 LOG_ERROR("Target not halted");
630 return ERROR_TARGET_NOT_HALTED;
631 }
632
633 /* config flash controller */
634 target_write_u32(target, FLASH_BBSR, bbsr);
635 target_write_u32(target, FLASH_NBBSR, nbbsr);
636 target_write_u32(target, FLASH_BBADR, bbadr >> 2);
637 target_write_u32(target, FLASH_NBBADR, nbbadr >> 2);
638
639 /* set bit 18 instruction TCM order as per flash programming manual */
640 arm966e_write_cp15(target, 62, 0x40000);
641
642 /* enable flash bank 1 */
643 target_write_u32(target, FLASH_CR, 0x18);
644 return ERROR_OK;
645 }
646
647 static const struct command_registration str9x_config_command_handlers[] = {
648 {
649 .name = "flash_config",
650 .handler = str9x_handle_flash_config_command,
651 .mode = COMMAND_EXEC,
652 .help = "Configure str9x flash controller, prior to "
653 "programming the flash.",
654 .usage = "bank_id BBSR NBBSR BBADR NBBADR",
655 },
656 COMMAND_REGISTRATION_DONE
657 };
658
659 static const struct command_registration str9x_command_handlers[] = {
660 {
661 .name = "str9x",
662 .mode = COMMAND_ANY,
663 .help = "str9x flash command group",
664 .usage = "",
665 .chain = str9x_config_command_handlers,
666 },
667 COMMAND_REGISTRATION_DONE
668 };
669
670 const struct flash_driver str9x_flash = {
671 .name = "str9x",
672 .commands = str9x_command_handlers,
673 .flash_bank_command = str9x_flash_bank_command,
674 .erase = str9x_erase,
675 .protect = str9x_protect,
676 .write = str9x_write,
677 .read = default_flash_read,
678 .probe = str9x_probe,
679 .auto_probe = str9x_probe,
680 .erase_check = default_flash_blank_check,
681 .protect_check = str9x_protect_check,
682 .free_driver_priv = default_flash_free_driver_priv,
683 };

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)