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

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)