adapter: run at default speed when clock speed not specified
[fw/openocd] / src / jtag / adapter.c
index 47a1d794b3bcfd65b86f1363de5b481e2126b620..519505dc32466fd68b538fc79b4e9dea79fddaab 100644 (file)
 struct adapter_driver *adapter_driver;
 const char * const jtag_only[] = { "jtag", NULL };
 
+enum adapter_clk_mode {
+       CLOCK_MODE_UNSELECTED = 0,
+       CLOCK_MODE_KHZ,
+       CLOCK_MODE_RCLK
+};
+
+#define DEFAULT_CLOCK_SPEED_KHZ                100U
+
 /**
  * Adapter configuration
  */
 static struct {
+       bool adapter_initialized;
        char *usb_location;
+       char *serial;
+       enum adapter_clk_mode clock_mode;
+       int speed_khz;
+       int rclk_fallback_speed_khz;
 } adapter_config;
 
+bool is_adapter_initialized(void)
+{
+       return adapter_config.adapter_initialized;
+}
+
+/**
+ * Do low-level setup like initializing registers, output signals,
+ * and clocking.
+ */
+int adapter_init(struct command_context *cmd_ctx)
+{
+       if (is_adapter_initialized())
+               return ERROR_OK;
+
+       if (!adapter_driver) {
+               /* nothing was previously specified by "adapter driver" command */
+               LOG_ERROR("Debug Adapter has to be specified, "
+                       "see \"adapter driver\" command");
+               return ERROR_JTAG_INVALID_INTERFACE;
+       }
+
+       int retval;
+
+       if (adapter_config.clock_mode == CLOCK_MODE_UNSELECTED) {
+               LOG_WARNING("An adapter speed is not selected in the init scripts."
+                       " OpenOCD will try to run the adapter at the low speed (%d kHz)",
+                       DEFAULT_CLOCK_SPEED_KHZ);
+               LOG_WARNING("To remove this warnings and achieve reasonable communication speed with the target,"
+                   " set \"adapter speed\" or \"jtag_rclk\" in the init scripts.");
+               retval = adapter_config_khz(DEFAULT_CLOCK_SPEED_KHZ);
+               if (retval != ERROR_OK)
+                       return ERROR_JTAG_INIT_FAILED;
+       }
+
+       retval = adapter_driver->init();
+       if (retval != ERROR_OK)
+               return retval;
+       adapter_config.adapter_initialized = true;
+
+       if (!adapter_driver->speed) {
+               LOG_INFO("This adapter doesn't support configurable speed");
+               return ERROR_OK;
+       }
+
+       int requested_khz = adapter_get_speed_khz();
+       int actual_khz = requested_khz;
+       int speed_var = 0;
+       retval = adapter_get_speed(&speed_var);
+       if (retval != ERROR_OK)
+               return retval;
+       retval = adapter_driver->speed(speed_var);
+       if (retval != ERROR_OK)
+               return retval;
+       retval = adapter_get_speed_readable(&actual_khz);
+       if (retval != ERROR_OK)
+               LOG_INFO("adapter-specific clock speed value %d", speed_var);
+       else if (actual_khz) {
+               /* Adaptive clocking -- JTAG-specific */
+               if ((adapter_config.clock_mode == CLOCK_MODE_RCLK)
+                               || ((adapter_config.clock_mode == CLOCK_MODE_KHZ) && !requested_khz)) {
+                       LOG_INFO("RCLK (adaptive clock speed) not supported - fallback to %d kHz"
+                       , actual_khz);
+               } else
+                       LOG_INFO("clock speed %d kHz", actual_khz);
+       } else
+               LOG_INFO("RCLK (adaptive clock speed)");
+
+       return ERROR_OK;
+}
+
+int adapter_quit(void)
+{
+       if (is_adapter_initialized() && adapter_driver->quit) {
+               /* close the JTAG interface */
+               int result = adapter_driver->quit();
+               if (result != ERROR_OK)
+                       LOG_ERROR("failed: %d", result);
+       }
+
+       free(adapter_config.serial);
+       free(adapter_config.usb_location);
+
+       struct jtag_tap *t = jtag_all_taps();
+       while (t) {
+               struct jtag_tap *n = t->next_tap;
+               jtag_tap_free(t);
+               t = n;
+       }
+
+       return ERROR_OK;
+}
+
+unsigned int adapter_get_speed_khz(void)
+{
+       return adapter_config.speed_khz;
+}
+
+static int adapter_khz_to_speed(unsigned int khz, int *speed)
+{
+       LOG_DEBUG("convert khz to adapter specific speed value");
+       adapter_config.speed_khz = khz;
+       if (!is_adapter_initialized())
+               return ERROR_OK;
+       LOG_DEBUG("have adapter set up");
+       if (!adapter_driver->khz) {
+               LOG_ERROR("Translation from khz to adapter speed not implemented");
+               return ERROR_FAIL;
+       }
+       int speed_div1;
+       int retval = adapter_driver->khz(adapter_get_speed_khz(), &speed_div1);
+       if (retval != ERROR_OK)
+               return retval;
+       *speed = speed_div1;
+       return ERROR_OK;
+}
+
+static int adapter_rclk_to_speed(unsigned int fallback_speed_khz, int *speed)
+{
+       int retval = adapter_khz_to_speed(0, speed);
+       if ((retval != ERROR_OK) && fallback_speed_khz) {
+               LOG_DEBUG("trying fallback speed...");
+               retval = adapter_khz_to_speed(fallback_speed_khz, speed);
+       }
+       return retval;
+}
+
+static int adapter_set_speed(int speed)
+{
+       /* this command can be called during CONFIG,
+        * in which case adapter isn't initialized */
+       return is_adapter_initialized() ? adapter_driver->speed(speed) : ERROR_OK;
+}
+
+int adapter_config_khz(unsigned int khz)
+{
+       LOG_DEBUG("handle adapter khz");
+       adapter_config.clock_mode = CLOCK_MODE_KHZ;
+       int speed = 0;
+       int retval = adapter_khz_to_speed(khz, &speed);
+       return (retval != ERROR_OK) ? retval : adapter_set_speed(speed);
+}
+
+int adapter_config_rclk(unsigned int fallback_speed_khz)
+{
+       LOG_DEBUG("handle adapter rclk");
+       adapter_config.clock_mode = CLOCK_MODE_RCLK;
+       adapter_config.rclk_fallback_speed_khz = fallback_speed_khz;
+       int speed = 0;
+       int retval = adapter_rclk_to_speed(fallback_speed_khz, &speed);
+       return (retval != ERROR_OK) ? retval : adapter_set_speed(speed);
+}
+
+int adapter_get_speed(int *speed)
+{
+       switch (adapter_config.clock_mode) {
+               case CLOCK_MODE_KHZ:
+                       adapter_khz_to_speed(adapter_get_speed_khz(), speed);
+                       break;
+               case CLOCK_MODE_RCLK:
+                       adapter_rclk_to_speed(adapter_config.rclk_fallback_speed_khz, speed);
+                       break;
+               default:
+                       LOG_ERROR("BUG: unknown adapter clock mode");
+                       return ERROR_FAIL;
+       }
+       return ERROR_OK;
+}
+
+int adapter_get_speed_readable(int *khz)
+{
+       int speed_var = 0;
+       int retval = adapter_get_speed(&speed_var);
+       if (retval != ERROR_OK)
+               return retval;
+       if (!is_adapter_initialized())
+               return ERROR_OK;
+       if (!adapter_driver->speed_div) {
+               LOG_ERROR("Translation from adapter speed to khz not implemented");
+               return ERROR_FAIL;
+       }
+       return adapter_driver->speed_div(speed_var, khz);
+}
+
+const char *adapter_get_required_serial(void)
+{
+       return adapter_config.serial;
+}
+
 /*
  * 1 char: bus
  * 2 * 7 chars: max 7 ports
@@ -181,8 +382,7 @@ COMMAND_HANDLER(handle_adapter_driver_command)
                        continue;
 
                if (adapter_drivers[i]->commands) {
-                       retval = register_commands(CMD_CTX, NULL,
-                                       adapter_drivers[i]->commands);
+                       retval = register_commands(CMD_CTX, NULL, adapter_drivers[i]->commands);
                        if (retval != ERROR_OK)
                                return retval;
                }
@@ -456,13 +656,13 @@ COMMAND_HANDLER(handle_adapter_speed_command)
                unsigned khz = 0;
                COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], khz);
 
-               retval = jtag_config_khz(khz);
+               retval = adapter_config_khz(khz);
                if (retval != ERROR_OK)
                        return retval;
        }
 
-       int cur_speed = jtag_get_speed_khz();
-       retval = jtag_get_speed_readable(&cur_speed);
+       int cur_speed = adapter_get_speed_khz();
+       retval = adapter_get_speed_readable(&cur_speed);
        if (retval != ERROR_OK)
                return retval;
 
@@ -474,6 +674,16 @@ COMMAND_HANDLER(handle_adapter_speed_command)
        return retval;
 }
 
+COMMAND_HANDLER(handle_adapter_serial_command)
+{
+       if (CMD_ARGC != 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       free(adapter_config.serial);
+       adapter_config.serial = strdup(CMD_ARGV[0]);
+       return ERROR_OK;
+}
+
 COMMAND_HANDLER(handle_adapter_reset_de_assert)
 {
        enum values {
@@ -621,6 +831,13 @@ static const struct command_registration adapter_command_handlers[] = {
                        "With or without argument, display current setting.",
                .usage = "[khz]",
        },
+       {
+               .name = "serial",
+               .handler = handle_adapter_serial_command,
+               .mode = COMMAND_CONFIG,
+               .help = "Set the serial number of the adapter",
+               .usage = "serial_string",
+       },
        {
                .name = "list",
                .handler = handle_adapter_list_command,
@@ -702,7 +919,7 @@ static const struct command_registration interface_command_handlers[] = {
  * @todo Remove internal assumptions that all debug adapters use JTAG for
  * transport.  Various types and data structures are not named generically.
  */
-int interface_register_commands(struct command_context *ctx)
+int adapter_register_commands(struct command_context *ctx)
 {
        return register_commands(ctx, NULL, interface_command_handlers);
 }