What is Reusing Built-In CCF Clock Ops in Linux Kernel

Reusing Built-In CCF Clock Ops-Free Linux Device Drivers Course
Free Linux Kernel Development Course · Common Clock Framework · Kernel 6.x
Chapter 4 · Lecture 16
Reading Time: 15 min
Level: Advanced

Keywords covered in this lecture

clk_hw_register vs registration helper
reuse clk_divider_ops linux
clk_fixed_rate_ops
custom clk_ops wrapping
free linux kernel development course
free linux device drivers course

Every base clock type case study in this chapter has quietly used two different
registration styles without calling out the distinction directly. This lecture makes
it explicit: the difference between the generic clk_hw_register() and
type-specific registration helpers like clk_hw_register_fixed_rate(),
and a third, more advanced option — reusing individual callbacks from a built-in
clk_ops struct while overriding just the one your hardware needs to
handle differently.

What You Will Learn

  • The difference between clk_hw_register() and type-specific registration helpers
  • Why clk_fixed_rate_ops implements both recalc_rate and recalc_accuracy
  • That built-in ops structs like clk_divider_ops are exported and directly reusable
  • How to override a single callback while forwarding the rest to a built-in ops struct
  • Building an original gated-divider clock that reuses two callbacks and overrides one

Prerequisites

  • Fixed-rate clock case study (previous lecture)
  • CCF base clock types overview
  • clk_hw embedding and container_of pattern

Two Ways to Register a Clock

clk_hw_register() (and its managed devm_ variant) is the
base registration interface behind every clock in the CCF. It takes a
struct clk_hw * that already has a fully populated
clk_init_data — including ops — attached to it.
Using it directly means you are responsible for building that clk_init_data
yourself, deciding which clk_ops struct to point at.

A type-specific registration helper, such as
clk_hw_register_fixed_rate(), exists purely to save you that work for a
known, common clock shape. Internally it builds the clk_init_data, points
ops at the framework’s own canonical struct for that type, and then calls
clk_hw_register() for you.

Two Registration Styles, One Underlying Call
clk_hw_register_fixed_rate()
builds clk_init_data + ops for you
clk_hw_register()

Take clk_hw_register_fixed_rate() as the example already covered in the
previous lecture:

struct clk_hw *clk_hw_register_fixed_rate(struct device *dev,
                                           const char *name,
                                           const char *parent_name,
                                           unsigned long flags,
                                           unsigned long fixed_rate);

You supply the name, parent, flags, and rate; the framework fills in the rest,
pointing ops at clk_fixed_rate_ops. Worth confirming here: this
built-in ops struct actually implements two callbacks, not just
recalc_rate — it also supplies recalc_accuracy, returning
whatever was stored in the clock’s fixed_accuracy field:

const struct clk_ops clk_fixed_rate_ops = {
    .recalc_rate     = clk_fixed_rate_recalc_rate,
    .recalc_accuracy = clk_fixed_rate_recalc_accuracy,
};

Reusing Individual Callbacks From a Built-In Ops Struct

Registration helpers cover the common case, but real hardware sometimes almost
matches a built-in type except for one detail. A classic example: a divider block that
behaves exactly like the CCF’s generic divider for reading and validating a rate, but
whose parent PLL must be temporarily disabled before the divider register can be
written, then re-enabled afterward.

Rather than reimplementing recalc_rate and round_rate from
scratch, you can reuse clk_divider_ops‘s own implementations directly,
since it is an exported, public struct, and only override set_rate:

Mixing Reused and Custom Callbacks in One clk_ops
.recalc_rate
forwarded to clk_divider_ops
|
.round_rate
forwarded to clk_divider_ops
|
.set_rate
custom, gates PLL first

Here is an original example applying this pattern to a small gated-divider clock,
embedding the CCF’s own struct clk_divider as an inner member rather than
reimplementing it:

/* ep_gated_divider.c - reuses clk_divider_ops, overrides only set_rate */
#include <linux/clk-provider.h>
#include <linux/clk.h>

struct ep_gated_divider {
    struct clk_divider divider;   /* CCF's own divider, embedded */
    struct clk_hw *pll_hw;        /* parent PLL this divider sits behind */
};

static inline struct ep_gated_divider *to_ep_gated_divider(struct clk_hw *hw)
{
    struct clk_divider *div = to_clk_divider(hw);

    return container_of(div, struct ep_gated_divider, divider);
}

static unsigned long ep_gated_div_recalc_rate(struct clk_hw *hw,
                                               unsigned long parent_rate)
{
    return clk_divider_ops.recalc_rate(hw, parent_rate);
}

static long ep_gated_div_round_rate(struct clk_hw *hw, unsigned long rate,
                                     unsigned long *prate)
{
    return clk_divider_ops.round_rate(hw, rate, prate);
}

static int ep_gated_div_set_rate(struct clk_hw *hw, unsigned long rate,
                                   unsigned long parent_rate)
{
    struct ep_gated_divider *gdiv = to_ep_gated_divider(hw);
    bool was_enabled = clk_hw_is_enabled(gdiv->pll_hw);
    int ret;

    if (was_enabled)
        clk_hw_disable(gdiv->pll_hw);

    ret = clk_divider_ops.set_rate(hw, rate, parent_rate);

    if (was_enabled)
        clk_hw_enable(gdiv->pll_hw);

    return ret;
}

static const struct clk_ops ep_gated_divider_ops = {
    .recalc_rate = ep_gated_div_recalc_rate,
    .round_rate  = ep_gated_div_round_rate,
    .set_rate    = ep_gated_div_set_rate,
};

Two callbacks — recalc_rate and round_rate
simply forward to clk_divider_ops‘s own implementations unchanged.
set_rate is the only place hardware-specific behavior lives: gate the PLL,
let the CCF’s built-in divider logic write the new divisor, then ungate the PLL. Nothing
about the divider bitfield math had to be reimplemented.

Three Ways to Build a Clock’s clk_ops

Approach Effort Use When
Type-specific registration helper Lowest — one function call Hardware matches a base type exactly
clk_hw_register() with fully custom clk_ops Highest — every callback written by hand Hardware behavior matches no built-in type at all
clk_hw_register() reusing individual built-in callbacks Medium — override only what differs Hardware is almost a base type, with one extra step

Common Mistakes

  • Reimplementing recalc_rate/round_rate logic that a built-in ops struct already provides
  • Forgetting that to_clk_divider(hw) must be called before reaching an outer embedding struct
  • Calling a built-in ops function pointer without checking it is actually set for that ops struct
  • Skipping the parent PLL gate/ungate step and risking a glitch during set_rate

Best Practices

  • Reach for a registration helper first, plain clk_hw_register() only when needed
  • Reuse a built-in ops struct’s callbacks whenever your hardware matches that type closely
  • Keep custom overrides focused on exactly the one behavior that differs
  • Embed the CCF’s own type struct (like clk_divider) rather than duplicating its fields

Summary and Key Takeaways

  • Registration helpers build clk_init_data and ops for you; clk_hw_register() does not
  • Built-in ops structs like clk_fixed_rate_ops and clk_divider_ops are exported and reusable
  • You can mix reused callbacks with one custom override in the same clk_ops
  • This pattern applies to every base clock type covered earlier in this chapter, not just dividers

Frequently Asked Questions

Do I need to allocate struct clk_fixed_rate or struct clk_divider myself?

Not when using a registration helper — the helper allocates and fills it in for
you. You only work with the struct directly when embedding it inside your own wrapper
for the mix-and-match pattern shown in this lecture.

Is clk_divider_ops safe to reference directly from another driver file?

Yes, it is an exported symbol intended for exactly this kind of reuse.

Can I reuse callbacks from more than one built-in ops struct at once?

Yes, nothing stops a custom clk_ops from forwarding different callbacks to different
built-in ops structs, as long as each one operates on the same underlying clk_hw
correctly.

When should I write a fully custom clk_ops instead of reusing one?

When the hardware’s behavior does not resemble any base clock type closely enough
for reuse to save meaningful effort.

Does clk_fixed_rate_ops really implement recalc_accuracy too?

Yes, in addition to recalc_rate, it returns the clock’s stored fixed_accuracy value
whenever accuracy is queried.

Suggested Images For This Lecture

  • Diagram: registration helper building clk_init_data before calling clk_hw_register()
  • Diagram: mixed clk_ops forwarding two callbacks and overriding one
  • Diagram: embedding struct clk_divider inside a larger gated-divider wrapper

Continue This Free Linux Kernel Development Course

Next, we move to the gate clock case study and implement a complete gate clk_ops
set from scratch, register by register.

Leave a Reply

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