From: Sumit Gupta <sumitg@nvidia.com>
To: <rafael@kernel.org>, <viresh.kumar@linaro.org>, <robh@kernel.org>,
<krzk+dt@kernel.org>, <conor+dt@kernel.org>, <treding@nvidia.com>,
<jonathanh@nvidia.com>, <linux-pm@vger.kernel.org>,
<linux-tegra@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
<devicetree@vger.kernel.org>
Cc: <bbasu@nvidia.com>, <sumitg@nvidia.com>
Subject: [PATCH 1/9] cpufreq: tegra194: move MPIDR read to CPU hotplug callback
Date: Wed, 4 Nov 2026 23:45:13 +0530 [thread overview]
Message-ID: <20261104181521.3003578-2-sumitg@nvidia.com> (raw)
In-Reply-To: <20261104181521.3003578-1-sumitg@nvidia.com>
At probe the driver reads the MPIDR of every possible CPU with
smp_call_function_single(). For a CPU that is offline at boot, the call
fails and the error is ignored, so the CPU's core and cluster IDs come
from an uninitialised variable.
Read MPIDR from a CPU-hotplug online callback instead, which runs on the
target CPU and caches its core and cluster IDs and MMIO scratch address.
This covers CPUs online at probe and CPUs onlined later.
The check that the SoC data covers all CPUs moves to probe, against
nr_cpu_ids. A CPU whose MPIDR does not fit the SoC data gets no scratch
address, and its NDIV access is skipped.
Fixes: 6b121b4cf7e1 ("cpufreq: tegra194: save CPU data to avoid repeated SMP calls")
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
drivers/cpufreq/tegra194-cpufreq.c | 114 ++++++++++++++++++++---------
1 file changed, 79 insertions(+), 35 deletions(-)
diff --git a/drivers/cpufreq/tegra194-cpufreq.c b/drivers/cpufreq/tegra194-cpufreq.c
index 1eb96e9f3310..be81ffd23edd 100644
--- a/drivers/cpufreq/tegra194-cpufreq.c
+++ b/drivers/cpufreq/tegra194-cpufreq.c
@@ -5,6 +5,7 @@
#include <linux/cpu.h>
#include <linux/cpufreq.h>
+#include <linux/cpuhotplug.h>
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -13,7 +14,7 @@
#include <linux/slab.h>
#include <linux/units.h>
-#include <asm/smp_plat.h>
+#include <asm/cputype.h>
#include <soc/tegra/bpmp.h>
#include <soc/tegra/bpmp-abi.h>
@@ -77,6 +78,8 @@ struct tegra194_cpufreq_data {
const struct tegra_cpufreq_soc *soc;
bool icc_dram_bw_scaling;
struct tegra_cpu_data *cpu_data;
+ enum cpuhp_state cpuhp_state;
+ struct hlist_node cpuhp_node;
};
static struct workqueue_struct *read_counters_wq;
@@ -113,7 +116,7 @@ static void tegra234_get_cpu_cluster_id(u32 cpu, u32 *cpuid, u32 *clusterid)
{
u64 mpidr;
- smp_call_function_single(cpu, tegra_get_cpu_mpidr, &mpidr, true);
+ tegra_get_cpu_mpidr(&mpidr);
if (cpuid)
*cpuid = MPIDR_AFFINITY_LEVEL(mpidr, 1);
@@ -121,11 +124,44 @@ static void tegra234_get_cpu_cluster_id(u32 cpu, u32 *cpuid, u32 *clusterid)
*clusterid = MPIDR_AFFINITY_LEVEL(mpidr, 2);
}
+static int tegra_cpufreq_cpu_online(unsigned int cpu, struct hlist_node *node)
+{
+ struct tegra194_cpufreq_data *data;
+ u32 cpuid, clusterid;
+
+ data = hlist_entry(node, struct tegra194_cpufreq_data, cpuhp_node);
+
+ data->soc->ops->get_cpu_cluster_id(cpu, &cpuid, &clusterid);
+
+ data->cpu_data[cpu].cpuid = cpuid;
+ data->cpu_data[cpu].clusterid = clusterid;
+
+ /* Derive per-core register address only when ccplex MMIO is mapped. */
+ if (data->regs) {
+ if (clusterid >= data->soc->num_clusters ||
+ cpuid >= data->soc->maxcpus_per_cluster) {
+ pr_warn_once("cpufreq: cpu%u: bad MPIDR (cluster %u, core %u)\n",
+ cpu, clusterid, cpuid);
+ } else {
+ u32 index = clusterid * data->soc->maxcpus_per_cluster + cpuid;
+
+ data->cpu_data[cpu].freq_core_reg =
+ SCRATCH_FREQ_CORE_REG(data, index);
+ }
+ }
+
+ return 0;
+}
+
static int tegra234_get_cpu_ndiv(u32 cpu, u32 cpuid, u32 clusterid, u64 *ndiv)
{
struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
+ void __iomem *reg = data->cpu_data[cpu].freq_core_reg;
+
+ if (!reg)
+ return -EINVAL;
- *ndiv = readl(data->cpu_data[cpu].freq_core_reg) & NDIV_MASK;
+ *ndiv = readl(reg) & NDIV_MASK;
return 0;
}
@@ -135,8 +171,12 @@ static void tegra234_set_cpu_ndiv(struct cpufreq_policy *policy, u64 ndiv)
struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
u32 cpu;
- for_each_cpu(cpu, policy->cpus)
- writel(ndiv, data->cpu_data[cpu].freq_core_reg);
+ for_each_cpu(cpu, policy->cpus) {
+ void __iomem *reg = data->cpu_data[cpu].freq_core_reg;
+
+ if (reg)
+ writel(ndiv, reg);
+ }
}
/*
@@ -208,7 +248,7 @@ static void tegra194_get_cpu_cluster_id(u32 cpu, u32 *cpuid, u32 *clusterid)
{
u64 mpidr;
- smp_call_function_single(cpu, tegra_get_cpu_mpidr, &mpidr, true);
+ tegra_get_cpu_mpidr(&mpidr);
if (cpuid)
*cpuid = MPIDR_AFFINITY_LEVEL(mpidr, 0);
@@ -682,28 +722,6 @@ tegra_cpufreq_bpmp_read_lut(struct platform_device *pdev, struct tegra_bpmp *bpm
return freq_table;
}
-static int tegra194_cpufreq_store_physids(unsigned int cpu, struct tegra194_cpufreq_data *data)
-{
- int num_cpus = data->soc->maxcpus_per_cluster * data->soc->num_clusters;
- u32 cpuid, clusterid;
- u64 mpidr_id;
-
- if (cpu > (num_cpus - 1)) {
- pr_err("cpufreq: wrong num of cpus or clusters in soc data\n");
- return -EINVAL;
- }
-
- data->soc->ops->get_cpu_cluster_id(cpu, &cpuid, &clusterid);
-
- mpidr_id = (clusterid * data->soc->maxcpus_per_cluster) + cpuid;
-
- data->cpu_data[cpu].cpuid = cpuid;
- data->cpu_data[cpu].clusterid = clusterid;
- data->cpu_data[cpu].freq_core_reg = SCRATCH_FREQ_CORE_REG(data, mpidr_id);
-
- return 0;
-}
-
static int tegra194_cpufreq_probe(struct platform_device *pdev)
{
const struct tegra_cpufreq_soc *soc;
@@ -711,7 +729,6 @@ static int tegra194_cpufreq_probe(struct platform_device *pdev)
struct tegra_bpmp *bpmp;
struct device *cpu_dev;
int err, i;
- u32 cpu;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
@@ -738,6 +755,12 @@ static int tegra194_cpufreq_probe(struct platform_device *pdev)
return PTR_ERR(data->regs);
}
+ if (nr_cpu_ids > data->soc->num_clusters *
+ data->soc->maxcpus_per_cluster) {
+ dev_err(&pdev->dev, "wrong num of cpus or clusters in soc data\n");
+ return -EINVAL;
+ }
+
data->cpu_data = devm_kcalloc(&pdev->dev, data->soc->num_clusters *
data->soc->maxcpus_per_cluster,
sizeof(*data->cpu_data), GFP_KERNEL);
@@ -766,11 +789,24 @@ static int tegra194_cpufreq_probe(struct platform_device *pdev)
}
}
- for_each_possible_cpu(cpu) {
- err = tegra194_cpufreq_store_physids(cpu, data);
- if (err)
- goto err_free_res;
- }
+ /*
+ * Install the hotplug state before cpufreq_register_driver(), so that
+ * cpu_data is filled in before cpufreq uses a CPU. This covers the
+ * CPUs online now and those onlined later.
+ *
+ * cpufreq_get_driver_data() returns NULL until the driver is
+ * registered. The _multi variant passes the callback its data
+ * through cpuhp_node instead.
+ */
+ err = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "cpufreq/tegra194:online",
+ tegra_cpufreq_cpu_online, NULL);
+ if (err < 0)
+ goto err_free_res;
+ data->cpuhp_state = err;
+
+ err = cpuhp_state_add_instance(data->cpuhp_state, &data->cpuhp_node);
+ if (err)
+ goto remove_cpuhp_state;
tegra194_cpufreq_driver.driver_data = data;
@@ -778,7 +814,7 @@ static int tegra194_cpufreq_probe(struct platform_device *pdev)
cpu_dev = get_cpu_device(0);
if (!cpu_dev) {
err = -EPROBE_DEFER;
- goto err_free_res;
+ goto remove_cpuhp_instance;
}
if (dev_pm_opp_of_get_opp_desc_node(cpu_dev)) {
@@ -791,6 +827,10 @@ static int tegra194_cpufreq_probe(struct platform_device *pdev)
if (!err)
goto put_bpmp;
+remove_cpuhp_instance:
+ cpuhp_state_remove_instance(data->cpuhp_state, &data->cpuhp_node);
+remove_cpuhp_state:
+ cpuhp_remove_multi_state(data->cpuhp_state);
err_free_res:
tegra194_cpufreq_free_resources();
put_bpmp:
@@ -800,7 +840,11 @@ static int tegra194_cpufreq_probe(struct platform_device *pdev)
static void tegra194_cpufreq_remove(struct platform_device *pdev)
{
+ struct tegra194_cpufreq_data *data = platform_get_drvdata(pdev);
+
cpufreq_unregister_driver(&tegra194_cpufreq_driver);
+ cpuhp_state_remove_instance(data->cpuhp_state, &data->cpuhp_node);
+ cpuhp_remove_multi_state(data->cpuhp_state);
tegra194_cpufreq_free_resources();
}
--
2.34.1
next prev parent reply other threads:[~2026-11-04 18:16 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-04 18:15 [PATCH 0/9] cpufreq: tegra194: add Tegra264 support Sumit Gupta
2026-11-04 18:15 ` Sumit Gupta [this message]
2026-11-04 18:15 ` [PATCH 2/9] cpufreq: tegra194: remove duplicate counter sampling loops Sumit Gupta
2026-11-04 18:15 ` [PATCH 3/9] cpufreq: tegra194: add tegra_counter_delta helper Sumit Gupta
2026-11-04 18:15 ` [PATCH 4/9] cpufreq: tegra194: add per-SoC counter mask Sumit Gupta
2026-11-04 18:15 ` [PATCH 5/9] cpufreq: tegra194: split MMIO mapping from ACTMON offset Sumit Gupta
2026-11-04 18:15 ` [PATCH 6/9] dt-bindings: arm: nvidia: Document the Tegra264 CCPLEX cluster Sumit Gupta
2026-11-04 18:15 ` [PATCH 7/9] cpufreq: tegra194: derive frequency domain from device tree Sumit Gupta
2026-11-04 18:15 ` [PATCH 8/9] cpufreq: tegra194: Add support for Tegra264 Sumit Gupta
2026-11-04 18:15 ` [PATCH 9/9] arm64: tegra: Add CCPLEX cluster node " Sumit Gupta
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=20261104181521.3003578-2-sumitg@nvidia.com \
--to=sumitg@nvidia.com \
--cc=bbasu@nvidia.com \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=jonathanh@nvidia.com \
--cc=krzk+dt@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=linux-tegra@vger.kernel.org \
--cc=rafael@kernel.org \
--cc=robh@kernel.org \
--cc=treding@nvidia.com \
--cc=viresh.kumar@linaro.org \
/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
all inboxes | Powered by JetHome®