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

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)