Linux Clock Provider Registration Guide-Free Linux Device Drivers Course
Free Linux Kernel Development Course — Common Clock Framework, Lecture 2
Common Clock Framework
Intermediate
6.x
Free Linux Kernel Development Course Topics
free linux device drivers course
free embedded linux course
free embedded systems course
linux clock provider registration
In this free Linux kernel development course lecture we continue our Common Clock Framework (CCF) chapter and focus on linux clock provider registration — the step where a clock driver hands its struct clk_hw to the kernel and becomes a real, usable clock. In the previous lecture we studied the core CCF data structures. Here we register one for real, watch it join the clock tree, and expose it to a consumer.
What You Will Learn
- How
clk_hw_register()anddevm_clk_hw_register()plug a clock into the Common Clock Framework - Why the legacy
clk_register()API still exists but should not be used in new drivers - How the CCF core builds the clock tree using the root list and the orphan list
- The difference between
struct clk(consumer handle) andstruct clk_core(framework-internal object) - How to bind a clock to a lookup name with
clk_hw_register_clkdev()for non-device-tree systems - How to write, build, and test an original fixed-rate clock provider driver on kernel 6.x
Prerequisites
- Completion of the previous lecture on CCF data structures (
clk_hw,clk_ops,clk_init_data) - Comfortable writing and loading a basic Linux kernel module
- Familiarity with platform drivers and
probe()/remove() - A kernel 6.x build environment (a Raspberry Pi or QEMU virt machine works fine)
Quick Recap: clk_init_data Fields
Before registering anything, every clock provider fills in a struct clk_init_data instance. This is only used at registration time and has no meaning afterward — its job is to hand the framework everything it needs to build the clock’s identity and position in the tree.
| Field | Purpose |
|---|---|
name |
Unique name of the clock, used for lookups and debugfs |
ops |
Pointer to the clk_ops callback table implemented by the driver |
parent_names / parent_data |
Array describing possible parent clocks |
num_parents |
Number of entries in the parent array |
flags |
Framework-level flags that can modify how certain ops behave |
Registering a Clock Provider
Once clk_hw.init points at a filled clk_init_data, the driver registers the clock with the kernel. On modern kernels the recommended entry point is the clk_hw-based API, not the older struct clk-based one.
/* Recommended on kernel 6.x: hw-based registration */
int clk_hw_register(struct device *dev, struct clk_hw *hw);
/* Managed (devm) version - auto-unregisters on driver detach */
int devm_clk_hw_register(struct device *dev, struct clk_hw *hw);
/* Legacy API - still present for old drivers, avoid in new code */
struct clk *clk_register(struct device *dev, struct clk_hw *hw);
The legacy clk_register() returns a consumer-facing struct clk, which blurs the line between provider code and consumer code. clk_hw_register() keeps that separation clean: provider drivers only ever touch struct clk_hw, and consumers only ever touch struct clk.
How the CCF Builds the Clock Tree
The framework tracks every registered clock through two internal lists maintained in drivers/clk/clk.c: a root list for clocks with no parent, and an orphan list for clocks whose named parent has not been registered yet. Every time a new clock finishes initialisation, the CCF walks the orphan list and re-parents any orphan that names the newly-arrived clock as its parent. This lets drivers register clocks in any order, without worrying about parent-before-child sequencing.
→ clk_root_list
→ clk_orphan_list
→ parent’s children list
struct clk vs struct clk_core
It helps to be precise about who owns what. struct clk_core is the framework’s private representation of the physical clock — there is exactly one per hardware clock. struct clk is a lightweight, per-user handle: every time a consumer calls clk_get(), it receives its own struct clk instance, even though all of those handles point back to the same underlying clk_core.
| Structure | Owner | Lifetime |
|---|---|---|
struct clk_hw |
Provider driver | One per physical clock, embedded in driver’s private struct |
struct clk_core |
CCF core (internal, private) | One per physical clock |
struct clk |
Consumer driver | One new instance per clk_get() call |
Naming a Clock for Lookup: clk_hw_register_clkdev()
On systems that still identify clocks by string name rather than a device-tree phandle, registering the clock alone is not enough — a consumer has no way to find it. The clock also needs an entry in the kernel’s clock lookup list, created with clk_hw_register_clkdev() (or the managed devm_clk_hw_register_clkdev()). This binds a clock name to a specific consumer device, mirroring what clk_register_clkdev() did for the legacy API.
int clk_hw_register_clkdev(struct clk_hw *hw, const char *con_id,
const char *dev_id);
Original Example: A Fixed-Rate Clock Provider
Let’s register a small, original clock provider from scratch. This driver models a fixed 24 MHz oscillator and demonstrates registration, root-list placement, and clean unregistration.
// ep_clk_provider_demo.c
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk-provider.h>
#include <linux/of.h>
struct ep_clk {
struct clk_hw hw;
};
static unsigned long ep_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
/* fixed oscillator: always 24 MHz regardless of parent */
return 24000000UL;
}
static const struct clk_ops ep_clk_ops = {
.recalc_rate = ep_clk_recalc_rate,
};
static int ep_clk_probe(struct platform_device *pdev)
{
struct ep_clk *ep;
struct clk_init_data init = {};
int ret;
ep = devm_kzalloc(&pdev->dev, sizeof(*ep), GFP_KERNEL);
if (!ep)
return -ENOMEM;
init.name = "ep-fixed-osc";
init.ops = &ep_clk_ops;
init.num_parents = 0; /* no parent -> goes into clk_root_list */
ep->hw.init = &init;
ret = devm_clk_hw_register(&pdev->dev, &ep->hw);
if (ret) {
dev_err(&pdev->dev, "clk_hw_register failed: %d\n", ret);
return ret;
}
/* bind the name so a consumer can clk_get(dev, "ep-fixed-osc") */
ret = devm_clk_hw_register_clkdev(&pdev->dev, &ep->hw,
"ep-fixed-osc", NULL);
if (ret)
return ret;
platform_set_drvdata(pdev, ep);
dev_info(&pdev->dev, "ep-fixed-osc registered at 24 MHz\n");
return 0;
}
static const struct of_device_id ep_clk_of_match[] = {
{ .compatible = "ep,fixed-osc-demo" },
{ }
};
MODULE_DEVICE_TABLE(of, ep_clk_of_match);
static struct platform_driver ep_clk_driver = {
.probe = ep_clk_probe,
.driver = {
.name = "ep_clk_provider_demo",
.of_match_table = ep_clk_of_match,
},
};
module_platform_driver(ep_clk_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeddedPathashala");
MODULE_DESCRIPTION("Original fixed-rate clock provider demo for CCF registration lecture");
Build and Run Steps
$ make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
$ sudo insmod ep_clk_provider_demo.ko
$ dmesg | tail -5
Expected dmesg Output
[ 102.441210] ep_clk_provider_demo ep_fixed_osc_demo: ep-fixed-osc registered at 24 MHz
[ 102.441233] clk: ep-fixed-osc: registered
You can confirm the clock joined the tree by inspecting the CCF debugfs summary (if CONFIG_COMMON_CLK_DEBUG is enabled):
$ sudo cat /sys/kernel/debug/clk/clk_summary | grep ep-fixed-osc
ep-fixed-osc 0 0 0 24000000 0 50000 0
clk_register() vs clk_hw_register() vs devm_clk_hw_register()
| API | Returns | Cleanup | Recommended for new drivers |
|---|---|---|---|
clk_register() |
struct clk * |
Manual clk_unregister() |
No — legacy only |
clk_hw_register() |
int (error code) |
Manual clk_hw_unregister() |
Yes |
devm_clk_hw_register() |
int (error code) |
Automatic on driver detach | Yes, preferred |
Real-World Use Cases
- SoC clock controller drivers that expose PLLs, dividers, and gates as a tree of clk providers
- PMIC drivers that provide an auxiliary clock output pin alongside regulators
- Board-level fixed oscillator nodes described in the device tree
- Codec or sensor drivers that need a small reference clock registered internally
Common Mistakes
- Calling
clk_hw_register()beforeclk_hw.initis fully populated - Forgetting to check the return value, then dereferencing an invalid clock later
- Mixing
clk_register()andclk_hw_register()APIs within the same driver - Registering with a non-unique
name, causing lookup ambiguity - Using plain
clk_hw_register()and forgetting manual unregistration onremove()
Best Practices
- Always prefer
devm_clk_hw_register()for automatic lifecycle management - Keep
clk_init_dataon the stack only until registration completes — it is not needed afterward - Use
clk_hw_register_clkdev()only on non-device-tree platforms; DT platforms should rely onof_clk_add_provider()instead - Check
clk_hw_register()‘s return value and propagate the error fromprobe()
Performance and Security Considerations
Clock registration happens once, typically at boot or driver probe, so it is not performance-critical. The main safety concern is correctness: an incorrectly registered clock (wrong recalc_rate, missing parent) can propagate an inaccurate rate to every downstream consumer, potentially clocking hardware outside its safe operating range. Always validate rate values returned by your clk_ops callbacks.
Summary / Key Takeaways
clk_hw_register()anddevm_clk_hw_register()are the modern way to register a clock provider;clk_register()is legacy- The CCF places every clock in either the root list or the orphan list, then reparents orphans automatically as their parents appear
struct clk_coreis the framework’s single internal object per clock;struct clkis a per-consumer handleclk_hw_register_clkdev()binds a name-based lookup entry for non-DT consumers
Conclusion
Registering a clock provider is the moment a driver’s clk_hw definition becomes a living part of the system’s clock tree. By preferring the clk_hw-based, devm-managed APIs, keeping provider and consumer code cleanly separated, and understanding how the root and orphan lists build the tree automatically, you can write clock provider drivers that are robust, easy to maintain, and ready for kernel 6.x. In the next lecture we will look at unregistering clock providers safely and walk through the consumer-side clk_get()/clk_put() path in detail.
Frequently Asked Questions
What is the difference between clk_register() and clk_hw_register()?
clk_register() is the legacy API that returns a consumer-facing struct clk, blurring provider/consumer separation. clk_hw_register() is the modern, recommended API that keeps provider drivers working only with struct clk_hw.
Should I use devm_clk_hw_register() in every new driver?
In most cases yes. It automatically unregisters the clock when the device detaches, removing the need for manual cleanup in remove().
What happens if a clock’s parent has not been registered yet?
The clock is placed on the clk_orphan_list. As soon as its named parent registers, the CCF walks the orphan list and reparents it automatically.
Is clk_init_data needed after registration completes?
No. Its only purpose is to seed struct clk_core during initialisation; once that is done, the data has no further meaning.
What is clk_hw_register_clkdev() used for?
It binds a clock to a lookup name for a specific consumer device on platforms that identify clocks by name instead of a device-tree phandle.
Can a single physical clock have multiple struct clk handles?
Yes. Every clk_get() call returns a fresh struct clk handle, but all handles for the same hardware clock share one underlying struct clk_core.
Why does clk_hw_register() return an int instead of a pointer?
Because it operates purely on the provider-side clk_hw the caller already owns; it only needs to report success or an error code, unlike the legacy API which allocates and returns a new struct clk.
Where can I verify these APIs for my kernel version?
Check include/linux/clk-provider.h and drivers/clk/clk.c in your kernel source tree, and the official documentation at docs.kernel.org’s driver-api/clk.html page.
Continue the Free Linux Kernel Development Course
More lectures on the Common Clock Framework, device drivers, and embedded Linux are on the way.
