When dealing with other kernel subsystems or automated tools it
is desirable to split the current etm_enable_hw() operation
in three: power up, configuration and enabling of the tracer.

That way it is possible to have more control on the operations
done by a tracer.

Signed-off-by: Mathieu Poirier <[email protected]>
---
 drivers/hwtracing/coresight/coresight-etm3x.c | 167 +++++++++++++++++++++-----
 include/linux/coresight.h                     |  10 +-
 2 files changed, 146 insertions(+), 31 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c 
b/drivers/hwtracing/coresight/coresight-etm3x.c
index d630b7ece735..999c62a59c70 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -247,11 +247,12 @@ static void etm_set_default(struct etm_drvdata *drvdata)
        drvdata->ctxid_mask = 0x0;
 }
 
-static void etm_enable_hw(void *info)
+static void etm_power_up_cpu(void *info)
 {
-       int i;
-       u32 etmcr;
-       struct etm_drvdata *drvdata = info;
+       struct coresight_device *csdev = info;
+       struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+       WARN_ON(drvdata->cpu != smp_processor_id());
 
        CS_UNLOCK(drvdata->base);
 
@@ -262,10 +263,65 @@ static void etm_enable_hw(void *info)
        /* Make sure all registers are accessible */
        etm_os_unlock(drvdata);
 
+       CS_LOCK(drvdata->base);
+}
+
+static int etm_power_up(struct coresight_device *csdev)
+{
+       struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+       /* tell the core we need this tracer */
+       pm_runtime_get_sync(csdev->dev.parent);
+
+       return smp_call_function_single(drvdata->cpu,
+                                       etm_power_up_cpu, csdev, 1);
+}
+
+static void etm_power_down_cpu(void *info)
+{
+       struct coresight_device *csdev = info;
+       struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+       WARN_ON(drvdata->cpu != smp_processor_id());
+
+       CS_UNLOCK(drvdata->base);
+       etm_clr_pwrup(drvdata);
+       etm_set_pwrdwn(drvdata);
+       CS_LOCK(drvdata->base);
+}
+
+static void etm_power_down(struct coresight_device *csdev)
+{
+       struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+       smp_call_function_single(drvdata->cpu,
+                                etm_power_down_cpu, csdev, 1);
+
+       /* tell the core this tracer is no longer needed */
+       pm_runtime_put(csdev->dev.parent);
+}
+
+/**
+ * etm_configure_cpu - configure ETM registers
+ * @csdev - the etm that needs to be configure.
+ *
+ * Applies a configuration set to the ETM registers _without_ enabling the
+ * tracer.  This function needs to be executed on the CPU who's tracer is
+ * being configured.
+ */
+static void etm_configure_cpu(void *info)
+{
+       int i;
+       u32 etmcr;
+       struct coresight_device *csdev = info;
+       struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+       WARN_ON(drvdata->cpu != smp_processor_id());
+
+       CS_UNLOCK(drvdata->base);
        etm_set_prog(drvdata);
 
        etmcr = etm_readl(drvdata, ETMCR);
-       etmcr &= (ETMCR_PWD_DWN | ETMCR_ETM_PRG);
        etmcr |= drvdata->port_size;
        etm_writel(drvdata, drvdata->ctrl | etmcr, ETMCR);
        etm_writel(drvdata, drvdata->trigger_event, ETMTRIGGER);
@@ -306,13 +362,71 @@ static void etm_enable_hw(void *info)
        /* No VMID comparator value selected */
        etm_writel(drvdata, 0x0, ETMVMIDCVR);
 
-       /* Ensures trace output is enabled from this ETM */
-       etm_writel(drvdata, drvdata->ctrl | ETMCR_ETM_EN | etmcr, ETMCR);
+       etm_clr_prog(drvdata);
+       CS_LOCK(drvdata->base);
+}
+
+static int etm_configure(struct coresight_device *csdev)
+{
+       struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+       return smp_call_function_single(drvdata->cpu,
+                                       etm_configure_cpu, csdev, 1);
+}
+
+/**
+ * etm_trace_enable - enable ETM tracer
+ * @csdev      - the etm that needs to be enabled/disabled.
+ * @enable     - whether to enable or disable the tracer.
+ *
+ * Only enables the tracer - register configuration should have been made
+ * prior to calling this function.  This should be executed on the CPU who's
+ * tracer is being enabled.
+ */
+static int etm_trace_enable(struct coresight_device *csdev, bool enable)
+{
+       u32 etmcr;
+       struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+       WARN_ON(drvdata->cpu != smp_processor_id());
+
+       /*
+        * It is assumed that etm_configure_cpu() has been called.  As such
+        * the ETM should be turned on, power applied to the trace registers
+        * and all registers accessible.
+        */
+       CS_UNLOCK(drvdata->base);
+       etm_set_prog(drvdata);
+
+       etmcr = etm_readl(drvdata, ETMCR);
+
+       enable ? (etmcr |= ETMCR_ETM_EN) :
+                (etmcr &= ~ETMCR_ETM_EN);
+
+       etm_writel(drvdata, ETMCR_ETM_EN | etmcr, ETMCR);
 
        etm_clr_prog(drvdata);
        CS_LOCK(drvdata->base);
 
+       return 0;
+}
+
+static void etm_config_enable(void *info)
+{
+       struct coresight_device *csdev = info;
+       struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+       spin_lock(&drvdata->spinlock);
+
+       etm_power_up_cpu(csdev);
+       etm_configure_cpu(csdev);
+       etm_trace_enable(csdev, true);
        dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu);
+
+       drvdata->enable = true;
+       drvdata->sticky_enable = true;
+
+       spin_unlock(&drvdata->spinlock);
 }
 
 static int etm_trace_id(struct coresight_device *csdev)
@@ -339,11 +453,8 @@ static int etm_trace_id(struct coresight_device *csdev)
 
 static int etm_enable(struct coresight_device *csdev)
 {
-       struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
        int ret;
-
-       pm_runtime_get_sync(csdev->dev.parent);
-       spin_lock(&drvdata->spinlock);
+       struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
        /*
         * Configure the ETM only if the CPU is online.  If it isn't online
@@ -352,34 +463,28 @@ static int etm_enable(struct coresight_device *csdev)
         */
        if (cpu_online(drvdata->cpu)) {
                ret = smp_call_function_single(drvdata->cpu,
-                                              etm_enable_hw, drvdata, 1);
+                                              etm_config_enable, csdev, 1);
                if (ret)
                        goto err;
        }
 
-       drvdata->enable = true;
-       drvdata->sticky_enable = true;
-
-       spin_unlock(&drvdata->spinlock);
-
        dev_info(drvdata->dev, "ETM tracing enabled\n");
        return 0;
 err:
-       spin_unlock(&drvdata->spinlock);
-       pm_runtime_put(csdev->dev.parent);
        return ret;
 }
 
-static void etm_disable_hw(void *info)
+static void etm_disable_powerdown(void *info)
 {
        int i;
        struct etm_drvdata *drvdata = info;
 
+       spin_lock(&drvdata->spinlock);
        CS_UNLOCK(drvdata->base);
        etm_set_prog(drvdata);
 
-       /* Program trace enable to low by using always false event */
-       etm_writel(drvdata, ETM_HARD_WIRE_RES_A | ETM_EVENT_NOT_A, ETMTEEVR);
+       etm_trace_enable(drvdata->csdev, false);
+       drvdata->enable = false;
 
        /* Read back sequencer and counters for post trace analysis */
        drvdata->seq_curr_state = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK);
@@ -387,8 +492,9 @@ static void etm_disable_hw(void *info)
        for (i = 0; i < drvdata->nr_cntr; i++)
                drvdata->cntr_val[i] = etm_readl(drvdata, ETMCNTVRn(i));
 
-       etm_set_pwrdwn(drvdata);
+       etm_power_down(drvdata->csdev);
        CS_LOCK(drvdata->base);
+       spin_unlock(&drvdata->spinlock);
 
        dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu);
 }
@@ -404,26 +510,27 @@ static void etm_disable(struct coresight_device *csdev)
         * DYING hotplug callback is serviced by the ETM driver.
         */
        get_online_cpus();
-       spin_lock(&drvdata->spinlock);
 
        /*
         * Executing etm_disable_hw on the cpu whose ETM is being disabled
         * ensures that register writes occur when cpu is powered.
         */
-       smp_call_function_single(drvdata->cpu, etm_disable_hw, drvdata, 1);
-       drvdata->enable = false;
+       smp_call_function_single(drvdata->cpu,
+                                etm_disable_powerdown, drvdata, 1);
 
-       spin_unlock(&drvdata->spinlock);
        put_online_cpus();
-       pm_runtime_put(csdev->dev.parent);
 
        dev_info(drvdata->dev, "ETM tracing disabled\n");
 }
 
 static const struct coresight_ops_source etm_source_ops = {
        .trace_id       = etm_trace_id,
+       .configure      = etm_configure,
+       .trace_enable   = etm_trace_enable,
        .enable         = etm_enable,
        .disable        = etm_disable,
+       .poweron        = etm_power_up,
+       .poweroff       = etm_power_down,
 };
 
 static const struct coresight_ops etm_cs_ops = {
@@ -1659,7 +1766,7 @@ static int etm_cpu_callback(struct notifier_block *nfb, 
unsigned long action,
                }
 
                if (etmdrvdata[cpu]->enable)
-                       etm_enable_hw(etmdrvdata[cpu]);
+                       etm_config_enable(etmdrvdata[cpu]->csdev);
                spin_unlock(&etmdrvdata[cpu]->spinlock);
                break;
 
@@ -1672,7 +1779,7 @@ static int etm_cpu_callback(struct notifier_block *nfb, 
unsigned long action,
        case CPU_DYING:
                spin_lock(&etmdrvdata[cpu]->spinlock);
                if (etmdrvdata[cpu]->enable)
-                       etm_disable_hw(etmdrvdata[cpu]);
+                       etm_disable_powerdown(etmdrvdata[cpu]->csdev);
                spin_unlock(&etmdrvdata[cpu]->spinlock);
                break;
        }
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index a7cabfa23b55..70f3dafa5194 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -206,14 +206,22 @@ struct coresight_ops_link {
  * struct coresight_ops_source - basic operations for a source
  * Operations available for sources.
  * @trace_id:  returns the value of the component's trace ID as known
-               to the HW.
+ *             to the HW.
+ * @configure: performs configuration for a source but doesn't enable it.
+ * @trace_enable: enable/disable tracing on a source.
  * @enable:    enables tracing for a source.
  * @disable:   disables tracing for a source.
+ * @poweron:   switch on power to a source.
+ * @poweroff:  switch off power to a source.
  */
 struct coresight_ops_source {
        int (*trace_id)(struct coresight_device *csdev);
+       int (*configure)(struct coresight_device *csdev);
+       int (*trace_enable)(struct coresight_device *csdev, bool enable);
        int (*enable)(struct coresight_device *csdev);
        void (*disable)(struct coresight_device *csdev);
+       int (*poweron)(struct coresight_device *csdev);
+       void (*poweroff)(struct coresight_device *csdev);
 };
 
 struct coresight_ops {
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to