arm-jtag-ew: Provide armjtagew_speed_div() in order to fix interactive use of `adapte...
[fw/openocd] / src / jtag / drivers / ulink.c
index 0e04fd661d106f007c3902ff0691ebb1b2ac9e15..73f1523f384a76aa8e2fe9fa681eef947e10aa34 100644 (file)
@@ -22,6 +22,7 @@
 #include "config.h"
 #endif
 
+#include <math.h>
 #include <jtag/interface.h>
 #include <jtag/commands.h>
 #include <target/image.h>
@@ -62,8 +63,7 @@
 /** Delay (in microseconds) to wait while EZ-USB performs ReNumeration. */
 #define ULINK_RENUMERATION_DELAY 1500000
 
-/** Location of OpenULINK firmware image. TODO: Provide some way of modifying
- *  this path, maybe in a separate OpenOCD command? */
+/** Default location of OpenULINK firmware image. */
 #define ULINK_FIRMWARE_FILE      PKGLIBDIR "/OpenULINK/ulink_firmware.hex"
 
 /** Maximum size of a single firmware section. Entire EZ-USB code space = 8kB */
@@ -95,6 +95,15 @@ enum ulink_payload_direction
   PAYLOAD_DIRECTION_IN
 };
 
+enum ulink_delay_type
+{
+  DELAY_CLOCK_TCK,
+  DELAY_CLOCK_TMS,
+  DELAY_SCAN_IN,
+  DELAY_SCAN_OUT,
+  DELAY_SCAN_IO
+};
+
 /**
  * OpenULINK command (OpenULINK command queue element).
  *
@@ -136,20 +145,24 @@ struct ulink_cmd {
   /** Pointer to corresponding OpenOCD command for post-processing */
   struct jtag_command *cmd_origin;
 
-  struct ulink_cmd *next;    ///< Pointer to next command (linked list)
+  struct ulink_cmd *next;     ///< Pointer to next command (linked list)
 };
 
-typedef struct ulink_cmd ulink_cmd_t;
-
 /** Describes one driver instance */
 struct ulink
 {
   struct usb_dev_handle *usb_handle;
   enum ulink_type type;
 
-  int commands_in_queue;     ///< Number of commands in queue
-  ulink_cmd_t *queue_start;  ///< Pointer to first command in queue
-  ulink_cmd_t *queue_end;    ///< Pointer to last command in queue
+  int delay_scan_in;             ///< Delay value for SCAN_IN commands
+  int delay_scan_out;            ///< Delay value for SCAN_OUT commands
+  int delay_scan_io;             ///< Delay value for SCAN_IO commands
+  int delay_clock_tck;           ///< Delay value for CLOCK_TMS commands
+  int delay_clock_tms;           ///< Delay value for CLOCK_TCK commands
+
+  int commands_in_queue;         ///< Number of commands in queue
+  struct ulink_cmd *queue_start; ///< Pointer to first command in queue
+  struct ulink_cmd *queue_end;   ///< Pointer to last command in queue
 };
 
 /**************************** Function Prototypes *****************************/
@@ -170,19 +183,19 @@ int ulink_write_firmware_section(struct ulink *device,
 void ulink_print_signal_states(uint8_t input_signals, uint8_t output_signals);
 
 /* OpenULINK command generation helper functions */
-int ulink_allocate_payload(ulink_cmd_t *ulink_cmd, int size,
+int ulink_allocate_payload(struct ulink_cmd *ulink_cmd, int size,
     enum ulink_payload_direction direction);
 
 /* OpenULINK command queue helper functions */
 int ulink_get_queue_size(struct ulink *device,
     enum ulink_payload_direction direction);
 void ulink_clear_queue(struct ulink *device);
-int ulink_append_queue(struct ulink *device, ulink_cmd_t *ulink_cmd);
+int ulink_append_queue(struct ulink *device, struct ulink_cmd *ulink_cmd);
 int ulink_execute_queued_commands(struct ulink *device, int timeout);
 
 #ifdef _DEBUG_JTAG_IO_
 const char * ulink_cmd_id_string(uint8_t id);
-void ulink_print_command(ulink_cmd_t *ulink_cmd);
+void ulink_print_command(struct ulink_cmd *ulink_cmd);
 void ulink_print_queue(struct ulink *device);
 #endif
 
@@ -197,23 +210,28 @@ int ulink_append_get_signals_cmd(struct ulink *device);
 int ulink_append_set_signals_cmd(struct ulink *device, uint8_t low,
     uint8_t high);
 int ulink_append_sleep_cmd(struct ulink *device, uint32_t us);
-int ulink_append_configure_tck_cmd(struct ulink *device, uint8_t delay_scan,
-    uint8_t delay_tck, uint8_t delay_tms);
+int ulink_append_configure_tck_cmd(struct ulink *device, int delay_scan_in,
+    int delay_scan_out, int delay_scan_io, int delay_tck, int delay_tms);
 int ulink_append_led_cmd(struct ulink *device, uint8_t led_state);
 int ulink_append_test_cmd(struct ulink *device);
 
+/* OpenULINK TCK frequency helper functions */
+int ulink_calculate_delay(enum ulink_delay_type type, long f, int *delay);
+int ulink_calculate_frequency(enum ulink_delay_type type, int delay, long *f);
+
 /* Interface between OpenULINK and OpenOCD */
-int ulink_queue_scan(struct ulink *device, struct jtag_command *cmd);
+static void ulink_set_end_state(tap_state_t endstate);
 int ulink_queue_statemove(struct ulink *device);
-int ulink_queue_reset(struct ulink *device, struct jtag_command *cmd);
-int ulink_queue_runtest(struct ulink *device, struct jtag_command *cmd);
+
+int ulink_queue_scan(struct ulink *device, struct jtag_command *cmd);
 int ulink_queue_tlr_reset(struct ulink *device, struct jtag_command *cmd);
+int ulink_queue_runtest(struct ulink *device, struct jtag_command *cmd);
+int ulink_queue_reset(struct ulink *device, struct jtag_command *cmd);
 int ulink_queue_pathmove(struct ulink *device, struct jtag_command *cmd);
 int ulink_queue_sleep(struct ulink *device, struct jtag_command *cmd);
+int ulink_queue_stableclocks(struct ulink *device, struct jtag_command *cmd);
 
-static void ulink_set_end_state(tap_state_t endstate);
-
-int ulink_post_process_scan(ulink_cmd_t *ulink_cmd);
+int ulink_post_process_scan(struct ulink_cmd *ulink_cmd);
 int ulink_post_process_queue(struct ulink *device);
 
 /* JTAG driver functions (registered in struct jtag_interface) */
@@ -378,8 +396,9 @@ int ulink_load_firmware(struct ulink *device, char *filename)
   ulink_firmware_image.base_address = 0;
   ulink_firmware_image.base_address_set = 0;
 
-  ret = image_open(&ulink_firmware_image, ULINK_FIRMWARE_FILE, "ihex");
+  ret = image_open(&ulink_firmware_image, filename, "ihex");
   if (ret != ERROR_OK) {
+    LOG_ERROR("Could not load firmware image");
     return ret;
   }
 
@@ -502,7 +521,7 @@ void ulink_print_signal_states(uint8_t input_signals, uint8_t output_signals)
  * @return on success: ERROR_OK
  * @return on failure: ERROR_FAIL
  */
-int ulink_allocate_payload(ulink_cmd_t *ulink_cmd, int size,
+int ulink_allocate_payload(struct ulink_cmd *ulink_cmd, int size,
     enum ulink_payload_direction direction)
 {
   uint8_t *payload;
@@ -559,7 +578,7 @@ int ulink_allocate_payload(ulink_cmd_t *ulink_cmd, int size,
 int ulink_get_queue_size(struct ulink *device,
     enum ulink_payload_direction direction)
 {
-  ulink_cmd_t *current = device->queue_start;
+  struct ulink_cmd *current = device->queue_start;
   int sum = 0;
 
   while (current != NULL) {
@@ -587,8 +606,8 @@ int ulink_get_queue_size(struct ulink *device,
  */
 void ulink_clear_queue(struct ulink *device)
 {
-  ulink_cmd_t *current = device->queue_start;
-  ulink_cmd_t *next = NULL;
+  struct ulink_cmd *current = device->queue_start;
+  struct ulink_cmd *next = NULL;
 
   while (current != NULL) {
     /* Save pointer to next element */
@@ -627,7 +646,7 @@ void ulink_clear_queue(struct ulink *device)
  * @return on success: ERROR_OK
  * @return on failure: ERROR_FAIL
  */
-int ulink_append_queue(struct ulink *device, ulink_cmd_t *ulink_cmd)
+int ulink_append_queue(struct ulink *device, struct ulink_cmd *ulink_cmd)
 {
   int newsize_out, newsize_in;
   int ret;
@@ -682,7 +701,7 @@ int ulink_append_queue(struct ulink *device, ulink_cmd_t *ulink_cmd)
  */
 int ulink_execute_queued_commands(struct ulink *device, int timeout)
 {
-  ulink_cmd_t *current;
+  struct ulink_cmd *current;
   int ret, i, index_out, index_in, count_out, count_in;
   uint8_t buffer[64];
 
@@ -780,6 +799,9 @@ const char * ulink_cmd_id_string(uint8_t id)
   case CMD_CLOCK_TCK:
     return "CMD_CLOCK_TCK";
     break;
+  case CMD_SLOW_CLOCK_TCK:
+    return "CMD_SLOW_CLOCK_TCK";
+    break;
   case CMD_SLEEP_US:
     return "CMD_SLEEP_US";
     break;
@@ -812,17 +834,18 @@ const char * ulink_cmd_id_string(uint8_t id)
  *
  * @param ulink_cmd pointer to OpenULINK command.
  */
-void ulink_print_command(ulink_cmd_t *ulink_cmd)
+void ulink_print_command(struct ulink_cmd *ulink_cmd)
 {
   int i;
 
-  printf("  %-22s | OUT size = %i, bytes = 0x", ulink_cmd_id_string(ulink_cmd->id),
-      ulink_cmd->payload_out_size);
+  printf("  %-22s | OUT size = %i, bytes = 0x",
+      ulink_cmd_id_string(ulink_cmd->id), ulink_cmd->payload_out_size);
 
   for (i = 0; i < ulink_cmd->payload_out_size; i++) {
     printf("%02X ", ulink_cmd->payload_out[i]);
   }
-  printf("\n                         | IN size  = %i\n", ulink_cmd->payload_in_size);
+  printf("\n                         | IN size  = %i\n",
+      ulink_cmd->payload_in_size);
 }
 
 /**
@@ -832,7 +855,7 @@ void ulink_print_command(ulink_cmd_t *ulink_cmd)
  */
 void ulink_print_queue(struct ulink *device)
 {
-  ulink_cmd_t *current;
+  struct ulink_cmd *current;
 
   printf("OpenULINK command queue:\n");
 
@@ -878,7 +901,7 @@ int ulink_append_scan_cmd(struct ulink *device, enum scan_type scan_type,
     uint8_t tms_count_start, uint8_t tms_sequence_start, uint8_t tms_count_end,
     uint8_t tms_sequence_end, struct jtag_command *origin, bool postprocess)
 {
-  ulink_cmd_t *cmd = calloc(1, sizeof(ulink_cmd_t));
+  struct ulink_cmd *cmd = calloc(1, sizeof(struct ulink_cmd));
   int ret, i, scan_size_bytes;
   uint8_t bits_last_byte;
 
@@ -902,23 +925,38 @@ int ulink_append_scan_cmd(struct ulink *device, enum scan_type scan_type,
   }
 
   /* Allocate out_payload depending on scan type */
-  // TODO: set command ID depending on interface speed settings (slow scan)
   switch (scan_type) {
   case SCAN_IN:
-    cmd->id = CMD_SCAN_IN;
+    if (device->delay_scan_in < 0) {
+      cmd->id = CMD_SCAN_IN;
+    }
+    else {
+      cmd->id = CMD_SLOW_SCAN_IN;
+    }
     ret = ulink_allocate_payload(cmd, 5, PAYLOAD_DIRECTION_OUT);
     break;
   case SCAN_OUT:
-    cmd->id = CMD_SCAN_OUT;
+    if (device->delay_scan_out < 0) {
+      cmd->id = CMD_SCAN_OUT;
+    }
+    else {
+      cmd->id = CMD_SLOW_SCAN_OUT;
+    }
     ret = ulink_allocate_payload(cmd, scan_size_bytes + 5, PAYLOAD_DIRECTION_OUT);
     break;
   case SCAN_IO:
-    cmd->id = CMD_SCAN_IO;
+    if (device->delay_scan_io < 0) {
+      cmd->id = CMD_SCAN_IO;
+    }
+    else {
+      cmd->id = CMD_SLOW_SCAN_IO;
+    }
     ret = ulink_allocate_payload(cmd, scan_size_bytes + 5, PAYLOAD_DIRECTION_OUT);
     break;
   default:
     LOG_ERROR("BUG: ulink_append_scan_cmd() encountered an unknown scan type");
     ret = ERROR_FAIL;
+    break;
   }
 
   if (ret != ERROR_OK) {
@@ -969,14 +1007,19 @@ int ulink_append_scan_cmd(struct ulink *device, enum scan_type scan_type,
 int ulink_append_clock_tms_cmd(struct ulink *device, uint8_t count,
     uint8_t sequence)
 {
-  ulink_cmd_t *cmd = calloc(1, sizeof(ulink_cmd_t));
+  struct ulink_cmd *cmd = calloc(1, sizeof(struct ulink_cmd));
   int ret;
 
   if (cmd == NULL) {
     return ERROR_FAIL;
   }
 
-  cmd->id = CMD_CLOCK_TMS;
+  if (device->delay_clock_tms < 0) {
+    cmd->id = CMD_CLOCK_TMS;
+  }
+  else {
+    cmd->id = CMD_SLOW_CLOCK_TMS;
+  }
 
   /* CMD_CLOCK_TMS has two OUT payload bytes and zero IN payload bytes */
   ret = ulink_allocate_payload(cmd, 2, PAYLOAD_DIRECTION_OUT);
@@ -1002,14 +1045,19 @@ int ulink_append_clock_tms_cmd(struct ulink *device, uint8_t count,
  */
 int ulink_append_clock_tck_cmd(struct ulink *device, uint16_t count)
 {
-  ulink_cmd_t *cmd = calloc(1, sizeof(ulink_cmd_t));
+  struct ulink_cmd *cmd = calloc(1, sizeof(struct ulink_cmd));
   int ret;
 
   if (cmd == NULL) {
     return ERROR_FAIL;
   }
 
-  cmd->id = CMD_CLOCK_TCK;
+  if (device->delay_clock_tck < 0) {
+    cmd->id = CMD_CLOCK_TCK;
+  }
+  else {
+    cmd->id = CMD_SLOW_CLOCK_TCK;
+  }
 
   /* CMD_CLOCK_TCK has two OUT payload bytes and zero IN payload bytes */
   ret = ulink_allocate_payload(cmd, 2, PAYLOAD_DIRECTION_OUT);
@@ -1032,7 +1080,7 @@ int ulink_append_clock_tck_cmd(struct ulink *device, uint16_t count)
  */
 int ulink_append_get_signals_cmd(struct ulink *device)
 {
-  ulink_cmd_t *cmd = calloc(1, sizeof(ulink_cmd_t));
+  struct ulink_cmd *cmd = calloc(1, sizeof(struct ulink_cmd));
   int ret;
 
   if (cmd == NULL) {
@@ -1072,7 +1120,7 @@ int ulink_append_get_signals_cmd(struct ulink *device)
 int ulink_append_set_signals_cmd(struct ulink *device, uint8_t low,
     uint8_t high)
 {
-  ulink_cmd_t *cmd = calloc(1, sizeof(ulink_cmd_t));
+  struct ulink_cmd *cmd = calloc(1, sizeof(struct ulink_cmd));
   int ret;
 
   if (cmd == NULL) {
@@ -1091,7 +1139,7 @@ int ulink_append_set_signals_cmd(struct ulink *device, uint8_t low,
   cmd->payload_out[0] = low;
   cmd->payload_out[1] = high;
 
-  return ulink_append_queue(device, cmd);;
+  return ulink_append_queue(device, cmd);
 }
 
 /**
@@ -1104,7 +1152,7 @@ int ulink_append_set_signals_cmd(struct ulink *device, uint8_t low,
  */
 int ulink_append_sleep_cmd(struct ulink *device, uint32_t us)
 {
-  ulink_cmd_t *cmd = calloc(1, sizeof(ulink_cmd_t));
+  struct ulink_cmd *cmd = calloc(1, sizeof(struct ulink_cmd));
   int ret;
 
   if (cmd == NULL) {
@@ -1130,16 +1178,18 @@ int ulink_append_sleep_cmd(struct ulink *device, uint32_t us)
  * Set TCK delay counters
  *
  * @param device pointer to struct ulink identifying ULINK driver instance.
- * @param delay_scan delay count top value in jtag_slow_scan() functions
- * @param delay_tck delay count top value in jtag_clock_tck() function
- * @param delay_tms delay count top value in jtag_slow_clock_tms() function
+ * @param delay_scan_in delay count top value in jtag_slow_scan_in() function.
+ * @param delay_scan_out delay count top value in jtag_slow_scan_out() function.
+ * @param delay_scan_io delay count top value in jtag_slow_scan_io() function.
+ * @param delay_tck delay count top value in jtag_clock_tck() function.
+ * @param delay_tms delay count top value in jtag_slow_clock_tms() function.
  * @return on success: ERROR_OK
  * @return on failure: ERROR_FAIL
  */
-int ulink_append_configure_tck_cmd(struct ulink *device, uint8_t delay_scan,
-    uint8_t delay_tck, uint8_t delay_tms)
+int ulink_append_configure_tck_cmd(struct ulink *device, int delay_scan_in,
+    int delay_scan_out, int delay_scan_io, int delay_tck, int delay_tms)
 {
-  ulink_cmd_t *cmd = calloc(1, sizeof(ulink_cmd_t));
+  struct ulink_cmd *cmd = calloc(1, sizeof(struct ulink_cmd));
   int ret;
 
   if (cmd == NULL) {
@@ -1148,16 +1198,47 @@ int ulink_append_configure_tck_cmd(struct ulink *device, uint8_t delay_scan,
 
   cmd->id = CMD_CONFIGURE_TCK_FREQ;
 
-  /* CMD_CONFIGURE_TCK_FREQ has three OUT payload bytes and zero
+  /* CMD_CONFIGURE_TCK_FREQ has five OUT payload bytes and zero
    * IN payload bytes */
-  ret = ulink_allocate_payload(cmd, 3, PAYLOAD_DIRECTION_OUT);
+  ret = ulink_allocate_payload(cmd, 5, PAYLOAD_DIRECTION_OUT);
   if (ret != ERROR_OK) {
     return ret;
   }
 
-  cmd->payload_out[0] = delay_scan;
-  cmd->payload_out[1] = delay_tck;
-  cmd->payload_out[2] = delay_tms;
+  if (delay_scan_in < 0) {
+    cmd->payload_out[0] = 0;
+  }
+  else {
+    cmd->payload_out[0] = (uint8_t)delay_scan_in;
+  }
+
+  if (delay_scan_out < 0) {
+    cmd->payload_out[1] = 0;
+  }
+  else {
+    cmd->payload_out[1] = (uint8_t)delay_scan_out;
+  }
+
+  if (delay_scan_io < 0) {
+    cmd->payload_out[2] = 0;
+  }
+  else {
+    cmd->payload_out[2] = (uint8_t)delay_scan_io;
+  }
+
+  if (delay_tck < 0) {
+    cmd->payload_out[3] = 0;
+  }
+  else {
+    cmd->payload_out[3] = (uint8_t)delay_tck;
+  }
+
+  if (delay_tms < 0) {
+    cmd->payload_out[4] = 0;
+  }
+  else {
+    cmd->payload_out[4] = (uint8_t)delay_tms;
+  }
 
   return ulink_append_queue(device, cmd);
 }
@@ -1179,7 +1260,7 @@ int ulink_append_configure_tck_cmd(struct ulink *device, uint8_t delay_scan,
  */
 int ulink_append_led_cmd(struct ulink *device, uint8_t led_state)
 {
-  ulink_cmd_t *cmd = calloc(1, sizeof(ulink_cmd_t));
+  struct ulink_cmd *cmd = calloc(1, sizeof(struct ulink_cmd));
   int ret;
 
   if (cmd == NULL) {
@@ -1209,7 +1290,7 @@ int ulink_append_led_cmd(struct ulink *device, uint8_t led_state)
  */
 int ulink_append_test_cmd(struct ulink *device)
 {
-  ulink_cmd_t *cmd = calloc(1, sizeof(ulink_cmd_t));
+  struct ulink_cmd *cmd = calloc(1, sizeof(struct ulink_cmd));
   int ret;
 
   if (cmd == NULL) {
@@ -1229,8 +1310,213 @@ int ulink_append_test_cmd(struct ulink *device)
   return ulink_append_queue(device, cmd);
 }
 
+/****************** OpenULINK TCK frequency helper functions ******************/
+
+/**
+ * Calculate delay values for a given TCK frequency.
+ *
+ * The OpenULINK firmware uses five different speed values for different
+ * commands. These speed values are calculated in these functions.
+ *
+ * The five different commands which support variable TCK frequency are
+ * implemented twice in the firmware:
+ *   1. Maximum possible frequency without any artificial delay
+ *   2. Variable frequency with artificial linear delay loop
+ *
+ * To set the ULINK to maximum frequency, it is only neccessary to use the
+ * corresponding command IDs. To set the ULINK to a lower frequency, the
+ * delay loop top values have to be calculated first. Then, a
+ * CMD_CONFIGURE_TCK_FREQ command needs to be sent to the ULINK device.
+ *
+ * The delay values are described by linear equations:
+ *    t = k * x + d
+ *    (t = period, k = constant, x = delay value, d = constant)
+ *
+ * Thus, the delay can be calculated as in the following equation:
+ *    x = (t - d) / k
+ *
+ * The constants in these equations have been determined and validated by
+ * measuring the frequency resulting from different delay values.
+ *
+ * @param type for which command to calculate the delay value.
+ * @param f TCK frequency for which to calculate the delay value in Hz.
+ * @param delay where to store resulting delay value.
+ * @return on success: ERROR_OK
+ * @return on failure: ERROR_FAIL
+ */
+int ulink_calculate_delay(enum ulink_delay_type type, long f, int *delay)
+{
+  float t, x, x_ceil;
+
+  /* Calculate period of requested TCK frequency */
+  t = 1.0 / (float)(f);
+
+  switch (type) {
+  case DELAY_CLOCK_TCK:
+    x = (t - (float)(6E-6)) / (float)(4E-6);
+    break;
+  case DELAY_CLOCK_TMS:
+    x = (t - (float)(8.5E-6)) / (float)(4E-6);
+    break;
+  case DELAY_SCAN_IN:
+    x = (t - (float)(8.8308E-6)) / (float)(4E-6);
+    break;
+  case DELAY_SCAN_OUT:
+    x = (t - (float)(1.0527E-5)) / (float)(4E-6);
+    break;
+  case DELAY_SCAN_IO:
+    x = (t - (float)(1.3132E-5)) / (float)(4E-6);
+    break;
+  default:
+    return ERROR_FAIL;
+    break;
+  }
+
+  /* Check if the delay value is negative. This happens when a frequency is
+   * requested that is too high for the delay loop implementation. In this
+   * case, set delay value to zero. */
+  if (x < 0) {
+    x = 0;
+  }
+
+  /* We need to convert the exact delay value to an integer. Therefore, we
+   * round the exact value UP to ensure that the resulting frequency is NOT
+   * higher than the requested frequency. */
+  x_ceil = ceilf(x);
+
+  /* Check if the value is within limits */
+  if (x_ceil > 255) {
+    return ERROR_FAIL;
+  }
+
+  *delay = (int)x_ceil;
+
+  return ERROR_OK;
+}
+
+/**
+ * Calculate frequency for a given delay value.
+ *
+ * Similar to the #ulink_calculate_delay function, this function calculates the
+ * TCK frequency for a given delay value by using linear equations of the form:
+ *    t = k * x + d
+ *    (t = period, k = constant, x = delay value, d = constant)
+ *
+ * @param type for which command to calculate the delay value.
+ * @param delay delay value for which to calculate the resulting TCK frequency.
+ * @param f where to store the resulting TCK frequency.
+ * @return on success: ERROR_OK
+ * @return on failure: ERROR_FAIL
+ */
+int ulink_calculate_frequency(enum ulink_delay_type type, int delay, long *f)
+{
+  float t, f_float, f_rounded;
+
+  if (delay > 255) {
+    return ERROR_FAIL;
+  }
+
+  switch (type) {
+  case DELAY_CLOCK_TCK:
+    if (delay < 0) {
+      t = (float)(2.666E-6);
+    }
+    else {
+      t = (float)(4E-6) * (float)(delay) + (float)(6E-6);
+    }
+    break;
+  case DELAY_CLOCK_TMS:
+    if (delay < 0) {
+      t = (float)(5.666E-6);
+    }
+    else {
+      t = (float)(4E-6) * (float)(delay) + (float)(8.5E-6);
+    }
+    break;
+  case DELAY_SCAN_IN:
+    if (delay < 0) {
+      t = (float)(5.5E-6);
+    }
+    else {
+      t = (float)(4E-6) * (float)(delay) + (float)(8.8308E-6);
+    }
+    break;
+  case DELAY_SCAN_OUT:
+    if (delay < 0) {
+      t = (float)(7.0E-6);
+    }
+    else {
+      t = (float)(4E-6) * (float)(delay) + (float)(1.0527E-5);
+    }
+    break;
+  case DELAY_SCAN_IO:
+    if (delay < 0) {
+      t = (float)(9.926E-6);
+    }
+    else {
+      t = (float)(4E-6) * (float)(delay) + (float)(1.3132E-5);
+    }
+    break;
+  default:
+    return ERROR_FAIL;
+    break;
+  }
+
+  f_float = 1.0 / t;
+  f_rounded = roundf(f_float);
+  *f = (long)f_rounded;
+
+  return ERROR_OK;
+}
+
 /******************* Interface between OpenULINK and OpenOCD ******************/
 
+/**
+ * Sets the end state follower (see interface.h) if \a endstate is a stable
+ * state.
+ *
+ * @param endstate the state the end state follower should be set to.
+ */
+static void ulink_set_end_state(tap_state_t endstate)
+{
+  if (tap_is_state_stable(endstate)) {
+    tap_set_end_state(endstate);
+  }
+  else {
+    LOG_ERROR("BUG: %s is not a valid end state", tap_state_name(endstate));
+    exit( EXIT_FAILURE);
+  }
+}
+
+/**
+ * Move from the current TAP state to the current TAP end state.
+ *
+ * @param device pointer to struct ulink identifying ULINK driver instance.
+ * @return on success: ERROR_OK
+ * @return on failure: ERROR_FAIL
+ */
+int ulink_queue_statemove(struct ulink *device)
+{
+  uint8_t tms_sequence, tms_count;
+  int ret;
+
+  if (tap_get_state() == tap_get_end_state()) {
+    /* Do nothing if we are already there */
+    return ERROR_OK;
+  }
+
+  tms_sequence = tap_get_tms_path(tap_get_state(), tap_get_end_state());
+  tms_count = tap_get_tms_path_len(tap_get_state(), tap_get_end_state());
+
+  ret = ulink_append_clock_tms_cmd(device, tms_count, tms_sequence);
+
+  if (ret == ERROR_OK) {
+    tap_set_state(tap_get_end_state());
+  }
+
+  return ret;
+}
+
 /**
  * Perform a scan operation on a JTAG register.
  *
@@ -1265,7 +1551,7 @@ int ulink_queue_scan(struct ulink *device, struct jtag_command *cmd)
   /* Determine scan type (IN/OUT/IO) */
   type = jtag_scan_type(cmd->cmd.scan);
 
-  /* Determine number of scan commands */
+  /* Determine number of scan commands with maximum payload */
   scans_max_payload = scan_size_bytes / 58;
 
   /* Determine size of last shift command */
@@ -1322,7 +1608,7 @@ int ulink_queue_scan(struct ulink *device, struct jtag_command *cmd)
     tms_sequence_resume = tap_get_tms_path(TAP_DRPAUSE, TAP_DRSHIFT);
   }
 
-  /* Generate scan commands with full payload */
+  /* Generate scan commands */
   bytecount = scan_size_bytes;
   while (bytecount > 0) {
     if (bytecount == scan_size_bytes) {
@@ -1390,78 +1676,24 @@ int ulink_queue_scan(struct ulink *device, struct jtag_command *cmd)
 }
 
 /**
- * Sets the end state follower (see interface.h) if \a endstate is a stable
- * state.
- *
- * @param endstate the state the end state follower should be set to.
- */
-static void ulink_set_end_state(tap_state_t endstate)
-{
-  if (tap_is_state_stable(endstate)) {
-    tap_set_end_state(endstate);
-  }
-  else {
-    LOG_ERROR("BUG: %s is not a valid end state", tap_state_name(endstate));
-    exit( EXIT_FAILURE);
-  }
-}
-
-/**
- * Move from the current TAP state to the current TAP end state.
+ * Move the TAP into the Test Logic Reset state.
  *
  * @param device pointer to struct ulink identifying ULINK driver instance.
+ * @param cmd pointer to the command that shall be executed.
  * @return on success: ERROR_OK
  * @return on failure: ERROR_FAIL
  */
-int ulink_queue_statemove(struct ulink *device)
+int ulink_queue_tlr_reset(struct ulink *device, struct jtag_command *cmd)
 {
-  uint8_t tms_sequence, tms_count;
   int ret;
 
-  if (tap_get_state() == tap_get_end_state()) {
-    /* Do nothing if we are already there */
-    return ERROR_OK;
-  }
-
-  tms_sequence = tap_get_tms_path(tap_get_state(), tap_get_end_state());
-  tms_count = tap_get_tms_path_len(tap_get_state(), tap_get_end_state());
-
-  ret = ulink_append_clock_tms_cmd(device, tms_count, tms_sequence);
+  ret = ulink_append_clock_tms_cmd(device, 5, 0xff);
 
   if (ret == ERROR_OK) {
-    tap_set_state(tap_get_end_state());
-  }
-
-  return ret;
-}
-
-/**
- * Execute a JTAG_RESET command
- *
- * @param cmd pointer to the command that shall be executed.
- * @return on success: ERROR_OK
- * @return on failure: ERROR_FAIL
- */
-int ulink_queue_reset(struct ulink *device, struct jtag_command *cmd)
-{
-  uint8_t low = 0, high = 0;
-
-  if (cmd->cmd.reset->trst) {
     tap_set_state(TAP_RESET);
-    high |= SIGNAL_TRST;
-  }
-  else {
-    low |= SIGNAL_TRST;
-  }
-
-  if (cmd->cmd.reset->srst) {
-    high |= SIGNAL_RESET;
-  }
-  else {
-    low |= SIGNAL_RESET;
   }
 
-  return ulink_append_set_signals_cmd(device, low, high);
+  return ret;
 }
 
 /**
@@ -1501,24 +1733,32 @@ int ulink_queue_runtest(struct ulink *device, struct jtag_command *cmd)
 }
 
 /**
- * Move the TAP into the Test Logic Reset state.
+ * Execute a JTAG_RESET command
  *
- * @param device pointer to struct ulink identifying ULINK driver instance.
  * @param cmd pointer to the command that shall be executed.
  * @return on success: ERROR_OK
  * @return on failure: ERROR_FAIL
  */
-int ulink_queue_tlr_reset(struct ulink *device, struct jtag_command *cmd)
+int ulink_queue_reset(struct ulink *device, struct jtag_command *cmd)
 {
-  int ret;
-
-  ret = ulink_append_clock_tms_cmd(device, 5, 0xff);
+  uint8_t low = 0, high = 0;
 
-  if (ret == ERROR_OK) {
+  if (cmd->cmd.reset->trst) {
     tap_set_state(TAP_RESET);
+    high |= SIGNAL_TRST;
+  }
+  else {
+    low |= SIGNAL_TRST;
   }
 
-  return ret;
+  if (cmd->cmd.reset->srst) {
+    high |= SIGNAL_RESET;
+  }
+  else {
+    low |= SIGNAL_RESET;
+  }
+
+  return ulink_append_set_signals_cmd(device, low, high);
 }
 
 /**
@@ -1531,7 +1771,57 @@ int ulink_queue_tlr_reset(struct ulink *device, struct jtag_command *cmd)
  */
 int ulink_queue_pathmove(struct ulink *device, struct jtag_command *cmd)
 {
-  // TODO: Implement this!
+    int ret, i, num_states, batch_size, state_count;
+  tap_state_t *path;
+  uint8_t tms_sequence;
+
+  num_states = cmd->cmd.pathmove->num_states;
+  path = cmd->cmd.pathmove->path;
+  state_count = 0;
+
+  while (num_states > 0) {
+    tms_sequence = 0;
+
+    /* Determine batch size */
+    if (num_states >= 8) {
+      batch_size = 8;
+    }
+    else {
+      batch_size = num_states;
+    }
+
+    for (i = 0; i < batch_size; i++) {
+      if (tap_state_transition(tap_get_state(), false) == path[state_count]) {
+        /* Append '0' transition: clear bit 'i' in tms_sequence */
+        buf_set_u32(&tms_sequence, i, 1, 0x0);
+      }
+      else if (tap_state_transition(tap_get_state(), true)
+          == path[state_count]) {
+        /* Append '1' transition: set bit 'i' in tms_sequence */
+        buf_set_u32(&tms_sequence, i, 1, 0x1);
+      }
+      else {
+        /* Invalid state transition */
+        LOG_ERROR("BUG: %s -> %s isn't a valid TAP state transition",
+            tap_state_name(tap_get_state()),
+            tap_state_name(path[state_count]));
+        return ERROR_FAIL;
+      }
+
+      tap_set_state(path[state_count]);
+      state_count++;
+      num_states--;
+    }
+
+    /* Append CLOCK_TMS command to OpenULINK command queue */
+    LOG_INFO(
+        "pathmove batch: count = %i, sequence = 0x%x", batch_size, tms_sequence);
+    ret = ulink_append_clock_tms_cmd(ulink_handle, batch_size, tms_sequence);
+    if (ret != ERROR_OK) {
+      return ret;
+    }
+  }
+
   return ERROR_OK;
 }
 
@@ -1550,6 +1840,55 @@ int ulink_queue_sleep(struct ulink *device, struct jtag_command *cmd)
   return ulink_append_sleep_cmd(device, cmd->cmd.sleep->us);
 }
 
+/**
+ * Generate TCK cycles while remaining in a stable state.
+ *
+ * @param device pointer to struct ulink identifying ULINK driver instance.
+ * @param cmd pointer to the command that shall be executed.
+ */
+int ulink_queue_stableclocks(struct ulink *device, struct jtag_command *cmd)
+{
+  int ret;
+  unsigned num_cycles;
+
+  if (!tap_is_state_stable(tap_get_state())) {
+    LOG_ERROR("JTAG_STABLECLOCKS: state not stable");
+    return ERROR_FAIL;
+  }
+
+  num_cycles = cmd->cmd.stableclocks->num_cycles;
+
+  /* TMS stays either high (Test Logic Reset state) or low (all other states) */
+  if (tap_get_state() == TAP_RESET) {
+    ret = ulink_append_set_signals_cmd(device, 0, SIGNAL_TMS);
+  }
+  else {
+    ret = ulink_append_set_signals_cmd(device, SIGNAL_TMS, 0);
+  }
+
+  if (ret != ERROR_OK) {
+    return ret;
+  }
+
+  while (num_cycles > 0) {
+    if (num_cycles > 0xFFFF) {
+      /* OpenULINK CMD_CLOCK_TCK can generate up to 0xFFFF (uint16_t) cycles */
+      ret = ulink_append_clock_tck_cmd(device, 0xFFFF);
+      num_cycles -= 0xFFFF;
+    }
+    else {
+      ret = ulink_append_clock_tck_cmd(device, num_cycles);
+      num_cycles = 0;
+    }
+
+    if (ret != ERROR_OK) {
+      return ret;
+    }
+  }
+
+  return ERROR_OK;
+}
+
 /**
  * Post-process JTAG_SCAN command
  *
@@ -1557,7 +1896,7 @@ int ulink_queue_sleep(struct ulink *device, struct jtag_command *cmd)
  * @return on success: ERROR_OK
  * @return on failure: ERROR_FAIL
  */
-int ulink_post_process_scan(ulink_cmd_t *ulink_cmd)
+int ulink_post_process_scan(struct ulink_cmd *ulink_cmd)
 {
   struct jtag_command *cmd = ulink_cmd->cmd_origin;
   int ret;
@@ -1590,7 +1929,7 @@ int ulink_post_process_scan(ulink_cmd_t *ulink_cmd)
  */
 int ulink_post_process_queue(struct ulink *device)
 {
-  ulink_cmd_t *current;
+  struct ulink_cmd *current;
   struct jtag_command *openocd_cmd;
   int ret;
 
@@ -1606,11 +1945,12 @@ int ulink_post_process_queue(struct ulink *device)
       case JTAG_SCAN:
         ret = ulink_post_process_scan(current);
         break;
-      case JTAG_RUNTEST:
       case JTAG_TLR_RESET:
-      case JTAG_PATHMOVE:
+      case JTAG_RUNTEST:
       case JTAG_RESET:
+      case JTAG_PATHMOVE:
       case JTAG_SLEEP:
+      case JTAG_STABLECLOCKS:
         /* Nothing to do for these commands */
         ret = ERROR_OK;
         break;
@@ -1618,6 +1958,7 @@ int ulink_post_process_queue(struct ulink *device)
         ret = ERROR_FAIL;
         LOG_ERROR("BUG: ulink_post_process_queue() encountered unknown JTAG "
             "command type");
+        break;
       }
 
       if (ret != ERROR_OK) {
@@ -1655,24 +1996,32 @@ static int ulink_execute_queue(void)
     case JTAG_SCAN:
       ret = ulink_queue_scan(ulink_handle, cmd);
       break;
-    case JTAG_RUNTEST:
-      ret = ulink_queue_runtest(ulink_handle, cmd);
-      break;
     case JTAG_TLR_RESET:
       ret = ulink_queue_tlr_reset(ulink_handle, cmd);
       break;
-    case JTAG_PATHMOVE:
-      ret = ulink_queue_pathmove(ulink_handle, cmd);
+    case JTAG_RUNTEST:
+      ret = ulink_queue_runtest(ulink_handle, cmd);
       break;
     case JTAG_RESET:
       ret = ulink_queue_reset(ulink_handle, cmd);
       break;
+    case JTAG_PATHMOVE:
+      ret = ulink_queue_pathmove(ulink_handle, cmd);
+      break;
     case JTAG_SLEEP:
       ret = ulink_queue_sleep(ulink_handle, cmd);
       break;
+    case JTAG_STABLECLOCKS:
+      ret = ulink_queue_stableclocks(ulink_handle, cmd);
+      break;
     default:
       ret = ERROR_FAIL;
       LOG_ERROR("BUG: encountered unknown JTAG command type");
+      break;
+    }
+
+    if (ret != ERROR_OK) {
+      return ret;
     }
 
     cmd = cmd->next;
@@ -1698,26 +2047,108 @@ static int ulink_execute_queue(void)
 /**
  * Set the TCK frequency of the ULINK adapter.
  *
- * @param khz ???
- * @param jtag_speed ???
+ * @param khz desired JTAG TCK frequency.
+ * @param jtag_speed where to store corresponding adapter-specific speed value.
  * @return on success: ERROR_OK
  * @return on failure: ERROR_FAIL
  */
 static int ulink_khz(int khz, int *jtag_speed)
 {
+  int ret;
+
   if (khz == 0) {
     LOG_ERROR("RCLK not supported");
     return ERROR_FAIL;
   }
 
-  LOG_INFO("ulink_khz: %i kHz", khz);
+  /* CLOCK_TCK commands are decoupled from others. Therefore, the frequency
+   * setting can be done independently from all other commands. */
+  if (khz >= 375) {
+    ulink_handle->delay_clock_tck = -1;
+  }
+  else {
+    ret = ulink_calculate_delay(DELAY_CLOCK_TCK, khz * 1000,
+        &ulink_handle->delay_clock_tck);
+    if (ret != ERROR_OK) {
+      return ret;
+    }
+  }
 
-  /* ULINK maximum TCK frequency is ~ 150 kHz */
-  if (khz > 150) {
-    return ERROR_FAIL;
+  /* SCAN_{IN,OUT,IO} commands invoke CLOCK_TMS commands. Therefore, if the
+   * requested frequency goes below the maximum frequency for SLOW_CLOCK_TMS
+   * commands, all SCAN commands MUST also use the variable frequency
+   * implementation! */
+  if (khz >= 176) {
+    ulink_handle->delay_clock_tms = -1;
+    ulink_handle->delay_scan_in = -1;
+    ulink_handle->delay_scan_out = -1;
+    ulink_handle->delay_scan_io = -1;
+  }
+  else {
+    ret = ulink_calculate_delay(DELAY_CLOCK_TMS, khz * 1000,
+        &ulink_handle->delay_clock_tms);
+    if (ret != ERROR_OK) {
+      return ret;
+    }
+
+    ret = ulink_calculate_delay(DELAY_SCAN_IN, khz * 1000,
+        &ulink_handle->delay_scan_in);
+    if (ret != ERROR_OK) {
+      return ret;
+    }
+
+    ret = ulink_calculate_delay(DELAY_SCAN_OUT, khz * 1000,
+        &ulink_handle->delay_scan_out);
+    if (ret != ERROR_OK) {
+      return ret;
+    }
+
+    ret = ulink_calculate_delay(DELAY_SCAN_IO, khz * 1000,
+        &ulink_handle->delay_scan_io);
+    if (ret != ERROR_OK) {
+      return ret;
+    }
+  }
+
+#ifdef _DEBUG_JTAG_IO_
+  long f_tck, f_tms, f_scan_in, f_scan_out, f_scan_io;
+
+  ulink_calculate_frequency(DELAY_CLOCK_TCK, ulink_handle->delay_clock_tck,
+      &f_tck);
+  ulink_calculate_frequency(DELAY_CLOCK_TMS, ulink_handle->delay_clock_tms,
+      &f_tms);
+  ulink_calculate_frequency(DELAY_SCAN_IN, ulink_handle->delay_scan_in,
+      &f_scan_in);
+  ulink_calculate_frequency(DELAY_SCAN_OUT, ulink_handle->delay_scan_out,
+      &f_scan_out);
+  ulink_calculate_frequency(DELAY_SCAN_IO, ulink_handle->delay_scan_io,
+      &f_scan_io);
+
+  DEBUG_JTAG_IO("ULINK TCK setup: delay_tck      = %i (%li Hz),",
+      ulink_handle->delay_clock_tck, f_tck);
+  DEBUG_JTAG_IO("                 delay_tms      = %i (%li Hz),",
+      ulink_handle->delay_clock_tms, f_tms);
+  DEBUG_JTAG_IO("                 delay_scan_in  = %i (%li Hz),",
+      ulink_handle->delay_scan_in, f_scan_in);
+  DEBUG_JTAG_IO("                 delay_scan_out = %i (%li Hz),",
+      ulink_handle->delay_scan_out, f_scan_out);
+  DEBUG_JTAG_IO("                 delay_scan_io  = %i (%li Hz),",
+      ulink_handle->delay_scan_io, f_scan_io);
+#endif
+
+  /* Configure the ULINK device with the new delay values */
+  ret = ulink_append_configure_tck_cmd(ulink_handle,
+      ulink_handle->delay_scan_in,
+      ulink_handle->delay_scan_out,
+      ulink_handle->delay_scan_io,
+      ulink_handle->delay_clock_tck,
+      ulink_handle->delay_clock_tms);
+
+  if (ret != ERROR_OK) {
+    return ret;
   }
 
-  *jtag_speed = 0;
+  *jtag_speed = khz;
 
   return ERROR_OK;
 }
@@ -1725,30 +2156,38 @@ static int ulink_khz(int khz, int *jtag_speed)
 /**
  * Set the TCK frequency of the ULINK adapter.
  *
- * @param speed ???
+ * Because of the way the TCK frequency is set up in the OpenULINK firmware,
+ * there are five different speed settings. To simplify things, the
+ * adapter-specific speed setting value is identical to the TCK frequency in
+ * khz.
+ *
+ * @param speed desired adapter-specific speed value.
  * @return on success: ERROR_OK
  * @return on failure: ERROR_FAIL
  */
 static int ulink_speed(int speed)
 {
-  return ERROR_OK;
+  int dummy;
+
+  return ulink_khz(speed, &dummy);
 }
 
 /**
+ * Convert adapter-specific speed value to corresponding TCK frequency in kHz.
+ *
+ * Because of the way the TCK frequency is set up in the OpenULINK firmware,
+ * there are five different speed settings. To simplify things, the
+ * adapter-specific speed setting value is identical to the TCK frequency in
+ * khz.
  *
+ * @param speed adapter-specific speed value.
+ * @param khz where to store corresponding TCK frequency in kHz.
+ * @return on success: ERROR_OK
+ * @return on failure: ERROR_FAIL
  */
 static int ulink_speed_div(int speed, int *khz)
 {
-  LOG_INFO("ulink_speed_div: %i", speed);
-
-  switch (speed) {
-  case 0:
-    *khz = 150;
-    break;
-  case 1:
-    *khz = 100;
-    break;
-  }
+  *khz = speed;
 
   return ERROR_OK;
 }
@@ -1874,10 +2313,44 @@ static int ulink_quit(void)
   return ret;
 }
 
+/**
+ * Set a custom path to ULINK firmware image and force downloading to ULINK.
+ */
+COMMAND_HANDLER(ulink_download_firmware_handler)
+{
+  int ret;
+
+  if (CMD_ARGC != 1) {
+    LOG_ERROR("Need exactly one argument to ulink_download_firmware");
+    return ERROR_FAIL;
+  }
+
+  LOG_INFO("Downloading ULINK firmware image %s", CMD_ARGV[0]);
+
+  /* Download firmware image in CMD_ARGV[0] */
+  ret = ulink_load_firmware_and_renumerate(&ulink_handle, (char *)CMD_ARGV[0],
+      ULINK_RENUMERATION_DELAY);
+
+  return ret;
+}
+
 /*************************** Command Registration **************************/
 
+static const struct command_registration ulink_command_handlers[] = {
+  {
+    .name = "ulink_download_firmware",
+    .handler = &ulink_download_firmware_handler,
+    .mode = COMMAND_EXEC,
+    .help = "download firmware image to ULINK device",
+    .usage = "path/to/ulink_firmware.hex",
+  },
+  COMMAND_REGISTRATION_DONE,
+};
+
 struct jtag_interface ulink_interface = {
   .name = "ulink",
+
+  .commands = ulink_command_handlers,
   .transports = jtag_only,
 
   .execute_queue = ulink_execute_queue,