FLASH/NOR: Remove useless file lpc288x.h
[openocd.git] / src / flash / nor / stellaris.c
1 /***************************************************************************
2 * Copyright (C) 2006 by Magnus Lundin *
3 * lundin@mlu.mine.nu *
4 * *
5 * Copyright (C) 2008 by Spencer Oliver *
6 * spen@spen-soft.co.uk *
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 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
22 ***************************************************************************/
23
24 /***************************************************************************
25 * STELLARIS flash is tested on LM3S811, LM3S6965, LM3s3748, more.
26 ***************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include "imp.h"
32 #include "stellaris.h"
33 #include <target/algorithm.h>
34 #include <target/armv7m.h>
35
36
37 #define DID0_VER(did0) ((did0 >> 28)&0x07)
38
39 static void stellaris_read_clock_info(struct flash_bank *bank);
40 static int stellaris_mass_erase(struct flash_bank *bank);
41
42 static struct {
43 uint32_t partno;
44 char *partname;
45 } StellarisParts[] =
46 {
47 {0x0001,"LM3S101"},
48 {0x0002,"LM3S102"},
49 {0x0019,"LM3S300"},
50 {0x0011,"LM3S301"},
51 {0x001A,"LM3S308"},
52 {0x0012,"LM3S310"},
53 {0x0013,"LM3S315"},
54 {0x0014,"LM3S316"},
55 {0x0017,"LM3S317"},
56 {0x0015,"LM3S328"},
57 {0x002A,"LM3S600"},
58 {0x0021,"LM3S601"},
59 {0x002B,"LM3S608"},
60 {0x0022,"LM3S610"},
61 {0x0023,"LM3S611"},
62 {0x0024,"LM3S612"},
63 {0x0025,"LM3S613"},
64 {0x0026,"LM3S615"},
65 {0x0028,"LM3S617"},
66 {0x0029,"LM3S618"},
67 {0x0027,"LM3S628"},
68 {0x0038,"LM3S800"},
69 {0x0031,"LM3S801"},
70 {0x0039,"LM3S808"},
71 {0x0032,"LM3S811"},
72 {0x0033,"LM3S812"},
73 {0x0034,"LM3S815"},
74 {0x0036,"LM3S817"},
75 {0x0037,"LM3S818"},
76 {0x0035,"LM3S828"},
77 {0x10BF,"LM3S1110"},
78 {0x10C3,"LM3S1133"},
79 {0x10C5,"LM3S1138"},
80 {0x10C1,"LM3S1150"},
81 {0x10C4,"LM3S1162"},
82 {0x10C2,"LM3S1165"},
83 {0x10C6,"LM3S1332"},
84 {0x10BC,"LM3S1435"},
85 {0x10BA,"LM3S1439"},
86 {0x10BB,"LM3S1512"},
87 {0x10C7,"LM3S1538"},
88 {0x10DB,"LM3S1601"},
89 {0x1006,"LM3S1607"},
90 {0x10DA,"LM3S1608"},
91 {0x10C0,"LM3S1620"},
92 {0x1003,"LM3S1625"},
93 {0x1004,"LM3S1626"},
94 {0x1005,"LM3S1627"},
95 {0x10B3,"LM3S1635"},
96 {0x10BD,"LM3S1637"},
97 {0x10B9,"LM3S1751"},
98 {0x1010,"LM3S1776"},
99 {0x1016,"LM3S1811"},
100 {0x103D,"LM3S1816"},
101 {0x10B4,"LM3S1850"},
102 {0x10DD,"LM3S1911"},
103 {0x10DC,"LM3S1918"},
104 {0x10B7,"LM3S1937"},
105 {0x10BE,"LM3S1958"},
106 {0x10B5,"LM3S1960"},
107 {0x10B8,"LM3S1968"},
108 {0x100F,"LM3S1J11"},
109 {0x103C,"LM3S1J16"},
110 {0x100E,"LM3S1N11"},
111 {0x103B,"LM3S1N16"},
112 {0x1030,"LM3S1W16"},
113 {0x102F,"LM3S1Z16"},
114 {0x1051,"LM3S2110"},
115 {0x1084,"LM3S2139"},
116 {0x1039,"LM3S2276"},
117 {0x10A2,"LM3S2410"},
118 {0x1059,"LM3S2412"},
119 {0x1056,"LM3S2432"},
120 {0x105A,"LM3S2533"},
121 {0x10E1,"LM3S2601"},
122 {0x10E0,"LM3S2608"},
123 {0x1033,"LM3S2616"},
124 {0x1057,"LM3S2620"},
125 {0x1085,"LM3S2637"},
126 {0x1053,"LM3S2651"},
127 {0x1080,"LM3S2671"},
128 {0x1050,"LM3S2678"},
129 {0x10A4,"LM3S2730"},
130 {0x1052,"LM3S2739"},
131 {0x103A,"LM3S2776"},
132 {0x106D,"LM3S2793"},
133 {0x10E3,"LM3S2911"},
134 {0x10E2,"LM3S2918"},
135 {0x1054,"LM3S2939"},
136 {0x108F,"LM3S2948"},
137 {0x1058,"LM3S2950"},
138 {0x1055,"LM3S2965"},
139 {0x106C,"LM3S2B93"},
140 {0x1043,"LM3S3651"},
141 {0x1044,"LM3S3739"},
142 {0x1049,"LM3S3748"},
143 {0x1045,"LM3S3749"},
144 {0x1042,"LM3S3826"},
145 {0x1041,"LM3S3J26"},
146 {0x1040,"LM3S3N26"},
147 {0x103F,"LM3S3W26"},
148 {0x103E,"LM3S3Z26"},
149 {0x1081,"LM3S5632"},
150 {0x100C,"LM3S5651"},
151 {0x108A,"LM3S5652"},
152 {0x104D,"LM3S5656"},
153 {0x1091,"LM3S5662"},
154 {0x1096,"LM3S5732"},
155 {0x1097,"LM3S5737"},
156 {0x10A0,"LM3S5739"},
157 {0x1099,"LM3S5747"},
158 {0x10A7,"LM3S5749"},
159 {0x109A,"LM3S5752"},
160 {0x109C,"LM3S5762"},
161 {0x1069,"LM3S5791"},
162 {0x100B,"LM3S5951"},
163 {0x104E,"LM3S5956"},
164 {0x1068,"LM3S5B91"},
165 {0x1009,"LM3S5K31"},
166 {0x104A,"LM3S5K36"},
167 {0x100A,"LM3S5P31"},
168 {0x1048,"LM3S5P36"},
169 {0x100D,"LM3S5P51"},
170 {0x104C,"LM3S5P56"},
171 {0x1007,"LM3S5R31"},
172 {0x104B,"LM3S5R36"},
173 {0x1047,"LM3S5T36"},
174 {0x1046,"LM3S5Y36"},
175 {0x10A1,"LM3S6100"},
176 {0x1074,"LM3S6110"},
177 {0x10A5,"LM3S6420"},
178 {0x1082,"LM3S6422"},
179 {0x1075,"LM3S6432"},
180 {0x1076,"LM3S6537"},
181 {0x1071,"LM3S6610"},
182 {0x10E7,"LM3S6611"},
183 {0x10E6,"LM3S6618"},
184 {0x1083,"LM3S6633"},
185 {0x108B,"LM3S6637"},
186 {0x10A3,"LM3S6730"},
187 {0x1077,"LM3S6753"},
188 {0x10E9,"LM3S6911"},
189 {0x10E8,"LM3S6918"},
190 {0x1089,"LM3S6938"},
191 {0x1072,"LM3S6950"},
192 {0x1078,"LM3S6952"},
193 {0x1073,"LM3S6965"},
194 {0x1064,"LM3S8530"},
195 {0x108E,"LM3S8538"},
196 {0x1061,"LM3S8630"},
197 {0x1063,"LM3S8730"},
198 {0x108D,"LM3S8733"},
199 {0x1086,"LM3S8738"},
200 {0x1065,"LM3S8930"},
201 {0x108C,"LM3S8933"},
202 {0x1088,"LM3S8938"},
203 {0x10A6,"LM3S8962"},
204 {0x1062,"LM3S8970"},
205 {0x10D7,"LM3S8971"},
206 {0x1067,"LM3S9790"},
207 {0x106B,"LM3S9792"},
208 {0x1020,"LM3S9997"},
209 {0x1066,"LM3S9B90"},
210 {0x106A,"LM3S9B92"},
211 {0x106E,"LM3S9B95"},
212 {0x106F,"LM3S9B96"},
213 {0x1018,"LM3S9L97"},
214 {0,"Unknown part"}
215 };
216
217 static char * StellarisClassname[5] =
218 {
219 "Sandstorm",
220 "Fury",
221 "Unknown",
222 "DustDevil",
223 "Tempest"
224 };
225
226 /***************************************************************************
227 * openocd command interface *
228 ***************************************************************************/
229
230 /* flash_bank stellaris <base> <size> 0 0 <target#>
231 */
232 FLASH_BANK_COMMAND_HANDLER(stellaris_flash_bank_command)
233 {
234 struct stellaris_flash_bank *stellaris_info;
235
236 if (CMD_ARGC < 6)
237 {
238 LOG_WARNING("incomplete flash_bank stellaris configuration");
239 return ERROR_FLASH_BANK_INVALID;
240 }
241
242 stellaris_info = calloc(sizeof(struct stellaris_flash_bank), 1);
243 bank->base = 0x0;
244 bank->driver_priv = stellaris_info;
245
246 stellaris_info->target_name = "Unknown target";
247
248 /* part wasn't probed for info yet */
249 stellaris_info->did1 = 0;
250
251 /* TODO Specify the main crystal speed in kHz using an optional
252 * argument; ditto, the speed of an external oscillator used
253 * instead of a crystal. Avoid programming flash using IOSC.
254 */
255 return ERROR_OK;
256 }
257
258 static int get_stellaris_info(struct flash_bank *bank, char *buf, int buf_size)
259 {
260 int printed, device_class;
261 struct stellaris_flash_bank *stellaris_info = bank->driver_priv;
262
263 if (stellaris_info->did1 == 0)
264 return ERROR_FLASH_BANK_NOT_PROBED;
265
266 /* Read main and master clock freqency register */
267 stellaris_read_clock_info(bank);
268
269 if (DID0_VER(stellaris_info->did0) > 0)
270 {
271 device_class = (stellaris_info->did0 >> 16) & 0xFF;
272 }
273 else
274 {
275 device_class = 0;
276 }
277 printed = snprintf(buf,
278 buf_size,
279 "\nTI/LMI Stellaris information: Chip is "
280 "class %i (%s) %s rev %c%i\n",
281 device_class,
282 StellarisClassname[device_class],
283 stellaris_info->target_name,
284 (int)('A' + ((stellaris_info->did0 >> 8) & 0xFF)),
285 (int)((stellaris_info->did0) & 0xFF));
286 buf += printed;
287 buf_size -= printed;
288
289 printed = snprintf(buf,
290 buf_size,
291 "did1: 0x%8.8" PRIx32 ", arch: 0x%4.4" PRIx32
292 ", eproc: %s, ramsize: %ik, flashsize: %ik\n",
293 stellaris_info->did1,
294 stellaris_info->did1,
295 "ARMv7M",
296 (int)((1 + ((stellaris_info->dc0 >> 16) & 0xFFFF))/4),
297 (int)((1 + (stellaris_info->dc0 & 0xFFFF))*2));
298 buf += printed;
299 buf_size -= printed;
300
301 printed = snprintf(buf,
302 buf_size,
303 "master clock: %ikHz%s, "
304 "rcc is 0x%" PRIx32 ", rcc2 is 0x%" PRIx32 "\n",
305 (int)(stellaris_info->mck_freq / 1000),
306 stellaris_info->mck_desc,
307 stellaris_info->rcc,
308 stellaris_info->rcc2);
309 buf += printed;
310 buf_size -= printed;
311
312 if (stellaris_info->num_lockbits > 0)
313 {
314 printed = snprintf(buf,
315 buf_size,
316 "pagesize: %" PRIi32 ", pages: %d, "
317 "lockbits: %i, pages per lockbit: %i\n",
318 stellaris_info->pagesize,
319 (unsigned) stellaris_info->num_pages,
320 stellaris_info->num_lockbits,
321 (unsigned) stellaris_info->pages_in_lockregion);
322 buf += printed;
323 buf_size -= printed;
324 }
325 return ERROR_OK;
326 }
327
328 /***************************************************************************
329 * chip identification and status *
330 ***************************************************************************/
331
332 /* Set the flash timimg register to match current clocking */
333 static void stellaris_set_flash_timing(struct flash_bank *bank)
334 {
335 struct stellaris_flash_bank *stellaris_info = bank->driver_priv;
336 struct target *target = bank->target;
337 uint32_t usecrl = (stellaris_info->mck_freq/1000000ul-1);
338
339 LOG_DEBUG("usecrl = %i",(int)(usecrl));
340 target_write_u32(target, SCB_BASE | USECRL, usecrl);
341 }
342
343 static const unsigned rcc_xtal[32] = {
344 [0x00] = 1000000, /* no pll */
345 [0x01] = 1843200, /* no pll */
346 [0x02] = 2000000, /* no pll */
347 [0x03] = 2457600, /* no pll */
348
349 [0x04] = 3579545,
350 [0x05] = 3686400,
351 [0x06] = 4000000, /* usb */
352 [0x07] = 4096000,
353
354 [0x08] = 4915200,
355 [0x09] = 5000000, /* usb */
356 [0x0a] = 5120000,
357 [0x0b] = 6000000, /* (reset) usb */
358
359 [0x0c] = 6144000,
360 [0x0d] = 7372800,
361 [0x0e] = 8000000, /* usb */
362 [0x0f] = 8192000,
363
364 /* parts before DustDevil use just 4 bits for xtal spec */
365
366 [0x10] = 10000000, /* usb */
367 [0x11] = 12000000, /* usb */
368 [0x12] = 12288000,
369 [0x13] = 13560000,
370
371 [0x14] = 14318180,
372 [0x15] = 16000000, /* usb */
373 [0x16] = 16384000,
374 };
375
376 /** Read clock configuration and set stellaris_info->usec_clocks. */
377 static void stellaris_read_clock_info(struct flash_bank *bank)
378 {
379 struct stellaris_flash_bank *stellaris_info = bank->driver_priv;
380 struct target *target = bank->target;
381 uint32_t rcc, rcc2, pllcfg, sysdiv, usesysdiv, bypass, oscsrc;
382 unsigned xtal;
383 unsigned long mainfreq;
384
385 target_read_u32(target, SCB_BASE | RCC, &rcc);
386 LOG_DEBUG("Stellaris RCC %" PRIx32 "", rcc);
387
388 target_read_u32(target, SCB_BASE | RCC2, &rcc2);
389 LOG_DEBUG("Stellaris RCC2 %" PRIx32 "", rcc);
390
391 target_read_u32(target, SCB_BASE | PLLCFG, &pllcfg);
392 LOG_DEBUG("Stellaris PLLCFG %" PRIx32 "", pllcfg);
393
394 stellaris_info->rcc = rcc;
395 stellaris_info->rcc = rcc2;
396
397 sysdiv = (rcc >> 23) & 0xF;
398 usesysdiv = (rcc >> 22) & 0x1;
399 bypass = (rcc >> 11) & 0x1;
400 oscsrc = (rcc >> 4) & 0x3;
401 xtal = (rcc >> 6) & stellaris_info->xtal_mask;
402
403 /* NOTE: post-Sandstorm parts have RCC2 which may override
404 * parts of RCC ... with more sysdiv options, option for
405 * 32768 Hz mainfreq, PLL controls. On Sandstorm it reads
406 * as zero, so the "use RCC2" flag is always clear.
407 */
408 if (rcc2 & (1 << 31)) {
409 sysdiv = (rcc2 >> 23) & 0x3F;
410 bypass = (rcc2 >> 11) & 0x1;
411 oscsrc = (rcc2 >> 4) & 0x7;
412
413 /* FIXME Tempest parts have an additional lsb for
414 * fractional sysdiv (200 MHz / 2.5 == 80 MHz)
415 */
416 }
417
418 stellaris_info->mck_desc = "";
419
420 switch (oscsrc)
421 {
422 case 0: /* MOSC */
423 mainfreq = rcc_xtal[xtal];
424 break;
425 case 1: /* IOSC */
426 mainfreq = stellaris_info->iosc_freq;
427 stellaris_info->mck_desc = stellaris_info->iosc_desc;
428 break;
429 case 2: /* IOSC/4 */
430 mainfreq = stellaris_info->iosc_freq / 4;
431 stellaris_info->mck_desc = stellaris_info->iosc_desc;
432 break;
433 case 3: /* lowspeed */
434 /* Sandstorm doesn't have this 30K +/- 30% osc */
435 mainfreq = 30000;
436 stellaris_info->mck_desc = " (±30%)";
437 break;
438 case 8: /* hibernation osc */
439 /* not all parts support hibernation */
440 mainfreq = 32768;
441 break;
442
443 default: /* NOTREACHED */
444 mainfreq = 0;
445 break;
446 }
447
448 /* PLL is used if it's not bypassed; its output is 200 MHz
449 * even when it runs at 400 MHz (adds divide-by-two stage).
450 */
451 if (!bypass)
452 mainfreq = 200000000;
453
454 if (usesysdiv)
455 stellaris_info->mck_freq = mainfreq/(1 + sysdiv);
456 else
457 stellaris_info->mck_freq = mainfreq;
458 }
459
460 /* Read device id register, main clock frequency register and fill in driver info structure */
461 static int stellaris_read_part_info(struct flash_bank *bank)
462 {
463 struct stellaris_flash_bank *stellaris_info = bank->driver_priv;
464 struct target *target = bank->target;
465 uint32_t did0, did1, ver, fam;
466 int i;
467
468 /* Read and parse chip identification register */
469 target_read_u32(target, SCB_BASE | DID0, &did0);
470 target_read_u32(target, SCB_BASE | DID1, &did1);
471 target_read_u32(target, SCB_BASE | DC0, &stellaris_info->dc0);
472 target_read_u32(target, SCB_BASE | DC1, &stellaris_info->dc1);
473 LOG_DEBUG("did0 0x%" PRIx32 ", did1 0x%" PRIx32 ", dc0 0x%" PRIx32 ", dc1 0x%" PRIx32 "",
474 did0, did1, stellaris_info->dc0, stellaris_info->dc1);
475
476 ver = did0 >> 28;
477 if ((ver != 0) && (ver != 1))
478 {
479 LOG_WARNING("Unknown did0 version, cannot identify target");
480 return ERROR_FLASH_OPERATION_FAILED;
481 }
482
483 if (did1 == 0)
484 {
485 LOG_WARNING("Cannot identify target as a Stellaris");
486 return ERROR_FLASH_OPERATION_FAILED;
487 }
488
489 ver = did1 >> 28;
490 fam = (did1 >> 24) & 0xF;
491 if (((ver != 0) && (ver != 1)) || (fam != 0))
492 {
493 LOG_WARNING("Unknown did1 version/family.");
494 return ERROR_FLASH_OPERATION_FAILED;
495 }
496
497 /* For Sandstorm, Fury, DustDevil: current data sheets say IOSC
498 * is 12 MHz, but some older parts have 15 MHz. A few data sheets
499 * even give _both_ numbers! We'll use current numbers; IOSC is
500 * always approximate.
501 *
502 * For Tempest: IOSC is calibrated, 16 MHz
503 */
504 stellaris_info->iosc_freq = 12000000;
505 stellaris_info->iosc_desc = " (±30%)";
506 stellaris_info->xtal_mask = 0x0f;
507
508 switch ((did0 >> 28) & 0x7) {
509 case 0: /* Sandstorm */
510 /*
511 * Current (2009-August) parts seem to be rev C2 and use 12 MHz.
512 * Parts before rev C0 used 15 MHz; some C0 parts use 15 MHz
513 * (LM3S618), but some other C0 parts are 12 MHz (LM3S811).
514 */
515 if (((did0 >> 8) & 0xff) < 2) {
516 stellaris_info->iosc_freq = 15000000;
517 stellaris_info->iosc_desc = " (±50%)";
518 }
519 break;
520 case 1:
521 switch ((did0 >> 16) & 0xff) {
522 case 1: /* Fury */
523 break;
524 case 4: /* Tempest */
525 stellaris_info->iosc_freq = 16000000; /* +/- 1% */
526 stellaris_info->iosc_desc = " (±1%)";
527 /* FALL THROUGH */
528 case 3: /* DustDevil */
529 stellaris_info->xtal_mask = 0x1f;
530 break;
531 default:
532 LOG_WARNING("Unknown did0 class");
533 }
534 break;
535 default:
536 LOG_WARNING("Unknown did0 version");
537 break;
538 }
539
540 for (i = 0; StellarisParts[i].partno; i++)
541 {
542 if (StellarisParts[i].partno == ((did1 >> 16) & 0xFFFF))
543 break;
544 }
545
546 stellaris_info->target_name = StellarisParts[i].partname;
547
548 stellaris_info->did0 = did0;
549 stellaris_info->did1 = did1;
550
551 stellaris_info->num_lockbits = 1 + (stellaris_info->dc0 & 0xFFFF);
552 stellaris_info->num_pages = 2 *(1 + (stellaris_info->dc0 & 0xFFFF));
553 stellaris_info->pagesize = 1024;
554 stellaris_info->pages_in_lockregion = 2;
555
556 /* REVISIT for at least Tempest parts, read NVMSTAT.FWB too.
557 * That exposes a 32-word Flash Write Buffer ... enabling
558 * writes of more than one word at a time.
559 */
560
561 return ERROR_OK;
562 }
563
564 /***************************************************************************
565 * flash operations *
566 ***************************************************************************/
567
568 static int stellaris_protect_check(struct flash_bank *bank)
569 {
570 struct stellaris_flash_bank *stellaris = bank->driver_priv;
571 int status = ERROR_OK;
572 unsigned i;
573 unsigned page;
574
575 if (stellaris->did1 == 0)
576 return ERROR_FLASH_BANK_NOT_PROBED;
577
578 for (i = 0; i < (unsigned) bank->num_sectors; i++)
579 bank->sectors[i].is_protected = -1;
580
581 /* Read each Flash Memory Protection Program Enable (FMPPE) register
582 * to report any pages that we can't write. Ignore the Read Enable
583 * register (FMPRE).
584 */
585 for (i = 0, page = 0;
586 i < DIV_ROUND_UP(stellaris->num_lockbits, 32u);
587 i++) {
588 uint32_t lockbits;
589
590 status = target_read_u32(bank->target,
591 SCB_BASE + (i ? (FMPPE0 + 4 * i) : FMPPE),
592 &lockbits);
593 LOG_DEBUG("FMPPE%d = %#8.8x (status %d)", i,
594 (unsigned) lockbits, status);
595 if (status != ERROR_OK)
596 goto done;
597
598 for (unsigned j = 0; j < 32; j++) {
599 unsigned k;
600
601 for (k = 0; k < stellaris->pages_in_lockregion; k++) {
602 if (page >= (unsigned) bank->num_sectors)
603 goto done;
604 bank->sectors[page++].is_protected =
605 !(lockbits & (1 << j));
606 }
607 }
608 }
609
610 done:
611 return status;
612 }
613
614 static int stellaris_erase(struct flash_bank *bank, int first, int last)
615 {
616 int banknr;
617 uint32_t flash_fmc, flash_cris;
618 struct stellaris_flash_bank *stellaris_info = bank->driver_priv;
619 struct target *target = bank->target;
620
621 if (bank->target->state != TARGET_HALTED)
622 {
623 LOG_ERROR("Target not halted");
624 return ERROR_TARGET_NOT_HALTED;
625 }
626
627 if (stellaris_info->did1 == 0)
628 return ERROR_FLASH_BANK_NOT_PROBED;
629
630 if ((first < 0) || (last < first) || (last >= (int)stellaris_info->num_pages))
631 {
632 return ERROR_FLASH_SECTOR_INVALID;
633 }
634
635 if ((first == 0) && (last == ((int)stellaris_info->num_pages-1)))
636 {
637 return stellaris_mass_erase(bank);
638 }
639
640 /* Refresh flash controller timing */
641 stellaris_read_clock_info(bank);
642 stellaris_set_flash_timing(bank);
643
644 /* Clear and disable flash programming interrupts */
645 target_write_u32(target, FLASH_CIM, 0);
646 target_write_u32(target, FLASH_MISC, PMISC | AMISC);
647
648 /* REVISIT this clobbers state set by any halted firmware ...
649 * it might want to process those IRQs.
650 */
651
652 for (banknr = first; banknr <= last; banknr++)
653 {
654 /* Address is first word in page */
655 target_write_u32(target, FLASH_FMA, banknr * stellaris_info->pagesize);
656 /* Write erase command */
657 target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_ERASE);
658 /* Wait until erase complete */
659 do
660 {
661 target_read_u32(target, FLASH_FMC, &flash_fmc);
662 }
663 while (flash_fmc & FMC_ERASE);
664
665 /* Check acess violations */
666 target_read_u32(target, FLASH_CRIS, &flash_cris);
667 if (flash_cris & (AMASK))
668 {
669 LOG_WARNING("Error erasing flash page %i, flash_cris 0x%" PRIx32 "", banknr, flash_cris);
670 target_write_u32(target, FLASH_CRIS, 0);
671 return ERROR_FLASH_OPERATION_FAILED;
672 }
673
674 bank->sectors[banknr].is_erased = 1;
675 }
676
677 return ERROR_OK;
678 }
679
680 static int stellaris_protect(struct flash_bank *bank, int set, int first, int last)
681 {
682 uint32_t fmppe, flash_fmc, flash_cris;
683 int lockregion;
684
685 struct stellaris_flash_bank *stellaris_info = bank->driver_priv;
686 struct target *target = bank->target;
687
688 if (bank->target->state != TARGET_HALTED)
689 {
690 LOG_ERROR("Target not halted");
691 return ERROR_TARGET_NOT_HALTED;
692 }
693
694 if (!set)
695 {
696 LOG_ERROR("Hardware doesn't suppport page-level unprotect. "
697 "Try the 'recover' command.");
698 return ERROR_INVALID_ARGUMENTS;
699 }
700
701 if (stellaris_info->did1 == 0)
702 return ERROR_FLASH_BANK_NOT_PROBED;
703
704 /* lockregions are 2 pages ... must protect [even..odd] */
705 if ((first < 0) || (first & 1)
706 || (last < first) || !(last & 1)
707 || (last >= 2 * stellaris_info->num_lockbits))
708 {
709 LOG_ERROR("Can't protect unaligned or out-of-range pages.");
710 return ERROR_FLASH_SECTOR_INVALID;
711 }
712
713 /* Refresh flash controller timing */
714 stellaris_read_clock_info(bank);
715 stellaris_set_flash_timing(bank);
716
717 /* convert from pages to lockregions */
718 first /= 2;
719 last /= 2;
720
721 /* FIXME this assumes single FMPPE, for a max of 64K of flash!!
722 * Current parts can be much bigger.
723 */
724 if (last >= 32) {
725 LOG_ERROR("No support yet for protection > 64K");
726 return ERROR_FLASH_OPERATION_FAILED;
727 }
728
729 target_read_u32(target, SCB_BASE | FMPPE, &fmppe);
730
731 for (lockregion = first; lockregion <= last; lockregion++)
732 fmppe &= ~(1 << lockregion);
733
734 /* Clear and disable flash programming interrupts */
735 target_write_u32(target, FLASH_CIM, 0);
736 target_write_u32(target, FLASH_MISC, PMISC | AMISC);
737
738 /* REVISIT this clobbers state set by any halted firmware ...
739 * it might want to process those IRQs.
740 */
741
742 LOG_DEBUG("fmppe 0x%" PRIx32 "",fmppe);
743 target_write_u32(target, SCB_BASE | FMPPE, fmppe);
744
745 /* Commit FMPPE */
746 target_write_u32(target, FLASH_FMA, 1);
747
748 /* Write commit command */
749 /* REVISIT safety check, since this cannot be undone
750 * except by the "Recover a locked device" procedure.
751 * REVISIT DustDevil-A0 parts have an erratum making FMPPE commits
752 * inadvisable ... it makes future mass erase operations fail.
753 */
754 LOG_WARNING("Flash protection cannot be removed once commited, commit is NOT executed !");
755 /* target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_COMT); */
756
757 /* Wait until erase complete */
758 do
759 {
760 target_read_u32(target, FLASH_FMC, &flash_fmc);
761 }
762 while (flash_fmc & FMC_COMT);
763
764 /* Check acess violations */
765 target_read_u32(target, FLASH_CRIS, &flash_cris);
766 if (flash_cris & (AMASK))
767 {
768 LOG_WARNING("Error setting flash page protection, flash_cris 0x%" PRIx32 "", flash_cris);
769 target_write_u32(target, FLASH_CRIS, 0);
770 return ERROR_FLASH_OPERATION_FAILED;
771 }
772
773 return ERROR_OK;
774 }
775
776 /* see contib/loaders/flash/stellaris.s for src */
777
778 static const uint8_t stellaris_write_code[] =
779 {
780 /*
781 Call with :
782 r0 = buffer address
783 r1 = destination address
784 r2 = bytecount (in) - endaddr (work)
785
786 Used registers:
787 r3 = pFLASH_CTRL_BASE
788 r4 = FLASHWRITECMD
789 r5 = #1
790 r6 = bytes written
791 r7 = temp reg
792 */
793 0x07,0x4B, /* ldr r3,pFLASH_CTRL_BASE */
794 0x08,0x4C, /* ldr r4,FLASHWRITECMD */
795 0x01,0x25, /* movs r5, 1 */
796 0x00,0x26, /* movs r6, #0 */
797 /* mainloop: */
798 0x19,0x60, /* str r1, [r3, #0] */
799 0x87,0x59, /* ldr r7, [r0, r6] */
800 0x5F,0x60, /* str r7, [r3, #4] */
801 0x9C,0x60, /* str r4, [r3, #8] */
802 /* waitloop: */
803 0x9F,0x68, /* ldr r7, [r3, #8] */
804 0x2F,0x42, /* tst r7, r5 */
805 0xFC,0xD1, /* bne waitloop */
806 0x04,0x31, /* adds r1, r1, #4 */
807 0x04,0x36, /* adds r6, r6, #4 */
808 0x96,0x42, /* cmp r6, r2 */
809 0xF4,0xD1, /* bne mainloop */
810 0x00,0xBE, /* bkpt #0 */
811 /* pFLASH_CTRL_BASE: */
812 0x00,0xD0,0x0F,0x40, /* .word 0x400FD000 */
813 /* FLASHWRITECMD: */
814 0x01,0x00,0x42,0xA4 /* .word 0xA4420001 */
815 };
816
817 static int stellaris_write_block(struct flash_bank *bank,
818 uint8_t *buffer, uint32_t offset, uint32_t wcount)
819 {
820 struct target *target = bank->target;
821 uint32_t buffer_size = 16384;
822 struct working_area *source;
823 struct working_area *write_algorithm;
824 uint32_t address = bank->base + offset;
825 struct reg_param reg_params[3];
826 struct armv7m_algorithm armv7m_info;
827 int retval = ERROR_OK;
828
829 /* power of two, and multiple of word size */
830 static const unsigned buf_min = 128;
831
832 /* for small buffers it's faster not to download an algorithm */
833 if (wcount * 4 < buf_min)
834 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
835
836 LOG_DEBUG("(bank=%p buffer=%p offset=%08" PRIx32 " wcount=%08" PRIx32 "",
837 bank, buffer, offset, wcount);
838
839 /* flash write code */
840 if (target_alloc_working_area(target, sizeof(stellaris_write_code), &write_algorithm) != ERROR_OK)
841 {
842 LOG_DEBUG("no working area for block memory writes");
843 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
844 };
845
846 /* plus a buffer big enough for this data */
847 if (wcount * 4 < buffer_size)
848 buffer_size = wcount * 4;
849
850 /* memory buffer */
851 while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK)
852 {
853 buffer_size /= 2;
854 if (buffer_size <= buf_min)
855 {
856 target_free_working_area(target, write_algorithm);
857 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
858 }
859 LOG_DEBUG("retry target_alloc_working_area(%s, size=%u)",
860 target_name(target), (unsigned) buffer_size);
861 };
862
863 retval = target_write_buffer(target, write_algorithm->address,
864 sizeof(stellaris_write_code),
865 (uint8_t *) stellaris_write_code);
866
867 armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
868 armv7m_info.core_mode = ARMV7M_MODE_ANY;
869
870 init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
871 init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
872 init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
873
874 while (wcount > 0)
875 {
876 uint32_t thisrun_count = (wcount > (buffer_size / 4)) ? (buffer_size / 4) : wcount;
877
878 target_write_buffer(target, source->address, thisrun_count * 4, buffer);
879
880 buf_set_u32(reg_params[0].value, 0, 32, source->address);
881 buf_set_u32(reg_params[1].value, 0, 32, address);
882 buf_set_u32(reg_params[2].value, 0, 32, 4*thisrun_count);
883 LOG_DEBUG("Algorithm flash write %u words to 0x%" PRIx32
884 ", %u remaining",
885 (unsigned) thisrun_count, address,
886 (unsigned) (wcount - thisrun_count));
887 retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
888 write_algorithm->address,
889 0,
890 10000, &armv7m_info);
891 if (retval != ERROR_OK)
892 {
893 LOG_ERROR("error %d executing stellaris "
894 "flash write algorithm",
895 retval);
896 retval = ERROR_FLASH_OPERATION_FAILED;
897 break;
898 }
899
900 buffer += thisrun_count * 4;
901 address += thisrun_count * 4;
902 wcount -= thisrun_count;
903 }
904
905 /* REVISIT we could speed up writing multi-section images by
906 * not freeing the initialized write_algorithm this way.
907 */
908
909 target_free_working_area(target, write_algorithm);
910 target_free_working_area(target, source);
911
912 destroy_reg_param(&reg_params[0]);
913 destroy_reg_param(&reg_params[1]);
914 destroy_reg_param(&reg_params[2]);
915
916 return retval;
917 }
918
919 static int stellaris_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count)
920 {
921 struct stellaris_flash_bank *stellaris_info = bank->driver_priv;
922 struct target *target = bank->target;
923 uint32_t address = offset;
924 uint32_t flash_cris, flash_fmc;
925 uint32_t words_remaining = (count / 4);
926 uint32_t bytes_remaining = (count & 0x00000003);
927 uint32_t bytes_written = 0;
928 int retval;
929
930 if (bank->target->state != TARGET_HALTED)
931 {
932 LOG_ERROR("Target not halted");
933 return ERROR_TARGET_NOT_HALTED;
934 }
935
936 LOG_DEBUG("(bank=%p buffer=%p offset=%08" PRIx32 " count=%08" PRIx32 "",
937 bank, buffer, offset, count);
938
939 if (stellaris_info->did1 == 0)
940 return ERROR_FLASH_BANK_NOT_PROBED;
941
942 if (offset & 0x3)
943 {
944 LOG_WARNING("offset size must be word aligned");
945 return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
946 }
947
948 if (offset + count > bank->size)
949 return ERROR_FLASH_DST_OUT_OF_BANK;
950
951 /* Refresh flash controller timing */
952 stellaris_read_clock_info(bank);
953 stellaris_set_flash_timing(bank);
954
955 /* Clear and disable flash programming interrupts */
956 target_write_u32(target, FLASH_CIM, 0);
957 target_write_u32(target, FLASH_MISC, PMISC | AMISC);
958
959 /* REVISIT this clobbers state set by any halted firmware ...
960 * it might want to process those IRQs.
961 */
962
963 /* multiple words to be programmed? */
964 if (words_remaining > 0)
965 {
966 /* try using a block write */
967 retval = stellaris_write_block(bank, buffer, offset,
968 words_remaining);
969 if (retval != ERROR_OK)
970 {
971 if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
972 {
973 LOG_DEBUG("writing flash word-at-a-time");
974 }
975 else if (retval == ERROR_FLASH_OPERATION_FAILED)
976 {
977 /* if an error occured, we examine the reason, and quit */
978 target_read_u32(target, FLASH_CRIS, &flash_cris);
979
980 LOG_ERROR("flash writing failed with CRIS: 0x%" PRIx32 "", flash_cris);
981 return ERROR_FLASH_OPERATION_FAILED;
982 }
983 }
984 else
985 {
986 buffer += words_remaining * 4;
987 address += words_remaining * 4;
988 words_remaining = 0;
989 }
990 }
991
992 while (words_remaining > 0)
993 {
994 if (!(address & 0xff))
995 LOG_DEBUG("0x%" PRIx32 "", address);
996
997 /* Program one word */
998 target_write_u32(target, FLASH_FMA, address);
999 target_write_buffer(target, FLASH_FMD, 4, buffer);
1000 target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_WRITE);
1001 /* LOG_DEBUG("0x%x 0x%x 0x%x",address,buf_get_u32(buffer, 0, 32),FMC_WRKEY | FMC_WRITE); */
1002 /* Wait until write complete */
1003 do
1004 {
1005 target_read_u32(target, FLASH_FMC, &flash_fmc);
1006 } while (flash_fmc & FMC_WRITE);
1007
1008 buffer += 4;
1009 address += 4;
1010 words_remaining--;
1011 }
1012
1013 if (bytes_remaining)
1014 {
1015 uint8_t last_word[4] = {0xff, 0xff, 0xff, 0xff};
1016 int i = 0;
1017
1018 while (bytes_remaining > 0)
1019 {
1020 last_word[i++] = *(buffer + bytes_written);
1021 bytes_remaining--;
1022 bytes_written++;
1023 }
1024
1025 if (!(address & 0xff))
1026 LOG_DEBUG("0x%" PRIx32 "", address);
1027
1028 /* Program one word */
1029 target_write_u32(target, FLASH_FMA, address);
1030 target_write_buffer(target, FLASH_FMD, 4, last_word);
1031 target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_WRITE);
1032 /* LOG_DEBUG("0x%x 0x%x 0x%x",address,buf_get_u32(buffer, 0, 32),FMC_WRKEY | FMC_WRITE); */
1033 /* Wait until write complete */
1034 do
1035 {
1036 target_read_u32(target, FLASH_FMC, &flash_fmc);
1037 } while (flash_fmc & FMC_WRITE);
1038 }
1039
1040 /* Check access violations */
1041 target_read_u32(target, FLASH_CRIS, &flash_cris);
1042 if (flash_cris & (AMASK))
1043 {
1044 LOG_DEBUG("flash_cris 0x%" PRIx32 "", flash_cris);
1045 return ERROR_FLASH_OPERATION_FAILED;
1046 }
1047 return ERROR_OK;
1048 }
1049
1050 static int stellaris_probe(struct flash_bank *bank)
1051 {
1052 struct stellaris_flash_bank *stellaris_info = bank->driver_priv;
1053 int retval;
1054
1055 /* If this is a stellaris chip, it has flash; probe() is just
1056 * to figure out how much is present. Only do it once.
1057 */
1058 if (stellaris_info->did1 != 0)
1059 return ERROR_OK;
1060
1061 /* stellaris_read_part_info() already handled error checking and
1062 * reporting. Note that it doesn't write, so we don't care about
1063 * whether the target is halted or not.
1064 */
1065 retval = stellaris_read_part_info(bank);
1066 if (retval != ERROR_OK)
1067 return retval;
1068
1069 if (bank->sectors)
1070 {
1071 free(bank->sectors);
1072 bank->sectors = NULL;
1073 }
1074
1075 /* provide this for the benefit of the NOR flash framework */
1076 bank->size = 1024 * stellaris_info->num_pages;
1077 bank->num_sectors = stellaris_info->num_pages;
1078 bank->sectors = calloc(bank->num_sectors, sizeof(struct flash_sector));
1079 for (int i = 0; i < bank->num_sectors; i++)
1080 {
1081 bank->sectors[i].offset = i * stellaris_info->pagesize;
1082 bank->sectors[i].size = stellaris_info->pagesize;
1083 bank->sectors[i].is_erased = -1;
1084 bank->sectors[i].is_protected = -1;
1085 }
1086
1087 return retval;
1088 }
1089
1090 static int stellaris_mass_erase(struct flash_bank *bank)
1091 {
1092 struct target *target = NULL;
1093 struct stellaris_flash_bank *stellaris_info = NULL;
1094 uint32_t flash_fmc;
1095
1096 stellaris_info = bank->driver_priv;
1097 target = bank->target;
1098
1099 if (target->state != TARGET_HALTED)
1100 {
1101 LOG_ERROR("Target not halted");
1102 return ERROR_TARGET_NOT_HALTED;
1103 }
1104
1105 if (stellaris_info->did1 == 0)
1106 return ERROR_FLASH_BANK_NOT_PROBED;
1107
1108 /* Refresh flash controller timing */
1109 stellaris_read_clock_info(bank);
1110 stellaris_set_flash_timing(bank);
1111
1112 /* Clear and disable flash programming interrupts */
1113 target_write_u32(target, FLASH_CIM, 0);
1114 target_write_u32(target, FLASH_MISC, PMISC | AMISC);
1115
1116 /* REVISIT this clobbers state set by any halted firmware ...
1117 * it might want to process those IRQs.
1118 */
1119
1120 target_write_u32(target, FLASH_FMA, 0);
1121 target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_MERASE);
1122 /* Wait until erase complete */
1123 do
1124 {
1125 target_read_u32(target, FLASH_FMC, &flash_fmc);
1126 }
1127 while (flash_fmc & FMC_MERASE);
1128
1129 /* if device has > 128k, then second erase cycle is needed
1130 * this is only valid for older devices, but will not hurt */
1131 if (stellaris_info->num_pages * stellaris_info->pagesize > 0x20000)
1132 {
1133 target_write_u32(target, FLASH_FMA, 0x20000);
1134 target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_MERASE);
1135 /* Wait until erase complete */
1136 do
1137 {
1138 target_read_u32(target, FLASH_FMC, &flash_fmc);
1139 }
1140 while (flash_fmc & FMC_MERASE);
1141 }
1142
1143 return ERROR_OK;
1144 }
1145
1146 COMMAND_HANDLER(stellaris_handle_mass_erase_command)
1147 {
1148 int i;
1149
1150 if (CMD_ARGC < 1)
1151 {
1152 command_print(CMD_CTX, "stellaris mass_erase <bank>");
1153 return ERROR_OK;
1154 }
1155
1156 struct flash_bank *bank;
1157 int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
1158 if (ERROR_OK != retval)
1159 return retval;
1160
1161 if (stellaris_mass_erase(bank) == ERROR_OK)
1162 {
1163 /* set all sectors as erased */
1164 for (i = 0; i < bank->num_sectors; i++)
1165 {
1166 bank->sectors[i].is_erased = 1;
1167 }
1168
1169 command_print(CMD_CTX, "stellaris mass erase complete");
1170 }
1171 else
1172 {
1173 command_print(CMD_CTX, "stellaris mass erase failed");
1174 }
1175
1176 return ERROR_OK;
1177 }
1178
1179 /**
1180 * Perform the Stellaris "Recovering a 'Locked' Device procedure.
1181 * This performs a mass erase and then restores all nonvolatile registers
1182 * (including USER_* registers and flash lock bits) to their defaults.
1183 * Accordingly, flash can be reprogrammed, and JTAG can be used.
1184 *
1185 * NOTE that DustDevil parts (at least rev A0 silicon) have errata which
1186 * can affect this operation if flash protection has been enabled.
1187 */
1188 COMMAND_HANDLER(stellaris_handle_recover_command)
1189 {
1190 struct flash_bank *bank;
1191 int retval;
1192
1193 retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
1194 if (retval != ERROR_OK)
1195 return retval;
1196
1197 /* REVISIT ... it may be worth sanity checking that the AP is
1198 * inactive before we start. ARM documents that switching a DP's
1199 * mode while it's active can cause fault modes that need a power
1200 * cycle to recover.
1201 */
1202
1203 /* assert SRST */
1204 if (!(jtag_get_reset_config() & RESET_HAS_SRST)) {
1205 LOG_ERROR("Can't recover Stellaris flash without SRST");
1206 return ERROR_FAIL;
1207 }
1208 jtag_add_reset(0, 1);
1209
1210 for (int i = 0; i < 5; i++) {
1211 retval = dap_to_swd(bank->target);
1212 if (retval != ERROR_OK)
1213 goto done;
1214
1215 retval = dap_to_jtag(bank->target);
1216 if (retval != ERROR_OK)
1217 goto done;
1218 }
1219
1220 /* de-assert SRST */
1221 jtag_add_reset(0, 0);
1222 retval = jtag_execute_queue();
1223
1224 /* wait 400+ msec ... OK, "1+ second" is simpler */
1225 usleep(1000);
1226
1227 /* USER INTERVENTION required for the power cycle
1228 * Restarting OpenOCD is likely needed because of mode switching.
1229 */
1230 LOG_INFO("USER ACTION: "
1231 "power cycle Stellaris chip, then restart OpenOCD.");
1232
1233 done:
1234 return retval;
1235 }
1236
1237 static const struct command_registration stellaris_exec_command_handlers[] = {
1238 {
1239 .name = "mass_erase",
1240 .handler = stellaris_handle_mass_erase_command,
1241 .mode = COMMAND_EXEC,
1242 .usage = "bank_id",
1243 .help = "erase entire device",
1244 },
1245 {
1246 .name = "recover",
1247 .handler = stellaris_handle_recover_command,
1248 .mode = COMMAND_EXEC,
1249 .usage = "bank_id",
1250 .help = "recover (and erase) locked device",
1251 },
1252 COMMAND_REGISTRATION_DONE
1253 };
1254 static const struct command_registration stellaris_command_handlers[] = {
1255 {
1256 .name = "stellaris",
1257 .mode = COMMAND_EXEC,
1258 .help = "Stellaris flash command group",
1259 .chain = stellaris_exec_command_handlers,
1260 },
1261 COMMAND_REGISTRATION_DONE
1262 };
1263
1264 struct flash_driver stellaris_flash = {
1265 .name = "stellaris",
1266 .commands = stellaris_command_handlers,
1267 .flash_bank_command = stellaris_flash_bank_command,
1268 .erase = stellaris_erase,
1269 .protect = stellaris_protect,
1270 .write = stellaris_write,
1271 .read = default_flash_read,
1272 .probe = stellaris_probe,
1273 .auto_probe = stellaris_probe,
1274 .erase_check = default_flash_mem_blank_check,
1275 .protect_check = stellaris_protect_check,
1276 .info = get_stellaris_info,
1277 };

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)