+/****************** 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;
+}
+