This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit e7ef45d39a9bae23f674390f4b162d38ae1012d0
Author: guanyi3 <[email protected]>
AuthorDate: Sat Aug 8 12:11:50 2026 +0800

    Documentation: add devfreq framework documentation
    
    Document the device frequency scaling framework: the QoS/governor 
arbitration model, the lower-half driver interface, built-in governors, 
in-kernel QoS requests, change notifications, procfs, and suspend/resume.
    
    Signed-off-by: guanyi3 <[email protected]>
---
 .../components/drivers/special/devfreq.rst         | 258 +++++++++++++++++++++
 Documentation/components/drivers/special/index.rst |   1 +
 2 files changed, 259 insertions(+)

diff --git a/Documentation/components/drivers/special/devfreq.rst 
b/Documentation/components/drivers/special/devfreq.rst
new file mode 100644
index 00000000000..85e2f142897
--- /dev/null
+++ b/Documentation/components/drivers/special/devfreq.rst
@@ -0,0 +1,258 @@
+========================
+Device Frequency Scaling
+========================
+
+The device frequency framework (devfreq) lets several unrelated parts of the
+system have an opinion about how fast a device 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. A governor
+decides, from moment to moment, where inside the arbitrated window the device
+should sit. Everything else is arbitration.
+
+Unlike the CPU frequency framework, which manages a single system-wide CPU
+policy, devfreq manages any number of independent devices, each registered by
+name. A GPU, a memory bus, and a DSP can each have their own devfreq instance,
+table and governor.
+
+It is enabled with ``CONFIG_DEVFREQ``.
+
+Design
+======
+
+A devfreq instance is created by a driver calling ``devfreq_register()`` with
+a name, a governor and a lower half. From then on two independent forces act
+on the frequency:
+
+- **QoS requests** narrow the allowed window. Each requester installs a
+  ``[min, max]`` window it can live with, and the framework aggregates every
+  window into a single ``[min, max]`` clamp on the device.
+- **The governor** picks a target inside that clamp. The ``performance``
+  governor always asks for the top of the window, ``powersave`` always asks
+  for the bottom, and ``ondemand`` moves between them according to load.
+
+Whenever the set of requests changes, the framework recomputes the aggregate
+window and lets the governor re-pick. The chosen frequency is then snapped to
+a real table entry and applied through the lower half.
+
+Frequencies are expressed in kHz throughout.
+
+Resolving Requests
+------------------
+
+Each QoS request carries a ``min`` and a ``max``. The aggregate window is the
+intersection of all of them: the highest ``min`` across every request, and
+the lowest ``max``. A floor is honoured here, so a requester that needs a
+device to run *at least* some speed can guarantee it, and a ceiling caps it.
+
+When the requests do not intersect (the aggregate ``min`` ends up above the
+aggregate ``max``) the driver's ``conflict_policy`` decides who wins:
+
+- ``DEVFREQ_CONFLICT_PREFER_HIGH`` clamps to the floor and chooses the higher
+  frequency. A device that would rather waste power than stall picks this.
+- ``DEVFREQ_CONFLICT_PREFER_LOW`` clamps to the ceiling and chooses the lower
+  frequency. A device protecting a thermal or power budget picks this.
+
+The resolved ``[min, max]`` is then snapped to the table: ``min`` rounds up to
+the lowest entry at or above it, ``max`` rounds down to the highest entry at
+or below it. The governor picks within that snapped range, and the lower half
+is told only "go to table entry N".
+
+The Lower Half
+==============
+
+A platform provides a ``struct devfreq_driver_s``. ``get_table`` and
+``target_index`` are mandatory; the rest may be NULL:
+
+.. code-block:: c
+
+  struct devfreq_driver_s
+  {
+    int conflict_policy;
+    CODE FAR const uint32_t *
+             (*get_table)(FAR struct devfreq_s *devfreq);
+    CODE int (*target_index)(FAR struct devfreq_s *devfreq,
+                             size_t index);
+    CODE uint32_t (*get_frequency)(FAR struct devfreq_s *devfreq);
+    CODE int (*suspend)(FAR struct devfreq_s *devfreq);
+    CODE int (*resume)(FAR struct devfreq_s *devfreq);
+  };
+
+``conflict_policy``
+  ``DEVFREQ_CONFLICT_PREFER_HIGH`` or ``DEVFREQ_CONFLICT_PREFER_LOW``, applied
+  when QoS windows do not intersect, as described above.
+
+``get_table``
+  Returns the frequency table, an array of ``uint32_t`` in kHz. It must
+  ascend, and it must end with an entry equal to ``DEVFREQ_ENTRY_END``. An
+  entry of ``DEVFREQ_ENTRY_INVALID`` is skipped, which lets a driver punch a
+  hole in an otherwise fixed table.
+
+``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 kHz. The framework consults it
+  rather than trusting a cached value, so an external change is noticed.
+
+``suspend`` and ``resume``
+  Called from ``devfreq_suspend()`` and ``devfreq_resume()``.
+
+Register the device once its hardware is ready:
+
+.. code-block:: c
+
+  static const struct devfreq_driver_s g_mydev_devfreq =
+  {
+    .conflict_policy = DEVFREQ_CONFLICT_PREFER_LOW,
+    .get_table       = mydev_get_table,
+    .target_index    = mydev_target_index,
+    .get_frequency   = mydev_get_frequency,
+  };
+
+  devfreq_register("gpu", devfreq_performance(),
+                   &g_mydev_devfreq, priv);
+
+``devfreq_register()`` returns a handle, or NULL on failure, including when a
+device of the same name is already registered. Pass the governor you want the
+device to start with; ``devfreq_performance()`` and ``devfreq_powersave()``
+return the two built-in governors, and the ondemand governor is available when
+``CONFIG_DEVFREQ_GOV_ONDEMAND`` is built in.
+
+.. code-block:: c
+
+  int devfreq_unregister(FAR struct devfreq_s *devfreq);
+
+``devfreq_unregister()`` stops the governor, tears the instance down and frees
+it.
+
+Governors
+=========
+
+A governor is a small ``struct devfreq_governor_s`` with lifecycle callbacks
+and a ``limit`` that returns the frequency the governor currently wants. The
+framework clamps that want to the QoS window before applying it.
+
+``performance``
+  Always wants the maximum of the window. Built in.
+
+``powersave``
+  Always wants the minimum of the window. Built in.
+
+``ondemand``
+  Samples CPU load periodically and scales between the window's bounds. When
+  load crosses ``CONFIG_DEVFREQ_LOAD_THRESHOLD`` it asks for the top;
+  otherwise it scales proportionally. The sampling interval defaults to
+  ``CONFIG_DEVFREQ_SAMPLE_RATE`` microseconds. Enabled with
+  ``CONFIG_DEVFREQ_GOV_ONDEMAND``.
+
+A driver may also supply its own governor to ``devfreq_register()`` instead of
+a built-in one.
+
+In-kernel Requests
+==================
+
+Kernel code constrains a device's frequency through three calls:
+
+.. code-block:: c
+
+  FAR struct qos_request_s *qos;
+
+  qos = devfreq_qos_add_request(devfreq,
+                                200000,    /* min kHz */
+                                800000);   /* max kHz */
+
+  devfreq_qos_update_request(devfreq, qos, 400000, 800000);
+
+  devfreq_qos_remove_request(devfreq, qos);
+
+Each call re-resolves the window and lets the governor re-pick before
+returning. ``devfreq_qos_remove_request()`` frees the request.
+
+The current frequency can be read at any time:
+
+.. code-block:: c
+
+  uint32_t khz = devfreq_get_frequency(devfreq);
+
+A device is looked up by name or by index when its handle is not already held:
+
+.. code-block:: c
+
+  FAR struct devfreq_s *devfreq = devfreq_find_by_name("gpu");
+
+Change Notifications
+====================
+
+Interested code can register a notifier block to hear about every frequency
+transition. The chain is called with ``DEVFREQ_PRECHANGE`` before the change
+and ``DEVFREQ_POSTCHANGE`` after, each carrying a ``struct devfreq_notifier_s``
+with the old and new frequencies. If the lower half's ``target_index`` fails,
+a compensating pair is sent so listeners always end on the hardware's true
+state.
+
+.. code-block:: c
+
+  devfreq_register_notifier(devfreq, &nb);
+  devfreq_unregister_notifier(devfreq, &nb);
+
+procfs
+======
+
+With ``CONFIG_DEVFREQ_PROCFS`` each registered device appears under
+``/proc/devfreq/<name>``. Reading it reports the device name, its current
+governor, the current frequency, whether it is suspended, and the frequency
+table:
+
+.. code-block:: text
+
+  nsh> cat /proc/devfreq/gpu
+   devfreq:     gpu
+   governor:    ondemand
+   cur_freq:    400000
+   suspended:   False
+   freq_table:  200000 400000 600000 800000
+
+Writing to the entry installs a frequency QoS constraint from user space, so
+an application can cap or floor a device without kernel code.
+
+With ``CONFIG_DEVFREQ_PROCFS_QOS`` the read also lists every outstanding QoS
+request as ``min, max`` pairs. When ``CONFIG_LIBC_BACKTRACE_DEPTH`` is greater
+than zero, each request is annotated with the call stack that installed it,
+which turns "who is holding this device down?" into a question with an answer.
+
+Suspend and Resume
+==================
+
+.. code-block:: c
+
+  devfreq_suspend(devfreq);
+  devfreq_resume(devfreq);
+
+These pass through to the lower half's ``suspend`` and ``resume`` and stop or
+restart the governor. While suspended the governor does not touch the
+hardware; requests are still accepted and recorded, and whatever they resolve
+to takes effect on resume.
+
+Configuration
+=============
+
+``CONFIG_DEVFREQ``
+  Enables the framework.
+
+``CONFIG_DEVFREQ_PROCFS``
+  Exposes each device under ``/proc/devfreq``. Requires ``CONFIG_FS_PROCFS``.
+
+``CONFIG_DEVFREQ_PROCFS_QOS``
+  Lists outstanding QoS requests, with call stacks when backtrace is
+  available, in the procfs output. Requires ``CONFIG_DEVFREQ_PROCFS``.
+
+``CONFIG_DEVFREQ_GOV_ONDEMAND``
+  Builds the ondemand governor. Requires CPU-load sampling
+  (``!CONFIG_SCHED_CPULOAD_NONE``).
+
+``CONFIG_DEVFREQ_SAMPLE_RATE``
+  The ondemand governor's sampling interval, in microseconds.
+
+``CONFIG_DEVFREQ_LOAD_THRESHOLD``
+  The load percentage at which ondemand jumps to the maximum frequency.
diff --git a/Documentation/components/drivers/special/index.rst 
b/Documentation/components/drivers/special/index.rst
index 1444d74eb9b..f3296845c8a 100644
--- a/Documentation/components/drivers/special/index.rst
+++ b/Documentation/components/drivers/special/index.rst
@@ -24,6 +24,7 @@ following section.
 
   audio.rst
   clk.rst
+  devfreq.rst
   devicetree.rst
   devmem.rst
   dma.rst

Reply via email to