Forum Discussion
Hi Arun,
In the arm64 config you can see the CPU_FREQ options which will help you implement governor functionality with frequency scaling: https://github.com/altera-fpga/linux-socfpga/blob/socfpga-6.12.33-lts/arch/arm64/configs/defconfig
Your kernel will look for operating points in your device tree to set available frequencies. Here is an idea of what the implementation could look like:
cpu0: cpu@0 {
...
operating-points-v2 = <&cpu_opp_table>;
...
};
...
cpu_opp_table: opp-table {
compatible = "operating-points-v2";
opp-1000000000 {
opp-hz = /bits/ 64 <1000000000>;
opp-microvolt = <950000>;
};
opp-1400000000 {
opp-hz = /bits/ 64 <1400000000>;
opp-microvolt = <1050000>;
};
};After reboot you can use the /sys/devices/system/cpu/cpu0/cpufreq utilities to check available governors, frequencies, and watch how the CPU responds to stress.
Hi AnnaK_Altera
Thanks for the guidance. We've enabled the relevant CONFIG_CPU_FREQ options in our arm64 kernel config and included #include "socfpga_agilex5.dtsi" in our device tree, which contains all the necessary CPU-related nodes including TSC and others.
However, after reboot, the /sys/devices/system/cpu/cpu0/cpufreq directory is still not appearing. We've also updated our kernel log file to help identify what might be missing. Could you please take a look and let us know if there's anything we've overlooked?
Thanks again for your support.
- JamesG_Altera2 months ago
New Contributor
Hi Arun_Prabakatr,
The source of all CPU clocks in an Agilex 5 system on chip is the Clock Manager. You can find some details about it here:
https://www.intel.com/content/www/us/en/docs/programmable/814346/25-3/clock-manager.htmlYou will find information about the Linux driver for the Clock Manager here:
https://altera-fpga.github.io/rel-25.3/linux-embedded/drivers/clock_manager/clock_manager/- JamesG_Altera1 month ago
New Contributor
Hi Arun_Prabakatr,
Here is some further information on this topic. The Linux driver for clock manager does not support modifying the clock frequency, only reading it. As described in the HPS Technical Reference Manual here...
https://www.intel.com/content/www/us/en/docs/programmable/814346/25-3/clock-manager-programming-model.html
... there are two supported ways of configuring clock frequencies for the HPS subsystem, including the CPU clocks:- Configure in the Parameters section of the HPS IP in Quartus Platform Designer before building the FPGA image
- Configure clock manager register settings in the bootloader, before starting Linux
Section 7.7 of the document goes on to give example register settings for different scenarios. Section 7.8 has details of the relevant registers: https://www.intel.com/content/www/us/en/docs/programmable/814346/25-3/clock-manager-address-map-and-register-49571.html
If you find this information helpful, please mark it as the accepted solution.
- TiensungA_Altera1 month ago
Occasional Contributor
JamesG_Altera is 100% correct. Please follow James's suggestion on the UBOOT SSBL to modify the register (ping-pong counter/divider) or Quartus. This is the recommended solution.
For the Linux part, I will just add a bit more information. We do not support setting the Clock rate through the clock manager driver in Linux yet. But customers are free to implement it if they want as this is open source. Hence, contributing to the open source community.
If you are keen to try, then, i created some basic steps on how to do this in Linux below.
Step 1: We need to define the Min, and max frequency or multiple operating frequencies for the CPU cores using the opp-table. I am just giving an example with some sample frequencies. We do not need to specify the operating voltage because the Agilex5 does not support it. We only support frequency scaling. As shown below, you have to add the DT bindings to the clock manager and define the device tree for opp-table. This is not the correct values. And it is only as a demostration.
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu0: cpu@0 {
compatible = "arm,cortex-a55";
reg = <0x0>;
device_type = "cpu";
enable-method = "psci";
next-level-cache = <&L2>;
clocks = <&clkmgr AGILEX5_CORE0_FREE_CLK>;
clock-names = "cpu";
operating-points-v2 = <&cpu_opp>; // cpufreq uses these
};
cpu1: cpu@1 {
compatible = "arm,cortex-a55";
reg = <0x100>;
device_type = "cpu";
enable-method = "psci";
next-level-cache = <&L2>;
clocks = <&clkmgr AGILEX5_CORE1_FREE_CLK>;
clock-names = "cpu";
operating-points-v2 = <&cpu_opp>; // cpufreq uses these
};
cpu2: cpu@2 {
compatible = "arm,cortex-a76";
reg = <0x200>;
device_type = "cpu";
enable-method = "psci";
next-level-cache = <&L2>;
clocks = <&clkmgr AGILEX5_CORE2_FREE_CLK>;
clock-names = "cpu";
operating-points-v2 = <&cpu_opp>; // cpufreq uses these
};
cpu3: cpu@3 {
compatible = "arm,cortex-a76";
reg = <0x300>;
device_type = "cpu";
enable-method = "psci";
next-level-cache = <&L2>;
clocks = <&clkmgr AGILEX5_CORE3_FREE_CLK>;
clock-names = "cpu";
operating-points-v2 = <&cpu_opp>; // cpufreq uses these
};
cpu_opp: opp-table {
compatible = "operating-points-v2";
opp-shared;
opp-min {
opp-hz = /bits/ 64 <10000000>; // 10MHz
};
opp-max {
opp-hz = /bits/ 64 <800000000>;// Example, 800MHz
};
};
Step 2:
You have to modify the drivers\clk\socfpga\clk-periph-s10.c to add the 2 ops function for .set_rate and .round_rate. The following codes are just POC and not production quality. The objective is to modify the Ping pong counter. The spec says, that, "Division setting for ping pong counter in clock slice. Divides the core01_clk frequency by this value + 1." The reset value is 0, and the divisor is 1. It will take the maximum frequency of the CPU core.
Intel Sundance Mesa HPS Register Map - core01ctr
static int clk_cpu_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hw);
unsigned long div = 1;
if (socfpgaclk->hw.reg)
{
/* Calculate divisor */
div = (parent_rate/rate) + 1; // We need to obtain the right divisor based on the max clock frequency. The requested rate is assumed to be always smaller than the parent_rate (maximum core frequency)
writel(div, socfpgaclk->hw.reg);
pr_err("DEBUG: clk_cpu_set_rate %d parent= %d, divisor = %d\n", rate, parent_rate, div);
}
return clk_peri_cnt_clk_recalc_rate(hw, parent_rate);
}
// This is not implemented in full yet. Just for demo.
static long clk_cpu_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
return rate;
}
static const struct clk_ops peri_cnt_clk_ops = {
.recalc_rate = clk_peri_cnt_clk_recalc_rate,
.get_parent = clk_periclk_get_parent,
.set_rate = clk_cpu_set_rate, // <- Define the new function here
.round_rate = clk_cpu_round_rate // Define the new function here.
};
Testing the solution. You should be able to see cpufreq once you have both Device tree and Device driver (clock manager) hacked.
You must set the governor to user-space in order for you to modify the cpu frequency from sysFS.
root@dhcp0:~# echo userspace > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
Modify the speed (in kHz)
echo 50000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed
root@dhcp0:~# ls /sys/devices/system/cpu/cpu0/cpufreq
affected_cpus related_cpus scaling_governor
cpuinfo_cur_freq scaling_available_frequencies scaling_max_freq
cpuinfo_max_freq scaling_available_governors scaling_min_freq
cpuinfo_min_freq scaling_cur_freq scaling_setspeed
cpuinfo_transition_latency scaling_driver stats