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

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)