jtag: drivers: xlnx-pcie-xvc: Add support for Xilinx XVC/PCIe
[openocd.git] / src / jtag / drivers / xlnx-pcie-xvc.c
1 /* SPDX-License-Identifier: GPL-2.0
2 *
3 * Copyright (c) 2019 Google, LLC.
4 * Author: Moritz Fischer <moritzf@google.com>
5 */
6
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
10
11 #include <stdint.h>
12 #include <stdlib.h>
13 #include <math.h>
14 #include <unistd.h>
15 #include <linux/pci.h>
16
17 #include <jtag/interface.h>
18 #include <jtag/swd.h>
19 #include <jtag/commands.h>
20 #include <helper/replacements.h>
21 #include <helper/bits.h>
22
23 #define PCIE_EXT_CAP_LST 0x100
24
25 #define XLNX_XVC_EXT_CAP 0x00
26 #define XLNX_XVC_VSEC_HDR 0x04
27 #define XLNX_XVC_LEN_REG 0x0C
28 #define XLNX_XVC_TMS_REG 0x10
29 #define XLNX_XVC_TDx_REG 0x14
30
31 #define XLNX_XVC_CAP_SIZE 0x20
32 #define XLNX_XVC_VSEC_ID 0x8
33 #define XLNX_XVC_MAX_BITS 0x20
34
35 struct xlnx_pcie_xvc {
36 int fd;
37 unsigned offset;
38 char *device;
39 };
40
41 static struct xlnx_pcie_xvc xlnx_pcie_xvc_state;
42 static struct xlnx_pcie_xvc *xlnx_pcie_xvc = &xlnx_pcie_xvc_state;
43
44 static int xlnx_pcie_xvc_read_reg(const int offset, uint32_t *val)
45 {
46 uint32_t res;
47 int err;
48
49 /* Note: This should be ok endianess-wise because by going
50 * through sysfs the kernel does the conversion in the config
51 * space accessor functions
52 */
53 err = pread(xlnx_pcie_xvc->fd, &res, sizeof(res),
54 xlnx_pcie_xvc->offset + offset);
55 if (err != sizeof(res)) {
56 LOG_ERROR("Failed to read offset %x", offset);
57 return ERROR_JTAG_DEVICE_ERROR;
58 }
59
60 if (val)
61 *val = res;
62
63 return ERROR_OK;
64 }
65
66 static int xlnx_pcie_xvc_write_reg(const int offset, const uint32_t val)
67 {
68 int err;
69
70 /* Note: This should be ok endianess-wise because by going
71 * through sysfs the kernel does the conversion in the config
72 * space accessor functions
73 */
74 err = pwrite(xlnx_pcie_xvc->fd, &val, sizeof(val),
75 xlnx_pcie_xvc->offset + offset);
76 if (err != sizeof(val)) {
77 LOG_ERROR("Failed to write offset: %x with value: %x",
78 offset, val);
79 return ERROR_JTAG_DEVICE_ERROR;
80 }
81
82 return ERROR_OK;
83 }
84
85 static int xlnx_pcie_xvc_transact(size_t num_bits, uint32_t tms, uint32_t tdi,
86 uint32_t *tdo)
87 {
88 int err;
89
90 err = xlnx_pcie_xvc_write_reg(XLNX_XVC_LEN_REG, num_bits);
91 if (err != ERROR_OK)
92 return err;
93
94 err = xlnx_pcie_xvc_write_reg(XLNX_XVC_TMS_REG, tms);
95 if (err != ERROR_OK)
96 return err;
97
98 err = xlnx_pcie_xvc_write_reg(XLNX_XVC_TDx_REG, tdi);
99 if (err != ERROR_OK)
100 return err;
101
102 err = xlnx_pcie_xvc_read_reg(XLNX_XVC_TDx_REG, tdo);
103 if (err != ERROR_OK)
104 return err;
105
106 if (tdo)
107 LOG_DEBUG_IO("Transact num_bits: %zu, tms: %x, tdi: %x, tdo: %x",
108 num_bits, tms, tdi, *tdo);
109 else
110 LOG_DEBUG_IO("Transact num_bits: %zu, tms: %x, tdi: %x, tdo: <null>",
111 num_bits, tms, tdi);
112 return ERROR_OK;
113 }
114
115 int xlnx_pcie_xvc_execute_stableclocks(struct jtag_command *cmd)
116 {
117 int tms = tap_get_state() == TAP_RESET ? 1 : 0;
118 size_t left = cmd->cmd.stableclocks->num_cycles;
119 size_t write;
120 int err;
121
122 LOG_DEBUG("stableclocks %i cycles", cmd->cmd.runtest->num_cycles);
123
124 while (left) {
125 write = MIN(XLNX_XVC_MAX_BITS, left);
126 err = xlnx_pcie_xvc_transact(write, tms, 0, NULL);
127 if (err != ERROR_OK)
128 return err;
129 left -= write;
130 };
131
132 return ERROR_OK;
133 }
134
135 static int xlnx_pcie_xvc_execute_statemove(size_t skip)
136 {
137 uint8_t tms_scan = tap_get_tms_path(tap_get_state(),
138 tap_get_end_state());
139 int tms_count = tap_get_tms_path_len(tap_get_state(),
140 tap_get_end_state());
141 int err;
142
143 LOG_DEBUG("statemove starting at (skip: %zu) %s end in %s", skip,
144 tap_state_name(tap_get_state()),
145 tap_state_name(tap_get_end_state()));
146
147
148 err = xlnx_pcie_xvc_transact(tms_count - skip, tms_scan >> skip, 0, NULL);
149 if (err != ERROR_OK)
150 return err;
151
152 tap_set_state(tap_get_end_state());
153
154 return ERROR_OK;
155 }
156
157 static int xlnx_pcie_xvc_execute_runtest(struct jtag_command *cmd)
158 {
159 int err = ERROR_OK;
160
161 LOG_DEBUG("runtest %i cycles, end in %i",
162 cmd->cmd.runtest->num_cycles,
163 cmd->cmd.runtest->end_state);
164
165 tap_state_t tmp_state = tap_get_end_state();
166
167 if (tap_get_state() != TAP_IDLE) {
168 tap_set_end_state(TAP_IDLE);
169 err = xlnx_pcie_xvc_execute_statemove(0);
170 if (err != ERROR_OK)
171 return err;
172 };
173
174 size_t left = cmd->cmd.runtest->num_cycles;
175 size_t write;
176
177 while (left) {
178 write = MIN(XLNX_XVC_MAX_BITS, left);
179 err = xlnx_pcie_xvc_transact(write, 0, 0, NULL);
180 if (err != ERROR_OK)
181 return err;
182 left -= write;
183 };
184
185 tap_set_end_state(tmp_state);
186 if (tap_get_state() != tap_get_end_state())
187 err = xlnx_pcie_xvc_execute_statemove(0);
188
189 return err;
190 }
191
192 static int xlnx_pcie_xvc_execute_pathmove(struct jtag_command *cmd)
193 {
194 size_t num_states = cmd->cmd.pathmove->num_states;
195 tap_state_t *path = cmd->cmd.pathmove->path;
196 int err = ERROR_OK;
197 size_t i;
198
199 LOG_DEBUG("pathmove: %i states, end in %i",
200 cmd->cmd.pathmove->num_states,
201 cmd->cmd.pathmove->path[cmd->cmd.pathmove->num_states - 1]);
202
203 for (i = 0; i < num_states; i++) {
204 if (path[i] == tap_state_transition(tap_get_state(), false)) {
205 err = xlnx_pcie_xvc_transact(1, 1, 0, NULL);
206 } else if (path[i] == tap_state_transition(tap_get_state(), true)) {
207 err = xlnx_pcie_xvc_transact(1, 0, 0, NULL);
208 } else {
209 LOG_ERROR("BUG: %s -> %s isn't a valid TAP transition.",
210 tap_state_name(tap_get_state()),
211 tap_state_name(path[i]));
212 err = ERROR_JTAG_QUEUE_FAILED;
213 }
214 if (err != ERROR_OK)
215 return err;
216 tap_set_state(path[i]);
217 }
218
219 tap_set_end_state(tap_get_state());
220
221 return ERROR_OK;
222 }
223
224 static int xlnx_pcie_xvc_execute_scan(struct jtag_command *cmd)
225 {
226 enum scan_type type = jtag_scan_type(cmd->cmd.scan);
227 tap_state_t saved_end_state = cmd->cmd.scan->end_state;
228 bool ir_scan = cmd->cmd.scan->ir_scan;
229 uint32_t tdi, tms, tdo;
230 uint8_t *buf, *rd_ptr;
231 int err, scan_size;
232 size_t write;
233 size_t left;
234
235 scan_size = jtag_build_buffer(cmd->cmd.scan, &buf);
236 rd_ptr = buf;
237 LOG_DEBUG("%s scan type %d %d bits; starts in %s end in %s",
238 (cmd->cmd.scan->ir_scan) ? "IR" : "DR", type, scan_size,
239 tap_state_name(tap_get_state()),
240 tap_state_name(cmd->cmd.scan->end_state));
241
242 /* If we're in TAP_DR_SHIFT state but need to do a IR_SCAN or
243 * vice-versa, do a statemove to corresponding other state, then restore
244 * end state
245 */
246 if (ir_scan && tap_get_state() != TAP_IRSHIFT) {
247 tap_set_end_state(TAP_IRSHIFT);
248 err = xlnx_pcie_xvc_execute_statemove(0);
249 if (err != ERROR_OK)
250 goto out_err;
251 tap_set_end_state(saved_end_state);
252 } else if (!ir_scan && (tap_get_state() != TAP_DRSHIFT)) {
253 tap_set_end_state(TAP_DRSHIFT);
254 err = xlnx_pcie_xvc_execute_statemove(0);
255 if (err != ERROR_OK)
256 goto out_err;
257 tap_set_end_state(saved_end_state);
258 }
259
260 left = scan_size;
261 while (left) {
262 write = MIN(XLNX_XVC_MAX_BITS, left);
263 /* the last TMS should be a 1, to leave the state */
264 tms = left <= XLNX_XVC_MAX_BITS ? BIT(write - 1) : 0;
265 tdi = (type != SCAN_IN) ? buf_get_u32(rd_ptr, 0, write) : 0;
266 err = xlnx_pcie_xvc_transact(write, tms, tdi, type != SCAN_OUT ?
267 &tdo : NULL);
268 if (err != ERROR_OK)
269 goto out_err;
270 left -= write;
271 if (type != SCAN_OUT)
272 buf_set_u32(rd_ptr, 0, write, tdo);
273 rd_ptr += sizeof(uint32_t);
274 };
275
276 err = jtag_read_buffer(buf, cmd->cmd.scan);
277 if (buf)
278 free(buf);
279
280 if (tap_get_state() != tap_get_end_state())
281 err = xlnx_pcie_xvc_execute_statemove(1);
282
283 return err;
284
285 out_err:
286 if (buf)
287 free(buf);
288 return err;
289 }
290
291 static void xlnx_pcie_xvc_execute_reset(struct jtag_command *cmd)
292 {
293 LOG_DEBUG("reset trst: %i srst: %i", cmd->cmd.reset->trst,
294 cmd->cmd.reset->srst);
295 }
296
297 static void xlnx_pcie_xvc_execute_sleep(struct jtag_command *cmd)
298 {
299 LOG_DEBUG("sleep %" PRIi32 "", cmd->cmd.sleep->us);
300 usleep(cmd->cmd.sleep->us);
301 }
302
303 static int xlnx_pcie_xvc_execute_tms(struct jtag_command *cmd)
304 {
305 const size_t num_bits = cmd->cmd.tms->num_bits;
306 const uint8_t *bits = cmd->cmd.tms->bits;
307 size_t left, write;
308 uint32_t tms;
309 int err;
310
311 LOG_DEBUG("execute tms %zu", num_bits);
312
313 left = num_bits;
314 while (left) {
315 write = MIN(XLNX_XVC_MAX_BITS, left);
316 tms = buf_get_u32(bits, 0, write);
317 err = xlnx_pcie_xvc_transact(write, tms, 0, NULL);
318 if (err != ERROR_OK)
319 return err;
320 left -= write;
321 bits += 4;
322 };
323
324 return ERROR_OK;
325 }
326
327 static int xlnx_pcie_xvc_execute_command(struct jtag_command *cmd)
328 {
329 LOG_DEBUG("%s: cmd->type: %u", __func__, cmd->type);
330 switch (cmd->type) {
331 case JTAG_STABLECLOCKS:
332 return xlnx_pcie_xvc_execute_stableclocks(cmd);
333 case JTAG_RUNTEST:
334 return xlnx_pcie_xvc_execute_runtest(cmd);
335 case JTAG_TLR_RESET:
336 tap_set_end_state(cmd->cmd.statemove->end_state);
337 return xlnx_pcie_xvc_execute_statemove(0);
338 case JTAG_PATHMOVE:
339 return xlnx_pcie_xvc_execute_pathmove(cmd);
340 case JTAG_SCAN:
341 return xlnx_pcie_xvc_execute_scan(cmd);
342 case JTAG_RESET:
343 xlnx_pcie_xvc_execute_reset(cmd);
344 break;
345 case JTAG_SLEEP:
346 xlnx_pcie_xvc_execute_sleep(cmd);
347 break;
348 case JTAG_TMS:
349 return xlnx_pcie_xvc_execute_tms(cmd);
350 default:
351 LOG_ERROR("BUG: Unknown JTAG command type encountered.");
352 return ERROR_JTAG_QUEUE_FAILED;
353 }
354
355 return ERROR_OK;
356 }
357
358 static int xlnx_pcie_xvc_execute_queue(void)
359 {
360 struct jtag_command *cmd = jtag_command_queue;
361 int ret;
362
363 while (cmd) {
364 ret = xlnx_pcie_xvc_execute_command(cmd);
365
366 if (ret != ERROR_OK)
367 return ret;
368
369 cmd = cmd->next;
370 }
371
372 return ERROR_OK;
373 }
374
375
376 static int xlnx_pcie_xvc_init(void)
377 {
378 char filename[PATH_MAX];
379 uint32_t cap, vh;
380 int err;
381
382 snprintf(filename, PATH_MAX, "/sys/bus/pci/devices/%s/config",
383 xlnx_pcie_xvc->device);
384 xlnx_pcie_xvc->fd = open(filename, O_RDWR | O_SYNC);
385 if (xlnx_pcie_xvc->fd < 0) {
386 LOG_ERROR("Failed to open device: %s", filename);
387 return ERROR_JTAG_INIT_FAILED;
388 }
389
390 LOG_INFO("Scanning PCIe device %s's for Xilinx XVC/PCIe ...",
391 xlnx_pcie_xvc->device);
392 /* Parse the PCIe extended capability list and try to find
393 * vendor specific header */
394 xlnx_pcie_xvc->offset = PCIE_EXT_CAP_LST;
395 while (xlnx_pcie_xvc->offset <= PCI_CFG_SPACE_EXP_SIZE - sizeof(cap) &&
396 xlnx_pcie_xvc->offset >= PCIE_EXT_CAP_LST) {
397 err = xlnx_pcie_xvc_read_reg(XLNX_XVC_EXT_CAP, &cap);
398 if (err != ERROR_OK)
399 return err;
400 LOG_DEBUG("Checking capability at 0x%x; id=0x%04x version=0x%x next=0x%x",
401 xlnx_pcie_xvc->offset,
402 PCI_EXT_CAP_ID(cap),
403 PCI_EXT_CAP_VER(cap),
404 PCI_EXT_CAP_NEXT(cap));
405 if (PCI_EXT_CAP_ID(cap) == PCI_EXT_CAP_ID_VNDR) {
406 err = xlnx_pcie_xvc_read_reg(XLNX_XVC_VSEC_HDR, &vh);
407 if (err != ERROR_OK)
408 return err;
409 LOG_DEBUG("Checking possible match at 0x%x; id: 0x%x; rev: 0x%x; length: 0x%x",
410 xlnx_pcie_xvc->offset,
411 PCI_VNDR_HEADER_ID(vh),
412 PCI_VNDR_HEADER_REV(vh),
413 PCI_VNDR_HEADER_LEN(vh));
414 if ((PCI_VNDR_HEADER_ID(vh) == XLNX_XVC_VSEC_ID) &&
415 (PCI_VNDR_HEADER_LEN(vh) == XLNX_XVC_CAP_SIZE))
416 break;
417 }
418 xlnx_pcie_xvc->offset = PCI_EXT_CAP_NEXT(cap);
419 }
420 if ((xlnx_pcie_xvc->offset > PCI_CFG_SPACE_EXP_SIZE - XLNX_XVC_CAP_SIZE) ||
421 xlnx_pcie_xvc->offset < PCIE_EXT_CAP_LST) {
422 close(xlnx_pcie_xvc->fd);
423 return ERROR_JTAG_INIT_FAILED;
424 }
425
426 LOG_INFO("Found Xilinx XVC/PCIe capability at offset: 0x%x", xlnx_pcie_xvc->offset);
427
428 return ERROR_OK;
429 }
430
431 static int xlnx_pcie_xvc_quit(void)
432 {
433 int err;
434
435 err = close(xlnx_pcie_xvc->fd);
436 if (err)
437 return err;
438
439 return ERROR_OK;
440 }
441
442 COMMAND_HANDLER(xlnx_pcie_xvc_handle_config_command)
443 {
444 if (CMD_ARGC < 1)
445 return ERROR_COMMAND_SYNTAX_ERROR;
446
447 /* we can't really free this in a safe manner, so at least
448 * limit the memory we're leaking by freeing the old one first
449 * before allocating a new one ...
450 */
451 if (xlnx_pcie_xvc->device)
452 free(xlnx_pcie_xvc->device);
453
454 xlnx_pcie_xvc->device = strdup(CMD_ARGV[0]);
455 return ERROR_OK;
456 }
457
458 static const struct command_registration xlnx_pcie_xvc_command_handlers[] = {
459 {
460 .name = "xlnx_pcie_xvc_config",
461 .handler = xlnx_pcie_xvc_handle_config_command,
462 .mode = COMMAND_CONFIG,
463 .help = "Configure XVC/PCIe JTAG adapter",
464 .usage = "device",
465 },
466 COMMAND_REGISTRATION_DONE
467 };
468
469 static const char * const xlnx_pcie_xvc_transports[] = { "jtag", NULL };
470
471 struct jtag_interface xlnx_pcie_xvc_interface = {
472 .name = "xlnx_pcie_xvc",
473 .commands = xlnx_pcie_xvc_command_handlers,
474 .transports = xlnx_pcie_xvc_transports,
475 .execute_queue = &xlnx_pcie_xvc_execute_queue,
476 .speed = NULL,
477 .speed_div = NULL,
478 .khz = NULL,
479 .init = &xlnx_pcie_xvc_init,
480 .quit = &xlnx_pcie_xvc_quit,
481 };

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)