arm_adi_v5: prevent possibly endless recursion in dap_dp_init()
[fw/openocd] / src / target / arm_adi_v5.c
index 11bf185496233a16324b469d9aa1a803b9b41fe8..59bb186c60b325803e2bd8400d3f2c3710b0f7b8 100644 (file)
@@ -51,7 +51,7 @@
  * is set in the DP_CTRL_STAT register, the SSTICKYORUN status is set and
  * further AP operations will fail.  There are two basic methods to avoid
  * such overrun errors.  One involves polling for status instead of using
- * transaction piplining.  The other involves adding delays to ensure the
+ * transaction pipelining.  The other involves adding delays to ensure the
  * AP has enough time to complete one operation before starting the next
  * one.  (For JTAG these delays are controlled by memaccess_tck.)
  */
@@ -652,35 +652,22 @@ int dap_dp_init(struct adiv5_dap *dap)
 
        LOG_DEBUG("%s", adiv5_dap_name(dap));
 
+       dap->do_reconnect = false;
        dap_invalidate_cache(dap);
 
        /*
         * Early initialize dap->dp_ctrl_stat.
-        * In jtag mode only, if the following atomic reads fail and set the
-        * sticky error, it will trigger the clearing of the sticky. Without this
-        * initialization system and debug power would be disabled while clearing
-        * the sticky error bit.
+        * In jtag mode only, if the following queue run (in dap_dp_poll_register)
+        * fails and sets the sticky error, it will trigger the clearing
+        * of the sticky. Without this initialization system and debug power
+        * would be disabled while clearing the sticky error bit.
         */
        dap->dp_ctrl_stat = CDBGPWRUPREQ | CSYSPWRUPREQ;
 
-       for (size_t i = 0; i < 30; i++) {
-               /* DP initialization */
-
-               retval = dap_dp_read_atomic(dap, DP_CTRL_STAT, NULL);
-               if (retval == ERROR_OK)
-                       break;
-       }
-
        /*
         * This write operation clears the sticky error bit in jtag mode only and
         * is ignored in swd mode. It also powers-up system and debug domains in
         * both jtag and swd modes, if not done before.
-        * Actually we do not need to clear the sticky error here because it has
-        * been already cleared (if it was set) in the previous atomic read. This
-        * write could be removed, but this initial part of dap_dp_init() is the
-        * result of years of fine tuning and there are strong concerns about any
-        * unnecessary code change. It doesn't harm, so let's keep it here and
-        * preserve the historical sequence of read/write operations!
         */
        retval = dap_queue_dp_write(dap, DP_CTRL_STAT, dap->dp_ctrl_stat | SSTICKYERR);
        if (retval != ERROR_OK)
@@ -731,6 +718,35 @@ int dap_dp_init(struct adiv5_dap *dap)
        return retval;
 }
 
+/**
+ * Initialize a DAP or do reconnect if DAP is not accessible.
+ *
+ * @param dap The DAP being initialized.
+ */
+int dap_dp_init_or_reconnect(struct adiv5_dap *dap)
+{
+       LOG_DEBUG("%s", adiv5_dap_name(dap));
+
+       /*
+        * Early initialize dap->dp_ctrl_stat.
+        * In jtag mode only, if the following atomic reads fail and set the
+        * sticky error, it will trigger the clearing of the sticky. Without this
+        * initialization system and debug power would be disabled while clearing
+        * the sticky error bit.
+        */
+       dap->dp_ctrl_stat = CDBGPWRUPREQ | CSYSPWRUPREQ;
+
+       dap->do_reconnect = false;
+
+       dap_dp_read_atomic(dap, DP_CTRL_STAT, NULL);
+       if (dap->do_reconnect) {
+               /* dap connect calls dap_dp_init() after transport dependent initialization */
+               return dap->ops->connect(dap);
+       } else {
+               return dap_dp_init(dap);
+       }
+}
+
 /**
  * Initialize a DAP.  This sets up the power domains, prepares the DP
  * for further use, and arranges to use AP #0 for all AP operations
@@ -872,7 +888,7 @@ int dap_find_ap(struct adiv5_dap *dap, enum ap_type type_to_find, struct adiv5_a
                 *  3-0  : AP Type (0=JTAG-AP 1=AHB-AP 2=APB-AP 4=AXI-AP)
                 */
 
-               /* Reading register for a non-existant AP should not cause an error,
+               /* Reading register for a non-existent AP should not cause an error,
                 * but just to be sure, try to continue searching if an error does happen.
                 */
                if ((retval == ERROR_OK) &&                  /* Register read success */
@@ -1479,15 +1495,118 @@ int dap_info_command(struct command_invocation *cmd,
 
 enum adiv5_cfg_param {
        CFG_DAP,
-       CFG_AP_NUM
+       CFG_AP_NUM,
+       CFG_BASEADDR,
+       CFG_CTIBASE, /* DEPRECATED */
 };
 
 static const Jim_Nvp nvp_config_opts[] = {
-       { .name = "-dap",    .value = CFG_DAP },
-       { .name = "-ap-num", .value = CFG_AP_NUM },
+       { .name = "-dap",       .value = CFG_DAP },
+       { .name = "-ap-num",    .value = CFG_AP_NUM },
+       { .name = "-baseaddr",  .value = CFG_BASEADDR },
+       { .name = "-ctibase",   .value = CFG_CTIBASE }, /* DEPRECATED */
        { .name = NULL, .value = -1 }
 };
 
+static int adiv5_jim_spot_configure(Jim_GetOptInfo *goi,
+               struct adiv5_dap **dap_p, int *ap_num_p, uint32_t *base_p)
+{
+       if (!goi->argc)
+               return JIM_OK;
+
+       Jim_SetEmptyResult(goi->interp);
+
+       Jim_Nvp *n;
+       int e = Jim_Nvp_name2value_obj(goi->interp, nvp_config_opts,
+                               goi->argv[0], &n);
+       if (e != JIM_OK)
+               return JIM_CONTINUE;
+
+       /* base_p can be NULL, then '-baseaddr' option is treated as unknown */
+       if (!base_p && (n->value == CFG_BASEADDR || n->value == CFG_CTIBASE))
+               return JIM_CONTINUE;
+
+       e = Jim_GetOpt_Obj(goi, NULL);
+       if (e != JIM_OK)
+               return e;
+
+       switch (n->value) {
+       case CFG_DAP:
+               if (goi->isconfigure) {
+                       Jim_Obj *o_t;
+                       struct adiv5_dap *dap;
+                       e = Jim_GetOpt_Obj(goi, &o_t);
+                       if (e != JIM_OK)
+                               return e;
+                       dap = dap_instance_by_jim_obj(goi->interp, o_t);
+                       if (!dap) {
+                               Jim_SetResultString(goi->interp, "DAP name invalid!", -1);
+                               return JIM_ERR;
+                       }
+                       if (*dap_p && *dap_p != dap) {
+                               Jim_SetResultString(goi->interp,
+                                       "DAP assignment cannot be changed!", -1);
+                               return JIM_ERR;
+                       }
+                       *dap_p = dap;
+               } else {
+                       if (goi->argc)
+                               goto err_no_param;
+                       if (!*dap_p) {
+                               Jim_SetResultString(goi->interp, "DAP not configured", -1);
+                               return JIM_ERR;
+                       }
+                       Jim_SetResultString(goi->interp, adiv5_dap_name(*dap_p), -1);
+               }
+               break;
+
+       case CFG_AP_NUM:
+               if (goi->isconfigure) {
+                       jim_wide ap_num;
+                       e = Jim_GetOpt_Wide(goi, &ap_num);
+                       if (e != JIM_OK)
+                               return e;
+                       if (ap_num < 0 || ap_num > DP_APSEL_MAX) {
+                               Jim_SetResultString(goi->interp, "Invalid AP number!", -1);
+                               return JIM_ERR;
+                       }
+                       *ap_num_p = ap_num;
+               } else {
+                       if (goi->argc)
+                               goto err_no_param;
+                       if (*ap_num_p == DP_APSEL_INVALID) {
+                               Jim_SetResultString(goi->interp, "AP number not configured", -1);
+                               return JIM_ERR;
+                       }
+                       Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, *ap_num_p));
+               }
+               break;
+
+       case CFG_CTIBASE:
+               LOG_WARNING("DEPRECATED! use \'-baseaddr' not \'-ctibase\'");
+               /* fall through */
+       case CFG_BASEADDR:
+               if (goi->isconfigure) {
+                       jim_wide base;
+                       e = Jim_GetOpt_Wide(goi, &base);
+                       if (e != JIM_OK)
+                               return e;
+                       *base_p = (uint32_t)base;
+               } else {
+                       if (goi->argc)
+                               goto err_no_param;
+                       Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, *base_p));
+               }
+               break;
+       };
+
+       return JIM_OK;
+
+err_no_param:
+       Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv, "NO PARAMS");
+       return JIM_ERR;
+}
+
 int adiv5_jim_configure(struct target *target, Jim_GetOptInfo *goi)
 {
        struct adiv5_private_config *pc;
@@ -1502,90 +1621,19 @@ int adiv5_jim_configure(struct target *target, Jim_GetOptInfo *goi)
 
        target->has_dap = true;
 
-       if (goi->argc > 0) {
-               Jim_Nvp *n;
-
-               Jim_SetEmptyResult(goi->interp);
-
-               /* check first if topmost item is for us */
-               e = Jim_Nvp_name2value_obj(goi->interp, nvp_config_opts,
-                                                                  goi->argv[0], &n);
-               if (e != JIM_OK)
-                       return JIM_CONTINUE;
-
-               e = Jim_GetOpt_Obj(goi, NULL);
-               if (e != JIM_OK)
-                       return e;
-
-               switch (n->value) {
-               case CFG_DAP:
-                       if (goi->isconfigure) {
-                               Jim_Obj *o_t;
-                               struct adiv5_dap *dap;
-                               e = Jim_GetOpt_Obj(goi, &o_t);
-                               if (e != JIM_OK)
-                                       return e;
-                               dap = dap_instance_by_jim_obj(goi->interp, o_t);
-                               if (dap == NULL) {
-                                       Jim_SetResultString(goi->interp, "DAP name invalid!", -1);
-                                       return JIM_ERR;
-                               }
-                               if (pc->dap != NULL && pc->dap != dap) {
-                                       Jim_SetResultString(goi->interp,
-                                               "DAP assignment cannot be changed after target was created!", -1);
-                                       return JIM_ERR;
-                               }
-                               if (target->tap_configured) {
-                                       Jim_SetResultString(goi->interp,
-                                               "-chain-position and -dap configparams are mutually exclusive!", -1);
-                                       return JIM_ERR;
-                               }
-                               pc->dap = dap;
-                               target->tap = dap->tap;
-                               target->dap_configured = true;
-                       } else {
-                               if (goi->argc != 0) {
-                                       Jim_WrongNumArgs(goi->interp,
-                                                                               goi->argc, goi->argv,
-                                       "NO PARAMS");
-                                       return JIM_ERR;
-                               }
-
-                               if (pc->dap == NULL) {
-                                       Jim_SetResultString(goi->interp, "DAP not configured", -1);
-                                       return JIM_ERR;
-                               }
-                               Jim_SetResultString(goi->interp, adiv5_dap_name(pc->dap), -1);
-                       }
-                       break;
+       e = adiv5_jim_spot_configure(goi, &pc->dap, &pc->ap_num, NULL);
+       if (e != JIM_OK)
+               return e;
 
-               case CFG_AP_NUM:
-                       if (goi->isconfigure) {
-                               jim_wide ap_num;
-                               e = Jim_GetOpt_Wide(goi, &ap_num);
-                               if (e != JIM_OK)
-                                       return e;
-                               if (ap_num < 0 || ap_num > DP_APSEL_MAX) {
-                                       Jim_SetResultString(goi->interp, "Invalid AP number!", -1);
-                                       return JIM_ERR;
-                               }
-                               pc->ap_num = ap_num;
-                       } else {
-                               if (goi->argc != 0) {
-                                       Jim_WrongNumArgs(goi->interp,
-                                                                        goi->argc, goi->argv,
-                                         "NO PARAMS");
-                                       return JIM_ERR;
-                               }
-
-                               if (pc->ap_num == DP_APSEL_INVALID) {
-                                       Jim_SetResultString(goi->interp, "AP number not configured", -1);
-                                       return JIM_ERR;
-                               }
-                               Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, pc->ap_num));
-                       }
-                       break;
+       if (pc->dap && !target->dap_configured) {
+               if (target->tap_configured) {
+                       pc->dap = NULL;
+                       Jim_SetResultString(goi->interp,
+                               "-chain-position and -dap configparams are mutually exclusive!", -1);
+                       return JIM_ERR;
                }
+               target->tap = pc->dap->tap;
+               target->dap_configured = true;
        }
 
        return JIM_OK;
@@ -1602,6 +1650,19 @@ int adiv5_verify_config(struct adiv5_private_config *pc)
        return ERROR_OK;
 }
 
+int adiv5_jim_mem_ap_spot_configure(struct adiv5_mem_ap_spot *cfg,
+               Jim_GetOptInfo *goi)
+{
+       return adiv5_jim_spot_configure(goi, &cfg->dap, &cfg->ap_num, &cfg->base);
+}
+
+int adiv5_mem_ap_spot_init(struct adiv5_mem_ap_spot *p)
+{
+       p->dap = NULL;
+       p->ap_num = DP_APSEL_INVALID;
+       p->base = 0;
+       return ERROR_OK;
+}
 
 COMMAND_HANDLER(handle_dap_info_command)
 {
@@ -1682,7 +1743,7 @@ COMMAND_HANDLER(dap_memaccess_command)
        }
        dap->ap[dap->apsel].memaccess_tck = memaccess_tck;
 
-       command_print(CMD, "memory bus access delay set to %" PRIi32 " tck",
+       command_print(CMD, "memory bus access delay set to %" PRIu32 " tck",
                        dap->ap[dap->apsel].memaccess_tck);
 
        return ERROR_OK;
@@ -1695,7 +1756,7 @@ COMMAND_HANDLER(dap_apsel_command)
 
        switch (CMD_ARGC) {
        case 0:
-               command_print(CMD, "%" PRIi32, dap->apsel);
+               command_print(CMD, "%" PRIu32, dap->apsel);
                return ERROR_OK;
        case 1:
                COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], apsel);
@@ -1721,7 +1782,7 @@ COMMAND_HANDLER(dap_apcsw_command)
 
        switch (CMD_ARGC) {
        case 0:
-               command_print(CMD, "ap %" PRIi32 " selected, csw 0x%8.8" PRIx32,
+               command_print(CMD, "ap %" PRIu32 " selected, csw 0x%8.8" PRIx32,
                        dap->apsel, apcsw);
                return ERROR_OK;
        case 1: