1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
5 * Copyright (C) 2008 by Spencer Oliver *
6 * spen@spen-soft.co.uk *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the *
20 * Free Software Foundation, Inc., *
21 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
22 ***************************************************************************/
29 #include <target/armv4_5.h>
30 #include <helper/binarybuffer.h>
31 #include <target/algorithm.h>
34 struct str7x_mem_layout mem_layout_str7bank0
[] = {
35 {0x00000000, 0x02000, 0x01},
36 {0x00002000, 0x02000, 0x02},
37 {0x00004000, 0x02000, 0x04},
38 {0x00006000, 0x02000, 0x08},
39 {0x00008000, 0x08000, 0x10},
40 {0x00010000, 0x10000, 0x20},
41 {0x00020000, 0x10000, 0x40},
42 {0x00030000, 0x10000, 0x80}
45 struct str7x_mem_layout mem_layout_str7bank1
[] = {
46 {0x00000000, 0x02000, 0x10000},
47 {0x00002000, 0x02000, 0x20000}
50 static int str7x_get_flash_adr(struct flash_bank
*bank
, uint32_t reg
)
52 struct str7x_flash_bank
*str7x_info
= bank
->driver_priv
;
53 return (str7x_info
->register_base
| reg
);
56 static int str7x_build_block_list(struct flash_bank
*bank
)
58 struct str7x_flash_bank
*str7x_info
= bank
->driver_priv
;
62 int b0_sectors
= 0, b1_sectors
= 0;
79 LOG_ERROR("BUG: unknown bank->size encountered");
83 num_sectors
= b0_sectors
+ b1_sectors
;
85 bank
->num_sectors
= num_sectors
;
86 bank
->sectors
= malloc(sizeof(struct flash_sector
) * num_sectors
);
87 str7x_info
->sector_bits
= malloc(sizeof(uint32_t) * num_sectors
);
91 for (i
= 0; i
< b0_sectors
; i
++)
93 bank
->sectors
[num_sectors
].offset
= mem_layout_str7bank0
[i
].sector_start
;
94 bank
->sectors
[num_sectors
].size
= mem_layout_str7bank0
[i
].sector_size
;
95 bank
->sectors
[num_sectors
].is_erased
= -1;
96 bank
->sectors
[num_sectors
].is_protected
= 1;
97 str7x_info
->sector_bits
[num_sectors
++] = mem_layout_str7bank0
[i
].sector_bit
;
100 for (i
= 0; i
< b1_sectors
; i
++)
102 bank
->sectors
[num_sectors
].offset
= mem_layout_str7bank1
[i
].sector_start
;
103 bank
->sectors
[num_sectors
].size
= mem_layout_str7bank1
[i
].sector_size
;
104 bank
->sectors
[num_sectors
].is_erased
= -1;
105 bank
->sectors
[num_sectors
].is_protected
= 1;
106 str7x_info
->sector_bits
[num_sectors
++] = mem_layout_str7bank1
[i
].sector_bit
;
112 /* flash bank str7x <base> <size> 0 0 <target#> <str71_variant>
114 FLASH_BANK_COMMAND_HANDLER(str7x_flash_bank_command
)
116 struct str7x_flash_bank
*str7x_info
;
120 LOG_WARNING("incomplete flash_bank str7x configuration");
121 return ERROR_FLASH_BANK_INVALID
;
124 str7x_info
= malloc(sizeof(struct str7x_flash_bank
));
125 bank
->driver_priv
= str7x_info
;
127 /* set default bits for str71x flash */
128 str7x_info
->busy_bits
= (FLASH_LOCK
| FLASH_BSYA1
| FLASH_BSYA0
);
129 str7x_info
->disable_bit
= (1 << 1);
131 if (strcmp(CMD_ARGV
[6], "STR71x") == 0)
133 str7x_info
->register_base
= 0x40100000;
135 else if (strcmp(CMD_ARGV
[6], "STR73x") == 0)
137 str7x_info
->register_base
= 0x80100000;
138 str7x_info
->busy_bits
= (FLASH_LOCK
| FLASH_BSYA0
);
140 else if (strcmp(CMD_ARGV
[6], "STR75x") == 0)
142 str7x_info
->register_base
= 0x20100000;
143 str7x_info
->disable_bit
= (1 << 0);
147 LOG_ERROR("unknown STR7x variant: '%s'", CMD_ARGV
[6]);
149 return ERROR_FLASH_BANK_INVALID
;
152 str7x_build_block_list(bank
);
154 str7x_info
->write_algorithm
= NULL
;
159 static uint32_t str7x_status(struct flash_bank
*bank
)
161 struct target
*target
= bank
->target
;
164 target_read_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), &retval
);
169 static uint32_t str7x_result(struct flash_bank
*bank
)
171 struct target
*target
= bank
->target
;
174 target_read_u32(target
, str7x_get_flash_adr(bank
, FLASH_ER
), &retval
);
179 static int str7x_protect_check(struct flash_bank
*bank
)
181 struct str7x_flash_bank
*str7x_info
= bank
->driver_priv
;
182 struct target
*target
= bank
->target
;
187 if (bank
->target
->state
!= TARGET_HALTED
)
189 LOG_ERROR("Target not halted");
190 return ERROR_TARGET_NOT_HALTED
;
193 target_read_u32(target
, str7x_get_flash_adr(bank
, FLASH_NVWPAR
), &retval
);
195 for (i
= 0; i
< bank
->num_sectors
; i
++)
197 if (retval
& str7x_info
->sector_bits
[i
])
198 bank
->sectors
[i
].is_protected
= 0;
200 bank
->sectors
[i
].is_protected
= 1;
206 static int str7x_erase(struct flash_bank
*bank
, int first
, int last
)
208 struct str7x_flash_bank
*str7x_info
= bank
->driver_priv
;
209 struct target
*target
= bank
->target
;
214 uint32_t sectors
= 0;
216 if (bank
->target
->state
!= TARGET_HALTED
)
218 LOG_ERROR("Target not halted");
219 return ERROR_TARGET_NOT_HALTED
;
222 for (i
= first
; i
<= last
; i
++)
224 sectors
|= str7x_info
->sector_bits
[i
];
227 LOG_DEBUG("sectors: 0x%" PRIx32
"", sectors
);
229 /* clear FLASH_ER register */
230 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_ER
), 0x0);
233 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
236 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR1
), cmd
);
238 cmd
= FLASH_SER
| FLASH_WMS
;
239 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
241 while (((retval
= str7x_status(bank
)) & str7x_info
->busy_bits
)) {
245 retval
= str7x_result(bank
);
249 LOG_ERROR("error erasing flash bank, FLASH_ER: 0x%" PRIx32
"", retval
);
250 return ERROR_FLASH_OPERATION_FAILED
;
253 for (i
= first
; i
<= last
; i
++)
254 bank
->sectors
[i
].is_erased
= 1;
259 static int str7x_protect(struct flash_bank
*bank
, int set
, int first
, int last
)
261 struct str7x_flash_bank
*str7x_info
= bank
->driver_priv
;
262 struct target
*target
= bank
->target
;
266 uint32_t protect_blocks
;
268 if (bank
->target
->state
!= TARGET_HALTED
)
270 LOG_ERROR("Target not halted");
271 return ERROR_TARGET_NOT_HALTED
;
274 protect_blocks
= 0xFFFFFFFF;
278 for (i
= first
; i
<= last
; i
++)
279 protect_blocks
&= ~(str7x_info
->sector_bits
[i
]);
282 /* clear FLASH_ER register */
283 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_ER
), 0x0);
286 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
288 cmd
= str7x_get_flash_adr(bank
, FLASH_NVWPAR
);
289 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_AR
), cmd
);
291 cmd
= protect_blocks
;
292 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_DR0
), cmd
);
294 cmd
= FLASH_SPR
| FLASH_WMS
;
295 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
297 while (((retval
= str7x_status(bank
)) & str7x_info
->busy_bits
)) {
301 retval
= str7x_result(bank
);
303 LOG_DEBUG("retval: 0x%8.8" PRIx32
"", retval
);
305 if (retval
& FLASH_ERER
)
306 return ERROR_FLASH_SECTOR_NOT_ERASED
;
307 else if (retval
& FLASH_WPF
)
308 return ERROR_FLASH_OPERATION_FAILED
;
313 static int str7x_write_block(struct flash_bank
*bank
, uint8_t *buffer
, uint32_t offset
, uint32_t count
)
315 struct str7x_flash_bank
*str7x_info
= bank
->driver_priv
;
316 struct target
*target
= bank
->target
;
317 uint32_t buffer_size
= 8192;
318 struct working_area
*source
;
319 uint32_t address
= bank
->base
+ offset
;
320 struct reg_param reg_params
[6];
321 struct arm_algorithm armv4_5_info
;
322 int retval
= ERROR_OK
;
324 uint32_t str7x_flash_write_code
[] = {
326 0xe3a04201, /* mov r4, #0x10000000 */
327 0xe5824000, /* str r4, [r2, #0x0] */
328 0xe5821010, /* str r1, [r2, #0x10] */
329 0xe4904004, /* ldr r4, [r0], #4 */
330 0xe5824008, /* str r4, [r2, #0x8] */
331 0xe4904004, /* ldr r4, [r0], #4 */
332 0xe582400c, /* str r4, [r2, #0xc] */
333 0xe3a04209, /* mov r4, #0x90000000 */
334 0xe5824000, /* str r4, [r2, #0x0] */
336 0xe5924000, /* ldr r4, [r2, #0x0] */
337 0xe1140005, /* tst r4, r5 */
338 0x1afffffc, /* bne busy */
339 0xe5924014, /* ldr r4, [r2, #0x14] */
340 0xe31400ff, /* tst r4, #0xff */
341 0x03140c01, /* tsteq r4, #0x100 */
342 0x1a000002, /* bne exit */
343 0xe2811008, /* add r1, r1, #0x8 */
344 0xe2533001, /* subs r3, r3, #1 */
345 0x1affffec, /* bne write */
347 0xeafffffe, /* b exit */
350 /* flash write code */
351 if (target_alloc_working_area(target
, 4 * 20, &str7x_info
->write_algorithm
) != ERROR_OK
)
353 LOG_WARNING("no working area available, can't do block memory writes");
354 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE
;
357 target_write_buffer(target
, str7x_info
->write_algorithm
->address
, 20 * 4, (uint8_t*)str7x_flash_write_code
);
360 while (target_alloc_working_area(target
, buffer_size
, &source
) != ERROR_OK
)
363 if (buffer_size
<= 256)
365 /* if we already allocated the writing code, but failed to get a buffer, free the algorithm */
366 if (str7x_info
->write_algorithm
)
367 target_free_working_area(target
, str7x_info
->write_algorithm
);
369 LOG_WARNING("no large enough working area available, can't do block memory writes");
370 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE
;
374 armv4_5_info
.common_magic
= ARM_COMMON_MAGIC
;
375 armv4_5_info
.core_mode
= ARM_MODE_SVC
;
376 armv4_5_info
.core_state
= ARM_STATE_ARM
;
378 init_reg_param(®_params
[0], "r0", 32, PARAM_OUT
);
379 init_reg_param(®_params
[1], "r1", 32, PARAM_OUT
);
380 init_reg_param(®_params
[2], "r2", 32, PARAM_OUT
);
381 init_reg_param(®_params
[3], "r3", 32, PARAM_OUT
);
382 init_reg_param(®_params
[4], "r4", 32, PARAM_IN
);
383 init_reg_param(®_params
[5], "r5", 32, PARAM_OUT
);
387 uint32_t thisrun_count
= (count
> (buffer_size
/ 8)) ? (buffer_size
/ 8) : count
;
389 target_write_buffer(target
, source
->address
, thisrun_count
* 8, buffer
);
391 buf_set_u32(reg_params
[0].value
, 0, 32, source
->address
);
392 buf_set_u32(reg_params
[1].value
, 0, 32, address
);
393 buf_set_u32(reg_params
[2].value
, 0, 32, str7x_get_flash_adr(bank
, FLASH_CR0
));
394 buf_set_u32(reg_params
[3].value
, 0, 32, thisrun_count
);
395 buf_set_u32(reg_params
[5].value
, 0, 32, str7x_info
->busy_bits
);
397 if ((retval
= target_run_algorithm(target
, 0, NULL
, 6, reg_params
, str7x_info
->write_algorithm
->address
, str7x_info
->write_algorithm
->address
+ (19 * 4), 10000, &armv4_5_info
)) != ERROR_OK
)
399 LOG_ERROR("error executing str7x flash write algorithm");
400 retval
= ERROR_FLASH_OPERATION_FAILED
;
404 if (buf_get_u32(reg_params
[4].value
, 0, 32) != 0x00)
406 retval
= ERROR_FLASH_OPERATION_FAILED
;
410 buffer
+= thisrun_count
* 8;
411 address
+= thisrun_count
* 8;
412 count
-= thisrun_count
;
415 target_free_working_area(target
, source
);
416 target_free_working_area(target
, str7x_info
->write_algorithm
);
418 destroy_reg_param(®_params
[0]);
419 destroy_reg_param(®_params
[1]);
420 destroy_reg_param(®_params
[2]);
421 destroy_reg_param(®_params
[3]);
422 destroy_reg_param(®_params
[4]);
423 destroy_reg_param(®_params
[5]);
428 static int str7x_write(struct flash_bank
*bank
, uint8_t *buffer
, uint32_t offset
, uint32_t count
)
430 struct target
*target
= bank
->target
;
431 struct str7x_flash_bank
*str7x_info
= bank
->driver_priv
;
432 uint32_t dwords_remaining
= (count
/ 8);
433 uint32_t bytes_remaining
= (count
& 0x00000007);
434 uint32_t address
= bank
->base
+ offset
;
435 uint32_t bytes_written
= 0;
438 uint32_t check_address
= offset
;
441 if (bank
->target
->state
!= TARGET_HALTED
)
443 LOG_ERROR("Target not halted");
444 return ERROR_TARGET_NOT_HALTED
;
449 LOG_WARNING("offset 0x%" PRIx32
" breaks required 8-byte alignment", offset
);
450 return ERROR_FLASH_DST_BREAKS_ALIGNMENT
;
453 for (i
= 0; i
< bank
->num_sectors
; i
++)
455 uint32_t sec_start
= bank
->sectors
[i
].offset
;
456 uint32_t sec_end
= sec_start
+ bank
->sectors
[i
].size
;
458 /* check if destination falls within the current sector */
459 if ((check_address
>= sec_start
) && (check_address
< sec_end
))
461 /* check if destination ends in the current sector */
462 if (offset
+ count
< sec_end
)
463 check_address
= offset
+ count
;
465 check_address
= sec_end
;
469 if (check_address
!= offset
+ count
)
470 return ERROR_FLASH_DST_OUT_OF_BANK
;
472 /* clear FLASH_ER register */
473 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_ER
), 0x0);
475 /* multiple dwords (8-byte) to be programmed? */
476 if (dwords_remaining
> 0)
478 /* try using a block write */
479 if ((retval
= str7x_write_block(bank
, buffer
, offset
, dwords_remaining
)) != ERROR_OK
)
481 if (retval
== ERROR_TARGET_RESOURCE_NOT_AVAILABLE
)
483 /* if block write failed (no sufficient working area),
484 * we use normal (slow) single dword accesses */
485 LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
487 else if (retval
== ERROR_FLASH_OPERATION_FAILED
)
489 /* if an error occured, we examine the reason, and quit */
490 retval
= str7x_result(bank
);
492 LOG_ERROR("flash writing failed with error code: 0x%x", retval
);
493 return ERROR_FLASH_OPERATION_FAILED
;
498 buffer
+= dwords_remaining
* 8;
499 address
+= dwords_remaining
* 8;
500 dwords_remaining
= 0;
504 while (dwords_remaining
> 0)
508 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
511 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_AR
), address
);
514 target_write_memory(target
, str7x_get_flash_adr(bank
, FLASH_DR0
), 4, 1, buffer
+ bytes_written
);
518 target_write_memory(target
, str7x_get_flash_adr(bank
, FLASH_DR1
), 4, 1, buffer
+ bytes_written
);
521 /* start programming cycle */
522 cmd
= FLASH_DWPG
| FLASH_WMS
;
523 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
525 while (((retval
= str7x_status(bank
)) & str7x_info
->busy_bits
))
530 retval
= str7x_result(bank
);
532 if (retval
& FLASH_PGER
)
533 return ERROR_FLASH_OPERATION_FAILED
;
534 else if (retval
& FLASH_WPF
)
535 return ERROR_FLASH_OPERATION_FAILED
;
543 uint8_t last_dword
[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
546 while (bytes_remaining
> 0)
548 last_dword
[i
++] = *(buffer
+ bytes_written
);
555 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
558 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_AR
), address
);
561 target_write_memory(target
, str7x_get_flash_adr(bank
, FLASH_DR0
), 4, 1, last_dword
);
565 target_write_memory(target
, str7x_get_flash_adr(bank
, FLASH_DR1
), 4, 1, last_dword
+ 4);
568 /* start programming cycle */
569 cmd
= FLASH_DWPG
| FLASH_WMS
;
570 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), cmd
);
572 while (((retval
= str7x_status(bank
)) & str7x_info
->busy_bits
))
577 retval
= str7x_result(bank
);
579 if (retval
& FLASH_PGER
)
580 return ERROR_FLASH_OPERATION_FAILED
;
581 else if (retval
& FLASH_WPF
)
582 return ERROR_FLASH_OPERATION_FAILED
;
588 static int str7x_probe(struct flash_bank
*bank
)
594 COMMAND_HANDLER(str7x_handle_part_id_command
)
600 static int str7x_info(struct flash_bank
*bank
, char *buf
, int buf_size
)
602 snprintf(buf
, buf_size
, "str7x flash driver info");
606 COMMAND_HANDLER(str7x_handle_disable_jtag_command
)
608 struct target
*target
= NULL
;
609 struct str7x_flash_bank
*str7x_info
= NULL
;
612 uint16_t ProtectionLevel
= 0;
613 uint16_t ProtectionRegs
;
617 command_print(CMD_CTX
, "str7x disable_jtag <bank>");
621 struct flash_bank
*bank
;
622 int retval
= CALL_COMMAND_HANDLER(flash_command_get_bank
, 0, &bank
);
623 if (ERROR_OK
!= retval
)
626 str7x_info
= bank
->driver_priv
;
628 target
= bank
->target
;
630 if (target
->state
!= TARGET_HALTED
)
632 LOG_ERROR("Target not halted");
633 return ERROR_TARGET_NOT_HALTED
;
636 /* first we get protection status */
638 target_read_u32(target
, str7x_get_flash_adr(bank
, FLASH_NVAPR0
), ®
);
640 if (!(reg
& str7x_info
->disable_bit
))
645 target_read_u32(target
, str7x_get_flash_adr(bank
, FLASH_NVAPR1
), ®
);
646 ProtectionRegs
= ~(reg
>> 16);
648 while (((ProtectionRegs
) != 0) && (ProtectionLevel
< 16))
650 ProtectionRegs
>>= 1;
654 if (ProtectionLevel
== 0)
656 flash_cmd
= FLASH_SPR
;
657 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), flash_cmd
);
658 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_AR
), 0x4010DFB8);
659 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_DR0
), 0xFFFFFFFD);
660 flash_cmd
= FLASH_SPR
| FLASH_WMS
;
661 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), flash_cmd
);
665 flash_cmd
= FLASH_SPR
;
666 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), flash_cmd
);
667 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_AR
), 0x4010DFBC);
668 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_DR0
), ~(1 << (15 + ProtectionLevel
)));
669 flash_cmd
= FLASH_SPR
| FLASH_WMS
;
670 target_write_u32(target
, str7x_get_flash_adr(bank
, FLASH_CR0
), flash_cmd
);
676 static const struct command_registration str7x_exec_command_handlers
[] = {
678 .name
= "disable_jtag",
679 .handler
= &str7x_handle_disable_jtag_command
,
680 .mode
= COMMAND_EXEC
,
681 .help
= "disable jtag access",
683 COMMAND_REGISTRATION_DONE
685 static const struct command_registration str7x_command_handlers
[] = {
689 .help
= "str7x flash command group",
690 .chain
= str7x_exec_command_handlers
,
692 COMMAND_REGISTRATION_DONE
695 struct flash_driver str7x_flash
= {
697 .commands
= str7x_command_handlers
,
698 .flash_bank_command
= &str7x_flash_bank_command
,
699 .erase
= &str7x_erase
,
700 .protect
= &str7x_protect
,
701 .write
= &str7x_write
,
702 .probe
= &str7x_probe
,
703 .auto_probe
= &str7x_probe
,
704 .erase_check
= &default_flash_blank_check
,
705 .protect_check
= &str7x_protect_check
,
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)