+static void fill_buffer(uint8_t *buf, uint32_t val, uint32_t len)
+{
+ unsigned int tap_pos = tap_length;
+
+ while (len > 32) {
+ buf_set_u32(buf, tap_pos, 32, val);
+ len -= 32;
+ tap_pos += 32;
+ }
+ if (len)
+ buf_set_u32(buf, tap_pos, len, val);
+}
+
+static void jlink_queue_data_out(const uint8_t *data, uint32_t len)
+{
+ const uint32_t dir_out = 0xffffffff;
+
+ if (data)
+ bit_copy(tdi_buffer, tap_length, data, 0, len);
+ else
+ fill_buffer(tdi_buffer, 0, len);
+ fill_buffer(tms_buffer, dir_out, len);
+ tap_length += len;
+}
+
+static void jlink_queue_data_in(uint32_t len)
+{
+ const uint32_t dir_in = 0;
+
+ fill_buffer(tms_buffer, dir_in, len);
+ tap_length += len;
+}
+
+static int jlink_swd_switch_seq(struct adiv5_dap *dap, enum swd_special_seq seq)
+{
+ const uint8_t *s;
+ unsigned int s_len;
+
+ switch (seq) {
+ case LINE_RESET:
+ LOG_DEBUG("SWD line reset");
+ s = swd_seq_line_reset;
+ s_len = swd_seq_line_reset_len;
+ break;
+ case JTAG_TO_SWD:
+ LOG_DEBUG("JTAG-to-SWD");
+ s = swd_seq_jtag_to_swd;
+ s_len = swd_seq_jtag_to_swd_len;
+ break;
+ case SWD_TO_JTAG:
+ LOG_DEBUG("SWD-to-JTAG");
+ s = swd_seq_swd_to_jtag;
+ s_len = swd_seq_swd_to_jtag_len;
+ break;
+ default:
+ LOG_ERROR("Sequence %d not supported", seq);
+ return ERROR_FAIL;
+ }
+
+ jlink_queue_data_out(s, s_len);
+
+ return ERROR_OK;
+}
+
+static int jlink_swd_run_queue(struct adiv5_dap *dap)
+{
+ LOG_DEBUG("Executing %d queued transactions", pending_scan_results_length);
+ int retval;
+
+ if (queued_retval != ERROR_OK) {
+ LOG_DEBUG("Skipping due to previous errors: %d", queued_retval);
+ goto skip;
+ }
+
+ /* A transaction must be followed by another transaction or at least 8 idle cycles to
+ * ensure that data is clocked through the AP. */
+ jlink_queue_data_out(NULL, 8);
+
+ size_t byte_length = DIV_ROUND_UP(tap_length, 8);
+
+ /* There's a comment in jlink_tap_execute saying JLink returns
+ * an extra NULL in packet when size of incoming message is a
+ * multiple of 64. Someone should verify if that's still the
+ * case with the current jlink firmware */
+
+ usb_out_buffer[0] = EMU_CMD_HW_JTAG3;
+ usb_out_buffer[1] = 0;
+ usb_out_buffer[2] = (tap_length >> 0) & 0xff;
+ usb_out_buffer[3] = (tap_length >> 8) & 0xff;
+ memcpy(usb_out_buffer + 4, tms_buffer, byte_length);
+ memcpy(usb_out_buffer + 4 + byte_length, tdi_buffer, byte_length);
+
+ retval = jlink_usb_message(jlink_handle, 4 + 2 * byte_length,
+ byte_length + 1);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("jlink_swd_run_queue failed USB io (%d)", retval);
+ goto skip;
+ }
+
+ retval = usb_in_buffer[byte_length];
+ if (retval) {
+ LOG_ERROR("jlink_swd_run_queue failed, result %d", retval);
+ goto skip;
+ }
+
+ for (int i = 0; i < pending_scan_results_length; i++) {
+ int ack = buf_get_u32(usb_in_buffer, pending_scan_results_buffer[i].first, 3);
+
+ if (ack != SWD_ACK_OK) {
+ LOG_ERROR("SWD ack not OK: %d %s", ack,
+ ack == SWD_ACK_WAIT ? "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK");
+ queued_retval = ack;
+ goto skip;
+ } else if (pending_scan_results_buffer[i].length) {
+ uint32_t data = buf_get_u32(usb_in_buffer, 3 + pending_scan_results_buffer[i].first, 32);
+ int parity = buf_get_u32(usb_in_buffer, 3 + 32 + pending_scan_results_buffer[i].first, 1);
+
+ if (parity != parity_u32(data)) {
+ LOG_ERROR("SWD Read data parity mismatch");
+ queued_retval = ERROR_FAIL;
+ goto skip;
+ }
+
+ if (pending_scan_results_buffer[i].buffer)
+ *(uint32_t *)pending_scan_results_buffer[i].buffer = data;
+ }
+ }
+
+skip:
+ jlink_tap_init();
+ retval = queued_retval;
+ queued_retval = ERROR_OK;
+
+ return retval;
+}
+
+static void jlink_swd_queue_cmd(struct adiv5_dap *dap, uint8_t cmd, uint32_t *dst, uint32_t data)
+{
+ uint8_t data_parity_trn[DIV_ROUND_UP(32 + 1, 8)];
+ if (tap_length + 46 + 8 + dap->memaccess_tck >= sizeof(tdi_buffer) * 8 ||
+ pending_scan_results_length == MAX_PENDING_SCAN_RESULTS) {
+ /* Not enough room in the queue. Run the queue. */
+ queued_retval = jlink_swd_run_queue(dap);
+ }
+
+ if (queued_retval != ERROR_OK)
+ return;
+
+ cmd |= SWD_CMD_START | SWD_CMD_PARK;
+
+ jlink_queue_data_out(&cmd, 8);
+
+ pending_scan_results_buffer[pending_scan_results_length].first = tap_length;
+
+ if (cmd & SWD_CMD_RnW) {
+ /* Queue a read transaction */
+ pending_scan_results_buffer[pending_scan_results_length].length = 32;
+ pending_scan_results_buffer[pending_scan_results_length].buffer = dst;
+
+ jlink_queue_data_in(1 + 3 + 32 + 1 + 1);
+ } else {
+ /* Queue a write transaction */
+ pending_scan_results_buffer[pending_scan_results_length].length = 0;
+ jlink_queue_data_in(1 + 3 + 1);
+
+ buf_set_u32(data_parity_trn, 0, 32, data);
+ buf_set_u32(data_parity_trn, 32, 1, parity_u32(data));
+
+ jlink_queue_data_out(data_parity_trn, 32 + 1);
+ }
+
+ pending_scan_results_length++;
+
+ /* Insert idle cycles after AP accesses to avoid WAIT */
+ if (cmd & SWD_CMD_APnDP)
+ jlink_queue_data_out(NULL, dap->memaccess_tck);
+}
+