Cadence virtual debug interface (vdebug) integration
[fw/openocd] / src / jtag / drivers / xlnx-pcie-xvc.c
index fabf0f39f98850f8001e724bf1a2e3bff392b830..c05b9cf4abf5e0e633be763d4142506377491bb7 100644 (file)
 #include <helper/replacements.h>
 #include <helper/bits.h>
 
+/* Available only from kernel v4.10 */
+#ifndef PCI_CFG_SPACE_EXP_SIZE
+#define PCI_CFG_SPACE_EXP_SIZE 4096
+#endif
+
 #define PCIE_EXT_CAP_LST       0x100
 
 #define XLNX_XVC_EXT_CAP       0x00
 #define XLNX_XVC_VSEC_HDR      0x04
 #define XLNX_XVC_LEN_REG       0x0C
 #define XLNX_XVC_TMS_REG       0x10
-#define XLNX_XVC_TDx_REG       0x14
+#define XLNX_XVC_TDX_REG       0x14
 
 #define XLNX_XVC_CAP_SIZE      0x20
 #define XLNX_XVC_VSEC_ID       0x8
 #define XLNX_XVC_MAX_BITS      0x20
 
+#define MASK_ACK(x) (((x) >> 9) & 0x7)
+#define MASK_PAR(x) ((int)((x) & 0x1))
+
 struct xlnx_pcie_xvc {
        int fd;
        unsigned offset;
@@ -46,7 +54,7 @@ static int xlnx_pcie_xvc_read_reg(const int offset, uint32_t *val)
        uint32_t res;
        int err;
 
-       /* Note: This should be ok endianess-wise because by going
+       /* Note: This should be ok endianness-wise because by going
         * through sysfs the kernel does the conversion in the config
         * space accessor functions
         */
@@ -67,14 +75,14 @@ static int xlnx_pcie_xvc_write_reg(const int offset, const uint32_t val)
 {
        int err;
 
-       /* Note: This should be ok endianess-wise because by going
+       /* Note: This should be ok endianness-wise because by going
         * through sysfs the kernel does the conversion in the config
         * space accessor functions
         */
        err = pwrite(xlnx_pcie_xvc->fd, &val, sizeof(val),
                     xlnx_pcie_xvc->offset + offset);
        if (err != sizeof(val)) {
-               LOG_ERROR("Failed to write offset: %x with value: %x",
+               LOG_ERROR("Failed to write offset: %x with value: %" PRIx32,
                          offset, val);
                return ERROR_JTAG_DEVICE_ERROR;
        }
@@ -95,24 +103,24 @@ static int xlnx_pcie_xvc_transact(size_t num_bits, uint32_t tms, uint32_t tdi,
        if (err != ERROR_OK)
                return err;
 
-       err = xlnx_pcie_xvc_write_reg(XLNX_XVC_TDx_REG, tdi);
+       err = xlnx_pcie_xvc_write_reg(XLNX_XVC_TDX_REG, tdi);
        if (err != ERROR_OK)
                return err;
 
-       err = xlnx_pcie_xvc_read_reg(XLNX_XVC_TDx_REG, tdo);
+       err = xlnx_pcie_xvc_read_reg(XLNX_XVC_TDX_REG, tdo);
        if (err != ERROR_OK)
                return err;
 
        if (tdo)
-               LOG_DEBUG_IO("Transact num_bits: %zu, tms: %x, tdi: %x, tdo: %x",
+               LOG_DEBUG_IO("Transact num_bits: %zu, tms: %" PRIx32 ", tdi: %" PRIx32 ", tdo: %" PRIx32,
                             num_bits, tms, tdi, *tdo);
        else
-               LOG_DEBUG_IO("Transact num_bits: %zu, tms: %x, tdi: %x, tdo: <null>",
+               LOG_DEBUG_IO("Transact num_bits: %zu, tms: %" PRIx32 ", tdi: %" PRIx32 ", tdo: <null>",
                             num_bits, tms, tdi);
        return ERROR_OK;
 }
 
-int xlnx_pcie_xvc_execute_stableclocks(struct jtag_command *cmd)
+static int xlnx_pcie_xvc_execute_stableclocks(struct jtag_command *cmd)
 {
        int tms = tap_get_state() == TAP_RESET ? 1 : 0;
        size_t left = cmd->cmd.stableclocks->num_cycles;
@@ -274,8 +282,7 @@ static int xlnx_pcie_xvc_execute_scan(struct jtag_command *cmd)
        };
 
        err = jtag_read_buffer(buf, cmd->cmd.scan);
-       if (buf)
-               free(buf);
+       free(buf);
 
        if (tap_get_state() != tap_get_end_state())
                err = xlnx_pcie_xvc_execute_statemove(1);
@@ -283,8 +290,7 @@ static int xlnx_pcie_xvc_execute_scan(struct jtag_command *cmd)
        return err;
 
 out_err:
-       if (buf)
-               free(buf);
+       free(buf);
        return err;
 }
 
@@ -296,7 +302,7 @@ static void xlnx_pcie_xvc_execute_reset(struct jtag_command *cmd)
 
 static void xlnx_pcie_xvc_execute_sleep(struct jtag_command *cmd)
 {
-       LOG_DEBUG("sleep %" PRIi32 "", cmd->cmd.sleep->us);
+       LOG_DEBUG("sleep %" PRIu32 "", cmd->cmd.sleep->us);
        usleep(cmd->cmd.sleep->us);
 }
 
@@ -397,7 +403,7 @@ static int xlnx_pcie_xvc_init(void)
                err = xlnx_pcie_xvc_read_reg(XLNX_XVC_EXT_CAP, &cap);
                if (err != ERROR_OK)
                        return err;
-               LOG_DEBUG("Checking capability at 0x%x; id=0x%04x version=0x%x next=0x%x",
+               LOG_DEBUG("Checking capability at 0x%x; id=0x%04" PRIx32 " version=0x%" PRIx32 " next=0x%" PRIx32,
                         xlnx_pcie_xvc->offset,
                         PCI_EXT_CAP_ID(cap),
                         PCI_EXT_CAP_VER(cap),
@@ -406,7 +412,7 @@ static int xlnx_pcie_xvc_init(void)
                        err = xlnx_pcie_xvc_read_reg(XLNX_XVC_VSEC_HDR, &vh);
                        if (err != ERROR_OK)
                                return err;
-                       LOG_DEBUG("Checking possible match at 0x%x; id: 0x%x; rev: 0x%x; length: 0x%x",
+                       LOG_DEBUG("Checking possible match at 0x%x; id: 0x%" PRIx32 "; rev: 0x%" PRIx32 "; length: 0x%" PRIx32,
                                 xlnx_pcie_xvc->offset,
                                 PCI_VNDR_HEADER_ID(vh),
                                 PCI_VNDR_HEADER_REV(vh),
@@ -448,16 +454,15 @@ COMMAND_HANDLER(xlnx_pcie_xvc_handle_config_command)
         * limit the memory we're leaking by freeing the old one first
         * before allocating a new one ...
         */
-       if (xlnx_pcie_xvc->device)
-               free(xlnx_pcie_xvc->device);
+       free(xlnx_pcie_xvc->device);
 
        xlnx_pcie_xvc->device = strdup(CMD_ARGV[0]);
        return ERROR_OK;
 }
 
-static const struct command_registration xlnx_pcie_xvc_command_handlers[] = {
+static const struct command_registration xlnx_pcie_xvc_subcommand_handlers[] = {
        {
-               .name = "xlnx_pcie_xvc_config",
+               .name = "config",
                .handler = xlnx_pcie_xvc_handle_config_command,
                .mode = COMMAND_CONFIG,
                .help = "Configure XVC/PCIe JTAG adapter",
@@ -466,16 +471,235 @@ static const struct command_registration xlnx_pcie_xvc_command_handlers[] = {
        COMMAND_REGISTRATION_DONE
 };
 
-static const char * const xlnx_pcie_xvc_transports[] = { "jtag", NULL };
+static const struct command_registration xlnx_pcie_xvc_command_handlers[] = {
+       {
+               .name = "xlnx_pcie_xvc",
+               .mode = COMMAND_ANY,
+               .help = "perform xlnx_pcie_xvc management",
+               .chain = xlnx_pcie_xvc_subcommand_handlers,
+               .usage = "",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+static struct jtag_interface xlnx_pcie_xvc_jtag_ops = {
+       .execute_queue = &xlnx_pcie_xvc_execute_queue,
+};
+
+static int xlnx_pcie_xvc_swd_sequence(const uint8_t *seq, size_t length)
+{
+       size_t left, write;
+       uint32_t send;
+       int err;
+
+       left = length;
+       while (left) {
+               write = MIN(XLNX_XVC_MAX_BITS, left);
+               send = buf_get_u32(seq, 0, write);
+               err = xlnx_pcie_xvc_transact(write, send, 0, NULL);
+               if (err != ERROR_OK)
+                       return err;
+               left -= write;
+               seq += sizeof(uint32_t);
+       };
+
+       return ERROR_OK;
+}
+
+static int xlnx_pcie_xvc_swd_switch_seq(enum swd_special_seq seq)
+{
+       switch (seq) {
+       case LINE_RESET:
+               LOG_DEBUG("SWD line reset");
+               return xlnx_pcie_xvc_swd_sequence(swd_seq_line_reset,
+                                                 swd_seq_line_reset_len);
+       case JTAG_TO_SWD:
+               LOG_DEBUG("JTAG-to-SWD");
+               return xlnx_pcie_xvc_swd_sequence(swd_seq_jtag_to_swd,
+                                                 swd_seq_jtag_to_swd_len);
+       case SWD_TO_JTAG:
+               LOG_DEBUG("SWD-to-JTAG");
+               return xlnx_pcie_xvc_swd_sequence(swd_seq_swd_to_jtag,
+                                                 swd_seq_swd_to_jtag_len);
+       default:
+               LOG_ERROR("Sequence %d not supported", seq);
+               return ERROR_FAIL;
+       }
+
+       return ERROR_OK;
+}
+
+static int queued_retval;
+
+static void xlnx_pcie_xvc_swd_write_reg(uint8_t cmd, uint32_t value,
+                                       uint32_t ap_delay_clk);
+
+static void swd_clear_sticky_errors(void)
+{
+       xlnx_pcie_xvc_swd_write_reg(swd_cmd(false,  false, DP_ABORT),
+               STKCMPCLR | STKERRCLR | WDERRCLR | ORUNERRCLR, 0);
+}
+
+static void xlnx_pcie_xvc_swd_read_reg(uint8_t cmd, uint32_t *value,
+                                      uint32_t ap_delay_clk)
+{
+       uint32_t res, ack, rpar;
+       int err;
+
+       assert(cmd & SWD_CMD_RNW);
+
+       cmd |= SWD_CMD_START | SWD_CMD_PARK;
+       /* cmd + ack */
+       err = xlnx_pcie_xvc_transact(12, cmd, 0, &res);
+       if (err != ERROR_OK)
+               goto err_out;
+
+       ack = MASK_ACK(res);
+
+       /* read data */
+       err = xlnx_pcie_xvc_transact(32, 0, 0, &res);
+       if (err != ERROR_OK)
+               goto err_out;
+
+       /* parity + trn */
+       err = xlnx_pcie_xvc_transact(2, 0, 0, &rpar);
+       if (err != ERROR_OK)
+               goto err_out;
+
+       LOG_DEBUG("%s %s %s reg %X = %08"PRIx32,
+                 ack == SWD_ACK_OK ? "OK" : ack == SWD_ACK_WAIT ?
+                 "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK",
+                 cmd & SWD_CMD_APNDP ? "AP" : "DP",
+                 cmd & SWD_CMD_RNW ? "read" : "write",
+                 (cmd & SWD_CMD_A32) >> 1,
+                 res);
+       switch (ack) {
+       case SWD_ACK_OK:
+               if (MASK_PAR(rpar) != parity_u32(res)) {
+                       LOG_DEBUG_IO("Wrong parity detected");
+                       queued_retval = ERROR_FAIL;
+                       return;
+               }
+               if (value)
+                       *value = res;
+               if (cmd & SWD_CMD_APNDP)
+                       err = xlnx_pcie_xvc_transact(ap_delay_clk, 0, 0, NULL);
+               queued_retval = err;
+               return;
+       case SWD_ACK_WAIT:
+               LOG_DEBUG_IO("SWD_ACK_WAIT");
+               swd_clear_sticky_errors();
+               return;
+       case SWD_ACK_FAULT:
+               LOG_DEBUG_IO("SWD_ACK_FAULT");
+               queued_retval = ack;
+               return;
+       default:
+               LOG_DEBUG_IO("No valid acknowledge: ack=%02"PRIx32, ack);
+               queued_retval = ack;
+               return;
+       }
+err_out:
+       queued_retval = err;
+}
+
+static void xlnx_pcie_xvc_swd_write_reg(uint8_t cmd, uint32_t value,
+                                       uint32_t ap_delay_clk)
+{
+       uint32_t res, ack;
+       int err;
+
+       assert(!(cmd & SWD_CMD_RNW));
+
+       cmd |= SWD_CMD_START | SWD_CMD_PARK;
+       /* cmd + trn + ack */
+       err = xlnx_pcie_xvc_transact(13, cmd, 0, &res);
+       if (err != ERROR_OK)
+               goto err_out;
+
+       ack = MASK_ACK(res);
+
+       /* write data */
+       err = xlnx_pcie_xvc_transact(32, value, 0, NULL);
+       if (err != ERROR_OK)
+               goto err_out;
+
+       /* parity + trn */
+       err = xlnx_pcie_xvc_transact(2, parity_u32(value), 0, NULL);
+       if (err != ERROR_OK)
+               goto err_out;
+
+       LOG_DEBUG("%s %s %s reg %X = %08"PRIx32,
+                 ack == SWD_ACK_OK ? "OK" : ack == SWD_ACK_WAIT ?
+                 "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK",
+                 cmd & SWD_CMD_APNDP ? "AP" : "DP",
+                 cmd & SWD_CMD_RNW ? "read" : "write",
+                 (cmd & SWD_CMD_A32) >> 1,
+                 value);
+
+       switch (ack) {
+       case SWD_ACK_OK:
+               if (cmd & SWD_CMD_APNDP)
+                       err = xlnx_pcie_xvc_transact(ap_delay_clk, 0, 0, NULL);
+               queued_retval = err;
+               return;
+       case SWD_ACK_WAIT:
+               LOG_DEBUG_IO("SWD_ACK_WAIT");
+               swd_clear_sticky_errors();
+               return;
+       case SWD_ACK_FAULT:
+               LOG_DEBUG_IO("SWD_ACK_FAULT");
+               queued_retval = ack;
+               return;
+       default:
+               LOG_DEBUG_IO("No valid acknowledge: ack=%02"PRIx32, ack);
+               queued_retval = ack;
+               return;
+       }
+
+err_out:
+       queued_retval = err;
+}
+
+static int xlnx_pcie_xvc_swd_run_queue(void)
+{
+       int err;
+
+       /* we want at least 8 idle cycles between each transaction */
+       err = xlnx_pcie_xvc_transact(8, 0, 0, NULL);
+       if (err != ERROR_OK)
+               return err;
+
+       err = queued_retval;
+       queued_retval = ERROR_OK;
+       LOG_DEBUG("SWD queue return value: %02x", err);
+
+       return err;
+}
+
+static int xlnx_pcie_xvc_swd_init(void)
+{
+       return ERROR_OK;
+}
+
+static const struct swd_driver xlnx_pcie_xvc_swd_ops = {
+       .init = xlnx_pcie_xvc_swd_init,
+       .switch_seq = xlnx_pcie_xvc_swd_switch_seq,
+       .read_reg = xlnx_pcie_xvc_swd_read_reg,
+       .write_reg = xlnx_pcie_xvc_swd_write_reg,
+       .run = xlnx_pcie_xvc_swd_run_queue,
+};
+
+static const char * const xlnx_pcie_xvc_transports[] = { "jtag", "swd", NULL };
 
-struct jtag_interface xlnx_pcie_xvc_interface = {
+struct adapter_driver xlnx_pcie_xvc_adapter_driver = {
        .name = "xlnx_pcie_xvc",
-       .commands = xlnx_pcie_xvc_command_handlers,
        .transports = xlnx_pcie_xvc_transports,
-       .execute_queue = &xlnx_pcie_xvc_execute_queue,
-       .speed = NULL,
-       .speed_div = NULL,
-       .khz = NULL,
+       .commands = xlnx_pcie_xvc_command_handlers,
+
        .init = &xlnx_pcie_xvc_init,
        .quit = &xlnx_pcie_xvc_quit,
+
+       .jtag_ops = &xlnx_pcie_xvc_jtag_ops,
+       .swd_ops  = &xlnx_pcie_xvc_swd_ops,
 };