February 24, 2020
Function | Description |
---|---|
Gate | The clock source can be turned off, or turned on. This functionnality is typically useful to save power. |
Parent | The clock source can have a parent. If it doesn't have a parent, it is known as a root clock. |
Multiplexer | The clock source can change its parent, choosing it among a list of possible parent clocks. |
Multiplier | The clock generates its output rate by multiplying the rate of its parent by a given factor. |
Divider | The clock generates its output rate by dividing the rate of its parent by a given factor. |
The Linux clock framework defines two kind of devices:
#clock-cells
which defines the dimension of the array of clocks. For example,
if a node clock-provider
includes a
property #clock-cells=<2>
, it means the provider
implements an array of clocks of dimension 2. Consequently, a
clock will be referenced by a phandle to the clock provider node
followed by two integers, e.g <&clock-provider 34 45>
. A dimension
of zero is supported and means that the provider only provides one
clock.clocks
property that lists its input clocks. It
also includes an optional clock-names
property that
gives a name to the consumed clocks. This name can be used from the
consumer driver to get a clock handle.All the clock sources represented in blue in Figure 1 are implemented by a single provider that you can see in Figure 2. It is interesting to note that the parent-child relationships that exist in the hardware clock tree do not show up in the Linux device tree representation. Those relationships are usually part of the provider's implementation. On the other hand, the provider-consumer relationships are represented in the Linux device tree by the clocks properties.
Function | Description |
---|---|
int clk_hw_register( struct device *dev, struct clk_hw *hw) |
Add a clock source to the global tree of clock sources. This is the basic registration function that can be used if you need to build your clock from scratch. Otherwise, use one of the next functions. |
struct clk_hw * clk_hw_register_fixed_rate( struct device *dev, const char *name, const char *parent_name, unsigned long flags, unsigned long fixed_rate) |
Registers a fixed rate clock specified by fixed_rate . |
struct clk_hw * clk_hw_register_gate( struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 bit_idx, u8 clk_gate_flags, spinlock_t *lock) |
Register a gating clock. The gate is controlled by the value of the bit at index bit_idx in the register reg . |
struct clk_hw * clk_hw_register_divider( struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags, spinlock_t *lock) |
Register a divider clock. The division factor is in the field of width bits in the register reg starting at bit shift |
struct clk_hw * clk_hw_register_mux( struct device *dev, const char *name, const char * const *parent_names, u8 num_parents, unsigned long flags, void __iomem *reg, u8 shift, u8 width, u8 clk_mux_flags, spinlock_t *lock) |
Register a multiplexer clock. The parent is selected by the
field of width bits starting at bit
index shift of the
register reg . Depending on the
flags clk_mux_flags , this the field encodes the
selected parent as a 0-based index, or as a 1-based index, or as
a one bit per possible parent. |
struct clk_hw * clk_hw_register_mux_table( struct device *dev, const char *name, const char * const *parent_names, u8 num_parents, unsigned long flags, void __iomem *reg, u8 shift, u32 mask, u8 clk_mux_flags, u32 *table, spinlock_t *lock) |
This is a variant of the previous function that takes an extra table as an input parameter that specifies what field value should be used to select the parent at a given index. |
struct clk_hw * clk_hw_register_fixed_factor( struct device *dev, const char *name, const char *parent_name, unsigned long flags, unsigned int mult, unsigned int div) |
Registers a fixed factor clock. The output frequency of this clock is the
parent clock rate divided by div and multiplied by mult . |
struct clk_hw * clk_hw_register_fractional_divider( struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth, u8 clk_divider_flags, spinlock_t *lock) |
Registers a fractional divider clock. Numerator is defined
by the field of mwidth bits at
postition mshift in the register reg ,
while denominator is defined by the field of nwidth
bits at postition nshift in the same register. |
struct clk_hw * clk_hw_register_composite( struct device *dev, const char *name, const char *const *parent_names, int num_parents, struct clk_hw *mux_hw, const struct clk_ops *mux_ops, struct clk_hw *rate_hw, const struct clk_ops *rate_ops, struct clk_hw *gate_hw, const struct clk_ops *gate_ops, unsigned long flags) |
Registers a composite clock that can combine a gate,
a multiplexer, and some rate setting functionnality. Input parameters can be passed using the following structures:
/* Mux data and operations */ struct clk_mux { struct clk_hw hw; void __iomem *reg; u32 *table; u32 mask; u8 shift; u8 flags; spinlock_t *lock; }; extern const struct clk_ops clk_mux_ops; /* Divider data and operations */ struct clk_divider { struct clk_hw hw; void __iomem *reg; u8 shift; u8 width; u8 flags; const struct clk_div_table *table; spinlock_t *lock; }; extern const struct clk_ops clk_divider_ops; /* Gate data and operations */ struct clk_gate { struct clk_hw hw; void __iomem *reg; u8 bit_idx; u8 flags; spinlock_t *lock; }; extern const struct clk_ops clk_gate_ops; |
struct clk_hw * clk_hw_register_gpio_gate( struct device *dev, const char *name, const char *parent_name, struct gpio_desc *gpiod, unsigned long flags) |
Registers a gating clock controlled by a GPIO. |
struct clk_hw * clk_hw_register_gpio_mux( struct device *dev, const char *name, const char * const *parent_names, u8 num_parents, struct gpio_desc *gpiod, unsigned long flags) |
Registers a multiplexer clock controlled by a GPIO. |
clk_hw_register()
function.
The struct clk_hw
passed as an input
parameter should contain the information defining the clock:
struct clk_hw { struct clk_core *core; struct clk *clk; const struct clk_init_data *init; };The fields
core
and clk
should be initialized to NULL
, and the init
field should contain all the clock initialization data:
struct clk_init_data { const char *name; const struct clk_ops *ops; /* Only one of the following three should be assigned */ const char * const *parent_names; const struct clk_parent_data *parent_data; const struct clk_hw **parent_hws; u8 num_parents; unsigned long flags; };As seen in the above structure, the clock is identified by a global name, operations, an array of parents, and some flags. Below are the most interesting operations in struct clk_ops:
/** * struct clk_ops - Callback operations for hardware clocks; these are to * be provided by the clock implementation, and will be called by drivers * through the clk_* api. * * @prepare: Prepare the clock for enabling. This must not return until * the clock is fully prepared, and it's safe to call clk_enable. * This callback is intended to allow clock implementations to * do any initialisation that may sleep. Called with * prepare_lock held. * * @unprepare: Release the clock from its prepared state. This will typically * undo any work done in the @prepare callback. Called with * prepare_lock held. * * @enable: Enable the clock atomically. This must not return until the * clock is generating a valid clock signal, usable by consumer * devices. Called with enable_lock held. This function must not * sleep. * * @disable: Disable the clock atomically. Called with enable_lock held. * This function must not sleep. * * @recalc_rate Recalculate the rate of this clock, by querying hardware. The * parent rate is an input parameter. It is up to the caller to * ensure that the prepare_mutex is held across this call. * Returns the calculated rate. Optional, but recommended - if * this op is not set then clock rate will be initialized to 0. * * @round_rate: Given a target rate as input, returns the closest rate actually * supported by the clock. The parent rate is an input/output * parameter. * * @determine_rate: Given a target rate as input, returns the closest rate * actually supported by the clock, and optionally the parent clock * that should be used to provide the clock rate. * * @set_parent: Change the input source of this clock; for clocks with multiple * possible parents specify a new parent by passing in the index * as a u8 corresponding to the parent in either the .parent_names * or .parents arrays. This function in affect translates an * array index into the value programmed into the hardware. * Returns 0 on success, -EERROR otherwise. * * @get_parent: Queries the hardware to determine the parent of a clock. The * return value is a u8 which specifies the index corresponding to * the parent clock. This index can be applied to either the * .parent_names or .parents arrays. In short, this function * translates the parent value read from hardware into an array * index. Currently only called when the clock is initialized by * __clk_init. This callback is mandatory for clocks with * multiple parents. It is optional (and unnecessary) for clocks * with 0 or 1 parents. * * @set_rate: Change the rate of this clock. The requested rate is specified * by the second argument, which should typically be the return * of .round_rate call. The third argument gives the parent rate * which is likely helpful for most .set_rate implementation. * Returns 0 on success, -EERROR otherwise. * * The clk_enable/clk_disable and clk_prepare/clk_unprepare pairs allow * implementations to split any work between atomic (enable) and sleepable * (prepare) contexts. If enabling a clock requires code that might sleep, * this must be done in clk_prepare. Clock enable code that will never be * called in a sleepable context may be implemented in clk_enable. * * Typically, drivers will call clk_prepare when a clock may be needed later * (eg. when a device is opened), and clk_enable when the clock is actually * required (eg. from an interrupt). Note that clk_prepare MUST have been * called before clk_enable. */ struct clk_ops { int (*prepare)(struct clk_hw *hw); void (*unprepare)(struct clk_hw *hw); int (*enable)(struct clk_hw *hw); void (*disable)(struct clk_hw *hw); unsigned long (*recalc_rate)(struct clk_hw *hw, unsigned long parent_rate); long (*round_rate)(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate); int (*determine_rate)(struct clk_hw *hw, struct clk_rate_request *req); int (*set_parent)(struct clk_hw *hw, u8 index); u8 (*get_parent)(struct clk_hw *hw); int (*set_rate)(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate); /*...*/ };
int of_clk_add_hw_provider( struct device_node *np, struct clk_hw *(*get)(struct of_phandle_args *clkspec, void *data), void *data);The provided
get
function pointer converts a clock specifier to an actual clock source object. The Linux Kernel provides two implementations for this get
function:
Function | Description |
---|---|
struct clk_hw * of_clk_hw_simple_get( struct of_phandle_args *clkspec, void *data) |
get function that can be used for a provider of dimension 0. Then the data passed to of_clk_add_hw_provider() has to be a pointer to the clk_hw which is the unique clock implemented by the provider. |
struct clk_hw * of_clk_hw_onecell_get( struct of_phandle_args *clkspec, void *data) |
get function that can be used for a provider of dimension 1. Then the data passed to of_clk_add_hw_provider() has to be a pointer to struct clk_hw_onecell_data .
struct clk_hw_onecell_data { unsigned int num; struct clk_hw *hws[]; }; |
Any node in the device tree that includes a clocks property becomes a clock consumer node and thus allows the code of its associated driver to control the referenced clocks using the Clock Consumer API:
Function | Description |
---|---|
struct clk *of_clk_get_by_name( struct device_node *np, const char *name) |
Get a clock object by name. The name is specified in the device tree node by the clock-names property. |
int clk_prepare(struct clk *clk) |
Prepare the clock to be enabled. This function cannot be called from atomic contexts. |
int clk_enable(struct clk *clk) |
Enable the clock, i.e turn it on. This function may be called from atomic contexts. |
int clk_disable(struct clk *clk) |
Disable the clock, i.e turn it off. This function may be called from atomic contexts. |
int clk_unprepare(struct clk *clk) |
Unprepare the clock. This function cannot be called from atomic contexts. |
int clk_set_parent( struct clk *clk, struct clk *parent) |
Selects the parent of the clock, among its list of possible parents. This function configures the multiplexer. |
int clk_set_rate( struct clk *clk, unsigned long rate) |
Modifies the rate of the clock. |
Note that calling the clk_set_rate()
function can do
pretty complex stuff like go through the entire clock tree to find the
better configuration, potentially reconfiguring multipliers, dividers
and multiplexers.
The framework also detects what clock nodes should be turned off. For example, disabling the UART controller's input clock will go through the clock tree and turn off all the nodes that are not used anymore, thus helping to save power.
# mount -t debugfs none /sys/kernel/debug # 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_kNote that the above information only shows the parent that is currently selected by each multiplexer. For example, you can only see that the current parent of the clock
usart2_k
is ck_hsi
. To get the list of all the possible parents of usart2_k
, you
need to look at its clock entry in debugfs:
/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 /sys/kernel/debug/clk/usart2_k# cat clk_possible_parents pclk1 pll4_q ck_hsi ck_csi ck_hse