|
@@ -34,6 +34,7 @@
|
|
|
#include <linux/pm_runtime.h>
|
|
|
#include <linux/perf_event.h>
|
|
|
#include <asm/sections.h>
|
|
|
+#include <asm/local.h>
|
|
|
|
|
|
#include "coresight-etm4x.h"
|
|
|
|
|
@@ -76,7 +77,7 @@ static int etm4_trace_id(struct coresight_device *csdev)
|
|
|
unsigned long flags;
|
|
|
int trace_id = -1;
|
|
|
|
|
|
- if (!drvdata->enable)
|
|
|
+ if (!local_read(&drvdata->mode))
|
|
|
return drvdata->trcid;
|
|
|
|
|
|
spin_lock_irqsave(&drvdata->spinlock, flags);
|
|
@@ -188,8 +189,7 @@ static void etm4_enable_hw(void *info)
|
|
|
dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu);
|
|
|
}
|
|
|
|
|
|
-static int etm4_enable(struct coresight_device *csdev,
|
|
|
- struct perf_event_attr *attr, u32 mode)
|
|
|
+static int etm4_enable_sysfs(struct coresight_device *csdev)
|
|
|
{
|
|
|
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
|
|
int ret;
|
|
@@ -204,18 +204,46 @@ static int etm4_enable(struct coresight_device *csdev,
|
|
|
etm4_enable_hw, drvdata, 1);
|
|
|
if (ret)
|
|
|
goto err;
|
|
|
- drvdata->enable = true;
|
|
|
- drvdata->sticky_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);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static int etm4_enable(struct coresight_device *csdev,
|
|
|
+ struct perf_event_attr *attr, u32 mode)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+ u32 val;
|
|
|
+ struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
|
|
+
|
|
|
+ val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);
|
|
|
+
|
|
|
+ /* Someone is already using the tracer */
|
|
|
+ if (val)
|
|
|
+ return -EBUSY;
|
|
|
+
|
|
|
+ switch (mode) {
|
|
|
+ case CS_MODE_SYSFS:
|
|
|
+ ret = etm4_enable_sysfs(csdev);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ ret = -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* The tracer didn't start */
|
|
|
+ if (ret)
|
|
|
+ local_set(&drvdata->mode, CS_MODE_DISABLED);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static void etm4_disable_hw(void *info)
|
|
|
{
|
|
|
u32 control;
|
|
@@ -238,7 +266,7 @@ static void etm4_disable_hw(void *info)
|
|
|
dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu);
|
|
|
}
|
|
|
|
|
|
-static void etm4_disable(struct coresight_device *csdev)
|
|
|
+static void etm4_disable_sysfs(struct coresight_device *csdev)
|
|
|
{
|
|
|
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
|
|
|
|
@@ -256,7 +284,6 @@ static void etm4_disable(struct coresight_device *csdev)
|
|
|
* ensures that register writes occur when cpu is powered.
|
|
|
*/
|
|
|
smp_call_function_single(drvdata->cpu, etm4_disable_hw, drvdata, 1);
|
|
|
- drvdata->enable = false;
|
|
|
|
|
|
spin_unlock(&drvdata->spinlock);
|
|
|
put_online_cpus();
|
|
@@ -264,6 +291,30 @@ static void etm4_disable(struct coresight_device *csdev)
|
|
|
dev_info(drvdata->dev, "ETM tracing disabled\n");
|
|
|
}
|
|
|
|
|
|
+static void etm4_disable(struct coresight_device *csdev)
|
|
|
+{
|
|
|
+ u32 mode;
|
|
|
+ struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * For as long as the tracer isn't disabled another entity can't
|
|
|
+ * change its status. As such we can read the status here without
|
|
|
+ * fearing it will change under us.
|
|
|
+ */
|
|
|
+ mode = local_read(&drvdata->mode);
|
|
|
+
|
|
|
+ switch (mode) {
|
|
|
+ case CS_MODE_DISABLED:
|
|
|
+ break;
|
|
|
+ case CS_MODE_SYSFS:
|
|
|
+ etm4_disable_sysfs(csdev);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (mode)
|
|
|
+ local_set(&drvdata->mode, CS_MODE_DISABLED);
|
|
|
+}
|
|
|
+
|
|
|
static const struct coresight_ops_source etm4_source_ops = {
|
|
|
.cpu_id = etm4_cpu_id,
|
|
|
.trace_id = etm4_trace_id,
|
|
@@ -531,7 +582,7 @@ static int etm4_cpu_callback(struct notifier_block *nfb, unsigned long action,
|
|
|
etmdrvdata[cpu]->os_unlock = true;
|
|
|
}
|
|
|
|
|
|
- if (etmdrvdata[cpu]->enable)
|
|
|
+ if (local_read(&etmdrvdata[cpu]->mode))
|
|
|
etm4_enable_hw(etmdrvdata[cpu]);
|
|
|
spin_unlock(&etmdrvdata[cpu]->spinlock);
|
|
|
break;
|
|
@@ -544,7 +595,7 @@ static int etm4_cpu_callback(struct notifier_block *nfb, unsigned long action,
|
|
|
|
|
|
case CPU_DYING:
|
|
|
spin_lock(&etmdrvdata[cpu]->spinlock);
|
|
|
- if (etmdrvdata[cpu]->enable)
|
|
|
+ if (local_read(&etmdrvdata[cpu]->mode))
|
|
|
etm4_disable_hw(etmdrvdata[cpu]);
|
|
|
spin_unlock(&etmdrvdata[cpu]->spinlock);
|
|
|
break;
|