[PATCH v2 09/12] coresight-tmc: Update management interface for CPU-bound TMCs

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

The current TMC management interface (sysfs attributes) assumes that
device registers can be accessed directly from any CPU. However, for
TMCs associated with specific CPU clusters, registers must be accessed
from a CPU within that cluster.

Replace the standard `coresight_simple_reg*` handlers with custom
accessors (`coresight_tmc_reg*`). These new handlers check if the TMC
is bound to a specific set of CPUs:
- If bound, they use `smp_call_function_single()` to read the register
on an appropriate CPU.
- If not bound (global TMC), they fall back to direct access.

This ensures correct register reads for per-cluster TMC devices while
maintaining backward compatibility for global TMCs.

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

diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c
index 0e1b5956398d3cefdd938a8a8404076eb4850b44..5b9f2e57c78f42f0f1460d8a8dcbac72b5f6085e 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-core.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-core.c
@@ -458,21 +458,130 @@ static enum tmc_mem_intf_width tmc_get_memwidth(u32 devid)
return memwidth;
}

+struct tmc_smp_arg {
+ struct tmc_drvdata *drvdata;
+ u32 offset;
+ int rc;
+};
+
+static void tmc_read_reg_smp_call(void *info)
+{
+ struct tmc_smp_arg *arg = info;
+
+ arg->rc = readl_relaxed(arg->drvdata->base + arg->offset);
+}
+
+static u32 cpu_tmc_read_reg(struct tmc_drvdata *drvdata, u32 offset)
+{
+ struct tmc_smp_arg arg = {
+ .drvdata = drvdata,
+ .offset = offset,
+ };
+ int cpu, ret = 0;
+
+ for_each_cpu(cpu, drvdata->supported_cpus) {
+ ret = smp_call_function_single(cpu,
+ tmc_read_reg_smp_call, &arg, 1);
+ if (!ret)
+ return arg.rc;
+ }
+
+ return ret;
+}
+
+static ssize_t coresight_tmc_reg32_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ struct cs_off_attribute *cs_attr = container_of(attr, struct cs_off_attribute, attr);
+ int ret;
+ u32 val;
+
+ ret = pm_runtime_resume_and_get(dev->parent);
+ if (ret < 0)
+ return ret;
+
+ if (!drvdata->supported_cpus)
+ val = readl_relaxed(drvdata->base + cs_attr->off);
+ else
+ val = cpu_tmc_read_reg(drvdata, cs_attr->off);
+
+ pm_runtime_put(dev->parent);
+
+ if (ret < 0)
+ return ret;
+ else
+ return sysfs_emit(buf, "0x%x\n", val);
+}
+
+static ssize_t coresight_tmc_reg64_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ struct cs_pair_attribute *cs_attr = container_of(attr, struct cs_pair_attribute, attr);
+ int ret;
+ u64 val;
+
+ ret = pm_runtime_resume_and_get(dev->parent);
+ if (ret < 0)
+ return ret;
+ if (!drvdata->supported_cpus) {
+ val = readl_relaxed(drvdata->base + cs_attr->lo_off) |
+ ((u64)readl_relaxed(drvdata->base + cs_attr->hi_off) << 32);
+ } else {
+ ret = cpu_tmc_read_reg(drvdata, cs_attr->lo_off);
+
+ if (ret < 0)
+ goto out;
+
+ val = ret;
+
+ ret = cpu_tmc_read_reg(drvdata, cs_attr->hi_off);
+ if (ret < 0)
+ goto out;
+
+ val |= ((u64)ret << 32);
+ }
+
+out:
+ pm_runtime_put_sync(dev->parent);
+ if (ret < 0)
+ return ret;
+ else
+ return sysfs_emit(buf, "0x%llx\n", val);
+}
+
+#define coresight_tmc_reg32(name, offset) \
+ (&((struct cs_off_attribute[]) { \
+ { \
+ __ATTR(name, 0444, coresight_tmc_reg32_show, NULL), \
+ offset \
+ } \
+ })[0].attr.attr)
+#define coresight_tmc_reg64(name, lo_off, hi_off) \
+ (&((struct cs_pair_attribute[]) { \
+ { \
+ __ATTR(name, 0444, coresight_tmc_reg64_show, NULL), \
+ lo_off, hi_off \
+ } \
+ })[0].attr.attr)
static struct attribute *coresight_tmc_mgmt_attrs[] = {
- coresight_simple_reg32(rsz, TMC_RSZ),
- coresight_simple_reg32(sts, TMC_STS),
- coresight_simple_reg64(rrp, TMC_RRP, TMC_RRPHI),
- coresight_simple_reg64(rwp, TMC_RWP, TMC_RWPHI),
- coresight_simple_reg32(trg, TMC_TRG),
- coresight_simple_reg32(ctl, TMC_CTL),
- coresight_simple_reg32(ffsr, TMC_FFSR),
- coresight_simple_reg32(ffcr, TMC_FFCR),
- coresight_simple_reg32(mode, TMC_MODE),
- coresight_simple_reg32(pscr, TMC_PSCR),
- coresight_simple_reg32(devid, CORESIGHT_DEVID),
- coresight_simple_reg64(dba, TMC_DBALO, TMC_DBAHI),
- coresight_simple_reg32(axictl, TMC_AXICTL),
- coresight_simple_reg32(authstatus, TMC_AUTHSTATUS),
+ coresight_tmc_reg32(rsz, TMC_RSZ),
+ coresight_tmc_reg32(sts, TMC_STS),
+ coresight_tmc_reg64(rrp, TMC_RRP, TMC_RRPHI),
+ coresight_tmc_reg64(rwp, TMC_RWP, TMC_RWPHI),
+ coresight_tmc_reg32(trg, TMC_TRG),
+ coresight_tmc_reg32(ctl, TMC_CTL),
+ coresight_tmc_reg32(ffsr, TMC_FFSR),
+ coresight_tmc_reg32(ffcr, TMC_FFCR),
+ coresight_tmc_reg32(mode, TMC_MODE),
+ coresight_tmc_reg32(pscr, TMC_PSCR),
+ coresight_tmc_reg32(devid, CORESIGHT_DEVID),
+ coresight_tmc_reg64(dba, TMC_DBALO, TMC_DBAHI),
+ coresight_tmc_reg32(axictl, TMC_AXICTL),
+ coresight_tmc_reg32(authstatus, TMC_AUTHSTATUS),
NULL,
};

--
2.34.1