@@ -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;
}
@@ -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 {
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 <mathieu.poirier@linaro.org> --- drivers/hwtracing/coresight/coresight-etm3x.c | 167 +++++++++++++++++++++----- include/linux/coresight.h | 10 +- 2 files changed, 146 insertions(+), 31 deletions(-)