Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 200 additions & 0 deletions Documentation/components/drivers/special/cpufreq.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
=====================
CPU Frequency Scaling
=====================

The CPU frequency framework lets several unrelated parts of the system have
an opinion about how fast the CPU should run, and resolves those opinions
into one frequency. A platform supplies a lower half: a table of the
frequencies its hardware supports and a way to move between them. Everything
above that is arbitration.

It is enabled with ``CONFIG_CPUFREQ``. There is one policy per system.

Design
======

Each requester installs a ``[min, max]`` window that it can live with. A
thermal cooling device installs one as a zone heats up, an application
holding ``/dev/cpufreq`` installs one, a power manager installs one. The
framework keeps every window and, whenever the set changes, recomputes a
single answer:

1. Take the lowest ``max`` across all installed requests. A request that
passes ``CPUFREQ_NO_LIMIT`` for a bound leaves that side unconstrained
and does not participate.
#. Choose the highest table entry at or below that ceiling.
#. Apply it through the lower half, if it differs from the entry currently
applied.

Speed is therefore the default: with no requests installed, the top table
entry is selected, and any single requester can pull the system down.

Two consequences of this are worth stating plainly, because they are design
decisions rather than oversights:

- **A floor never raises the frequency.** Because the resolver already picks
the highest permitted entry, any ``min`` that can be satisfied already is.
The ``min`` field is accepted and stored so that a request expresses a
complete window, but it does not participate in the calculation.
- **When windows do not intersect, the lowest ceiling wins.** A requester
asking for less speed is presumed to be protecting something, so its
ceiling is honoured and a conflicting floor is not.

The lower half is never told who asked for what. It only ever hears "go to
table entry N".

The Lower Half
==============

A platform provides a ``struct cpufreq_driver``. ``get_table`` and
``target_index`` are mandatory; the rest may be NULL:

.. code-block:: c

struct cpufreq_driver
{
CODE FAR const struct cpufreq_frequency_table *
(*get_table)(FAR struct cpufreq_policy *policy);
CODE int (*target_index)(FAR struct cpufreq_policy *policy,
unsigned int index);
CODE int (*get_frequency)(FAR struct cpufreq_policy *policy);
CODE int (*suspend)(FAR struct cpufreq_policy *policy);
CODE int (*resume)(FAR struct cpufreq_policy *policy);
};

``get_table``
Returns the frequency table. It must ascend, and it must end with an entry
whose ``frequency`` is ``CPUFREQ_TABLE_END``.

``target_index``
Moves the hardware to the table entry at ``index``. This is the only call
that changes the frequency.

``get_frequency``
Reports where the hardware actually is, in table units. Called once during
initialisation so the framework can start from the truth rather than an
assumption. If NULL, the framework assumes the lowest entry.

``suspend`` and ``resume``
Called from ``cpufreq_suspend()`` and ``cpufreq_resume()``.

The frequency unit is the lower half's choice. kHz is the Linux convention
and a reasonable default, but the framework does not care as long as every
consumer of the same policy agrees.

Bring the framework up once, after the hardware it drives is ready:

.. code-block:: c

static struct cpufreq_driver g_mychip_cpufreq =
{
.get_table = mychip_get_table,
.target_index = mychip_target_index,
.get_frequency = mychip_get_frequency,
};

cpufreq_init(&g_mychip_cpufreq);

``cpufreq_init()`` returns ``-EBUSY`` if called twice, and registers
``/dev/cpufreq`` when ``CONFIG_CPUFREQ_CHARDEV`` is set.

.. note::
``driver`` must remain the first member of ``struct cpufreq_policy``.
Existing consumers reach the lower half by casting a policy pointer.

In-kernel Requests
==================

Kernel code constrains the frequency through three calls:

.. code-block:: c

FAR struct cpufreq_qos *qos;

qos = cpufreq_qos_add_request(cpufreq_policy_get(),
CPUFREQ_NO_LIMIT, /* min */
800000); /* max */

cpufreq_qos_update_request(qos, CPUFREQ_NO_LIMIT, 1200000);

cpufreq_qos_remove_request(qos);

Each call re-resolves the frequency before returning.
``cpufreq_qos_remove_request()`` frees the request.

``cpufreq_policy_get()`` returns NULL before ``cpufreq_init()`` has run.

/dev/cpufreq
============

With ``CONFIG_CPUFREQ_CHARDEV`` the policy is also a character device, so an
application can read the frequency and install a request of its own. Each
open descriptor owns at most one request, which is released when the
descriptor closes, including on task exit.

``CPUFREQIOC_GET_FREQUENCY``
Arg: ``FAR unsigned int *``. Receives the current frequency.

``CPUFREQIOC_SET_REQUEST``
Arg: ``FAR const struct cpufreq_request_s *``. Installs or updates this
descriptor's request. Either bound may be ``CPUFREQ_NO_LIMIT``.

``CPUFREQIOC_CLEAR_REQUEST``
No argument. Removes this descriptor's request.

``CPUFREQIOC_GET_TABLE``
Arg: ``FAR struct cpufreq_table_query_s *``. Copies out the frequency
table. Set ``frequencies`` and ``maxlen``; ``nentries`` is returned as the
number the table really has, which may exceed ``maxlen``.

.. code-block:: c

struct cpufreq_request_s req =
{
.min = CPUFREQ_NO_LIMIT,
.max = 800000,
};

int fd = open("/dev/cpufreq", O_RDONLY);
ioctl(fd, CPUFREQIOC_SET_REQUEST, (unsigned long)&req);

/* The cap holds for as long as this descriptor is open */

close(fd);

Thermal Integration
===================

``CONFIG_THERMAL_CDEV_CPUFREQ`` registers CPU frequency as a thermal cooling
device, so a thermal zone can throttle the CPU with no board code in
between. The cooling device counts states downward from the top of the
frequency table: state 0 is unthrottled, and each step installs a tighter
window through the same QoS interface described above.

See :doc:`../thermal/index` for how zones, trip points and cooling devices
fit together.

Suspend and Resume
==================

.. code-block:: c

cpufreq_suspend();
cpufreq_resume();

These pass through to the lower half's ``suspend`` and ``resume``. While
suspended, the resolver leaves the hardware alone: requests are still
accepted and still recorded, and whatever they resolve to is applied on
resume.

Configuration
=============

``CONFIG_CPUFREQ``
Enables the framework.

``CONFIG_CPUFREQ_CHARDEV``
Registers ``/dev/cpufreq``. Default on.

``CONFIG_THERMAL_CDEV_CPUFREQ``
Registers the thermal cooling device. Requires ``CONFIG_THERMAL``.
1 change: 1 addition & 0 deletions Documentation/components/drivers/special/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ following section.

audio.rst
clk.rst
cpufreq.rst
devicetree.rst
devmem.rst
dma.rst
Expand Down
1 change: 1 addition & 0 deletions drivers/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ source "drivers/power/Kconfig"
source "drivers/regmap/Kconfig"
source "drivers/rpmsg/Kconfig"
source "drivers/rptun/Kconfig"
source "drivers/cpufreq/Kconfig"
source "drivers/sensors/Kconfig"
source "drivers/serial/Kconfig"
source "drivers/thermal/Kconfig"
Expand Down
1 change: 1 addition & 0 deletions drivers/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ include rpmsg/Make.defs
include rptun/Make.defs
include sensors/Make.defs
include serial/Make.defs
include cpufreq/Make.defs
include spi/Make.defs
include syslog/Make.defs
include thermal/Make.defs
Expand Down
25 changes: 25 additions & 0 deletions drivers/cpufreq/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# ##############################################################################
# drivers/cpufreq/CMakeLists.txt
#
# SPDX-License-Identifier: Apache-2.0
#
# Licensed to the Apache Software Foundation (ASF) under one or more contributor
# license agreements. See the NOTICE file distributed with this work for
# additional information regarding copyright ownership. The ASF licenses this
# file to you under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
#
# ##############################################################################

if(CONFIG_CPUFREQ)
target_sources(drivers PRIVATE cpufreq.c)

@xiaoxiang781216 xiaoxiang781216 Aug 7, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's better to integrate the well test and more functionality from:
https://github.com/open-vela/nuttx/tree/dev/drivers/devfreq
Instead rewrite from scratch by AI.
BTW, it the origin cpufreq framework work with thermal framework directly and extend to support any device frequency scaling.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Fishwaldo here is the pr: #19741

endif()
27 changes: 27 additions & 0 deletions drivers/cpufreq/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#
# For a description of the syntax of this configuration file,
# see the file kconfig-language.txt in the NuttX tools repository.
#

menuconfig CPUFREQ
bool "CPU frequency scaling"
default n
---help---
A single-policy CPU frequency framework. A platform provides a
lower half (a frequency table and a way to move between its
entries). Each requester (a thermal cooling device, an
application, a power manager) installs a [min, max] request,
and the resolved frequency is the highest table entry under
the lowest ceiling.

if CPUFREQ

config CPUFREQ_CHARDEV
bool "/dev/cpufreq character device"
default y
---help---
Expose the policy as /dev/cpufreq. Each open descriptor owns
at most one frequency request, installed by ioctl and released
on close, including on task exit.

endif # CPUFREQ
30 changes: 30 additions & 0 deletions drivers/cpufreq/Make.defs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
############################################################################
# drivers/cpufreq/Make.defs
#
# SPDX-License-Identifier: Apache-2.0
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
############################################################################

ifeq ($(CONFIG_CPUFREQ),y)

CSRCS += cpufreq.c

DEPPATH += --dep-path cpufreq
VPATH += cpufreq

endif
Loading
Loading