flash/nor/spi: add adesto AT25DF081A
[openocd.git] / src / flash / nor / fm4.c
1 /*
2 * Spansion FM4 flash
3 *
4 * Copyright (c) 2015 Andreas Färber
5 *
6 * Based on S6E2DH_MN709-00013 for S6E2DH/DF/D5/D3 series
7 * Based on S6E2CC_MN709-00007 for S6E2CC/C5/C4/C3/C2/C1 series
8 * Based on MB9B560R_MN709-00005 for MB9BFx66/x67/x68 series
9 * Based on MB9B560L_MN709-00006 for MB9BFx64/x65/x66 series
10 */
11
12 #ifdef HAVE_CONFIG_H
13 #include "config.h"
14 #endif
15
16 #include "imp.h"
17 #include <helper/binarybuffer.h>
18 #include <target/algorithm.h>
19 #include <target/armv7m.h>
20
21 #define FLASH_BASE 0x40000000
22 #define FASZR (FLASH_BASE + 0x000)
23 #define DFCTRLR (FLASH_BASE + 0x030)
24 #define DFCTRLR_DFE (1UL << 0)
25
26 #define WDG_BASE 0x40011000
27 #define WDG_CTL (WDG_BASE + 0x008)
28 #define WDG_LCK (WDG_BASE + 0xC00)
29
30 enum fm4_variant {
31 mb9bfx64,
32 mb9bfx65,
33 mb9bfx66,
34 mb9bfx67,
35 mb9bfx68,
36
37 s6e2cx8,
38 s6e2cx9,
39 s6e2cxa,
40
41 s6e2dx,
42 };
43
44 struct fm4_flash_bank {
45 enum fm4_variant variant;
46 int macro_nr;
47 bool probed;
48 };
49
50 static int fm4_disable_hw_watchdog(struct target *target)
51 {
52 int retval;
53
54 retval = target_write_u32(target, WDG_LCK, 0x1ACCE551);
55 if (retval != ERROR_OK)
56 return retval;
57
58 retval = target_write_u32(target, WDG_LCK, 0xE5331AAE);
59 if (retval != ERROR_OK)
60 return retval;
61
62 retval = target_write_u32(target, WDG_CTL, 0);
63 if (retval != ERROR_OK)
64 return retval;
65
66 return ERROR_OK;
67 }
68
69 static int fm4_enter_flash_cpu_programming_mode(struct target *target)
70 {
71 uint32_t u32_value;
72 int retval;
73
74 /* FASZR ASZ = CPU programming mode */
75 retval = target_write_u32(target, FASZR, 0x00000001);
76 if (retval != ERROR_OK)
77 return retval;
78 retval = target_read_u32(target, FASZR, &u32_value);
79 if (retval != ERROR_OK)
80 return retval;
81
82 return ERROR_OK;
83 }
84
85 static int fm4_enter_flash_cpu_rom_mode(struct target *target)
86 {
87 uint32_t u32_value;
88 int retval;
89
90 /* FASZR ASZ = CPU ROM mode */
91 retval = target_write_u32(target, FASZR, 0x00000002);
92 if (retval != ERROR_OK)
93 return retval;
94 retval = target_read_u32(target, FASZR, &u32_value);
95 if (retval != ERROR_OK)
96 return retval;
97
98 return ERROR_OK;
99 }
100
101 static int fm4_flash_erase(struct flash_bank *bank, int first, int last)
102 {
103 struct target *target = bank->target;
104 struct working_area *workarea;
105 struct reg_param reg_params[4];
106 struct armv7m_algorithm armv7m_algo;
107 unsigned i;
108 int retval, sector;
109 const uint8_t erase_sector_code[] = {
110 #include "../../../contrib/loaders/flash/fm4/erase.inc"
111 };
112
113 if (target->state != TARGET_HALTED) {
114 LOG_WARNING("Cannot communicate... target not halted.");
115 return ERROR_TARGET_NOT_HALTED;
116 }
117
118 LOG_DEBUG("Spansion FM4 erase sectors %d to %d", first, last);
119
120 retval = fm4_disable_hw_watchdog(target);
121 if (retval != ERROR_OK)
122 return retval;
123
124 retval = fm4_enter_flash_cpu_programming_mode(target);
125 if (retval != ERROR_OK)
126 return retval;
127
128 retval = target_alloc_working_area(target, sizeof(erase_sector_code),
129 &workarea);
130 if (retval != ERROR_OK) {
131 LOG_ERROR("No working area available.");
132 retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
133 goto err_alloc_code;
134 }
135 retval = target_write_buffer(target, workarea->address,
136 sizeof(erase_sector_code), erase_sector_code);
137 if (retval != ERROR_OK)
138 goto err_write_code;
139
140 armv7m_algo.common_magic = ARMV7M_COMMON_MAGIC;
141 armv7m_algo.core_mode = ARM_MODE_THREAD;
142
143 init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
144 init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
145 init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
146 init_reg_param(&reg_params[3], "r3", 32, PARAM_IN);
147
148 for (sector = first; sector <= last; sector++) {
149 uint32_t addr = bank->base + bank->sectors[sector].offset;
150 uint32_t result;
151
152 buf_set_u32(reg_params[0].value, 0, 32, (addr & ~0xffff) | 0xAA8);
153 buf_set_u32(reg_params[1].value, 0, 32, (addr & ~0xffff) | 0x554);
154 buf_set_u32(reg_params[2].value, 0, 32, addr);
155
156 retval = target_run_algorithm(target,
157 0, NULL,
158 ARRAY_SIZE(reg_params), reg_params,
159 workarea->address, 0,
160 1000, &armv7m_algo);
161 if (retval != ERROR_OK) {
162 LOG_ERROR("Error executing flash sector erase "
163 "programming algorithm");
164 retval = ERROR_FLASH_OPERATION_FAILED;
165 goto err_run;
166 }
167
168 result = buf_get_u32(reg_params[3].value, 0, 32);
169 if (result == 2) {
170 LOG_ERROR("Timeout error from flash sector erase programming algorithm");
171 retval = ERROR_FLASH_OPERATION_FAILED;
172 goto err_run_ret;
173 } else if (result != 0) {
174 LOG_ERROR("Unexpected error %d from flash sector erase programming algorithm", result);
175 retval = ERROR_FLASH_OPERATION_FAILED;
176 goto err_run_ret;
177 } else
178 retval = ERROR_OK;
179
180 bank->sectors[sector].is_erased = 1;
181 }
182
183 err_run_ret:
184 err_run:
185 for (i = 0; i < ARRAY_SIZE(reg_params); i++)
186 destroy_reg_param(&reg_params[i]);
187
188 err_write_code:
189 target_free_working_area(target, workarea);
190
191 err_alloc_code:
192 if (retval != ERROR_OK)
193 fm4_enter_flash_cpu_rom_mode(target);
194 else
195 retval = fm4_enter_flash_cpu_rom_mode(target);
196
197 return retval;
198 }
199
200 static int fm4_flash_write(struct flash_bank *bank, const uint8_t *buffer,
201 uint32_t offset, uint32_t byte_count)
202 {
203 struct target *target = bank->target;
204 struct working_area *code_workarea, *data_workarea;
205 struct reg_param reg_params[6];
206 struct armv7m_algorithm armv7m_algo;
207 uint32_t halfword_count = DIV_ROUND_UP(byte_count, 2);
208 uint32_t result;
209 unsigned i;
210 int retval;
211 const uint8_t write_block_code[] = {
212 #include "../../../contrib/loaders/flash/fm4/write.inc"
213 };
214
215 LOG_DEBUG("Spansion FM4 write at 0x%08" PRIx32 " (%" PRId32 " bytes)",
216 offset, byte_count);
217
218 if (offset & 0x1) {
219 LOG_ERROR("offset 0x%" PRIx32 " breaks required 2-byte alignment",
220 offset);
221 return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
222 }
223 if (byte_count & 0x1) {
224 LOG_WARNING("length %" PRId32 " is not 2-byte aligned, rounding up",
225 byte_count);
226 }
227
228 if (target->state != TARGET_HALTED) {
229 LOG_WARNING("Cannot communicate... target not halted.");
230 return ERROR_TARGET_NOT_HALTED;
231 }
232
233 retval = fm4_disable_hw_watchdog(target);
234 if (retval != ERROR_OK)
235 return retval;
236
237 retval = target_alloc_working_area(target, sizeof(write_block_code),
238 &code_workarea);
239 if (retval != ERROR_OK) {
240 LOG_ERROR("No working area available for write code.");
241 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
242 }
243 retval = target_write_buffer(target, code_workarea->address,
244 sizeof(write_block_code), write_block_code);
245 if (retval != ERROR_OK)
246 goto err_write_code;
247
248 retval = target_alloc_working_area(target,
249 MIN(halfword_count * 2, target_get_working_area_avail(target)),
250 &data_workarea);
251 if (retval != ERROR_OK) {
252 LOG_ERROR("No working area available for write data.");
253 retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
254 goto err_alloc_data;
255 }
256
257 armv7m_algo.common_magic = ARMV7M_COMMON_MAGIC;
258 armv7m_algo.core_mode = ARM_MODE_THREAD;
259
260 init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
261 init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
262 init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
263 init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT);
264 init_reg_param(&reg_params[4], "r4", 32, PARAM_OUT);
265 init_reg_param(&reg_params[5], "r5", 32, PARAM_IN);
266
267 retval = fm4_enter_flash_cpu_programming_mode(target);
268 if (retval != ERROR_OK)
269 goto err_flash_mode;
270
271 while (byte_count > 0) {
272 uint32_t halfwords = MIN(halfword_count, data_workarea->size / 2);
273 uint32_t addr = bank->base + offset;
274
275 LOG_DEBUG("copying %" PRId32 " bytes to SRAM 0x%08" TARGET_PRIxADDR,
276 MIN(halfwords * 2, byte_count), data_workarea->address);
277
278 retval = target_write_buffer(target, data_workarea->address,
279 MIN(halfwords * 2, byte_count), buffer);
280 if (retval != ERROR_OK) {
281 LOG_ERROR("Error writing data buffer");
282 retval = ERROR_FLASH_OPERATION_FAILED;
283 goto err_write_data;
284 }
285
286 LOG_DEBUG("writing 0x%08" PRIx32 "-0x%08" PRIx32 " (%" PRId32 "x)",
287 addr, addr + halfwords * 2 - 1, halfwords);
288
289 buf_set_u32(reg_params[0].value, 0, 32, (addr & ~0xffff) | 0xAA8);
290 buf_set_u32(reg_params[1].value, 0, 32, (addr & ~0xffff) | 0x554);
291 buf_set_u32(reg_params[2].value, 0, 32, addr);
292 buf_set_u32(reg_params[3].value, 0, 32, data_workarea->address);
293 buf_set_u32(reg_params[4].value, 0, 32, halfwords);
294
295 retval = target_run_algorithm(target,
296 0, NULL,
297 ARRAY_SIZE(reg_params), reg_params,
298 code_workarea->address, 0,
299 5 * 60 * 1000, &armv7m_algo);
300 if (retval != ERROR_OK) {
301 LOG_ERROR("Error executing flash sector erase "
302 "programming algorithm");
303 retval = ERROR_FLASH_OPERATION_FAILED;
304 goto err_run;
305 }
306
307 result = buf_get_u32(reg_params[5].value, 0, 32);
308 if (result == 2) {
309 LOG_ERROR("Timeout error from flash write "
310 "programming algorithm");
311 retval = ERROR_FLASH_OPERATION_FAILED;
312 goto err_run_ret;
313 } else if (result != 0) {
314 LOG_ERROR("Unexpected error %d from flash write "
315 "programming algorithm", result);
316 retval = ERROR_FLASH_OPERATION_FAILED;
317 goto err_run_ret;
318 } else
319 retval = ERROR_OK;
320
321 halfword_count -= halfwords;
322 offset += halfwords * 2;
323 buffer += halfwords * 2;
324 byte_count -= MIN(halfwords * 2, byte_count);
325 }
326
327 err_run_ret:
328 err_run:
329 err_write_data:
330 retval = fm4_enter_flash_cpu_rom_mode(target);
331
332 err_flash_mode:
333 for (i = 0; i < ARRAY_SIZE(reg_params); i++)
334 destroy_reg_param(&reg_params[i]);
335
336 target_free_working_area(target, data_workarea);
337 err_alloc_data:
338 err_write_code:
339 target_free_working_area(target, code_workarea);
340
341 return retval;
342 }
343
344 static int mb9bf_probe(struct flash_bank *bank)
345 {
346 struct fm4_flash_bank *fm4_bank = bank->driver_priv;
347 uint32_t flash_addr = bank->base;
348 int i;
349
350 switch (fm4_bank->variant) {
351 case mb9bfx64:
352 bank->num_sectors = 8;
353 break;
354 case mb9bfx65:
355 bank->num_sectors = 10;
356 break;
357 case mb9bfx66:
358 bank->num_sectors = 12;
359 break;
360 case mb9bfx67:
361 bank->num_sectors = 16;
362 break;
363 case mb9bfx68:
364 bank->num_sectors = 20;
365 break;
366 default:
367 return ERROR_FLASH_OPER_UNSUPPORTED;
368 }
369
370 LOG_DEBUG("%d sectors", bank->num_sectors);
371 bank->sectors = calloc(bank->num_sectors,
372 sizeof(struct flash_sector));
373 for (i = 0; i < bank->num_sectors; i++) {
374 if (i < 4)
375 bank->sectors[i].size = 8 * 1024;
376 else if (i == 4)
377 bank->sectors[i].size = 32 * 1024;
378 else
379 bank->sectors[i].size = 64 * 1024;
380 bank->sectors[i].offset = flash_addr - bank->base;
381 bank->sectors[i].is_erased = -1;
382 bank->sectors[i].is_protected = -1;
383
384 bank->size += bank->sectors[i].size;
385 flash_addr += bank->sectors[i].size;
386 }
387
388 return ERROR_OK;
389 }
390
391 static void s6e2cc_init_sector(struct flash_sector *sector, int sa)
392 {
393 if (sa < 8)
394 sector->size = 8 * 1024;
395 else if (sa == 8)
396 sector->size = 32 * 1024;
397 else
398 sector->size = 64 * 1024;
399
400 sector->is_erased = -1;
401 sector->is_protected = -1;
402 }
403
404 static int s6e2cc_probe(struct flash_bank *bank)
405 {
406 struct target *target = bank->target;
407 struct fm4_flash_bank *fm4_bank = bank->driver_priv;
408 uint32_t u32_value;
409 uint32_t flash_addr = bank->base;
410 int i, retval, num_sectors, num_extra_sectors;
411
412 retval = target_read_u32(target, DFCTRLR, &u32_value);
413 if (retval != ERROR_OK)
414 return retval;
415 if (u32_value & DFCTRLR_DFE) {
416 LOG_WARNING("Dual Flash mode is not implemented.");
417 return ERROR_FLASH_OPER_UNSUPPORTED;
418 }
419
420 switch (fm4_bank->variant) {
421 case s6e2cx8:
422 num_sectors = (fm4_bank->macro_nr == 0) ? 20 : 0;
423 break;
424 case s6e2cx9:
425 num_sectors = (fm4_bank->macro_nr == 0) ? 20 : 12;
426 break;
427 case s6e2cxa:
428 num_sectors = 20;
429 break;
430 default:
431 return ERROR_FLASH_OPER_UNSUPPORTED;
432 }
433 num_extra_sectors = (fm4_bank->macro_nr == 0) ? 1 : 4;
434 bank->num_sectors = num_sectors + num_extra_sectors;
435
436 LOG_DEBUG("%d sectors", bank->num_sectors);
437 bank->sectors = calloc(bank->num_sectors,
438 sizeof(struct flash_sector));
439 for (i = 0; i < num_sectors; i++) {
440 int sa = 4 + i;
441 bank->sectors[i].offset = flash_addr - bank->base;
442 s6e2cc_init_sector(&bank->sectors[i], sa);
443
444 bank->size += bank->sectors[i].size;
445 flash_addr += bank->sectors[i].size;
446 }
447
448 flash_addr = (fm4_bank->macro_nr == 0) ? 0x00406000 : 0x00408000;
449 for (; i < bank->num_sectors; i++) {
450 int sa = 4 - num_extra_sectors + (i - num_sectors);
451 bank->sectors[i].offset = flash_addr - bank->base;
452 s6e2cc_init_sector(&bank->sectors[i], sa);
453
454 /*
455 * Don't increase bank->size for these sectors
456 * to avoid an overlap between Flash Macros #0 and #1.
457 */
458 flash_addr += bank->sectors[i].size;
459 }
460
461 return ERROR_OK;
462 }
463
464 static int s6e2dh_probe(struct flash_bank *bank)
465 {
466 uint32_t flash_addr = bank->base;
467 int i;
468
469 bank->num_sectors = 10;
470 bank->sectors = calloc(bank->num_sectors,
471 sizeof(struct flash_sector));
472 for (i = 0; i < bank->num_sectors; i++) {
473 if (i < 4)
474 bank->sectors[i].size = 8 * 1024;
475 else if (i == 4)
476 bank->sectors[i].size = 32 * 1024;
477 else
478 bank->sectors[i].size = 64 * 1024;
479 bank->sectors[i].offset = flash_addr - bank->base;
480 bank->sectors[i].is_erased = -1;
481 bank->sectors[i].is_protected = -1;
482
483 bank->size += bank->sectors[i].size;
484 flash_addr += bank->sectors[i].size;
485 }
486
487 return ERROR_OK;
488 }
489
490 static int fm4_probe(struct flash_bank *bank)
491 {
492 struct fm4_flash_bank *fm4_bank = bank->driver_priv;
493 int retval;
494
495 if (fm4_bank->probed)
496 return ERROR_OK;
497
498 if (bank->target->state != TARGET_HALTED) {
499 LOG_WARNING("Cannot communicate... target not halted.");
500 return ERROR_TARGET_NOT_HALTED;
501 }
502
503 switch (fm4_bank->variant) {
504 case mb9bfx64:
505 case mb9bfx65:
506 case mb9bfx66:
507 case mb9bfx67:
508 case mb9bfx68:
509 retval = mb9bf_probe(bank);
510 break;
511 case s6e2cx8:
512 case s6e2cx9:
513 case s6e2cxa:
514 retval = s6e2cc_probe(bank);
515 break;
516 case s6e2dx:
517 retval = s6e2dh_probe(bank);
518 break;
519 default:
520 return ERROR_FLASH_OPER_UNSUPPORTED;
521 }
522 if (retval != ERROR_OK)
523 return retval;
524
525 fm4_bank->probed = true;
526
527 return ERROR_OK;
528 }
529
530 static int fm4_auto_probe(struct flash_bank *bank)
531 {
532 struct fm4_flash_bank *fm4_bank = bank->driver_priv;
533
534 if (fm4_bank->probed)
535 return ERROR_OK;
536
537 return fm4_probe(bank);
538 }
539
540 static int fm4_get_info_command(struct flash_bank *bank, char *buf, int buf_size)
541 {
542 struct fm4_flash_bank *fm4_bank = bank->driver_priv;
543 const char *name;
544
545 if (bank->target->state != TARGET_HALTED) {
546 LOG_WARNING("Cannot communicate... target not halted.");
547 return ERROR_TARGET_NOT_HALTED;
548 }
549
550 switch (fm4_bank->variant) {
551 case mb9bfx64:
552 name = "MB9BFx64";
553 break;
554 case mb9bfx65:
555 name = "MB9BFx65";
556 break;
557 case mb9bfx66:
558 name = "MB9BFx66";
559 break;
560 case mb9bfx67:
561 name = "MB9BFx67";
562 break;
563 case mb9bfx68:
564 name = "MB9BFx68";
565 break;
566 case s6e2cx8:
567 name = "S6E2Cx8";
568 break;
569 case s6e2cx9:
570 name = "S6E2Cx9";
571 break;
572 case s6e2cxa:
573 name = "S6E2CxA";
574 break;
575 case s6e2dx:
576 name = "S6E2Dx";
577 break;
578 default:
579 name = "unknown";
580 break;
581 }
582
583 switch (fm4_bank->variant) {
584 case s6e2cx8:
585 case s6e2cx9:
586 case s6e2cxa:
587 snprintf(buf, buf_size, "%s MainFlash Macro #%i",
588 name, fm4_bank->macro_nr);
589 break;
590 default:
591 snprintf(buf, buf_size, "%s MainFlash", name);
592 break;
593 }
594
595 return ERROR_OK;
596 }
597
598 static bool fm4_name_match(const char *s, const char *pattern)
599 {
600 int i = 0;
601
602 while (s[i]) {
603 /* If the match string is shorter, ignore excess */
604 if (!pattern[i])
605 return true;
606 /* Use x as wildcard */
607 if (pattern[i] != 'x' && tolower(s[i]) != tolower(pattern[i]))
608 return false;
609 i++;
610 }
611 return true;
612 }
613
614 static int mb9bf_bank_setup(struct flash_bank *bank, const char *variant)
615 {
616 struct fm4_flash_bank *fm4_bank = bank->driver_priv;
617
618 if (fm4_name_match(variant, "MB9BFx64")) {
619 fm4_bank->variant = mb9bfx64;
620 } else if (fm4_name_match(variant, "MB9BFx65")) {
621 fm4_bank->variant = mb9bfx65;
622 } else if (fm4_name_match(variant, "MB9BFx66")) {
623 fm4_bank->variant = mb9bfx66;
624 } else if (fm4_name_match(variant, "MB9BFx67")) {
625 fm4_bank->variant = mb9bfx67;
626 } else if (fm4_name_match(variant, "MB9BFx68")) {
627 fm4_bank->variant = mb9bfx68;
628 } else {
629 LOG_WARNING("MB9BF variant %s not recognized.", variant);
630 return ERROR_FLASH_OPER_UNSUPPORTED;
631 }
632
633 return ERROR_OK;
634 }
635
636 static int s6e2cc_bank_setup(struct flash_bank *bank, const char *variant)
637 {
638 struct fm4_flash_bank *fm4_bank = bank->driver_priv;
639
640 if (fm4_name_match(variant, "S6E2Cx8")) {
641 fm4_bank->variant = s6e2cx8;
642 } else if (fm4_name_match(variant, "S6E2Cx9")) {
643 fm4_bank->variant = s6e2cx9;
644 } else if (fm4_name_match(variant, "S6E2CxA")) {
645 fm4_bank->variant = s6e2cxa;
646 } else {
647 LOG_WARNING("S6E2CC variant %s not recognized.", variant);
648 return ERROR_FLASH_OPER_UNSUPPORTED;
649 }
650
651 return ERROR_OK;
652 }
653
654 FLASH_BANK_COMMAND_HANDLER(fm4_flash_bank_command)
655 {
656 struct fm4_flash_bank *fm4_bank;
657 const char *variant;
658 int ret;
659
660 if (CMD_ARGC < 7)
661 return ERROR_COMMAND_SYNTAX_ERROR;
662
663 variant = CMD_ARGV[6];
664
665 fm4_bank = malloc(sizeof(struct fm4_flash_bank));
666 if (!fm4_bank)
667 return ERROR_FLASH_OPERATION_FAILED;
668
669 fm4_bank->probed = false;
670 fm4_bank->macro_nr = (bank->base == 0x00000000) ? 0 : 1;
671
672 bank->driver_priv = fm4_bank;
673
674 if (fm4_name_match(variant, "MB9BF"))
675 ret = mb9bf_bank_setup(bank, variant);
676 else if (fm4_name_match(variant, "S6E2Cx"))
677 ret = s6e2cc_bank_setup(bank, variant);
678 else if (fm4_name_match(variant, "S6E2Dx")) {
679 fm4_bank->variant = s6e2dx;
680 ret = ERROR_OK;
681 } else {
682 LOG_WARNING("Family %s not recognized.", variant);
683 ret = ERROR_FLASH_OPER_UNSUPPORTED;
684 }
685 if (ret != ERROR_OK)
686 free(fm4_bank);
687 return ret;
688 }
689
690 static const struct command_registration fm4_exec_command_handlers[] = {
691 COMMAND_REGISTRATION_DONE
692 };
693
694 static const struct command_registration fm4_command_handlers[] = {
695 {
696 .name = "fm4",
697 .mode = COMMAND_ANY,
698 .help = "fm4 flash command group",
699 .usage = "",
700 .chain = fm4_exec_command_handlers,
701 },
702 COMMAND_REGISTRATION_DONE
703 };
704
705 struct flash_driver fm4_flash = {
706 .name = "fm4",
707 .commands = fm4_command_handlers,
708 .flash_bank_command = fm4_flash_bank_command,
709 .info = fm4_get_info_command,
710 .probe = fm4_probe,
711 .auto_probe = fm4_auto_probe,
712 .read = default_flash_read,
713 .erase = fm4_flash_erase,
714 .erase_check = default_flash_blank_check,
715 .write = fm4_flash_write,
716 .free_driver_priv = default_flash_free_driver_priv,
717 };

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)