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

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)