[PATCH v2 08/12] coresight-tmc-etf: Refactor enable function for CPU cluster ETF support

Yuanfang Zhang
2025-12-18T08:10:53Z

TMC-ETF devices associated with specific CPU clusters share the cluster's
power domain. Accessing their registers requires the cluster to be powered
on, which can only be guaranteed by running code on a CPU within that
cluster.

Refactor the enablement logic to support this requirement:
1. Split `tmc_etf_enable_hw` and `tmc_etb_enable_hw` into local and
SMP-aware variants:
- `*_local`: Performs the actual register access.
- `*_smp_call`: Wrapper for `smp_call_function_single`.
- The main entry point now detects if the device is CPU-bound and uses
`smp_call_function_single` to execute the local variant on an
appropriate CPU if necessary.

2. Adjust locking in `tmc_enable_etf_sink_sysfs` and `tmc_enable_etf_link`:
- Drop the spinlock before calling `tmc_etf_enable_hw`. This is
necessary because `smp_call_function_single` (used for cross-CPU
calls) may require interrupts enabled or might sleep/wait, which is
unsafe under a spinlock.
- Re-acquire the lock afterwards to update driver state.

Signed-off-by: Yuanfang Zhang <yuanfang.zhang@oss.qualcomm.com>
---
drivers/hwtracing/coresight/coresight-tmc-etf.c | 87 ++++++++++++++++++++++---
1 file changed, 77 insertions(+), 10 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
index 8882b1c4cdc05353fb2efd6a9ba862943048f0ff..11357788e9d93c53980e99e0ef78450e393f4059 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -47,7 +47,7 @@ static int __tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
return rc;
}

-static int tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
+static int tmc_etb_enable_hw_local(struct tmc_drvdata *drvdata)
{
int rc = coresight_claim_device(drvdata->csdev);

@@ -60,6 +60,36 @@ static int tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
return rc;
}

+struct tmc_smp_arg {
+ struct tmc_drvdata *drvdata;
+ int rc;
+};
+
+static void tmc_etb_enable_hw_smp_call(void *info)
+{
+ struct tmc_smp_arg *arg = info;
+
+ arg->rc = tmc_etb_enable_hw_local(arg->drvdata);
+}
+
+static int tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
+{
+ int cpu, ret;
+ struct tmc_smp_arg arg = { 0 };
+
+ if (!drvdata->supported_cpus)
+ return tmc_etb_enable_hw_local(drvdata);
+
+ arg.drvdata = drvdata;
+ for_each_cpu(cpu, drvdata->supported_cpus) {
+ ret = smp_call_function_single(cpu,
+ tmc_etb_enable_hw_smp_call, &arg, 1);
+ if (!ret)
+ return arg.rc;
+ }
+ return ret;
+}
+
static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata)
{
char *bufp;
@@ -130,7 +160,7 @@ static int __tmc_etf_enable_hw(struct tmc_drvdata *drvdata)
return rc;
}

-static int tmc_etf_enable_hw(struct tmc_drvdata *drvdata)
+static int tmc_etf_enable_hw_local(struct tmc_drvdata *drvdata)
{
int rc = coresight_claim_device(drvdata->csdev);

@@ -143,6 +173,32 @@ static int tmc_etf_enable_hw(struct tmc_drvdata *drvdata)
return rc;
}

+static void tmc_etf_enable_hw_smp_call(void *info)
+{
+ struct tmc_smp_arg *arg = info;
+
+ arg->rc = tmc_etf_enable_hw_local(arg->drvdata);
+}
+
+static int tmc_etf_enable_hw(struct tmc_drvdata *drvdata)
+{
+ int cpu, ret;
+ struct tmc_smp_arg arg = { 0 };
+
+ if (!drvdata->supported_cpus)
+ return tmc_etf_enable_hw_local(drvdata);
+
+ arg.drvdata = drvdata;
+
+ for_each_cpu(cpu, drvdata->supported_cpus) {
+ ret = smp_call_function_single(cpu,
+ tmc_etf_enable_hw_smp_call, &arg, 1);
+ if (!ret)
+ return arg.rc;
+ }
+ return ret;
+}
+
static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata)
{
struct coresight_device *csdev = drvdata->csdev;
@@ -228,7 +284,11 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev)
used = true;
drvdata->buf = buf;
}
+ raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
ret = tmc_etb_enable_hw(drvdata);
+
+ raw_spin_lock_irqsave(&drvdata->spinlock, flags);
if (!ret) {
coresight_set_mode(csdev, CS_MODE_SYSFS);
csdev->refcnt++;
@@ -291,7 +351,11 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev,
break;
}

- ret = tmc_etb_enable_hw(drvdata);
+ if (drvdata->supported_cpus &&
+ !cpumask_test_cpu(smp_processor_id(), drvdata->supported_cpus))
+ break;
+
+ ret = tmc_etb_enable_hw_local(drvdata);
if (!ret) {
/* Associate with monitored process. */
drvdata->pid = pid;
@@ -376,19 +440,22 @@ static int tmc_enable_etf_link(struct coresight_device *csdev,
return -EBUSY;
}

- if (csdev->refcnt == 0) {
+ if (csdev->refcnt == 0)
+ first_enable = true;
+
+ if (!first_enable)
+ csdev->refcnt++;
+
+ raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
+ if (first_enable) {
ret = tmc_etf_enable_hw(drvdata);
if (!ret) {
coresight_set_mode(csdev, CS_MODE_SYSFS);
- first_enable = true;
+ csdev->refcnt++;
+ dev_dbg(&csdev->dev, "TMC-ETF enabled\n");
}
}
- if (!ret)
- csdev->refcnt++;
- raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);

- if (first_enable)
- dev_dbg(&csdev->dev, "TMC-ETF enabled\n");
return ret;
}

--
2.34.1