This is an automated email from Gerrit. Paul Fertser (fercer...@gmail.com) just uploaded a new patch set to Gerrit, which you can find at http://openocd.zylin.com/5975
-- gerrit commit 30773d372cb86800f0b2f4aca93cf378686a3f81 Author: Paul Fertser <fercer...@gmail.com> Date: Mon Dec 14 12:23:58 2020 +0300 [RFC] jtag: drivers: introduce q&d support for Linux kernel JTAG API This is a basic client to test patches from [1] (which can only work after some bugfixing, see the review comments). Only playing back an SVF for a Lattice CPLD run-time tested. [1] https://patchwork.kernel.org/project/linux-arm-kernel/cover/20200413222920.4722-1-ernesto.cor...@intel.com/ Change-Id: I9ef81323a06cebc284fad067ae332c69780a126b Signed-off-by: Paul Fertser <fercer...@gmail.com> diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am index f7a54b0..190e1b8 100644 --- a/src/jtag/drivers/Makefile.am +++ b/src/jtag/drivers/Makefile.am @@ -190,6 +190,8 @@ if XDS110 DRIVERFILES += %D%/xds110.c endif +DRIVERFILES += %D%/kernel_jtag.c + DRIVERHEADERS = \ %D%/bitbang.h \ %D%/bitq.h \ diff --git a/src/jtag/drivers/kernel_jtag.c b/src/jtag/drivers/kernel_jtag.c new file mode 100644 index 0000000..2b77dc2 --- /dev/null +++ b/src/jtag/drivers/kernel_jtag.c @@ -0,0 +1,436 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2020 Gagar.IN LLC + * by Paul Fertser <fercer...@gmail.com> + * + * Copyright (C) 2012 by Andreas Fritiofson + * andreas.fritiof...@gmail.com + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* project specific includes */ +#include <jtag/drivers/jtag_usb_common.h> +#include <jtag/interface.h> +#include <transport/transport.h> +#include <helper/time_support.h> + +#include <assert.h> + +#include "kernel_jtag.h" +#include <sys/ioctl.h> + +static int jtag_fd; + +static uint8_t tap_state_to_kernel(tap_state_t state) +{ + switch (state) { + case TAP_DREXIT2: + return JTAG_STATE_EXIT2DR; + case TAP_DREXIT1: + return JTAG_STATE_EXIT1DR; + case TAP_DRSHIFT: + return JTAG_STATE_SHIFTDR; + case TAP_DRPAUSE: + return JTAG_STATE_PAUSEDR; + case TAP_IRSELECT: + return JTAG_STATE_SELECTIR; + case TAP_DRUPDATE: + return JTAG_STATE_UPDATEDR; + case TAP_DRCAPTURE: + return JTAG_STATE_CAPTUREDR; + case TAP_DRSELECT: + return JTAG_STATE_SELECTDR; + case TAP_IREXIT2: + return JTAG_STATE_EXIT2IR; + case TAP_IREXIT1: + return JTAG_STATE_EXIT1IR; + case TAP_IRSHIFT: + return JTAG_STATE_SHIFTIR; + case TAP_IRPAUSE: + return JTAG_STATE_PAUSEIR; + case TAP_IDLE: + return JTAG_STATE_IDLE; + case TAP_IRUPDATE: + return JTAG_STATE_UPDATEIR; + case TAP_IRCAPTURE: + return JTAG_STATE_CAPTUREIR; + case TAP_RESET: + return JTAG_STATE_TLRESET; + case TAP_INVALID: + default: + return JTAG_STATE_UPDATEIR + 1; + } +} + +/** + * Function move_to_state + * moves the TAP controller from the current state to a + * \a goal_state through a path given by tap_get_tms_path(). State transition + * logging is performed by delegation to clock_tms(). + * + * @param goal_state is the destination state for the move. + */ +static int move_to_state(tap_state_t goal_state) +{ + tap_state_t start_state = tap_get_state(); + + /* goal_state is 1/2 of a tuple/pair of states which allow convenient + lookup of the required TMS pattern to move to this state from the + start state. + */ + + /* do the 2 lookups */ + uint8_t tms_bits = tap_get_tms_path(start_state, goal_state); + int tms_count = tap_get_tms_path_len(start_state, goal_state); + assert(tms_count <= 8); + + LOG_DEBUG_IO("start=%s goal=%s", tap_state_name(start_state), tap_state_name(goal_state)); + + /* Track state transitions step by step */ + for (int i = 0; i < tms_count; i++) + tap_set_state(tap_state_transition(tap_get_state(), (tms_bits >> i) & 1)); + + struct jtag_end_tap_state endstate; + endstate.endstate = tap_state_to_kernel(goal_state); + endstate.reset = JTAG_NO_RESET; + endstate.tck = 0; + int retval = ioctl(jtag_fd, JTAG_SIOCSTATE, &endstate); + if (retval < 0) { + LOG_ERROR("couldn't set move to state"); + return ERROR_FAIL; + } + return ERROR_OK; +} + +static int kernel_jtag_speed(int speed) +{ + int retval; + uint32_t freq = speed; + + retval = ioctl(jtag_fd, JTAG_SIOCFREQ, &freq); + + if (retval < 0) { + LOG_ERROR("couldn't set JTAG TCK speed"); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int kernel_jtag_speed_div(int speed, int *khz) +{ + *khz = speed / 1000; + return ERROR_OK; +} + +static int kernel_jtag_khz(int khz, int *jtag_speed) +{ + if (khz == 0) { + /* TODO */ + LOG_DEBUG("RCLK not supported"); + return ERROR_FAIL; + } + + *jtag_speed = khz * 1000; + return ERROR_OK; +} + +static void kernel_jtag_end_state(tap_state_t state) +{ + if (tap_is_state_stable(state)) + tap_set_end_state(state); + else { + LOG_ERROR("BUG: %s is not a stable end state", tap_state_name(state)); + exit(-1); + } +} + +static void kernel_jtag_execute_runtest(struct jtag_command *cmd) +{ + LOG_DEBUG_IO("runtest %i cycles, end in %s", + cmd->cmd.runtest->num_cycles, + tap_state_name(cmd->cmd.runtest->end_state)); + + if (tap_get_state() != TAP_IDLE) + move_to_state(TAP_IDLE); + + /* TODO: Reuse kernel_jtag_execute_stableclocks */ + + move_to_state(TAP_IDLE); + + struct tck_bitbang bitbang; + bitbang.tms = 0; + bitbang.tdi = 0; + + for (int i = 0; i < cmd->cmd.runtest->num_cycles; i++) { + int retval = ioctl(jtag_fd, JTAG_IOCBITBANG, &bitbang); + if (retval < 0) { + LOG_ERROR("CBITBANG error"); + return; + } + } + + kernel_jtag_end_state(cmd->cmd.runtest->end_state); + + if (tap_get_state() != tap_get_end_state()) + move_to_state(tap_get_end_state()); + + LOG_DEBUG_IO("runtest: %i, end in %s", + cmd->cmd.runtest->num_cycles, + tap_state_name(tap_get_end_state())); +} + +static void kernel_jtag_execute_statemove(struct jtag_command *cmd) +{ + LOG_DEBUG_IO("statemove end in %s", + tap_state_name(cmd->cmd.statemove->end_state)); + + kernel_jtag_end_state(cmd->cmd.statemove->end_state); + + /* shortest-path move to desired end state */ + if (tap_get_state() != tap_get_end_state() || tap_get_end_state() == TAP_RESET) + move_to_state(tap_get_end_state()); +} + +static void kernel_jtag_execute_pathmove(struct jtag_command *cmd) +{ + tap_state_t *path = cmd->cmd.pathmove->path; + int num_states = cmd->cmd.pathmove->num_states; + + LOG_DEBUG_IO("pathmove: %i states, current: %s end: %s", num_states, + tap_state_name(tap_get_state()), + tap_state_name(path[num_states-1])); + + int state_count = 0; + unsigned bit_count = 0; + uint8_t tms_byte = 0; + + LOG_DEBUG_IO("-"); + + /* this loop verifies that the path is legal and logs each state in the path */ + while (num_states--) { + + /* either TMS=0 or TMS=1 must work ... */ + if (tap_state_transition(tap_get_state(), false) + == path[state_count]) + buf_set_u32(&tms_byte, bit_count++, 1, 0x0); + else if (tap_state_transition(tap_get_state(), true) + == path[state_count]) { + buf_set_u32(&tms_byte, bit_count++, 1, 0x1); + + /* ... or else the caller goofed BADLY */ + } else { + LOG_ERROR("BUG: %s -> %s isn't a valid " + "TAP state transition", + tap_state_name(tap_get_state()), + tap_state_name(path[state_count])); + exit(-1); + } + + tap_set_state(path[state_count]); + state_count++; + + if (bit_count == 7 || num_states == 0) { + /* TODO mpsse_clock_tms_cs_out(mpsse_ctx, + &tms_byte, + 0, + bit_count, + false, + ftdi_jtag_mode); */ + bit_count = 0; + } + } + tap_set_end_state(tap_get_state()); + move_to_state(tap_get_state()); +} + +static void kernel_jtag_execute_scan(struct jtag_command *cmd) +{ + LOG_DEBUG_IO("%s type:%d", cmd->cmd.scan->ir_scan ? "IRSCAN" : "DRSCAN", + jtag_scan_type(cmd->cmd.scan)); + + /* Make sure there are no trailing fields with num_bits == 0, or the logic below will fail. */ + while (cmd->cmd.scan->num_fields > 0 + && cmd->cmd.scan->fields[cmd->cmd.scan->num_fields - 1].num_bits == 0) { + cmd->cmd.scan->num_fields--; + LOG_DEBUG_IO("discarding trailing empty field"); + } + + if (cmd->cmd.scan->num_fields == 0) { + LOG_DEBUG_IO("empty scan, doing nothing"); + return; + } + + kernel_jtag_end_state(cmd->cmd.scan->end_state); + + struct scan_field *field = cmd->cmd.scan->fields; + unsigned scan_size = 0; + struct jtag_xfer xfer; + xfer.type = cmd->cmd.scan->ir_scan ? JTAG_SIR_XFER : JTAG_SDR_XFER; + /* TODO why doesn't this work ... + * xfer.endstate = cmd->cmd.scan->ir_scan ? JTAG_STATE_PAUSEIR : JTAG_STATE_PAUSEDR; + */ + xfer.endstate = JTAG_STATE_IDLE; + xfer.padding = 0; + + for (int i = 0; i < cmd->cmd.scan->num_fields; i++, field++) { + scan_size += field->num_bits; + LOG_DEBUG_IO("%s%s field %d/%d %d bits", + field->in_value ? "in" : "", + field->out_value ? "out" : "", + i, + cmd->cmd.scan->num_fields, + field->num_bits); + + xfer.length = field->num_bits; + + /*if (i == cmd->cmd.scan->num_fields - 1) + xfer.endstate = tap_state_to_kernel(tap_get_end_state());*/ + + if (field->in_value && field->out_value) + xfer.direction = JTAG_READ_WRITE_XFER; + else if (field->in_value) + xfer.direction = JTAG_READ_XFER; + else + xfer.direction = JTAG_WRITE_XFER; + + if (field->out_value) + xfer.tdio = (uint64_t)(uintptr_t)field->out_value; + else + xfer.tdio = (uint64_t)(uintptr_t)field->in_value; + + int retval = ioctl(jtag_fd, JTAG_IOCXFER, &xfer); + if (retval < 0) { + LOG_ERROR("Error doing JTAG_IOCXFER"); + return; + } + + if (field->in_value && field->out_value) + bit_copy(field->in_value, 0, field->out_value, 0, field->num_bits); + } + + tap_set_state(tap_get_end_state()); + + LOG_DEBUG_IO("%s scan, %i bits, end in %s", + (cmd->cmd.scan->ir_scan) ? "IR" : "DR", scan_size, + tap_state_name(tap_get_end_state())); +} + +static void kernel_jtag_execute_sleep(struct jtag_command *cmd) +{ + LOG_DEBUG_IO("sleep %" PRIi32, cmd->cmd.sleep->us); + + jtag_sleep(cmd->cmd.sleep->us); + LOG_DEBUG_IO("sleep %" PRIi32 " usec while in %s", + cmd->cmd.sleep->us, + tap_state_name(tap_get_state())); +} + +static void kernel_jtag_execute_stableclocks(struct jtag_command *cmd) +{ + /* this is only allowed while in a stable state. A check for a stable + * state was done in jtag_add_clocks() + */ + struct tck_bitbang bitbang; + bitbang.tms = 0; + bitbang.tdi = 0; + + for (int i = 0; i < cmd->cmd.runtest->num_cycles; i++) { + int retval = ioctl(jtag_fd, JTAG_IOCBITBANG, &bitbang); + if (retval < 0) { + LOG_ERROR("CBITBANG error"); + return; + } + } + + LOG_DEBUG_IO("clocks %i while in %s", + cmd->cmd.stableclocks->num_cycles, + tap_state_name(tap_get_state())); +} + +static void kernel_jtag_execute_command(struct jtag_command *cmd) +{ + switch (cmd->type) { + case JTAG_RESET: + /* TODO kernel_jtag_execute_reset(cmd); */ + break; + case JTAG_RUNTEST: + kernel_jtag_execute_runtest(cmd); + break; + case JTAG_TLR_RESET: + kernel_jtag_execute_statemove(cmd); + break; + case JTAG_PATHMOVE: + kernel_jtag_execute_pathmove(cmd); + break; + case JTAG_SCAN: + kernel_jtag_execute_scan(cmd); + break; + case JTAG_SLEEP: + kernel_jtag_execute_sleep(cmd); + break; + case JTAG_STABLECLOCKS: + kernel_jtag_execute_stableclocks(cmd); + break; + case JTAG_TMS: + /* TODO kernel_jtag_execute_tms(cmd); */ + break; + default: + LOG_ERROR("BUG: unknown JTAG command type encountered: %d", cmd->type); + break; + } +} + +static int kernel_jtag_initialize(void) +{ + jtag_fd = open("/dev/jtag0", O_RDWR); + if (jtag_fd == -1) { + LOG_ERROR("Can't open JTAG device node"); + return ERROR_FAIL; + } + + kernel_jtag_speed(jtag_get_speed_khz() * 1000); + + /* TODO HW mode doesn't work */ + struct jtag_mode mode = { .feature = JTAG_XFER_MODE, + .mode = JTAG_XFER_SW_MODE }; + ioctl(jtag_fd, JTAG_SIOCMODE, &mode); + + return ERROR_OK; +} + +static int kernel_jtag_quit(void) +{ + close(jtag_fd); + return ERROR_OK; +} + +static int kernel_jtag_execute_queue(void) +{ + for (struct jtag_command *cmd = jtag_command_queue; cmd; cmd = cmd->next) + kernel_jtag_execute_command(cmd); + + return ERROR_OK; +} + +static const char * const kernel_jtag_transports[] = { "jtag", NULL }; + +static struct jtag_interface kernel_jtag_interface = { + /* TODO .supported = DEBUG_CAP_TMS_SEQ, */ + .execute_queue = kernel_jtag_execute_queue, +}; + +struct adapter_driver kernel_jtag_adapter_driver = { + .name = "kernel_jtag", + .transports = kernel_jtag_transports, + + .init = kernel_jtag_initialize, + .quit = kernel_jtag_quit, + .speed = kernel_jtag_speed, + .speed_div = kernel_jtag_speed_div, + .khz = kernel_jtag_khz, + .jtag_ops = &kernel_jtag_interface, +}; diff --git a/src/jtag/drivers/kernel_jtag.h b/src/jtag/drivers/kernel_jtag.h new file mode 100644 index 0000000..97517ce --- /dev/null +++ b/src/jtag/drivers/kernel_jtag.h @@ -0,0 +1,197 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* include/uapi/linux/jtag.h - JTAG class driver uapi + * + * Copyright (c) 2018 Mellanox Technologies. All rights reserved. + * Copyright (c) 2018 Oleksandr Shamray <oleksan...@mellanox.com> + */ + +#ifndef __UAPI_LINUX_JTAG_H +#define __UAPI_LINUX_JTAG_H + +#include <linux/types.h> + +/* + * JTAG_XFER_MODE: JTAG transfer mode. Used to set JTAG controller transfer mode + * This is bitmask for feature param in jtag_mode for ioctl JTAG_SIOCMODE + */ +#define JTAG_XFER_MODE 0 +/* + * JTAG_CONTROL_MODE: JTAG controller mode. Used to set JTAG controller mode + * This is bitmask for feature param in jtag_mode for ioctl JTAG_SIOCMODE + */ +#define JTAG_CONTROL_MODE 1 +/* + * JTAG_SLAVE_MODE: JTAG slave mode. Used to set JTAG controller slave mode + * This is bitmask for mode param in jtag_mode for ioctl JTAG_SIOCMODE + */ +#define JTAG_SLAVE_MODE 0 +/* + * JTAG_MASTER_MODE: JTAG master mode. Used to set JTAG controller master mode + * This is bitmask for mode param in jtag_mode for ioctl JTAG_SIOCMODE + */ +#define JTAG_MASTER_MODE 1 +/* + * JTAG_XFER_HW_MODE: JTAG hardware mode. Used to set HW drived or bitbang + * mode. This is bitmask for mode param in jtag_mode for ioctl JTAG_SIOCMODE + */ +#define JTAG_XFER_HW_MODE 1 +/* + * JTAG_XFER_SW_MODE: JTAG software mode. Used to set SW drived or bitbang + * mode. This is bitmask for mode param in jtag_mode for ioctl JTAG_SIOCMODE + */ +#define JTAG_XFER_SW_MODE 0 + +/** + * enum jtag_endstate: + * + * @JTAG_STATE_TLRESET: JTAG state machine Test Logic Reset state + * @JTAG_STATE_IDLE: JTAG state machine IDLE state + * @JTAG_STATE_SELECTDR: JTAG state machine SELECT_DR state + * @JTAG_STATE_CAPTUREDR: JTAG state machine CAPTURE_DR state + * @JTAG_STATE_SHIFTDR: JTAG state machine SHIFT_DR state + * @JTAG_STATE_EXIT1DR: JTAG state machine EXIT-1 DR state + * @JTAG_STATE_PAUSEDR: JTAG state machine PAUSE_DR state + * @JTAG_STATE_EXIT2DR: JTAG state machine EXIT-2 DR state + * @JTAG_STATE_UPDATEDR: JTAG state machine UPDATE DR state + * @JTAG_STATE_SELECTIR: JTAG state machine SELECT_IR state + * @JTAG_STATE_CAPTUREIR: JTAG state machine CAPTURE_IR state + * @JTAG_STATE_SHIFTIR: JTAG state machine SHIFT_IR state + * @JTAG_STATE_EXIT1IR: JTAG state machine EXIT-1 IR state + * @JTAG_STATE_PAUSEIR: JTAG state machine PAUSE_IR state + * @JTAG_STATE_EXIT2IR: JTAG state machine EXIT-2 IR state + * @JTAG_STATE_UPDATEIR: JTAG state machine UPDATE IR state + */ +enum jtag_endstate { + JTAG_STATE_TLRESET, + JTAG_STATE_IDLE, + JTAG_STATE_SELECTDR, + JTAG_STATE_CAPTUREDR, + JTAG_STATE_SHIFTDR, + JTAG_STATE_EXIT1DR, + JTAG_STATE_PAUSEDR, + JTAG_STATE_EXIT2DR, + JTAG_STATE_UPDATEDR, + JTAG_STATE_SELECTIR, + JTAG_STATE_CAPTUREIR, + JTAG_STATE_SHIFTIR, + JTAG_STATE_EXIT1IR, + JTAG_STATE_PAUSEIR, + JTAG_STATE_EXIT2IR, + JTAG_STATE_UPDATEIR +}; + +/** + * enum jtag_reset: + * + * @JTAG_NO_RESET: JTAG run TAP from current state + * @JTAG_FORCE_RESET: JTAG force TAP to reset state + */ +enum jtag_reset { + JTAG_NO_RESET = 0, + JTAG_FORCE_RESET = 1, +}; + +/** + * enum jtag_xfer_type: + * + * @JTAG_SIR_XFER: SIR transfer + * @JTAG_SDR_XFER: SDR transfer + */ +enum jtag_xfer_type { + JTAG_SIR_XFER = 0, + JTAG_SDR_XFER = 1, +}; + +/** + * enum jtag_xfer_direction: + * + * @JTAG_READ_XFER: read transfer + * @JTAG_WRITE_XFER: write transfer + * @JTAG_READ_WRITE_XFER: read & write transfer + */ +enum jtag_xfer_direction { + JTAG_READ_XFER = 1, + JTAG_WRITE_XFER = 2, + JTAG_READ_WRITE_XFER = 3, +}; + +/** + * struct jtag_end_tap_state - forces JTAG state machine to go into a TAPC + * state + * + * @reset: 0 - run IDLE/PAUSE from current state + * 1 - go through TEST_LOGIC/RESET state before IDLE/PAUSE + * @end: completion flag + * @tck: clock counter + * + * Structure provide interface to JTAG device for JTAG set state execution. + */ +struct jtag_end_tap_state { + __u8 reset; + __u8 endstate; + __u8 tck; +}; + +/** + * struct jtag_xfer - jtag xfer: + * + * @type: transfer type + * @direction: xfer direction + * @length: xfer bits len + * @tdio : xfer data array + * @endir: xfer end state + * + * Structure provide interface to JTAG device for JTAG SDR/SIR xfer execution. + */ +struct jtag_xfer { + __u8 type; + __u8 direction; + __u8 endstate; + __u8 padding; + __u32 length; + __u64 tdio; +}; + +/** + * struct jtag_bitbang - jtag bitbang: + * + * @tms: JTAG TMS + * @tdi: JTAG TDI (input) + * @tdo: JTAG TDO (output) + * + * Structure provide interface to JTAG device for JTAG bitbang execution. + */ +struct tck_bitbang { + __u8 tms; + __u8 tdi; + __u8 tdo; +} __attribute__((__packed__)); + +/** + * struct jtag_mode - jtag mode: + * + * @feature: 0 - JTAG feature setting selector for JTAG controller HW/SW + * 1 - JTAG feature setting selector for controller + * bus(master/slave) mode. + * @mode: (0 - SW / 1 - HW) for JTAG_XFER_MODE feature(0) + * (0 - Slave / 1 - Master) for JTAG_CONTROL_MODE feature(1) + * + * Structure provide configuration modes to JTAG device. + */ +struct jtag_mode { + __u32 feature; + __u32 mode; +}; + +/* ioctl interface */ +#define __JTAG_IOCTL_MAGIC 0xb2 + +#define JTAG_SIOCSTATE _IOW(__JTAG_IOCTL_MAGIC, 0, struct jtag_end_tap_state) +#define JTAG_SIOCFREQ _IOW(__JTAG_IOCTL_MAGIC, 1, unsigned int) +#define JTAG_GIOCFREQ _IOR(__JTAG_IOCTL_MAGIC, 2, unsigned int) +#define JTAG_IOCXFER _IOWR(__JTAG_IOCTL_MAGIC, 3, struct jtag_xfer) +#define JTAG_GIOCSTATUS _IOWR(__JTAG_IOCTL_MAGIC, 4, enum jtag_endstate) +#define JTAG_SIOCMODE _IOW(__JTAG_IOCTL_MAGIC, 5, unsigned int) +#define JTAG_IOCBITBANG _IOW(__JTAG_IOCTL_MAGIC, 6, unsigned int) + +#endif /* __UAPI_LINUX_JTAG_H */ diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c index 061a78f..f88de35 100644 --- a/src/jtag/interfaces.c +++ b/src/jtag/interfaces.c @@ -152,6 +152,7 @@ extern struct adapter_driver stlink_dap_adapter_driver; #if BUILD_RSHIM == 1 extern struct adapter_driver rshim_dap_adapter_driver; #endif +extern struct adapter_driver kernel_jtag_adapter_driver; #endif /* standard drivers */ /** @@ -272,6 +273,7 @@ struct adapter_driver *adapter_drivers[] = { #if BUILD_RSHIM == 1 &rshim_dap_adapter_driver, #endif + &kernel_jtag_adapter_driver, #endif /* standard drivers */ NULL, }; -- _______________________________________________ OpenOCD-devel mailing list OpenOCD-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openocd-devel