1 /***************************************************************************
2 * Copyright (C) 2009 by Duane Ellis *
3 * openocd@duaneellis.com *
5 * Copyright (C) 2010 by Olaf Lüke (at91sam3s* support) *
6 * olaf@uni-paderborn.de *
8 * Copyright (C) 2011 by Olivier Schonken, Jim Norris *
9 * (at91sam3x* & at91sam4 support)* *
11 * Copyright (C) 2015 Morgan Quigley *
12 * (atsamv, atsams, and atsame support) *
14 * This program is free software; you can redistribute it and/or modify *
15 * it under the terms of the GNU General public License as published by *
16 * the Free Software Foundation; either version 2 of the License, or *
17 * (at your option) any later version. *
19 * This program is distributed in the hope that it will be useful, *
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
21 * MERCHANTABILITY or FITNESS for A PARTICULAR PURPOSE. See the *
22 * GNU General public License for more details. *
24 ***************************************************************************/
26 /* Some of the the lower level code was based on code supplied by
27 * ATMEL under this copyright. */
29 /* BEGIN ATMEL COPYRIGHT */
30 /* ----------------------------------------------------------------------------
31 * ATMEL Microcontroller Software Support
32 * ----------------------------------------------------------------------------
33 * Copyright (c) 2009, Atmel Corporation
35 * All rights reserved.
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions are met:
40 * - Redistributions of source code must retain the above copyright notice,
41 * this list of conditions and the disclaimer below.
43 * Atmel's name may not be used to endorse or promote products derived from
44 * this software without specific prior written permission.
46 * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
47 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
48 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
49 * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
50 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
51 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
52 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
53 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
54 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
55 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
56 * ----------------------------------------------------------------------------
58 /* END ATMEL COPYRIGHT */
65 #include <helper/time_support.h>
67 #define REG_NAME_WIDTH (12)
69 #define SAMV_EFC_FCMD_GETD (0x0) /* (EFC) Get Flash Descriptor */
70 #define SAMV_EFC_FCMD_WP (0x1) /* (EFC) Write Page */
71 #define SAMV_EFC_FCMD_WPL (0x2) /* (EFC) Write Page and Lock */
72 #define SAMV_EFC_FCMD_EWP (0x3) /* (EFC) Erase Page and Write Page */
73 #define SAMV_EFC_FCMD_EWPL (0x4) /* (EFC) Erase Page, Write Page then Lock*/
74 #define SAMV_EFC_FCMD_EA (0x5) /* (EFC) Erase All */
75 #define SAMV_EFC_FCMD_EPA (0x7) /* (EFC) Erase pages */
76 #define SAMV_EFC_FCMD_SLB (0x8) /* (EFC) Set Lock Bit */
77 #define SAMV_EFC_FCMD_CLB (0x9) /* (EFC) Clear Lock Bit */
78 #define SAMV_EFC_FCMD_GLB (0xA) /* (EFC) Get Lock Bit */
79 #define SAMV_EFC_FCMD_SFB (0xB) /* (EFC) Set Fuse Bit */
80 #define SAMV_EFC_FCMD_CFB (0xC) /* (EFC) Clear Fuse Bit */
81 #define SAMV_EFC_FCMD_GFB (0xD) /* (EFC) Get Fuse Bit */
83 #define OFFSET_EFC_FMR 0
84 #define OFFSET_EFC_FCR 4
85 #define OFFSET_EFC_FSR 8
86 #define OFFSET_EFC_FRR 12
88 #define SAMV_CHIPID_CIDR (0x400E0940)
89 #define SAMV_NUM_GPNVM_BITS 9
90 #define SAMV_CONTROLLER_ADDR (0x400e0c00)
91 #define SAMV_SECTOR_SIZE 16384
92 #define SAMV_PAGE_SIZE 512
93 #define SAMV_FLASH_BASE 0x00400000
95 extern struct flash_driver atsamv_flash
;
97 struct samv_flash_bank
{
100 unsigned gpnvm
[SAMV_NUM_GPNVM_BITS
];
103 /* The actual sector size of the SAMV7 flash memory is 128K bytes.
104 * 16 sectors for a 2048KB device. The lock regions are 16KB per lock
105 * region, with a 2048KB device having 128 lock regions.
106 * For the best results, num_sectors is thus set to the number of lock
107 * regions, and the sector_size set to the lock region size. Page
108 * erases are used to erase 16KB sections when programming */
110 static int samv_efc_get_status(struct target
*target
, uint32_t *v
)
112 int r
= target_read_u32(target
, SAMV_CONTROLLER_ADDR
+ OFFSET_EFC_FSR
, v
);
116 static int samv_efc_get_result(struct target
*target
, uint32_t *v
)
119 int r
= target_read_u32(target
, SAMV_CONTROLLER_ADDR
+ OFFSET_EFC_FRR
, &rv
);
125 static int samv_efc_start_command(struct target
*target
,
126 unsigned command
, unsigned argument
)
129 samv_efc_get_status(target
, &v
);
131 LOG_ERROR("flash controller is not ready");
135 v
= (0x5A << 24) | (argument
<< 8) | command
;
136 LOG_DEBUG("starting flash command: 0x%08x", (unsigned int)(v
));
137 int r
= target_write_u32(target
, SAMV_CONTROLLER_ADDR
+ OFFSET_EFC_FCR
, v
);
139 LOG_DEBUG("write failed");
143 static int samv_efc_perform_command(struct target
*target
,
144 unsigned command
, unsigned argument
, uint32_t *status
)
148 long long ms_now
, ms_end
;
153 r
= samv_efc_start_command(target
, command
, argument
);
157 ms_end
= 10000 + timeval_ms();
160 r
= samv_efc_get_status(target
, &v
);
163 ms_now
= timeval_ms();
164 if (ms_now
> ms_end
) {
166 LOG_ERROR("Command timeout");
169 } while ((v
& 1) == 0);
171 /* if requested, copy the flash controller error bits back to the caller */
177 static int samv_erase_pages(struct target
*target
,
178 int first_page
, int num_pages
, uint32_t *status
)
200 * According to the datasheet FARG[15:2] defines the page from which
201 * the erase will start.This page must be modulo 4, 8, 16 or 32
202 * according to the number of pages to erase. FARG[1:0] defines the
203 * number of pages to be erased. Previously (firstpage << 2) was used
204 * to conform to this, seems it should not be shifted...
206 return samv_efc_perform_command(target
, SAMV_EFC_FCMD_EPA
,
207 first_page
| erase_pages
, status
);
210 static int samv_get_gpnvm(struct target
*target
, unsigned gpnvm
, unsigned *out
)
215 if (gpnvm
>= SAMV_NUM_GPNVM_BITS
) {
216 LOG_ERROR("invalid gpnvm %d, max: %d", gpnvm
, SAMV_NUM_GPNVM_BITS
);
220 r
= samv_efc_perform_command(target
, SAMV_EFC_FCMD_GFB
, 0, NULL
);
222 LOG_ERROR("samv_get_gpnvm failed");
226 r
= samv_efc_get_result(target
, &v
);
229 *out
= (v
>> gpnvm
) & 1;
234 static int samv_clear_gpnvm(struct target
*target
, unsigned gpnvm
)
239 if (gpnvm
>= SAMV_NUM_GPNVM_BITS
) {
240 LOG_ERROR("invalid gpnvm %d, max: %d", gpnvm
, SAMV_NUM_GPNVM_BITS
);
243 r
= samv_get_gpnvm(target
, gpnvm
, &v
);
245 LOG_DEBUG("get gpnvm failed: %d", r
);
248 r
= samv_efc_perform_command(target
, SAMV_EFC_FCMD_CFB
, gpnvm
, NULL
);
249 LOG_DEBUG("clear gpnvm result: %d", r
);
253 static int samv_set_gpnvm(struct target
*target
, unsigned gpnvm
)
257 if (gpnvm
>= SAMV_NUM_GPNVM_BITS
) {
258 LOG_ERROR("invalid gpnvm %d, max: %d", gpnvm
, SAMV_NUM_GPNVM_BITS
);
262 r
= samv_get_gpnvm(target
, gpnvm
, &v
);
266 r
= ERROR_OK
; /* the gpnvm bit is already set */
268 /* we need to set it */
269 r
= samv_efc_perform_command(target
, SAMV_EFC_FCMD_SFB
, gpnvm
, NULL
);
274 static int samv_flash_unlock(struct target
*target
,
275 unsigned start_sector
, unsigned end_sector
)
280 uint32_t pages_per_sector
;
282 /* todo: look into this... i think this should be done on lock regions */
283 pages_per_sector
= SAMV_SECTOR_SIZE
/ SAMV_PAGE_SIZE
;
284 while (start_sector
<= end_sector
) {
285 pg
= start_sector
* pages_per_sector
;
286 r
= samv_efc_perform_command(target
, SAMV_EFC_FCMD_CLB
, pg
, &status
);
294 static int samv_flash_lock(struct target
*target
,
295 unsigned start_sector
, unsigned end_sector
)
299 uint32_t pages_per_sector
;
302 /* todo: look into this... i think this should be done on lock regions */
303 pages_per_sector
= SAMV_SECTOR_SIZE
/ SAMV_PAGE_SIZE
;
304 while (start_sector
<= end_sector
) {
305 pg
= start_sector
* pages_per_sector
;
306 r
= samv_efc_perform_command(target
, SAMV_EFC_FCMD_SLB
, pg
, &status
);
314 static int samv_protect_check(struct flash_bank
*bank
)
319 r
= samv_efc_perform_command(bank
->target
, SAMV_EFC_FCMD_GLB
, 0, NULL
);
321 samv_efc_get_result(bank
->target
, &v
[0]);
322 samv_efc_get_result(bank
->target
, &v
[1]);
323 samv_efc_get_result(bank
->target
, &v
[2]);
324 r
= samv_efc_get_result(bank
->target
, &v
[3]);
329 for (int x
= 0; x
< bank
->num_sectors
; x
++)
330 bank
->sectors
[x
].is_protected
= (!!(v
[x
>> 5] & (1 << (x
% 32))));
334 FLASH_BANK_COMMAND_HANDLER(samv_flash_bank_command
)
336 LOG_INFO("flash bank command");
337 struct samv_flash_bank
*samv_info
;
338 samv_info
= calloc(1, sizeof(struct samv_flash_bank
));
339 bank
->driver_priv
= samv_info
;
343 static int samv_get_device_id(struct flash_bank
*bank
, uint32_t *device_id
)
345 return target_read_u32(bank
->target
, SAMV_CHIPID_CIDR
, device_id
);
348 static int samv_probe(struct flash_bank
*bank
)
351 int r
= samv_get_device_id(bank
, &device_id
);
354 LOG_INFO("device id = 0x%08" PRIx32
"", device_id
);
356 uint8_t eproc
= (device_id
>> 5) & 0x7;
358 LOG_ERROR("unexpected eproc code: %d was expecting 0 (cortex-m7)", eproc
);
362 uint8_t nvm_size_code
= (device_id
>> 8) & 0xf;
363 switch (nvm_size_code
) {
365 bank
->size
= 1024 * 1024;
368 bank
->size
= 2048 * 1024;
371 LOG_ERROR("unrecognized flash size code: %d", nvm_size_code
);
376 struct samv_flash_bank
*samv_info
= bank
->driver_priv
;
377 samv_info
->size_bytes
= bank
->size
;
378 samv_info
->probed
= 1;
380 bank
->base
= SAMV_FLASH_BASE
;
381 bank
->num_sectors
= bank
->size
/ SAMV_SECTOR_SIZE
;
382 bank
->sectors
= calloc(bank
->num_sectors
, sizeof(struct flash_sector
));
383 for (int s
= 0; s
< (int)bank
->num_sectors
; s
++) {
384 bank
->sectors
[s
].size
= SAMV_SECTOR_SIZE
;
385 bank
->sectors
[s
].offset
= s
* SAMV_SECTOR_SIZE
;
386 bank
->sectors
[s
].is_erased
= -1;
387 bank
->sectors
[s
].is_protected
= -1;
390 r
= samv_protect_check(bank
);
397 static int samv_auto_probe(struct flash_bank
*bank
)
399 struct samv_flash_bank
*samv_info
= bank
->driver_priv
;
400 if (samv_info
->probed
)
402 return samv_probe(bank
);
405 static int samv_erase(struct flash_bank
*bank
, int first
, int last
)
407 const int page_count
= 32; /* 32 pages equals 16 KB lock region */
409 if (bank
->target
->state
!= TARGET_HALTED
) {
410 LOG_ERROR("Target not halted");
411 return ERROR_TARGET_NOT_HALTED
;
414 int r
= samv_auto_probe(bank
);
418 /* easy case: we've been requested to erase the entire flash */
419 if ((first
== 0) && ((last
+ 1) == (int)(bank
->num_sectors
)))
420 return samv_efc_perform_command(bank
->target
, SAMV_EFC_FCMD_EA
, 0, NULL
);
422 LOG_INFO("erasing lock regions %d-%d...", first
, last
);
424 for (int i
= first
; i
<= last
; i
++) {
426 r
= samv_erase_pages(bank
->target
, (i
* page_count
), page_count
, &status
);
427 LOG_INFO("erasing lock region %d", i
);
429 LOG_ERROR("error performing erase page @ lock region number %d",
431 if (status
& (1 << 2)) {
432 LOG_ERROR("lock region %d is locked", (unsigned int)(i
));
435 if (status
& (1 << 1)) {
436 LOG_ERROR("flash command error @lock region %d", (unsigned int)(i
));
443 static int samv_protect(struct flash_bank
*bank
, int set
, int first
, int last
)
445 if (bank
->target
->state
!= TARGET_HALTED
) {
446 LOG_ERROR("Target not halted");
447 return ERROR_TARGET_NOT_HALTED
;
452 r
= samv_flash_lock(bank
->target
, (unsigned)(first
), (unsigned)(last
));
454 r
= samv_flash_unlock(bank
->target
, (unsigned)(first
), (unsigned)(last
));
459 static int samv_page_read(struct target
*target
,
460 unsigned page_num
, uint8_t *buf
)
462 uint32_t addr
= SAMV_FLASH_BASE
+ page_num
* SAMV_PAGE_SIZE
;
463 int r
= target_read_memory(target
, addr
, 4, SAMV_PAGE_SIZE
/ 4, buf
);
465 LOG_ERROR("flash program failed to read page @ 0x%08x",
466 (unsigned int)(addr
));
470 static int samv_page_write(struct target
*target
,
471 unsigned pagenum
, const uint8_t *buf
)
474 const uint32_t addr
= SAMV_FLASH_BASE
+ pagenum
* SAMV_PAGE_SIZE
;
477 LOG_DEBUG("write page %u at address 0x%08x", pagenum
, (unsigned int)addr
);
478 r
= target_write_memory(target
, addr
, 4, SAMV_PAGE_SIZE
/ 4, buf
);
480 LOG_ERROR("failed to buffer page at 0x%08x", (unsigned int)addr
);
484 r
= samv_efc_perform_command(target
, SAMV_EFC_FCMD_WP
, pagenum
, &status
);
486 LOG_ERROR("error performing write page at 0x%08x", (unsigned int)addr
);
487 if (status
& (1 << 2)) {
488 LOG_ERROR("page at 0x%08x is locked", (unsigned int)addr
);
491 if (status
& (1 << 1)) {
492 LOG_ERROR("flash command error at 0x%08x", (unsigned int)addr
);
498 static int samv_write(struct flash_bank
*bank
, const uint8_t *buffer
,
499 uint32_t offset
, uint32_t count
)
501 if (bank
->target
->state
!= TARGET_HALTED
) {
502 LOG_ERROR("target not halted");
503 return ERROR_TARGET_NOT_HALTED
;
509 if ((offset
+ count
) > bank
->size
) {
510 LOG_ERROR("flash write error - past end of bank");
511 LOG_ERROR(" offset: 0x%08x, count 0x%08x, bank end: 0x%08x",
512 (unsigned int)(offset
),
513 (unsigned int)(count
),
514 (unsigned int)(bank
->size
));
518 uint8_t pagebuffer
[SAMV_PAGE_SIZE
] = {0};
519 uint32_t page_cur
= offset
/ SAMV_PAGE_SIZE
;
520 uint32_t page_end
= (offset
+ count
- 1) / SAMV_PAGE_SIZE
;
522 LOG_DEBUG("offset: 0x%08x, count: 0x%08x",
523 (unsigned int)(offset
), (unsigned int)(count
));
524 LOG_DEBUG("page start: %d, page end: %d", (int)(page_cur
), (int)(page_end
));
526 /* Special case: all one page */
528 /* (1) non-aligned start */
530 /* (3) non-aligned end. */
533 uint32_t page_offset
;
535 /* handle special case - all one page. */
536 if (page_cur
== page_end
) {
537 LOG_DEBUG("special case, all in one page");
538 r
= samv_page_read(bank
->target
, page_cur
, pagebuffer
);
542 page_offset
= offset
& (SAMV_PAGE_SIZE
-1);
543 memcpy(pagebuffer
+ page_offset
, buffer
, count
);
545 r
= samv_page_write(bank
->target
, page_cur
, pagebuffer
);
551 /* step 1) handle the non-aligned starting address */
552 page_offset
= offset
& (SAMV_PAGE_SIZE
- 1);
554 LOG_DEBUG("non-aligned start");
555 /* read the partial page */
556 r
= samv_page_read(bank
->target
, page_cur
, pagebuffer
);
560 /* over-write with new data */
561 uint32_t n
= SAMV_PAGE_SIZE
- page_offset
;
562 memcpy(pagebuffer
+ page_offset
, buffer
, n
);
564 r
= samv_page_write(bank
->target
, page_cur
, pagebuffer
);
574 /* By checking that offset is correct here, we also fix a clang warning */
575 assert(offset
% SAMV_PAGE_SIZE
== 0);
577 /* step 2) handle the full pages */
578 LOG_DEBUG("full page loop: cur=%d, end=%d, count = 0x%08x",
579 (int)page_cur
, (int)page_end
, (unsigned int)(count
));
581 while ((page_cur
< page_end
) && (count
>= SAMV_PAGE_SIZE
)) {
582 r
= samv_page_write(bank
->target
, page_cur
, buffer
);
585 count
-= SAMV_PAGE_SIZE
;
586 buffer
+= SAMV_PAGE_SIZE
;
590 /* step 3) write final page, if it's partial (otherwise it's already done) */
592 LOG_DEBUG("final partial page, count = 0x%08x", (unsigned int)(count
));
593 /* we have a partial page */
594 r
= samv_page_read(bank
->target
, page_cur
, pagebuffer
);
597 memcpy(pagebuffer
, buffer
, count
); /* data goes at start of page */
598 r
= samv_page_write(bank
->target
, page_cur
, pagebuffer
);
605 static int samv_get_info(struct flash_bank
*bank
, char *buf
, int buf_size
)
607 struct samv_flash_bank
*samv_info
= bank
->driver_priv
;
608 if (!samv_info
->probed
) {
609 int r
= samv_probe(bank
);
613 snprintf(buf
, buf_size
, "Cortex-M7 detected with %d kB flash",
618 COMMAND_HANDLER(samv_handle_gpnvm_command
)
620 struct flash_bank
*bank
= get_flash_bank_by_num_noprobe(0);
623 struct samv_flash_bank
*samv_info
= bank
->driver_priv
;
624 struct target
*target
= bank
->target
;
626 if (target
->state
!= TARGET_HALTED
) {
627 LOG_ERROR("target not halted");
628 return ERROR_TARGET_NOT_HALTED
;
632 if (!samv_info
->probed
) {
633 r
= samv_auto_probe(bank
);
648 if (!strcmp(CMD_ARGV
[0], "show") && !strcmp(CMD_ARGV
[1], "all"))
652 COMMAND_PARSE_NUMBER(u32
, CMD_ARGV
[1], v32
);
657 return ERROR_COMMAND_SYNTAX_ERROR
;
662 if (!strcmp("show", CMD_ARGV
[0])) {
666 for (int x
= 0; x
< SAMV_NUM_GPNVM_BITS
; x
++) {
667 r
= samv_get_gpnvm(target
, x
, &v
);
670 command_print(CMD_CTX
, "samv-gpnvm%u: %u", x
, v
);
674 if ((who
>= 0) && (((unsigned)who
) < SAMV_NUM_GPNVM_BITS
)) {
675 r
= samv_get_gpnvm(target
, who
, &v
);
676 command_print(CMD_CTX
, "samv-gpnvm%u: %u", who
, v
);
679 command_print(CMD_CTX
, "invalid gpnvm: %u", who
);
680 return ERROR_COMMAND_SYNTAX_ERROR
;
685 command_print(CMD_CTX
, "missing gpnvm number");
686 return ERROR_COMMAND_SYNTAX_ERROR
;
689 if (!strcmp("set", CMD_ARGV
[0]))
690 r
= samv_set_gpnvm(target
, who
);
691 else if (!strcmp("clr", CMD_ARGV
[0]) || !strcmp("clear", CMD_ARGV
[0]))
692 r
= samv_clear_gpnvm(target
, who
);
694 command_print(CMD_CTX
, "unknown command: %s", CMD_ARGV
[0]);
695 r
= ERROR_COMMAND_SYNTAX_ERROR
;
700 static const struct command_registration atsamv_exec_command_handlers
[] = {
703 .handler
= samv_handle_gpnvm_command
,
704 .mode
= COMMAND_EXEC
,
705 .usage
= "[('clr'|'set'|'show') bitnum]",
706 .help
= "Without arguments, shows all bits in the gpnvm "
707 "register. Otherwise, clears, sets, or shows one "
708 "General Purpose Non-Volatile Memory (gpnvm) bit.",
710 COMMAND_REGISTRATION_DONE
713 static const struct command_registration atsamv_command_handlers
[] = {
717 .help
= "atsamv flash command group",
719 .chain
= atsamv_exec_command_handlers
,
721 COMMAND_REGISTRATION_DONE
724 struct flash_driver atsamv_flash
= {
726 .commands
= atsamv_command_handlers
,
727 .flash_bank_command
= samv_flash_bank_command
,
729 .protect
= samv_protect
,
731 .read
= default_flash_read
,
733 .auto_probe
= samv_auto_probe
,
734 .erase_check
= default_flash_blank_check
,
735 .protect_check
= samv_protect_check
,
736 .info
= samv_get_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)