1. Purpose[edit | edit source]
This article explains how to change the CPU operating point (also known as OPP). An operating point corresponds to the frequency of the processor and the voltage that needs to be supplied to sustain it.
It also shows how it is possible to define multiple operating points, allowing the system to jump among them at run time: this is the dynamic voltage and frequency scaling (DVFS). Some cautions are given at the end of the article, to help the user in its DVFS deployment.
2. Hardware side[edit | edit source]
On STM32 Arm® Cortex® MPUs products, the Cortex-A core:
- is clocked by PLL1 from the RCC internal peripheral in OpenSTLinux:
the PLL1 output frequency can be directly propagated to the core, or it can go through an intermediate MPUDIV divider - is supplied with
The part number tells the device maximum supported frequency with associated usage conditions
- up to 1 GHz for the STM32MP13
- up to 800 MHz for the STM32MP15
- up to 1.5 GHz for the STM32MP21/ STM32MP23 / STM32MP25
Otherwise, the frequency must be kept below the nominal frequency:
- 650 MHz for STM32MP1 series
- 1.2 GHz for STM32MP2 series.
3. Software side[edit | edit source]
3.1. Boot time[edit | edit source]
In OpenSTLinux distribution, TF-A BL2 sets the CPU nominal frequency, that is the maximum frequency sustainable with the nominal voltage.
Notice that the processor must receive the nominal voltage during TF-A BL2 execution, whether configuring the STPMIC from TF-A BL2 itself or getting it from a discrete power supply, depending on the hardware board configuration.
3.1.1. On STM32MP13x lines
[edit | edit source]
3.1.1.1. VDDCPU voltage configuration[edit | edit source]
The example below sets STPMIC1 BUCK1 minimal voltage to 1.25 V, allowing to provide the expected nominal voltage on VDDCPU for the CPU:
&cpu0 {
cpu-supply = <&vddcpu>;
};
...
pmic: stpmic@33 {
vddcpu: buck1 {
regulator-name = "vddcpu";
regulator-min-microvolt = <1250000>;
...
};
3.1.1.2. CPU frequency configuration[edit | edit source]
TF-A is using an algorithm to calculate the PLL1 settings in drivers/st/clk/clk-stm32mp13.c .
3.1.2. On STM32MP15x lines
[edit | edit source]
3.1.2.1. VDDCORE voltage configuration[edit | edit source]
The example below sets STPMIC1 BUCK1 minimal voltage to 1.2 V, allowing to provide the expected nominal voltage on VDDCORE for the CPU:
&cpu0 {
cpu-supply = <&vddcore>;
};
...
pmic: stpmic@33 {
vddcore: buck1 {
regulator-name = "vddcore";
regulator-min-microvolt = <1200000>;
...
};
3.1.2.2. CPU frequency configuration[edit | edit source]
TF-A is using an algorithm to calculate the PLL1 settings in drivers/st/clk/stm32mp1_clk.c .
3.1.3. On STM32MP2 series[edit | edit source]
3.1.3.1. VDDCPU voltage configuration[edit | edit source]
The example below sets STPMIC25 BUCK1 minimal voltage to 0.8 V, allowing to provide the expected nominal voltage on VDDCPU for the CPU:
&cpu0 {
cpu-supply = <&vddcpu>;
};
...
pmic2: stpmic2@33 {
vddcpu: buck1 {
regulator-name = "vddcpu";
regulator-min-microvolt = <800000>;
...
};
3.1.3.2. CPU frequency configuration[edit | edit source]
STM32MP2 clock tree initialization in TF-A BL2 and in OP-TEE is using the default PLL1 setting found in RCC node of device tree with st,pll
property.
For example in fdts/stm32mp257f-ev1-ca35tdcid-rcc.dtsi :
pll1: st,pll-1 { st,pll = <&pll1_cfg_1200Mhz>; pll1_cfg_1200Mhz: pll1-cfg-1200Mhz { cfg = <30 1 1 1>; src = <MUX_CFG(MUX_MUXSEL5, MUXSEL_HSE)>; }; };
OpenSTLinux assume this setting allow to boot at a mominal frequency, without voltage modification.
3.2. Runtime[edit | edit source]
3.2.1. On STM32MP13x lines
[edit | edit source]
3.2.1.1. Overview[edit | edit source]
By default, in OpenSTLinux distribution, the OP-TEE "soc" device tree file defines an OPP table that contains one or several frequency / voltage pair(s).
![]() |
Whatever the implementation is, if your part number supports up to 1 GHz, ensure that the VDDCPU minimum voltage is increased from 1.25 V to 1.35 V while running above 650 MHz |
3.2.1.2. OPP table[edit | edit source]
At runtime, Linux will automatically request to OP-TEE SCMI interface to switch between the available operating points according to the CPU load and thanks to Linux cpufreq framework, configured with "ondemand" governor policy (see admin-guide/pm/cpufreq.html). This feature is called dynamic voltage and frequency scaling (DVFS).
In core/arch/arm/dts/stm32mp131.dtsi , two operating points ([1 GHz ; 1.35 V]; [650 MHz ; 1.25 V]) are defined and the supported OPP are defined with the bitfield opp-supported-hw
- bit 0: 650 MHz capable part number = STM32MP13xA / STM32MP13xC
- bit 1: 1 GHz capable part number = STM32MP13xD / STM32MP13xF
cpu0_opp_table: cpu0-opp-table {
compatible = "operating-points-v2";
opp-650000000 {
opp-hz = /bits/ 64 <650000000>;
opp-microvolt = <1250000>;
opp-supported-hw = <0x3>;
st,opp-default;
};
opp-1000000000 {
opp-hz = /bits/ 64 <1000000000>;
opp-microvolt = <1350000>;
opp-supported-hw = <0x2>;
st,opp-default;
};
};
The PLL1 configurations needed to reach the above frequencies must be described via the st,clk_opp, st,pll@0 and st,pll_vco property in 'rcc' device tree node on OP-TEE device tree.
For example in core/arch/arm/dts/stm32mp135f-dk.dts :
&rcc { st,pll_vco { pll1_vco_2000Mhz: pll1-vco-2000Mhz { src = <CLK_PLL12_HSE>; divmn = <1 82>; frac = <0xAAA>; }; pll1_vco_1300Mhz: pll1-vco-1300Mhz { src = <CLK_PLL12_HSE>; divmn = <2 80>; frac = <0x800>; }; ... }; /* VCO = 1300.0 MHz => P = 650 (CPU) */ pll1: st,pll@0 { compatible = "st,stm32mp1-pll"; reg = <0>; st,pll = <&pll1_cfg1>; pll1_cfg1: pll1_cfg1 { st,pll_vco = <&pll1_vco_1300Mhz>; st,pll_div_pqr = <0 1 1>; }; pll1_cfg2: pll1_cfg2 { st,pll_vco = <&pll1_vco_2000Mhz>; st,pll_div_pqr = <0 1 1>; }; }; .... st,clk_opp { st,ck_mpu { cfg_1 { hz = < 1000000000 >; st,clksrc = < CLK_MPU_PLL1P >; st,pll = < &pll1_cfg2 >; }; cfg_2 { hz = < 650000000 >; st,clksrc = < CLK_MPU_PLL1P >; st,pll = < &pll1_cfg1 >; }; }; }; };
Notes:
- OP-TEE selects the highest st,opp-default supported OPP during the cold boot
- This description is valid for cold boot, but also when coming back from Standby low power mode.
3.2.2. On STM32MP15x lines
[edit | edit source]
3.2.2.1. Overview[edit | edit source]
By default, in OpenSTLinux distribution, the OP-TEE OS and Linux kernel "soc" device tree file defines an OPP table that contains one or several frequency / voltage pair(s).
![]() |
Whatever the implementation is, if your part number supports up to 800 MHz, ensure that the VDDCORE minimum voltage is increased from 1.2 V to 1.35 V while running above 650 MHz |
3.2.2.2. OPP table[edit | edit source]
At runtime, the Linux kernel will automatically switch between the available operating points according to the CPU load and thanks to Linux cpufreq framework, configured with "ondemand" governor policy (see admin-guide/pm/cpufreq.html). This feature is called dynamic voltage and frequency scaling (DVFS).
In Linux device tree arch/arm/boot/dts/st/stm32mp151.dtsi and in OP-TEE device tree core/arch/arm/dts/stm32mp151.dtsi , two operating points ([800 MHz ; 1.35 V] and [400 MHz ; 1.2 V]) are defined for a 800 MHz capable part number (opp-supported-hw = <0x2>), one operating points ([400 MHz ; 1.2 V]) are defined for a 650 MHz capable part number (opp-supported-hw = <0x1>):
cpu0_opp_table: cpu0-opp-table {
compatible = "operating-points-v2";
opp-shared;
opp-400000000 {
opp-hz = /bits/ 64 <400000000>;
opp-microvolt = <1200000>;
opp-supported-hw = <0x2>;
opp-suspend;
};
opp-650000000 {
opp-hz = /bits/ 64 <650000000>;
opp-microvolt = <1200000>;
opp-supported-hw = <0x1>;
};
opp-800000000 {
opp-hz = /bits/ 64 <800000000>;
opp-microvolt = <1350000>;
opp-supported-hw = <0x2>;
};
};
OP-TEE is using an algorithm to calculate the PLL1 settings in core/drivers/clk/clk-stm32mp15.c , pll1_config_from_opp_khz().
Notes:
- This same OPP table must be present in both the BL32 (OP-TEE) and Linux device tree
- OP-TEE configure the OPP selected by supported OPP with st,opp-default and with the higher frequency during the cold boot
- This description is valid for cold boot, but also when coming back from Standby low power mode.
- During cold boot, BL32 (OP-TEE computes and saves the PLL1 settings for all operating points available in the device tree in compliance with the hardware capabilities. These saved parameters are used later to increase the performance of the system-state transitions.
- Selecting OPP frequencies that can be reached with RCC MPUDIV divider and without configuring again the PLL1 is recommended in order to get the fastest switch time between the OPP.
3.2.3. On STM32MP2 series[edit | edit source]
3.2.3.1. Overview[edit | edit source]
By default, the OpenSTLinux distribution defines an OPP table that contains one or several frequency / voltage pair(s):
- in the OP-TEE "soc" device tree file for A35-TD flavor
- in the TF-A and Linux "board" device tree file for M33-TD flavor
3.2.3.2. OPP table for A35-TD flavor
[edit | edit source]
At runtime, Linux will automatically request to OP-TEE SCMI interface to switch between the available operating points according to the CPU load and thanks to Linux cpufreq framework, configured with "schedutil" governor policy (default in drivers/cpufreq/Kconfig for ARM64 see admin-guide/pm/cpufreq.html). This feature is called dynamic voltage and frequency scaling (DVFS).
In OP-TEE SoC device tree file (for example in core/arch/arm/dts/stm32mp251.dtsi ), the two operating points [1.5GHz ; 0.9.1V] and [1.2GHz ; 0.8V] are defined with the associated bit-field opp-supported-hw
- bit 0: supported by 1.2 GHz part number = STM32MP2xxA / STM32MP2xxC
- bit 1: supported by 1.5 GHz part number = STM32MP2xxD / STM32MP2xxF
cpu0_opp_table: cpu0-opp-table {
compatible = "operating-points-v2";
opp-1200000000 {
opp-hz = /bits/ 64 <1200000000>;
opp-microvolt = <800000>;
opp-supported-hw = <0x3>;
st,opp-default;
};
opp-1500000000 {
opp-hz = /bits/ 64 <1500000000>;
opp-microvolt = <910000>;
opp-supported-hw = <0x2>;
};
};
The PLL1 configurations needed to reach the above frequencies must be described via the st,clk_opp, st,pll-1 and st,pll_vco property in 'rcc' device tree node on OP-TEE device tree.
For example in core/arch/arm/dts/stm32mp257f-dk-ca35tdcid-rcc.dtsi , generated by STM32MP2 clock tree:
&rcc { pll1: st,pll-1 { st,pll = <&pll1_cfg_1200Mhz>; pll1_cfg_1200Mhz: pll1-cfg-1200Mhz { cfg = <30 1 1 1>; src = <MUX_CFG(MUX_MUXSEL5, MUXSEL_HSE)>; }; pll1_cfg_1500Mhz: pll1-cfg-1500Mhz { cfg = <75 2 1 1>; src = <MUX_CFG(MUX_MUXSEL5, MUXSEL_HSE)>; }; };
And in core/arch/arm/dts/stm32mp257f-dk.dts :
&rcc { st,c1msrd = <2>; st,clk_opp { st,ck_cpu1 { cfg_1 { hz = <1500000000>; st,clksrc = <0>; st,pll = <&pll1_cfg_1500Mhz>; }; cfg_2 { hz = <1200000000>; st,clksrc = <0>; st,pll = <&pll1_cfg_1200Mhz>; }; }; }; };
Notes:
- OP-TEE selects the highest st,opp-default supported OPP during the cold boot
- This description is valid for cold boot, but also when coming back from Standby low power mode.
3.2.3.3. OPP table for M33-TD flavor
[edit | edit source]
At runtime, Linux will request to TF-A BL31 to switch between the available frequencies and according to the CPU load and thanks to Linux cpufreq framework, configured with "schedutil" governor policy (default in drivers/cpufreq/Kconfig for ARM64 see admin-guide/pm/cpufreq.html).
The supported frequencies are defined in Linux device tree and should be aligned on TF-A OPP tables.
In Linux the CA35SS clock driver requests the cortex-A35 frequency with a SMC interface STM32_SIP_SMC_STGEN_SET_RATE.
For example in stm32mp2/m33-td/tf-a/stm32mp257f-ev1-cm33tdcid-ostl-sdcard.dts , we have the definition for the 2 frequencies 1.5GHz and 1.2GHz, used for suspend phases with opp-suspend
.
/ {
ca35ss_clk: ca35ss-clk {
compatible = "st,stm32mp2-ca35ss-clk";
#clock-cells = <0>;
};
cpu0_opp_table: cpu0-opp-table {
compatible = "operating-points-v2";
opp-shared;
opp-1200000000 {
opp-hz = /bits/ 64 <1200000000>;
opp-suspend;
};
opp-1500000000 {
opp-hz = /bits/ 64 <1500000000>;
};
};
};
&cpu0 {
operating-points-v2 = <&cpu0_opp_table>;
clocks = <&ca35ss_clk>;
};
&cpu1 {
operating-points-v2 = <&cpu0_opp_table>;
clocks = <&ca35ss_clk>;
};
The associated voltage of VDDCPU for each the OPP is describe in TF-A device tree.
In TF-A board device tree file (for example in stm32mp2/m33-td/tf-a/stm32mp257f-ev1-cm33tdcid-ostl-sdcard.dts ), the two operating points [1.5GHz ; 0.9.1V] and [1.2GHz ; 0.8V] are defined with the associated bit-field opp-supported-hw
- bit 0: supported by 1.2 GHz part number = STM32MP2xxA / STM32MP2xxC
- bit 1: supported by 1.5 GHz part number = STM32MP2xxD / STM32MP2xxF
cpu0_opp_table: cpu0-opp-table {
compatible = "operating-points-v2";
opp-1200000000 {
opp-hz = /bits/ 64 <1200000000>;
opp-microvolt = <800000>;
opp-supported-hw = <0x3>;
st,opp-default;
st,pll = <&pll1_cfg_1200Mhz>;
};
opp-1500000000 {
opp-hz = /bits/ 64 <1500000000>;
opp-microvolt = <910000>;
opp-supported-hw = <0x2>;
st,pll = <&pll1_cfg_1500MHz>;
};
};
The PLL1 configurations are described via the st,pll-1 node in 'rcc' device tree node on TF-A device tree.
For example in stm32mp2/m33-td/tf-a/stm32mp257f-ev1-cm33tdcid-ostl-rcc.dtsi , generated by STM32MP2 clock tree:
&rcc { pll1: st,pll-1 { st,pll = <&pll1_cfg_1200Mhz>; pll1_cfg_1200Mhz: pll1-cfg-1200Mhz { cfg = <30 1 1 1>; src = <MUX_CFG(MUX_MUXSEL5, MUXSEL_HSE)>; }; pll1_cfg_1500Mhz: pll1-cfg-1500Mhz { cfg = <75 2 1 1>; src = <MUX_CFG(MUX_MUXSEL5, MUXSEL_HSE)>; }; };
The associated VDDCPU are requested to TF-M with a SCMI request on VOLTD_SCMI_STPMIC2_BUCK in plat/st/stm32mp2/services/ca35ss_clk_svc.c .
4. Dynamic voltage and frequency scaling (DVFS) caution[edit | edit source]
As stated above, as soon as at least two operating points are defined in the OPP table, Linux kernel will automatically switch between them at runtime thanks to Linux cpufreq framework, configured with ondemand (for AArch32, STM32MP1 series) or schedutil (for AArch64, STM32MP2 series) governor policy (see admin-guide/pm/cpufreq.html). It is important to notice that cpufreq framework is only monitoring the CPU load to select the OPP because this can lead to some limitation during use cases where the CPU is not loaded a lot but high reactivity is needed to respect some real time constraints, like interrupt management.
You can check the DVFS statistic with:
echo 1 > /sys/devices/system/cpu/cpufreq/policy0/stats/reset more /sys/devices/system/cpu/cpufreq/policy0/stats/* | cat
If you face some system issue where the CPU reactivity may be the root cause whereas DVFS is enabled, consider doing a trial with performance governor (see admin-guide/pm/cpufreq.html).
echo performance > /sys/devices/system/cpu/cpufreq/policy0/scaling_governor
You can also force the frequency with the userspace governor (for 1.2GHz with <freq value> = 1200000):
echo userspace > /sys/devices/system/cpu/cpufreq/policy0/scaling_governor echo <freq value> > /sys/devices/system/cpu/cpufreq/policy0/scaling_setspeed
5. How to go further[edit | edit source]
- admin-guide/pm/cpufreq.html
- cpu-freq/cpufreq-stats.html
- Guidelines for Lifetime Usage Estimates: AN5438 and AN5729
- Clock device tree configuration and "how to build a clock tree".