FLASH/NOR: Remove useless file str9x.h
[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 for (i = first; i <= last; i++)
284 {
285 int retval;
286 adr = bank->base + bank->sectors[i].offset;
287
288 /* erase sectors or block */
289 if ((retval = target_write_u16(target, adr, erase_cmd)) != ERROR_OK)
290 {
291 return retval;
292 }
293 if ((retval = target_write_u16(target, adr, 0xD0)) != ERROR_OK)
294 {
295 return retval;
296 }
297
298 /* get status */
299 if ((retval = target_write_u16(target, adr, 0x70)) != ERROR_OK)
300 {
301 return retval;
302 }
303
304 int timeout;
305 for (timeout = 0; timeout < total_timeout; timeout++)
306 {
307 if ((retval = target_read_u8(target, adr, &status)) != ERROR_OK)
308 {
309 return retval;
310 }
311 if (status & 0x80)
312 break;
313 alive_sleep(1);
314 }
315 if (timeout == total_timeout)
316 {
317 LOG_ERROR("erase timed out");
318 return ERROR_FAIL;
319 }
320
321 /* clear status, also clear read array */
322 if ((retval = target_write_u16(target, adr, 0x50)) != ERROR_OK)
323 {
324 return retval;
325 }
326
327 /* read array command */
328 if ((retval = target_write_u16(target, adr, 0xFF)) != ERROR_OK)
329 {
330 return retval;
331 }
332
333 if (status & 0x22)
334 {
335 LOG_ERROR("error erasing flash bank, status: 0x%x", status);
336 return ERROR_FLASH_OPERATION_FAILED;
337 }
338
339 /* If we ran erase bank command, we are finished */
340 if (erase_cmd == 0x80)
341 break;
342 }
343
344 for (i = first; i <= last; i++)
345 bank->sectors[i].is_erased = 1;
346
347 return ERROR_OK;
348 }
349
350 static int str9x_protect(struct flash_bank *bank,
351 int set, int first, int last)
352 {
353 struct target *target = bank->target;
354 int i;
355 uint32_t adr;
356 uint8_t status;
357
358 if (bank->target->state != TARGET_HALTED)
359 {
360 LOG_ERROR("Target not halted");
361 return ERROR_TARGET_NOT_HALTED;
362 }
363
364 for (i = first; i <= last; i++)
365 {
366 /* Level One Protection */
367
368 adr = bank->base + bank->sectors[i].offset;
369
370 target_write_u16(target, adr, 0x60);
371 if (set)
372 target_write_u16(target, adr, 0x01);
373 else
374 target_write_u16(target, adr, 0xD0);
375
376 /* query status */
377 target_read_u8(target, adr, &status);
378
379 /* clear status, also clear read array */
380 target_write_u16(target, adr, 0x50);
381
382 /* read array command */
383 target_write_u16(target, adr, 0xFF);
384 }
385
386 return ERROR_OK;
387 }
388
389 static int str9x_write_block(struct flash_bank *bank,
390 uint8_t *buffer, uint32_t offset, uint32_t count)
391 {
392 struct str9x_flash_bank *str9x_info = bank->driver_priv;
393 struct target *target = bank->target;
394 uint32_t buffer_size = 32768;
395 struct working_area *source;
396 uint32_t address = bank->base + offset;
397 struct reg_param reg_params[4];
398 struct arm_algorithm armv4_5_info;
399 int retval = ERROR_OK;
400
401 /* see contib/loaders/flash/str9x.s for src */
402
403 static const uint32_t str9x_flash_write_code[] = {
404 /* write: */
405 0xe3c14003, /* bic r4, r1, #3 */
406 0xe3a03040, /* mov r3, #0x40 */
407 0xe1c430b0, /* strh r3, [r4, #0] */
408 0xe0d030b2, /* ldrh r3, [r0], #2 */
409 0xe0c130b2, /* strh r3, [r1], #2 */
410 0xe3a03070, /* mov r3, #0x70 */
411 0xe1c430b0, /* strh r3, [r4, #0] */
412 /* busy: */
413 0xe5d43000, /* ldrb r3, [r4, #0] */
414 0xe3130080, /* tst r3, #0x80 */
415 0x0afffffc, /* beq busy */
416 0xe3a05050, /* mov r5, #0x50 */
417 0xe1c450b0, /* strh r5, [r4, #0] */
418 0xe3a050ff, /* mov r5, #0xFF */
419 0xe1c450b0, /* strh r5, [r4, #0] */
420 0xe3130012, /* tst r3, #0x12 */
421 0x1a000001, /* bne exit */
422 0xe2522001, /* subs r2, r2, #1 */
423 0x1affffed, /* bne write */
424 /* exit: */
425 0xe1200070, /* bkpt #0 */
426 };
427
428 /* flash write code */
429 if (target_alloc_working_area(target, sizeof(str9x_flash_write_code),
430 &str9x_info->write_algorithm) != ERROR_OK)
431 {
432 LOG_WARNING("no working area available, can't do block memory writes");
433 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
434 };
435
436 target_write_buffer(target, str9x_info->write_algorithm->address,
437 sizeof(str9x_flash_write_code),
438 (uint8_t*)str9x_flash_write_code);
439
440 /* memory buffer */
441 while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK)
442 {
443 buffer_size /= 2;
444 if (buffer_size <= 256)
445 {
446 /* if we already allocated the writing code, but failed to get a
447 * buffer, free the algorithm */
448 if (str9x_info->write_algorithm)
449 target_free_working_area(target, str9x_info->write_algorithm);
450
451 LOG_WARNING("no large enough working area available, can't do block memory writes");
452 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
453 }
454 }
455
456 armv4_5_info.common_magic = ARM_COMMON_MAGIC;
457 armv4_5_info.core_mode = ARM_MODE_SVC;
458 armv4_5_info.core_state = ARM_STATE_ARM;
459
460 init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
461 init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
462 init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
463 init_reg_param(&reg_params[3], "r3", 32, PARAM_IN);
464
465 while (count > 0)
466 {
467 uint32_t thisrun_count = (count > (buffer_size / 2)) ? (buffer_size / 2) : count;
468
469 target_write_buffer(target, source->address, thisrun_count * 2, buffer);
470
471 buf_set_u32(reg_params[0].value, 0, 32, source->address);
472 buf_set_u32(reg_params[1].value, 0, 32, address);
473 buf_set_u32(reg_params[2].value, 0, 32, thisrun_count);
474
475 if ((retval = target_run_algorithm(target, 0, NULL, 4, reg_params,
476 str9x_info->write_algorithm->address,
477 0, 10000, &armv4_5_info)) != ERROR_OK)
478 {
479 LOG_ERROR("error executing str9x flash write algorithm");
480 retval = ERROR_FLASH_OPERATION_FAILED;
481 break;
482 }
483
484 if (buf_get_u32(reg_params[3].value, 0, 32) != 0x80)
485 {
486 retval = ERROR_FLASH_OPERATION_FAILED;
487 break;
488 }
489
490 buffer += thisrun_count * 2;
491 address += thisrun_count * 2;
492 count -= thisrun_count;
493 }
494
495 target_free_working_area(target, source);
496 target_free_working_area(target, str9x_info->write_algorithm);
497
498 destroy_reg_param(&reg_params[0]);
499 destroy_reg_param(&reg_params[1]);
500 destroy_reg_param(&reg_params[2]);
501 destroy_reg_param(&reg_params[3]);
502
503 return retval;
504 }
505
506 static int str9x_write(struct flash_bank *bank,
507 uint8_t *buffer, uint32_t offset, uint32_t count)
508 {
509 struct target *target = bank->target;
510 uint32_t words_remaining = (count / 2);
511 uint32_t bytes_remaining = (count & 0x00000001);
512 uint32_t address = bank->base + offset;
513 uint32_t bytes_written = 0;
514 uint8_t status;
515 int retval;
516 uint32_t check_address = offset;
517 uint32_t bank_adr;
518 int i;
519
520 if (bank->target->state != TARGET_HALTED)
521 {
522 LOG_ERROR("Target not halted");
523 return ERROR_TARGET_NOT_HALTED;
524 }
525
526 if (offset & 0x1)
527 {
528 LOG_WARNING("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset);
529 return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
530 }
531
532 for (i = 0; i < bank->num_sectors; i++)
533 {
534 uint32_t sec_start = bank->sectors[i].offset;
535 uint32_t sec_end = sec_start + bank->sectors[i].size;
536
537 /* check if destination falls within the current sector */
538 if ((check_address >= sec_start) && (check_address < sec_end))
539 {
540 /* check if destination ends in the current sector */
541 if (offset + count < sec_end)
542 check_address = offset + count;
543 else
544 check_address = sec_end;
545 }
546 }
547
548 if (check_address != offset + count)
549 return ERROR_FLASH_DST_OUT_OF_BANK;
550
551 /* multiple half words (2-byte) to be programmed? */
552 if (words_remaining > 0)
553 {
554 /* try using a block write */
555 if ((retval = str9x_write_block(bank, buffer, offset, words_remaining)) != ERROR_OK)
556 {
557 if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
558 {
559 /* if block write failed (no sufficient working area),
560 * we use normal (slow) single dword accesses */
561 LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
562 }
563 else if (retval == ERROR_FLASH_OPERATION_FAILED)
564 {
565 LOG_ERROR("flash writing failed with error code: 0x%x", retval);
566 return ERROR_FLASH_OPERATION_FAILED;
567 }
568 }
569 else
570 {
571 buffer += words_remaining * 2;
572 address += words_remaining * 2;
573 words_remaining = 0;
574 }
575 }
576
577 while (words_remaining > 0)
578 {
579 bank_adr = address & ~0x03;
580
581 /* write data command */
582 target_write_u16(target, bank_adr, 0x40);
583 target_write_memory(target, address, 2, 1, buffer + bytes_written);
584
585 /* get status command */
586 target_write_u16(target, bank_adr, 0x70);
587
588 int timeout;
589 for (timeout = 0; timeout < 1000; timeout++)
590 {
591 target_read_u8(target, bank_adr, &status);
592 if (status & 0x80)
593 break;
594 alive_sleep(1);
595 }
596 if (timeout == 1000)
597 {
598 LOG_ERROR("write timed out");
599 return ERROR_FAIL;
600 }
601
602 /* clear status reg and read array */
603 target_write_u16(target, bank_adr, 0x50);
604 target_write_u16(target, bank_adr, 0xFF);
605
606 if (status & 0x10)
607 return ERROR_FLASH_OPERATION_FAILED;
608 else if (status & 0x02)
609 return ERROR_FLASH_OPERATION_FAILED;
610
611 bytes_written += 2;
612 words_remaining--;
613 address += 2;
614 }
615
616 if (bytes_remaining)
617 {
618 uint8_t last_halfword[2] = {0xff, 0xff};
619 i = 0;
620
621 while (bytes_remaining > 0)
622 {
623 last_halfword[i++] = *(buffer + bytes_written);
624 bytes_remaining--;
625 bytes_written++;
626 }
627
628 bank_adr = address & ~0x03;
629
630 /* write data command */
631 target_write_u16(target, bank_adr, 0x40);
632 target_write_memory(target, address, 2, 1, last_halfword);
633
634 /* query status command */
635 target_write_u16(target, bank_adr, 0x70);
636
637 int timeout;
638 for (timeout = 0; timeout < 1000; timeout++)
639 {
640 target_read_u8(target, bank_adr, &status);
641 if (status & 0x80)
642 break;
643 alive_sleep(1);
644 }
645 if (timeout == 1000)
646 {
647 LOG_ERROR("write timed out");
648 return ERROR_FAIL;
649 }
650
651 /* clear status reg and read array */
652 target_write_u16(target, bank_adr, 0x50);
653 target_write_u16(target, bank_adr, 0xFF);
654
655 if (status & 0x10)
656 return ERROR_FLASH_OPERATION_FAILED;
657 else if (status & 0x02)
658 return ERROR_FLASH_OPERATION_FAILED;
659 }
660
661 return ERROR_OK;
662 }
663
664 static int str9x_probe(struct flash_bank *bank)
665 {
666 return ERROR_OK;
667 }
668
669 #if 0
670 COMMAND_HANDLER(str9x_handle_part_id_command)
671 {
672 return ERROR_OK;
673 }
674 #endif
675
676 static int get_str9x_info(struct flash_bank *bank, char *buf, int buf_size)
677 {
678 snprintf(buf, buf_size, "str9x flash driver info");
679 return ERROR_OK;
680 }
681
682 COMMAND_HANDLER(str9x_handle_flash_config_command)
683 {
684 struct str9x_flash_bank *str9x_info;
685 struct target *target = NULL;
686
687 if (CMD_ARGC < 5)
688 {
689 return ERROR_COMMAND_SYNTAX_ERROR;
690 }
691
692 struct flash_bank *bank;
693 int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
694 if (ERROR_OK != retval)
695 return retval;
696
697 uint32_t bbsr, nbbsr, bbadr, nbbadr;
698 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], bbsr);
699 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], nbbsr);
700 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], bbadr);
701 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[4], nbbadr);
702
703 str9x_info = bank->driver_priv;
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)