jtag/drivers: Add vid_pid command to OpenJTAG
[openocd.git] / src / jtag / drivers / am335xgpio.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2
3 /***************************************************************************
4 * Copyright (C) 2022 by Steve Marple, stevemarple@googlemail.com *
5 * *
6 * Based on bcm2835gpio.c and linuxgpiod.c *
7 ***************************************************************************/
8
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif
12
13 #include <jtag/adapter.h>
14 #include <jtag/interface.h>
15 #include <transport/transport.h>
16 #include "bitbang.h"
17
18 #include <sys/mman.h>
19
20 /* GPIO register base addresses. Values taken from "AM335x and AMIC110 Sitara
21 * Processors Technical Reference Manual", Chapter 2 Memory Map.
22 */
23 #define AM335XGPIO_NUM_GPIO_PER_CHIP 32
24 #define AM335XGPIO_NUM_GPIO_CHIPS 4
25 #define AM335XGPIO_GPIO0_HW_ADDR 0x44E07000
26 #define AM335XGPIO_GPIO1_HW_ADDR 0x4804C000
27 #define AM335XGPIO_GPIO2_HW_ADDR 0x481AC000
28 #define AM335XGPIO_GPIO3_HW_ADDR 0x481AE000
29
30 /* 32-bit offsets from GPIO chip base address. Values taken from "AM335x and
31 * AMIC110 Sitara Processors Technical Reference Manual", Chapter 25
32 * General-Purpose Input/Output.
33 */
34 #define AM335XGPIO_GPIO_OE_OFFSET (0x134 / 4)
35 #define AM335XGPIO_GPIO_DATAIN_OFFSET (0x138 / 4)
36 #define AM335XGPIO_GPIO_DATAOUT_OFFSET (0x13C / 4) /* DATAOUT register uses 0 for output, 1 for input */
37 #define AM335XGPIO_GPIO_CLEARDATAOUT_OFFSET (0x190 / 4)
38 #define AM335XGPIO_GPIO_SETDATAOUT_OFFSET (0x194 / 4)
39
40 #define AM335XGPIO_READ_REG(chip_num, offset) \
41 (*(am335xgpio_gpio_chip_mmap_addr[(chip_num)] + (offset)))
42
43 #define AM335XGPIO_WRITE_REG(chip_num, offset, value) \
44 (*(am335xgpio_gpio_chip_mmap_addr[(chip_num)] + (offset)) = (value))
45
46 #define AM335XGPIO_SET_REG_BITS(chip_num, offset, bit_mask) \
47 (*(am335xgpio_gpio_chip_mmap_addr[(chip_num)] + (offset)) |= (bit_mask))
48
49 #define AM335XGPIO_CLEAR_REG_BITS(chip_num, offset, bit_mask) \
50 (*(am335xgpio_gpio_chip_mmap_addr[(chip_num)] + (offset)) &= ~(bit_mask))
51
52 #define AM335XGPIO_SET_INPUT(gpio_config) \
53 AM335XGPIO_SET_REG_BITS((gpio_config)->chip_num, AM335XGPIO_GPIO_OE_OFFSET, BIT((gpio_config)->gpio_num))
54 #define AM335XGPIO_SET_OUTPUT(gpio_config) \
55 AM335XGPIO_CLEAR_REG_BITS((gpio_config)->chip_num, AM335XGPIO_GPIO_OE_OFFSET, BIT((gpio_config)->gpio_num))
56 #define AM335XGPIO_SET_HIGH(gpio_config) \
57 AM335XGPIO_WRITE_REG((gpio_config)->chip_num, AM335XGPIO_GPIO_SETDATAOUT_OFFSET, BIT((gpio_config)->gpio_num))
58 #define AM335XGPIO_SET_LOW(gpio_config) \
59 AM335XGPIO_WRITE_REG((gpio_config)->chip_num, AM335XGPIO_GPIO_CLEARDATAOUT_OFFSET, BIT((gpio_config)->gpio_num))
60
61 enum amx335gpio_initial_gpio_mode {
62 AM335XGPIO_GPIO_MODE_INPUT,
63 AM335XGPIO_GPIO_MODE_OUTPUT_LOW,
64 AM335XGPIO_GPIO_MODE_OUTPUT_HIGH,
65 };
66
67 static const uint32_t am335xgpio_gpio_chip_hw_addr[AM335XGPIO_NUM_GPIO_CHIPS] = {
68 AM335XGPIO_GPIO0_HW_ADDR,
69 AM335XGPIO_GPIO1_HW_ADDR,
70 AM335XGPIO_GPIO2_HW_ADDR,
71 AM335XGPIO_GPIO3_HW_ADDR,
72 };
73
74 /* Memory-mapped address pointers */
75 static volatile uint32_t *am335xgpio_gpio_chip_mmap_addr[AM335XGPIO_NUM_GPIO_CHIPS];
76
77 static int dev_mem_fd;
78 static enum amx335gpio_initial_gpio_mode initial_gpio_mode[ADAPTER_GPIO_IDX_NUM];
79
80 /* Transition delay coefficients */
81 static int speed_coeff = 600000;
82 static int speed_offset = 575;
83 static unsigned int jtag_delay;
84
85 static const struct adapter_gpio_config *adapter_gpio_config;
86
87 static bool is_gpio_config_valid(const struct adapter_gpio_config *gpio_config)
88 {
89 return gpio_config->chip_num < AM335XGPIO_NUM_GPIO_CHIPS
90 && gpio_config->gpio_num < AM335XGPIO_NUM_GPIO_PER_CHIP;
91 }
92
93 static int get_gpio_value(const struct adapter_gpio_config *gpio_config)
94 {
95 unsigned int shift = gpio_config->gpio_num;
96 uint32_t value = AM335XGPIO_READ_REG(gpio_config->chip_num, AM335XGPIO_GPIO_DATAIN_OFFSET);
97 value = (value >> shift) & 1;
98 return value ^ (gpio_config->active_low ? 1 : 0);
99 }
100
101 static void set_gpio_value(const struct adapter_gpio_config *gpio_config, int value)
102 {
103 value = value ^ (gpio_config->active_low ? 1 : 0);
104 switch (gpio_config->drive) {
105 case ADAPTER_GPIO_DRIVE_MODE_PUSH_PULL:
106 if (value)
107 AM335XGPIO_SET_HIGH(gpio_config);
108 else
109 AM335XGPIO_SET_LOW(gpio_config);
110 /* For performance reasons assume the GPIO is already set as an output
111 * and therefore the call can be omitted here.
112 */
113 break;
114 case ADAPTER_GPIO_DRIVE_MODE_OPEN_DRAIN:
115 if (value) {
116 AM335XGPIO_SET_INPUT(gpio_config);
117 } else {
118 AM335XGPIO_SET_LOW(gpio_config);
119 AM335XGPIO_SET_OUTPUT(gpio_config);
120 }
121 break;
122 case ADAPTER_GPIO_DRIVE_MODE_OPEN_SOURCE:
123 if (value) {
124 AM335XGPIO_SET_HIGH(gpio_config);
125 AM335XGPIO_SET_OUTPUT(gpio_config);
126 } else {
127 AM335XGPIO_SET_INPUT(gpio_config);
128 }
129 break;
130 }
131 }
132
133 static enum amx335gpio_initial_gpio_mode get_gpio_mode(const struct adapter_gpio_config *gpio_config)
134 {
135 if (AM335XGPIO_READ_REG(gpio_config->chip_num, AM335XGPIO_GPIO_OE_OFFSET) & BIT(gpio_config->gpio_num))
136 return AM335XGPIO_GPIO_MODE_INPUT;
137
138 /* Return output level too so that pin mode can be fully restored */
139 if (AM335XGPIO_READ_REG(gpio_config->chip_num, AM335XGPIO_GPIO_DATAOUT_OFFSET) & BIT(gpio_config->gpio_num))
140 return AM335XGPIO_GPIO_MODE_OUTPUT_HIGH;
141 return AM335XGPIO_GPIO_MODE_OUTPUT_LOW;
142 }
143
144 static const char *get_gpio_mode_name(enum amx335gpio_initial_gpio_mode gpio_mode)
145 {
146 switch (gpio_mode) {
147 case AM335XGPIO_GPIO_MODE_INPUT:
148 return "input";
149 case AM335XGPIO_GPIO_MODE_OUTPUT_LOW:
150 return "output (low)";
151 case AM335XGPIO_GPIO_MODE_OUTPUT_HIGH:
152 return "output (high)";
153 }
154 return "unknown";
155 }
156
157 static void initialize_gpio(enum adapter_gpio_config_index idx)
158 {
159 if (!is_gpio_config_valid(&adapter_gpio_config[idx]))
160 return;
161
162 initial_gpio_mode[idx] = get_gpio_mode(&adapter_gpio_config[idx]);
163 LOG_DEBUG("saved GPIO mode for %s (GPIO %d %d): %s",
164 adapter_gpio_get_name(idx), adapter_gpio_config[idx].chip_num, adapter_gpio_config[idx].gpio_num,
165 get_gpio_mode_name(initial_gpio_mode[idx]));
166
167 if (adapter_gpio_config[idx].pull != ADAPTER_GPIO_PULL_NONE) {
168 LOG_WARNING("am335xgpio does not support pull-up or pull-down settings (signal %s)",
169 adapter_gpio_get_name(idx));
170 }
171
172 switch (adapter_gpio_config[idx].init_state) {
173 case ADAPTER_GPIO_INIT_STATE_INACTIVE:
174 set_gpio_value(&adapter_gpio_config[idx], 0);
175 break;
176 case ADAPTER_GPIO_INIT_STATE_ACTIVE:
177 set_gpio_value(&adapter_gpio_config[idx], 1);
178 break;
179 case ADAPTER_GPIO_INIT_STATE_INPUT:
180 AM335XGPIO_SET_INPUT(&adapter_gpio_config[idx]);
181 break;
182 }
183
184 /* Direction for non push-pull is already set by set_gpio_value() */
185 if (adapter_gpio_config[idx].drive == ADAPTER_GPIO_DRIVE_MODE_PUSH_PULL
186 && adapter_gpio_config[idx].init_state != ADAPTER_GPIO_INIT_STATE_INPUT)
187 AM335XGPIO_SET_OUTPUT(&adapter_gpio_config[idx]);
188 }
189
190 static void restore_gpio(enum adapter_gpio_config_index idx)
191 {
192 if (is_gpio_config_valid(&adapter_gpio_config[idx])) {
193 switch (initial_gpio_mode[idx]) {
194 case AM335XGPIO_GPIO_MODE_INPUT:
195 AM335XGPIO_SET_INPUT(&adapter_gpio_config[idx]);
196 break;
197 case AM335XGPIO_GPIO_MODE_OUTPUT_LOW:
198 AM335XGPIO_SET_LOW(&adapter_gpio_config[idx]);
199 AM335XGPIO_SET_OUTPUT(&adapter_gpio_config[idx]);
200 break;
201 case AM335XGPIO_GPIO_MODE_OUTPUT_HIGH:
202 AM335XGPIO_SET_HIGH(&adapter_gpio_config[idx]);
203 AM335XGPIO_SET_OUTPUT(&adapter_gpio_config[idx]);
204 break;
205 }
206 }
207 }
208
209 static bb_value_t am335xgpio_read(void)
210 {
211 return get_gpio_value(&adapter_gpio_config[ADAPTER_GPIO_IDX_TDO]) ? BB_HIGH : BB_LOW;
212 }
213
214 static int am335xgpio_write(int tck, int tms, int tdi)
215 {
216 set_gpio_value(&adapter_gpio_config[ADAPTER_GPIO_IDX_TDI], tdi);
217 set_gpio_value(&adapter_gpio_config[ADAPTER_GPIO_IDX_TMS], tms);
218 set_gpio_value(&adapter_gpio_config[ADAPTER_GPIO_IDX_TCK], tck); /* Write clock last */
219
220 for (unsigned int i = 0; i < jtag_delay; ++i)
221 asm volatile ("");
222
223 return ERROR_OK;
224 }
225
226 static int am335xgpio_swd_write(int swclk, int swdio)
227 {
228 set_gpio_value(&adapter_gpio_config[ADAPTER_GPIO_IDX_SWDIO], swdio);
229 set_gpio_value(&adapter_gpio_config[ADAPTER_GPIO_IDX_SWCLK], swclk); /* Write clock last */
230
231 for (unsigned int i = 0; i < jtag_delay; ++i)
232 asm volatile ("");
233
234 return ERROR_OK;
235 }
236
237 /* (1) assert or (0) deassert reset lines */
238 static int am335xgpio_reset(int trst, int srst)
239 {
240 /* As the "adapter reset_config" command keeps the srst and trst gpio drive
241 * mode settings in sync we can use our standard set_gpio_value() function
242 * that honours drive mode and active low.
243 */
244 if (is_gpio_config_valid(&adapter_gpio_config[ADAPTER_GPIO_IDX_SRST]))
245 set_gpio_value(&adapter_gpio_config[ADAPTER_GPIO_IDX_SRST], srst);
246
247 if (is_gpio_config_valid(&adapter_gpio_config[ADAPTER_GPIO_IDX_TRST]))
248 set_gpio_value(&adapter_gpio_config[ADAPTER_GPIO_IDX_TRST], trst);
249
250 LOG_DEBUG("trst %d gpio: %d %d, srst %d gpio: %d %d",
251 trst,
252 (int)adapter_gpio_config[ADAPTER_GPIO_IDX_TRST].chip_num,
253 (int)adapter_gpio_config[ADAPTER_GPIO_IDX_TRST].gpio_num,
254 srst,
255 (int)adapter_gpio_config[ADAPTER_GPIO_IDX_SRST].chip_num,
256 (int)adapter_gpio_config[ADAPTER_GPIO_IDX_SRST].gpio_num);
257 return ERROR_OK;
258 }
259
260 static void am335xgpio_swdio_drive(bool is_output)
261 {
262 if (is_output) {
263 if (is_gpio_config_valid(&adapter_gpio_config[ADAPTER_GPIO_IDX_SWDIO_DIR]))
264 set_gpio_value(&adapter_gpio_config[ADAPTER_GPIO_IDX_SWDIO_DIR], 1);
265 AM335XGPIO_SET_OUTPUT(&adapter_gpio_config[ADAPTER_GPIO_IDX_SWDIO]);
266 } else {
267 AM335XGPIO_SET_INPUT(&adapter_gpio_config[ADAPTER_GPIO_IDX_SWDIO]);
268 if (is_gpio_config_valid(&adapter_gpio_config[ADAPTER_GPIO_IDX_SWDIO_DIR]))
269 set_gpio_value(&adapter_gpio_config[ADAPTER_GPIO_IDX_SWDIO_DIR], 0);
270 }
271 }
272
273 static int am335xgpio_swdio_read(void)
274 {
275 return get_gpio_value(&adapter_gpio_config[ADAPTER_GPIO_IDX_SWDIO]);
276 }
277
278 static int am335xgpio_blink(int on)
279 {
280 if (is_gpio_config_valid(&adapter_gpio_config[ADAPTER_GPIO_IDX_LED]))
281 set_gpio_value(&adapter_gpio_config[ADAPTER_GPIO_IDX_LED], on);
282
283 return ERROR_OK;
284 }
285
286 static struct bitbang_interface am335xgpio_bitbang = {
287 .read = am335xgpio_read,
288 .write = am335xgpio_write,
289 .swdio_read = am335xgpio_swdio_read,
290 .swdio_drive = am335xgpio_swdio_drive,
291 .swd_write = am335xgpio_swd_write,
292 .blink = am335xgpio_blink
293 };
294
295 static int am335xgpio_khz(int khz, int *jtag_speed)
296 {
297 if (!khz) {
298 LOG_DEBUG("RCLK not supported");
299 return ERROR_FAIL;
300 }
301 *jtag_speed = speed_coeff / khz - speed_offset;
302 if (*jtag_speed < 0)
303 *jtag_speed = 0;
304 return ERROR_OK;
305 }
306
307 static int am335xgpio_speed_div(int speed, int *khz)
308 {
309 *khz = speed_coeff / (speed + speed_offset);
310 return ERROR_OK;
311 }
312
313 static int am335xgpio_speed(int speed)
314 {
315 jtag_delay = speed;
316 return ERROR_OK;
317 }
318
319 COMMAND_HANDLER(am335xgpio_handle_speed_coeffs)
320 {
321 if (CMD_ARGC == 2) {
322 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], speed_coeff);
323 COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], speed_offset);
324 }
325
326 command_print(CMD, "AM335x GPIO config: speed_coeffs = %d, speed_offset = %d",
327 speed_coeff, speed_offset);
328 return ERROR_OK;
329 }
330
331 static const struct command_registration am335xgpio_subcommand_handlers[] = {
332 {
333 .name = "speed_coeffs",
334 .handler = am335xgpio_handle_speed_coeffs,
335 .mode = COMMAND_CONFIG,
336 .help = "SPEED_COEFF and SPEED_OFFSET for delay calculations.",
337 .usage = "[SPEED_COEFF SPEED_OFFSET]",
338 },
339 COMMAND_REGISTRATION_DONE
340 };
341
342 static const struct command_registration am335xgpio_command_handlers[] = {
343 {
344 .name = "am335xgpio",
345 .mode = COMMAND_ANY,
346 .help = "perform am335xgpio management",
347 .chain = am335xgpio_subcommand_handlers,
348 .usage = "",
349 },
350 COMMAND_REGISTRATION_DONE
351 };
352
353 static const char * const am335xgpio_transports[] = { "jtag", "swd", NULL };
354
355 static struct jtag_interface am335xgpio_interface = {
356 .supported = DEBUG_CAP_TMS_SEQ,
357 .execute_queue = bitbang_execute_queue,
358 };
359
360 static bool am335xgpio_jtag_mode_possible(void)
361 {
362 if (!is_gpio_config_valid(&adapter_gpio_config[ADAPTER_GPIO_IDX_TCK]))
363 return false;
364 if (!is_gpio_config_valid(&adapter_gpio_config[ADAPTER_GPIO_IDX_TMS]))
365 return false;
366 if (!is_gpio_config_valid(&adapter_gpio_config[ADAPTER_GPIO_IDX_TDI]))
367 return false;
368 if (!is_gpio_config_valid(&adapter_gpio_config[ADAPTER_GPIO_IDX_TDO]))
369 return false;
370 return true;
371 }
372
373 static bool am335xgpio_swd_mode_possible(void)
374 {
375 if (!is_gpio_config_valid(&adapter_gpio_config[ADAPTER_GPIO_IDX_SWCLK]))
376 return false;
377 if (!is_gpio_config_valid(&adapter_gpio_config[ADAPTER_GPIO_IDX_SWDIO]))
378 return false;
379 return true;
380 }
381
382 static void am335xgpio_munmap(void)
383 {
384 for (unsigned int i = 0; i < AM335XGPIO_NUM_GPIO_CHIPS && am335xgpio_gpio_chip_mmap_addr[i] != MAP_FAILED; ++i)
385 if (munmap((void *)am335xgpio_gpio_chip_mmap_addr[i], sysconf(_SC_PAGE_SIZE)) < 0)
386 LOG_ERROR("Cannot unmap GPIO memory for chip %d: %s", i, strerror(errno));
387 }
388
389 static int am335xgpio_init(void)
390 {
391 LOG_INFO("AM335x GPIO JTAG/SWD bitbang driver");
392
393 bitbang_interface = &am335xgpio_bitbang;
394 adapter_gpio_config = adapter_gpio_get_config();
395
396 if (transport_is_jtag() && !am335xgpio_jtag_mode_possible()) {
397 LOG_ERROR("Require tck, tms, tdi and tdo gpios for JTAG mode");
398 return ERROR_JTAG_INIT_FAILED;
399 }
400
401 if (transport_is_swd() && !am335xgpio_swd_mode_possible()) {
402 LOG_ERROR("Require swclk and swdio gpio for SWD mode");
403 return ERROR_JTAG_INIT_FAILED;
404 }
405
406 dev_mem_fd = open("/dev/gpiomem", O_RDWR | O_SYNC);
407 if (dev_mem_fd < 0) {
408 LOG_DEBUG("Cannot open /dev/gpiomem, fallback to /dev/mem");
409 dev_mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
410 }
411 if (dev_mem_fd < 0) {
412 LOG_ERROR("open: %s", strerror(errno));
413 return ERROR_JTAG_INIT_FAILED;
414 }
415
416 for (unsigned int i = 0; i < AM335XGPIO_NUM_GPIO_CHIPS; ++i) {
417 am335xgpio_gpio_chip_mmap_addr[i] = mmap(NULL, sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_WRITE,
418 MAP_SHARED, dev_mem_fd, am335xgpio_gpio_chip_hw_addr[i]);
419
420 if (am335xgpio_gpio_chip_mmap_addr[i] == MAP_FAILED) {
421 LOG_ERROR("mmap: %s", strerror(errno));
422 am335xgpio_munmap();
423 close(dev_mem_fd);
424 return ERROR_JTAG_INIT_FAILED;
425 }
426 }
427 close(dev_mem_fd);
428
429 /* Configure JTAG/SWD signals. Default directions and initial states are handled
430 * by adapter.c and "adapter gpio" command.
431 */
432 if (transport_is_jtag()) {
433 initialize_gpio(ADAPTER_GPIO_IDX_TDO);
434 initialize_gpio(ADAPTER_GPIO_IDX_TDI);
435 initialize_gpio(ADAPTER_GPIO_IDX_TMS);
436 initialize_gpio(ADAPTER_GPIO_IDX_TCK);
437 initialize_gpio(ADAPTER_GPIO_IDX_TRST);
438 }
439
440 if (transport_is_swd()) {
441 /* swdio and its buffer should be initialized in the order that prevents
442 * two outputs from being connected together. This will occur if the
443 * swdio GPIO of the AM335x is configured as an output while its
444 * external buffer is configured to send the swdio signal from the
445 * target to the AM335x.
446 */
447 if (adapter_gpio_config[ADAPTER_GPIO_IDX_SWDIO].init_state == ADAPTER_GPIO_INIT_STATE_INPUT) {
448 initialize_gpio(ADAPTER_GPIO_IDX_SWDIO);
449 initialize_gpio(ADAPTER_GPIO_IDX_SWDIO_DIR);
450 } else {
451 initialize_gpio(ADAPTER_GPIO_IDX_SWDIO_DIR);
452 initialize_gpio(ADAPTER_GPIO_IDX_SWDIO);
453 }
454
455 initialize_gpio(ADAPTER_GPIO_IDX_SWCLK);
456 }
457
458 initialize_gpio(ADAPTER_GPIO_IDX_SRST);
459 initialize_gpio(ADAPTER_GPIO_IDX_LED);
460
461 return ERROR_OK;
462 }
463
464 static int am335xgpio_quit(void)
465 {
466 if (transport_is_jtag()) {
467 restore_gpio(ADAPTER_GPIO_IDX_TDO);
468 restore_gpio(ADAPTER_GPIO_IDX_TDI);
469 restore_gpio(ADAPTER_GPIO_IDX_TMS);
470 restore_gpio(ADAPTER_GPIO_IDX_TCK);
471 restore_gpio(ADAPTER_GPIO_IDX_TRST);
472 }
473
474 if (transport_is_swd()) {
475 /* Restore swdio/swdio_dir to their initial modes, even if that means
476 * connecting two outputs. Begin by making swdio an input so that the
477 * current and final states of swdio and swdio_dir do not have to be
478 * considered to calculate the safe restoration order.
479 */
480 AM335XGPIO_SET_INPUT(&adapter_gpio_config[ADAPTER_GPIO_IDX_SWDIO]);
481 restore_gpio(ADAPTER_GPIO_IDX_SWDIO_DIR);
482 restore_gpio(ADAPTER_GPIO_IDX_SWDIO);
483
484 restore_gpio(ADAPTER_GPIO_IDX_SWCLK);
485 }
486
487 restore_gpio(ADAPTER_GPIO_IDX_SRST);
488 restore_gpio(ADAPTER_GPIO_IDX_LED);
489
490 am335xgpio_munmap();
491
492 return ERROR_OK;
493 }
494
495 struct adapter_driver am335xgpio_adapter_driver = {
496 .name = "am335xgpio",
497 .transports = am335xgpio_transports,
498 .commands = am335xgpio_command_handlers,
499
500 .init = am335xgpio_init,
501 .quit = am335xgpio_quit,
502 .reset = am335xgpio_reset,
503 .speed = am335xgpio_speed,
504 .khz = am335xgpio_khz,
505 .speed_div = am335xgpio_speed_div,
506
507 .jtag_ops = &am335xgpio_interface,
508 .swd_ops = &bitbang_swd,
509 };

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)