[PATCH 7/9] cpufreq: tegra194: derive frequency domain from device tree
From: Sumit Gupta
Date: Wed Nov 04 2026 - 13:54:32 EST
The driver currently creates one policy per cluster, so a frequency
domain that spans clusters cannot form a single policy.
Group CPUs by the generic "performance-domains" property instead.
of_perf_domain_get_sharing_cpumask() adds to the policy each CPU that
names the same domain, including offline ones. Without the property,
CPUs keep the cluster grouping, so current Tegra194/234/238 device
trees are unaffected.
A policy can now span clusters. It still uses one cluster's BPMP
frequency table. Clusters in a domain share a clock and report the same
NDIV limits, so that table fits the whole domain.
Store the domain index as clkid for every CPU in the policy. It can
then be used to select the domain's frequency register, whichever CPU
manages the policy.
Signed-off-by: Sumit Gupta <sumitg@xxxxxxxxxx>
---
drivers/cpufreq/tegra194-cpufreq.c | 41 ++++++++++++++++++++++++------
1 file changed, 33 insertions(+), 8 deletions(-)
diff --git a/drivers/cpufreq/tegra194-cpufreq.c b/drivers/cpufreq/tegra194-cpufreq.c
index 047166463efe..1328aa0a1bd1 100644
--- a/drivers/cpufreq/tegra194-cpufreq.c
+++ b/drivers/cpufreq/tegra194-cpufreq.c
@@ -42,6 +42,7 @@
struct tegra_cpu_data {
u32 cpuid;
u32 clusterid;
+ u32 clkid; /* performance-domain id */
void __iomem *freq_core_reg;
};
@@ -528,21 +529,45 @@ static int tegra194_cpufreq_init(struct cpufreq_policy *policy)
{
struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
int maxcpus_per_cluster = data->soc->maxcpus_per_cluster;
- u32 clusterid = data->cpu_data[policy->cpu].clusterid;
struct cpufreq_frequency_table *freq_table;
struct cpufreq_frequency_table *bpmp_lut;
- u32 start_cpu, cpu;
+ struct of_phandle_args args;
+ u32 start_cpu, cpu, clusterid;
int ret;
+ /* Covers all possible CPUs, so offline siblings are grouped too. */
+ ret = of_perf_domain_get_sharing_cpumask(policy->cpu, "performance-domains",
+ "#performance-domain-cells",
+ policy->cpus, &args);
+ if (!ret) {
+ if (args.args_count != 1) {
+ pr_err("cpufreq: cpu%u: invalid #performance-domain-cells\n",
+ policy->cpu);
+ of_node_put(args.np);
+ return -EINVAL;
+ }
+
+ for_each_cpu(cpu, policy->cpus)
+ data->cpu_data[cpu].clkid = args.args[0];
+
+ of_node_put(args.np);
+ } else if (ret == -ENOENT) {
+ /* Legacy grouping for DTs without performance-domains. */
+ start_cpu = rounddown(policy->cpu, maxcpus_per_cluster);
+ for (cpu = start_cpu; cpu < (start_cpu + maxcpus_per_cluster); cpu++)
+ if (cpu_possible(cpu))
+ cpumask_set_cpu(cpu, policy->cpus);
+ } else {
+ pr_err("cpufreq: cpu%u: failed to get performance domain: %d\n",
+ policy->cpu, ret);
+ return ret;
+ }
+
+ /* The BPMP LUT is indexed by the MPIDR cluster id, not clkid. */
+ clusterid = data->cpu_data[policy->cpu].clusterid;
if (clusterid >= data->soc->num_clusters || !data->bpmp_luts[clusterid])
return -EINVAL;
- start_cpu = rounddown(policy->cpu, maxcpus_per_cluster);
- /* set same policy for all cpus in a cluster */
- for (cpu = start_cpu; cpu < (start_cpu + maxcpus_per_cluster); cpu++) {
- if (cpu_possible(cpu))
- cpumask_set_cpu(cpu, policy->cpus);
- }
policy->cpuinfo.transition_latency = TEGRA_CPUFREQ_TRANSITION_LATENCY;
bpmp_lut = data->bpmp_luts[clusterid];
--
2.34.1