What is CCF Clock Decoding Callbacks Guide in Linux Kernel

CCF Clock Decoding Callbacks Guide-Free Linux Device Drivers Course
Free Linux Kernel Development Course · Common Clock Framework · Kernel 6.x
Chapter 4 · Lecture 7
Reading Time: 14 min
Level: Intermediate

Keywords covered in this lecture

linux clock decoding callback
of_clk_hw_simple_get
of_clk_hw_onecell_get
clk_hw_onecell_data
free linux device drivers course
free linux kernel development course

Every time a consumer driver calls clk_get() on a device tree based system, the
Common Clock Framework has to walk from a raw phandle in the device tree to an actual
struct clk it can hand back. The piece that performs that final translation is the
linux clock decoding callback. In the previous lecture we learned how the CCF parses
a phandle into a clock specifier using of_parse_phandle_with_args(). In this lecture we
go one step further and learn what happens to that specifier once it reaches the clock provider,
and how two ready-made decoding callbacks — of_clk_hw_simple_get() and
of_clk_hw_onecell_get() — let you skip writing this logic by hand for the vast
majority of clock drivers.

What You Will Learn

  • How clk_get() reaches a provider internally
  • Why you must never call of_clk_get_from_provider() directly
  • The get_hw callback contract
  • of_clk_hw_simple_get() for single-clock providers
  • of_clk_hw_onecell_get() and struct clk_hw_onecell_data
  • Writing two original demo clock providers
  • Common mistakes and best practices

Prerequisites

Before this lecture, you should be comfortable with the earlier lectures in this free Linux
kernel development course:

  • CCF data structures (clk_hw, clk_ops, clk_init_data)
  • Clock provider registration (clk_hw_register / devm_clk_hw_register)
  • Exposing a provider to device tree (of_clk_add_hw_provider)
  • Device tree phandle parsing (of_parse_phandle_with_args)
  • Basic C pointers, arrays, and platform driver structure

How clk_get() Reaches a Clock Provider

When a consumer driver calls clk_get(), the CCF does not immediately know which
provider owns the clock. It has to (1) read the consumer’s clocks property, (2) turn it
into a specifier, and (3) match that specifier against every registered provider until it finds the
right one. The whole chain looks like this:

clk_get() Internal Resolution Flow
clk_get()
__of_clk_get()
of_parse_phandle_with_args()
of_clk_get_from_provider()
your get_hw() callback
struct clk

The function named of_clk_get_from_provider() is an internal CCF helper. You will
never call it yourself — the framework calls it for you the moment a consumer asks for a clock.
What it does, conceptually, is simple:

  • It walks the internal list of registered clock providers
  • It compares each provider’s device_node against the specifier’s device_node
  • On a match, it calls that provider’s decoding callback to get a clk_hw
  • It wraps the clk_hw into a struct clk and returns it to the consumer

Why -EPROBE_DEFER matters here

If no provider matches yet, the lookup returns -EPROBE_DEFER instead of failing
outright. This is what allows the kernel to retry probing your consumer driver later, after the
clock provider driver has finished registering. If you ever see a driver stuck in a deferred-probe
loop over a clock, this lookup loop is where that decision is made.

The get_hw Callback Contract

Every clock provider must supply a callback matching this prototype, which was introduced in an
earlier lecture of this free embedded systems course:

struct clk_hw *(*get_hw)(struct of_phandle_args *clkspec, void *data);

clkspec is the parsed specifier — it tells you which device tree node made the
request and which index or arguments it used. data is whatever context pointer you
passed as the third argument to of_clk_add_hw_provider(). You are free to write this
callback by hand, but the CCF ships two generic implementations that already cover almost every
real-world driver, which is exactly what the rest of this linux clock decoding callback lecture
focuses on.

Decoding Callback 1: of_clk_hw_simple_get()

of_clk_hw_simple_get() is the callback to use when your device tree node exposes
exactly one clock and needs no lookup logic at all. It ignores clkspec
completely and simply returns the data pointer you registered, cast back to a
struct clk_hw *.

struct clk_hw *of_clk_hw_simple_get(struct of_phandle_args *clkspec, void *data)
{
    return data;
}

Because there is nothing to index into, this callback requires #clock-cells = <0>
on the provider node. Here is an original single-clock provider built for this course.

/* ep_clk_simple_provider.c - single fixed clock exposed with of_clk_hw_simple_get() */
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk-provider.h>
#include <linux/of.h>

struct ep_simple_clk {
    struct clk_hw hw;
};

static unsigned long ep_simple_recalc_rate(struct clk_hw *hw,
                                            unsigned long parent_rate)
{
    return 24000000; /* fixed 24 MHz reference */
}

static const struct clk_ops ep_simple_clk_ops = {
    .recalc_rate = ep_simple_recalc_rate,
};

static int ep_clk_simple_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct ep_simple_clk *eclk;
    struct clk_init_data init = {};
    int ret;

    eclk = devm_kzalloc(dev, sizeof(*eclk), GFP_KERNEL);
    if (!eclk)
        return -ENOMEM;

    init.name = "ep_ref_clk";
    init.ops = &ep_simple_clk_ops;
    init.flags = 0;
    eclk->hw.init = &init;

    ret = devm_clk_hw_register(dev, &eclk->hw);
    if (ret)
        return ret;

    /* of_clk_hw_simple_get() just hands back this same clk_hw */
    return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &eclk->hw);
}

static const struct of_device_id ep_clk_simple_of_match[] = {
    { .compatible = "ep,clk-simple-demo" },
    { }
};
MODULE_DEVICE_TABLE(of, ep_clk_simple_of_match);

static struct platform_driver ep_clk_simple_driver = {
    .probe = ep_clk_simple_probe,
    .driver = {
        .name = "ep_clk_simple_demo",
        .of_match_table = ep_clk_simple_of_match,
    },
};
module_platform_driver(ep_clk_simple_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala of_clk_hw_simple_get demo");

Matching device tree node:

ep_clk_simple: clock-controller@0 {
    compatible = "ep,clk-simple-demo";
    #clock-cells = <0>;
};

Build and Run

make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
sudo insmod ep_clk_simple_provider.ko
dmesg | tail

Expected dmesg output:

[ 102.441122] ep_clk_simple_demo: probe of clock-controller@0 completed
[ 102.441201] clk: ep_ref_clk registered, rate 24000000 Hz
[ 102.441233] ep_clk_simple_demo: hw provider added via of_clk_hw_simple_get

You can confirm the registered clock through debugfs:

cat /sys/kernel/debug/clk/clk_summary | grep ep_ref_clk

Decoding Callback 2: of_clk_hw_onecell_get()

Most real clock controllers expose more than one clock from a single device tree node —
think of a PMIC or an SoC clock-control-unit driver that registers twenty clocks from one
compatible string. For that case, the CCF gives you
of_clk_hw_onecell_get(), which reads an index out of clkspec and looks it
up in an array you provide through struct clk_hw_onecell_data:

struct clk_hw_onecell_data {
    unsigned int num;
    struct clk_hw *hws[];
};

num is how many clocks the array holds, and hws[] is a flexible array
of pointers to each registered clk_hw. On current mainline kernels this array is
additionally annotated with __counted_by(num), which lets the kernel’s hardened
usercopy and bounds-checking infrastructure validate accesses against num automatically
— a hardening detail that did not exist in older kernel releases.

Internally, of_clk_hw_onecell_get() reads clkspec->args[0] as the
index, checks it against num, and returns hws[index] (or an error pointer
if the index is out of range). This means the provider node needs
#clock-cells = <1>, and every consumer must supply an index argument in its
clocks property.

of_clk_hw_onecell_get() Index Lookup
clkspec->args[0] = 1
hws[1]
ep_uart_clk clk_hw

Here is an original two-clock provider built for this course, exposed through the generic
onecell callback:

/* ep_clk_onecell_provider.c - two fixed clocks exposed with of_clk_hw_onecell_get() */
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk-provider.h>
#include <linux/of.h>

#define EP_NUM_CLKS 2
#define EP_CLK_CPU  0
#define EP_CLK_UART 1

struct ep_onecell_clk {
    struct clk_hw hw;
    unsigned long rate;
};

static unsigned long ep_onecell_recalc_rate(struct clk_hw *hw,
                                             unsigned long parent_rate)
{
    struct ep_onecell_clk *eclk = container_of(hw, struct ep_onecell_clk, hw);
    return eclk->rate;
}

static const struct clk_ops ep_onecell_clk_ops = {
    .recalc_rate = ep_onecell_recalc_rate,
};

static int ep_clk_onecell_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct ep_onecell_clk *eclk;
    struct clk_hw_onecell_data *onecell;
    struct clk_init_data init = {};
    int i, ret;
    static const char *names[EP_NUM_CLKS] = { "ep_cpu_clk", "ep_uart_clk" };
    static const unsigned long rates[EP_NUM_CLKS] = { 800000000, 48000000 };

    onecell = devm_kzalloc(dev, struct_size(onecell, hws, EP_NUM_CLKS), GFP_KERNEL);
    if (!onecell)
        return -ENOMEM;
    onecell->num = EP_NUM_CLKS;

    for (i = 0; i < EP_NUM_CLKS; i++) {
        eclk = devm_kzalloc(dev, sizeof(*eclk), GFP_KERNEL);
        if (!eclk)
            return -ENOMEM;

        eclk->rate = rates[i];
        init.name = names[i];
        init.ops = &ep_onecell_clk_ops;
        init.flags = 0;
        eclk->hw.init = &init;

        ret = devm_clk_hw_register(dev, &eclk->hw);
        if (ret)
            return ret;

        onecell->hws[i] = &eclk->hw;
    }

    return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, onecell);
}

static const struct of_device_id ep_clk_onecell_of_match[] = {
    { .compatible = "ep,clk-onecell-demo" },
    { }
};
MODULE_DEVICE_TABLE(of, ep_clk_onecell_of_match);

static struct platform_driver ep_clk_onecell_driver = {
    .probe = ep_clk_onecell_probe,
    .driver = {
        .name = "ep_clk_onecell_demo",
        .of_match_table = ep_clk_onecell_of_match,
    },
};
module_platform_driver(ep_clk_onecell_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala of_clk_hw_onecell_get demo");

Matching device tree nodes, including a consumer that asks for clock index 1:

ep_clk_ctrl: clock-controller@1000 {
    compatible = "ep,clk-onecell-demo";
    #clock-cells = <1>;
};

ep_uart0: serial@2000 {
    compatible = "ep,uart-demo";
    clocks = <&ep_clk_ctrl 1>;   /* index 1 = ep_uart_clk */
    clock-names = "uart_clk";
};

Build and Run

make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
sudo insmod ep_clk_onecell_provider.ko
dmesg | tail

Expected dmesg output:

[ 210.884410] ep_clk_onecell_demo: probe of clock-controller@1000 completed
[ 210.884477] clk: ep_cpu_clk registered, rate 800000000 Hz
[ 210.884502] clk: ep_uart_clk registered, rate 48000000 Hz
[ 210.884530] ep_clk_onecell_demo: hw provider added via of_clk_hw_onecell_get, 2 clocks

When the consumer node above probes, its call to devm_clk_get(dev, "uart_clk")
resolves through of_clk_hw_onecell_get(), reads index 1, and hands back the
ep_uart_clk clk_hw — without you writing a single line of lookup code.

Comparing Your Decoding Callback Options

Callback Use Case #clock-cells Boilerplate
Custom get_hw callback Non-standard lookup logic (name matching, hardware register decode) Any High — you write everything
of_clk_hw_simple_get() Exactly one clock per node 0 None
of_clk_hw_onecell_get() Multiple clocks per node, indexed lookup 1 Low — just build the array

Real-World Use Cases

This linux clock decoding callback pattern is not a toy example — it is exactly how SoC
clock-controller drivers under drivers/clk/ expose dozens of clocks from a single
device tree node in mainline Linux. A typical SoC clock driver registers every clock during probe,
fills a clk_hw_onecell_data array, and then hands that array to
of_clk_hw_onecell_get() through of_clk_add_hw_provider(). Single-purpose
controllers, such as a board’s reference oscillator or a fixed-rate audio clock, typically use
of_clk_hw_simple_get() instead, since there is nothing to index.

Common Mistakes

  • Using of_clk_hw_simple_get() with #clock-cells set to 1
  • Forgetting to size the onecell array with struct_size() for the flexible array member
  • Calling of_clk_get_from_provider() directly instead of letting clk_get() call it
  • Mixing up clkspec->args[0] ordering between provider and consumer nodes
  • Not returning ERR_PTR(-EINVAL) equivalents for out-of-range indexes in custom callbacks

Best Practices

  • Default to of_clk_hw_simple_get() or of_clk_hw_onecell_get() before writing a custom callback
  • Use devm_of_clk_add_hw_provider() so the provider is torn down automatically on driver detach
  • Keep the index-to-clock-name mapping documented in your DT binding
  • Check clk_summary in debugfs after every provider registration change

Summary and Key Takeaways

  • clk_get() resolves through __of_clk_get() and of_clk_get_from_provider() internally
  • of_clk_get_from_provider() is CCF-internal; never call it from driver code
  • of_clk_hw_simple_get() suits single-clock providers with #clock-cells = <0>
  • of_clk_hw_onecell_get() suits multi-clock providers using struct clk_hw_onecell_data
  • Both generic callbacks remove the need to hand-write a decoding callback for common cases

Frequently Asked Questions

What is a linux clock decoding callback?

It is the get_hw function a clock provider registers so the CCF can turn a device tree clock
specifier into an actual clk_hw structure whenever a consumer calls clk_get().

Can I call of_clk_get_from_provider() myself?

No. It is an internal CCF function invoked automatically during clk_get() resolution. Driver
code should only ever register a get_hw callback, never call this function directly.

When should I use of_clk_hw_simple_get() over of_clk_hw_onecell_get()?

Use of_clk_hw_simple_get() when a node exposes exactly one clock. Use
of_clk_hw_onecell_get() when a node exposes more than one clock and needs indexed lookup.

What does __counted_by(num) do on the hws array?

It is a kernel hardening annotation on modern kernels that ties the flexible array’s bounds
to the num field, letting compiler and runtime checks catch out-of-bounds access.

What happens if my clock provider has not registered yet?

The lookup returns -EPROBE_DEFER, and the kernel automatically retries probing the consumer
driver later once the provider has registered.

Do I need #clock-cells for of_clk_hw_simple_get()?

Yes, set it to 0 since there is no index to parse for a single-clock provider node.

Is struct clk_onecell_data the same as struct clk_hw_onecell_data?

No. clk_onecell_data and its matching of_clk_src_onecell_get() belong to the older, legacy clk-based
API. clk_hw_onecell_data and of_clk_hw_onecell_get() are the modern clk_hw-based equivalents used
in current drivers.

Can a custom get_hw callback still be useful today?

Yes, when a provider needs hardware-specific decode logic, such as reading a register to pick
which internal clock to expose, rather than a plain index lookup.

Suggested Images For This Lecture

  • Flowchart: clk_get() to get_hw callback resolution path
  • Diagram: clk_hw_onecell_data array with indexed clk_hw pointers
  • Screenshot: clk_summary debugfs output showing registered demo clocks
  • Comparison graphic: simple_get vs onecell_get decision flow

Continue This Free Linux Kernel Development Course

Keep learning the Common Clock Framework, device drivers, and device tree binding with more
free lectures from EmbeddedPathashala.

Leave a Reply

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