CCF linux kernel
clk_hw clk_ops
free linux kernel development course
free linux device drivers course
free embedded systems course
free embedded linux course
The Linux Common Clock Framework (CCF) is the subsystem that lets the kernel enable, disable, and change the rate of every clock line on a System on Chip using one uniform API, instead of a different hand-written API per platform. If you are learning Linux device driver development, understanding the CCF is essential, because almost every peripheral driver — I2C, SPI, UART, display, and audio controllers — depends on a clock being turned on before it can be used at all. This lecture is part of our free Linux kernel development course and free embedded Linux course, and it lays the conceptual foundation before we write a real clock provider driver in the next lecture.
What You Will Learn
- Why embedded systems need a clock management subsystem
- The problem the Linux Common Clock Framework was built to solve
- The difference between a clock provider and a clock consumer
- The core CCF data structures:
clk_hw,clk_ops,clk_init_data,clk, andclk_core - How to build and test a minimal fixed-rate clock provider module
- Common mistakes, best practices, and security considerations around clock drivers
Prerequisites
- Comfortable writing and building basic Linux kernel modules
- Familiarity with platform drivers and the Device Tree (covered earlier in this course)
- A Linux kernel 6.x source tree, or a board/VM you can build and boot modules on
- Basic C and pointer/struct handling
Why Embedded Systems Need Clock Management
Every digital circuit in a System on Chip — CPU cores, memory controllers, UARTs, timers, display engines — needs a clock signal to drive its internal logic. Clocks in embedded systems serve two main purposes. The first is synchronization: registers, state machines, and buses all need a common timing reference to move data correctly. The second is power management: an SoC can save a significant amount of power by gating off the clock to a block that is currently idle, and by scaling clock rates up or down depending on system load.
Because of this, the Linux kernel has always needed some way for driver code to ask “turn my clock on,” “what rate is my clock running at,” or “set my clock to this rate.” The question was never whether this API should exist — it was how to implement it without duplicating the same logic across dozens of SoC families.
The Problem Before the Linux Common Clock Framework
In the early days, each platform (each SoC family supported by the kernel) implemented its own version of the basic clock API — its own way to get a clock reference, enable or disable it, and get or set its rate. Since every machine directory wrote this logic independently, the kernel accumulated many near-identical files, each reinventing the same bookkeeping around a completely different piece of hardware underneath. This meant more code to maintain, more bugs to fix in parallel across platforms, and a driver-facing API that was not guaranteed to behave the same way from one SoC to the next.
What Is the Linux Common Clock Framework (CCF)?
The Common Clock Framework is the kernel subsystem that unifies clock management under one hardware-independent interface. It is enabled by the CONFIG_COMMON_CLK kernel configuration option, and it lives primarily in drivers/clk/clk.c, with its public data structures declared in include/linux/clk-provider.h (provider-facing) and include/linux/clk.h (consumer-facing).
Note: the CCF does not manage Real-Time Clocks (RTCs) or timekeeping devices — those belong to their own separate kernel subsystems. A “clock” in the CCF sense is a hardware clock line: an oscillator, PLL, divider, gate, or mux feeding a peripheral or CPU core.
(drivers/clk/*)
drivers/clk/clk.c
(I2C, SPI, UART…)
Clock Providers vs Clock Consumers
| Role | Who implements it | What it does |
|---|---|---|
| Provider | SoC clock driver author | Connects to the CCF and exposes the real clock tree, matching the SoC datasheet |
| Consumer | Peripheral driver author | Requests a clock by name/index and calls the common clk_*() API on it |
| Both | Some complex drivers | Provides one or more clocks while also consuming a parent clock supplied by another provider |
The Two Halves of the CCF
The CCF is deliberately split into two halves that never need to know the internal details of each other:
- The framework core — generic code you never modify. It defines
struct clkand implements every consumer-facing function (clk_get,clk_enable,clk_set_rate, and so on) on top of whateverclk_opsa provider supplies. - The hardware-specific half — the code you write for a new piece of clock hardware. It supplies a
struct clk_opswith callbacks the core will invoke, plus a hardware-specific structure that wraps astruct clk_hw.
CCF Core Data Structures Explained
| Structure | Used by | Purpose |
|---|---|---|
struct clk_hw |
Provider | Base handle every hardware clock type embeds; links a hardware-specific struct back into the core |
struct clk_ops |
Provider | Hardware-specific callbacks (enable, disable, recalc_rate, set_rate…) invoked by the core |
struct clk_init_data |
Provider | Static registration data (name, ops, parent names, flags) handed to the core once at registration time |
struct clk |
Consumer | What consumer drivers actually hold a pointer to; every consumer API call takes this |
struct clk_core |
Internal | The framework’s own internal representation of a clock; consumers and providers never touch it directly |
Understanding struct clk_hw
struct clk_hw is never used on its own. A provider driver always embeds it inside its own hardware-specific structure, then uses the standard kernel container_of() pattern to get back from a clk_hw pointer to the full driver structure. This is the same embedding pattern used throughout the kernel for base/derived structures.
/* Every clock provider follows this embedding pattern */
struct ep_fixed_clk {
struct clk_hw hw; /* must be embedded, not pointed to */
unsigned long rate; /* our own hardware-specific data */
};
/* Recover our structure from the clk_hw pointer the core gives us */
#define to_ep_fixed_clk(_hw) container_of(_hw, struct ep_fixed_clk, hw)
Understanding struct clk_ops
Every callback in clk_ops receives a struct clk_hw * as its first argument — never a raw hardware pointer. Only a handful of callbacks are mandatory, and which ones depends on the clock type you are modeling (a fixed oscillator needs far fewer callbacks than a programmable PLL).
static unsigned long ep_fixed_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct ep_fixed_clk *fclk = to_ep_fixed_clk(hw);
return fclk->rate;
}
static const struct clk_ops ep_fixed_clk_ops = {
.recalc_rate = ep_fixed_clk_recalc_rate,
};
Original Example: A Minimal Fixed-Rate Clock Provider
To make this concrete, here is an original, minimal clock provider module — ep_fixed_clk — that registers one fixed-rate clock line with the CCF and prints its rate through the consumer API. This keeps the focus on how the pieces connect, rather than on real SoC register access.
#include <linux/module.h>
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/err.h>
#define EP_FIXED_RATE_HZ 24000000UL /* 24 MHz, just for the demo */
struct ep_fixed_clk {
struct clk_hw hw;
unsigned long rate;
};
#define to_ep_fixed_clk(_hw) container_of(_hw, struct ep_fixed_clk, hw)
static unsigned long ep_fixed_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct ep_fixed_clk *fclk = to_ep_fixed_clk(hw);
return fclk->rate;
}
static const struct clk_ops ep_fixed_clk_ops = {
.recalc_rate = ep_fixed_clk_recalc_rate,
};
static struct ep_fixed_clk ep_clk = {
.rate = EP_FIXED_RATE_HZ,
};
static struct clk_hw *ep_hw;
static int __init ep_fixed_clk_init(void)
{
struct clk_init_data init = {};
struct clk *consumer_clk;
int ret;
init.name = "ep_fixed_clk24m";
init.ops = &ep_fixed_clk_ops;
init.flags = 0;
ep_clk.hw.init = &init;
ep_hw = &ep_clk.hw;
ret = clk_hw_register(NULL, ep_hw);
if (ret) {
pr_err("ep_fixed_clk: registration failed (%d)\n", ret);
return ret;
}
/* Act as our own consumer, purely for this demo */
consumer_clk = ep_hw->clk;
pr_info("ep_fixed_clk: registered '%s' at %lu Hz\n",
clk_hw_get_name(ep_hw), clk_get_rate(consumer_clk));
return 0;
}
static void __exit ep_fixed_clk_exit(void)
{
clk_hw_unregister(ep_hw);
pr_info("ep_fixed_clk: unregistered\n");
}
module_init(ep_fixed_clk_init);
module_exit(ep_fixed_clk_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala minimal CCF fixed-rate clock demo");
Build and Run Steps
# Makefile
obj-m += ep_fixed_clk.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
$ make
$ sudo insmod ep_fixed_clk.ko
$ dmesg | tail -n 3
Expected dmesg Output
[ 1234.567890] ep_fixed_clk: registered 'ep_fixed_clk24m' at 24000000 Hz
Removing the module cleanly unregisters the clock:
$ sudo rmmod ep_fixed_clk
$ dmesg | tail -n 1
[ 1240.112233] ep_fixed_clk: unregistered
Common Mistakes When Working With the CCF
- Pointing to a clk_hw instead of embedding it —
struct clk_hwmust be a member of your driver structure, not a separately allocated pointer, orcontainer_of()will not work. - Forgetting to set
hw.initbefore registration — the core readsclk_init_dataonly duringclk_hw_register(); setting it afterward has no effect. - Implementing callbacks your clock type doesn’t need — a plain fixed clock never needs
set_rate; adding it just invites inconsistent state. - Ignoring the return value of registration functions — a failed
clk_hw_register()leaves no valid clock behind, and any consumer that depends on it should fail probing with-EPROBE_DEFERor a real error, not silently continue.
Best Practices for Clock Provider Drivers
- Prefer the devres-managed variants, such as
devm_clk_hw_register(), in real platform drivers so cleanup happens automatically on probe failure or driver removal. - Keep provider structures private to the driver file; never let consumer code reach into a
clk_hw-embedding structure directly. - Register clocks with Device Tree–based lookup (
of_clk_add_hw_provider()) rather than relying on global clock names wherever possible. - Implement only the
clk_opscallbacks that map to real hardware behavior — an unimplemented callback should simply be absent, not a stub that does nothing.
Real-World Use Cases
The CCF is used across virtually every modern ARM, RISC-V, and MIPS SoC supported by Linux. A typical SoC clock tree — visible today under /sys/kernel/debug/clk/clk_summary — includes a crystal oscillator feeding one or more PLLs, which in turn feed dividers and gates for each peripheral block: UARTs, I2C/SPI controllers, display controllers, audio codecs, and CPU cores. Runtime PM frameworks and CPU frequency scaling drivers both call directly into the CCF consumer API to gate clocks off when a device is idle and to change CPU clock rates for performance scaling.
Performance Considerations
Clock rate changes and clock gating operations are not free — reparenting a clock or waiting for a PLL to lock can take microseconds to milliseconds depending on the hardware. Provider drivers should avoid unnecessary rate recalculations in hot paths, and consumer drivers should cache a clock reference obtained once at probe time rather than repeatedly calling clk_get().
Security Considerations
Because a misbehaving or malicious consumer could otherwise request a clock rate or state that damages hardware or destabilizes clocks shared by other devices, the CCF enforces rate constraints (via clk_set_rate_range() and similar APIs) and access through the driver core’s device model rather than exposing raw register access to userspace. Debugfs clock summary output is read-only and does not permit rate changes from a user without kernel driver access.
Summary and Key Takeaways
- The Linux Common Clock Framework replaces duplicated, platform-specific clock code with one uniform API, enabled by
CONFIG_COMMON_CLK. - Providers implement hardware access; consumers call the common
clk_*()API — a driver can be both. struct clk_hwties the hardware-specific half to the framework core; it must always be embedded, never pointed to separately.struct clk_opssupplies only the callbacks a given clock type actually needs.struct clk_init_datais consumed once, at registration time.
Conclusion
This lecture built the conceptual foundation of the Linux Common Clock Framework: why it exists, how providers and consumers divide responsibility, and how the five core data structures fit together, backed by a minimal working fixed-rate clock provider you can build and test today. In the next lecture of this free Linux kernel development course, we will write a complete Device Tree–based clock provider driver with multiple clock outputs and explore the consumer-side API in depth.
Frequently Asked Questions
What is the Linux Common Clock Framework used for?
It provides one hardware-independent API for enabling, disabling, and changing the rate of clock lines on an SoC, replacing platform-specific clock code that used to be duplicated across machine directories.
Is the Common Clock Framework the same as RTC support?
No. Real-Time Clocks and timekeeping devices are handled by separate kernel subsystems; the CCF only manages hardware clock lines that drive digital logic.
What kernel config option enables the CCF?
CONFIG_COMMON_CLK must be enabled for CCF support to be compiled into the kernel.
What is the difference between a clock provider and a clock consumer?
A provider driver implements the hardware-specific callbacks for a real clock device; a consumer driver simply calls the common clk_*() API to use a clock it does not own.
Why must struct clk_hw be embedded instead of pointed to?
The kernel recovers your driver’s full structure from a clk_hw pointer using container_of(), which only works correctly when clk_hw is a direct member of that structure.
Do all clock providers need to implement every clk_ops callback?
No. Only the callbacks relevant to that clock type are required — a fixed-rate clock, for example, typically only needs recalc_rate.
What is struct clk_core used for?
It is the framework’s internal representation of a clock. Neither provider nor consumer drivers access it directly — it is private to drivers/clk/clk.c.
Where can I inspect the live clock tree on a running kernel?
Mount debugfs and read /sys/kernel/debug/clk/clk_summary, which dumps every registered clock, its parent, rate, and enable count.
Is this lecture part of a free course?
Yes, this is part of EmbeddedPathashala’s free Linux kernel development course, free Linux device drivers course, and free embedded systems course.
What will the next lecture in this chapter cover?
Writing a complete, Device Tree–registered clock provider driver with multiple outputs, followed by the full clock consumer API.
Continue the Free Linux Kernel Development Course
Explore more free Linux device drivers and embedded systems lectures on EmbeddedPathashala.
