What is CCF Base Clock Types Overview in Linux Kernel

CCF Base Clock Types Overview-Free Linux Device Drivers Course
Free Linux Kernel Development Course · Common Clock Framework · Kernel 6.x
Chapter 4 · Lecture 11
Reading Time: 13 min
Level: Intermediate

Keywords covered in this lecture

linux base clock types
clk_hw_register_gate
clk_hw_register_divider
clk_hw_register_composite
free linux kernel development course
free embedded systems course

Every custom get_hw callback, every embedded clk_hw wrapper, and every static
metadata table we have built so far in this chapter still needed one thing to actually
control hardware: a set of clk_ops. Writing every op by hand for every clock is rarely
necessary, because the CCF ships a handful of ready-made linux base clock
types
that already implement the common behaviors. This lecture is a guided
tour of all six, so you know which one to reach for before writing a single custom
callback.

What You Will Learn

  • The six CCF base clock types and what each one can and cannot do
  • Which registration helper function matches each type
  • How composite clocks combine mux, rate, and gate behavior
  • Registering a fixed-rate clock and a gate clock end to end
  • Choosing the right base type instead of writing custom clk_ops

Prerequisites

  • struct clk_hw, struct clk_ops, struct clk_init_data basics
  • Static clock metadata table pattern (previous lecture)
  • Basic register/bitfield manipulation in a device driver

Providing Clock Ops: The Six Base Clock Types

struct clk_hw is only the base structure the CCF is built on. On top of it,
the framework provides six ready-made clock variants that cover the vast majority of what
real hardware clock lines actually do: turn on and off, divide a rate, or pick between
parents. Each has its own registration helper and its own restrictions on what it is
allowed to change.

Where the Six Base Types Sit on Top of clk_hw
struct clk_hw
fixed-rate
gate
mux
fixed-factor
divider
composite

fixed-rate

A fixed-rate clock always runs at one unchangeable rate and cannot be gated off. It is
typically used for crystal oscillators and external reference clocks. Registered with
clk_hw_register_fixed_rate() (or its devm_ managed variant).

gate

A gate clock runs at exactly its parent’s rate and cannot change rate itself; all it
can do is turn the clock signal on or off through a register bit. Registered with
clk_hw_register_gate(). This is the type used by the demo drivers in
earlier lectures of this chapter.

mux

A mux clock cannot gate and cannot divide, but it can select which of several parent
clocks feeds it, and it reports whichever rate that selected parent currently has.
Registered with clk_hw_register_mux().

fixed-factor

A fixed-factor clock multiplies and divides its parent’s rate by fixed integer
constants baked in at registration time; it offers no gating and no runtime rate
adjustment. Registered with clk_hw_register_fixed_factor().

divider

A divider clock divides its parent’s rate using a selectable divisor, chosen from a
bitfield or a lookup table supplied at registration time. It cannot gate. Registered with
clk_hw_register_divider() or clk_hw_register_divider_table()
for non-linear divisor tables.

composite

A composite clock is not a new behavior on its own — it combines any of a mux,
a rate-changing block (typically a divider), and a gate into a single clk_hw, which
matches how a lot of real hardware clock lines are actually wired: select a parent,
divide it, then gate it, all as one logical clock line. Registered with
clk_hw_register_composite(), passing in the mux/rate/gate sub-blocks and
their respective ops.

Base Clock Type Comparison

Type Can Gate? Can Change Rate? Registration Helper
fixed-rate No No clk_hw_register_fixed_rate()
gate Yes No clk_hw_register_gate()
mux No Indirectly, via parent selection clk_hw_register_mux()
fixed-factor No No (fixed multiply/divide only) clk_hw_register_fixed_factor()
divider No Yes, via divisor selection clk_hw_register_divider()
composite Yes (via gate sub-block) Yes (via rate sub-block) clk_hw_register_composite()

Quick Demo: Fixed-Rate Parent Feeding a Gate

Here is a short, original example that registers an oscillator as a fixed-rate clock
and a single gated output derived from it, using only the built-in base types instead of
any custom clk_ops:

/* ep_basic_clk_tree.c - fixed-rate oscillator feeding a gate clock */
#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_tree_data {
    void __iomem *base;
    spinlock_t lock;
    struct clk_hw *osc_hw;
    struct clk_hw *gate_hw;
};

static int ep_basic_tree_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct ep_tree_data *data;

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

    spin_lock_init(&data->lock);

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

    data->osc_hw = devm_clk_hw_register_fixed_rate(dev, "ep_osc", NULL, 0, 24000000);
    if (IS_ERR(data->osc_hw))
        return PTR_ERR(data->osc_hw);

    data->gate_hw = devm_clk_hw_register_gate(dev, "ep_gated_out", "ep_osc", 0,
                                               data->base, 0, 0, &data->lock);
    if (IS_ERR(data->gate_hw))
        return PTR_ERR(data->gate_hw);

    return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, data->gate_hw);
}

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

static struct platform_driver ep_basic_tree_driver = {
    .probe = ep_basic_tree_probe,
    .driver = {
        .name = "ep_clk_basic_tree_demo",
        .of_match_table = ep_basic_tree_of_match,
    },
};
module_platform_driver(ep_basic_tree_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala base clock types demo");

Build and Run

make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
sudo insmod ep_basic_clk_tree.ko
cat /sys/kernel/debug/clk/clk_summary | grep ep_

Expected clk_summary excerpt:

ep_osc                    1        1        0    24000000          0
   ep_gated_out           1        1        0    24000000          0

Notice that neither clock needed a single hand-written clk_ops callback — both
base types already provided everything required.

Common Mistakes

  • Writing a custom gate clk_ops when clk_hw_register_gate() already covers the need
  • Expecting a mux or fixed-factor clock to support gating
  • Assuming composite always needs all three sub-blocks (mux, rate, gate can be optional)
  • Forgetting that fixed-factor rate changes require re-registering, not runtime set_rate

Best Practices

  • Reach for a base clock type before writing any custom clk_ops
  • Use composite only when a single hardware clock line genuinely combines mux/rate/gate
  • Prefer the devm_ variants of each registration helper for automatic cleanup
  • Match the base type to what the datasheet says the hardware block can do, not the other way around

Summary and Key Takeaways

  • The CCF ships six base clock types: fixed-rate, gate, mux, fixed-factor, divider, composite
  • Each has its own registration helper and its own gating/rate-change restrictions
  • Composite combines mux, rate, and gate sub-blocks into a single logical clock
  • Most real clock lines can be built from these six without any custom clk_ops

Frequently Asked Questions

Do I ever need to write custom clk_ops at all?

Yes, when the hardware behavior does not match any of the six base types, such as a
clock whose enable sequence requires several dependent register writes in a specific
order.

Can a mux clock also gate?

No. A plain mux clock only selects a parent; if you need both parent selection and
gating in one logical clock, use a composite clock instead.

What is the difference between divider and fixed-factor?

A divider clock can change its divisor at runtime through a register field, while a
fixed-factor clock has its multiply/divide constants fixed at registration time.

Does a composite clock need all three sub-blocks?

No. You can pass NULL for any sub-block you do not need, such as a composite with only
a divider and a gate but no mux.

Which base type is most common in SoC clock controllers?

Gate and divider clocks are extremely common for peripheral clock branches, while
composite clocks are common for core and bus clocks that need mux, divide, and gate
together.

Suggested Images For This Lecture

  • Diagram: six base clock types layered on top of struct clk_hw
  • Diagram: composite clock combining mux, divider, and gate sub-blocks
  • Screenshot: clk_summary showing the fixed-rate to gate demo tree

Continue This Free Linux Kernel Development Course

Next, we go deep on the gate clock type and implement its clk_ops from scratch to
understand exactly what clk_hw_register_gate() does for you.

Leave a Reply

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