45a3fd67789236c9f97f5d424e8d9a916299f931
[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
198 if (bank->target->state != TARGET_HALTED)
199 {
200 LOG_ERROR("Target not halted");
201 return ERROR_TARGET_NOT_HALTED;
202 }
203
204 /* read level one protection */
205
206 if (str9x_info->variant)
207 {
208 if (str9x_info->bank1)
209 {
210 adr = bank1start + 0x18;
211 if ((retval=target_write_u16(target, adr, 0x90))!=ERROR_OK)
212 {
213 return retval;
214 }
215 if ((retval=target_read_u16(target, adr, (u16*)&status))!=ERROR_OK)
216 {
217 return retval;
218 }
219 }
220 else
221 {
222 adr = bank1start + 0x14;
223 if ((retval=target_write_u16(target, adr, 0x90))!=ERROR_OK)
224 {
225 return retval;
226 }
227 if ((retval=target_read_u32(target, adr, &status))!=ERROR_OK)
228 {
229 return retval;
230 }
231 }
232 }
233 else
234 {
235 adr = bank1start + 0x10;
236 if ((retval=target_write_u16(target, adr, 0x90))!=ERROR_OK)
237 {
238 return retval;
239 }
240 if ((retval=target_read_u16(target, adr, (u16*)&status))!=ERROR_OK)
241 {
242 return retval;
243 }
244 }
245
246 /* read array command */
247 if ((retval=target_write_u16(target, adr, 0xFF))!=ERROR_OK)
248 {
249 return retval;
250 }
251
252 for (i = 0; i < bank->num_sectors; i++)
253 {
254 if (status & str9x_info->sector_bits[i])
255 bank->sectors[i].is_protected = 1;
256 else
257 bank->sectors[i].is_protected = 0;
258 }
259
260 return ERROR_OK;
261 }
262
263 static int str9x_erase(struct flash_bank_s *bank, int first, int last)
264 {
265 target_t *target = bank->target;
266 int i;
267 u32 adr;
268 u8 status;
269 u8 erase_cmd;
270
271 if (bank->target->state != TARGET_HALTED)
272 {
273 LOG_ERROR("Target not halted");
274 return ERROR_TARGET_NOT_HALTED;
275 }
276
277 /* Check if we erase whole bank */
278 if ((first == 0) && (last == (bank->num_sectors - 1)))
279 {
280 /* Optimize to run erase bank command instead of sector */
281 erase_cmd = 0x80;
282 }
283 else
284 {
285 /* Erase sector command */
286 erase_cmd = 0x20;
287 }
288
289 for (i = first; i <= last; i++)
290 {
291 int retval;
292 adr = bank->base + bank->sectors[i].offset;
293
294 /* erase sectors */
295 if ((retval=target_write_u16(target, adr, erase_cmd))!=ERROR_OK)
296 {
297 return retval;
298 }
299 if ((retval=target_write_u16(target, adr, 0xD0))!=ERROR_OK)
300 {
301 return retval;
302 }
303
304 /* get status */
305 if ((retval=target_write_u16(target, adr, 0x70))!=ERROR_OK)
306 {
307 return retval;
308 }
309
310 int timeout;
311 for (timeout=0; timeout<1000; timeout++) {
312 if ((retval=target_read_u8(target, adr, &status))!=ERROR_OK)
313 {
314 return retval;
315 }
316 if( status & 0x80 )
317 break;
318 alive_sleep(1);
319 }
320 if (timeout==1000)
321 {
322 LOG_ERROR("erase timed out");
323 return ERROR_FAIL;
324 }
325
326 /* clear status, also clear read array */
327 if ((retval=target_write_u16(target, adr, 0x50))!=ERROR_OK)
328 {
329 return retval;
330 }
331
332 /* read array command */
333 if ((retval=target_write_u16(target, adr, 0xFF))!=ERROR_OK)
334 {
335 return retval;
336 }
337
338 if( status & 0x22 )
339 {
340 LOG_ERROR("error erasing flash bank, status: 0x%x", status);
341 return ERROR_FLASH_OPERATION_FAILED;
342 }
343
344 /* If we ran erase bank command, we are finished */
345 if (erase_cmd == 0x80)
346 break;
347 }
348
349 for (i = first; i <= last; i++)
350 bank->sectors[i].is_erased = 1;
351
352 return ERROR_OK;
353 }
354
355 static int str9x_protect(struct flash_bank_s *bank,
356 int set, int first, int last)
357 {
358 target_t *target = bank->target;
359 int i;
360 u32 adr;
361 u8 status;
362
363 if (bank->target->state != TARGET_HALTED)
364 {
365 LOG_ERROR("Target not halted");
366 return ERROR_TARGET_NOT_HALTED;
367 }
368
369 for (i = first; i <= last; i++)
370 {
371 /* Level One Protection */
372
373 adr = bank->base + bank->sectors[i].offset;
374
375 target_write_u16(target, adr, 0x60);
376 if( set )
377 target_write_u16(target, adr, 0x01);
378 else
379 target_write_u16(target, adr, 0xD0);
380
381 /* query status */
382 target_read_u8(target, adr, &status);
383
384 /* clear status, also clear read array */
385 target_write_u16(target, adr, 0x50);
386
387 /* read array command */
388 target_write_u16(target, adr, 0xFF);
389 }
390
391 return ERROR_OK;
392 }
393
394 static int str9x_write_block(struct flash_bank_s *bank,
395 u8 *buffer, u32 offset, u32 count)
396 {
397 str9x_flash_bank_t *str9x_info = bank->driver_priv;
398 target_t *target = bank->target;
399 u32 buffer_size = 8192;
400 working_area_t *source;
401 u32 address = bank->base + offset;
402 reg_param_t reg_params[4];
403 armv4_5_algorithm_t armv4_5_info;
404 int retval = ERROR_OK;
405
406 u32 str9x_flash_write_code[] = {
407 /* write: */
408 0xe3c14003, /* bic r4, r1, #3 */
409 0xe3a03040, /* mov r3, #0x40 */
410 0xe1c430b0, /* strh r3, [r4, #0] */
411 0xe0d030b2, /* ldrh r3, [r0], #2 */
412 0xe0c130b2, /* strh r3, [r1], #2 */
413 0xe3a03070, /* mov r3, #0x70 */
414 0xe1c430b0, /* strh r3, [r4, #0] */
415 /* busy: */
416 0xe5d43000, /* ldrb r3, [r4, #0] */
417 0xe3130080, /* tst r3, #0x80 */
418 0x0afffffc, /* beq busy */
419 0xe3a05050, /* mov r5, #0x50 */
420 0xe1c450b0, /* strh r5, [r4, #0] */
421 0xe3a050ff, /* mov r5, #0xFF */
422 0xe1c450b0, /* strh r5, [r4, #0] */
423 0xe3130012, /* tst r3, #0x12 */
424 0x1a000001, /* bne exit */
425 0xe2522001, /* subs r2, r2, #1 */
426 0x1affffed, /* bne write */
427 /* exit: */
428 0xeafffffe, /* b exit */
429 };
430
431 /* flash write code */
432 if (target_alloc_working_area(target, 4 * 19, &str9x_info->write_algorithm) != ERROR_OK)
433 {
434 LOG_WARNING("no working area available, can't do block memory writes");
435 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
436 };
437
438 target_write_buffer(target, str9x_info->write_algorithm->address, 19 * 4, (u8*)str9x_flash_write_code);
439
440 /* memory buffer */
441 while (target_alloc_working_area(target, buffer_size, &source) != ERROR_OK)
442 {
443 buffer_size /= 2;
444 if (buffer_size <= 256)
445 {
446 /* if we already allocated the writing code, but failed to get a buffer, free the algorithm */
447 if (str9x_info->write_algorithm)
448 target_free_working_area(target, str9x_info->write_algorithm);
449
450 LOG_WARNING("no large enough working area available, can't do block memory writes");
451 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
452 }
453 }
454
455 armv4_5_info.common_magic = ARMV4_5_COMMON_MAGIC;
456 armv4_5_info.core_mode = ARMV4_5_MODE_SVC;
457 armv4_5_info.core_state = ARMV4_5_STATE_ARM;
458
459 init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
460 init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
461 init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
462 init_reg_param(&reg_params[3], "r3", 32, PARAM_IN);
463
464 while (count > 0)
465 {
466 u32 thisrun_count = (count > (buffer_size / 2)) ? (buffer_size / 2) : count;
467
468 target_write_buffer(target, source->address, thisrun_count * 2, buffer);
469
470 buf_set_u32(reg_params[0].value, 0, 32, source->address);
471 buf_set_u32(reg_params[1].value, 0, 32, address);
472 buf_set_u32(reg_params[2].value, 0, 32, thisrun_count);
473
474 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)
475 {
476 LOG_ERROR("error executing str9x flash write algorithm");
477 retval = ERROR_FLASH_OPERATION_FAILED;
478 break;
479 }
480
481 if (buf_get_u32(reg_params[3].value, 0, 32) != 0x80)
482 {
483 retval = ERROR_FLASH_OPERATION_FAILED;
484 break;
485 }
486
487 buffer += thisrun_count * 2;
488 address += thisrun_count * 2;
489 count -= thisrun_count;
490 }
491
492 target_free_working_area(target, source);
493 target_free_working_area(target, str9x_info->write_algorithm);
494
495 destroy_reg_param(&reg_params[0]);
496 destroy_reg_param(&reg_params[1]);
497 destroy_reg_param(&reg_params[2]);
498 destroy_reg_param(&reg_params[3]);
499
500 return retval;
501 }
502
503 static int str9x_write(struct flash_bank_s *bank,
504 u8 *buffer, u32 offset, u32 count)
505 {
506 target_t *target = bank->target;
507 u32 words_remaining = (count / 2);
508 u32 bytes_remaining = (count & 0x00000001);
509 u32 address = bank->base + offset;
510 u32 bytes_written = 0;
511 u8 status;
512 int retval;
513 u32 check_address = offset;
514 u32 bank_adr;
515 int i;
516
517 if (bank->target->state != TARGET_HALTED)
518 {
519 LOG_ERROR("Target not halted");
520 return ERROR_TARGET_NOT_HALTED;
521 }
522
523 if (offset & 0x1)
524 {
525 LOG_WARNING("offset 0x%x breaks required 2-byte alignment", offset);
526 return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
527 }
528
529 for (i = 0; i < bank->num_sectors; i++)
530 {
531 u32 sec_start = bank->sectors[i].offset;
532 u32 sec_end = sec_start + bank->sectors[i].size;
533
534 /* check if destination falls within the current sector */
535 if ((check_address >= sec_start) && (check_address < sec_end))
536 {
537 /* check if destination ends in the current sector */
538 if (offset + count < sec_end)
539 check_address = offset + count;
540 else
541 check_address = sec_end;
542 }
543 }
544
545 if (check_address != offset + count)
546 return ERROR_FLASH_DST_OUT_OF_BANK;
547
548 /* multiple half words (2-byte) to be programmed? */
549 if (words_remaining > 0)
550 {
551 /* try using a block write */
552 if ((retval = str9x_write_block(bank, buffer, offset, words_remaining)) != ERROR_OK)
553 {
554 if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
555 {
556 /* if block write failed (no sufficient working area),
557 * we use normal (slow) single dword accesses */
558 LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
559 }
560 else if (retval == ERROR_FLASH_OPERATION_FAILED)
561 {
562 LOG_ERROR("flash writing failed with error code: 0x%x", retval);
563 return ERROR_FLASH_OPERATION_FAILED;
564 }
565 }
566 else
567 {
568 buffer += words_remaining * 2;
569 address += words_remaining * 2;
570 words_remaining = 0;
571 }
572 }
573
574 while (words_remaining > 0)
575 {
576 bank_adr = address & ~0x03;
577
578 /* write data command */
579 target_write_u16(target, bank_adr, 0x40);
580 target->type->write_memory(target, address, 2, 1, buffer + bytes_written);
581
582 /* get status command */
583 target_write_u16(target, bank_adr, 0x70);
584
585 int timeout;
586 for (timeout=0; timeout<1000; timeout++)
587 {
588 target_read_u8(target, bank_adr, &status);
589 if( status & 0x80 )
590 break;
591 alive_sleep(1);
592 }
593 if (timeout==1000)
594 {
595 LOG_ERROR("write timed out");
596 return ERROR_FAIL;
597 }
598
599 /* clear status reg and read array */
600 target_write_u16(target, bank_adr, 0x50);
601 target_write_u16(target, bank_adr, 0xFF);
602
603 if (status & 0x10)
604 return ERROR_FLASH_OPERATION_FAILED;
605 else if (status & 0x02)
606 return ERROR_FLASH_OPERATION_FAILED;
607
608 bytes_written += 2;
609 words_remaining--;
610 address += 2;
611 }
612
613 if (bytes_remaining)
614 {
615 u8 last_halfword[2] = {0xff, 0xff};
616 int i = 0;
617
618 while(bytes_remaining > 0)
619 {
620 last_halfword[i++] = *(buffer + bytes_written);
621 bytes_remaining--;
622 bytes_written++;
623 }
624
625 bank_adr = address & ~0x03;
626
627 /* write data comamnd */
628 target_write_u16(target, bank_adr, 0x40);
629 target->type->write_memory(target, address, 2, 1, last_halfword);
630
631 /* query status command */
632 target_write_u16(target, bank_adr, 0x70);
633
634 int timeout;
635 for (timeout=0; timeout<1000; timeout++)
636 {
637 target_read_u8(target, bank_adr, &status);
638 if( status & 0x80 )
639 break;
640 alive_sleep(1);
641 }
642 if (timeout==1000)
643 {
644 LOG_ERROR("write timed out");
645 return ERROR_FAIL;
646 }
647
648 /* clear status reg and read array */
649 target_write_u16(target, bank_adr, 0x50);
650 target_write_u16(target, bank_adr, 0xFF);
651
652 if (status & 0x10)
653 return ERROR_FLASH_OPERATION_FAILED;
654 else if (status & 0x02)
655 return ERROR_FLASH_OPERATION_FAILED;
656 }
657
658 return ERROR_OK;
659 }
660
661 static int str9x_probe(struct flash_bank_s *bank)
662 {
663 return ERROR_OK;
664 }
665
666 #if 0
667 static int str9x_handle_part_id_command(struct command_context_s *cmd_ctx,
668 char *cmd, char **args, int argc)
669 {
670 return ERROR_OK;
671 }
672 #endif
673
674 static int str9x_info(struct flash_bank_s *bank, char *buf, int buf_size)
675 {
676 snprintf(buf, buf_size, "str9x flash driver info" );
677 return ERROR_OK;
678 }
679
680 static int str9x_handle_flash_config_command(struct command_context_s *cmd_ctx,
681 char *cmd, char **args, int argc)
682 {
683 str9x_flash_bank_t *str9x_info;
684 flash_bank_t *bank;
685 target_t *target = NULL;
686
687 if (argc < 5)
688 {
689 return ERROR_COMMAND_SYNTAX_ERROR;
690 }
691
692 bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0));
693 if (!bank)
694 {
695 command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]);
696 return ERROR_OK;
697 }
698
699 str9x_info = bank->driver_priv;
700
701 target = bank->target;
702
703 if (bank->target->state != TARGET_HALTED)
704 {
705 LOG_ERROR("Target not halted");
706 return ERROR_TARGET_NOT_HALTED;
707 }
708
709 /* config flash controller */
710 target_write_u32(target, FLASH_BBSR, strtoul(args[1], NULL, 0));
711 target_write_u32(target, FLASH_NBBSR, strtoul(args[2], NULL, 0));
712 target_write_u32(target, FLASH_BBADR, (strtoul(args[3], NULL, 0) >> 2));
713 target_write_u32(target, FLASH_NBBADR, (strtoul(args[4], NULL, 0) >> 2));
714
715 /* set bit 18 instruction TCM order as per flash programming manual */
716 arm966e_write_cp15(target, 62, 0x40000);
717
718 /* enable flash bank 1 */
719 target_write_u32(target, FLASH_CR, 0x18);
720 return ERROR_OK;
721 }

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)