674522c73464047bf5e482b01aab79018289ca86
[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 return ERROR_COMMAND_SYNTAX_ERROR;
162 }
163
164 str9x_info = malloc(sizeof(struct str9x_flash_bank));
165 bank->driver_priv = str9x_info;
166
167 str9x_build_block_list(bank);
168
169 str9x_info->write_algorithm = NULL;
170
171 return ERROR_OK;
172 }
173
174 static int str9x_protect_check(struct flash_bank *bank)
175 {
176 int retval;
177 struct str9x_flash_bank *str9x_info = bank->driver_priv;
178 struct target *target = bank->target;
179
180 int i;
181 uint32_t adr;
182 uint32_t status = 0;
183 uint16_t hstatus = 0;
184
185 if (bank->target->state != TARGET_HALTED)
186 {
187 LOG_ERROR("Target not halted");
188 return ERROR_TARGET_NOT_HALTED;
189 }
190
191 /* read level one protection */
192
193 if (str9x_info->variant)
194 {
195 if (str9x_info->bank1)
196 {
197 adr = bank1start + 0x18;
198 if ((retval = target_write_u16(target, adr, 0x90)) != ERROR_OK)
199 {
200 return retval;
201 }
202 if ((retval = target_read_u16(target, adr, &hstatus)) != ERROR_OK)
203 {
204 return retval;
205 }
206 status = hstatus;
207 }
208 else
209 {
210 adr = bank1start + 0x14;
211 if ((retval = target_write_u16(target, adr, 0x90)) != ERROR_OK)
212 {
213 return retval;
214 }
215 if ((retval = target_read_u32(target, adr, &status)) != ERROR_OK)
216 {
217 return retval;
218 }
219 }
220 }
221 else
222 {
223 adr = bank1start + 0x10;
224 if ((retval = target_write_u16(target, adr, 0x90)) != ERROR_OK)
225 {
226 return retval;
227 }
228 if ((retval = target_read_u16(target, adr, &hstatus)) != ERROR_OK)
229 {
230 return retval;
231 }
232 status = hstatus;
233 }
234
235 /* read array command */
236 if ((retval = target_write_u16(target, adr, 0xFF)) != ERROR_OK)
237 {
238 return retval;
239 }
240
241 for (i = 0; i < bank->num_sectors; i++)
242 {
243 if (status & str9x_info->sector_bits[i])
244 bank->sectors[i].is_protected = 1;
245 else
246 bank->sectors[i].is_protected = 0;
247 }
248
249 return ERROR_OK;
250 }
251
252 static int str9x_erase(struct flash_bank *bank, int first, int last)
253 {
254 struct target *target = bank->target;
255 int i;
256 uint32_t adr;
257 uint8_t status;
258 uint8_t erase_cmd;
259 int total_timeout;
260
261 if (bank->target->state != TARGET_HALTED)
262 {
263 LOG_ERROR("Target not halted");
264 return ERROR_TARGET_NOT_HALTED;
265 }
266
267 /* Check if we can erase whole bank */
268 if ((first == 0) && (last == (bank->num_sectors - 1)))
269 {
270 /* Optimize to run erase bank command instead of sector */
271 erase_cmd = 0x80;
272 /* Add timeout duration since erase bank takes more time */
273 total_timeout = 1000 * bank->num_sectors;
274 }
275 else
276 {
277 /* Erase sector command */
278 erase_cmd = 0x20;
279 total_timeout = 1000;
280 }
281
282 /* this is so the compiler can *know* */
283 assert(total_timeout > 0);
284
285 for (i = first; i <= last; i++)
286 {
287 int retval;
288 adr = bank->base + bank->sectors[i].offset;
289
290 /* erase sectors or block */
291 if ((retval = target_write_u16(target, adr, erase_cmd)) != ERROR_OK)
292 {
293 return retval;
294 }
295 if ((retval = target_write_u16(target, adr, 0xD0)) != ERROR_OK)
296 {
297 return retval;
298 }
299
300 /* get status */
301 if ((retval = target_write_u16(target, adr, 0x70)) != ERROR_OK)
302 {
303 return retval;
304 }
305
306 int timeout;
307 for (timeout = 0; timeout < total_timeout; timeout++)
308 {
309 if ((retval = target_read_u8(target, adr, &status)) != ERROR_OK)
310 {
311 return retval;
312 }
313 if (status & 0x80)
314 break;
315 alive_sleep(1);
316 }
317 if (timeout == total_timeout)
318 {
319 LOG_ERROR("erase timed out");
320 return ERROR_FAIL;
321 }
322
323 /* clear status, also clear read array */
324 if ((retval = target_write_u16(target, adr, 0x50)) != ERROR_OK)
325 {
326 return retval;
327 }
328
329 /* read array command */
330 if ((retval = target_write_u16(target, adr, 0xFF)) != ERROR_OK)
331 {
332 return retval;
333 }
334
335 if (status & 0x22)
336 {
337 LOG_ERROR("error erasing flash bank, status: 0x%x", status);
338 return ERROR_FLASH_OPERATION_FAILED;
339 }
340
341 /* If we ran erase bank command, we are finished */
342 if (erase_cmd == 0x80)
343 break;
344 }
345
346 for (i = first; i <= last; i++)
347 bank->sectors[i].is_erased = 1;
348
349 return ERROR_OK;
350 }
351
352 static int str9x_protect(struct flash_bank *bank,
353 int set, int first, int last)
354 {
355 struct target *target = bank->target;
356 int i;
357 uint32_t adr;
358 uint8_t status;
359
360 if (bank->target->state != TARGET_HALTED)
361 {
362 LOG_ERROR("Target not halted");
363 return ERROR_TARGET_NOT_HALTED;
364 }
365
366 for (i = first; i <= last; i++)
367 {
368 /* Level One Protection */
369
370 adr = bank->base + bank->sectors[i].offset;
371
372 target_write_u16(target, adr, 0x60);
373 if (set)
374 target_write_u16(target, adr, 0x01);
375 else
376 target_write_u16(target, adr, 0xD0);
377
378 /* query status */
379 target_read_u8(target, adr, &status);
380
381 /* clear status, also clear read array */
382 target_write_u16(target, adr, 0x50);
383
384 /* read array command */
385 target_write_u16(target, adr, 0xFF);
386 }
387
388 return ERROR_OK;
389 }
390
391 static int str9x_write_block(struct flash_bank *bank,
392 uint8_t *buffer, uint32_t offset, uint32_t count)
393 {
394 struct str9x_flash_bank *str9x_info = bank->driver_priv;
395 struct target *target = bank->target;
396 uint32_t buffer_size = 32768;
397 struct working_area *source;
398 uint32_t address = bank->base + offset;
399 struct reg_param reg_params[4];
400 struct arm_algorithm armv4_5_info;
401 int retval = ERROR_OK;
402
403 /* see contib/loaders/flash/str9x.s for src */
404
405 static const uint32_t str9x_flash_write_code[] = {
406 /* write: */
407 0xe3c14003, /* bic r4, r1, #3 */
408 0xe3a03040, /* mov r3, #0x40 */
409 0xe1c430b0, /* strh r3, [r4, #0] */
410 0xe0d030b2, /* ldrh r3, [r0], #2 */
411 0xe0c130b2, /* strh r3, [r1], #2 */
412 0xe3a03070, /* mov r3, #0x70 */
413 0xe1c430b0, /* strh r3, [r4, #0] */
414 /* busy: */
415 0xe5d43000, /* ldrb r3, [r4, #0] */
416 0xe3130080, /* tst r3, #0x80 */
417 0x0afffffc, /* beq busy */
418 0xe3a05050, /* mov r5, #0x50 */
419 0xe1c450b0, /* strh r5, [r4, #0] */
420 0xe3a050ff, /* mov r5, #0xFF */
421 0xe1c450b0, /* strh r5, [r4, #0] */
422 0xe3130012, /* tst r3, #0x12 */
423 0x1a000001, /* bne exit */
424 0xe2522001, /* subs r2, r2, #1 */
425 0x1affffed, /* bne write */
426 /* exit: */
427 0xe1200070, /* bkpt #0 */
428 };
429
430 /* flash write code */
431 if (target_alloc_working_area(target, sizeof(str9x_flash_write_code),
432 &str9x_info->write_algorithm) != ERROR_OK)
433 {
434 LOG_WARNING("no working area available, can't do block memory writes");
435 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
436 };
437
438 target_write_buffer(target, str9x_info->write_algorithm->address,
439 sizeof(str9x_flash_write_code),
440 (uint8_t*)str9x_flash_write_code);
441
442 /* memory buffer */
443 while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK)
444 {
445 buffer_size /= 2;
446 if (buffer_size <= 256)
447 {
448 /* if we already allocated the writing code, but failed to get a
449 * buffer, free the algorithm */
450 if (str9x_info->write_algorithm)
451 target_free_working_area(target, str9x_info->write_algorithm);
452
453 LOG_WARNING("no large enough working area available, can't do block memory writes");
454 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
455 }
456 }
457
458 armv4_5_info.common_magic = ARM_COMMON_MAGIC;
459 armv4_5_info.core_mode = ARM_MODE_SVC;
460 armv4_5_info.core_state = ARM_STATE_ARM;
461
462 init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
463 init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
464 init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
465 init_reg_param(&reg_params[3], "r3", 32, PARAM_IN);
466
467 while (count > 0)
468 {
469 uint32_t thisrun_count = (count > (buffer_size / 2)) ? (buffer_size / 2) : count;
470
471 target_write_buffer(target, source->address, thisrun_count * 2, buffer);
472
473 buf_set_u32(reg_params[0].value, 0, 32, source->address);
474 buf_set_u32(reg_params[1].value, 0, 32, address);
475 buf_set_u32(reg_params[2].value, 0, 32, thisrun_count);
476
477 if ((retval = target_run_algorithm(target, 0, NULL, 4, reg_params,
478 str9x_info->write_algorithm->address,
479 0, 10000, &armv4_5_info)) != ERROR_OK)
480 {
481 LOG_ERROR("error executing str9x flash write algorithm");
482 retval = ERROR_FLASH_OPERATION_FAILED;
483 break;
484 }
485
486 if (buf_get_u32(reg_params[3].value, 0, 32) != 0x80)
487 {
488 retval = ERROR_FLASH_OPERATION_FAILED;
489 break;
490 }
491
492 buffer += thisrun_count * 2;
493 address += thisrun_count * 2;
494 count -= thisrun_count;
495 }
496
497 target_free_working_area(target, source);
498 target_free_working_area(target, str9x_info->write_algorithm);
499
500 destroy_reg_param(&reg_params[0]);
501 destroy_reg_param(&reg_params[1]);
502 destroy_reg_param(&reg_params[2]);
503 destroy_reg_param(&reg_params[3]);
504
505 return retval;
506 }
507
508 static int str9x_write(struct flash_bank *bank,
509 uint8_t *buffer, uint32_t offset, uint32_t count)
510 {
511 struct target *target = bank->target;
512 uint32_t words_remaining = (count / 2);
513 uint32_t bytes_remaining = (count & 0x00000001);
514 uint32_t address = bank->base + offset;
515 uint32_t bytes_written = 0;
516 uint8_t status;
517 int retval;
518 uint32_t check_address = offset;
519 uint32_t bank_adr;
520 int i;
521
522 if (bank->target->state != TARGET_HALTED)
523 {
524 LOG_ERROR("Target not halted");
525 return ERROR_TARGET_NOT_HALTED;
526 }
527
528 if (offset & 0x1)
529 {
530 LOG_WARNING("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset);
531 return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
532 }
533
534 for (i = 0; i < bank->num_sectors; i++)
535 {
536 uint32_t sec_start = bank->sectors[i].offset;
537 uint32_t sec_end = sec_start + bank->sectors[i].size;
538
539 /* check if destination falls within the current sector */
540 if ((check_address >= sec_start) && (check_address < sec_end))
541 {
542 /* check if destination ends in the current sector */
543 if (offset + count < sec_end)
544 check_address = offset + count;
545 else
546 check_address = sec_end;
547 }
548 }
549
550 if (check_address != offset + count)
551 return ERROR_FLASH_DST_OUT_OF_BANK;
552
553 /* multiple half words (2-byte) to be programmed? */
554 if (words_remaining > 0)
555 {
556 /* try using a block write */
557 if ((retval = str9x_write_block(bank, buffer, offset, words_remaining)) != ERROR_OK)
558 {
559 if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
560 {
561 /* if block write failed (no sufficient working area),
562 * we use normal (slow) single dword accesses */
563 LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
564 }
565 else if (retval == ERROR_FLASH_OPERATION_FAILED)
566 {
567 LOG_ERROR("flash writing failed");
568 return ERROR_FLASH_OPERATION_FAILED;
569 }
570 }
571 else
572 {
573 buffer += words_remaining * 2;
574 address += words_remaining * 2;
575 words_remaining = 0;
576 }
577 }
578
579 while (words_remaining > 0)
580 {
581 bank_adr = address & ~0x03;
582
583 /* write data command */
584 target_write_u16(target, bank_adr, 0x40);
585 target_write_memory(target, address, 2, 1, buffer + bytes_written);
586
587 /* get status command */
588 target_write_u16(target, bank_adr, 0x70);
589
590 int timeout;
591 for (timeout = 0; timeout < 1000; timeout++)
592 {
593 target_read_u8(target, bank_adr, &status);
594 if (status & 0x80)
595 break;
596 alive_sleep(1);
597 }
598 if (timeout == 1000)
599 {
600 LOG_ERROR("write timed out");
601 return ERROR_FAIL;
602 }
603
604 /* clear status reg and read array */
605 target_write_u16(target, bank_adr, 0x50);
606 target_write_u16(target, bank_adr, 0xFF);
607
608 if (status & 0x10)
609 return ERROR_FLASH_OPERATION_FAILED;
610 else if (status & 0x02)
611 return ERROR_FLASH_OPERATION_FAILED;
612
613 bytes_written += 2;
614 words_remaining--;
615 address += 2;
616 }
617
618 if (bytes_remaining)
619 {
620 uint8_t last_halfword[2] = {0xff, 0xff};
621
622 /* copy the last remaining bytes into the write buffer */
623 memcpy(last_halfword, buffer+bytes_written, bytes_remaining);
624
625 bank_adr = address & ~0x03;
626
627 /* write data command */
628 target_write_u16(target, bank_adr, 0x40);
629 target_write_memory(target, address, 2, 1, last_halfword);
630
631 /* query status command */
632 target_write_u16(target, bank_adr, 0x70);
633
634 int timeout;
635 for (timeout = 0; timeout < 1000; timeout++)
636 {
637 target_read_u8(target, bank_adr, &status);
638 if (status & 0x80)
639 break;
640 alive_sleep(1);
641 }
642 if (timeout == 1000)
643 {
644 LOG_ERROR("write timed out");
645 return ERROR_FAIL;
646 }
647
648 /* clear status reg and read array */
649 target_write_u16(target, bank_adr, 0x50);
650 target_write_u16(target, bank_adr, 0xFF);
651
652 if (status & 0x10)
653 return ERROR_FLASH_OPERATION_FAILED;
654 else if (status & 0x02)
655 return ERROR_FLASH_OPERATION_FAILED;
656 }
657
658 return ERROR_OK;
659 }
660
661 static int str9x_probe(struct flash_bank *bank)
662 {
663 return ERROR_OK;
664 }
665
666 #if 0
667 COMMAND_HANDLER(str9x_handle_part_id_command)
668 {
669 return ERROR_OK;
670 }
671 #endif
672
673 static int get_str9x_info(struct flash_bank *bank, char *buf, int buf_size)
674 {
675 snprintf(buf, buf_size, "str9x flash driver info");
676 return ERROR_OK;
677 }
678
679 COMMAND_HANDLER(str9x_handle_flash_config_command)
680 {
681 struct target *target = NULL;
682
683 if (CMD_ARGC < 5)
684 {
685 return ERROR_COMMAND_SYNTAX_ERROR;
686 }
687
688 struct flash_bank *bank;
689 int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
690 if (ERROR_OK != retval)
691 return retval;
692
693 uint32_t bbsr, nbbsr, bbadr, nbbadr;
694 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], bbsr);
695 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], nbbsr);
696 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], bbadr);
697 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[4], nbbadr);
698
699 target = bank->target;
700
701 if (bank->target->state != TARGET_HALTED)
702 {
703 LOG_ERROR("Target not halted");
704 return ERROR_TARGET_NOT_HALTED;
705 }
706
707 /* config flash controller */
708 target_write_u32(target, FLASH_BBSR, bbsr);
709 target_write_u32(target, FLASH_NBBSR, nbbsr);
710 target_write_u32(target, FLASH_BBADR, bbadr >> 2);
711 target_write_u32(target, FLASH_NBBADR, nbbadr >> 2);
712
713 /* set bit 18 instruction TCM order as per flash programming manual */
714 arm966e_write_cp15(target, 62, 0x40000);
715
716 /* enable flash bank 1 */
717 target_write_u32(target, FLASH_CR, 0x18);
718 return ERROR_OK;
719 }
720
721 static const struct command_registration str9x_config_command_handlers[] = {
722 {
723 .name = "flash_config",
724 .handler = str9x_handle_flash_config_command,
725 .mode = COMMAND_EXEC,
726 .help = "Configure str9x flash controller, prior to "
727 "programming the flash.",
728 .usage = "bank_id BBSR NBBSR BBADR NBBADR",
729 },
730 COMMAND_REGISTRATION_DONE
731 };
732
733 static const struct command_registration str9x_command_handlers[] = {
734 {
735 .name = "str9x",
736 .mode = COMMAND_ANY,
737 .help = "str9x flash command group",
738 .chain = str9x_config_command_handlers,
739 },
740 COMMAND_REGISTRATION_DONE
741 };
742
743 struct flash_driver str9x_flash = {
744 .name = "str9x",
745 .commands = str9x_command_handlers,
746 .flash_bank_command = str9x_flash_bank_command,
747 .erase = str9x_erase,
748 .protect = str9x_protect,
749 .write = str9x_write,
750 .read = default_flash_read,
751 .probe = str9x_probe,
752 .auto_probe = str9x_probe,
753 .erase_check = default_flash_blank_check,
754 .protect_check = str9x_protect_check,
755 .info = get_str9x_info,
756 };

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)