flash: Analog Devices ADuCM360 support
[openocd.git] / src / flash / nor / nuc1x.c
1 /***************************************************************************
2 * Copyright (C) 2011 by James K. Larson *
3 * jlarson@pacifier.com *
4 * *
5 * Copyright (C) 2013 Nemui Trinomius *
6 * nemuisan_kawausogasuki@live.jp *
7 * *
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. *
12 * *
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. *
17 * *
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 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
22 ***************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include "imp.h"
29
30 /* nuc1x register locations */
31 #define NUC1X_SYS_BASE 0x50000000
32 #define NUC1X_SYS_WRPROT 0x50000100
33 #define NUC1X_SYS_IPRSTC1 0x50000008
34
35 #define NUC1X_SYSCLK_BASE 0x50000200
36 #define NUC1X_SYSCLK_PWRCON 0x50000200
37 #define NUC1X_SYSCLK_CLKSEL0 0x50000210
38 #define NUC1X_SYSCLK_CLKDIV 0x50000218
39 #define NUC1X_SYSCLK_AHBCLK 0x50000204
40
41 #define NUC1X_FLASH_BASE 0x5000C000
42 #define NUC1X_FLASH_ISPCON 0x5000C000
43 #define NUC1X_FLASH_ISPCMD 0x5000C00C
44 #define NUC1X_FLASH_ISPADR 0x5000C004
45 #define NUC1X_FLASH_ISPDAT 0x5000C008
46 #define NUC1X_FLASH_ISPTRG 0x5000C010
47
48 /* Command register bits */
49 #define PWRCON_OSC22M (1 << 2)
50 #define PWRCON_XTL12M (1 << 0)
51
52 #define IPRSTC1_CPU_RST (1<<1)
53 #define IPRSTC1_CHIP_RST (1<<0)
54
55 #define AHBCLK_ISP_EN (1 << 2)
56
57 #define ISPCON_ISPEN (1 << 0)
58 #define ISPCON_BS_AP (0 << 1)
59 #define ISPCON_BS_LP (1 << 1)
60 #define ISPCON_CFGUEN (1 << 4)
61 #define ISPCON_LDUEN (1 << 5)
62 #define ISPCON_ISPFF (1 << 6)
63
64 /* isp commands */
65 #define ISPCMD_FCTRL (0x2)
66 #define ISPCMD_FCEN (1 << 4)
67 #define ISPCMD_FOEN (1 << 5)
68 #define ISPCMD_ERASE (0x2 | ISPCMD_FOEN)
69 #define ISPCMD_WRITE (0x1 | ISPCMD_FOEN)
70 #define ISPTRG_ISPGO (1 << 0)
71
72 /* access unlock keys */
73 #define KEY1 0x59
74 #define KEY2 0x16
75 #define KEY3 0x88
76 #define LOCK 0x00
77
78 /* part structs */
79 static const struct {
80 const char *partname;
81 uint32_t partno;
82 uint16_t num_page;
83 }
84 NuMicroParts[] = {
85 /*PART NO*/ /*PART ID*/ /*NUM PAGE*/
86 {"NUC100LC1", 0x00010008, 64},
87 {"NUC100LD1", 0x00010005, 128},
88 {"NUC100LD2", 0x00010004, 128},
89 {"NUC100RC1", 0x00010017, 64},
90 {"NUC100RD1", 0x00010014, 128},
91 {"NUC100RD2", 0x00010013, 128},
92
93 {"NUC100LD3", 0x00010003, 128},
94 {"NUC100LE3", 0x00010000, 256},
95 {"NUC100RD3", 0x00010012, 128},
96 {"NUC100RE3", 0x00010009, 256},
97 {"NUC100VD2", 0x00010022, 128},
98 {"NUC100VD3", 0x00010021, 128},
99 {"NUC100VE3", 0x00010018, 256},
100
101 {"NUC120LC1", 0x00012008, 64},
102 {"NUC120LD1", 0x00012005, 128},
103 {"NUC120LD2", 0x00012004, 128},
104 {"NUC120RC1", 0x00012017, 64},
105 {"NUC120RD1", 0x00012014, 128},
106 {"NUC120RD2", 0x00012013, 128},
107
108 {"NUC120LD3", 0x00012003, 128},
109 {"NUC120LE3", 0x00012000, 256},
110 {"NUC120RD3", 0x00012012, 128},
111 {"NUC120RE3", 0x00012009, 256},
112 {"NUC120VD2", 0x00012022, 128},
113 {"NUC120VD3", 0x00012021, 128},
114 {"NUC120VE3", 0x00012018, 256},
115
116 {"NUC122ZD2", 0x00012231, 128},
117 {"NUC122ZC1", 0x00012235, 64},
118 {"NUC122LD2", 0x00012204, 128},
119 {"NUC122LC1", 0x00012208, 64},
120 {"NUC122RD2", 0x00012213, 128},
121 {"NUC122RC1", 0x00012217, 64},
122
123 {"NUC123ZD4", 0x00012255, 136},
124 {"NUC123ZC2", 0x00012245, 68},
125 {"NUC123LD4", 0x00012235, 136},
126 {"NUC123LC2", 0x00012225, 68},
127 {"NUC123SD4", 0x00012215, 136},
128 {"NUC123SC2", 0x00012205, 68},
129
130 {"NUC130LC1", 0x00013008, 64},
131 {"NUC130LD2", 0x00013004, 128},
132 {"NUC130LE3", 0x00013000, 256},
133 {"NUC130RC1", 0x00013017, 64},
134 {"NUC130RD2", 0x00013013, 128},
135 {"NUC130RE3", 0x00013009, 256},
136 {"NUC130VE3", 0x00013018, 256},
137
138 {"M052L", 0x00005200, 16},
139 {"M052Z", 0x00005203, 16},
140 {"M054L", 0x00005400, 32},
141 {"M054Z", 0x00005403, 32},
142 {"M058L", 0x00005800, 64},
143 {"M058Z", 0x00005803, 64},
144 {"M0516L", 0x00005A00, 128},
145 {"M0516Z", 0x00005A03, 128},
146
147 {"MINI51L", 0x00205100, 8},
148 {"MINI51Z", 0x00205103, 8},
149 {"MINI52L", 0x00205200, 16},
150 {"MINI52Z", 0x00205203, 16},
151 {"MINI54L", 0x00205400, 32},
152 {"MINI54Z", 0x00205403, 32},
153
154 {"UNKNOWN", 0x00000000, 256},
155 };
156
157 static int nuc1x_unlock(struct flash_bank *bank)
158 {
159 uint32_t is_protected;
160 struct target *target = bank->target;
161
162 /* Check to see if Nuc is unlocked or not */
163 int retval = target_read_u32(target, NUC1X_SYS_WRPROT, &is_protected);
164 if (retval != ERROR_OK)
165 return retval;
166
167 LOG_DEBUG("protected = 0x%08" PRIx32 "", is_protected);
168 if (is_protected == 0) { /* means protected - so unlock it */
169 /* unlock flash registers */
170 retval = target_write_u32(target, NUC1X_SYS_WRPROT, KEY1);
171 if (retval != ERROR_OK)
172 return retval;
173 retval = target_write_u32(target, NUC1X_SYS_WRPROT, KEY2);
174 if (retval != ERROR_OK)
175 return retval;
176 retval = target_write_u32(target, NUC1X_SYS_WRPROT, KEY3);
177 if (retval != ERROR_OK)
178 return retval;
179 }
180 /* Check that unlock worked */
181 retval = target_read_u32(target, NUC1X_SYS_WRPROT, &is_protected);
182 if (retval != ERROR_OK)
183 return retval;
184
185 if (is_protected == 1) { /* means unprotected */
186 LOG_DEBUG("protection removed");
187 } else {
188 LOG_DEBUG("still protected!!");
189 }
190
191 return ERROR_OK;
192 }
193
194 static int nuc1x_reset(struct flash_bank *bank)
195 {
196 struct target *target = bank->target;
197
198 nuc1x_unlock(bank);
199
200 int retval = target_write_u32(target, NUC1X_SYS_IPRSTC1, IPRSTC1_CPU_RST);
201 if (retval != ERROR_OK)
202 return retval;
203
204 return ERROR_OK;
205 }
206
207 static int nuc1x_reset2lprom(struct flash_bank *bank)
208 {
209 struct target *target = bank->target;
210
211 nuc1x_unlock(bank);
212 int retval = target_write_u32(target, NUC1X_FLASH_ISPCON, ISPCON_BS_LP);
213 if (retval != ERROR_OK)
214 return retval;
215
216 nuc1x_reset(bank);
217
218 return ERROR_OK;
219 }
220
221 static int nuc1x_init_iap(struct flash_bank *bank)
222 {
223 struct target *target = bank->target;
224
225 if (target->state != TARGET_HALTED) {
226 LOG_ERROR("Target not halted");
227 return ERROR_TARGET_NOT_HALTED;
228 }
229
230 int retval = nuc1x_unlock(bank);
231 if (retval != ERROR_OK)
232 return retval;
233
234 /* enable isp clock and ispen bit */
235 retval = target_write_u32(target, NUC1X_SYSCLK_AHBCLK, AHBCLK_ISP_EN);
236 if (retval != ERROR_OK)
237 return retval;
238
239 retval = target_write_u32(target, NUC1X_FLASH_ISPCON, ISPCON_ISPFF | ISPCON_LDUEN | ISPCON_CFGUEN | ISPCON_ISPEN);
240 if (retval != ERROR_OK)
241 return retval;
242
243 return ERROR_OK;
244 }
245
246 /* Private bank information for nuc1x. */
247 struct nuc1x_flash_bank {
248 struct working_area *write_algorithm;
249 int probed;
250 };
251
252 /* This is the function called in the config file. */
253 FLASH_BANK_COMMAND_HANDLER(nuc1x_flash_bank_command)
254 {
255 struct nuc1x_flash_bank *bank_info;
256
257 if (CMD_ARGC < 6)
258 return ERROR_COMMAND_SYNTAX_ERROR;
259
260 LOG_INFO("add flash_bank nuc1x %s", bank->name);
261
262 bank_info = malloc(sizeof(struct nuc1x_flash_bank));
263
264 memset(bank_info, 0, sizeof(struct nuc1x_flash_bank));
265
266 bank->driver_priv = bank_info;
267
268 return ERROR_OK;
269
270 }
271
272 /* Protection checking - examines the lock bit. */
273 static int nuc1x_protect_check(struct flash_bank *bank)
274 {
275 uint32_t is_protected, set;
276 struct target *target = bank->target;
277 int i;
278
279 if (target->state != TARGET_HALTED) {
280 LOG_ERROR("Target not halted");
281 return ERROR_TARGET_NOT_HALTED;
282 }
283
284 /* Check to see if Nuc is unlocked or not */
285 int retval = target_read_u32(target, NUC1X_SYS_WRPROT, &is_protected);
286 if (retval != ERROR_OK)
287 return retval;
288
289 LOG_INFO("is_protected = 0x%08" PRIx32 "", is_protected);
290 if (is_protected == 0) { /* means protected */
291 set = 1;
292 } else {
293 set = 0;
294 }
295 for (i = 0; i < bank->num_sectors; i++)
296 bank->sectors[i].is_protected = set;
297
298 return ERROR_OK;
299 }
300
301 static int nuc1x_erase(struct flash_bank *bank, int first, int last)
302 {
303 struct target *target = bank->target;
304 uint32_t timeout, status;
305 int i;
306
307 if (bank->target->state != TARGET_HALTED) {
308 LOG_ERROR("Target not halted");
309 return ERROR_TARGET_NOT_HALTED;
310 }
311
312 LOG_INFO("Nuvoton NUC: Sector Erase ... (%d to %d)", first, last);
313
314 int retval = nuc1x_reset2lprom(bank);
315 if (retval != ERROR_OK)
316 return retval;
317
318 retval = nuc1x_init_iap(bank);
319 if (retval != ERROR_OK)
320 return retval;
321
322 retval = nuc1x_unlock(bank);
323 if (retval != ERROR_OK)
324 return retval;
325
326 retval = target_write_u32(target, NUC1X_FLASH_ISPCMD, ISPCMD_ERASE);
327 if (retval != ERROR_OK)
328 return retval;
329
330 for (i = first; i <= last; i++) {
331 LOG_DEBUG("erasing sector %d at address 0x%" PRIx32 "", i, bank->base + bank->sectors[i].offset);
332 retval = target_write_u32(target, NUC1X_FLASH_ISPADR, bank->base + bank->sectors[i].offset);
333 if (retval != ERROR_OK)
334 return retval;
335 retval = target_write_u32(target, NUC1X_FLASH_ISPTRG, ISPTRG_ISPGO); /* This is the only bit available */
336 if (retval != ERROR_OK)
337 return retval;
338
339 /* wait for busy to clear - check the GO flag */
340 timeout = 100;
341 for (;;) {
342 retval = target_read_u32(target, NUC1X_FLASH_ISPTRG, &status);
343 if (retval != ERROR_OK)
344 return retval;
345 LOG_DEBUG("status: 0x%" PRIx32 "", status);
346 if (status == 0)
347 break;
348 if (timeout-- <= 0) {
349 LOG_DEBUG("timed out waiting for flash");
350 return ERROR_FAIL;
351 }
352 busy_sleep(1); /* can use busy sleep for short times. */
353 }
354
355 /* check for failure */
356 retval = target_read_u32(target, NUC1X_FLASH_ISPCON, &status);
357 if (retval != ERROR_OK)
358 return retval;
359 if ((status & ISPCON_ISPFF) != 0) {
360 LOG_DEBUG("failure: 0x%" PRIx32 "", status);
361 /* if bit is set, then must write to it to clear it. */
362 retval = target_write_u32(target, NUC1X_FLASH_ISPCON, ISPCON_ISPFF);
363 if (retval != ERROR_OK)
364 return retval;
365 } else {
366 bank->sectors[i].is_erased = 1;
367 }
368 }
369
370 retval = nuc1x_reset(bank);
371 if (retval != ERROR_OK)
372 return retval;
373
374 /* done, */
375 LOG_DEBUG("Erase done.");
376
377 return ERROR_OK;
378 }
379
380 /* The write routine stub. */
381 static int nuc1x_write(struct flash_bank *bank, const uint8_t *buffer,
382 uint32_t offset, uint32_t count)
383 {
384 struct target *target = bank->target;
385 uint32_t i, timeout, status;
386
387 if (bank->target->state != TARGET_HALTED) {
388 LOG_ERROR("Target not halted");
389 return ERROR_TARGET_NOT_HALTED;
390 }
391
392 LOG_INFO("Nuvoton NUC: FLASH Write ...");
393
394 int retval = nuc1x_reset2lprom(bank);
395 if (retval != ERROR_OK)
396 return retval;
397
398 retval = nuc1x_init_iap(bank);
399 if (retval != ERROR_OK)
400 return retval;
401
402 retval = nuc1x_unlock(bank);
403 if (retval != ERROR_OK)
404 return retval;
405
406 retval = target_write_u32(target, NUC1X_FLASH_ISPCMD, ISPCMD_WRITE);
407 if (retval != ERROR_OK)
408 return retval;
409
410 /* program command */
411 for (i = 0; i < count; i += 4) {
412
413 LOG_DEBUG("write longword @ %08" PRIX32, (uint32_t)(offset + i));
414
415 uint8_t padding[4] = {0xff, 0xff, 0xff, 0xff};
416 memcpy(padding, buffer + i, MIN(4, count-i));
417
418 retval = target_write_u32(target, NUC1X_FLASH_ISPADR, bank->base + offset + i);
419 if (retval != ERROR_OK)
420 return retval;
421 retval = target_write_memory(target, NUC1X_FLASH_ISPDAT, 4, 1, padding);
422 if (retval != ERROR_OK)
423 return retval;
424 retval = target_write_u32(target, NUC1X_FLASH_ISPTRG, ISPTRG_ISPGO);
425 if (retval != ERROR_OK)
426 return retval;
427
428 /* wait for busy to clear - check the GO flag */
429 timeout = 100;
430 for (;;) {
431 retval = target_read_u32(target, NUC1X_FLASH_ISPTRG, &status);
432 if (retval != ERROR_OK)
433 return retval;
434 LOG_DEBUG("status: 0x%" PRIx32 "", status);
435 if (status == 0)
436 break;
437 if (timeout-- <= 0) {
438 LOG_DEBUG("timed out waiting for flash");
439 return ERROR_FAIL;
440 }
441 busy_sleep(1); /* can use busy sleep for short times. */
442 }
443
444 /* check for failure */
445 retval = target_read_u32(target, NUC1X_FLASH_ISPCON, &status);
446 if (retval != ERROR_OK)
447 return retval;
448 if ((status & ISPCON_ISPFF) != 0) {
449 LOG_DEBUG("failure: 0x%" PRIx32 "", status);
450 /* if bit is set, then must write to it to clear it. */
451 retval = target_write_u32(target, NUC1X_FLASH_ISPCON, ISPCON_ISPFF);
452 if (retval != ERROR_OK)
453 return retval;
454 } else {
455 LOG_DEBUG("Write OK");
456 }
457 }
458
459 retval = nuc1x_reset(bank);
460 if (retval != ERROR_OK)
461 return retval;
462
463 /* done, */
464 LOG_DEBUG("Write done.");
465
466 return ERROR_OK;
467 }
468
469 /* The probe routine for the nuc. Only recognizes the nuc120 right now. */
470 static int nuc1x_probe(struct flash_bank *bank)
471 {
472 struct target *target = bank->target;
473 struct nuc1x_flash_bank *nuc1x_info = bank->driver_priv;
474 int i;
475 uint16_t num_pages;
476 uint32_t device_id;
477 int page_size;
478 uint32_t base_address = 0x00000000;
479
480 nuc1x_info->probed = 0;
481
482 /* read nuc1x device id register */
483 int retval = target_read_u32(target, 0x50000000, &device_id);
484 if (retval != ERROR_OK)
485 return retval;
486
487 page_size = 512; /* all nuc parts has 512 byte per sector */
488
489 /* search part numbers */
490 for (i = 0; NuMicroParts[i].partno; i++) {
491 if (NuMicroParts[i].partno == (device_id & 0x0FFFFFFF)) {
492 num_pages = NuMicroParts[i].num_page;
493 break;
494 }
495 }
496 if (!(NuMicroParts[i].partno == 0x00000000)) {
497 LOG_INFO("DeviceID : 0x%08" PRIx32 "", device_id);
498 LOG_INFO("Detect %s%cN!", NuMicroParts[i].partname, (char)('A'+(device_id>>28)));
499 } else {
500 LOG_INFO("No NUC Device Detected...");
501 return ERROR_FAIL;
502 }
503
504 if (bank->sectors) {
505 free(bank->sectors);
506 bank->sectors = NULL;
507 }
508
509 bank->base = base_address;
510 bank->size = (num_pages * page_size);
511 bank->num_sectors = num_pages;
512 bank->sectors = malloc(sizeof(struct flash_sector) * num_pages);
513
514 for (i = 0; i < num_pages; i++) {
515 bank->sectors[i].offset = i * page_size;
516 bank->sectors[i].size = page_size;
517 bank->sectors[i].is_erased = -1;
518 bank->sectors[i].is_protected = 1;
519 }
520
521 nuc1x_info->probed = 1;
522
523 LOG_DEBUG("Nuvoton NUC: Probed ...");
524
525 return ERROR_OK;
526 }
527
528 /* Standard approach to autoprobing. */
529 static int nuc1x_auto_probe(struct flash_bank *bank)
530 {
531 struct nuc1x_flash_bank *nuc1x_info = bank->driver_priv;
532 if (nuc1x_info->probed)
533 return ERROR_OK;
534 return nuc1x_probe(bank);
535 }
536
537 /* Info doesn't really add much, but works correctly. */
538 static int get_nuc1x_info(struct flash_bank *bank, char *buf, int buf_size)
539 {
540 struct target *target = bank->target;
541 uint32_t i, device_id;
542
543 /* read nuc1x device id register */
544 int retval = target_read_u32(target, 0x50000000, &device_id);
545 if (retval != ERROR_OK)
546 return retval;
547
548 /* search part numbers */
549 for (i = 0; NuMicroParts[i].partno; i++) {
550 if (NuMicroParts[i].partno == (device_id & 0x0FFFFFFF))
551 break;
552 }
553 if (!(NuMicroParts[i].partno == 0x00000000)) {
554 LOG_INFO("DeviceID : 0x%08" PRIx32 "", device_id);
555 LOG_INFO("Detect %s%cN!", NuMicroParts[i].partname, (char)('A'+(device_id>>28)));
556 } else {
557 LOG_INFO("No NUC Device Detected...");
558 return ERROR_FAIL;
559 }
560
561 return ERROR_OK;
562 }
563
564 /* The nuc120 doesn't support mass erase, so this will probably be removed soon.
565 * The structure is left for now until I am sure I don't want to add any custom
566 * commands. */
567 static int nuc1x_mass_erase(struct flash_bank *bank)
568 {
569 struct target *target = bank->target;
570 int retval = ERROR_OK;
571
572 if (target->state != TARGET_HALTED) {
573 LOG_ERROR("Target not halted");
574 return ERROR_TARGET_NOT_HALTED;
575 }
576
577 LOG_INFO("Nuvoton NUC: Chip Erase ... (may take several seconds)");
578
579 return retval;
580 }
581
582 COMMAND_HANDLER(nuc1x_handle_mass_erase_command)
583 {
584 int i; /* for erasing sectors */
585 if (CMD_ARGC < 1) {
586 command_print(CMD_CTX, "nuc1x mass_erase <bank>");
587 return ERROR_OK;
588 }
589
590 struct flash_bank *bank;
591 int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
592 if (ERROR_OK != retval)
593 return retval;
594
595 retval = nuc1x_mass_erase(bank);
596 if (retval == ERROR_OK) {
597 /* set all sectors as erased */
598 for (i = 0; i < bank->num_sectors; i++)
599 bank->sectors[i].is_erased = 1;
600
601 command_print(CMD_CTX, "nuc1x mass erase complete");
602 } else
603 command_print(CMD_CTX, "nuc1x mass erase failed");
604
605 return retval;
606 }
607
608 static const struct command_registration nuc1x_exec_command_handlers[] = {
609 {
610 .name = "mass_erase",
611 .handler = nuc1x_handle_mass_erase_command,
612 .mode = COMMAND_EXEC,
613 .usage = "bank_id",
614 .help = "Erase entire Flash device.",
615 },
616 COMMAND_REGISTRATION_DONE
617 };
618
619 static const struct command_registration nuc1x_command_handlers[] = {
620 {
621 .name = "nuc1x",
622 .mode = COMMAND_ANY,
623 .help = "nuc1x Flash command group",
624 .chain = nuc1x_exec_command_handlers,
625 },
626 COMMAND_REGISTRATION_DONE
627 };
628 struct flash_driver nuc1x_flash = {
629 .name = "nuc1x",
630 .commands = nuc1x_command_handlers,
631 .flash_bank_command = nuc1x_flash_bank_command,
632 .erase = nuc1x_erase,
633 .write = nuc1x_write,
634 .read = default_flash_read,
635 .probe = nuc1x_probe,
636 .auto_probe = nuc1x_auto_probe,
637 .erase_check = default_flash_blank_check,
638 .protect_check = nuc1x_protect_check,
639 .info = get_nuc1x_info,
640 };

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)