This article gives information about the Linux® Clock framework.
Clocks are generally provided by internal/external oscillators or PLLs.
They can pass through a gate, a muxing, a divider or a multiplier.
All peripheral clocks are organized as a tree.
All these elements are managed by the Common Clock Framework (CCF)
1. Framework purpose[edit | edit source]
The purpose of this article is to introduce the Common Clock Framework. It provides general information and, based on examples, explains how to use it.
Linux Common Clock framework offers a generic API for configuring and controlling the different system clocks.
Drivers can easily enable/disable clocks, change their frequency, change the parent of the clocks associated to peripherals without any knowledge of the clock characteristics.
All clock tree specificities (such as clock source selection, muxing, dividor and gate) are abstracted by the Common Clock framework and the associated system clock driver.
2. System overview[edit | edit source]
2.1. Component description[edit | edit source]
- foo driver: any peripheral driver which needs to control and activate clock(s) associated to a given peripheral.
- Clock core: the Common Clock framework is the generic Linux kernel interface that controls the clock nodes available in the system.
- It features two generic interfaces:
- - The upper interface unifies the definition and control of the clocks for all Linux platforms. This agnostic API is used by peripheral drivers for configuring and controlling the clocks associated to a specific peripheral.
- - The lower interface allows the registration of platform specific functions in order to manage a platform specific clock tree.
- clk-stm32mp: STM32 specific clock driver that supports RCC clocks.
- clk-scmi: SCMI clock driver to manage secured clocks.
- RCC: reset and clock controler RCC.
2.2. API description[edit | edit source]
Documentation on the Common Clock framework can be found in the kernel documentation:
- devm_get_clk(): looks up and obtains from the device tree a managed reference to a clock producer, to a root clock or to a clock node.
- clk_prepare_enable(): selects a parent clock, configures the corresponding multiplexor and dividor, and enables the clock gating.
- clk_disable_unprepare(): unprepares and gate a clock.
- clk_get_rate(): obtains the current frequency (in Hz) for a given clock.
- clk_set_rate(): sets the frequency for a given clock. If a clock has several parents, the clock framework can change the parent in order to obtain a better frequency.
- clk_get_parent(): gets the parent clock source for a given clock
- clk_set_parent(): sets the parent clock source for a given clock
- ...
3. Configuration[edit | edit source]
3.1. Kernel configuration[edit | edit source]
By default the Common Clock framework is activated in the kernel configuration.
3.2. Device tree configuration[edit | edit source]
A detailed device tree configuration is described in Clock device tree configuration.
4. How to use the Common Clock framework[edit | edit source]
This paragraph describes how a standard peripheral driver can retrieve its clock configuration from the device tree and configure it.
The clocks associated to a given peripheral are declared in the device tree as described in https://github.com/devicetree-org/dt-schema/blob/main/dtschema/schemas/clock/clock.yaml.
Specific platform define statements that abstract hardware clock offsets are defined in clock dt-binding header file:
- for STM32MP15x lines include/dt-bindings/clock/stm32mp1-clks.h }
- for STM32MP13x lines include/dt-bindings/clock/stm32mp13-clks.h }
- for STM32MP25x lines include/dt-bindings/clock/stm32mp25-clks.h }
Below an example of clock association(FOO_K) to a foo driver:
foo: foo@adcdefgh {
compatible = "foo-driver";
...
clocks = <&rcc FOO_K>;
...
};
Before using the clock specified in the device tree node, the foo driver has to request it at probe execution.
static int foo_probe(struct platform_device *pdev) { struct clk *clk; ... clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(clk)) { ret = PTR_ERR(clk); dev_err(&pdev->dev, "clk get failed: %d\n", ret); goto err_master_put; } ... }
The clock can then be configured and enabled using the Common Clock framework API.
If the peripheral needs several clocks, a name must be associated to each clock handle in the device tree node. The specified names must be used by the driver to retrieve the corresponding pointer for each clock.
Below an example of foo driver managing 2 clocks (FOO1_K and FOO2_K):
foo: foo@abcdefgh { compatible = "foo-driver"; clocks = <&rcc FOO1_K>, <&rcc FOO2_K>; clock-names = "foo1", "foo2"; };
static int foo_probe(struct platform_device *pdev) { priv->foo1clk = devm_clk_get(&pdev->dev, "foo1"); if (IS_ERR(priv->foo1clk)) { ret = PTR_ERR(priv->foo1clk); if (ret != -ENOENT) { dev_err(&pdev->dev, "Can't get 'foo1' clock\n"); return ret; } } priv->foo2clk = devm_clk_get(&pdev->dev, "foo2")'; if (IS_ERR(priv->foo2clk)) { ret = PTR_ERR(priv->foo2clk); if (ret != -ENOENT) { dev_err(&pdev->dev, "Can't get 'foo2' clock\n"); return ret; } } }
ret = clk_prepare_enable(priv->foo1clk); if (ret < 0) { dev_err(dev, "foo1 clk enable failed\n"); goto err_bclk_disable; }
5. How to trace and debug the framework[edit | edit source]
5.1. Tracing using dynamic debug[edit | edit source]
By default, no kernel log showing the clock activity. However the user can enable the dynamic debug for the clock framework driver:
dmesg -n8 echo "file drivers/clk/* +p" > /sys/kernel/debug/dynamic_debug/control
Refer to dynamic debug for more details.
5.1.1. How to monitor with debugfs of clock framework[edit | edit source]
Information on clocks are available in Debugfs interface located in the '/sys/kernel/debug/clk' directory.
It helps viewing all the clocks registered in tree format.
Show clock tree:
cat /sys/kernel/debug/clk/clk_summary clock count count count rate accuracy phase cycle enable ------------------------------------------------------------------------------------------------------- ck_usbo_48m 1 1 0 48000000 0 0 50000 Y usbo_k 0 0 0 48000000 0 0 50000 N usart1_k 0 0 0 64000000 0 0 50000 Y spi6_k 0 0 0 64000000 0 0 50000 Y rtcapb 1 1 0 66625000 0 0 50000 Y ck_rtc 2 2 0 32768 0 0 50000 Y rtc_lsco 1 1 0 32768 0 0 50000 Y i2c6_k 0 0 0 64000000 0 0 50000 Y i2c4_k 0 0 0 64000000 0 0 50000 Y hash1 0 0 0 266500000 0 0 50000 Y gpioz 1 1 0 266500000 0 0 50000 Y cryp1 0 0 0 266500000 0 0 50000 Y bsec 0 0 0 66625000 0 0 50000 Y ck_axi 3 3 0 266500000 0 0 50000 Y ck_trace 0 0 0 133250000 0 0 50000 N ck_sys_dbg 0 0 0 266500000 0 0 50000 Y qspi_k 0 0 0 266500000 0 0 50000 N fmc_k 0 0 0 266500000 0 0 50000 N ethstp 0 0 0 266500000 0 0 50000 N usbh 1 1 0 266500000 0 0 50000 Y crc1 0 0 0 266500000 0 0 50000 N ethmac 1 1 0 266500000 0 0 50000 Y ethtx 1 1 0 266500000 0 0 50000 Y gpu 0 0 0 266500000 0 0 50000 N mdma 0 0 0 266500000 0 0 50000 N pclk5 0 0 0 66625000 0 0 50000 Y pclk4 0 0 0 133250000 0 0 50000 Y ddrperfm 0 0 0 133250000 0 0 50000 N stgenro 0 0 0 133250000 0 0 50000 N usbphy 0 0 0 133250000 0 0 50000 Y iwdg2 0 0 0 133250000 0 0 50000 N dsi 0 0 0 133250000 0 0 50000 N ltdc 0 0 0 133250000 0 0 50000 N ck_mpu 0 0 0 650000000 0 0 50000 Y ck_mco2 0 0 0 650000000 0 0 50000 N pll2_r 0 0 0 533000000 0 0 50000 Y pll2_q 0 0 0 533000000 0 0 50000 Y gpu_k 0 0 0 533000000 0 0 50000 N ck_lsi 0 0 0 32000 0 0 50000 Y dac12_k 0 0 0 32000 0 0 50000 N rng2_k 0 0 0 32000 0 0 50000 N ck_lse 0 0 0 32768 0 0 50000 Y lptim5_k 0 0 0 32768 0 0 50000 N lptim4_k 0 0 0 32768 0 0 50000 N cec_k 0 0 0 32768 0 0 50000 N ck_csi 0 0 0 4000000 0 0 50000 Y ck_per 0 0 0 4000000 0 0 50000 Y ck_hsi 1 1 0 64000000 0 0 50000 Y ck_mco1 0 0 0 64000000 0 0 50000 N uart8_k 0 0 0 64000000 0 0 50000 N uart7_k 0 0 0 64000000 0 0 50000 N uart6_k 0 0 0 64000000 0 0 50000 N uart5_k 0 0 0 64000000 0 0 50000 N uart4_k 1 1 0 64000000 0 0 50000 Y usart3_k 0 0 0 64000000 0 0 50000 N usart2_k 0 0 0 64000000 0 0 50000 N i2c5_k 0 0 0 64000000 0 0 50000 N i2c3_k 0 0 0 64000000 0 0 50000 N i2c2_k 0 0 0 64000000 0 0 50000 N i2c1_k 0 0 0 64000000 0 0 50000 N spi5_k 0 0 0 64000000 0 0 50000 N spi4_k 0 0 0 64000000 0 0 50000 N ck_hse 4 5 0 24000000 0 0 50000 Y usbphy_k 1 1 0 24000000 0 0 50000 Y pll4 1 1 0 594000000 0 0 50000 Y pll4_r 0 0 0 74250000 0 0 50000 Y fdcan_k 0 0 0 74250000 0 0 50000 N pll4_q 0 0 0 29700000 0 0 50000 N ltdc_px 0 0 0 29700000 0 0 50000 N dsi_px 0 0 0 29700000 0 0 50000 N pll4_p 1 1 0 99000000 0 0 50000 Y ck_ker_eth 2 2 0 99000000 0 0 50000 Y ethptp_k 1 1 0 99000000 0 0 50000 Y ethck_k 1 1 0 99000000 0 0 50000 Y ethrx 1 1 0 99000000 0 0 50000 Y spdif_k 0 0 0 99000000 0 0 50000 N sdmmc3_k 0 0 0 99000000 0 0 50000 N sdmmc2_k 0 0 0 99000000 0 0 50000 N sdmmc1_k 0 0 0 99000000 0 0 50000 N pll3 1 1 0 417755859 0 0 50000 Y pll3_r 0 0 0 11290699 0 0 50000 Y pll3_q 0 0 0 24573875 0 0 47058 Y adfsdm_k 0 0 0 24573875 0 0 50000 N adc12_k 0 0 0 24573875 0 0 50000 Y sai4_k 0 0 0 24573875 0 0 50000 N sai3_k 0 0 0 24573875 0 0 50000 N sai2_k 0 0 0 24573875 0 0 50000 N sai1_k 0 0 0 24573875 0 0 50000 N spi3_k 0 0 0 24573875 0 0 50000 N spi2_k 0 0 0 24573875 0 0 50000 N spi1_k 0 0 0 24573875 0 0 50000 N pll3_p 1 1 0 208877930 0 0 50000 Y ck_mcu 15 15 0 208877930 0 0 50000 Y dfsdm_k 0 0 0 208877930 0 0 50000 N gpiok 0 0 0 208877930 0 0 50000 N gpioj 0 0 0 208877930 0 0 50000 N gpioi 1 1 0 208877930 0 0 50000 Y gpioh 1 1 0 208877930 0 0 50000 Y gpiog 1 1 0 208877930 0 0 50000 Y gpiof 1 1 0 208877930 0 0 50000 Y gpioe 1 1 0 208877930 0 0 50000 Y gpiod 1 1 0 208877930 0 0 50000 Y gpioc 1 1 0 208877930 0 0 50000 Y gpiob 1 1 0 208877930 0 0 50000 Y gpioa 1 1 0 208877930 0 0 50000 Y ipcc 4 4 0 208877930 0 0 50000 Y hsem 1 1 0 208877930 0 0 50000 Y crc2 0 0 0 208877930 0 0 50000 N rng2 0 0 0 208877930 0 0 50000 N hash2 0 0 0 208877930 0 0 50000 N cryp2 0 0 0 208877930 0 0 50000 N dcmi 0 0 0 208877930 0 0 50000 N sdmmc3 0 0 0 208877930 0 0 50000 N usbo 0 0 0 208877930 0 0 50000 N adc12 0 0 0 208877930 0 0 50000 Y dmamux 1 1 0 208877930 0 0 50000 Y dma2 0 0 0 208877930 0 0 50000 N dma1 1 1 0 208877930 0 0 50000 Y pclk3 1 2 0 104438965 0 0 50000 Y lptim3_k 0 0 0 104438965 0 0 50000 N lptim2_k 0 0 0 104438965 0 0 50000 N hdp 0 0 0 104438965 0 0 50000 N pmbctrl 0 0 0 104438965 0 0 50000 N tmpsens 1 1 0 104438965 0 0 50000 Y vref 0 0 0 104438965 0 0 50000 Y syscfg 0 1 0 104438965 0 0 50000 N sai4 0 0 0 104438965 0 0 50000 N lptim5 0 0 0 104438965 0 0 50000 N lptim4 0 0 0 104438965 0 0 50000 N lptim3 0 0 0 104438965 0 0 50000 N lptim2 0 0 0 104438965 0 0 50000 N pclk2 0 0 0 104438965 0 0 50000 Y fdcan 0 0 0 104438965 0 0 50000 N dfsdm 0 0 0 104438965 0 0 50000 N sai3 0 0 0 104438965 0 0 50000 N sai2 0 0 0 104438965 0 0 50000 N sai1 0 0 0 104438965 0 0 50000 N usart6 0 0 0 104438965 0 0 50000 N spi5 0 0 0 104438965 0 0 50000 N spi4 0 0 0 104438965 0 0 50000 N spi1 0 0 0 104438965 0 0 50000 N tim17 0 0 0 104438965 0 0 50000 N tim16 0 0 0 104438965 0 0 50000 N tim15 0 0 0 104438965 0 0 50000 N tim8 0 0 0 104438965 0 0 50000 N tim1 0 0 0 104438965 0 0 50000 N ck2_tim 0 0 0 208877930 0 0 50000 Y tim17_k 0 0 0 208877930 0 0 50000 N tim16_k 0 0 0 208877930 0 0 50000 N tim15_k 0 0 0 208877930 0 0 50000 N tim8_k 0 0 0 208877930 0 0 50000 N tim1_k 0 0 0 208877930 0 0 50000 N pclk1 0 0 0 104438965 0 0 50000 Y lptim1_k 0 0 0 104438965 0 0 50000 N mdio 0 0 0 104438965 0 0 50000 N dac12 0 0 0 104438965 0 0 50000 N cec 0 0 0 104438965 0 0 50000 N spdif 0 0 0 104438965 0 0 50000 N i2c5 0 0 0 104438965 0 0 50000 N i2c3 0 0 0 104438965 0 0 50000 N i2c2 0 0 0 104438965 0 0 50000 N i2c1 0 0 0 104438965 0 0 50000 N uart8 0 0 0 104438965 0 0 50000 N uart7 0 0 0 104438965 0 0 50000 N uart5 0 0 0 104438965 0 0 50000 N uart4 0 0 0 104438965 0 0 50000 Y usart3 0 0 0 104438965 0 0 50000 N usart2 0 0 0 104438965 0 0 50000 N spi3 0 0 0 104438965 0 0 50000 N spi2 0 0 0 104438965 0 0 50000 N lptim1 0 0 0 104438965 0 0 50000 N tim14 0 0 0 104438965 0 0 50000 N tim13 0 0 0 104438965 0 0 50000 N tim12 0 0 0 104438965 0 0 50000 N tim7 0 0 0 104438965 0 0 50000 N tim6 0 0 0 104438965 0 0 50000 N tim5 0 0 0 104438965 0 0 50000 N tim4 0 0 0 104438965 0 0 50000 N tim3 0 0 0 104438965 0 0 50000 N tim2 0 0 0 104438965 0 0 50000 N ck1_tim 0 0 0 208877930 0 0 50000 Y tim14_k 0 0 0 208877930 0 0 50000 N tim13_k 0 0 0 208877930 0 0 50000 N tim12_k 0 0 0 208877930 0 0 50000 N tim7_k 0 0 0 208877930 0 0 50000 N tim6_k 0 0 0 208877930 0 0 50000 N tim5_k 0 0 0 208877930 0 0 50000 N tim4_k 0 0 0 208877930 0 0 50000 N tim3_k 0 0 0 208877930 0 0 50000 N tim2_k 0 0 0 208877930 0 0 50000 N clk-hse-div2 0 0 0 12000000 0 0 50000 Y dsi_k 0 0 0 0 0 0 50000 N
- Note: Some clocks are managed by SCMI and necessarily roots (they have no parents from the kernel's point of view).
In addition, each clock has a dedicated directory in which you can find the information such as prepare count, enable count, rate and accuracy:
/sys/kernel/debug/clk# cd usart2_k /sys/kernel/debug/clk/usart2_k# ls clk_accuracy clk_flags clk_phase clk_prepare_count clk_enable_count clk_notifier_count clk_possible_parents clk_rate
Additional information, such as flags, notifier count and possible parents (for clocks with multiple parents) are also available. Just execute a 'cat' command to display them.
/sys/kernel/debug/clk/usart2_k# cat clk_possible_parents pclk1 pll4_q ck_hsi ck_csi ck_hse
5.1.2. How to monitor with debugfs of RCC driver[edit | edit source]
Some clocks are managed by the secured world (throught SCMI) and could be not visible from Kernel.
Then to have a complete clock summary use a cat
command on stm32_clk_summary file.
Show complete clock tree:
cat /sys/kernel/debug/clk/stm32_clk_summary hardware clock rate enable ------------------------------------------------------ ck_hsi 64000000 Y spi4_k 64000000 N spi5_k 64000000 N spi6_k 64000000 N i2c1_k 64000000 N i2c2_k 64000000 N i2c3_k 64000000 N i2c5_k 64000000 N i2c4_k 64000000 N i2c6_k 64000000 N usart1_k 64000000 N usart2_k 64000000 N usart3_k 64000000 N uart4_k 64000000 Y uart5_k 64000000 N uart6_k 64000000 N uart7_k 64000000 N uart8_k 64000000 N ck_mco1 64000000 N ck_csi 4000000 Y ck_per 4000000 ? ck_lsi 32000 Y rng1_k 32000 N rng2_k 32000 N dac12_k 32000 N ck_hse 24000000 Y clk-hse-div2 12000000 ? pll1 650000000 Y pll1_p 650000000 Y pll1_p_div 325000000 ? ck_mpu 650000000 ? ck_mco2 650000000 N pll2 533000000 Y pll2_p 266500000 Y ck_axi 266500000 ? pclk4 133250000 ? ltdc 133250000 N dsi 133250000 N iwdg2 133250000 N usbphy 133250000 Y stgenro 133250000 N ddrperfm 133250000 N pclk5 66625000 ? spi6 66625000 N i2c4 66625000 N i2c6 66625000 N usart1 66625000 N rtcapb 66625000 Y tzpc 66625000 Y iwdg1 66625000 N bsec 66625000 Y stgen 66625000 Y tzc1 266500000 Y tzc2 266500000 Y gpioz 266500000 Y cryp1 266500000 N hash1 266500000 N rng1 266500000 N bkpsram 266500000 N mdma 266500000 N gpu 266500000 N ethtx 266500000 Y ethrx 266500000 Y ethmac 266500000 Y crc1 266500000 N usbh 266500000 Y ethstp 266500000 N sdmmc1_k 266500000 N sdmmc2_k 266500000 N fmc_k 266500000 N qspi_k 266500000 N ck_sys_dbg 266500000 Y ck_trace 133250000 N pll2_q 533000000 Y gpu_k 533000000 N pll2_r 533000000 Y pll3 417755859 Y pll3_p 208877930 Y ck_mcu 208877930 ? pclk1 104438965 ? ck1_tim 208877930 ? tim2_k 208877930 N tim3_k 208877930 N tim4_k 208877930 N tim5_k 208877930 N tim6_k 208877930 N tim7_k 208877930 N tim12_k 208877930 N tim13_k 208877930 N tim14_k 208877930 N tim2 104438965 N tim3 104438965 N tim4 104438965 N tim5 104438965 N tim6 104438965 N tim7 104438965 N tim12 104438965 N tim13 104438965 N tim14 104438965 N lptim1 104438965 N spi2 104438965 N spi3 104438965 N usart2 104438965 N usart3 104438965 N uart4 104438965 Y uart5 104438965 N uart7 104438965 N uart8 104438965 N i2c1 104438965 N i2c2 104438965 N i2c3 104438965 N i2c5 104438965 N spdif 104438965 N cec 104438965 N dac12 104438965 N mdio 104438965 N lptim1_k 104438965 N pclk2 104438965 ? ck2_tim 208877930 ? tim1_k 208877930 N tim8_k 208877930 N tim15_k 208877930 N tim16_k 208877930 N tim17_k 208877930 N tim1 104438965 N tim8 104438965 N tim15 104438965 N tim16 104438965 N tim17 104438965 N spi1 104438965 N spi4 104438965 N spi5 104438965 N usart6 104438965 N sai1 104438965 N sai2 104438965 N sai3 104438965 N dfsdm 104438965 N fdcan 104438965 N pclk3 104438965 ? lptim2 104438965 N lptim3 104438965 N lptim4 104438965 N lptim5 104438965 N sai4 104438965 N syscfg 104438965 N vref 104438965 Y tmpsens 104438965 Y pmbctrl 104438965 N hdp 104438965 N lptim2_k 104438965 N lptim3_k 104438965 N dma1 208877930 Y dma2 208877930 N dmamux 208877930 Y adc12 208877930 Y usbo 208877930 N sdmmc3 208877930 N dcmi 208877930 N cryp2 208877930 N hash2 208877930 N rng2 208877930 N crc2 208877930 N hsem 208877930 Y ipcc 208877930 Y gpioa 208877930 Y gpiob 208877930 Y gpioc 208877930 Y gpiod 208877930 Y gpioe 208877930 Y gpiof 208877930 Y gpiog 208877930 Y gpioh 208877930 Y gpioi 208877930 Y gpioj 208877930 N gpiok 208877930 N dfsdm_k 208877930 N pll3_q 24573875 Y spi1_k 24573875 N spi2_k 24573875 N spi3_k 24573875 N sai1_k 24573875 N sai2_k 24573875 N sai3_k 24573875 N sai4_k 24573875 N adc12_k 24573875 Y adfsdm_k 24573875 N pll3_r 11290699 Y pll4 594000000 Y pll4_p 99000000 Y sdmmc3_k 99000000 N spdif_k 99000000 N ethck_k 99000000 Y ethptp_k 99000000 ? pll4_q 29700000 N dsi_px 29700000 N ltdc_px 29700000 N pll4_r 74250000 Y fdcan_k 74250000 N usbphy_k 24000000 Y stgen_k 24000000 Y ck_lse 32768 Y cec_k 32768 N lptim4_k 32768 N lptim5_k 32768 N ck_rtc 32768 Y ck_usbo_48m 48000000 ? usbo_k 48000000 N i2s_ckin 0 ?
6. Source code location[edit | edit source]
The clock framework is composed of:
- clock core files: drivers/clk/clk.c
- clock driver file:
- for STM32MP15x lines drivers/clk/clk-stm32mp1.c
- for STM32MP13x lines drivers/clk/stm32/clk-stm32mp13.c
- for STM32MP25x lines drivers/clk/stm32/clk-stm32mp25.c
- clock dt-binding interface:
- for STM32MP15x lines include/dt-bindings/clock/stm32mp1-clks.h }
- for STM32MP13x lines include/dt-bindings/clock/stm32mp13-clks.h }
- for STM32MP25x lines include/dt-bindings/clock/stm32mp25-clks.h }
7. To go further[edit | edit source]
See "how to build a clock tree" in
8. References[edit | edit source]