jtag: linuxgpiod: drop extra parenthesis
[openocd.git] / src / flash / nor / str9x.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2
3 /***************************************************************************
4 * Copyright (C) 2005 by Dominic Rath *
5 * Dominic.Rath@gmx.de *
6 * *
7 * Copyright (C) 2008 by Spencer Oliver *
8 * spen@spen-soft.co.uk *
9 *
10 * Copyright (C) 2008 by Oyvind Harboe *
11 * oyvind.harboe@zylin.com *
12 ***************************************************************************/
13
14 #ifdef HAVE_CONFIG_H
15 #include "config.h"
16 #endif
17
18 #include "imp.h"
19 #include <target/arm966e.h>
20 #include <target/algorithm.h>
21
22 /* Flash registers */
23
24 #define FLASH_BBSR 0x54000000 /* Boot Bank Size Register */
25 #define FLASH_NBBSR 0x54000004 /* Non-Boot Bank Size Register */
26 #define FLASH_BBADR 0x5400000C /* Boot Bank Base Address Register */
27 #define FLASH_NBBADR 0x54000010 /* Non-Boot Bank Base Address Register */
28 #define FLASH_CR 0x54000018 /* Control Register */
29 #define FLASH_SR 0x5400001C /* Status Register */
30 #define FLASH_BCE5ADDR 0x54000020 /* BC Fifth Entry Target Address Register */
31
32 struct str9x_flash_bank {
33 uint32_t *sector_bits;
34 int variant;
35 int bank1;
36 };
37
38 enum str9x_status_codes {
39 STR9X_CMD_SUCCESS = 0,
40 STR9X_INVALID_COMMAND = 1,
41 STR9X_SRC_ADDR_ERROR = 2,
42 STR9X_DST_ADDR_ERROR = 3,
43 STR9X_SRC_ADDR_NOT_MAPPED = 4,
44 STR9X_DST_ADDR_NOT_MAPPED = 5,
45 STR9X_COUNT_ERROR = 6,
46 STR9X_INVALID_SECTOR = 7,
47 STR9X_SECTOR_NOT_BLANK = 8,
48 STR9X_SECTOR_NOT_PREPARED = 9,
49 STR9X_COMPARE_ERROR = 10,
50 STR9X_BUSY = 11
51 };
52
53 static uint32_t bank1start = 0x00080000;
54
55 static int str9x_build_block_list(struct flash_bank *bank)
56 {
57 struct str9x_flash_bank *str9x_info = bank->driver_priv;
58
59 int i;
60 unsigned int num_sectors;
61 int b0_sectors = 0, b1_sectors = 0;
62 uint32_t offset = 0;
63
64 /* set if we have large flash str9 */
65 str9x_info->variant = 0;
66 str9x_info->bank1 = 0;
67
68 switch (bank->size) {
69 case (256 * 1024):
70 b0_sectors = 4;
71 break;
72 case (512 * 1024):
73 b0_sectors = 8;
74 break;
75 case (1024 * 1024):
76 bank1start = 0x00100000;
77 str9x_info->variant = 1;
78 b0_sectors = 16;
79 break;
80 case (2048 * 1024):
81 bank1start = 0x00200000;
82 str9x_info->variant = 1;
83 b0_sectors = 32;
84 break;
85 case (128 * 1024):
86 str9x_info->variant = 1;
87 str9x_info->bank1 = 1;
88 b1_sectors = 8;
89 bank1start = bank->base;
90 break;
91 case (32 * 1024):
92 str9x_info->bank1 = 1;
93 b1_sectors = 4;
94 bank1start = bank->base;
95 break;
96 default:
97 LOG_ERROR("BUG: unknown bank->size encountered");
98 exit(-1);
99 }
100
101 num_sectors = b0_sectors + b1_sectors;
102
103 bank->num_sectors = num_sectors;
104 bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors);
105 str9x_info->sector_bits = malloc(sizeof(uint32_t) * num_sectors);
106
107 num_sectors = 0;
108
109 for (i = 0; i < b0_sectors; i++) {
110 bank->sectors[num_sectors].offset = offset;
111 bank->sectors[num_sectors].size = 0x10000;
112 offset += bank->sectors[i].size;
113 bank->sectors[num_sectors].is_erased = -1;
114 bank->sectors[num_sectors].is_protected = 1;
115 str9x_info->sector_bits[num_sectors++] = (1 << i);
116 }
117
118 for (i = 0; i < b1_sectors; i++) {
119 bank->sectors[num_sectors].offset = offset;
120 bank->sectors[num_sectors].size = str9x_info->variant == 0 ? 0x2000 : 0x4000;
121 offset += bank->sectors[i].size;
122 bank->sectors[num_sectors].is_erased = -1;
123 bank->sectors[num_sectors].is_protected = 1;
124 if (str9x_info->variant)
125 str9x_info->sector_bits[num_sectors++] = (1 << i);
126 else
127 str9x_info->sector_bits[num_sectors++] = (1 << (i + 8));
128 }
129
130 return ERROR_OK;
131 }
132
133 /* flash bank str9x <base> <size> 0 0 <target#>
134 */
135 FLASH_BANK_COMMAND_HANDLER(str9x_flash_bank_command)
136 {
137 struct str9x_flash_bank *str9x_info;
138
139 if (CMD_ARGC < 6)
140 return ERROR_COMMAND_SYNTAX_ERROR;
141
142 str9x_info = malloc(sizeof(struct str9x_flash_bank));
143 bank->driver_priv = str9x_info;
144
145 str9x_build_block_list(bank);
146
147 return ERROR_OK;
148 }
149
150 static int str9x_protect_check(struct flash_bank *bank)
151 {
152 int retval;
153 struct str9x_flash_bank *str9x_info = bank->driver_priv;
154 struct target *target = bank->target;
155
156 uint32_t adr;
157 uint32_t status = 0;
158 uint16_t hstatus = 0;
159
160 if (bank->target->state != TARGET_HALTED) {
161 LOG_ERROR("Target not halted");
162 return ERROR_TARGET_NOT_HALTED;
163 }
164
165 /* read level one protection */
166
167 if (str9x_info->variant) {
168 if (str9x_info->bank1) {
169 adr = bank1start + 0x18;
170 retval = target_write_u16(target, adr, 0x90);
171 if (retval != ERROR_OK)
172 return retval;
173 retval = target_read_u16(target, adr, &hstatus);
174 if (retval != ERROR_OK)
175 return retval;
176 status = hstatus;
177 } else {
178 adr = bank1start + 0x14;
179 retval = target_write_u16(target, adr, 0x90);
180 if (retval != ERROR_OK)
181 return retval;
182 retval = target_read_u32(target, adr, &status);
183 if (retval != ERROR_OK)
184 return retval;
185 }
186 } else {
187 adr = bank1start + 0x10;
188 retval = target_write_u16(target, adr, 0x90);
189 if (retval != ERROR_OK)
190 return retval;
191 retval = target_read_u16(target, adr, &hstatus);
192 if (retval != ERROR_OK)
193 return retval;
194 status = hstatus;
195 }
196
197 /* read array command */
198 retval = target_write_u16(target, adr, 0xFF);
199 if (retval != ERROR_OK)
200 return retval;
201
202 for (unsigned int i = 0; i < bank->num_sectors; i++) {
203 if (status & str9x_info->sector_bits[i])
204 bank->sectors[i].is_protected = 1;
205 else
206 bank->sectors[i].is_protected = 0;
207 }
208
209 return ERROR_OK;
210 }
211
212 static int str9x_erase(struct flash_bank *bank, unsigned int first,
213 unsigned int last)
214 {
215 struct target *target = bank->target;
216 uint32_t adr;
217 uint8_t status;
218 uint8_t erase_cmd;
219 int total_timeout;
220
221 if (bank->target->state != TARGET_HALTED) {
222 LOG_ERROR("Target not halted");
223 return ERROR_TARGET_NOT_HALTED;
224 }
225
226 /* Check if we can erase whole bank */
227 if ((first == 0) && (last == (bank->num_sectors - 1))) {
228 /* Optimize to run erase bank command instead of sector */
229 erase_cmd = 0x80;
230 /* Add timeout duration since erase bank takes more time */
231 total_timeout = 1000 * bank->num_sectors;
232 } else {
233 /* Erase sector command */
234 erase_cmd = 0x20;
235 total_timeout = 1000;
236 }
237
238 /* this is so the compiler can *know* */
239 assert(total_timeout > 0);
240
241 for (unsigned int i = first; i <= last; i++) {
242 int retval;
243 adr = bank->base + bank->sectors[i].offset;
244
245 /* erase sectors or block */
246 retval = target_write_u16(target, adr, erase_cmd);
247 if (retval != ERROR_OK)
248 return retval;
249 retval = target_write_u16(target, adr, 0xD0);
250 if (retval != ERROR_OK)
251 return retval;
252
253 /* get status */
254 retval = target_write_u16(target, adr, 0x70);
255 if (retval != ERROR_OK)
256 return retval;
257
258 int timeout;
259 for (timeout = 0; timeout < total_timeout; timeout++) {
260 retval = target_read_u8(target, adr, &status);
261 if (retval != ERROR_OK)
262 return retval;
263 if (status & 0x80)
264 break;
265 alive_sleep(1);
266 }
267 if (timeout == total_timeout) {
268 LOG_ERROR("erase timed out");
269 return ERROR_FAIL;
270 }
271
272 /* clear status, also clear read array */
273 retval = target_write_u16(target, adr, 0x50);
274 if (retval != ERROR_OK)
275 return retval;
276
277 /* read array command */
278 retval = target_write_u16(target, adr, 0xFF);
279 if (retval != ERROR_OK)
280 return retval;
281
282 if (status & 0x22) {
283 LOG_ERROR("error erasing flash bank, status: 0x%x", status);
284 return ERROR_FLASH_OPERATION_FAILED;
285 }
286
287 /* If we ran erase bank command, we are finished */
288 if (erase_cmd == 0x80)
289 break;
290 }
291
292 return ERROR_OK;
293 }
294
295 static int str9x_protect(struct flash_bank *bank, int set, unsigned int first,
296 unsigned int last)
297 {
298 struct target *target = bank->target;
299 uint32_t adr;
300 uint8_t status;
301
302 if (bank->target->state != TARGET_HALTED) {
303 LOG_ERROR("Target not halted");
304 return ERROR_TARGET_NOT_HALTED;
305 }
306
307 for (unsigned int i = first; i <= last; i++) {
308 /* Level One Protection */
309
310 adr = bank->base + bank->sectors[i].offset;
311
312 target_write_u16(target, adr, 0x60);
313 if (set)
314 target_write_u16(target, adr, 0x01);
315 else
316 target_write_u16(target, adr, 0xD0);
317
318 /* query status */
319 target_read_u8(target, adr, &status);
320
321 /* clear status, also clear read array */
322 target_write_u16(target, adr, 0x50);
323
324 /* read array command */
325 target_write_u16(target, adr, 0xFF);
326 }
327
328 return ERROR_OK;
329 }
330
331 static int str9x_write_block(struct flash_bank *bank,
332 const uint8_t *buffer, uint32_t offset, uint32_t count)
333 {
334 struct target *target = bank->target;
335 uint32_t buffer_size = 32768;
336 struct working_area *write_algorithm;
337 struct working_area *source;
338 uint32_t address = bank->base + offset;
339 struct reg_param reg_params[4];
340 struct arm_algorithm arm_algo;
341 int retval = ERROR_OK;
342
343 /* see contrib/loaders/flash/str9x.s for src */
344
345 static const uint32_t str9x_flash_write_code[] = {
346 /* write: */
347 0xe3c14003, /* bic r4, r1, #3 */
348 0xe3a03040, /* mov r3, #0x40 */
349 0xe1c430b0, /* strh r3, [r4, #0] */
350 0xe0d030b2, /* ldrh r3, [r0], #2 */
351 0xe0c130b2, /* strh r3, [r1], #2 */
352 0xe3a03070, /* mov r3, #0x70 */
353 0xe1c430b0, /* strh r3, [r4, #0] */
354 /* busy: */
355 0xe5d43000, /* ldrb r3, [r4, #0] */
356 0xe3130080, /* tst r3, #0x80 */
357 0x0afffffc, /* beq busy */
358 0xe3a05050, /* mov r5, #0x50 */
359 0xe1c450b0, /* strh r5, [r4, #0] */
360 0xe3a050ff, /* mov r5, #0xFF */
361 0xe1c450b0, /* strh r5, [r4, #0] */
362 0xe3130012, /* tst r3, #0x12 */
363 0x1a000001, /* bne exit */
364 0xe2522001, /* subs r2, r2, #1 */
365 0x1affffed, /* bne write */
366 /* exit: */
367 0xe1200070, /* bkpt #0 */
368 };
369
370 /* flash write code */
371 if (target_alloc_working_area(target, sizeof(str9x_flash_write_code),
372 &write_algorithm) != ERROR_OK) {
373 LOG_WARNING("no working area available, can't do block memory writes");
374 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
375 }
376
377 uint8_t code[sizeof(str9x_flash_write_code)];
378 target_buffer_set_u32_array(target, code, ARRAY_SIZE(str9x_flash_write_code),
379 str9x_flash_write_code);
380 target_write_buffer(target, write_algorithm->address, sizeof(code), code);
381
382 /* memory buffer */
383 while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
384 buffer_size /= 2;
385 if (buffer_size <= 256) {
386 /* we already allocated the writing code, but failed to get a
387 * buffer, free the algorithm */
388 target_free_working_area(target, write_algorithm);
389
390 LOG_WARNING("no large enough working area available, can't do block memory writes");
391 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
392 }
393 }
394
395 arm_algo.common_magic = ARM_COMMON_MAGIC;
396 arm_algo.core_mode = ARM_MODE_SVC;
397 arm_algo.core_state = ARM_STATE_ARM;
398
399 init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
400 init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
401 init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
402 init_reg_param(&reg_params[3], "r3", 32, PARAM_IN);
403
404 while (count > 0) {
405 uint32_t thisrun_count = (count > (buffer_size / 2)) ? (buffer_size / 2) : count;
406
407 target_write_buffer(target, source->address, thisrun_count * 2, buffer);
408
409 buf_set_u32(reg_params[0].value, 0, 32, source->address);
410 buf_set_u32(reg_params[1].value, 0, 32, address);
411 buf_set_u32(reg_params[2].value, 0, 32, thisrun_count);
412
413 retval = target_run_algorithm(target, 0, NULL, 4, reg_params,
414 write_algorithm->address,
415 0, 10000, &arm_algo);
416 if (retval != ERROR_OK) {
417 LOG_ERROR("error executing str9x flash write algorithm");
418 retval = ERROR_FLASH_OPERATION_FAILED;
419 break;
420 }
421
422 if (buf_get_u32(reg_params[3].value, 0, 32) != 0x80) {
423 retval = ERROR_FLASH_OPERATION_FAILED;
424 break;
425 }
426
427 buffer += thisrun_count * 2;
428 address += thisrun_count * 2;
429 count -= thisrun_count;
430 }
431
432 target_free_working_area(target, source);
433 target_free_working_area(target, write_algorithm);
434
435 destroy_reg_param(&reg_params[0]);
436 destroy_reg_param(&reg_params[1]);
437 destroy_reg_param(&reg_params[2]);
438 destroy_reg_param(&reg_params[3]);
439
440 return retval;
441 }
442
443 static int str9x_write(struct flash_bank *bank,
444 const uint8_t *buffer, uint32_t offset, uint32_t count)
445 {
446 struct target *target = bank->target;
447 uint32_t words_remaining = (count / 2);
448 uint32_t bytes_remaining = (count & 0x00000001);
449 uint32_t address = bank->base + offset;
450 uint32_t bytes_written = 0;
451 uint8_t status;
452 int retval;
453 uint32_t check_address = offset;
454 uint32_t bank_adr;
455
456 if (bank->target->state != TARGET_HALTED) {
457 LOG_ERROR("Target not halted");
458 return ERROR_TARGET_NOT_HALTED;
459 }
460
461 if (offset & 0x1) {
462 LOG_WARNING("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset);
463 return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
464 }
465
466 for (unsigned int i = 0; i < bank->num_sectors; i++) {
467 uint32_t sec_start = bank->sectors[i].offset;
468 uint32_t sec_end = sec_start + bank->sectors[i].size;
469
470 /* check if destination falls within the current sector */
471 if ((check_address >= sec_start) && (check_address < sec_end)) {
472 /* check if destination ends in the current sector */
473 if (offset + count < sec_end)
474 check_address = offset + count;
475 else
476 check_address = sec_end;
477 }
478 }
479
480 if (check_address != offset + count)
481 return ERROR_FLASH_DST_OUT_OF_BANK;
482
483 /* multiple half words (2-byte) to be programmed? */
484 if (words_remaining > 0) {
485 /* try using a block write */
486 retval = str9x_write_block(bank, buffer, offset, words_remaining);
487 if (retval != ERROR_OK) {
488 if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
489 /* if block write failed (no sufficient working area),
490 * we use normal (slow) single dword accesses */
491 LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
492 } else if (retval == ERROR_FLASH_OPERATION_FAILED) {
493 LOG_ERROR("flash writing failed");
494 return ERROR_FLASH_OPERATION_FAILED;
495 }
496 } else {
497 buffer += words_remaining * 2;
498 address += words_remaining * 2;
499 words_remaining = 0;
500 }
501 }
502
503 while (words_remaining > 0) {
504 bank_adr = address & ~0x03;
505
506 /* write data command */
507 target_write_u16(target, bank_adr, 0x40);
508 target_write_memory(target, address, 2, 1, buffer + bytes_written);
509
510 /* get status command */
511 target_write_u16(target, bank_adr, 0x70);
512
513 int timeout;
514 for (timeout = 0; timeout < 1000; timeout++) {
515 target_read_u8(target, bank_adr, &status);
516 if (status & 0x80)
517 break;
518 alive_sleep(1);
519 }
520 if (timeout == 1000) {
521 LOG_ERROR("write timed out");
522 return ERROR_FAIL;
523 }
524
525 /* clear status reg and read array */
526 target_write_u16(target, bank_adr, 0x50);
527 target_write_u16(target, bank_adr, 0xFF);
528
529 if (status & 0x10)
530 return ERROR_FLASH_OPERATION_FAILED;
531 else if (status & 0x02)
532 return ERROR_FLASH_OPERATION_FAILED;
533
534 bytes_written += 2;
535 words_remaining--;
536 address += 2;
537 }
538
539 if (bytes_remaining) {
540 uint8_t last_halfword[2] = {0xff, 0xff};
541
542 /* copy the last remaining bytes into the write buffer */
543 memcpy(last_halfword, buffer+bytes_written, bytes_remaining);
544
545 bank_adr = address & ~0x03;
546
547 /* write data command */
548 target_write_u16(target, bank_adr, 0x40);
549 target_write_memory(target, address, 2, 1, last_halfword);
550
551 /* query status command */
552 target_write_u16(target, bank_adr, 0x70);
553
554 int timeout;
555 for (timeout = 0; timeout < 1000; timeout++) {
556 target_read_u8(target, bank_adr, &status);
557 if (status & 0x80)
558 break;
559 alive_sleep(1);
560 }
561 if (timeout == 1000) {
562 LOG_ERROR("write timed out");
563 return ERROR_FAIL;
564 }
565
566 /* clear status reg and read array */
567 target_write_u16(target, bank_adr, 0x50);
568 target_write_u16(target, bank_adr, 0xFF);
569
570 if (status & 0x10)
571 return ERROR_FLASH_OPERATION_FAILED;
572 else if (status & 0x02)
573 return ERROR_FLASH_OPERATION_FAILED;
574 }
575
576 return ERROR_OK;
577 }
578
579 static int str9x_probe(struct flash_bank *bank)
580 {
581 return ERROR_OK;
582 }
583
584 #if 0
585 COMMAND_HANDLER(str9x_handle_part_id_command)
586 {
587 return ERROR_OK;
588 }
589 #endif
590
591 COMMAND_HANDLER(str9x_handle_flash_config_command)
592 {
593 struct target *target = NULL;
594
595 if (CMD_ARGC < 5)
596 return ERROR_COMMAND_SYNTAX_ERROR;
597
598 struct flash_bank *bank;
599 int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
600 if (retval != ERROR_OK)
601 return retval;
602
603 uint32_t bbsr, nbbsr, bbadr, nbbadr;
604 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], bbsr);
605 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], nbbsr);
606 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], bbadr);
607 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[4], nbbadr);
608
609 target = bank->target;
610
611 if (bank->target->state != TARGET_HALTED) {
612 LOG_ERROR("Target not halted");
613 return ERROR_TARGET_NOT_HALTED;
614 }
615
616 /* config flash controller */
617 target_write_u32(target, FLASH_BBSR, bbsr);
618 target_write_u32(target, FLASH_NBBSR, nbbsr);
619 target_write_u32(target, FLASH_BBADR, bbadr >> 2);
620 target_write_u32(target, FLASH_NBBADR, nbbadr >> 2);
621
622 /* set bit 18 instruction TCM order as per flash programming manual */
623 arm966e_write_cp15(target, 62, 0x40000);
624
625 /* enable flash bank 1 */
626 target_write_u32(target, FLASH_CR, 0x18);
627 return ERROR_OK;
628 }
629
630 static const struct command_registration str9x_config_command_handlers[] = {
631 {
632 .name = "flash_config",
633 .handler = str9x_handle_flash_config_command,
634 .mode = COMMAND_EXEC,
635 .help = "Configure str9x flash controller, prior to "
636 "programming the flash.",
637 .usage = "bank_id BBSR NBBSR BBADR NBBADR",
638 },
639 COMMAND_REGISTRATION_DONE
640 };
641
642 static const struct command_registration str9x_command_handlers[] = {
643 {
644 .name = "str9x",
645 .mode = COMMAND_ANY,
646 .help = "str9x flash command group",
647 .usage = "",
648 .chain = str9x_config_command_handlers,
649 },
650 COMMAND_REGISTRATION_DONE
651 };
652
653 const struct flash_driver str9x_flash = {
654 .name = "str9x",
655 .commands = str9x_command_handlers,
656 .flash_bank_command = str9x_flash_bank_command,
657 .erase = str9x_erase,
658 .protect = str9x_protect,
659 .write = str9x_write,
660 .read = default_flash_read,
661 .probe = str9x_probe,
662 .auto_probe = str9x_probe,
663 .erase_check = default_flash_blank_check,
664 .protect_check = str9x_protect_check,
665 .free_driver_priv = default_flash_free_driver_priv,
666 };

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)