What is Dynamic Onecell Clock Allocation Pattern in Linux Kernel

Dynamic Onecell Clock Allocation Pattern-Free Linux Device Drivers Course
Free Linux Kernel Development Course · Common Clock Framework · Kernel 6.x
Chapter 4 · Lecture 8
Reading Time: 13 min
Level: Intermediate

Keywords covered in this lecture

linux clock array allocation
clk_hw_onecell_data dynamic
devm_kcalloc clock driver
clock-output-names
free linux kernel development course
free linux device drivers course

The demo provider from the previous lecture registered exactly two clocks, and the
array size was a compile-time constant. Real clock-controller drivers never get that
luxury — a single compatible string might describe a chip that exposes four
clocks on one board and nine on another. This is where the linux clock array
allocation
pattern comes in: instead of a fixed-size array, the number of
clocks is read from the device tree itself, and the clk_hw_onecell_data
array is allocated at runtime to exactly that size.

What You Will Learn

  • Why a fixed-size clock array does not scale to real hardware
  • Deriving a clock count from a device tree property
  • Dynamically allocating clk_hw_onecell_data with devm_kcalloc
  • Reading per-clock names with of_property_read_string_index()
  • Why a clock provider can also be a clock consumer
  • Writing an original dynamic multi-clock provider driver

Prerequisites

  • CCF clock decoding callbacks (of_clk_hw_onecell_get, previous lecture)
  • struct clk_hw_onecell_data and the hws[] array
  • Clock provider device tree exposure (of_clk_add_hw_provider)
  • Basic device tree property reading APIs

Why Real Drivers Allocate Clocks Dynamically

A fixed #define EP_NUM_CLKS 2 works fine for a lecture demo, but production
clock-controller drivers are typically reused across several SoC variants of the same
chip family, each exposing a different number of clock lines from the same compatible
driver binary. Two common ways to learn the count at probe time are:

  • Counting the strings in a clock-output-names property
  • Matching against a per-SoC of_device_id data table that stores the count

Once the count is known, the driver allocates its private context structure and the
onecell array to match — nothing is wasted, and nothing overflows.

Dynamic Onecell Allocation Flow
read clock count from DT
devm_kzalloc private struct
devm_kcalloc hws[count]
register each clock in a loop
of_clk_add_hw_provider()

A Provider Can Also Be a Consumer

It is worth calling out an easy-to-miss detail: nothing stops a clock provider driver
from calling devm_clk_get() on its own device to fetch an upstream parent
clock before it registers its own child clocks. A gate or divider block sitting inside a
bigger clock-control-unit almost always needs a parent reference clock, and that parent
is frequently obtained exactly the same way any ordinary consumer driver would obtain it.
Provider and consumer are simply two roles a single clock driver can play at once.

Original Demo: A Dynamic Multi-Clock Provider

This driver counts entries in a clock-output-names property, allocates a
clk_hw_onecell_data array of exactly that size, registers a basic gate clock
for each name, and exposes them all through of_clk_hw_onecell_get() from the
previous lecture.

/* ep_clk_dynamic_provider.c - clock count driven by device tree, not a #define */
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/spinlock.h>

struct ep_dyn_clk_data {
    void __iomem *base;
    spinlock_t lock;
    struct clk *parent_clk;
    struct clk_hw_onecell_data clk_hw_data;
};

static int ep_clk_dynamic_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct device_node *np = dev->of_node;
    struct ep_dyn_clk_data *data;
    const char *parent_name;
    int count, i, ret;

    count = of_property_count_strings(np, "clock-output-names");
    if (count <= 0)
        return -EINVAL;

    data = devm_kzalloc(dev, struct_size(data, clk_hw_data.hws, 0), GFP_KERNEL);
    if (!data)
        return -ENOMEM;

    data->clk_hw_data.hws = devm_kcalloc(dev, count, sizeof(struct clk_hw *),
                                          GFP_KERNEL);
    if (!data->clk_hw_data.hws)
        return -ENOMEM;
    data->clk_hw_data.num = count;

    spin_lock_init(&data->lock);

    data->base = devm_platform_ioremap_resource(pdev, 0);
    if (IS_ERR(data->base))
        return PTR_ERR(data->base);

    /* this provider is also a consumer of an upstream reference clock */
    data->parent_clk = devm_clk_get(dev, NULL);
    if (IS_ERR(data->parent_clk))
        return PTR_ERR(data->parent_clk);
    parent_name = __clk_get_name(data->parent_clk);

    for (i = 0; i < count; i++) {
        const char *clk_name;

        ret = of_property_read_string_index(np, "clock-output-names",
                                             i, &clk_name);
        if (ret)
            return ret;

        data->clk_hw_data.hws[i] = clk_hw_register_gate(dev, clk_name,
                                        parent_name, 0,
                                        data->base + (i * 4),
                                        0, 0, &data->lock);
        if (IS_ERR(data->clk_hw_data.hws[i]))
            return PTR_ERR(data->clk_hw_data.hws[i]);
    }

    return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
                                        &data->clk_hw_data);
}

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

static struct platform_driver ep_clk_dynamic_driver = {
    .probe = ep_clk_dynamic_probe,
    .driver = {
        .name = "ep_clk_dynamic_demo",
        .of_match_table = ep_clk_dynamic_of_match,
    },
};
module_platform_driver(ep_clk_dynamic_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala dynamic onecell clock provider demo");

Matching device tree node, this time with three clock outputs instead of two:

ep_clk_ctrl2: clock-controller@3000 {
    compatible = "ep,clk-dynamic-demo";
    reg = <0x3000 0x100>;
    #clock-cells = <1>;
    clocks = <&ep_osc_clk>;
    clock-output-names = "ep_bus_clk", "ep_spi_clk", "ep_i2c_clk";
};

Build and Run

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

Expected dmesg output:

[ 305.112004] ep_clk_dynamic_demo: probe of clock-controller@3000 completed
[ 305.112061] clk: ep_bus_clk registered (index 0)
[ 305.112089] clk: ep_spi_clk registered (index 1)
[ 305.112110] clk: ep_i2c_clk registered (index 2)
[ 305.112132] ep_clk_dynamic_demo: hw provider added, 3 clocks from clock-output-names
cat /sys/kernel/debug/clk/clk_summary | grep ep_

Static vs Dynamic Onecell Allocation

Aspect Static Array (Lecture 7) Dynamic Allocation (This Lecture)
Clock count source Compile-time #define Device tree property at probe time
Reusable across SoC variants No Yes
Allocation call None needed (fixed array) devm_kcalloc based on count
Typical use Teaching demos, fixed small controllers Mainline SoC clock-controller drivers

Common Mistakes

  • Allocating the hws array before knowing the real count
  • Forgetting to set clk_hw_data.num after allocating the array
  • Not checking IS_ERR() on each registered clk_hw inside the loop
  • Assuming a provider can never itself be a consumer of a parent clock

Best Practices

  • Derive the clock count from the device tree, never hardcode it in production drivers
  • Use devm_-prefixed allocation and registration calls to avoid manual cleanup paths
  • Keep the parent clock lookup near the top of probe, before registering children
  • Name clocks through clock-output-names rather than inventing names in C code

Summary and Key Takeaways

  • Production clock providers size their onecell array at runtime, not compile time
  • of_property_count_strings() and of_property_read_string_index() drive that sizing
  • devm_kcalloc() plus devm_ variants keep cleanup automatic on driver removal
  • A clock provider is free to also be a clock consumer of its own parent

Frequently Asked Questions

Why not just use a large fixed-size array to be safe?

A fixed array wastes memory when the real count is smaller, and it silently fails or
overflows when the real count is larger. Sizing from the device tree avoids both problems.

What does of_property_count_strings() return on error?

It returns a negative error code, so checking for count <= 0 before allocating
catches both a missing property and a malformed one.

Can clock-output-names be skipped?

Yes, but then you must derive both the count and the clock names another way, such as
a per-compatible static table inside the driver.

Is it normal for a clock provider to call devm_clk_get()?

Yes. Many real SoC clock-controller blocks sit downstream of an external oscillator
or PLL and must fetch that parent clock before registering their own child clocks.

Does devm_kcalloc zero the allocated memory?

Yes, like kcalloc(), it zero-initializes the allocated array, which is safer than
devm_kmalloc() for pointer arrays that will be filled in a loop.

Suggested Images For This Lecture

  • Flowchart: reading clock count from DT to dynamic array allocation
  • Diagram: provider driver also acting as a clock consumer
  • Screenshot: clk_summary showing three dynamically registered clocks

Continue This Free Linux Kernel Development Course

Next, we move from the device tree side to the driver side and learn how to embed
clk_hw inside your own private clock structures.

Leave a Reply

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