jtag: linuxgpiod: drop extra parenthesis
[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, unsigned int first,
102 unsigned int last)
103 {
104 struct target *target = bank->target;
105 struct working_area *workarea;
106 struct reg_param reg_params[4];
107 struct armv7m_algorithm armv7m_algo;
108 unsigned i;
109 int retval;
110 const uint8_t erase_sector_code[] = {
111 #include "../../../contrib/loaders/flash/fm4/erase.inc"
112 };
113
114 if (target->state != TARGET_HALTED) {
115 LOG_WARNING("Cannot communicate... target not halted.");
116 return ERROR_TARGET_NOT_HALTED;
117 }
118
119 LOG_DEBUG("Spansion FM4 erase sectors %u to %u", first, last);
120
121 retval = fm4_disable_hw_watchdog(target);
122 if (retval != ERROR_OK)
123 return retval;
124
125 retval = fm4_enter_flash_cpu_programming_mode(target);
126 if (retval != ERROR_OK)
127 return retval;
128
129 retval = target_alloc_working_area(target, sizeof(erase_sector_code),
130 &workarea);
131 if (retval != ERROR_OK) {
132 LOG_ERROR("No working area available.");
133 retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
134 goto err_alloc_code;
135 }
136 retval = target_write_buffer(target, workarea->address,
137 sizeof(erase_sector_code), erase_sector_code);
138 if (retval != ERROR_OK)
139 goto err_write_code;
140
141 armv7m_algo.common_magic = ARMV7M_COMMON_MAGIC;
142 armv7m_algo.core_mode = ARM_MODE_THREAD;
143
144 init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
145 init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
146 init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
147 init_reg_param(&reg_params[3], "r3", 32, PARAM_IN);
148
149 for (unsigned int sector = first; sector <= last; sector++) {
150 uint32_t addr = bank->base + bank->sectors[sector].offset;
151 uint32_t result;
152
153 buf_set_u32(reg_params[0].value, 0, 32, (addr & ~0xffff) | 0xAA8);
154 buf_set_u32(reg_params[1].value, 0, 32, (addr & ~0xffff) | 0x554);
155 buf_set_u32(reg_params[2].value, 0, 32, addr);
156
157 retval = target_run_algorithm(target,
158 0, NULL,
159 ARRAY_SIZE(reg_params), reg_params,
160 workarea->address, 0,
161 1000, &armv7m_algo);
162 if (retval != ERROR_OK) {
163 LOG_ERROR("Error executing flash sector erase "
164 "programming algorithm");
165 retval = ERROR_FLASH_OPERATION_FAILED;
166 goto err_run;
167 }
168
169 result = buf_get_u32(reg_params[3].value, 0, 32);
170 if (result == 2) {
171 LOG_ERROR("Timeout error from flash sector erase programming algorithm");
172 retval = ERROR_FLASH_OPERATION_FAILED;
173 goto err_run_ret;
174 } else if (result != 0) {
175 LOG_ERROR("Unexpected error %" PRIu32 " from flash sector erase programming algorithm", result);
176 retval = ERROR_FLASH_OPERATION_FAILED;
177 goto err_run_ret;
178 } else
179 retval = ERROR_OK;
180 }
181
182 err_run_ret:
183 err_run:
184 for (i = 0; i < ARRAY_SIZE(reg_params); i++)
185 destroy_reg_param(&reg_params[i]);
186
187 err_write_code:
188 target_free_working_area(target, workarea);
189
190 err_alloc_code:
191 if (retval != ERROR_OK)
192 fm4_enter_flash_cpu_rom_mode(target);
193 else
194 retval = fm4_enter_flash_cpu_rom_mode(target);
195
196 return retval;
197 }
198
199 static int fm4_flash_write(struct flash_bank *bank, const uint8_t *buffer,
200 uint32_t offset, uint32_t byte_count)
201 {
202 struct target *target = bank->target;
203 struct working_area *code_workarea, *data_workarea;
204 struct reg_param reg_params[6];
205 struct armv7m_algorithm armv7m_algo;
206 uint32_t halfword_count = DIV_ROUND_UP(byte_count, 2);
207 uint32_t result;
208 unsigned i;
209 int retval, retval2 = ERROR_OK;
210 const uint8_t write_block_code[] = {
211 #include "../../../contrib/loaders/flash/fm4/write.inc"
212 };
213
214 LOG_DEBUG("Spansion FM4 write at 0x%08" PRIx32 " (%" PRIu32 " bytes)",
215 offset, byte_count);
216
217 if (offset & 0x1) {
218 LOG_ERROR("offset 0x%" PRIx32 " breaks required 2-byte alignment",
219 offset);
220 return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
221 }
222 if (byte_count & 0x1) {
223 LOG_WARNING("length %" PRIu32 " is not 2-byte aligned, rounding up",
224 byte_count);
225 }
226
227 if (target->state != TARGET_HALTED) {
228 LOG_WARNING("Cannot communicate... target not halted.");
229 return ERROR_TARGET_NOT_HALTED;
230 }
231
232 retval = fm4_disable_hw_watchdog(target);
233 if (retval != ERROR_OK)
234 return retval;
235
236 retval = target_alloc_working_area(target, sizeof(write_block_code),
237 &code_workarea);
238 if (retval != ERROR_OK) {
239 LOG_ERROR("No working area available for write code.");
240 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
241 }
242 retval = target_write_buffer(target, code_workarea->address,
243 sizeof(write_block_code), write_block_code);
244 if (retval != ERROR_OK)
245 goto err_write_code;
246
247 retval = target_alloc_working_area(target,
248 MIN(halfword_count * 2, target_get_working_area_avail(target)),
249 &data_workarea);
250 if (retval != ERROR_OK) {
251 LOG_ERROR("No working area available for write data.");
252 retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
253 goto err_alloc_data;
254 }
255
256 armv7m_algo.common_magic = ARMV7M_COMMON_MAGIC;
257 armv7m_algo.core_mode = ARM_MODE_THREAD;
258
259 init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
260 init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
261 init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
262 init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT);
263 init_reg_param(&reg_params[4], "r4", 32, PARAM_OUT);
264 init_reg_param(&reg_params[5], "r5", 32, PARAM_IN);
265
266 retval = fm4_enter_flash_cpu_programming_mode(target);
267 if (retval != ERROR_OK)
268 goto err_flash_mode;
269
270 while (byte_count > 0) {
271 uint32_t halfwords = MIN(halfword_count, data_workarea->size / 2);
272 uint32_t addr = bank->base + offset;
273
274 LOG_DEBUG("copying %" PRIu32 " bytes to SRAM " TARGET_ADDR_FMT,
275 MIN(halfwords * 2, byte_count), data_workarea->address);
276
277 retval = target_write_buffer(target, data_workarea->address,
278 MIN(halfwords * 2, byte_count), buffer);
279 if (retval != ERROR_OK) {
280 LOG_ERROR("Error writing data buffer");
281 retval = ERROR_FLASH_OPERATION_FAILED;
282 goto err_write_data;
283 }
284
285 LOG_DEBUG("writing 0x%08" PRIx32 "-0x%08" PRIx32 " (%" PRIu32 "x)",
286 addr, addr + halfwords * 2 - 1, halfwords);
287
288 buf_set_u32(reg_params[0].value, 0, 32, (addr & ~0xffff) | 0xAA8);
289 buf_set_u32(reg_params[1].value, 0, 32, (addr & ~0xffff) | 0x554);
290 buf_set_u32(reg_params[2].value, 0, 32, addr);
291 buf_set_u32(reg_params[3].value, 0, 32, data_workarea->address);
292 buf_set_u32(reg_params[4].value, 0, 32, halfwords);
293
294 retval = target_run_algorithm(target,
295 0, NULL,
296 ARRAY_SIZE(reg_params), reg_params,
297 code_workarea->address, 0,
298 5 * 60 * 1000, &armv7m_algo);
299 if (retval != ERROR_OK) {
300 LOG_ERROR("Error executing flash sector erase "
301 "programming algorithm");
302 retval = ERROR_FLASH_OPERATION_FAILED;
303 goto err_run;
304 }
305
306 result = buf_get_u32(reg_params[5].value, 0, 32);
307 if (result == 2) {
308 LOG_ERROR("Timeout error from flash write "
309 "programming algorithm");
310 retval = ERROR_FLASH_OPERATION_FAILED;
311 goto err_run_ret;
312 } else if (result != 0) {
313 LOG_ERROR("Unexpected error %" PRIu32 " from flash write "
314 "programming algorithm", result);
315 retval = ERROR_FLASH_OPERATION_FAILED;
316 goto err_run_ret;
317 } else
318 retval = ERROR_OK;
319
320 halfword_count -= halfwords;
321 offset += halfwords * 2;
322 buffer += halfwords * 2;
323 byte_count -= MIN(halfwords * 2, byte_count);
324 }
325
326 err_run_ret:
327 err_run:
328 err_write_data:
329 retval2 = fm4_enter_flash_cpu_rom_mode(target);
330
331 err_flash_mode:
332 for (i = 0; i < ARRAY_SIZE(reg_params); i++)
333 destroy_reg_param(&reg_params[i]);
334
335 target_free_working_area(target, data_workarea);
336 err_alloc_data:
337 err_write_code:
338 target_free_working_area(target, code_workarea);
339
340 if (retval != ERROR_OK)
341 return retval;
342 return retval2;
343 }
344
345 static int mb9bf_probe(struct flash_bank *bank)
346 {
347 struct fm4_flash_bank *fm4_bank = bank->driver_priv;
348 uint32_t flash_addr = bank->base;
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("%u sectors", bank->num_sectors);
371 bank->sectors = calloc(bank->num_sectors,
372 sizeof(struct flash_sector));
373 for (unsigned int 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 retval;
411 unsigned int i, num_extra_sectors, num_sectors;
412
413 retval = target_read_u32(target, DFCTRLR, &u32_value);
414 if (retval != ERROR_OK)
415 return retval;
416 if (u32_value & DFCTRLR_DFE) {
417 LOG_WARNING("Dual Flash mode is not implemented.");
418 return ERROR_FLASH_OPER_UNSUPPORTED;
419 }
420
421 switch (fm4_bank->variant) {
422 case S6E2CX8:
423 num_sectors = (fm4_bank->macro_nr == 0) ? 20 : 0;
424 break;
425 case S6E2CX9:
426 num_sectors = (fm4_bank->macro_nr == 0) ? 20 : 12;
427 break;
428 case S6E2CXA:
429 num_sectors = 20;
430 break;
431 default:
432 return ERROR_FLASH_OPER_UNSUPPORTED;
433 }
434 num_extra_sectors = (fm4_bank->macro_nr == 0) ? 1 : 4;
435 bank->num_sectors = num_sectors + num_extra_sectors;
436
437 LOG_DEBUG("%u sectors", bank->num_sectors);
438 bank->sectors = calloc(bank->num_sectors,
439 sizeof(struct flash_sector));
440 for (i = 0; i < num_sectors; i++) {
441 int sa = 4 + i;
442 bank->sectors[i].offset = flash_addr - bank->base;
443 s6e2cc_init_sector(&bank->sectors[i], sa);
444
445 bank->size += bank->sectors[i].size;
446 flash_addr += bank->sectors[i].size;
447 }
448
449 flash_addr = (fm4_bank->macro_nr == 0) ? 0x00406000 : 0x00408000;
450 for (; i < bank->num_sectors; i++) {
451 int sa = 4 - num_extra_sectors + (i - num_sectors);
452 bank->sectors[i].offset = flash_addr - bank->base;
453 s6e2cc_init_sector(&bank->sectors[i], sa);
454
455 /*
456 * Don't increase bank->size for these sectors
457 * to avoid an overlap between Flash Macros #0 and #1.
458 */
459 flash_addr += bank->sectors[i].size;
460 }
461
462 return ERROR_OK;
463 }
464
465 static int s6e2dh_probe(struct flash_bank *bank)
466 {
467 uint32_t flash_addr = bank->base;
468
469 bank->num_sectors = 10;
470 bank->sectors = calloc(bank->num_sectors,
471 sizeof(struct flash_sector));
472 for (unsigned int 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, struct command_invocation *cmd)
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 command_print_sameline(cmd, "%s MainFlash Macro #%i", name, fm4_bank->macro_nr);
588 break;
589 default:
590 command_print_sameline(cmd, "%s MainFlash", name);
591 break;
592 }
593
594 return ERROR_OK;
595 }
596
597 static bool fm4_name_match(const char *s, const char *pattern)
598 {
599 int i = 0;
600
601 while (s[i]) {
602 /* If the match string is shorter, ignore excess */
603 if (!pattern[i])
604 return true;
605 /* Use x as wildcard */
606 if (pattern[i] != 'x' && tolower(s[i]) != tolower(pattern[i]))
607 return false;
608 i++;
609 }
610 return true;
611 }
612
613 static int mb9bf_bank_setup(struct flash_bank *bank, const char *variant)
614 {
615 struct fm4_flash_bank *fm4_bank = bank->driver_priv;
616
617 if (fm4_name_match(variant, "MB9BFx64")) {
618 fm4_bank->variant = MB9BFX64;
619 } else if (fm4_name_match(variant, "MB9BFx65")) {
620 fm4_bank->variant = MB9BFX65;
621 } else if (fm4_name_match(variant, "MB9BFx66")) {
622 fm4_bank->variant = MB9BFX66;
623 } else if (fm4_name_match(variant, "MB9BFx67")) {
624 fm4_bank->variant = MB9BFX67;
625 } else if (fm4_name_match(variant, "MB9BFx68")) {
626 fm4_bank->variant = MB9BFX68;
627 } else {
628 LOG_WARNING("MB9BF variant %s not recognized.", variant);
629 return ERROR_FLASH_OPER_UNSUPPORTED;
630 }
631
632 return ERROR_OK;
633 }
634
635 static int s6e2cc_bank_setup(struct flash_bank *bank, const char *variant)
636 {
637 struct fm4_flash_bank *fm4_bank = bank->driver_priv;
638
639 if (fm4_name_match(variant, "S6E2Cx8")) {
640 fm4_bank->variant = S6E2CX8;
641 } else if (fm4_name_match(variant, "S6E2Cx9")) {
642 fm4_bank->variant = S6E2CX9;
643 } else if (fm4_name_match(variant, "S6E2CxA")) {
644 fm4_bank->variant = S6E2CXA;
645 } else {
646 LOG_WARNING("S6E2CC variant %s not recognized.", variant);
647 return ERROR_FLASH_OPER_UNSUPPORTED;
648 }
649
650 return ERROR_OK;
651 }
652
653 FLASH_BANK_COMMAND_HANDLER(fm4_flash_bank_command)
654 {
655 struct fm4_flash_bank *fm4_bank;
656 const char *variant;
657 int ret;
658
659 if (CMD_ARGC < 7)
660 return ERROR_COMMAND_SYNTAX_ERROR;
661
662 variant = CMD_ARGV[6];
663
664 fm4_bank = malloc(sizeof(struct fm4_flash_bank));
665 if (!fm4_bank)
666 return ERROR_FLASH_OPERATION_FAILED;
667
668 fm4_bank->probed = false;
669 fm4_bank->macro_nr = (bank->base == 0x00000000) ? 0 : 1;
670
671 bank->driver_priv = fm4_bank;
672
673 if (fm4_name_match(variant, "MB9BF"))
674 ret = mb9bf_bank_setup(bank, variant);
675 else if (fm4_name_match(variant, "S6E2Cx"))
676 ret = s6e2cc_bank_setup(bank, variant);
677 else if (fm4_name_match(variant, "S6E2Dx")) {
678 fm4_bank->variant = S6E2DX;
679 ret = ERROR_OK;
680 } else {
681 LOG_WARNING("Family %s not recognized.", variant);
682 ret = ERROR_FLASH_OPER_UNSUPPORTED;
683 }
684 if (ret != ERROR_OK)
685 free(fm4_bank);
686 return ret;
687 }
688
689 static const struct command_registration fm4_exec_command_handlers[] = {
690 COMMAND_REGISTRATION_DONE
691 };
692
693 static const struct command_registration fm4_command_handlers[] = {
694 {
695 .name = "fm4",
696 .mode = COMMAND_ANY,
697 .help = "fm4 flash command group",
698 .usage = "",
699 .chain = fm4_exec_command_handlers,
700 },
701 COMMAND_REGISTRATION_DONE
702 };
703
704 const struct flash_driver fm4_flash = {
705 .name = "fm4",
706 .commands = fm4_command_handlers,
707 .flash_bank_command = fm4_flash_bank_command,
708 .info = fm4_get_info_command,
709 .probe = fm4_probe,
710 .auto_probe = fm4_auto_probe,
711 .read = default_flash_read,
712 .erase = fm4_flash_erase,
713 .erase_check = default_flash_blank_check,
714 .write = fm4_flash_write,
715 .free_driver_priv = default_flash_free_driver_priv,
716 };

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)