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

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)