Fundamentals 12 min read

Mastering Linux Kernel Clock Framework: Providers, Consumers, and Registration

This article explains the Linux kernel Common Clock Framework (CCF), covering clock providers, consumers, the underlying data structures, registration APIs for various clock types, and the essential operations to acquire and control clocks in device drivers.

Liangxu Linux
Liangxu Linux
Liangxu Linux
Mastering Linux Kernel Clock Framework: Providers, Consumers, and Registration

Clock Provider

The clock is the pulse of a SoC, governing CPU frequency, UART baud rate, I2S sample rate, I2C speed, etc. All clocks originate from one or more sources and form a clock tree, viewable via cat /sys/kernel/debug/clk/clk_summary. The kernel uses the CCF framework to manage clocks, with providers on the right, the CCF in the middle, and consumers on the left.

Clock provider diagram
Clock provider diagram

Root nodes are usually an Oscillator (active) or a Crystal (passive).

Intermediate nodes include PLL (frequency boost), Divider (frequency reduction), Mux (select one of multiple paths), and Gate (ON/OFF control).

Leaf nodes are hardware blocks that consume the clock.

Based on their characteristics, clocks are classified into six categories: fixed‑rate, gate, divider, mux, fixed‑factor, and composite.

Clock data structure diagram
Clock data structure diagram

Data Structures

All six clock types are represented by struct clk_hw. The kernel extracts the hardware block characteristics into this structure.

struct clk_hw {
  // Pointer to the corresponding clk_core instance in CCF
  struct clk_core *core;
  // Handle returned to consumers via clk_get
  struct clk *clk;
  const struct clk_init_data *init;
};

struct clk_init_data {
  const char *name;                     // Clock name
  const struct clk_ops *ops;            // Operations provided by the driver
  const char * const *parent_names;     // Names of parent clocks
  const struct clk_parent_data *parent_data;
  struct clk_hw **parent_hws;
  u8 num_parents;
  unsigned long flags;
};

For a fixed‑rate clock, the specific structure is:

struct clk_fixed_rate {
  // Embedded clk_hw (base class)
  struct clk_hw hw;
  unsigned long fixed_rate;
  unsigned long fixed_accuracy;
  u8 flags;
};

Other clock types follow a similar pattern.

Clock registration diagram
Clock registration diagram

Registration Methods

After understanding the data structures, each clock type is registered with a specific API.

Fixed‑rate clock : Simple clocks with a constant frequency. They can be described in Device Tree or registered directly.

CLK_OF_DECLARE(fixed_clk, "fixed-clock", of_fixed_clk_setup);

struct clk *clk_register_fixed_rate(struct device *dev,
                                     const char *name,
                                     const char *parent_name,
                                     unsigned long flags,
                                     unsigned long fixed_rate);

Gate clock : Can be enabled/disabled via callbacks.

struct clk *clk_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);

Divider clock : Supports setting a division factor.

struct clk *clk_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);

struct clk *clk_register_divider_table(struct device *dev,
                                       const char *name,
                                       const char *parent_name,
                                       unsigned long flags,
                                       void __iomem *reg, u8 shift, u8 width,
                                       u8 clk_divider_flags,
                                       const struct clk_div_table *table,
                                       spinlock_t *lock);

Mux clock : Selects one of several parents.

struct clk *clk_register_mux(struct device *dev,
                             const char *name,
                             const char **parent_names, u8 num_parents,
                             unsigned long flags,
                             void __iomem *reg, u8 shift, u8 width,
                             u8 clk_mux_flags, spinlock_t *lock);

struct clk *clk_register_mux_table(struct device *dev,
                                   const char *name,
                                   const char **parent_names, u8 num_parents,
                                   unsigned long flags,
                                   void __iomem *reg, u8 shift, u32 mask,
                                   u8 clk_mux_flags, u32 *table,
                                   spinlock_t *lock);

Fixed‑factor clock : Frequency = parent * mult / div.

struct clk *clk_register_fixed_factor(struct device *dev,
                                      const char *name,
                                      const char *parent_name,
                                      unsigned long flags,
                                      unsigned int mult, unsigned int div);

Composite clock : Combination of mux, divider, gate, etc.

struct clk *clk_register_composite(struct device *dev,
                                   const char *name,
                                   const char **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);

All these registration functions eventually call clk_register, which returns a struct clk * that is stored in an array and exposed to the rest of the system via of_clk_add_provider.

Clock Consumer

Getting a Clock

Consumers obtain a clock handle by name using APIs such as clk_get, devm_clk_get, clk_get_sys, of_clk_get, etc. The following shows the implementation of clk_get and the underlying of‑tree helpers.

struct clk *clk_get(struct device *dev, const char *con_id)
{
    const char *dev_id = dev ? dev_name(dev) : NULL;
    struct clk *clk;

    if (dev) {
        /* Scan "clock-names" and match con_id */
        clk = __of_clk_get_by_name(dev->of_node, dev_id, con_id);
        if (!IS_ERR(clk) || PTR_ERR(clk) == -EPROBE_DEFER)
            return clk;
    }
    return clk_get_sys(dev_id, con_id);
}
struct clk *of_clk_get(struct device_node *np, int index)
{
    struct of_phandle_args clkspec;
    struct clk *clk;
    int rc;

    if (index < 0)
        return ERR_PTR(-EINVAL);

    rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index, &clkspec);
    if (rc)
        return ERR_PTR(rc);
    clk = of_clk_get_from_provider(&clkspec);
    of_node_put(clkspec.np);
    return clk;
}
struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec)
{
    struct of_clk_provider *provider;
    struct clk *clk = ERR_PTR(-ENOENT);

    mutex_lock(&of_clk_lock);
    list_for_each_entry(provider, &of_clk_providers, link) {
        if (provider->node == clkspec->np)
            clk = provider->get(clkspec, provider->data);
        if (!IS_ERR(clk))
            break;
    }
    mutex_unlock(&of_clk_lock);
    return clk;
}

These functions link the consumer side to the provider side via of_clk_add_provider.

Operating a Clock

// Prepare/enable and cleanup functions (may sleep)
int clk_prepare(struct clk *clk);
void clk_unprepare(struct clk *clk);

// Enable/disable without sleeping
static inline int clk_enable(struct clk *clk);
static inline void clk_disable(struct clk *clk);

// Rate manipulation
static inline unsigned long clk_get_rate(struct clk *clk);
static inline int clk_set_rate(struct clk *clk, unsigned long rate);
static inline long clk_round_rate(struct clk *clk, unsigned long rate);

// Parent selection
static inline int clk_set_parent(struct clk *clk, struct clk *parent);
static inline struct clk *clk_get_parent(struct clk *clk);

// Combined prepare+enable and disable+unprepare helpers
static inline int clk_prepare_enable(struct clk *clk);
static inline void clk_disable_unprepare(struct clk *clk);

Summary

The Common Clock Framework provides a unified way to describe, register, and use clocks in the Linux kernel. Clock providers define the hardware topology and expose registration functions for various clock types, while consumers acquire clocks via standardized lookup APIs and control them through a consistent set of operations.

Overall clock framework summary
Overall clock framework summary
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

CCFDevice DriversClock Framework
Liangxu Linux
Written by

Liangxu Linux

Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.