Fishwaldo opened a new pull request, #19737:
URL: https://github.com/apache/nuttx/pull/19737

   **This is an RFC.** The code is complete and tested on hardware, but it adds 
a
   new subsystem with a public header, so I would rather have agreement on the
   design than merge it and discover the API is wrong. Specific questions are at
   the end. Happy to rework any of it.
   
   ## Summary
   
     * The tree has carried consumers of a cpufreq framework for two years
       without the framework. `drivers/thermal/thermal_cpufreq_cooling.c`
       includes `nuttx/cpufreq.h` and speaks of policies and QoS requests;
       `thermal_dummy.c` defines a whole lower half and calls `cpufreq_init()`.
       Both sit behind `THERMAL_CDEV_CPUFREQ` and `THERMAL_DUMMY_CPUFREQ`, whose
       dependency on `CPUFREQ` no configuration could ever satisfy.
     * So the API was not designed here. It is already fixed by that code, down
       to the order of the driver's operations and the detail that a policy can
       be cast to reach its driver. This supplies the missing half to that exact
       contract rather than inventing a new one.
     * **The shape.** One policy per system. A platform provides a lower half: 
an
       ascending frequency table and a way to move between its entries. Each
       requester (a thermal cooling device, an application holding
       `/dev/cpufreq`, a power manager) installs a `[min, max]` window. The
       resolved frequency is the highest table entry at or below the lowest
       ceiling. Speed is the default; any one requester can cap it; when windows
       do not intersect the lowest ceiling wins. The lower half only ever hears
       "go to table entry N".
     * With `CPUFREQ_CHARDEV` the policy is also `/dev/cpufreq`, with ioctls to
       read the current frequency, list the table, and install a request. Each
       open descriptor owns at most one request, released on close, including on
       task exit.
     * Suspend and resume pass through to the lower half. While suspended the
       resolver leaves the hardware alone and applies whatever changed on the 
way
       back.
     * Documentation is included, as
       `Documentation/components/drivers/special/cpufreq.rst`.
   
   ## Questions for reviewers
   
   1. **One policy per system.** Linux has one per CPU or per cluster. This has
      one, globally, because that is what the existing consumers assume
      (`cpufreq_policy_get()` takes no argument). Fine for now, or should the
      API carry a policy handle from the start? Adding one later is a break.
   2. **`min` is structurally inert.** The resolver picks the highest entry 
under
      the lowest ceiling, which is already the maximum permitted, so a floor is
      either already satisfied or can only be met by violating a ceiling. It is
      accepted and stored, never read. I left it because the existing thermal
      consumer passes it and because a future governor that selects below 
maximum
      would make it meaningful. Keep it, keep it but reject inverted windows 
with
      `-EINVAL`, or drop it from the API?
   3. **No governors.** The policy is "run at the highest frequency nobody has
      capped". No ondemand, no schedutil, no idle-driven scaling. Is that the
      right default for NuttX, or should there be a governor hook now?
   4. **Userspace surface.** `/dev/cpufreq` with four ioctls. thermal exposes
      `/proc/thermal` for inspection; cpufreq has no procfs equivalent, so there
      is no way to see the installed requests. Worth adding?
   5. **Frequency unit.** The lower half chooses it. kHz is the Linux convention
      and what this port uses, but nothing enforces it, and mixing units across
      consumers of one policy would be silently wrong. Fix it as kHz in the API?
   6. **`driver` must be the first member of `struct cpufreq_policy`**, because
      `thermal_cpufreq_cooling.c` casts a policy pointer to reach the lower 
half.
      That is an unenforced ABI constraint I have documented rather than fixed.
      Leave it, or add an accessor and change that caller?
   
   ## Impact
   
     * Is new feature added? Is existing feature changed? **NEW**, and it makes
       two existing but unbuildable features reachable.
     * Impact on user? **NO** unless enabled. `CONFIG_CPUFREQ` defaults off.
     * Impact on build? **NO** beyond the new directory when enabled.
     * Impact on hardware? **NO** directly; a platform opts in by providing a
       lower half.
     * Impact on documentation? **YES, provided.** New page under specialized
       drivers, plus Kconfig help.
     * Impact on security? **NO.** `/dev/cpufreq` grants no more than the 
ability
       to cap the CPU's speed, and a request dies with its descriptor.
     * Impact on compatibility? **NO.** Nothing that builds today changes.
     * Build-mode dependence? **NO.**
   
   ## Testing
   
     I confirm that changes are verified on local setup and works as intended:
   
     * Build Host: macOS 26.5.1, arm64 (Apple Silicon), xPack riscv-none-elf-gcc
       15.2.0
     * Target: **real hardware.** RISC-V, ESWIN EIC7700X EVB at 1.4 GHz
       (downstream board port, not yet upstream), kernel build, with a real
       lower half over the SoC's CPU PLL and a real die temperature sensor
     * Docs: `sphinx-build` clean, no warnings from the new page
   
     Both existing consumers were driven, which is the point of the exercise.
   
     First, `THERMAL_DUMMY` with its cpufreq half enabled. The dummy zone's
     simulated temperature drives the previously dead cooling device, which
     installs and updates QoS requests through this framework:
   
     ```
     ##### CMD 1: ls /dev/cpufreq
      /dev/cpufreq
     ##### CMD 2: cat /proc/thermal/cpu-thermal
     z:cpu-thermal t:73 t:1 h:16 l:0 c:fan0 s:3|3
     z:cpu-thermal t:73 t:1 h:3 l:3 c:cpufreq s:3|3
     z:cpu-thermal t:73 t:2 h:2 l:0 c:cpufreq s:3|2
     ```
   
     Then the real thing: the board's own die zone, throttling actual silicon as
     it heats and releasing as it cools. `c:cpufreq s:N` is the cooling state.
   
     ```
     [CPU0] cpu: measured 1400 MHz, clock tree says 1400 MHz
   
     z:die t:39 t:2 h:8 l:0 c:cpufreq s:0|(invalid)     <- 39C, unthrottled
     z:die t:40 t:2 h:8 l:0 c:cpufreq s:1|1             <- 40C, first step
     z:die t:40 t:2 h:8 l:0 c:cpufreq s:8|8             <- stepped to the cap
     z:die t:34 t:2 h:8 l:0 c:cpufreq s:0|0             <- cooled, released
     ```
   
     A userspace floor held through `/dev/cpufreq`, which also demonstrates
     question 2 above: the request is accepted and the frequency stays at the
     top entry, because that is where it already was.
   
     ```
     holding 1400 floor: 1400000 kHz
     holding 1400 floor: 1400000 kHz
     holding 1400 floor: 1400000 kHz
     ```
   
     `/proc/thermal` showing the cpufreq cooling device stepping its state is
     upstream's own two-year-old code running for the first time.
   
   ## PR verification Self-Check
   
     * [x] This PR introduces only one functional change.
     * [x] I have updated all required description fields above.
     * [x] My PR adheres to Contributing Guidelines and Documentation.
     * [ ] My PR is still work in progress (not ready for review).
     * [x] My PR is ready for review and can be safely merged into a codebase.
   
   ---
   
   *Claude (claude-opus-5) assisted with authoring this framework, its code
   comments, the documentation and this PR description. The commits carry
   `Assisted-by:` tags per 
[CONTRIBUTING.md](https://github.com/apache/nuttx/blob/master/CONTRIBUTING.md) 
ยง1.5.*
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to