Cpufreq Linux Driver for Zynq and Zynq Ultrascale+ MPSoC

Introduction

This page gives an overview of the CPU freq driver which is available as part of the ZynqMP Linux distribution.
Paths, files, links and documentation on this page are given relative to the Linux kernel source tree.

It is documented in Documentation/cpu-freq/.

Cpufreq often uses the Operating Performance Point library, which is explained in Documentation/power/opp.txt. The cpufreq framework comes with different frequency scaling governors, which scale the CPU frequency according to system load (for details please refer to the documentation).
The driver exposes a control interface through sysFS in /sys/devices/system/cpu/cpu0/cpufreq/.

HW IP features

Dynamic frequency scaling for Zynqmp Ultrascale+ MPSoc APU.

Features supported in driver

Dynamic frequency scaling for Zynqmp Ultrascale+ MPSoc APU.

Frequencies supported in Zynq Ultrascale+ MPSoC

We do not support changing the parent pll rates (pllfreq). We support only integral divisors of pllfreq.
cpufreq_zynqultrascale1.png
In the above example the acpu frequencies are limited to pllfreq/x where x is integral numbers 1, 2 , 3 .. etc
pllfreq = osc_frequency * PLL_multiplier

Lets take the example of zcu102
In zcu102 the osc frequency is 33.33
The default hdf has PLL multiplier of 72.
pllfreq = 33.33*72 = 2399.76

the possible frequencies are integral divisors of 2399.76M
1199.88 M
799.92 M
599.94 M
479.95M
399.96M
342.82M
etc

Missing Features, Known Issues and Limitations

zynqmp Ultrascale+:
None

zynq:
  • When running at a too low frequency, USB breaks. The lowest tested and working frequency is 222.222 MHz. Highest frequency where it is known broken is 111.111 MHz
  • Suspend breaks on reduced frequencies
  • Due to dependencies between the CPU frequency and timers, the range of valid frequencies is limited/frequency changes may fail (see: https://github.com/Xilinx/linux-xlnx/commit/356cbded4131b067ec5fa4430c15c2fac867fa2b)
  • cpufreq statistics don't work
  • System clock runs slower proportionately to the difference between the default clock rate and that set by cpufreq if a timer other than the TTC (e.g. arm_global_timer) is used as clocksource


Kernel Configuration

The following config options should be enabled in order to build the cpufreq driver

CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPUFREQ_DT=y
CONFIG_CPUFREQ_DT_PLATDEV=y
  Symbol: CPU_FREQ [=y]
  Type  : boolean
   Prompt: CPU Frequency scaling
   Location:
     -> CPU Power Management
     -> CPU Frequency scaling
    Defined at drivers/cpufreq/Kconfig:3
     Selects: SRCU [=y]
Devicetree
cpus {
#address-cells = <1>;
#size-cells = <0>;
 
cpu0: cpu@0 {
compatible = "arm,cortex-a53", "arm,armv8";
device_type = "cpu";
enable-method = "psci";
operating-points-v2 = <&cpu_opp_table>;
reg = <0x0>;
cpu-idle-states = <&CPU_SLEEP_0>;
};
 
cpu1: cpu@1 {
compatible = "arm,cortex-a53", "arm,armv8";
device_type = "cpu";
enable-method = "psci";
reg = <0x1>;
operating-points-v2 = <&cpu_opp_table>;
cpu-idle-states = <&CPU_SLEEP_0>;
};
 
cpu2: cpu@2 {
compatible = "arm,cortex-a53", "arm,armv8";
device_type = "cpu";
enable-method = "psci";
reg = <0x2>;
operating-points-v2 = <&cpu_opp_table>;
cpu-idle-states = <&CPU_SLEEP_0>;
};
 
cpu3: cpu@3 {
compatible = "arm,cortex-a53", "arm,armv8";
device_type = "cpu";
enable-method = "psci";
reg = <0x3>;
operating-points-v2 = <&cpu_opp_table>;
cpu-idle-states = <&CPU_SLEEP_0>;
};
 
cpu_opp_table: cpu_opp_table {
compatible = "operating-points-v2";
opp-shared;
opp00 {
opp-hz = /bits/ 64 <1199999988>;
opp-microvolt = <1000000>;
clock-latency-ns = <500000>;
};
opp01 {
opp-hz = /bits/ 64 <599999994>;
opp-microvolt = <1000000>;
clock-latency-ns = <500000>;
};
opp02 {
opp-hz = /bits/ 64 <399999996>;
opp-microvolt = <1000000>;
clock-latency-ns = <500000>;
};
opp03 {
opp-hz = /bits/ 64 <299999997>;
opp-microvolt = <1000000>;
clock-latency-ns = <500000>;
};
};

Test Procedure


/sys/devices/system/cpu/cpufreq/policy0/stats/ti
275000 0
366666 0
550000 0
1100000 18170

roott@Xilinx-EP108-REMUS:/sys/devices/system/cpu/cpufreq/policy0# cat affected_cpu
0 1 2 3
root@Xilinx-EP108-REMUS:/sys/devices/system/cpu/cpufreq/policy0#
ilable_frequenciesREMUS:/sys/devices/system/cpu/cpufreq/policy0# cat scaling_ava
275000 366666 550000 1100000
root@Xilinx-EP108-REMUS:/sys/devices/system/cpu/cpufreq/policy0#
root@Xilinx-EP108-REMUS:/sys/devices/system/cpu/cpufreq/policy0#
ilable_governors REMUS:/sys/devices/system/cpu/cpufreq/policy0# cat scaling_avai
conservative ondemand userspace powersave performance
root@Xilinx-EP108-REMUS:/sys/devices/system/cpu/cpufreq/policy0#
root@Xilinx-EP108-REMUS:/sys/devices/system/cpu/cpufreq/policy0#
scaling_setspeed -REMUS:/sys/devices/system/cpu/cpufreq/policy0# echo 275000 >
PMUFW: PmMmioRead: (NODE_APU) addr=0xFD1A0060, value=0x3000200
PMUFW: PmMmioWrite: (NODE_APU) addr=0xFD1A0060, mask=0xFFFFFFFF, value=0x3000500
PMUFW: PmMmioRead: (NODE_APU) addr=0xFD1A0060, value=0x3000500
root@Xilinx-EP108-REMUS:/sys/devices/system/cpu/cpufreq/policy0#
_freq ilinx-EP108-REMUS:/sys/devices/system/cpu/cpufreq/policy0# cat scaling_cur
275000

in_state nx-EP108-REMUS:/sys/devices/system/cpu/cpufreq/policy0# cat stats/time_i
275000 3275
366666 0
550000 0
1100000 32448
root@Xilinx-EP108-REMUS:/sys/devices/system/cpu/cpufreq/policy0#

Mainline status

The driver is currently in sync with the mainline.

Change Log

2017.1
Summary:
  • Added device tree node
  • Added cpufreq driver support
Commits:
ca1cf28 cpufreq: dt: Add zynqmp to the cpufreq dt platdev
c0b91f5 dts: zynqmp: Update the OPPs for cpu freq

2017.2
No change

2017.3
No change

2017.4
No change

Related Links


||
cpus {

#address-cells = <1>;

#size-cells = <0>;



cpu0: cpu@0 {

compatible = "arm,cortex-a53", "arm,armv8";

device_type = "cpu";

enable-method = "psci";

operating-points-v2 = <&cpu_opp_table>;

reg = <0x0>;

cpu-idle-states = <&CPU_SLEEP_0>;

};



cpu1: cpu@1 {

compatible = "arm,cortex-a53", "arm,armv8";

device_type = "cpu";

enable-method = "psci";

reg = <0x1>;

operating-points-v2 = <&cpu_opp_table>;

cpu-idle-states = <&CPU_SLEEP_0>;

};



cpu2: cpu@2 {

compatible = "arm,cortex-a53", "arm,armv8";

device_type = "cpu";

enable-method = "psci";

reg = <0x2>;

operating-points-v2 = <&cpu_opp_table>;

cpu-idle-states = <&CPU_SLEEP_0>;

};



cpu3: cpu@3 {

compatible = "arm,cortex-a53", "arm,armv8";

device_type = "cpu";

enable-method = "psci";

reg = <0x3>;

operating-points-v2 = <&cpu_opp_table>;

cpu-idle-states = <&CPU_SLEEP_0>;

};



idle-states {

entry-method = "arm,psci";



CPU_SLEEP_0: cpu-sleep-0 {

compatible = "arm,idle-state";

arm,psci-suspend-param = <0x40000000>;

local-timer-stop;

entry-latency-us = <300>;

exit-latency-us = <600>;

min-residency-us = <100000>;

};

};

};



cpu_opp_table: cpu_opp_table {

compatible = "operating-points-v2";

opp-shared;

opp00 {

opp-hz = /bits/ 64 <1199999988>;

opp-microvolt = <1000000>;

clock-latency-ns = <500000>;

};

opp01 {

opp-hz = /bits/ 64 <599999994>;

opp-microvolt = <1000000>;

clock-latency-ns = <500000>;

};

opp02 {

opp-hz = /bits/ 64 <399999996>;

opp-microvolt = <1000000>;

clock-latency-ns = <500000>;

};

opp03 {

opp-hz = /bits/ 64 <299999997>;

opp-microvolt = <1000000>;

clock-latency-ns = <500000>;

};

};