Encapsulate JTAG minidriver functions, plan for new header file.
[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_MINIDRIVER_H
24 #include "embeddedice.h"
25 #include "bitbang.h"
26
27 #include <cyg/hal/hal_io.h> // low level i/o
28 #include <cyg/hal/hal_diag.h>
29
30
31 #define ZYLIN_VERSION "1.52"
32 #define ZYLIN_DATE __DATE__
33 #define ZYLIN_TIME __TIME__
34 #define ZYLIN_OPENOCD "$Revision$"
35 #define ZYLIN_OPENOCD_VERSION "Zylin JTAG ZY1000 " ZYLIN_VERSION " " ZYLIN_DATE " " ZYLIN_TIME
36 const char *zylin_config_dir="/config/settings";
37
38 /* low level command set
39 */
40 int zy1000_read(void);
41 static void zy1000_write(int tck, int tms, int tdi);
42 void zy1000_reset(int trst, int srst);
43
44
45 int zy1000_speed(int speed);
46 int zy1000_register_commands(struct command_context_s *cmd_ctx);
47 int zy1000_init(void);
48 int zy1000_quit(void);
49
50 /* interface commands */
51 int zy1000_handle_zy1000_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
52
53 static int zy1000_khz(int khz, int *jtag_speed)
54 {
55 if (khz==0)
56 {
57 *jtag_speed=0;
58 }
59 else
60 {
61 *jtag_speed=64000/khz;
62 }
63 return ERROR_OK;
64 }
65
66 static int zy1000_speed_div(int speed, int *khz)
67 {
68 if (speed==0)
69 {
70 *khz = 0;
71 }
72 else
73 {
74 *khz=64000/speed;
75 }
76
77 return ERROR_OK;
78 }
79
80 static bool readPowerDropout(void)
81 {
82 cyg_uint32 state;
83 // sample and clear power dropout
84 HAL_WRITE_UINT32(ZY1000_JTAG_BASE+0x10, 0x80);
85 HAL_READ_UINT32(ZY1000_JTAG_BASE+0x10, state);
86 bool powerDropout;
87 powerDropout = (state & 0x80) != 0;
88 return powerDropout;
89 }
90
91
92 static bool readSRST(void)
93 {
94 cyg_uint32 state;
95 // sample and clear SRST sensing
96 HAL_WRITE_UINT32(ZY1000_JTAG_BASE+0x10, 0x00000040);
97 HAL_READ_UINT32(ZY1000_JTAG_BASE+0x10, state);
98 bool srstAsserted;
99 srstAsserted = (state & 0x40) != 0;
100 return srstAsserted;
101 }
102
103 static int zy1000_srst_asserted(int *srst_asserted)
104 {
105 *srst_asserted=readSRST();
106 return ERROR_OK;
107 }
108
109 static int zy1000_power_dropout(int *dropout)
110 {
111 *dropout=readPowerDropout();
112 return ERROR_OK;
113 }
114
115
116 jtag_interface_t zy1000_interface =
117 {
118 .name = "ZY1000",
119 .execute_queue = bitbang_execute_queue,
120 .speed = zy1000_speed,
121 .register_commands = zy1000_register_commands,
122 .init = zy1000_init,
123 .quit = zy1000_quit,
124 .khz = zy1000_khz,
125 .speed_div = zy1000_speed_div,
126 .power_dropout = zy1000_power_dropout,
127 .srst_asserted = zy1000_srst_asserted,
128 };
129
130 bitbang_interface_t zy1000_bitbang =
131 {
132 .read = zy1000_read,
133 .write = zy1000_write,
134 .reset = zy1000_reset
135 };
136
137
138
139 static void zy1000_write(int tck, int tms, int tdi)
140 {
141
142 }
143
144 int zy1000_read(void)
145 {
146 return -1;
147 }
148
149 extern bool readSRST(void);
150
151 void zy1000_reset(int trst, int srst)
152 {
153 LOG_DEBUG("zy1000 trst=%d, srst=%d", trst, srst);
154 if(!srst)
155 {
156 ZY1000_POKE(ZY1000_JTAG_BASE+0x14, 0x00000001);
157 }
158 else
159 {
160 /* Danger!!! if clk!=0 when in
161 * idle in TAP_IDLE, reset halt on str912 will fail.
162 */
163 ZY1000_POKE(ZY1000_JTAG_BASE+0x10, 0x00000001);
164 }
165
166 if(!trst)
167 {
168 ZY1000_POKE(ZY1000_JTAG_BASE+0x14, 0x00000002);
169 }
170 else
171 {
172 /* assert reset */
173 ZY1000_POKE(ZY1000_JTAG_BASE+0x10, 0x00000002);
174 }
175
176 if (trst||(srst&&(jtag_reset_config & RESET_SRST_PULLS_TRST)))
177 {
178 waitIdle();
179 /* we're now in the RESET state until trst is deasserted */
180 ZY1000_POKE(ZY1000_JTAG_BASE+0x20, TAP_RESET);
181 } else
182 {
183 /* We'll get RCLK failure when we assert TRST, so clear any false positives here */
184 ZY1000_POKE(ZY1000_JTAG_BASE+0x14, 0x400);
185 }
186
187 /* wait for srst to float back up */
188 if (!srst)
189 {
190 int i;
191 for (i=0; i<1000; i++)
192 {
193 // We don't want to sense our own reset, so we clear here.
194 // There is of course a timing hole where we could loose
195 // a "real" reset.
196 if (!readSRST())
197 break;
198
199 /* wait 1ms */
200 alive_sleep(1);
201 }
202
203 if (i==1000)
204 {
205 LOG_USER("SRST didn't deassert after %dms", i);
206 } else if (i>1)
207 {
208 LOG_USER("SRST took %dms to deassert", i);
209 }
210 }
211 }
212
213 int zy1000_speed(int speed)
214 {
215 if(speed == 0)
216 {
217 /*0 means RCLK*/
218 speed = 0;
219 ZY1000_POKE(ZY1000_JTAG_BASE+0x10, 0x100);
220 LOG_DEBUG("jtag_speed using RCLK");
221 }
222 else
223 {
224 if(speed > 8190 || speed < 2)
225 {
226 LOG_USER("valid ZY1000 jtag_speed=[8190,2]. Divisor is 64MHz / even values between 8190-2, i.e. min 7814Hz, max 32MHz");
227 return ERROR_INVALID_ARGUMENTS;
228 }
229
230 LOG_USER("jtag_speed %d => JTAG clk=%f", speed, 64.0/(float)speed);
231 ZY1000_POKE(ZY1000_JTAG_BASE+0x14, 0x100);
232 ZY1000_POKE(ZY1000_JTAG_BASE+0x1c, speed&~1);
233 }
234 return ERROR_OK;
235 }
236
237 static bool savePower;
238
239
240 static void setPower(bool power)
241 {
242 savePower = power;
243 if (power)
244 {
245 HAL_WRITE_UINT32(ZY1000_JTAG_BASE+0x14, 0x8);
246 } else
247 {
248 HAL_WRITE_UINT32(ZY1000_JTAG_BASE+0x10, 0x8);
249 }
250 }
251
252 int handle_power_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
253 {
254 if (argc > 1)
255 {
256 return ERROR_INVALID_ARGUMENTS;
257 }
258
259 if (argc == 1)
260 {
261 if (strcmp(args[0], "on") == 0)
262 {
263 setPower(1);
264 }
265 else if (strcmp(args[0], "off") == 0)
266 {
267 setPower(0);
268 } else
269 {
270 command_print(cmd_ctx, "arg is \"on\" or \"off\"");
271 return ERROR_INVALID_ARGUMENTS;
272 }
273 }
274
275 command_print(cmd_ctx, "Target power %s", savePower ? "on" : "off");
276
277 return ERROR_OK;
278 }
279
280
281 /* Give TELNET a way to find out what version this is */
282 static int jim_zy1000_version(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
283 {
284 if ((argc < 1) || (argc > 2))
285 return JIM_ERR;
286 char buff[128];
287 const char *version_str=NULL;
288
289 if (argc == 1)
290 {
291 version_str=ZYLIN_OPENOCD_VERSION;
292 } else
293 {
294 const char *str = Jim_GetString(argv[1], NULL);
295 if (strcmp("openocd", str) == 0)
296 {
297 int revision;
298 revision = atol(ZYLIN_OPENOCD+strlen("XRevision: "));
299 sprintf(buff, "%d", revision);
300 version_str=buff;
301 }
302 else if (strcmp("zy1000", str) == 0)
303 {
304 version_str=ZYLIN_VERSION;
305 }
306 else if (strcmp("date", str) == 0)
307 {
308 version_str=ZYLIN_DATE;
309 }
310 else
311 {
312 return JIM_ERR;
313 }
314 }
315
316 Jim_SetResult(interp, Jim_NewStringObj(interp, version_str, -1));
317
318 return JIM_OK;
319 }
320
321
322 static int
323 zylinjtag_Jim_Command_powerstatus(Jim_Interp *interp,
324 int argc,
325 Jim_Obj * const *argv)
326 {
327 if (argc != 1)
328 {
329 Jim_WrongNumArgs(interp, 1, argv, "powerstatus");
330 return JIM_ERR;
331 }
332
333 cyg_uint32 status;
334 ZY1000_PEEK(ZY1000_JTAG_BASE+0x10, status);
335
336 Jim_SetResult(interp, Jim_NewIntObj(interp, (status&0x80)!=0));
337
338 return JIM_OK;
339 }
340
341 int zy1000_register_commands(struct command_context_s *cmd_ctx)
342 {
343 register_command(cmd_ctx, NULL, "power", handle_power_command, COMMAND_ANY,
344 "power <on/off> - turn power switch to target on/off. No arguments - print status.");
345
346 Jim_CreateCommand(interp, "zy1000_version", jim_zy1000_version, NULL, NULL);
347
348
349 Jim_CreateCommand(interp, "powerstatus", zylinjtag_Jim_Command_powerstatus, NULL, NULL);
350
351 return ERROR_OK;
352 }
353
354
355
356
357 int zy1000_init(void)
358 {
359 LOG_USER("%s", ZYLIN_OPENOCD_VERSION);
360
361 ZY1000_POKE(ZY1000_JTAG_BASE+0x10, 0x30); // Turn on LED1 & LED2
362
363 setPower(true); // on by default
364
365
366 /* deassert resets. Important to avoid infinite loop waiting for SRST to deassert */
367 zy1000_reset(0, 0);
368 zy1000_speed(jtag_speed);
369
370 bitbang_interface = &zy1000_bitbang;
371
372 return ERROR_OK;
373 }
374
375 int zy1000_quit(void)
376 {
377
378 return ERROR_OK;
379 }
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, 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, 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, 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, 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, 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)