[PATCH 1/3] cpufreq: Add a driver frequency resolution callback
Zhongqiu Han
zhongqiu.han at oss.qualcomm.com
Fri Oct 2 03:20:35 PDT 2026
On 9/29/2026 6:29 PM, Christian Loehle wrote:
> Without a frequency table, cpufreq treats the policy range as continuous
> even when the driver selects discrete performance levels. Governors can
> then issue different kHz requests for the same driver setting.
>
> Add ->resolve_freq() for table-less ->target() drivers to canonicalize
> requests within the supplied limits using CPUFREQ_RELATION_{L,H,C}.
> Document the callback and verification contracts so callers can safely
> cache resolved requests.
>
> Signed-off-by: Christian Loehle <christian.loehle at arm.com>
Reviewed-by: Zhongqiu Han <zhongqiu.han at oss.qualcomm.com>
> ---
> Documentation/admin-guide/pm/cpufreq.rst | 4 ++++
> Documentation/cpu-freq/cpu-drivers.rst | 19 +++++++++++++++++++
> drivers/cpufreq/cpufreq.c | 17 +++++++++++++++--
> include/linux/cpufreq.h | 10 ++++++++++
> 4 files changed, 48 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/admin-guide/pm/cpufreq.rst b/Documentation/admin-guide/pm/cpufreq.rst
> index 34baf20cc202..e634b87a62a8 100644
> --- a/Documentation/admin-guide/pm/cpufreq.rst
> +++ b/Documentation/admin-guide/pm/cpufreq.rst
> @@ -144,6 +144,10 @@ that belong to the same policy (including both online and offline CPUs). That
> mask is then used by the core to populate the policy pointers for all of the
> CPUs in it.
>
> +A table-less driver whose discrete frequencies are derived at runtime can
> +instead provide a ``->resolve_freq()`` callback to map arbitrary requests to
> +deterministic, supported frequencies.
> +
> The next major initialization step for a new policy object is to attach a
> scaling governor to it (to begin with, that is the default scaling governor
> determined by the kernel command line or configuration, but it may be changed
> diff --git a/Documentation/cpu-freq/cpu-drivers.rst b/Documentation/cpu-freq/cpu-drivers.rst
> index 17c69f83691e..4f327760bc04 100644
> --- a/Documentation/cpu-freq/cpu-drivers.rst
> +++ b/Documentation/cpu-freq/cpu-drivers.rst
> @@ -86,6 +86,9 @@ And optionally
> .set_boost - A pointer to a per-policy function to enable/disable boost
> frequencies.
>
> + .resolve_freq - A pointer to a frequency-resolution function for table-less
> + drivers with discrete, runtime-derived frequencies. See below.
> +
>
> 1.2 Per-CPU Initialization
> --------------------------
> @@ -170,6 +173,22 @@ limits on their own. These shall use the ->setpolicy() callback.
> 1.5. target/target_index
> ------------------------
>
> +Table-less ``->target()`` drivers may provide ``->resolve_freq()`` to map a
> +clamped target to a supported frequency within the supplied limits.
> +``CPUFREQ_RELATION_L`` selects the lowest frequency at or above the target,
> +H the highest at or below it, and C the closest, choosing higher on ties.
> +If no supported frequency is at or above the target, L returns the highest
> +supported frequency in the interval. If none is at or below the target,
> +H returns the lowest supported frequency in the interval.
> +``CPUFREQ_RELATION_E`` is stripped before the call.
> +
> +Equivalent requests must resolve to the same frequency, which the target
> +callbacks must map to one canonical driver request even if performance levels
> +share a kHz value. This lets callers cache resolved requests. ``->verify()``
> +must leave a supported frequency in every accepted limit interval.
> +The callback must not sleep: it may run in scheduler context.
> +Policies providing both a frequency table and this callback are rejected.
> +
> The target_index call has two arguments: ``struct cpufreq_policy *policy``,
> and ``unsigned int`` index (into the exposed frequency table).
>
> diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
> index 54dde8419bdc..44bda2f32fcf 100644
> --- a/drivers/cpufreq/cpufreq.c
> +++ b/drivers/cpufreq/cpufreq.c
> @@ -480,8 +480,12 @@ static unsigned int __resolve_freq(struct cpufreq_policy *policy,
>
> target_freq = clamp_val(target_freq, min, max);
>
> - if (!policy->freq_table)
> + if (!policy->freq_table) {
> + if (cpufreq_driver->resolve_freq)
> + return cpufreq_driver->resolve_freq(policy, target_freq, min, max,
> + relation & ~CPUFREQ_RELATION_E);
> return target_freq;
> + }
>
> idx = cpufreq_frequency_table_target(policy, target_freq, min, max, relation);
> policy->cached_resolved_idx = idx;
> @@ -495,7 +499,10 @@ static unsigned int __resolve_freq(struct cpufreq_policy *policy,
> * @policy: associated policy to interrogate
> * @target_freq: target frequency to resolve.
> *
> - * The target to driver frequency mapping is cached in the policy.
> + * The frequency-table resolution path caches the mapping in the policy.
> + *
> + * Keep the policy active and exclude driver teardown; a policy reference
> + * alone does not protect driver-private data.
> *
> * Return: Lowest driver-supported frequency greater than or equal to the
> * given target_freq, subject to policy (min/max) and driver limitations.
> @@ -1445,6 +1452,11 @@ static int cpufreq_policy_online(struct cpufreq_policy *policy,
> * If there is a problem with its frequency table, take it
> * offline and drop it.
> */
> + if (policy->freq_table && cpufreq_driver->resolve_freq) {
> + ret = -EINVAL;
> + goto out_offline_policy;
> + }
> +
> ret = cpufreq_table_validate_and_sort(policy);
> if (ret)
> goto out_offline_policy;
> @@ -2925,6 +2937,7 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)
>
> if (!driver_data || !driver_data->verify || !driver_data->init ||
> (driver_data->target_index && driver_data->target) ||
> + (driver_data->resolve_freq && !driver_data->target) ||
> (!!driver_data->setpolicy == (driver_data->target_index || driver_data->target)) ||
> (!driver_data->get_intermediate != !driver_data->target_intermediate) ||
> (!driver_data->online != !driver_data->offline) ||
> diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
> index d3d0d9d02aa4..a0a7619d11fd 100644
> --- a/include/linux/cpufreq.h
> +++ b/include/linux/cpufreq.h
> @@ -364,6 +364,16 @@ struct cpufreq_driver {
> int (*target)(struct cpufreq_policy *policy,
> unsigned int target_freq,
> unsigned int relation); /* Deprecated */
> + /*
> + * Optional for table-less ->target() drivers. Resolve a clamped request
> + * within the supplied limits using CPUFREQ_RELATION_{L,H,C}. Must not
> + * sleep. See Documentation/cpu-freq/cpu-drivers.rst for the contract.
> + */
> + unsigned int (*resolve_freq)(struct cpufreq_policy *policy,
> + unsigned int target_freq,
> + unsigned int min_freq,
> + unsigned int max_freq,
> + unsigned int relation);
> int (*target_index)(struct cpufreq_policy *policy,
> unsigned int index);
> unsigned int (*fast_switch)(struct cpufreq_policy *policy,
--
Thx and BRs,
Zhongqiu Han
More information about the linux-arm-kernel
mailing list