coresight: etm4x: Configure tracers to emit timestamps
authorMathieu Poirier <mathieu.poirier@linaro.org>
Thu, 25 Apr 2019 19:52:54 +0000 (13:52 -0600)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 25 Apr 2019 20:00:16 +0000 (22:00 +0200)
Configure timestamps to be emitted at regular intervals in the trace
stream to temporally correlate instructions executed on different CPUs.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Tested-by: Leo Yan <leo.yan@linaro.org>
Tested-by: Robert Walker <robert.walker@arm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/hwtracing/coresight/coresight-etm4x.c

index d64192c298600000220ce637c75c2e8e8799a111..8bb0092c7ec2e7786a5fbed6b79f0c55319050e1 100644 (file)
@@ -204,6 +204,91 @@ static void etm4_enable_hw_smp_call(void *info)
        arg->rc = etm4_enable_hw(arg->drvdata);
 }
 
+/*
+ * The goal of function etm4_config_timestamp_event() is to configure a
+ * counter that will tell the tracer to emit a timestamp packet when it
+ * reaches zero.  This is done in order to get a more fine grained idea
+ * of when instructions are executed so that they can be correlated
+ * with execution on other CPUs.
+ *
+ * To do this the counter itself is configured to self reload and
+ * TRCRSCTLR1 (always true) used to get the counter to decrement.  From
+ * there a resource selector is configured with the counter and the
+ * timestamp control register to use the resource selector to trigger the
+ * event that will insert a timestamp packet in the stream.
+ */
+static int etm4_config_timestamp_event(struct etmv4_drvdata *drvdata)
+{
+       int ctridx, ret = -EINVAL;
+       int counter, rselector;
+       u32 val = 0;
+       struct etmv4_config *config = &drvdata->config;
+
+       /* No point in trying if we don't have at least one counter */
+       if (!drvdata->nr_cntr)
+               goto out;
+
+       /* Find a counter that hasn't been initialised */
+       for (ctridx = 0; ctridx < drvdata->nr_cntr; ctridx++)
+               if (config->cntr_val[ctridx] == 0)
+                       break;
+
+       /* All the counters have been configured already, bail out */
+       if (ctridx == drvdata->nr_cntr) {
+               pr_debug("%s: no available counter found\n", __func__);
+               ret = -ENOSPC;
+               goto out;
+       }
+
+       /*
+        * Searching for an available resource selector to use, starting at
+        * '2' since every implementation has at least 2 resource selector.
+        * ETMIDR4 gives the number of resource selector _pairs_,
+        * hence multiply by 2.
+        */
+       for (rselector = 2; rselector < drvdata->nr_resource * 2; rselector++)
+               if (!config->res_ctrl[rselector])
+                       break;
+
+       if (rselector == drvdata->nr_resource * 2) {
+               pr_debug("%s: no available resource selector found\n",
+                        __func__);
+               ret = -ENOSPC;
+               goto out;
+       }
+
+       /* Remember what counter we used */
+       counter = 1 << ctridx;
+
+       /*
+        * Initialise original and reload counter value to the smallest
+        * possible value in order to get as much precision as we can.
+        */
+       config->cntr_val[ctridx] = 1;
+       config->cntrldvr[ctridx] = 1;
+
+       /* Set the trace counter control register */
+       val =  0x1 << 16        |  /* Bit 16, reload counter automatically */
+              0x0 << 7         |  /* Select single resource selector */
+              0x1;                /* Resource selector 1, i.e always true */
+
+       config->cntr_ctrl[ctridx] = val;
+
+       val = 0x2 << 16         | /* Group 0b0010 - Counter and sequencers */
+             counter << 0;       /* Counter to use */
+
+       config->res_ctrl[rselector] = val;
+
+       val = 0x0 << 7          | /* Select single resource selector */
+             rselector;          /* Resource selector */
+
+       config->ts_ctrl = val;
+
+       ret = 0;
+out:
+       return ret;
+}
+
 static int etm4_parse_event_config(struct etmv4_drvdata *drvdata,
                                   struct perf_event *event)
 {
@@ -239,9 +324,24 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata,
                /* TRM: Must program this for cycacc to work */
                config->ccctlr = ETM_CYC_THRESHOLD_DEFAULT;
        }
-       if (attr->config & BIT(ETM_OPT_TS))
+       if (attr->config & BIT(ETM_OPT_TS)) {
+               /*
+                * Configure timestamps to be emitted at regular intervals in
+                * order to correlate instructions executed on different CPUs
+                * (CPU-wide trace scenarios).
+                */
+               ret = etm4_config_timestamp_event(drvdata);
+
+               /*
+                * No need to go further if timestamp intervals can't
+                * be configured.
+                */
+               if (ret)
+                       goto out;
+
                /* bit[11], Global timestamp tracing bit */
                config->cfg |= BIT(11);
+       }
 
        if (attr->config & BIT(ETM_OPT_CTXTID))
                /* bit[6], Context ID tracing bit */