What is Linux Clock Provider Registration Guide in Linux Kernel

Linux Clock Provider Registration Guide-Free Linux Device Drivers Course

Free Linux Kernel Development Course — Common Clock Framework, Lecture 2

Chapter
Common Clock Framework
Level
Intermediate
Kernel
6.x

Free Linux Kernel Development Course Topics

free linux kernel development course
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() and devm_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) and struct 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.

Clock Registration Flow
Driver fills clk_hw.init with clk_init_data
clk_hw_register() / devm_clk_hw_register() called
Framework allocates struct clk_core, links clk_hw <-> clk_core
__clk_core_init() places clock in the tree

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.

Root List vs Orphan List
num_parents == 0
→ clk_root_list
named parent not yet registered
→ clk_orphan_list
named parent already registered
→ 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() before clk_hw.init is fully populated
  • Forgetting to check the return value, then dereferencing an invalid clock later
  • Mixing clk_register() and clk_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 on remove()

Best Practices

  • Always prefer devm_clk_hw_register() for automatic lifecycle management
  • Keep clk_init_data on 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 on of_clk_add_provider() instead
  • Check clk_hw_register()‘s return value and propagate the error from probe()

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() and devm_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_core is the framework’s single internal object per clock; struct clk is a per-consumer handle
  • clk_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.

 

Leave a Reply

Your email address will not be published. Required fields are marked *