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.
1. Framework purpose[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 source]
2.1. Component description[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-stm32mp1: stm32mp1 specific clock driver that supports RCC clocks.
- RCC: reset and clock controler RCC.
2.2. API description[edit source]
Documentation on the Common Clock framework can be found in the kernel documentation folder: https://www.kernel.org/doc/Documentation/driver-api/clk.rst
- Main kernel clock API
- 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 source]
3.1. Kernel configuration[edit source]
By default the Common Clock framework is activated in the kernel configuration.
3.2. Device tree configuration[edit source]
A detailed device tree configuration is described in Clock device tree configuration.
4. How to use the Common Clock framework[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://www.kernel.org/doc/Documentation/devicetree/bindings/clock/clock-bindings.txt.
Specific platform define statements that abstract hardware clock offsets are defined in dt-bindings/clock/stm32mp1-clks.h header file.
Below an example of clock association 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:
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 source]
5.1. Tracing using dynamic debug[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[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:
Board $> cat /sys/kernel/debug/clk/clk_summary clock enable_cnt prepare_cnt rate accuracy phase ---------------------------------------------------------------------------------------- clk-ext-camera 0 0 24000000 0 0 ck_usbo_48m 1 1 48000000 0 0 usbo_k 1 1 48000000 0 0 ck_dsi_phy 0 0 0 0 0 dsi_k 0 0 0 0 0 i2s_ckin 0 0 0 0 0 clk-csi 0 0 4000000 0 0 ck_csi 0 0 4000000 0 0 rng2_k 0 0 4000000 0 0 rng1_k 0 0 4000000 0 0 clk-lsi 1 1 32000 0 0 ck_lsi 1 1 32000 0 0 dac12_k 0 0 32000 0 0 clk-lse 1 1 32768 0 0 ck_lse 1 1 32768 0 0 ck_rtc 1 1 32768 0 0 cec_k 0 0 32768 0 0 clk-hsi 1 1 64000000 0 0 clk-hsi-div 1 1 64000000 0 0 ck_hsi 2 2 64000000 0 0 ck_mco1 0 0 64000000 0 0 uart8_k 0 0 64000000 0 0 uart7_k 0 0 64000000 0 0 uart6_k 0 0 64000000 0 0 uart5_k 0 0 64000000 0 0 uart4_k 1 1 64000000 0 0 usart3_k 0 0 64000000 0 0 usart2_k 0 0 64000000 0 0 usart1_k 0 0 64000000 0 0 i2c6_k 0 0 64000000 0 0 i2c4_k 1 1 64000000 0 0 i2c5_k 0 0 64000000 0 0 i2c3_k 0 0 64000000 0 0 i2c2_k 0 0 64000000 0 0 i2c1_k 0 0 64000000 0 0 spi6_k 0 0 64000000 0 0 spi5_k 0 0 64000000 0 0 spi4_k 0 0 64000000 0 0 clk-hse 1 1 24000000 0 0 ck_hse 6 6 24000000 0 0 ck_hse_rtc 0 0 1000000 0 0 stgen_k 1 1 24000000 0 0 usbphy_k 1 1 24000000 0 0 ck_per 0 0 24000000 0 0 adc12_k 0 0 24000000 0 0 ref4 1 1 24000000 0 0 pll4 1 1 508000000 0 0 pll4_r 0 0 56444445 0 0 pll4_q 1 1 50800000 0 0 ltdc_px 1 1 50800000 0 0 dsi_px 0 0 50800000 0 0 fdcan_k 0 0 50800000 0 0 pll4_p 0 0 56444445 0 0 ref3 1 1 24000000 0 0 pll3 2 3 786431640 0 0 pll3_r 1 1 98303955 0 0 sdmmc3_k 0 0 98303955 0 0 sdmmc2_k 0 0 98303955 0 0 sdmmc1_k 1 1 98303955 0 0 pll3_q 0 3 49151978 0 0 adfsdm_k 0 0 49151978 0 0 sai4_k 0 1 49151978 0 0 sai3_k 0 0 49151978 0 0 sai2_k 0 2 49151978 0 0 sai1_k 0 0 49151978 0 0 spi3_k 0 0 49151978 0 0 spi2_k 0 0 49151978 0 0 spi1_k 0 0 49151978 0 0 spdif_k 0 1 49151978 0 0 pll3_p 1 1 196607910 0 0 ck_mcu 6 19 196607910 0 0 dfsdm_k 0 1 196607910 0 0 gpiok 0 1 196607910 0 0 gpioj 0 1 196607910 0 0 gpioi 0 1 196607910 0 0 gpioh 0 1 196607910 0 0 gpiog 0 1 196607910 0 0 gpiof 0 1 196607910 0 0 gpioe 0 1 196607910 0 0 gpiod 0 1 196607910 0 0 gpioc 0 1 196607910 0 0 gpiob 0 1 196607910 0 0 gpioa 0 1 196607910 0 0 ipcc 2 2 196607910 0 0 hsem 0 0 196607910 0 0 crc2 0 0 196607910 0 0 rng2 0 0 196607910 0 0 hash2 0 0 196607910 0 0 cryp2 0 0 196607910 0 0 dcmi 0 0 196607910 0 0 sdmmc3 0 0 196607910 0 0 usbo 0 0 196607910 0 0 adc12 0 0 196607910 0 0 dmamux 1 1 196607910 0 0 dma2 1 1 196607910 0 0 dma1 1 1 196607910 0 0 pclk3 1 1 98303955 0 0 lptim5_k 0 0 98303955 0 0 lptim4_k 0 0 98303955 0 0 lptim3_k 0 0 98303955 0 0 lptim2_k 0 0 98303955 0 0 hdp 0 0 98303955 0 0 pmbctrl 0 0 98303955 0 0 tmpsens 0 0 98303955 0 0 vref 0 0 98303955 0 0 syscfg 1 1 98303955 0 0 sai4 0 0 98303955 0 0 lptim5 0 0 98303955 0 0 lptim4 0 0 98303955 0 0 lptim3 0 0 98303955 0 0 lptim2 0 0 98303955 0 0 pclk2 0 0 98303955 0 0 fdcan 0 0 98303955 0 0 dfsdm 0 0 98303955 0 0 sai3 0 0 98303955 0 0 sai2 0 0 98303955 0 0 sai1 0 0 98303955 0 0 usart6 0 0 98303955 0 0 spi5 0 0 98303955 0 0 spi4 0 0 98303955 0 0 spi1 0 0 98303955 0 0 tim17 0 0 98303955 0 0 tim16 0 0 98303955 0 0 tim15 0 0 98303955 0 0 tim8 0 0 98303955 0 0 tim1 0 0 98303955 0 0 ck2_tim 0 0 196607910 0 0 tim17_k 0 0 196607910 0 0 tim16_k 0 0 196607910 0 0 tim15_k 0 0 196607910 0 0 tim8_k 0 0 196607910 0 0 tim1_k 0 0 196607910 0 0 pclk1 0 2 98303955 0 0 lptim1_k 0 0 98303955 0 0 mdio 0 0 98303955 0 0 dac12 0 1 98303955 0 0 cec 0 0 98303955 0 0 spdif 0 0 98303955 0 0 i2c5 0 0 98303955 0 0 i2c3 0 0 98303955 0 0 i2c2 0 0 98303955 0 0 i2c1 0 0 98303955 0 0 uart8 0 0 98303955 0 0 uart7 0 0 98303955 0 0 uart5 0 0 98303955 0 0 uart4 0 0 98303955 0 0 usart3 0 0 98303955 0 0 usart2 0 0 98303955 0 0 spi3 0 0 98303955 0 0 spi2 0 0 98303955 0 0 lptim1 0 0 98303955 0 0 tim14 0 0 98303955 0 0 tim13 0 0 98303955 0 0 tim12 0 0 98303955 0 0 tim7 0 0 98303955 0 0 tim6 0 0 98303955 0 0 tim5 0 0 98303955 0 0 tim4 0 0 98303955 0 0 tim3 0 0 98303955 0 0 tim2 0 0 98303955 0 0 ck1_tim 0 1 196607910 0 0 tim14_k 0 0 196607910 0 0 tim13_k 0 0 196607910 0 0 tim12_k 0 0 196607910 0 0 tim7_k 0 0 196607910 0 0 tim6_k 0 1 196607910 0 0 tim5_k 0 0 196607910 0 0 tim4_k 0 0 196607910 0 0 tim3_k 0 0 196607910 0 0 tim2_k 0 0 196607910 0 0 ref1 2 2 24000000 0 0 pll2 2 2 533000000 0 0 pll2_r 1 1 533000000 0 0 pll2_q 0 0 533000000 0 0 gpu_k 0 0 533000000 0 0 pll2_p 1 1 266500000 0 0 ck_axi 9 10 266500000 0 0 ck_trace 0 0 133250000 0 0 ck_sys_dbg 0 0 266500000 0 0 qspi_k 1 1 266500000 0 0 fmc_k 0 0 266500000 0 0 ethstp 0 0 266500000 0 0 usbh 1 1 266500000 0 0 crc1 0 0 266500000 0 0 sdmmc2 0 0 266500000 0 0 sdmmc1 0 0 266500000 0 0 qspi 0 0 266500000 0 0 fmc 0 0 266500000 0 0 ethmac 1 1 266500000 0 0 ethrx 1 1 266500000 0 0 ethtx 1 1 266500000 0 0 gpu 0 0 266500000 0 0 mdma 1 1 266500000 0 0 bkpsram 0 0 266500000 0 0 rng1 0 0 266500000 0 0 hash1 0 0 266500000 0 0 cryp1 0 0 266500000 0 0 gpioz 0 1 266500000 0 0 tzc2 0 0 266500000 0 0 tzc1 0 0 266500000 0 0 pclk5 1 1 66625000 0 0 stgen 0 0 66625000 0 0 bsec 0 0 66625000 0 0 iwdg1 0 0 66625000 0 0 tzpc 0 0 66625000 0 0 rtcapb 2 2 66625000 0 0 usart1 0 0 66625000 0 0 i2c6 0 0 66625000 0 0 i2c4 0 0 66625000 0 0 spi6 0 0 66625000 0 0 pclk4 1 1 133250000 0 0 stgenro 0 0 133250000 0 0 usbphy 0 0 133250000 0 0 iwdg2 1 1 133250000 0 0 dsi 0 0 133250000 0 0 ltdc 0 0 133250000 0 0 pll1 1 1 650000000 0 0 pll1_p 1 1 650000000 0 0 ck_mpu 1 1 650000000 0 0 ck_mco2 0 0 650000000 0 0 clk-hse-div2 0 0 12000000 0 0 ethptp_k 0 0 0 0 0 ethck_k
In addition, each clock has a dedicated directory in which you can find the information such as prepare count, enable count, rate and accuracy:
Board $>/sys/kernel/debug/clk# cd usart2_k Board $>/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.
Board $>:/sys/kernel/debug/clk/usart2_k# cat clk_possible_parents pclk1 pll4_q ck_hsi ck_csi ck_hse
6. Source code location[edit source]
stm32mp1 clock driver source [1]
clock dt-binding interface [2]
7. To go further[edit source]
See "how to build a clock tree" in STM32MP15_clock_tree.
8. References[edit source]