1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
24 #include "replacements.h"
31 #include "algorithm.h"
32 #include "binarybuffer.h"
38 str7x_mem_layout_t mem_layout
[] = {
39 {0x00000000, 0x02000, 0x01},
40 {0x00002000, 0x02000, 0x02},
41 {0x00004000, 0x02000, 0x04},
42 {0x00006000, 0x02000, 0x08},
43 {0x00008000, 0x08000, 0x10},
44 {0x00010000, 0x10000, 0x20},
45 {0x00020000, 0x10000, 0x40},
46 {0x00030000, 0x10000, 0x80},
47 {0x000C0000, 0x02000, 0x10000},
48 {0x000C2000, 0x02000, 0x20000},
51 int str7x_register_commands(struct command_context_s
*cmd_ctx
);
52 int str7x_flash_bank_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
, struct flash_bank_s
*bank
);
53 int str7x_erase(struct flash_bank_s
*bank
, int first
, int last
);
54 int str7x_protect(struct flash_bank_s
*bank
, int set
, int first
, int last
);
55 int str7x_write(struct flash_bank_s
*bank
, u8
*buffer
, u32 offset
, u32 count
);
56 int str7x_probe(struct flash_bank_s
*bank
);
57 int str7x_handle_part_id_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
);
58 int str7x_protect_check(struct flash_bank_s
*bank
);
59 int str7x_erase_check(struct flash_bank_s
*bank
);
60 int str7x_info(struct flash_bank_s
*bank
, char *buf
, int buf_size
);
62 flash_driver_t str7x_flash
=
65 .register_commands
= str7x_register_commands
,
66 .flash_bank_command
= str7x_flash_bank_command
,
68 .protect
= str7x_protect
,
71 .erase_check
= str7x_erase_check
,
72 .protect_check
= str7x_protect_check
,
76 int str7x_register_commands(struct command_context_s
*cmd_ctx
)
82 int str7x_get_flash_adr(struct flash_bank_s
*bank
, u32 reg
)
84 return (bank
->base
| reg
);
87 int str7x_build_block_list(struct flash_bank_s
*bank
)
89 str7x_flash_bank_t
*str7x_info
= bank
->driver_priv
;
92 int num_sectors
= 0, b0_sectors
= 0, b1_sectors
= 0;
109 ERROR("BUG: unknown bank->size encountered");
113 if( str7x_info
->bank1
== 1 )
118 num_sectors
= b0_sectors
+ b1_sectors
;
120 bank
->num_sectors
= num_sectors
;
121 bank
->sectors
= malloc(sizeof(flash_sector_t
) * num_sectors
);
122 str7x_info
->sector_bits
= malloc(sizeof(u32
) * num_sectors
);
123 str7x_info
->sector_bank
= malloc(sizeof(u32
) * num_sectors
);
127 for (i
= 0; i
< b0_sectors
; i
++)
129 bank
->sectors
[num_sectors
].offset
= mem_layout
[i
].sector_start
;
130 bank
->sectors
[num_sectors
].size
= mem_layout
[i
].sector_size
;
131 bank
->sectors
[num_sectors
].is_erased
= -1;
132 bank
->sectors
[num_sectors
].is_protected
= 1;
133 str7x_info
->sector_bank
[num_sectors
] = 0;
134 str7x_info
->sector_bits
[num_sectors
++] = mem_layout
[i
].sector_bit
;
139 for (i
= 8; i
< 10; i
++)
141 bank
->sectors
[num_sectors
].offset
= mem_layout
[i
].sector_start
;
142 bank
->sectors
[num_sectors
].size
= mem_layout
[i
].sector_size
;
143 bank
->sectors
[num_sectors
].is_erased
= -1;
144 bank
->sectors
[num_sectors
].is_protected
= 1;
145 str7x_info
->sector_bank
[num_sectors
] = 1;
146 str7x_info
->sector_bits
[num_sectors
++] = mem_layout
[i
].sector_bit
;
153 /* flash bank str7x <base> <size> 0 0 <str71_variant> <target#>
155 int str7x_flash_bank_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
, struct flash_bank_s
*bank
)
157 str7x_flash_bank_t
*str7x_info
;
161 WARNING("incomplete flash_bank str7x configuration");
162 return ERROR_FLASH_BANK_INVALID
;
165 str7x_info
= malloc(sizeof(str7x_flash_bank_t
));
166 bank
->driver_priv
= str7x_info
;
168 if (strcmp(args
[5], "STR71x") == 0)
170 str7x_info
->bank1
= 1;
171 if (bank
->base
!= 0x40000000)
173 WARNING("overriding flash base address for STR71x device with 0x40000000");
174 bank
->base
= 0x40000000;
177 else if (strcmp(args
[5], "STR73x") == 0)
179 str7x_info
->bank1
= 0;
180 if (bank
->base
!= 0x80000000)
182 WARNING("overriding flash base address for STR73x device with 0x80000000");
183 bank
->base
= 0x80000000;
186 else if (strcmp(args
[5], "STR75x") == 0)
188 str7x_info
->bank1
= 1;
189 if (bank
->base
!= 0x20000000)
191 WARNING("overriding flash base address for STR75x device with 0x20000000");
192 bank
->base
= 0x20000000;
197 ERROR("unknown STR7x variant");
199 return ERROR_FLASH_BANK_INVALID
;
202 str7x_info
->target
= get_target_by_num(strtoul(args
[6], NULL
, 0));
203 if (!str7x_info
->target
)
205 ERROR("no target '%s' configured", args
[6]);
209 str7x_build_block_list(bank
);
211 str7x_info
->write_algorithm
= NULL
;
216 u32
str7x_status(struct flash_bank_s
*bank
)
218 str7x_flash_bank_t
*str7x_info
= bank
->driver_priv
;
219 target_t
*target
= str7x_info
->target
;
222 target_read_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), &retval
);
227 u32
str7x_result(struct flash_bank_s
*bank
)
229 str7x_flash_bank_t
*str7x_info
= bank
->driver_priv
;
230 target_t
*target
= str7x_info
->target
;
233 target_read_u32(target
, str7x_get_flash_adr(bank
, FLASH_ER
), &retval
);
238 int str7x_blank_check(struct flash_bank_s
*bank
, int first
, int last
)
240 str7x_flash_bank_t
*str7x_info
= bank
->driver_priv
;
241 target_t
*target
= str7x_info
->target
;
246 if ((first
< 0) || (last
> bank
->num_sectors
))
247 return ERROR_FLASH_SECTOR_INVALID
;
249 if (str7x_info
->target
->state
!= TARGET_HALTED
)
251 return ERROR_TARGET_NOT_HALTED
;
254 buffer
= malloc(256);
256 for (i
= first
; i
<= last
; i
++)
258 bank
->sectors
[i
].is_erased
= 1;
260 target
->type
->read_memory(target
, bank
->base
+ bank
->sectors
[i
].offset
, 4, 256/4, buffer
);
262 for (nBytes
= 0; nBytes
< 256; nBytes
++)
264 if (buffer
[nBytes
] != 0xFF)
266 bank
->sectors
[i
].is_erased
= 0;
277 int str7x_protect_check(struct flash_bank_s
*bank
)
279 str7x_flash_bank_t
*str7x_info
= bank
->driver_priv
;
280 target_t
*target
= str7x_info
->target
;
285 if (str7x_info
->target
->state
!= TARGET_HALTED
)
287 return ERROR_TARGET_NOT_HALTED
;
290 target_read_u32(target
, str7x_get_flash_adr(bank
, FLASH_NVWPAR
), &retval
);
292 for (i
= 0; i
< bank
->num_sectors
; i
++)
294 if (retval
& str7x_info
->sector_bits
[i
])
295 bank
->sectors
[i
].is_protected
= 0;
297 bank
->sectors
[i
].is_protected
= 1;
303 int str7x_erase(struct flash_bank_s
*bank
, int first
, int last
)
305 str7x_flash_bank_t
*str7x_info
= bank
->driver_priv
;
306 target_t
*target
= str7x_info
->target
;
311 u32 b0_sectors
= 0, b1_sectors
= 0;
313 if (str7x_info
->target
->state
!= TARGET_HALTED
)
315 return ERROR_TARGET_NOT_HALTED
;
318 for (i
= first
; i
<= last
; i
++)
320 if (str7x_info
->sector_bank
[i
] == 0)
321 b0_sectors
|= str7x_info
->sector_bits
[i
];
322 else if (str7x_info
->sector_bank
[i
] == 1)
323 b1_sectors
|= str7x_info
->sector_bits
[i
];
325 ERROR("BUG: str7x_info->sector_bank[i] neither 0 nor 1 (%i)", str7x_info
->sector_bank
[i
]);
330 DEBUG("b0_sectors: 0x%x", b0_sectors
);
332 /* clear FLASH_ER register */
333 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_ER
), 0x0);
336 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
339 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR1
), cmd
);
341 cmd
= FLASH_SER
|FLASH_WMS
;
342 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
344 while (((retval
= str7x_status(bank
)) & (FLASH_BSYA1
|FLASH_BSYA2
))){
348 retval
= str7x_result(bank
);
352 ERROR("error erasing flash bank, FLASH_ER: 0x%x", retval
);
353 return ERROR_FLASH_OPERATION_FAILED
;
359 DEBUG("b1_sectors: 0x%x", b1_sectors
);
361 /* clear FLASH_ER register */
362 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_ER
), 0x0);
365 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
368 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR1
), cmd
);
370 cmd
= FLASH_SER
|FLASH_WMS
;
371 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
373 while (((retval
= str7x_status(bank
)) & (FLASH_BSYA1
|FLASH_BSYA2
))){
377 retval
= str7x_result(bank
);
381 ERROR("error erasing flash bank, FLASH_ER: 0x%x", retval
);
382 return ERROR_FLASH_OPERATION_FAILED
;
386 for (i
= first
; i
<= last
; i
++)
387 bank
->sectors
[i
].is_erased
= 1;
392 int str7x_protect(struct flash_bank_s
*bank
, int set
, int first
, int last
)
394 str7x_flash_bank_t
*str7x_info
= bank
->driver_priv
;
395 target_t
*target
= str7x_info
->target
;
401 if (str7x_info
->target
->state
!= TARGET_HALTED
)
403 return ERROR_TARGET_NOT_HALTED
;
406 protect_blocks
= 0xFFFFFFFF;
410 for (i
= first
; i
<= last
; i
++)
411 protect_blocks
&= ~(str7x_info
->sector_bits
[i
]);
414 /* clear FLASH_ER register */
415 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_ER
), 0x0);
418 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
420 cmd
= str7x_get_flash_adr(bank
, FLASH_NVWPAR
);
421 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_AR
), cmd
);
423 cmd
= protect_blocks
;
424 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_DR0
), cmd
);
426 cmd
= FLASH_SPR
|FLASH_WMS
;
427 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
429 while (((retval
= str7x_status(bank
)) & (FLASH_BSYA1
|FLASH_BSYA2
))){
433 retval
= str7x_result(bank
);
435 DEBUG("retval: 0x%8.8x", retval
);
437 if (retval
& FLASH_ERER
)
438 return ERROR_FLASH_SECTOR_NOT_ERASED
;
439 else if (retval
& FLASH_WPF
)
440 return ERROR_FLASH_OPERATION_FAILED
;
445 int str7x_write_block(struct flash_bank_s
*bank
, u8
*buffer
, u32 offset
, u32 count
)
447 str7x_flash_bank_t
*str7x_info
= bank
->driver_priv
;
448 target_t
*target
= str7x_info
->target
;
449 u32 buffer_size
= 8192;
450 working_area_t
*source
;
451 u32 address
= bank
->base
+ offset
;
452 reg_param_t reg_params
[5];
453 armv4_5_algorithm_t armv4_5_info
;
454 int retval
= ERROR_OK
;
456 u32 str7x_flash_write_code
[] = {
458 0xe3a04201, /* mov r4, #0x10000000 */
459 0xe5824000, /* str r4, [r2, #0x0] */
460 0xe5821010, /* str r1, [r2, #0x10] */
461 0xe4904004, /* ldr r4, [r0], #4 */
462 0xe5824008, /* str r4, [r2, #0x8] */
463 0xe4904004, /* ldr r4, [r0], #4 */
464 0xe582400c, /* str r4, [r2, #0xc] */
465 0xe3a04209, /* mov r4, #0x90000000 */
466 0xe5824000, /* str r4, [r2, #0x0] */
468 0xe5924000, /* ldr r4, [r2, #0x0] */
469 0xe3140016, /* tst r4, #0x16 */
470 0x1afffffc, /* bne busy */
471 0xe5924014, /* ldr r4, [r2, #0x14] */
472 0xe31400ff, /* tst r4, #0xff */
473 0x03140c01, /* tsteq r4, #0x100 */
474 0x1a000002, /* bne exit */
475 0xe2811008, /* add r1, r1, #0x8 */
476 0xe2533001, /* subs r3, r3, #1 */
477 0x1affffec, /* bne write */
479 0xeafffffe, /* b exit */
482 u8 str7x_flash_write_code_buf
[80];
485 /* flash write code */
486 if (!str7x_info
->write_algorithm
)
488 if (target_alloc_working_area(target
, 4 * 20, &str7x_info
->write_algorithm
) != ERROR_OK
)
490 WARNING("no working area available, can't do block memory writes");
491 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE
;
494 /* convert flash writing code into a buffer in target endianness */
495 for (i
= 0; i
< 20; i
++)
496 target_buffer_set_u32(target
, str7x_flash_write_code_buf
+ i
*4, str7x_flash_write_code
[i
]);
498 target_write_buffer(target
, str7x_info
->write_algorithm
->address
, 20 * 4, str7x_flash_write_code_buf
);
502 while (target_alloc_working_area(target
, buffer_size
, &source
) != ERROR_OK
)
505 if (buffer_size
<= 256)
507 /* if we already allocated the writing code, but failed to get a buffer, free the algorithm */
508 if (str7x_info
->write_algorithm
)
509 target_free_working_area(target
, str7x_info
->write_algorithm
);
511 WARNING("no large enough working area available, can't do block memory writes");
512 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE
;
516 armv4_5_info
.common_magic
= ARMV4_5_COMMON_MAGIC
;
517 armv4_5_info
.core_mode
= ARMV4_5_MODE_SVC
;
518 armv4_5_info
.core_state
= ARMV4_5_STATE_ARM
;
520 init_reg_param(®_params
[0], "r0", 32, PARAM_OUT
);
521 init_reg_param(®_params
[1], "r1", 32, PARAM_OUT
);
522 init_reg_param(®_params
[2], "r2", 32, PARAM_OUT
);
523 init_reg_param(®_params
[3], "r3", 32, PARAM_OUT
);
524 init_reg_param(®_params
[4], "r4", 32, PARAM_IN
);
528 u32 thisrun_count
= (count
> (buffer_size
/ 8)) ? (buffer_size
/ 8) : count
;
530 target_write_buffer(target
, source
->address
, thisrun_count
* 8, buffer
);
532 buf_set_u32(reg_params
[0].value
, 0, 32, source
->address
);
533 buf_set_u32(reg_params
[1].value
, 0, 32, address
);
534 buf_set_u32(reg_params
[2].value
, 0, 32, str7x_get_flash_adr(bank
, FLASH_CR0
));
535 buf_set_u32(reg_params
[3].value
, 0, 32, thisrun_count
);
537 if ((retval
= target
->type
->run_algorithm(target
, 0, NULL
, 5, reg_params
, str7x_info
->write_algorithm
->address
, str7x_info
->write_algorithm
->address
+ (19 * 4), 10000, &armv4_5_info
)) != ERROR_OK
)
539 ERROR("error executing str7x flash write algorithm");
543 if (buf_get_u32(reg_params
[4].value
, 0, 32) != 0x00)
545 retval
= ERROR_FLASH_OPERATION_FAILED
;
549 buffer
+= thisrun_count
* 8;
550 address
+= thisrun_count
* 8;
551 count
-= thisrun_count
;
554 target_free_working_area(target
, source
);
556 destroy_reg_param(®_params
[0]);
557 destroy_reg_param(®_params
[1]);
558 destroy_reg_param(®_params
[2]);
559 destroy_reg_param(®_params
[3]);
560 destroy_reg_param(®_params
[4]);
565 int str7x_write(struct flash_bank_s
*bank
, u8
*buffer
, u32 offset
, u32 count
)
567 str7x_flash_bank_t
*str7x_info
= bank
->driver_priv
;
568 target_t
*target
= str7x_info
->target
;
569 u32 dwords_remaining
= (count
/ 8);
570 u32 bytes_remaining
= (count
& 0x00000007);
571 u32 address
= bank
->base
+ offset
;
572 u32 bytes_written
= 0;
575 u32 check_address
= offset
;
578 if (str7x_info
->target
->state
!= TARGET_HALTED
)
580 return ERROR_TARGET_NOT_HALTED
;
585 WARNING("offset 0x%x breaks required 8-byte alignment", offset
);
586 return ERROR_FLASH_DST_BREAKS_ALIGNMENT
;
589 for (i
= 0; i
< bank
->num_sectors
; i
++)
591 u32 sec_start
= bank
->sectors
[i
].offset
;
592 u32 sec_end
= sec_start
+ bank
->sectors
[i
].size
;
594 /* check if destination falls within the current sector */
595 if ((check_address
>= sec_start
) && (check_address
< sec_end
))
597 /* check if destination ends in the current sector */
598 if (offset
+ count
< sec_end
)
599 check_address
= offset
+ count
;
601 check_address
= sec_end
;
605 if (check_address
!= offset
+ count
)
606 return ERROR_FLASH_DST_OUT_OF_BANK
;
608 /* clear FLASH_ER register */
609 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_ER
), 0x0);
611 /* multiple dwords (8-byte) to be programmed? */
612 if (dwords_remaining
> 0)
614 /* try using a block write */
615 if ((retval
= str7x_write_block(bank
, buffer
, offset
, dwords_remaining
)) != ERROR_OK
)
617 if (retval
== ERROR_TARGET_RESOURCE_NOT_AVAILABLE
)
619 /* if block write failed (no sufficient working area),
620 * we use normal (slow) single dword accesses */
621 WARNING("couldn't use block writes, falling back to single memory accesses");
623 else if (retval
== ERROR_FLASH_OPERATION_FAILED
)
625 /* if an error occured, we examine the reason, and quit */
626 retval
= str7x_result(bank
);
628 ERROR("flash writing failed with error code: 0x%x", retval
);
629 return ERROR_FLASH_OPERATION_FAILED
;
634 buffer
+= dwords_remaining
* 8;
635 address
+= dwords_remaining
* 8;
636 dwords_remaining
= 0;
640 while (dwords_remaining
> 0)
644 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
647 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_AR
), address
);
650 target
->type
->write_memory(target
, str7x_get_flash_adr(bank
, FLASH_DR0
), 4, 1, buffer
+ bytes_written
);
654 target
->type
->write_memory(target
, str7x_get_flash_adr(bank
, FLASH_DR1
), 4, 1, buffer
+ bytes_written
);
657 /* start programming cycle */
658 cmd
= FLASH_DWPG
| FLASH_WMS
;
659 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
661 while (((retval
= str7x_status(bank
)) & (FLASH_BSYA1
| FLASH_BSYA2
)))
666 retval
= str7x_result(bank
);
668 if (retval
& FLASH_PGER
)
669 return ERROR_FLASH_OPERATION_FAILED
;
670 else if (retval
& FLASH_WPF
)
671 return ERROR_FLASH_OPERATION_FAILED
;
679 u8 last_dword
[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
682 while(bytes_remaining
> 0)
684 last_dword
[i
++] = *(buffer
+ bytes_written
);
691 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
694 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_AR
), address
);
697 target
->type
->write_memory(target
, str7x_get_flash_adr(bank
, FLASH_DR0
), 4, 1, last_dword
);
701 target
->type
->write_memory(target
, str7x_get_flash_adr(bank
, FLASH_DR1
), 4, 1, last_dword
+ 4);
704 /* start programming cycle */
705 cmd
= FLASH_DWPG
| FLASH_WMS
;
706 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
708 while (((retval
= str7x_status(bank
)) & (FLASH_BSYA1
| FLASH_BSYA2
)))
713 retval
= str7x_result(bank
);
715 if (retval
& FLASH_PGER
)
716 return ERROR_FLASH_OPERATION_FAILED
;
717 else if (retval
& FLASH_WPF
)
718 return ERROR_FLASH_OPERATION_FAILED
;
724 int str7x_probe(struct flash_bank_s
*bank
)
729 int str7x_handle_part_id_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
734 int str7x_erase_check(struct flash_bank_s
*bank
)
736 return str7x_blank_check(bank
, 0, bank
->num_sectors
- 1);
739 int str7x_info(struct flash_bank_s
*bank
, char *buf
, int buf_size
)
741 snprintf(buf
, buf_size
, "str7x flash driver info" );
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)