summaryrefslogtreecommitdiff
path: root/drivers/hwtracing/coresight/coresight-etm4x-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hwtracing/coresight/coresight-etm4x-core.c')
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x-core.c140
1 files changed, 95 insertions, 45 deletions
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
index 020f070bf17d..560975b70474 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
@@ -446,10 +446,24 @@ static int etm4_enable_trace_unit(struct etmv4_drvdata *drvdata)
etm4x_relaxed_write32(csa, TRCRSR_TA, TRCRSR);
etm4x_allow_trace(drvdata);
+
+ /*
+ * According to software usage PKLXF in Arm ARM (ARM DDI 0487 L.a),
+ * execute a Context synchronization event to guarantee the trace unit
+ * will observe the new values of the System registers.
+ */
+ if (!csa->io_mem)
+ isb();
+
/* Enable the trace unit */
etm4x_relaxed_write32(csa, 1, TRCPRGCTLR);
- /* Synchronize the register updates for sysreg access */
+ /*
+ * As recommended by section 4.3.7 ("Synchronization when using system
+ * instructions to progrom the trace unit") of ARM IHI 0064H.b, the
+ * self-hosted trace analyzer must perform a Context synchronization
+ * event between writing to the TRCPRGCTLR and reading the TRCSTATR.
+ */
if (!csa->io_mem)
isb();
@@ -461,10 +475,16 @@ static int etm4_enable_trace_unit(struct etmv4_drvdata *drvdata)
}
/*
- * As recommended by section 4.3.7 ("Synchronization when using the
- * memory-mapped interface") of ARM IHI 0064D
+ * As recommended in section 4.3.7 (Synchronization of register updates)
+ * of ARM IHI 0064H.b, the self-hosted trace analyzer always executes an
+ * ISB instruction after programming the trace unit registers.
+ *
+ * For the memory-mapped interface, the registers are mapped as Device
+ * type (Device-nGnRE). Reading back the value of any register in the
+ * trace unit ensures that all writes have completed. Therefore, polling
+ * on TRCSTATR guarantees that the writing TRCPRGCTLR is complete, and
+ * no explicit dsb() is required at here.
*/
- dsb(sy);
isb();
return 0;
@@ -589,13 +609,26 @@ done:
return rc;
}
-static void etm4_enable_hw_smp_call(void *info)
+static void etm4_enable_sysfs_smp_call(void *info)
{
struct etm4_enable_arg *arg = info;
+ struct coresight_device *csdev;
if (WARN_ON(!arg))
return;
+
+ csdev = arg->drvdata->csdev;
+ if (!coresight_take_mode(csdev, CS_MODE_SYSFS)) {
+ /* Someone is already using the tracer */
+ arg->rc = -EBUSY;
+ return;
+ }
+
arg->rc = etm4_enable_hw(arg->drvdata);
+
+ /* The tracer didn't start */
+ if (arg->rc)
+ coresight_set_mode(csdev, CS_MODE_DISABLED);
}
/*
@@ -808,13 +841,14 @@ static int etm4_enable_perf(struct coresight_device *csdev,
struct perf_event *event,
struct coresight_path *path)
{
- int ret = 0;
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+ int ret;
- if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) {
- ret = -EINVAL;
- goto out;
- }
+ if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id()))
+ return -EINVAL;
+
+ if (!coresight_take_mode(csdev, CS_MODE_PERF))
+ return -EBUSY;
/* Configure the tracer based on the session's specifics */
ret = etm4_parse_event_config(csdev, event);
@@ -830,6 +864,9 @@ static int etm4_enable_perf(struct coresight_device *csdev,
ret = etm4_enable_hw(drvdata);
out:
+ /* Failed to start tracer; roll back to DISABLED mode */
+ if (ret)
+ coresight_set_mode(csdev, CS_MODE_DISABLED);
return ret;
}
@@ -861,7 +898,7 @@ static int etm4_enable_sysfs(struct coresight_device *csdev, struct coresight_pa
*/
arg.drvdata = drvdata;
ret = smp_call_function_single(drvdata->cpu,
- etm4_enable_hw_smp_call, &arg, 1);
+ etm4_enable_sysfs_smp_call, &arg, 1);
if (!ret)
ret = arg.rc;
if (!ret)
@@ -882,11 +919,6 @@ static int etm4_enable(struct coresight_device *csdev, struct perf_event *event,
{
int ret;
- if (!coresight_take_mode(csdev, mode)) {
- /* Someone is already using the tracer */
- return -EBUSY;
- }
-
switch (mode) {
case CS_MODE_SYSFS:
ret = etm4_enable_sysfs(csdev, path);
@@ -898,10 +930,6 @@ static int etm4_enable(struct coresight_device *csdev, struct perf_event *event,
ret = -EINVAL;
}
- /* The tracer didn't start */
- if (ret)
- coresight_set_mode(csdev, CS_MODE_DISABLED);
-
return ret;
}
@@ -923,11 +951,16 @@ static void etm4_disable_trace_unit(struct etmv4_drvdata *drvdata)
*/
etm4x_prohibit_trace(drvdata);
/*
- * Make sure everything completes before disabling, as recommended
- * by section 7.3.77 ("TRCVICTLR, ViewInst Main Control Register,
- * SSTATUS") of ARM IHI 0064D
+ * Prevent being speculative at the point of disabling the trace unit,
+ * as recommended by section 7.3.77 ("TRCVICTLR, ViewInst Main Control
+ * Register, SSTATUS") of ARM IHI 0064D
*/
dsb(sy);
+ /*
+ * According to software usage VKHHY in Arm ARM (ARM DDI 0487 L.a),
+ * execute a Context synchronization event to guarantee no new
+ * program-flow trace is generated.
+ */
isb();
/* Trace synchronization barrier, is a nop if not supported */
tsb_csync();
@@ -947,16 +980,22 @@ static void etm4_disable_trace_unit(struct etmv4_drvdata *drvdata)
dev_err(etm_dev,
"timeout while waiting for PM stable Trace Status\n");
/*
- * As recommended by section 4.3.7 (Synchronization of register updates)
- * of ARM IHI 0064H.b.
+ * As recommended in section 4.3.7 (Synchronization of register updates)
+ * of ARM IHI 0064H.b, the self-hosted trace analyzer always executes an
+ * ISB instruction after programming the trace unit registers.
+ *
+ * For the memory-mapped interface, the registers are mapped as Device
+ * type (Device-nGnRE). Reading back the value of any register in the
+ * trace unit ensures that all writes have completed. Therefore, polling
+ * on TRCSTATR guarantees that the writing TRCPRGCTLR is complete, and
+ * no explicit dsb() is required at here.
*/
isb();
}
-static void etm4_disable_hw(void *info)
+static void etm4_disable_hw(struct etmv4_drvdata *drvdata)
{
u32 control;
- struct etmv4_drvdata *drvdata = info;
struct etmv4_config *config = &drvdata->config;
struct coresight_device *csdev = drvdata->csdev;
struct csdev_access *csa = &csdev->access;
@@ -993,6 +1032,15 @@ static void etm4_disable_hw(void *info)
"cpu: %d disable smp call done\n", drvdata->cpu);
}
+static void etm4_disable_sysfs_smp_call(void *info)
+{
+ struct etmv4_drvdata *drvdata = info;
+
+ etm4_disable_hw(drvdata);
+
+ coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED);
+}
+
static int etm4_disable_perf(struct coresight_device *csdev,
struct perf_event *event)
{
@@ -1022,6 +1070,8 @@ static int etm4_disable_perf(struct coresight_device *csdev,
/* TRCVICTLR::SSSTATUS, bit[9] */
filters->ssstatus = (control & BIT(9));
+ coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED);
+
/*
* perf will release trace ids when _free_aux() is
* called at the end of the session.
@@ -1047,7 +1097,8 @@ static void etm4_disable_sysfs(struct coresight_device *csdev)
* Executing etm4_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, etm4_disable_hw, drvdata, 1);
+ smp_call_function_single(drvdata->cpu, etm4_disable_sysfs_smp_call,
+ drvdata, 1);
raw_spin_unlock(&drvdata->spinlock);
@@ -1087,9 +1138,6 @@ static void etm4_disable(struct coresight_device *csdev,
etm4_disable_perf(csdev, event);
break;
}
-
- if (mode)
- coresight_set_mode(csdev, CS_MODE_DISABLED);
}
static int etm4_resume_perf(struct coresight_device *csdev)
@@ -1823,9 +1871,11 @@ static int __etm4_cpu_save(struct etmv4_drvdata *drvdata)
goto out;
}
+ if (!drvdata->paused)
+ etm4_disable_trace_unit(drvdata);
+
state = drvdata->save_state;
- state->trcprgctlr = etm4x_read32(csa, TRCPRGCTLR);
if (drvdata->nr_pe)
state->trcprocselr = etm4x_read32(csa, TRCPROCSELR);
state->trcconfigr = etm4x_read32(csa, TRCCONFIGR);
@@ -1908,7 +1958,7 @@ static int __etm4_cpu_save(struct etmv4_drvdata *drvdata)
state->trcpdcr = etm4x_read32(csa, TRCPDCR);
/* wait for TRCSTATR.IDLE to go up */
- if (etm4x_wait_status(csa, TRCSTATR_PMSTABLE_BIT, 1)) {
+ if (etm4x_wait_status(csa, TRCSTATR_IDLE_BIT, 1)) {
dev_err(etm_dev,
"timeout while waiting for Idle Trace Status\n");
etm4_os_unlock(drvdata);
@@ -1916,8 +1966,6 @@ static int __etm4_cpu_save(struct etmv4_drvdata *drvdata)
goto out;
}
- drvdata->state_needs_restore = true;
-
/*
* Power can be removed from the trace unit now. We do this to
* potentially save power on systems that respect the TRCPDCR_PU
@@ -1935,14 +1983,14 @@ static int etm4_cpu_save(struct etmv4_drvdata *drvdata)
{
int ret = 0;
- /* Save the TRFCR irrespective of whether the ETM is ON */
- if (drvdata->trfcr)
- drvdata->save_trfcr = read_trfcr();
+ if (pm_save_enable != PARAM_PM_SAVE_SELF_HOSTED)
+ return 0;
+
/*
* Save and restore the ETM Trace registers only if
* the ETM is active.
*/
- if (coresight_get_mode(drvdata->csdev) && drvdata->save_state)
+ if (coresight_get_mode(drvdata->csdev))
ret = __etm4_cpu_save(drvdata);
return ret;
}
@@ -1959,7 +2007,6 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata)
etm4_cs_unlock(drvdata, csa);
etm4x_relaxed_write32(csa, state->trcclaimset, TRCCLAIMSET);
- etm4x_relaxed_write32(csa, state->trcprgctlr, TRCPRGCTLR);
if (drvdata->nr_pe)
etm4x_relaxed_write32(csa, state->trcprocselr, TRCPROCSELR);
etm4x_relaxed_write32(csa, state->trcconfigr, TRCCONFIGR);
@@ -2033,8 +2080,6 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata)
if (!drvdata->skip_power_up)
etm4x_relaxed_write32(csa, state->trcpdcr, TRCPDCR);
- drvdata->state_needs_restore = false;
-
/*
* As recommended by section 4.3.7 ("Synchronization when using the
* memory-mapped interface") of ARM IHI 0064D
@@ -2044,14 +2089,19 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata)
/* Unlock the OS lock to re-enable trace and external debug access */
etm4_os_unlock(drvdata);
+
+ if (!drvdata->paused)
+ etm4_enable_trace_unit(drvdata);
+
etm4_cs_lock(drvdata, csa);
}
static void etm4_cpu_restore(struct etmv4_drvdata *drvdata)
{
- if (drvdata->trfcr)
- write_trfcr(drvdata->save_trfcr);
- if (drvdata->state_needs_restore)
+ if (pm_save_enable != PARAM_PM_SAVE_SELF_HOSTED)
+ return;
+
+ if (coresight_get_mode(drvdata->csdev))
__etm4_cpu_restore(drvdata);
}