eac80041ac465feee2a66e262bf071db4b09074c
[openocd.git] / src / jtag / zy1000.c
1 /***************************************************************************
2 * Copyright (C) 2007-2008 by Øyvind Harboe *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
18 ***************************************************************************/
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23
24 #include "log.h"
25 #include "jtag.h"
26 #include "bitbang.h"
27 #include "../target/embeddedice.h"
28
29
30 #include <cyg/hal/hal_io.h> // low level i/o
31 #include <cyg/hal/hal_diag.h>
32
33 #include <stdlib.h>
34
35 #define ZYLIN_VERSION "1.51"
36 #define ZYLIN_DATE __DATE__
37 #define ZYLIN_TIME __TIME__
38 #define ZYLIN_OPENOCD "$Revision$"
39 #define ZYLIN_OPENOCD_VERSION "Zylin JTAG ZY1000 " ZYLIN_VERSION " " ZYLIN_DATE " " ZYLIN_TIME
40 const char *zylin_config_dir="/config/settings";
41
42 /* low level command set
43 */
44 int zy1000_read(void);
45 static void zy1000_write(int tck, int tms, int tdi);
46 void zy1000_reset(int trst, int srst);
47
48
49 int zy1000_speed(int speed);
50 int zy1000_register_commands(struct command_context_s *cmd_ctx);
51 int zy1000_init(void);
52 int zy1000_quit(void);
53
54 /* interface commands */
55 int zy1000_handle_zy1000_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
56
57 static int zy1000_khz(int khz, int *jtag_speed)
58 {
59 if (khz==0)
60 {
61 *jtag_speed=0;
62 }
63 else
64 {
65 *jtag_speed=64000/khz;
66 }
67 return ERROR_OK;
68 }
69
70 static int zy1000_speed_div(int speed, int *khz)
71 {
72 if (speed==0)
73 {
74 *khz = 0;
75 }
76 else
77 {
78 *khz=64000/speed;
79 }
80
81 return ERROR_OK;
82 }
83
84 static bool readPowerDropout(void)
85 {
86 cyg_uint32 state;
87 // sample and clear power dropout
88 HAL_WRITE_UINT32(ZY1000_JTAG_BASE+0x10, 0x80);
89 HAL_READ_UINT32(ZY1000_JTAG_BASE+0x10, state);
90 bool powerDropout;
91 powerDropout = (state & 0x80) != 0;
92 return powerDropout;
93 }
94
95
96 static bool readSRST(void)
97 {
98 cyg_uint32 state;
99 // sample and clear SRST sensing
100 HAL_WRITE_UINT32(ZY1000_JTAG_BASE+0x10, 0x00000040);
101 HAL_READ_UINT32(ZY1000_JTAG_BASE+0x10, state);
102 bool srstAsserted;
103 srstAsserted = (state & 0x40) != 0;
104 return srstAsserted;
105 }
106
107 static int zy1000_srst_asserted(int *srst_asserted)
108 {
109 *srst_asserted=readSRST();
110 return ERROR_OK;
111 }
112
113 static int zy1000_power_dropout(int *dropout)
114 {
115 *dropout=readPowerDropout();
116 return ERROR_OK;
117 }
118
119
120 jtag_interface_t zy1000_interface =
121 {
122 .name = "ZY1000",
123 .execute_queue = bitbang_execute_queue,
124 .speed = zy1000_speed,
125 .register_commands = zy1000_register_commands,
126 .init = zy1000_init,
127 .quit = zy1000_quit,
128 .khz = zy1000_khz,
129 .speed_div = zy1000_speed_div,
130 .power_dropout = zy1000_power_dropout,
131 .srst_asserted = zy1000_srst_asserted,
132 };
133
134 bitbang_interface_t zy1000_bitbang =
135 {
136 .read = zy1000_read,
137 .write = zy1000_write,
138 .reset = zy1000_reset
139 };
140
141
142
143 static void zy1000_write(int tck, int tms, int tdi)
144 {
145
146 }
147
148 int zy1000_read(void)
149 {
150 return -1;
151 }
152
153 extern bool readSRST(void);
154
155 void zy1000_reset(int trst, int srst)
156 {
157 LOG_DEBUG("zy1000 trst=%d, srst=%d", trst, srst);
158 if(!srst)
159 {
160 ZY1000_POKE(ZY1000_JTAG_BASE+0x14, 0x00000001);
161 }
162 else
163 {
164 /* Danger!!! if clk!=0 when in
165 * idle in TAP_IDLE, reset halt on str912 will fail.
166 */
167 ZY1000_POKE(ZY1000_JTAG_BASE+0x10, 0x00000001);
168 }
169
170 if(!trst)
171 {
172 ZY1000_POKE(ZY1000_JTAG_BASE+0x14, 0x00000002);
173 }
174 else
175 {
176 /* assert reset */
177 ZY1000_POKE(ZY1000_JTAG_BASE+0x10, 0x00000002);
178 }
179
180 if (trst||(srst&&(jtag_reset_config & RESET_SRST_PULLS_TRST)))
181 {
182 waitIdle();
183 /* we're now in the RESET state until trst is deasserted */
184 ZY1000_POKE(ZY1000_JTAG_BASE+0x20, TAP_RESET);
185 } else
186 {
187 /* We'll get RCLK failure when we assert TRST, so clear any false positives here */
188 ZY1000_POKE(ZY1000_JTAG_BASE+0x14, 0x400);
189 }
190
191 /* wait for srst to float back up */
192 if (!srst)
193 {
194 int i;
195 for (i=0; i<1000; i++)
196 {
197 // We don't want to sense our own reset, so we clear here.
198 // There is of course a timing hole where we could loose
199 // a "real" reset.
200 if (!readSRST())
201 break;
202
203 /* wait 1ms */
204 alive_sleep(1);
205 }
206
207 if (i==1000)
208 {
209 LOG_USER("SRST didn't deassert after %dms", i);
210 } else if (i>1)
211 {
212 LOG_USER("SRST took %dms to deassert", i);
213 }
214 }
215 }
216
217 int zy1000_speed(int speed)
218 {
219 if(speed == 0)
220 {
221 /*0 means RCLK*/
222 speed = 0;
223 ZY1000_POKE(ZY1000_JTAG_BASE+0x10, 0x100);
224 LOG_DEBUG("jtag_speed using RCLK");
225 }
226 else
227 {
228 if(speed > 8190 || speed < 2)
229 {
230 LOG_USER("valid ZY1000 jtag_speed=[8190,2]. Divisor is 64MHz / even values between 8190-2, i.e. min 7814Hz, max 32MHz");
231 return ERROR_INVALID_ARGUMENTS;
232 }
233
234 LOG_USER("jtag_speed %d => JTAG clk=%f", speed, 64.0/(float)speed);
235 ZY1000_POKE(ZY1000_JTAG_BASE+0x14, 0x100);
236 ZY1000_POKE(ZY1000_JTAG_BASE+0x1c, speed&~1);
237 }
238 return ERROR_OK;
239 }
240
241 static bool savePower;
242
243
244 static void setPower(bool power)
245 {
246 savePower = power;
247 if (power)
248 {
249 HAL_WRITE_UINT32(ZY1000_JTAG_BASE+0x14, 0x8);
250 } else
251 {
252 HAL_WRITE_UINT32(ZY1000_JTAG_BASE+0x10, 0x8);
253 }
254 }
255
256 int handle_power_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
257 {
258 if (argc > 1)
259 {
260 return ERROR_INVALID_ARGUMENTS;
261 }
262
263 if (argc == 1)
264 {
265 if (strcmp(args[0], "on") == 0)
266 {
267 setPower(1);
268 }
269 else if (strcmp(args[0], "off") == 0)
270 {
271 setPower(0);
272 } else
273 {
274 command_print(cmd_ctx, "arg is \"on\" or \"off\"");
275 return ERROR_INVALID_ARGUMENTS;
276 }
277 }
278
279 command_print(cmd_ctx, "Target power %s", savePower ? "on" : "off");
280
281 return ERROR_OK;
282 }
283
284
285 /* Give TELNET a way to find out what version this is */
286 static int jim_zy1000_version(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
287 {
288 if ((argc < 1) || (argc > 2))
289 return JIM_ERR;
290 char buff[128];
291 const char *version_str=NULL;
292
293 if (argc == 1)
294 {
295 version_str=ZYLIN_OPENOCD_VERSION;
296 } else
297 {
298 const char *str = Jim_GetString(argv[1], NULL);
299 if (strcmp("openocd", str) == 0)
300 {
301 int revision;
302 revision = atol(ZYLIN_OPENOCD+strlen("XRevision: "));
303 sprintf(buff, "%d", revision);
304 version_str=buff;
305 }
306 else if (strcmp("zy1000", str) == 0)
307 {
308 version_str=ZYLIN_VERSION;
309 }
310 else if (strcmp("date", str) == 0)
311 {
312 version_str=ZYLIN_DATE;
313 }
314 else
315 {
316 return JIM_ERR;
317 }
318 }
319
320 Jim_SetResult(interp, Jim_NewStringObj(interp, version_str, -1));
321
322 return JIM_OK;
323 }
324
325
326 static int
327 zylinjtag_Jim_Command_powerstatus(Jim_Interp *interp,
328 int argc,
329 Jim_Obj * const *argv)
330 {
331 if (argc != 1)
332 {
333 Jim_WrongNumArgs(interp, 1, argv, "powerstatus");
334 return JIM_ERR;
335 }
336
337 cyg_uint32 status;
338 ZY1000_PEEK(ZY1000_JTAG_BASE+0x10, status);
339
340 Jim_SetResult(interp, Jim_NewIntObj(interp, (status&0x80)!=0));
341
342 return JIM_OK;
343 }
344
345 int zy1000_register_commands(struct command_context_s *cmd_ctx)
346 {
347 register_command(cmd_ctx, NULL, "power", handle_power_command, COMMAND_ANY,
348 "power <on/off> - turn power switch to target on/off. No arguments - print status.");
349
350 Jim_CreateCommand(interp, "zy1000_version", jim_zy1000_version, NULL, NULL);
351
352
353 Jim_CreateCommand(interp, "powerstatus", zylinjtag_Jim_Command_powerstatus, NULL, NULL);
354
355 return ERROR_OK;
356 }
357
358
359
360
361 int zy1000_init(void)
362 {
363 LOG_USER("%s", ZYLIN_OPENOCD_VERSION);
364
365 ZY1000_POKE(ZY1000_JTAG_BASE+0x10, 0x30); // Turn on LED1 & LED2
366
367 setPower(true); // on by default
368
369
370 /* deassert resets. Important to avoid infinite loop waiting for SRST to deassert */
371 zy1000_reset(0, 0);
372 zy1000_speed(jtag_speed);
373
374 bitbang_interface = &zy1000_bitbang;
375
376 return ERROR_OK;
377 }
378
379 int zy1000_quit(void)
380 {
381
382 return ERROR_OK;
383 }
384
385
386
387
388 int interface_jtag_execute_queue(void)
389 {
390 cyg_uint32 empty;
391
392 waitIdle();
393 ZY1000_PEEK(ZY1000_JTAG_BASE+0x10, empty);
394 /* clear JTAG error register */
395 ZY1000_POKE(ZY1000_JTAG_BASE+0x14, 0x400);
396
397 if ((empty&0x400)!=0)
398 {
399 LOG_WARNING("RCLK timeout");
400 /* the error is informative only as we don't want to break the firmware if there
401 * is a false positive.
402 */
403 // return ERROR_FAIL;
404 }
405 return ERROR_OK;
406 }
407
408
409
410
411
412 static cyg_uint32 getShiftValue(void)
413 {
414 cyg_uint32 value;
415 waitIdle();
416 ZY1000_PEEK(ZY1000_JTAG_BASE+0xc, value);
417 VERBOSE(LOG_INFO("getShiftValue %08x", value));
418 return value;
419 }
420 #if 0
421 static cyg_uint32 getShiftValueFlip(void)
422 {
423 cyg_uint32 value;
424 waitIdle();
425 ZY1000_PEEK(ZY1000_JTAG_BASE+0x18, value);
426 VERBOSE(LOG_INFO("getShiftValue %08x (flipped)", value));
427 return value;
428 }
429 #endif
430
431 #if 0
432 static void shiftValueInnerFlip(const tap_state_t state, const tap_state_t endState, int repeat, cyg_uint32 value)
433 {
434 VERBOSE(LOG_INFO("shiftValueInner %s %s %d %08x (flipped)", tap_state_name(state), tap_state_name(endState), repeat, value));
435 cyg_uint32 a,b;
436 a=state;
437 b=endState;
438 ZY1000_POKE(ZY1000_JTAG_BASE+0xc, value);
439 ZY1000_POKE(ZY1000_JTAG_BASE+0x8, (1<<15)|(repeat<<8)|(a<<4)|b);
440 VERBOSE(getShiftValueFlip());
441 }
442 #endif
443
444 extern int jtag_check_value(u8 *captured, void *priv);
445
446 static void gotoEndState(void)
447 {
448 setCurrentState(cmd_queue_end_state);
449 }
450
451 static __inline void scanFields(int num_fields, scan_field_t *fields, tap_state_t shiftState, tap_state_t end_state)
452 {
453 int i;
454 int j;
455 int k;
456
457 for (i = 0; i < num_fields; i++)
458 {
459 cyg_uint32 value;
460
461 static u8 *in_buff=NULL; /* pointer to buffer for scanned data */
462 static int in_buff_size=0;
463 u8 *inBuffer=NULL;
464
465
466 // figure out where to store the input data
467 int num_bits=fields[i].num_bits;
468 if (fields[i].in_value!=NULL)
469 {
470 inBuffer=fields[i].in_value;
471 }
472
473 // here we shuffle N bits out/in
474 j=0;
475 while (j<num_bits)
476 {
477 tap_state_t pause_state;
478 int l;
479 k=num_bits-j;
480 pause_state=(shiftState==TAP_DRSHIFT)?TAP_DRSHIFT:TAP_IRSHIFT;
481 if (k>32)
482 {
483 k=32;
484 /* we have more to shift out */
485 } else if (i == num_fields-1)
486 {
487 /* this was the last to shift out this time */
488 pause_state=end_state;
489 }
490
491 // we have (num_bits+7)/8 bytes of bits to toggle out.
492 // bits are pushed out LSB to MSB
493 value=0;
494 if (fields[i].out_value!=NULL)
495 {
496 for (l=0; l<k; l+=8)
497 {
498 value|=fields[i].out_value[(j+l)/8]<<l;
499 }
500 }
501 /* mask away unused bits for easier debugging */
502 value&=~(((u32)0xffffffff)<<k);
503
504 shiftValueInner(shiftState, pause_state, k, value);
505
506 if (inBuffer!=NULL)
507 {
508 // data in, LSB to MSB
509 value=getShiftValue();
510 // we're shifting in data to MSB, shift data to be aligned for returning the value
511 value >>= 32-k;
512
513 for (l=0; l<k; l+=8)
514 {
515 inBuffer[(j+l)/8]=(value>>l)&0xff;
516 }
517 }
518 j+=k;
519 }
520 }
521 }
522
523 int interface_jtag_add_end_state(tap_state_t state)
524 {
525 return ERROR_OK;
526 }
527
528
529 int interface_jtag_add_ir_scan(int num_fields, scan_field_t *fields, tap_state_t state)
530 {
531
532 int j;
533 int scan_size = 0;
534 jtag_tap_t *tap, *nextTap;
535 for(tap = jtag_NextEnabledTap(NULL); tap!= NULL; tap=nextTap)
536 {
537 nextTap=jtag_NextEnabledTap(tap);
538 tap_state_t end_state;
539 if (nextTap==NULL)
540 {
541 end_state = cmd_queue_end_state;
542 } else
543 {
544 end_state = TAP_IRSHIFT;
545 }
546
547 int found = 0;
548
549 scan_size = tap->ir_length;
550
551 /* search the list */
552 for (j=0; j < num_fields; j++)
553 {
554 if (tap == fields[j].tap)
555 {
556 found = 1;
557
558 scanFields(1, fields+j, TAP_IRSHIFT, end_state);
559 /* update device information */
560 buf_cpy(fields[j].out_value, tap->cur_instr, scan_size);
561
562 tap->bypass = 0;
563 break;
564 }
565 }
566
567 if (!found)
568 {
569 /* if a device isn't listed, set it to BYPASS */
570 u8 ones[]={0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
571
572 scan_field_t tmp;
573 memset(&tmp, 0, sizeof(tmp));
574 tmp.out_value = ones;
575 tmp.num_bits = scan_size;
576 scanFields(1, &tmp, TAP_IRSHIFT, end_state);
577 /* update device information */
578 buf_cpy(tmp.out_value, tap->cur_instr, scan_size);
579 tap->bypass = 1;
580 }
581 }
582
583 return ERROR_OK;
584 }
585
586
587
588
589
590 int interface_jtag_add_plain_ir_scan(int num_fields, scan_field_t *fields, tap_state_t state)
591 {
592 scanFields(num_fields, fields, TAP_IRSHIFT, cmd_queue_end_state);
593
594 return ERROR_OK;
595 }
596
597 /*extern jtag_command_t **jtag_get_last_command_p(void);*/
598
599 int interface_jtag_add_dr_scan(int num_fields, scan_field_t *fields, tap_state_t state)
600 {
601
602 int j;
603 jtag_tap_t *tap, *nextTap;
604 for(tap = jtag_NextEnabledTap(NULL); tap!= NULL; tap=nextTap)
605 {
606 nextTap=jtag_NextEnabledTap(tap);
607 int found=0;
608 tap_state_t end_state;
609 if (nextTap==NULL)
610 {
611 end_state = cmd_queue_end_state;
612 } else
613 {
614 end_state = TAP_DRSHIFT;
615 }
616
617 for (j=0; j < num_fields; j++)
618 {
619 if (tap == fields[j].tap)
620 {
621 found = 1;
622
623 scanFields(1, fields+j, TAP_DRSHIFT, end_state);
624 }
625 }
626 if (!found)
627 {
628 scan_field_t tmp;
629 /* program the scan field to 1 bit length, and ignore it's value */
630 tmp.num_bits = 1;
631 tmp.out_value = NULL;
632 tmp.in_value = NULL;
633
634 scanFields(1, &tmp, TAP_DRSHIFT, end_state);
635 }
636 else
637 {
638 }
639 }
640 return ERROR_OK;
641 }
642
643 int interface_jtag_add_plain_dr_scan(int num_fields, scan_field_t *fields, tap_state_t state)
644 {
645 scanFields(num_fields, fields, TAP_DRSHIFT, cmd_queue_end_state);
646 return ERROR_OK;
647 }
648
649
650 int interface_jtag_add_tlr()
651 {
652 setCurrentState(TAP_RESET);
653 return ERROR_OK;
654 }
655
656
657
658
659 extern int jtag_nsrst_delay;
660 extern int jtag_ntrst_delay;
661
662 int interface_jtag_add_reset(int req_trst, int req_srst)
663 {
664 zy1000_reset(req_trst, req_srst);
665 return ERROR_OK;
666 }
667
668 static int zy1000_jtag_add_clocks(int num_cycles, tap_state_t state, tap_state_t clockstate)
669 {
670 /* num_cycles can be 0 */
671 setCurrentState(clockstate);
672
673 /* execute num_cycles, 32 at the time. */
674 int i;
675 for (i=0; i<num_cycles; i+=32)
676 {
677 int num;
678 num=32;
679 if (num_cycles-i<num)
680 {
681 num=num_cycles-i;
682 }
683 shiftValueInner(clockstate, clockstate, num, 0);
684 }
685
686 #if !TEST_MANUAL()
687 /* finish in end_state */
688 setCurrentState(state);
689 #else
690 tap_state_t t=TAP_IDLE;
691 /* test manual drive code on any target */
692 int tms;
693 u8 tms_scan = tap_get_tms_path(t, state);
694
695 for (i = 0; i < 7; i++)
696 {
697 tms = (tms_scan >> i) & 1;
698 waitIdle();
699 ZY1000_POKE(ZY1000_JTAG_BASE+0x28, tms);
700 }
701 waitIdle();
702 ZY1000_POKE(ZY1000_JTAG_BASE+0x20, state);
703 #endif
704
705
706 return ERROR_OK;
707 }
708
709 int interface_jtag_add_runtest(int num_cycles, tap_state_t state)
710 {
711 return zy1000_jtag_add_clocks(num_cycles, state, TAP_IDLE);
712 }
713
714 int interface_jtag_add_clocks(int num_cycles)
715 {
716 return zy1000_jtag_add_clocks(num_cycles, cmd_queue_cur_state, cmd_queue_end_state);
717 }
718
719 int interface_jtag_add_sleep(u32 us)
720 {
721 jtag_sleep(us);
722 return ERROR_OK;
723 }
724
725 int interface_jtag_add_pathmove(int num_states, tap_state_t *path)
726 {
727 int state_count;
728 int tms = 0;
729
730 /*wait for the fifo to be empty*/
731 waitIdle();
732
733 state_count = 0;
734
735 tap_state_t cur_state=cmd_queue_cur_state;
736
737 while (num_states)
738 {
739 if (tap_state_transition(cur_state, false) == path[state_count])
740 {
741 tms = 0;
742 }
743 else if (tap_state_transition(cur_state, true) == path[state_count])
744 {
745 tms = 1;
746 }
747 else
748 {
749 LOG_ERROR("BUG: %s -> %s isn't a valid TAP transition", tap_state_name(cur_state), tap_state_name(path[state_count]));
750 exit(-1);
751 }
752
753 waitIdle();
754 ZY1000_POKE(ZY1000_JTAG_BASE+0x28, tms);
755
756 cur_state = path[state_count];
757 state_count++;
758 num_states--;
759 }
760
761 waitIdle();
762 ZY1000_POKE(ZY1000_JTAG_BASE+0x20, cur_state);
763 return ERROR_OK;
764 }
765
766
767
768 void embeddedice_write_dcc(jtag_tap_t *tap, int reg_addr, u8 *buffer, int little, int count)
769 {
770 // static int const reg_addr=0x5;
771 tap_state_t end_state=cmd_queue_end_state;
772 if (jtag_NextEnabledTap(jtag_NextEnabledTap(NULL))==NULL)
773 {
774 /* better performance via code duplication */
775 if (little)
776 {
777 int i;
778 for (i = 0; i < count; i++)
779 {
780 shiftValueInner(TAP_DRSHIFT, TAP_DRSHIFT, 32, fast_target_buffer_get_u32(buffer, 1));
781 shiftValueInner(TAP_DRSHIFT, end_state, 6, reg_addr|(1<<5));
782 buffer+=4;
783 }
784 } else
785 {
786 int i;
787 for (i = 0; i < count; i++)
788 {
789 shiftValueInner(TAP_DRSHIFT, TAP_DRSHIFT, 32, fast_target_buffer_get_u32(buffer, 0));
790 shiftValueInner(TAP_DRSHIFT, end_state, 6, reg_addr|(1<<5));
791 buffer+=4;
792 }
793 }
794 }
795 else
796 {
797 int i;
798 for (i = 0; i < count; i++)
799 {
800 embeddedice_write_reg_inner(tap, reg_addr, fast_target_buffer_get_u32(buffer, little));
801 buffer += 4;
802 }
803 }
804 }
805
806 int loadFile(const char *fileName, void **data, int *len);
807
808 /* boolean parameter stored on config */
809 int boolParam(char *var)
810 {
811 bool result = false;
812 char *name = alloc_printf("%s/%s", zylin_config_dir, var);
813 if (name == NULL)
814 return result;
815
816 void *data;
817 int len;
818 if (loadFile(name, &data, &len) == ERROR_OK)
819 {
820 if (len > 1)
821 len = 1;
822 result = strncmp((char *) data, "1", len) == 0;
823 free(data);
824 }
825 free(name);
826 return result;
827 }
828
829

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)