Skip to main content

TECH VEDA

Embedded Linux on Edge-AI 23rd Sept 2026 enrollingLinux kernel & Device drivers starts on 24th Oct 2026 enrollingCorporate on-site training - Submit proposal Pick your modulessignup for free monthly live Masterclass Register
Deep Dives

The Linux Common Clock Framework: How Clocks Are Modeled and Debugged

A deep dive into the Linux Common Clock Framework: struct clk_ops, the prepare/enable split, devm_clk_get_enabled, and debugging clocks disabled at boot via clk_summary.

The Linux Common Clock Framework: How Clocks Are Modeled and Debugged

The Linux Common Clock Framework splits every clock into two halves: a generic core (struct clk_core) that does accounting and locking, and a hardware-specific set of callbacks (struct clk_ops) that touch registers. Drivers consume clocks through a small API such as clk_prepare_enable(), while clock providers implement .enable, .set_rate and similar operations. Understanding the prepare-versus-enable split and the automatic disabling of unused clocks explains most of the clock bugs seen during board bring-up.

On almost every embedded SoC, a peripheral does nothing until its clock is running. The Common Clock Framework is the kernel subsystem that models those clocks, tracks who is using them, and turns them on and off in the right order. It is enabled by the CONFIG_COMMON_CLK option and its core lives in drivers/clk/clk.c. This article traces how the framework is structured, how a driver actually consumes a clock, and how to debug the single most common clock failure during board bring-up: a peripheral that works from the bootloader and then stops partway through boot.

What a clock is, and why drivers need one

In digital hardware, a clock is a periodic signal that steps a synchronous circuit forward one operation at a time. Every synchronous block on a SoC advances its internal state on the edges of a clock: a UART, an SPI controller, a timer, a display pipeline and each CPU core all run off one. The frequency of that signal sets how fast the block runs, and for many peripherals it determines correctness. A UART divides its input clock to produce a baud rate, an I2C controller derives its bus speed from its clock, and an MMC host times its data lines against one. If the frequency is wrong, the peripheral either runs at the wrong speed or does not communicate at all.

These clocks are generated on-chip by oscillators and phase-locked loops (PLLs), then shaped by dividers, multiplexers and gates before they reach a peripheral. To save power, the hardware lets software switch most clocks off when the block they feed is idle and switch them back on when it is needed. A clock that is gated off makes its peripheral unresponsive: registers may read back as zero, writes are lost, and on some buses the access can hang.

This is why a driver cannot simply map a peripheral’s registers and start using it. Before the first register access, the driver must ensure the peripheral’s clock is running at the correct rate, and to let the system idle cleanly it should release that clock when the device is removed or suspended. Managing which clocks are on, at what rate, and on behalf of which driver, is exactly the job the Common Clock Framework exists to coordinate.

Why the kernel needs a Common Clock Framework

Before this framework existed, every ARM platform carried its own private clock code. The result was a large amount of duplicated logic for reference counting, parent-child relationships, and rate calculation. The Common Clock Framework unifies that shared accounting in one place and leaves only the register-level details to each SoC. A clock tree on a modern SoC can have hundreds of nodes: oscillators, PLLs, dividers, multiplexers and gates, arranged as a tree where a child clock derives its rate from its parent. The framework represents that whole tree with one common structure and lets each node plug in its own behaviour.

The two halves: struct clk_core and struct clk_ops

The interface is deliberately split into two halves, each hidden from the details of the other. The first half is the generic core. A trimmed version from drivers/clk/clk.c looks like this:

struct clk_core {
        const char              *name;
        const struct clk_ops    *ops;
        struct clk_hw           *hw;
        struct module           *owner;
        struct clk_core         *parent;
        const char              **parent_names;
        struct clk_core         **parents;
        u8                      num_parents;
        u8                      new_parent_index;
        ...
};

This holds the tree topology and the framework-level bookkeeping such as enable and prepare counts. The second half is the hardware-specific behaviour, expressed as function pointers in struct clk_ops (declared in include/linux/clk-provider.h):

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);
        int             (*is_enabled)(struct clk_hw *hw);
        unsigned long   (*recalc_rate)(struct clk_hw *hw,
                                       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);
        ...
};

The two halves are tied together by struct clk_hw, which is embedded inside each hardware-specific structure and pointed to from the core. Not every callback is mandatory. A pure gate clock only needs .enable, .disable and .is_enabled; a clock that can change frequency needs .recalc_rate, .set_rate and either .determine_rate or a round-rate style callback; a multiplexer needs .set_parent and .get_parent.

A minimal gate clock in source

The simplest real clock type is a gate: a single register bit that turns a clock on or off. Its hardware structure, from drivers/clk/clk-gate.c, embeds a clk_hw alongside the register address and bit index:

struct clk_gate {
        struct clk_hw   hw;
        void __iomem    *reg;
        u8              bit_idx;
        ...
};

The framework recovers the full structure from the generic clk_hw pointer using container_of, wrapped in a helper macro:

#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw)

When a driver calls clk_enable(), the core resolves the operation to this clock’s .enable callback, which recovers the struct clk_gate and sets its control bit. This container_of pattern is used by every clock hardware type in the framework, so once you recognise it you can read any SoC clock driver. You register your own clock at run time with a hardware-specific helper such as clk_hw_register_gate() (or the older clk_register()), which populates the core structure and links it into the tree.

Prepare versus enable, and why there are two steps

The part of the framework that confuses newcomers most is that turning a clock on is a two-stage operation: clk_prepare() then clk_enable(). The reason is locking. Enable runs under a spinlock and must never sleep, so it is safe to call from atomic context, for example from an interrupt handler. It is meant only for fast register writes. Prepare runs under a mutex and is allowed to sleep. That matters for clocks whose enable path is slow or blocking: a PLL that needs to wait for lock, or a clock controlled over an I2C bus where the register access itself sleeps.

Because of this rule, driver code must call prepare from a context that can sleep, and only then call enable. The framework offers a convenience wrapper, clk_prepare_enable(), that does both, and a matching clk_disable_unprepare() for teardown. The convenience wrapper is safe only where sleeping is allowed, which is the normal case in a probe function.

The consumer side: getting and enabling a clock

A device driver does not touch clk_ops at all. It uses the consumer API declared in include/linux/clk.h. The classic sequence is to look up the clock by name, then prepare and enable it:

struct clk *clk;

clk = clk_get(dev, "bus");
if (IS_ERR(clk))
        return PTR_ERR(clk);

ret = clk_prepare_enable(clk);
if (ret)
        return ret;

Modern drivers should prefer the managed helper devm_clk_get_enabled(), which looks up the clock, calls clk_prepare_enable(), and registers automatic cleanup so the clock is disabled and unprepared when the device is removed:

clk = devm_clk_get_enabled(dev, "bus");
if (IS_ERR(clk))
        return PTR_ERR(clk);

This single call removes an entire class of leak and ordering bugs because the disable path is handled for you. If you understand the two-stage model behind it, you also understand why this helper must run in a sleepable context. Engineers who want to go deeper into how drivers acquire and manage such resources will find this covered in TECH VEDA’s Linux device drivers training.

A real debug session: the clock that dies at boot

Here is a failure seen constantly during bring-up. A UART or a display works while the bootloader has its clock running, then goes silent partway through kernel boot. The cause is a framework feature, not a bug. Late in boot the framework runs clk_disable_unused() as a late_initcall and turns off every clock whose enable count is still zero. A clock left on by the bootloader but never claimed by a driver with clk_prepare_enable() has an enable count of zero, so the framework considers it unused and disables it.

The first diagnostic step is to see which clocks the framework is turning off. Boot with the clock trace event printed to the console:

raghu@techveda.org:~$ cat /proc/cmdline
console=ttyS0,115200 tp_printk trace_event=clk:clk_disable

The disable events for your peripheral’s clock will appear right before it stops working. To confirm the root cause, inspect the enable count directly, which brings us to the framework’s main debugging surface.

Reading clk_summary

When CONFIG_COMMON_CLK and debugfs are enabled, the framework exports the whole clock tree in one readable file:

raghu@techveda.org:~$ cat /sys/kernel/debug/clk/clk_summary
   clock          enable_cnt  prepare_cnt   rate
 osc24m                   3            3    24000000
    pll1                  2            2   996000000
       uart_clk           0            0    48000000

Each clock directory under /sys/kernel/debug/clk/ also exposes individual read-only files, including clk_rate, clk_flags, clk_prepare_count, clk_enable_count and clk_notifier_count. In the output above, the uart_clk line shows an enable count of zero even though the UART is transmitting, which is the exact signature of a clock left on by the bootloader and about to be disabled. The correct fix is to make the driver claim the clock with devm_clk_get_enabled() during probe so its enable count is non-zero.

For triage only, you can keep boot clocks on by adding clk_ignore_unused to the kernel command line, or a clock provider can set the CLK_IGNORE_UNUSED flag on a specific clock. These are useful while a driver is being fixed, but they are not the real solution, because they leave power management incomplete.

Key takeaways

  • A clock is the periodic signal that drives a synchronous peripheral; a driver must ensure the clock runs at the correct rate before touching the hardware.
  • The Common Clock Framework separates a generic core (struct clk_core) from hardware callbacks (struct clk_ops), joined by struct clk_hw.
  • Turning a clock on has two stages: clk_prepare() may sleep and runs under a mutex; clk_enable() is atomic and runs under a spinlock.
  • Drivers consume clocks through clk.h; prefer devm_clk_get_enabled() so prepare, enable and cleanup are handled automatically.
  • A peripheral that dies partway through boot is usually a clock disabled by clk_disable_unused() because no driver claimed it.
  • /sys/kernel/debug/clk/clk_summary shows enable and prepare counts for the whole tree and is the fastest way to confirm the cause.
Was this worth your time?

Frequently asked questions

What is a clock in an embedded system, and why does a driver need it?
A clock is a periodic signal that steps a synchronous peripheral forward and sets the rate at which it runs. A UART, SPI, I2C or MMC block does nothing, or communicates at the wrong speed, if its clock is off or set to the wrong frequency. A driver must therefore enable the peripheral’s clock at the correct rate before accessing its registers.

What is the difference between clk_prepare and clk_enable?
Prepare runs under a mutex and is allowed to sleep, so it suits slow operations such as waiting for a PLL to lock or accessing a clock over an I2C bus. Enable runs under a spinlock, must not sleep, and is safe to call from atomic context such as an interrupt handler. Prepare must always be called before enable.

Why does my peripheral stop working partway through boot?
Late in boot the framework calls clk_disable_unused(), which turns off any clock whose enable count is still zero. If the bootloader enabled the clock but no driver claimed it with clk_prepare_enable(), the framework treats it as unused and disables it. The fix is to have the driver acquire and enable the clock in its probe function.

How do I see the state of all clocks on a running system?
Read /sys/kernel/debug/clk/clk_summary, which lists every registered clock with its enable count, prepare count and rate. Individual per-clock files under /sys/kernel/debug/clk/ expose values such as clk_rate, clk_flags, clk_prepare_count and clk_enable_count.

Further reading

RB
Raghu Bharadwaj

Founder, TECH VEDA — 20+ years teaching the Linux kernel, device drivers and embedded systems.

Follow on LinkedIn

Get new posts by email

Kernel, embedded Linux and AI-era engineering — a few sharp reads a month. No spam.

We email occasionally and never share your address.