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

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)