63cfd18f70f511299d77915d8ae1cc135b79e3d1
[openocd.git] / src / flash / nor / str9x.c
1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
4 * *
5 * Copyright (C) 2008 by Spencer Oliver *
6 * spen@spen-soft.co.uk *
7 *
8 * Copyright (C) 2008 by Oyvind Harboe *
9 * oyvind.harboe@zylin.com *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * GNU General Public License for more details. *
20 * *
21 * You should have received a copy of the GNU General Public License *
22 * along with this program; if not, write to the *
23 * Free Software Foundation, Inc., *
24 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
25 ***************************************************************************/
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include "imp.h"
31 #include <target/arm966e.h>
32 #include <target/algorithm.h>
33
34
35 /* Flash registers */
36
37 #define FLASH_BBSR 0x54000000 /* Boot Bank Size Register */
38 #define FLASH_NBBSR 0x54000004 /* Non-Boot Bank Size Register */
39 #define FLASH_BBADR 0x5400000C /* Boot Bank Base Address Register */
40 #define FLASH_NBBADR 0x54000010 /* Non-Boot Bank Base Address Register */
41 #define FLASH_CR 0x54000018 /* Control Register */
42 #define FLASH_SR 0x5400001C /* Status Register */
43 #define FLASH_BCE5ADDR 0x54000020 /* BC Fifth Entry Target Address Register */
44
45
46 struct str9x_flash_bank
47 {
48 uint32_t *sector_bits;
49 int variant;
50 int bank1;
51 struct working_area *write_algorithm;
52 };
53
54 enum str9x_status_codes
55 {
56 STR9X_CMD_SUCCESS = 0,
57 STR9X_INVALID_COMMAND = 1,
58 STR9X_SRC_ADDR_ERROR = 2,
59 STR9X_DST_ADDR_ERROR = 3,
60 STR9X_SRC_ADDR_NOT_MAPPED = 4,
61 STR9X_DST_ADDR_NOT_MAPPED = 5,
62 STR9X_COUNT_ERROR = 6,
63 STR9X_INVALID_SECTOR = 7,
64 STR9X_SECTOR_NOT_BLANK = 8,
65 STR9X_SECTOR_NOT_PREPARED = 9,
66 STR9X_COMPARE_ERROR = 10,
67 STR9X_BUSY = 11
68 };
69
70 static uint32_t bank1start = 0x00080000;
71
72 static int str9x_build_block_list(struct flash_bank *bank)
73 {
74 struct str9x_flash_bank *str9x_info = bank->driver_priv;
75
76 int i;
77 int num_sectors;
78 int b0_sectors = 0, b1_sectors = 0;
79 uint32_t offset = 0;
80
81 /* set if we have large flash str9 */
82 str9x_info->variant = 0;
83 str9x_info->bank1 = 0;
84
85 switch (bank->size)
86 {
87 case (256 * 1024):
88 b0_sectors = 4;
89 break;
90 case (512 * 1024):
91 b0_sectors = 8;
92 break;
93 case (1024 * 1024):
94 bank1start = 0x00100000;
95 str9x_info->variant = 1;
96 b0_sectors = 16;
97 break;
98 case (2048 * 1024):
99 bank1start = 0x00200000;
100 str9x_info->variant = 1;
101 b0_sectors = 32;
102 break;
103 case (128 * 1024):
104 str9x_info->variant = 1;
105 str9x_info->bank1 = 1;
106 b1_sectors = 8;
107 bank1start = bank->base;
108 break;
109 case (32 * 1024):
110 str9x_info->bank1 = 1;
111 b1_sectors = 4;
112 bank1start = bank->base;
113 break;
114 default:
115 LOG_ERROR("BUG: unknown bank->size encountered");
116 exit(-1);
117 }
118
119 num_sectors = b0_sectors + b1_sectors;
120
121 bank->num_sectors = num_sectors;
122 bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors);
123 str9x_info->sector_bits = malloc(sizeof(uint32_t) * num_sectors);
124
125 num_sectors = 0;
126
127 for (i = 0; i < b0_sectors; i++)
128 {
129 bank->sectors[num_sectors].offset = offset;
130 bank->sectors[num_sectors].size = 0x10000;
131 offset += bank->sectors[i].size;
132 bank->sectors[num_sectors].is_erased = -1;
133 bank->sectors[num_sectors].is_protected = 1;
134 str9x_info->sector_bits[num_sectors++] = (1 << i);
135 }
136
137 for (i = 0; i < b1_sectors; i++)
138 {
139 bank->sectors[num_sectors].offset = offset;
140 bank->sectors[num_sectors].size = str9x_info->variant == 0 ? 0x2000 : 0x4000;
141 offset += bank->sectors[i].size;
142 bank->sectors[num_sectors].is_erased = -1;
143 bank->sectors[num_sectors].is_protected = 1;
144 if (str9x_info->variant)
145 str9x_info->sector_bits[num_sectors++] = (1 << i);
146 else
147 str9x_info->sector_bits[num_sectors++] = (1 << (i + 8));
148 }
149
150 return ERROR_OK;
151 }
152
153 /* flash bank str9x <base> <size> 0 0 <target#>
154 */
155 FLASH_BANK_COMMAND_HANDLER(str9x_flash_bank_command)
156 {
157 struct str9x_flash_bank *str9x_info;
158
159 if (CMD_ARGC < 6)
160 {
161 LOG_WARNING("incomplete flash_bank str9x configuration");
162 return ERROR_FLASH_BANK_INVALID;
163 }
164
165 str9x_info = malloc(sizeof(struct str9x_flash_bank));
166 bank->driver_priv = str9x_info;
167
168 str9x_build_block_list(bank);
169
170 str9x_info->write_algorithm = NULL;
171
172 return ERROR_OK;
173 }
174
175 static int str9x_protect_check(struct flash_bank *bank)
176 {
177 int retval;
178 struct str9x_flash_bank *str9x_info = bank->driver_priv;
179 struct target *target = bank->target;
180
181 int i;
182 uint32_t adr;
183 uint32_t status = 0;
184 uint16_t hstatus = 0;
185
186 if (bank->target->state != TARGET_HALTED)
187 {
188 LOG_ERROR("Target not halted");
189 return ERROR_TARGET_NOT_HALTED;
190 }
191
192 /* read level one protection */
193
194 if (str9x_info->variant)
195 {
196 if (str9x_info->bank1)
197 {
198 adr = bank1start + 0x18;
199 if ((retval = target_write_u16(target, adr, 0x90)) != ERROR_OK)
200 {
201 return retval;
202 }
203 if ((retval = target_read_u16(target, adr, &hstatus)) != ERROR_OK)
204 {
205 return retval;
206 }
207 status = hstatus;
208 }
209 else
210 {
211 adr = bank1start + 0x14;
212 if ((retval = target_write_u16(target, adr, 0x90)) != ERROR_OK)
213 {
214 return retval;
215 }
216 if ((retval = target_read_u32(target, adr, &status)) != ERROR_OK)
217 {
218 return retval;
219 }
220 }
221 }
222 else
223 {
224 adr = bank1start + 0x10;
225 if ((retval = target_write_u16(target, adr, 0x90)) != ERROR_OK)
226 {
227 return retval;
228 }
229 if ((retval = target_read_u16(target, adr, &hstatus)) != ERROR_OK)
230 {
231 return retval;
232 }
233 status = hstatus;
234 }
235
236 /* read array command */
237 if ((retval = target_write_u16(target, adr, 0xFF)) != ERROR_OK)
238 {
239 return retval;
240 }
241
242 for (i = 0; i < bank->num_sectors; i++)
243 {
244 if (status & str9x_info->sector_bits[i])
245 bank->sectors[i].is_protected = 1;
246 else
247 bank->sectors[i].is_protected = 0;
248 }
249
250 return ERROR_OK;
251 }
252
253 static int str9x_erase(struct flash_bank *bank, int first, int last)
254 {
255 struct target *target = bank->target;
256 int i;
257 uint32_t adr;
258 uint8_t status;
259 uint8_t erase_cmd;
260 int total_timeout;
261
262 if (bank->target->state != TARGET_HALTED)
263 {
264 LOG_ERROR("Target not halted");
265 return ERROR_TARGET_NOT_HALTED;
266 }
267
268 /* Check if we can erase whole bank */
269 if ((first == 0) && (last == (bank->num_sectors - 1)))
270 {
271 /* Optimize to run erase bank command instead of sector */
272 erase_cmd = 0x80;
273 /* Add timeout duration since erase bank takes more time */
274 total_timeout = 1000 * bank->num_sectors;
275 }
276 else
277 {
278 /* Erase sector command */
279 erase_cmd = 0x20;
280 total_timeout = 1000;
281 }
282
283 /* this is so the compiler can *know* */
284 assert(total_timeout > 0);
285
286 for (i = first; i <= last; i++)
287 {
288 int retval;
289 adr = bank->base + bank->sectors[i].offset;
290
291 /* erase sectors or block */
292 if ((retval = target_write_u16(target, adr, erase_cmd)) != ERROR_OK)
293 {
294 return retval;
295 }
296 if ((retval = target_write_u16(target, adr, 0xD0)) != ERROR_OK)
297 {
298 return retval;
299 }
300
301 /* get status */
302 if ((retval = target_write_u16(target, adr, 0x70)) != ERROR_OK)
303 {
304 return retval;
305 }
306
307 int timeout;
308 for (timeout = 0; timeout < total_timeout; timeout++)
309 {
310 if ((retval = target_read_u8(target, adr, &status)) != ERROR_OK)
311 {
312 return retval;
313 }
314 if (status & 0x80)
315 break;
316 alive_sleep(1);
317 }
318 if (timeout == total_timeout)
319 {
320 LOG_ERROR("erase timed out");
321 return ERROR_FAIL;
322 }
323
324 /* clear status, also clear read array */
325 if ((retval = target_write_u16(target, adr, 0x50)) != ERROR_OK)
326 {
327 return retval;
328 }
329
330 /* read array command */
331 if ((retval = target_write_u16(target, adr, 0xFF)) != ERROR_OK)
332 {
333 return retval;
334 }
335
336 if (status & 0x22)
337 {
338 LOG_ERROR("error erasing flash bank, status: 0x%x", status);
339 return ERROR_FLASH_OPERATION_FAILED;
340 }
341
342 /* If we ran erase bank command, we are finished */
343 if (erase_cmd == 0x80)
344 break;
345 }
346
347 for (i = first; i <= last; i++)
348 bank->sectors[i].is_erased = 1;
349
350 return ERROR_OK;
351 }
352
353 static int str9x_protect(struct flash_bank *bank,
354 int set, int first, int last)
355 {
356 struct target *target = bank->target;
357 int i;
358 uint32_t adr;
359 uint8_t status;
360
361 if (bank->target->state != TARGET_HALTED)
362 {
363 LOG_ERROR("Target not halted");
364 return ERROR_TARGET_NOT_HALTED;
365 }
366
367 for (i = first; i <= last; i++)
368 {
369 /* Level One Protection */
370
371 adr = bank->base + bank->sectors[i].offset;
372
373 target_write_u16(target, adr, 0x60);
374 if (set)
375 target_write_u16(target, adr, 0x01);
376 else
377 target_write_u16(target, adr, 0xD0);
378
379 /* query status */
380 target_read_u8(target, adr, &status);
381
382 /* clear status, also clear read array */
383 target_write_u16(target, adr, 0x50);
384
385 /* read array command */
386 target_write_u16(target, adr, 0xFF);
387 }
388
389 return ERROR_OK;
390 }
391
392 static int str9x_write_block(struct flash_bank *bank,
393 uint8_t *buffer, uint32_t offset, uint32_t count)
394 {
395 struct str9x_flash_bank *str9x_info = bank->driver_priv;
396 struct target *target = bank->target;
397 uint32_t buffer_size = 32768;
398 struct working_area *source;
399 uint32_t address = bank->base + offset;
400 struct reg_param reg_params[4];
401 struct arm_algorithm armv4_5_info;
402 int retval = ERROR_OK;
403
404 /* see contib/loaders/flash/str9x.s for src */
405
406 static const uint32_t str9x_flash_write_code[] = {
407 /* write: */
408 0xe3c14003, /* bic r4, r1, #3 */
409 0xe3a03040, /* mov r3, #0x40 */
410 0xe1c430b0, /* strh r3, [r4, #0] */
411 0xe0d030b2, /* ldrh r3, [r0], #2 */
412 0xe0c130b2, /* strh r3, [r1], #2 */
413 0xe3a03070, /* mov r3, #0x70 */
414 0xe1c430b0, /* strh r3, [r4, #0] */
415 /* busy: */
416 0xe5d43000, /* ldrb r3, [r4, #0] */
417 0xe3130080, /* tst r3, #0x80 */
418 0x0afffffc, /* beq busy */
419 0xe3a05050, /* mov r5, #0x50 */
420 0xe1c450b0, /* strh r5, [r4, #0] */
421 0xe3a050ff, /* mov r5, #0xFF */
422 0xe1c450b0, /* strh r5, [r4, #0] */
423 0xe3130012, /* tst r3, #0x12 */
424 0x1a000001, /* bne exit */
425 0xe2522001, /* subs r2, r2, #1 */
426 0x1affffed, /* bne write */
427 /* exit: */
428 0xe1200070, /* bkpt #0 */
429 };
430
431 /* flash write code */
432 if (target_alloc_working_area(target, sizeof(str9x_flash_write_code),
433 &str9x_info->write_algorithm) != ERROR_OK)
434 {
435 LOG_WARNING("no working area available, can't do block memory writes");
436 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
437 };
438
439 target_write_buffer(target, str9x_info->write_algorithm->address,
440 sizeof(str9x_flash_write_code),
441 (uint8_t*)str9x_flash_write_code);
442
443 /* memory buffer */
444 while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK)
445 {
446 buffer_size /= 2;
447 if (buffer_size <= 256)
448 {
449 /* if we already allocated the writing code, but failed to get a
450 * buffer, free the algorithm */
451 if (str9x_info->write_algorithm)
452 target_free_working_area(target, str9x_info->write_algorithm);
453
454 LOG_WARNING("no large enough working area available, can't do block memory writes");
455 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
456 }
457 }
458
459 armv4_5_info.common_magic = ARM_COMMON_MAGIC;
460 armv4_5_info.core_mode = ARM_MODE_SVC;
461 armv4_5_info.core_state = ARM_STATE_ARM;
462
463 init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
464 init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
465 init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
466 init_reg_param(&reg_params[3], "r3", 32, PARAM_IN);
467
468 while (count > 0)
469 {
470 uint32_t thisrun_count = (count > (buffer_size / 2)) ? (buffer_size / 2) : count;
471
472 target_write_buffer(target, source->address, thisrun_count * 2, buffer);
473
474 buf_set_u32(reg_params[0].value, 0, 32, source->address);
475 buf_set_u32(reg_params[1].value, 0, 32, address);
476 buf_set_u32(reg_params[2].value, 0, 32, thisrun_count);
477
478 if ((retval = target_run_algorithm(target, 0, NULL, 4, reg_params,
479 str9x_info->write_algorithm->address,
480 0, 10000, &armv4_5_info)) != ERROR_OK)
481 {
482 LOG_ERROR("error executing str9x flash write algorithm");
483 retval = ERROR_FLASH_OPERATION_FAILED;
484 break;
485 }
486
487 if (buf_get_u32(reg_params[3].value, 0, 32) != 0x80)
488 {
489 retval = ERROR_FLASH_OPERATION_FAILED;
490 break;
491 }
492
493 buffer += thisrun_count * 2;
494 address += thisrun_count * 2;
495 count -= thisrun_count;
496 }
497
498 target_free_working_area(target, source);
499 target_free_working_area(target, str9x_info->write_algorithm);
500
501 destroy_reg_param(&reg_params[0]);
502 destroy_reg_param(&reg_params[1]);
503 destroy_reg_param(&reg_params[2]);
504 destroy_reg_param(&reg_params[3]);
505
506 return retval;
507 }
508
509 static int str9x_write(struct flash_bank *bank,
510 uint8_t *buffer, uint32_t offset, uint32_t count)
511 {
512 struct target *target = bank->target;
513 uint32_t words_remaining = (count / 2);
514 uint32_t bytes_remaining = (count & 0x00000001);
515 uint32_t address = bank->base + offset;
516 uint32_t bytes_written = 0;
517 uint8_t status;
518 int retval;
519 uint32_t check_address = offset;
520 uint32_t bank_adr;
521 int i;
522
523 if (bank->target->state != TARGET_HALTED)
524 {
525 LOG_ERROR("Target not halted");
526 return ERROR_TARGET_NOT_HALTED;
527 }
528
529 if (offset & 0x1)
530 {
531 LOG_WARNING("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset);
532 return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
533 }
534
535 for (i = 0; i < bank->num_sectors; i++)
536 {
537 uint32_t sec_start = bank->sectors[i].offset;
538 uint32_t sec_end = sec_start + bank->sectors[i].size;
539
540 /* check if destination falls within the current sector */
541 if ((check_address >= sec_start) && (check_address < sec_end))
542 {
543 /* check if destination ends in the current sector */
544 if (offset + count < sec_end)
545 check_address = offset + count;
546 else
547 check_address = sec_end;
548 }
549 }
550
551 if (check_address != offset + count)
552 return ERROR_FLASH_DST_OUT_OF_BANK;
553
554 /* multiple half words (2-byte) to be programmed? */
555 if (words_remaining > 0)
556 {
557 /* try using a block write */
558 if ((retval = str9x_write_block(bank, buffer, offset, words_remaining)) != ERROR_OK)
559 {
560 if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
561 {
562 /* if block write failed (no sufficient working area),
563 * we use normal (slow) single dword accesses */
564 LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
565 }
566 else if (retval == ERROR_FLASH_OPERATION_FAILED)
567 {
568 LOG_ERROR("flash writing failed");
569 return ERROR_FLASH_OPERATION_FAILED;
570 }
571 }
572 else
573 {
574 buffer += words_remaining * 2;
575 address += words_remaining * 2;
576 words_remaining = 0;
577 }
578 }
579
580 while (words_remaining > 0)
581 {
582 bank_adr = address & ~0x03;
583
584 /* write data command */
585 target_write_u16(target, bank_adr, 0x40);
586 target_write_memory(target, address, 2, 1, buffer + bytes_written);
587
588 /* get status command */
589 target_write_u16(target, bank_adr, 0x70);
590
591 int timeout;
592 for (timeout = 0; timeout < 1000; timeout++)
593 {
594 target_read_u8(target, bank_adr, &status);
595 if (status & 0x80)
596 break;
597 alive_sleep(1);
598 }
599 if (timeout == 1000)
600 {
601 LOG_ERROR("write timed out");
602 return ERROR_FAIL;
603 }
604
605 /* clear status reg and read array */
606 target_write_u16(target, bank_adr, 0x50);
607 target_write_u16(target, bank_adr, 0xFF);
608
609 if (status & 0x10)
610 return ERROR_FLASH_OPERATION_FAILED;
611 else if (status & 0x02)
612 return ERROR_FLASH_OPERATION_FAILED;
613
614 bytes_written += 2;
615 words_remaining--;
616 address += 2;
617 }
618
619 if (bytes_remaining)
620 {
621 uint8_t last_halfword[2] = {0xff, 0xff};
622 i = 0;
623
624 while (bytes_remaining > 0)
625 {
626 last_halfword[i++] = *(buffer + bytes_written);
627 bytes_remaining--;
628 bytes_written++;
629 }
630
631 bank_adr = address & ~0x03;
632
633 /* write data command */
634 target_write_u16(target, bank_adr, 0x40);
635 target_write_memory(target, address, 2, 1, last_halfword);
636
637 /* query status command */
638 target_write_u16(target, bank_adr, 0x70);
639
640 int timeout;
641 for (timeout = 0; timeout < 1000; timeout++)
642 {
643 target_read_u8(target, bank_adr, &status);
644 if (status & 0x80)
645 break;
646 alive_sleep(1);
647 }
648 if (timeout == 1000)
649 {
650 LOG_ERROR("write timed out");
651 return ERROR_FAIL;
652 }
653
654 /* clear status reg and read array */
655 target_write_u16(target, bank_adr, 0x50);
656 target_write_u16(target, bank_adr, 0xFF);
657
658 if (status & 0x10)
659 return ERROR_FLASH_OPERATION_FAILED;
660 else if (status & 0x02)
661 return ERROR_FLASH_OPERATION_FAILED;
662 }
663
664 return ERROR_OK;
665 }
666
667 static int str9x_probe(struct flash_bank *bank)
668 {
669 return ERROR_OK;
670 }
671
672 #if 0
673 COMMAND_HANDLER(str9x_handle_part_id_command)
674 {
675 return ERROR_OK;
676 }
677 #endif
678
679 static int get_str9x_info(struct flash_bank *bank, char *buf, int buf_size)
680 {
681 snprintf(buf, buf_size, "str9x flash driver info");
682 return ERROR_OK;
683 }
684
685 COMMAND_HANDLER(str9x_handle_flash_config_command)
686 {
687 struct target *target = NULL;
688
689 if (CMD_ARGC < 5)
690 {
691 return ERROR_COMMAND_SYNTAX_ERROR;
692 }
693
694 struct flash_bank *bank;
695 int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
696 if (ERROR_OK != retval)
697 return retval;
698
699 uint32_t bbsr, nbbsr, bbadr, nbbadr;
700 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], bbsr);
701 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], nbbsr);
702 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], bbadr);
703 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[4], nbbadr);
704
705 target = bank->target;
706
707 if (bank->target->state != TARGET_HALTED)
708 {
709 LOG_ERROR("Target not halted");
710 return ERROR_TARGET_NOT_HALTED;
711 }
712
713 /* config flash controller */
714 target_write_u32(target, FLASH_BBSR, bbsr);
715 target_write_u32(target, FLASH_NBBSR, nbbsr);
716 target_write_u32(target, FLASH_BBADR, bbadr >> 2);
717 target_write_u32(target, FLASH_NBBADR, nbbadr >> 2);
718
719 /* set bit 18 instruction TCM order as per flash programming manual */
720 arm966e_write_cp15(target, 62, 0x40000);
721
722 /* enable flash bank 1 */
723 target_write_u32(target, FLASH_CR, 0x18);
724 return ERROR_OK;
725 }
726
727 static const struct command_registration str9x_config_command_handlers[] = {
728 {
729 .name = "flash_config",
730 .handler = str9x_handle_flash_config_command,
731 .mode = COMMAND_EXEC,
732 .help = "Configure str9x flash controller, prior to "
733 "programming the flash.",
734 .usage = "bank_id BBSR NBBSR BBADR NBBADR",
735 },
736 COMMAND_REGISTRATION_DONE
737 };
738
739 static const struct command_registration str9x_command_handlers[] = {
740 {
741 .name = "str9x",
742 .mode = COMMAND_ANY,
743 .help = "str9x flash command group",
744 .chain = str9x_config_command_handlers,
745 },
746 COMMAND_REGISTRATION_DONE
747 };
748
749 struct flash_driver str9x_flash = {
750 .name = "str9x",
751 .commands = str9x_command_handlers,
752 .flash_bank_command = str9x_flash_bank_command,
753 .erase = str9x_erase,
754 .protect = str9x_protect,
755 .write = str9x_write,
756 .read = default_flash_read,
757 .probe = str9x_probe,
758 .auto_probe = str9x_probe,
759 .erase_check = default_flash_blank_check,
760 .protect_check = str9x_protect_check,
761 .info = get_str9x_info,
762 };

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)