From: Yuanfang Zhang <yuanfang.zhang@oss.qualcomm.com>
To: Suzuki K Poulose <suzuki.poulose@arm.com>,
Mike Leach <mike.leach@linaro.org>,
James Clark <james.clark@linaro.org>,
Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Conor Dooley <conor+dt@kernel.org>,
Mathieu Poirier <mathieu.poirier@linaro.org>,
Leo Yan <leo.yan@linux.dev>,
Alexander Shishkin <alexander.shishkin@linux.intel.com>,
Bjorn Andersson <andersson@kernel.org>,
Konrad Dybcio <konradybcio@kernel.org>
Cc: kernel@oss.qualcomm.com, coresight@lists.linaro.org,
linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
Yuanfang Zhang <yuanfang.zhang@oss.qualcomm.com>
Subject: [PATCH 02/12] coresight-funnel: Add support for CPU cluster funnel
Date: Mon, 27 Oct 2025 23:28:04 -0700 [thread overview]
Message-ID: <20251027-cpu_cluster_component_pm-v1-2-31355ac588c2@oss.qualcomm.com> (raw)
In-Reply-To: <20251027-cpu_cluster_component_pm-v1-0-31355ac588c2@oss.qualcomm.com>
The CPU cluster funnel is a type of CoreSight funnel that resides inside
the CPU cluster's power domain. Unlike dynamic funnels, CPU funnels are
coupled with CPU clusters and share their power management characteristics.
When the CPU cluster enters low-power mode (LPM), the funnel's registers
become inaccessible. Moreover, runtime PM operations alone cannot bring
the CPU cluster out of LPM, making standard register access unreliable.
This patch enhances the existing CoreSight funnel platform driver to
support CPU cluster funnels by:
- Add cpumask to funnel_drvdata to store CPU cluster's mask for CPU
cluster funnel.
- Retrieving the associated CPU cluster's cpumask from the power domain.
- Using smp_call_function_single() to do clear self claim tag operation.
- Refactoring funnel_enable function. For cluster funnels, use
smp_call_function_single() to ensure register visibility.
- Encapsulating coresight registration in funnel_add_coresight_dev().
- Reusing the existing platform driver infrastructure to minimize
duplication and maintain compatibility with static funnel devices.
This ensures funnel operations are safe and functional even when the CPU
cluster is in low-power mode.
Signed-off-by: Yuanfang Zhang <yuanfang.zhang@oss.qualcomm.com>
---
drivers/hwtracing/coresight/coresight-funnel.c | 185 ++++++++++++++++++++-----
1 file changed, 154 insertions(+), 31 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c
index 3b248e54471a38f501777fe162fea850d1c851b3..12c29eb98b2122a3ade4cbed9a0d91c67ec777ed 100644
--- a/drivers/hwtracing/coresight/coresight-funnel.c
+++ b/drivers/hwtracing/coresight/coresight-funnel.c
@@ -15,6 +15,7 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/coresight.h>
#include <linux/amba/bus.h>
@@ -40,6 +41,7 @@ DEFINE_CORESIGHT_DEVLIST(funnel_devs, "funnel");
* @csdev: component vitals needed by the framework.
* @priority: port selection order.
* @spinlock: serialize enable/disable operations.
+ * @cpumask: CPU mask representing the CPUs related to this funnel.
*/
struct funnel_drvdata {
void __iomem *base;
@@ -48,6 +50,13 @@ struct funnel_drvdata {
struct coresight_device *csdev;
unsigned long priority;
raw_spinlock_t spinlock;
+ struct cpumask *cpumask;
+};
+
+struct funnel_smp_arg {
+ struct funnel_drvdata *drvdata;
+ int port;
+ int rc;
};
static int dynamic_funnel_enable_hw(struct funnel_drvdata *drvdata, int port)
@@ -76,6 +85,33 @@ static int dynamic_funnel_enable_hw(struct funnel_drvdata *drvdata, int port)
return rc;
}
+static void funnel_enable_hw_smp_call(void *info)
+{
+ struct funnel_smp_arg *arg = info;
+
+ arg->rc = dynamic_funnel_enable_hw(arg->drvdata, arg->port);
+}
+
+static int funnel_enable_hw(struct funnel_drvdata *drvdata, int port)
+{
+ int cpu, ret;
+ struct funnel_smp_arg arg = { 0 };
+
+ if (!drvdata->cpumask)
+ return dynamic_funnel_enable_hw(drvdata, port);
+
+ arg.drvdata = drvdata;
+ arg.port = port;
+
+ for_each_cpu(cpu, drvdata->cpumask) {
+ ret = smp_call_function_single(cpu,
+ funnel_enable_hw_smp_call, &arg, 1);
+ if (!ret)
+ return arg.rc;
+ }
+ return ret;
+}
+
static int funnel_enable(struct coresight_device *csdev,
struct coresight_connection *in,
struct coresight_connection *out)
@@ -86,19 +122,24 @@ static int funnel_enable(struct coresight_device *csdev,
bool first_enable = false;
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
- if (in->dest_refcnt == 0) {
- if (drvdata->base)
- rc = dynamic_funnel_enable_hw(drvdata, in->dest_port);
- if (!rc)
- first_enable = true;
- }
- if (!rc)
+
+ if (in->dest_refcnt == 0)
+ first_enable = true;
+ else
in->dest_refcnt++;
+
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
- if (first_enable)
- dev_dbg(&csdev->dev, "FUNNEL inport %d enabled\n",
- in->dest_port);
+ if (first_enable) {
+ if (drvdata->base)
+ rc = funnel_enable_hw(drvdata, in->dest_port);
+ if (!rc) {
+ in->dest_refcnt++;
+ dev_dbg(&csdev->dev, "FUNNEL inport %d enabled\n",
+ in->dest_port);
+ }
+ }
+
return rc;
}
@@ -188,15 +229,39 @@ static u32 get_funnel_ctrl_hw(struct funnel_drvdata *drvdata)
return functl;
}
+static void get_funnel_ctrl_smp_call(void *info)
+{
+ struct funnel_smp_arg *arg = info;
+
+ arg->rc = get_funnel_ctrl_hw(arg->drvdata);
+}
+
static ssize_t funnel_ctrl_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
u32 val;
+ int cpu, ret;
struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ struct funnel_smp_arg arg = { 0 };
pm_runtime_get_sync(dev->parent);
-
- val = get_funnel_ctrl_hw(drvdata);
+ if (!drvdata->cpumask) {
+ val = get_funnel_ctrl_hw(drvdata);
+ } else {
+ arg.drvdata = drvdata;
+ for_each_cpu(cpu, drvdata->cpumask) {
+ ret = smp_call_function_single(cpu,
+ get_funnel_ctrl_smp_call, &arg, 1);
+ if (!ret)
+ break;
+ }
+ if (!ret) {
+ val = arg.rc;
+ } else {
+ pm_runtime_put(dev->parent);
+ return ret;
+ }
+ }
pm_runtime_put(dev->parent);
@@ -211,22 +276,68 @@ static struct attribute *coresight_funnel_attrs[] = {
};
ATTRIBUTE_GROUPS(coresight_funnel);
+static void funnel_clear_self_claim_tag(struct funnel_drvdata *drvdata)
+{
+ struct csdev_access access = CSDEV_ACCESS_IOMEM(drvdata->base);
+
+ coresight_clear_self_claim_tag(&access);
+}
+
+static void funnel_init_on_cpu(void *info)
+{
+ struct funnel_drvdata *drvdata = info;
+
+ funnel_clear_self_claim_tag(drvdata);
+}
+
+static int funnel_add_coresight_dev(struct device *dev)
+{
+ struct coresight_desc desc = { 0 };
+ struct funnel_drvdata *drvdata = dev_get_drvdata(dev);
+
+ if (drvdata->base) {
+ desc.groups = coresight_funnel_groups;
+ desc.access = CSDEV_ACCESS_IOMEM(drvdata->base);
+ }
+
+ desc.name = coresight_alloc_device_name(&funnel_devs, dev);
+ if (!desc.name)
+ return -ENOMEM;
+
+ desc.type = CORESIGHT_DEV_TYPE_LINK;
+ desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
+ desc.ops = &funnel_cs_ops;
+ desc.pdata = dev->platform_data;
+ desc.dev = dev;
+
+ drvdata->csdev = coresight_register(&desc);
+ if (IS_ERR(drvdata->csdev))
+ return PTR_ERR(drvdata->csdev);
+ return 0;
+}
+
+static struct cpumask *funnel_get_cpumask(struct device *dev)
+{
+ struct generic_pm_domain *pd;
+
+ pd = pd_to_genpd(dev->pm_domain);
+ if (pd)
+ return pd->cpus;
+
+ return NULL;
+}
+
static int funnel_probe(struct device *dev, struct resource *res)
{
void __iomem *base;
struct coresight_platform_data *pdata = NULL;
struct funnel_drvdata *drvdata;
- struct coresight_desc desc = { 0 };
- int ret;
+ int cpu, ret;
if (is_of_node(dev_fwnode(dev)) &&
of_device_is_compatible(dev->of_node, "arm,coresight-funnel"))
dev_warn_once(dev, "Uses OBSOLETE CoreSight funnel binding\n");
- desc.name = coresight_alloc_device_name(&funnel_devs, dev);
- if (!desc.name)
- return -ENOMEM;
-
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
@@ -244,9 +355,6 @@ static int funnel_probe(struct device *dev, struct resource *res)
if (IS_ERR(base))
return PTR_ERR(base);
drvdata->base = base;
- desc.groups = coresight_funnel_groups;
- desc.access = CSDEV_ACCESS_IOMEM(base);
- coresight_clear_self_claim_tag(&desc.access);
}
dev_set_drvdata(dev, drvdata);
@@ -258,23 +366,37 @@ static int funnel_probe(struct device *dev, struct resource *res)
dev->platform_data = pdata;
raw_spin_lock_init(&drvdata->spinlock);
- desc.type = CORESIGHT_DEV_TYPE_LINK;
- desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
- desc.ops = &funnel_cs_ops;
- desc.pdata = pdata;
- desc.dev = dev;
- drvdata->csdev = coresight_register(&desc);
- if (IS_ERR(drvdata->csdev))
- return PTR_ERR(drvdata->csdev);
- return 0;
+ if (is_of_node(dev_fwnode(dev)) &&
+ of_device_is_compatible(dev->of_node, "arm,coresight-cpu-funnel")) {
+ drvdata->cpumask = funnel_get_cpumask(dev);
+ if (!drvdata->cpumask)
+ return -EINVAL;
+
+ cpus_read_lock();
+ for_each_cpu(cpu, drvdata->cpumask) {
+ ret = smp_call_function_single(cpu,
+ funnel_init_on_cpu, drvdata, 1);
+ if (!ret)
+ break;
+ }
+ cpus_read_unlock();
+
+ if (ret)
+ return 0;
+ } else if (res) {
+ funnel_clear_self_claim_tag(drvdata);
+ }
+
+ return funnel_add_coresight_dev(dev);
}
static int funnel_remove(struct device *dev)
{
struct funnel_drvdata *drvdata = dev_get_drvdata(dev);
- coresight_unregister(drvdata->csdev);
+ if (drvdata->csdev)
+ coresight_unregister(drvdata->csdev);
return 0;
}
@@ -341,6 +463,7 @@ static void funnel_platform_remove(struct platform_device *pdev)
static const struct of_device_id funnel_match[] = {
{.compatible = "arm,coresight-static-funnel"},
+ {.compatible = "arm,coresight-cpu-funnel"},
{}
};
--
2.34.1
next prev parent reply other threads:[~2025-10-28 6:28 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-28 6:28 [PATCH 00/12] coresight: Add CPU cluster funnel/replicator/tmc support Yuanfang Zhang
2025-10-28 6:28 ` [PATCH 01/12] dt-bindings: arm: coresight: Add cpu cluster tmc/funnel/replicator support Yuanfang Zhang
2025-10-28 9:09 ` Krzysztof Kozlowski
2025-10-29 9:39 ` Mike Leach
2025-10-28 6:28 ` Yuanfang Zhang [this message]
2025-10-28 6:28 ` [PATCH 03/12] coresight-funnel: Handle delay probe for CPU cluster funnel Yuanfang Zhang
2025-10-28 6:28 ` [PATCH 04/12] coresight-replicator: Add support for CPU cluster replicator Yuanfang Zhang
2025-10-28 6:28 ` [PATCH 05/12] coresight-replicator: Handle delayed probe " Yuanfang Zhang
2025-10-28 6:28 ` [PATCH 06/12] coresight-replicator: Update mgmt_attrs for CPU cluster replicator compatibility Yuanfang Zhang
2025-10-28 6:28 ` [PATCH 07/12] coresight-tmc: Add support for CPU cluster ETF and refactor probe flow Yuanfang Zhang
2025-10-28 6:28 ` [PATCH 08/12] coresight-tmc-etf: Refactor enable function for CPU cluster ETF support Yuanfang Zhang
2025-10-28 6:28 ` [PATCH 09/12] coresight-tmc: Update tmc_mgmt_attrs for CPU cluster TMC compatibility Yuanfang Zhang
2025-10-28 6:28 ` [PATCH 10/12] coresight-tmc: Handle delayed probe for CPU cluster TMC Yuanfang Zhang
2025-10-28 6:28 ` [PATCH 11/12] coresight: add 'cs_mode' to link enable functions Yuanfang Zhang
2025-10-28 6:28 ` [PATCH 12/12] arm64: dts: qcom: x1e80100: add Coresight nodes for APSS debug block Yuanfang Zhang
2025-10-28 9:28 ` Konrad Dybcio
2025-10-29 11:01 ` [PATCH 00/12] coresight: Add CPU cluster funnel/replicator/tmc support Mike Leach
2025-10-30 7:51 ` yuanfang zhang
2025-10-30 9:58 ` Mike Leach
2025-10-31 10:16 ` yuanfang zhang
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251027-cpu_cluster_component_pm-v1-2-31355ac588c2@oss.qualcomm.com \
--to=yuanfang.zhang@oss.qualcomm.com \
--cc=alexander.shishkin@linux.intel.com \
--cc=andersson@kernel.org \
--cc=conor+dt@kernel.org \
--cc=coresight@lists.linaro.org \
--cc=devicetree@vger.kernel.org \
--cc=james.clark@linaro.org \
--cc=kernel@oss.qualcomm.com \
--cc=konradybcio@kernel.org \
--cc=krzk+dt@kernel.org \
--cc=leo.yan@linux.dev \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mathieu.poirier@linaro.org \
--cc=mike.leach@linaro.org \
--cc=robh@kernel.org \
--cc=suzuki.poulose@arm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox