flash: fix typos and duplicated words
[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 for (unsigned int i = first; i <= last; i++)
304 bank->sectors[i].is_erased = 1;
305
306 return ERROR_OK;
307 }
308
309 static int str9x_protect(struct flash_bank *bank, int set, unsigned int first,
310 unsigned int last)
311 {
312 struct target *target = bank->target;
313 uint32_t adr;
314 uint8_t status;
315
316 if (bank->target->state != TARGET_HALTED) {
317 LOG_ERROR("Target not halted");
318 return ERROR_TARGET_NOT_HALTED;
319 }
320
321 for (unsigned int i = first; i <= last; i++) {
322 /* Level One Protection */
323
324 adr = bank->base + bank->sectors[i].offset;
325
326 target_write_u16(target, adr, 0x60);
327 if (set)
328 target_write_u16(target, adr, 0x01);
329 else
330 target_write_u16(target, adr, 0xD0);
331
332 /* query status */
333 target_read_u8(target, adr, &status);
334
335 /* clear status, also clear read array */
336 target_write_u16(target, adr, 0x50);
337
338 /* read array command */
339 target_write_u16(target, adr, 0xFF);
340 }
341
342 return ERROR_OK;
343 }
344
345 static int str9x_write_block(struct flash_bank *bank,
346 const uint8_t *buffer, uint32_t offset, uint32_t count)
347 {
348 struct target *target = bank->target;
349 uint32_t buffer_size = 32768;
350 struct working_area *write_algorithm;
351 struct working_area *source;
352 uint32_t address = bank->base + offset;
353 struct reg_param reg_params[4];
354 struct arm_algorithm arm_algo;
355 int retval = ERROR_OK;
356
357 /* see contrib/loaders/flash/str9x.s for src */
358
359 static const uint32_t str9x_flash_write_code[] = {
360 /* write: */
361 0xe3c14003, /* bic r4, r1, #3 */
362 0xe3a03040, /* mov r3, #0x40 */
363 0xe1c430b0, /* strh r3, [r4, #0] */
364 0xe0d030b2, /* ldrh r3, [r0], #2 */
365 0xe0c130b2, /* strh r3, [r1], #2 */
366 0xe3a03070, /* mov r3, #0x70 */
367 0xe1c430b0, /* strh r3, [r4, #0] */
368 /* busy: */
369 0xe5d43000, /* ldrb r3, [r4, #0] */
370 0xe3130080, /* tst r3, #0x80 */
371 0x0afffffc, /* beq busy */
372 0xe3a05050, /* mov r5, #0x50 */
373 0xe1c450b0, /* strh r5, [r4, #0] */
374 0xe3a050ff, /* mov r5, #0xFF */
375 0xe1c450b0, /* strh r5, [r4, #0] */
376 0xe3130012, /* tst r3, #0x12 */
377 0x1a000001, /* bne exit */
378 0xe2522001, /* subs r2, r2, #1 */
379 0x1affffed, /* bne write */
380 /* exit: */
381 0xe1200070, /* bkpt #0 */
382 };
383
384 /* flash write code */
385 if (target_alloc_working_area(target, sizeof(str9x_flash_write_code),
386 &write_algorithm) != ERROR_OK) {
387 LOG_WARNING("no working area available, can't do block memory writes");
388 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
389 }
390
391 uint8_t code[sizeof(str9x_flash_write_code)];
392 target_buffer_set_u32_array(target, code, ARRAY_SIZE(str9x_flash_write_code),
393 str9x_flash_write_code);
394 target_write_buffer(target, write_algorithm->address, sizeof(code), code);
395
396 /* memory buffer */
397 while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
398 buffer_size /= 2;
399 if (buffer_size <= 256) {
400 /* we already allocated the writing code, but failed to get a
401 * buffer, free the algorithm */
402 target_free_working_area(target, write_algorithm);
403
404 LOG_WARNING("no large enough working area available, can't do block memory writes");
405 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
406 }
407 }
408
409 arm_algo.common_magic = ARM_COMMON_MAGIC;
410 arm_algo.core_mode = ARM_MODE_SVC;
411 arm_algo.core_state = ARM_STATE_ARM;
412
413 init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
414 init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
415 init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
416 init_reg_param(&reg_params[3], "r3", 32, PARAM_IN);
417
418 while (count > 0) {
419 uint32_t thisrun_count = (count > (buffer_size / 2)) ? (buffer_size / 2) : count;
420
421 target_write_buffer(target, source->address, thisrun_count * 2, buffer);
422
423 buf_set_u32(reg_params[0].value, 0, 32, source->address);
424 buf_set_u32(reg_params[1].value, 0, 32, address);
425 buf_set_u32(reg_params[2].value, 0, 32, thisrun_count);
426
427 retval = target_run_algorithm(target, 0, NULL, 4, reg_params,
428 write_algorithm->address,
429 0, 10000, &arm_algo);
430 if (retval != ERROR_OK) {
431 LOG_ERROR("error executing str9x flash write algorithm");
432 retval = ERROR_FLASH_OPERATION_FAILED;
433 break;
434 }
435
436 if (buf_get_u32(reg_params[3].value, 0, 32) != 0x80) {
437 retval = ERROR_FLASH_OPERATION_FAILED;
438 break;
439 }
440
441 buffer += thisrun_count * 2;
442 address += thisrun_count * 2;
443 count -= thisrun_count;
444 }
445
446 target_free_working_area(target, source);
447 target_free_working_area(target, write_algorithm);
448
449 destroy_reg_param(&reg_params[0]);
450 destroy_reg_param(&reg_params[1]);
451 destroy_reg_param(&reg_params[2]);
452 destroy_reg_param(&reg_params[3]);
453
454 return retval;
455 }
456
457 static int str9x_write(struct flash_bank *bank,
458 const uint8_t *buffer, uint32_t offset, uint32_t count)
459 {
460 struct target *target = bank->target;
461 uint32_t words_remaining = (count / 2);
462 uint32_t bytes_remaining = (count & 0x00000001);
463 uint32_t address = bank->base + offset;
464 uint32_t bytes_written = 0;
465 uint8_t status;
466 int retval;
467 uint32_t check_address = offset;
468 uint32_t bank_adr;
469
470 if (bank->target->state != TARGET_HALTED) {
471 LOG_ERROR("Target not halted");
472 return ERROR_TARGET_NOT_HALTED;
473 }
474
475 if (offset & 0x1) {
476 LOG_WARNING("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset);
477 return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
478 }
479
480 for (unsigned int i = 0; i < bank->num_sectors; i++) {
481 uint32_t sec_start = bank->sectors[i].offset;
482 uint32_t sec_end = sec_start + bank->sectors[i].size;
483
484 /* check if destination falls within the current sector */
485 if ((check_address >= sec_start) && (check_address < sec_end)) {
486 /* check if destination ends in the current sector */
487 if (offset + count < sec_end)
488 check_address = offset + count;
489 else
490 check_address = sec_end;
491 }
492 }
493
494 if (check_address != offset + count)
495 return ERROR_FLASH_DST_OUT_OF_BANK;
496
497 /* multiple half words (2-byte) to be programmed? */
498 if (words_remaining > 0) {
499 /* try using a block write */
500 retval = str9x_write_block(bank, buffer, offset, words_remaining);
501 if (retval != ERROR_OK) {
502 if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
503 /* if block write failed (no sufficient working area),
504 * we use normal (slow) single dword accesses */
505 LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
506 } else if (retval == ERROR_FLASH_OPERATION_FAILED) {
507 LOG_ERROR("flash writing failed");
508 return ERROR_FLASH_OPERATION_FAILED;
509 }
510 } else {
511 buffer += words_remaining * 2;
512 address += words_remaining * 2;
513 words_remaining = 0;
514 }
515 }
516
517 while (words_remaining > 0) {
518 bank_adr = address & ~0x03;
519
520 /* write data command */
521 target_write_u16(target, bank_adr, 0x40);
522 target_write_memory(target, address, 2, 1, buffer + bytes_written);
523
524 /* get status command */
525 target_write_u16(target, bank_adr, 0x70);
526
527 int timeout;
528 for (timeout = 0; timeout < 1000; timeout++) {
529 target_read_u8(target, bank_adr, &status);
530 if (status & 0x80)
531 break;
532 alive_sleep(1);
533 }
534 if (timeout == 1000) {
535 LOG_ERROR("write timed out");
536 return ERROR_FAIL;
537 }
538
539 /* clear status reg and read array */
540 target_write_u16(target, bank_adr, 0x50);
541 target_write_u16(target, bank_adr, 0xFF);
542
543 if (status & 0x10)
544 return ERROR_FLASH_OPERATION_FAILED;
545 else if (status & 0x02)
546 return ERROR_FLASH_OPERATION_FAILED;
547
548 bytes_written += 2;
549 words_remaining--;
550 address += 2;
551 }
552
553 if (bytes_remaining) {
554 uint8_t last_halfword[2] = {0xff, 0xff};
555
556 /* copy the last remaining bytes into the write buffer */
557 memcpy(last_halfword, buffer+bytes_written, bytes_remaining);
558
559 bank_adr = address & ~0x03;
560
561 /* write data command */
562 target_write_u16(target, bank_adr, 0x40);
563 target_write_memory(target, address, 2, 1, last_halfword);
564
565 /* query status command */
566 target_write_u16(target, bank_adr, 0x70);
567
568 int timeout;
569 for (timeout = 0; timeout < 1000; timeout++) {
570 target_read_u8(target, bank_adr, &status);
571 if (status & 0x80)
572 break;
573 alive_sleep(1);
574 }
575 if (timeout == 1000) {
576 LOG_ERROR("write timed out");
577 return ERROR_FAIL;
578 }
579
580 /* clear status reg and read array */
581 target_write_u16(target, bank_adr, 0x50);
582 target_write_u16(target, bank_adr, 0xFF);
583
584 if (status & 0x10)
585 return ERROR_FLASH_OPERATION_FAILED;
586 else if (status & 0x02)
587 return ERROR_FLASH_OPERATION_FAILED;
588 }
589
590 return ERROR_OK;
591 }
592
593 static int str9x_probe(struct flash_bank *bank)
594 {
595 return ERROR_OK;
596 }
597
598 #if 0
599 COMMAND_HANDLER(str9x_handle_part_id_command)
600 {
601 return ERROR_OK;
602 }
603 #endif
604
605 COMMAND_HANDLER(str9x_handle_flash_config_command)
606 {
607 struct target *target = NULL;
608
609 if (CMD_ARGC < 5)
610 return ERROR_COMMAND_SYNTAX_ERROR;
611
612 struct flash_bank *bank;
613 int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
614 if (ERROR_OK != retval)
615 return retval;
616
617 uint32_t bbsr, nbbsr, bbadr, nbbadr;
618 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], bbsr);
619 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], nbbsr);
620 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], bbadr);
621 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[4], nbbadr);
622
623 target = bank->target;
624
625 if (bank->target->state != TARGET_HALTED) {
626 LOG_ERROR("Target not halted");
627 return ERROR_TARGET_NOT_HALTED;
628 }
629
630 /* config flash controller */
631 target_write_u32(target, FLASH_BBSR, bbsr);
632 target_write_u32(target, FLASH_NBBSR, nbbsr);
633 target_write_u32(target, FLASH_BBADR, bbadr >> 2);
634 target_write_u32(target, FLASH_NBBADR, nbbadr >> 2);
635
636 /* set bit 18 instruction TCM order as per flash programming manual */
637 arm966e_write_cp15(target, 62, 0x40000);
638
639 /* enable flash bank 1 */
640 target_write_u32(target, FLASH_CR, 0x18);
641 return ERROR_OK;
642 }
643
644 static const struct command_registration str9x_config_command_handlers[] = {
645 {
646 .name = "flash_config",
647 .handler = str9x_handle_flash_config_command,
648 .mode = COMMAND_EXEC,
649 .help = "Configure str9x flash controller, prior to "
650 "programming the flash.",
651 .usage = "bank_id BBSR NBBSR BBADR NBBADR",
652 },
653 COMMAND_REGISTRATION_DONE
654 };
655
656 static const struct command_registration str9x_command_handlers[] = {
657 {
658 .name = "str9x",
659 .mode = COMMAND_ANY,
660 .help = "str9x flash command group",
661 .usage = "",
662 .chain = str9x_config_command_handlers,
663 },
664 COMMAND_REGISTRATION_DONE
665 };
666
667 const struct flash_driver str9x_flash = {
668 .name = "str9x",
669 .commands = str9x_command_handlers,
670 .flash_bank_command = str9x_flash_bank_command,
671 .erase = str9x_erase,
672 .protect = str9x_protect,
673 .write = str9x_write,
674 .read = default_flash_read,
675 .probe = str9x_probe,
676 .auto_probe = str9x_probe,
677 .erase_check = default_flash_blank_check,
678 .protect_check = str9x_protect_check,
679 .free_driver_priv = default_flash_free_driver_priv,
680 };

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)