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;
188 ERROR("unknown STR7x variant");
190 return ERROR_FLASH_BANK_INVALID
;
193 str7x_info
->target
= get_target_by_num(strtoul(args
[6], NULL
, 0));
194 if (!str7x_info
->target
)
196 ERROR("no target '%s' configured", args
[6]);
200 str7x_build_block_list(bank
);
202 str7x_info
->write_algorithm
= NULL
;
207 u32
str7x_status(struct flash_bank_s
*bank
)
209 str7x_flash_bank_t
*str7x_info
= bank
->driver_priv
;
210 target_t
*target
= str7x_info
->target
;
213 target_read_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), &retval
);
218 u32
str7x_result(struct flash_bank_s
*bank
)
220 str7x_flash_bank_t
*str7x_info
= bank
->driver_priv
;
221 target_t
*target
= str7x_info
->target
;
224 target_read_u32(target
, str7x_get_flash_adr(bank
, FLASH_ER
), &retval
);
229 int str7x_blank_check(struct flash_bank_s
*bank
, int first
, int last
)
231 str7x_flash_bank_t
*str7x_info
= bank
->driver_priv
;
232 target_t
*target
= str7x_info
->target
;
237 if ((first
< 0) || (last
> bank
->num_sectors
))
238 return ERROR_FLASH_SECTOR_INVALID
;
240 if (str7x_info
->target
->state
!= TARGET_HALTED
)
242 return ERROR_TARGET_NOT_HALTED
;
245 buffer
= malloc(256);
247 for (i
= first
; i
<= last
; i
++)
249 bank
->sectors
[i
].is_erased
= 1;
251 target
->type
->read_memory(target
, bank
->base
+ bank
->sectors
[i
].offset
, 4, 256/4, buffer
);
253 for (nBytes
= 0; nBytes
< 256; nBytes
++)
255 if (buffer
[nBytes
] != 0xFF)
257 bank
->sectors
[i
].is_erased
= 0;
268 int str7x_protect_check(struct flash_bank_s
*bank
)
270 str7x_flash_bank_t
*str7x_info
= bank
->driver_priv
;
271 target_t
*target
= str7x_info
->target
;
276 if (str7x_info
->target
->state
!= TARGET_HALTED
)
278 return ERROR_TARGET_NOT_HALTED
;
281 target_read_u32(target
, str7x_get_flash_adr(bank
, FLASH_NVWPAR
), &retval
);
283 for (i
= 0; i
< bank
->num_sectors
; i
++)
285 if (retval
& str7x_info
->sector_bits
[i
])
286 bank
->sectors
[i
].is_protected
= 0;
288 bank
->sectors
[i
].is_protected
= 1;
294 int str7x_erase(struct flash_bank_s
*bank
, int first
, int last
)
296 str7x_flash_bank_t
*str7x_info
= bank
->driver_priv
;
297 target_t
*target
= str7x_info
->target
;
302 u32 b0_sectors
= 0, b1_sectors
= 0;
304 if (str7x_info
->target
->state
!= TARGET_HALTED
)
306 return ERROR_TARGET_NOT_HALTED
;
309 for (i
= first
; i
<= last
; i
++)
311 if (str7x_info
->sector_bank
[i
] == 0)
312 b0_sectors
|= str7x_info
->sector_bits
[i
];
313 else if (str7x_info
->sector_bank
[i
] == 1)
314 b1_sectors
|= str7x_info
->sector_bits
[i
];
316 ERROR("BUG: str7x_info->sector_bank[i] neither 0 nor 1 (%i)", str7x_info
->sector_bank
[i
]);
321 DEBUG("b0_sectors: 0x%x", b0_sectors
);
323 /* clear FLASH_ER register */
324 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_ER
), 0x0);
327 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
330 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR1
), cmd
);
332 cmd
= FLASH_SER
|FLASH_WMS
;
333 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
335 while (((retval
= str7x_status(bank
)) & (FLASH_BSYA1
|FLASH_BSYA2
))){
339 retval
= str7x_result(bank
);
343 ERROR("error erasing flash bank, FLASH_ER: 0x%x", retval
);
344 return ERROR_FLASH_OPERATION_FAILED
;
350 DEBUG("b1_sectors: 0x%x", b1_sectors
);
352 /* clear FLASH_ER register */
353 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_ER
), 0x0);
356 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
359 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR1
), cmd
);
361 cmd
= FLASH_SER
|FLASH_WMS
;
362 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
364 while (((retval
= str7x_status(bank
)) & (FLASH_BSYA1
|FLASH_BSYA2
))){
368 retval
= str7x_result(bank
);
372 ERROR("error erasing flash bank, FLASH_ER: 0x%x", retval
);
373 return ERROR_FLASH_OPERATION_FAILED
;
377 for (i
= first
; i
<= last
; i
++)
378 bank
->sectors
[i
].is_erased
= 1;
383 int str7x_protect(struct flash_bank_s
*bank
, int set
, int first
, int last
)
385 str7x_flash_bank_t
*str7x_info
= bank
->driver_priv
;
386 target_t
*target
= str7x_info
->target
;
392 if (str7x_info
->target
->state
!= TARGET_HALTED
)
394 return ERROR_TARGET_NOT_HALTED
;
397 protect_blocks
= 0xFFFFFFFF;
401 for (i
= first
; i
<= last
; i
++)
402 protect_blocks
&= ~(str7x_info
->sector_bits
[i
]);
405 /* clear FLASH_ER register */
406 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_ER
), 0x0);
409 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
411 cmd
= str7x_get_flash_adr(bank
, FLASH_NVWPAR
);
412 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_AR
), cmd
);
414 cmd
= protect_blocks
;
415 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_DR0
), cmd
);
417 cmd
= FLASH_SPR
|FLASH_WMS
;
418 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
420 while (((retval
= str7x_status(bank
)) & (FLASH_BSYA1
|FLASH_BSYA2
))){
424 retval
= str7x_result(bank
);
426 DEBUG("retval: 0x%8.8x", retval
);
428 if (retval
& FLASH_ERER
)
429 return ERROR_FLASH_SECTOR_NOT_ERASED
;
430 else if (retval
& FLASH_WPF
)
431 return ERROR_FLASH_OPERATION_FAILED
;
436 int str7x_write_block(struct flash_bank_s
*bank
, u8
*buffer
, u32 offset
, u32 count
)
438 str7x_flash_bank_t
*str7x_info
= bank
->driver_priv
;
439 target_t
*target
= str7x_info
->target
;
440 u32 buffer_size
= 8192;
441 working_area_t
*source
;
442 u32 address
= bank
->base
+ offset
;
443 reg_param_t reg_params
[5];
444 armv4_5_algorithm_t armv4_5_info
;
447 u32 str7x_flash_write_code
[] = {
449 0xe3a04201, /* mov r4, #0x10000000 */
450 0xe5824000, /* str r4, [r2, #0x0] */
451 0xe5821010, /* str r1, [r2, #0x10] */
452 0xe4904004, /* ldr r4, [r0], #4 */
453 0xe5824008, /* str r4, [r2, #0x8] */
454 0xe4904004, /* ldr r4, [r0], #4 */
455 0xe582400c, /* str r4, [r2, #0xc] */
456 0xe3a04209, /* mov r4, #0x90000000 */
457 0xe5824000, /* str r4, [r2, #0x0] */
459 0xe5924000, /* ldr r4, [r2, #0x0] */
460 0xe3140016, /* tst r4, #0x16 */
461 0x1afffffc, /* bne busy */
462 0xe5924014, /* ldr r4, [r2, #0x14] */
463 0xe31400ff, /* tst r4, #0xff */
464 0x03140c01, /* tsteq r4, #0x100 */
465 0x1a000002, /* bne exit */
466 0xe2811008, /* add r1, r1, #0x8 */
467 0xe2533001, /* subs r3, r3, #1 */
468 0x1affffec, /* bne write */
470 0xeafffffe, /* b exit */
473 u8 str7x_flash_write_code_buf
[80];
476 /* flash write code */
477 if (!str7x_info
->write_algorithm
)
479 if (target_alloc_working_area(target
, 4 * 20, &str7x_info
->write_algorithm
) != ERROR_OK
)
481 WARNING("no working area available, can't do block memory writes");
482 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE
;
485 /* convert flash writing code into a buffer in target endianness */
486 for (i
= 0; i
< 20; i
++)
487 target_buffer_set_u32(target
, str7x_flash_write_code_buf
+ i
*4, str7x_flash_write_code
[i
]);
489 target_write_buffer(target
, str7x_info
->write_algorithm
->address
, 20 * 4, str7x_flash_write_code_buf
);
493 while (target_alloc_working_area(target
, buffer_size
, &source
) != ERROR_OK
)
496 if (buffer_size
<= 256)
498 /* if we already allocated the writing code, but failed to get a buffer, free the algorithm */
499 if (str7x_info
->write_algorithm
)
500 target_free_working_area(target
, str7x_info
->write_algorithm
);
502 WARNING("no large enough working area available, can't do block memory writes");
503 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE
;
507 armv4_5_info
.common_magic
= ARMV4_5_COMMON_MAGIC
;
508 armv4_5_info
.core_mode
= ARMV4_5_MODE_SVC
;
509 armv4_5_info
.core_state
= ARMV4_5_STATE_ARM
;
511 init_reg_param(®_params
[0], "r0", 32, PARAM_OUT
);
512 init_reg_param(®_params
[1], "r1", 32, PARAM_OUT
);
513 init_reg_param(®_params
[2], "r2", 32, PARAM_OUT
);
514 init_reg_param(®_params
[3], "r3", 32, PARAM_OUT
);
515 init_reg_param(®_params
[4], "r4", 32, PARAM_IN
);
519 u32 thisrun_count
= (count
> (buffer_size
/ 8)) ? (buffer_size
/ 8) : count
;
521 target_write_buffer(target
, source
->address
, thisrun_count
* 8, buffer
);
523 buf_set_u32(reg_params
[0].value
, 0, 32, source
->address
);
524 buf_set_u32(reg_params
[1].value
, 0, 32, address
);
525 buf_set_u32(reg_params
[2].value
, 0, 32, str7x_get_flash_adr(bank
, FLASH_CR0
));
526 buf_set_u32(reg_params
[3].value
, 0, 32, thisrun_count
);
528 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
)
530 ERROR("error executing str7x flash write algorithm");
531 return ERROR_FLASH_OPERATION_FAILED
;
534 if (buf_get_u32(reg_params
[4].value
, 0, 32) != 0x00)
536 return ERROR_FLASH_OPERATION_FAILED
;
539 buffer
+= thisrun_count
* 8;
540 address
+= thisrun_count
* 8;
541 count
-= thisrun_count
;
544 target_free_working_area(target
, source
);
546 destroy_reg_param(®_params
[0]);
547 destroy_reg_param(®_params
[1]);
548 destroy_reg_param(®_params
[2]);
549 destroy_reg_param(®_params
[3]);
550 destroy_reg_param(®_params
[4]);
555 int str7x_write(struct flash_bank_s
*bank
, u8
*buffer
, u32 offset
, u32 count
)
557 str7x_flash_bank_t
*str7x_info
= bank
->driver_priv
;
558 target_t
*target
= str7x_info
->target
;
559 u32 dwords_remaining
= (count
/ 8);
560 u32 bytes_remaining
= (count
& 0x00000007);
561 u32 address
= bank
->base
+ offset
;
562 u32 bytes_written
= 0;
565 u32 check_address
= offset
;
568 if (str7x_info
->target
->state
!= TARGET_HALTED
)
570 return ERROR_TARGET_NOT_HALTED
;
575 WARNING("offset 0x%x breaks required 8-byte alignment", offset
);
576 return ERROR_FLASH_DST_BREAKS_ALIGNMENT
;
579 for (i
= 0; i
< bank
->num_sectors
; i
++)
581 u32 sec_start
= bank
->sectors
[i
].offset
;
582 u32 sec_end
= sec_start
+ bank
->sectors
[i
].size
;
584 /* check if destination falls within the current sector */
585 if ((check_address
>= sec_start
) && (check_address
< sec_end
))
587 /* check if destination ends in the current sector */
588 if (offset
+ count
< sec_end
)
589 check_address
= offset
+ count
;
591 check_address
= sec_end
;
595 if (check_address
!= offset
+ count
)
596 return ERROR_FLASH_DST_OUT_OF_BANK
;
598 /* clear FLASH_ER register */
599 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_ER
), 0x0);
601 /* multiple dwords (8-byte) to be programmed? */
602 if (dwords_remaining
> 0)
604 /* try using a block write */
605 if ((retval
= str7x_write_block(bank
, buffer
, offset
, dwords_remaining
)) != ERROR_OK
)
607 if (retval
== ERROR_TARGET_RESOURCE_NOT_AVAILABLE
)
609 /* if block write failed (no sufficient working area),
610 * we use normal (slow) single dword accesses */
611 WARNING("couldn't use block writes, falling back to single memory accesses");
613 else if (retval
== ERROR_FLASH_OPERATION_FAILED
)
615 /* if an error occured, we examine the reason, and quit */
616 retval
= str7x_result(bank
);
618 ERROR("flash writing failed with error code: 0x%x", retval
);
619 return ERROR_FLASH_OPERATION_FAILED
;
624 buffer
+= dwords_remaining
* 8;
625 address
+= dwords_remaining
* 8;
626 dwords_remaining
= 0;
630 while (dwords_remaining
> 0)
634 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
637 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_AR
), address
);
640 target
->type
->write_memory(target
, str7x_get_flash_adr(bank
, FLASH_DR0
), 4, 1, buffer
+ bytes_written
);
644 target
->type
->write_memory(target
, str7x_get_flash_adr(bank
, FLASH_DR1
), 4, 1, buffer
+ bytes_written
);
647 /* start programming cycle */
648 cmd
= FLASH_DWPG
| FLASH_WMS
;
649 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
651 while (((retval
= str7x_status(bank
)) & (FLASH_BSYA1
| FLASH_BSYA2
)))
656 retval
= str7x_result(bank
);
658 if (retval
& FLASH_PGER
)
659 return ERROR_FLASH_OPERATION_FAILED
;
660 else if (retval
& FLASH_WPF
)
661 return ERROR_FLASH_OPERATION_FAILED
;
669 u8 last_dword
[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
672 while(bytes_remaining
> 0)
674 last_dword
[i
++] = *(buffer
+ bytes_written
);
681 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
684 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_AR
), address
);
687 target
->type
->write_memory(target
, str7x_get_flash_adr(bank
, FLASH_DR0
), 4, 1, last_dword
);
691 target
->type
->write_memory(target
, str7x_get_flash_adr(bank
, FLASH_DR1
), 4, 1, last_dword
+ 4);
694 /* start programming cycle */
695 cmd
= FLASH_DWPG
| FLASH_WMS
;
696 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
698 while (((retval
= str7x_status(bank
)) & (FLASH_BSYA1
| FLASH_BSYA2
)))
703 retval
= str7x_result(bank
);
705 if (retval
& FLASH_PGER
)
706 return ERROR_FLASH_OPERATION_FAILED
;
707 else if (retval
& FLASH_WPF
)
708 return ERROR_FLASH_OPERATION_FAILED
;
714 int str7x_probe(struct flash_bank_s
*bank
)
719 int str7x_handle_part_id_command(struct command_context_s
*cmd_ctx
, char *cmd
, char **args
, int argc
)
724 int str7x_erase_check(struct flash_bank_s
*bank
)
726 return str7x_blank_check(bank
, 0, bank
->num_sectors
- 1);
729 int str7x_info(struct flash_bank_s
*bank
, char *buf
, int buf_size
)
731 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)