What is Fixed-Rate Clock Case Study in Linux Kernel

Fixed-Rate Clock Case Stud-Free Linux Device Drivers Course
Free Linux Kernel Development Course · Common Clock Framework · Kernel 6.x
Chapter 4 · Lecture 15
Reading Time: 14 min
Level: Intermediate

Keywords covered in this lecture

linux fixed rate clock
struct clk_fixed_rate
clk_hw_register_fixed_rate
to_clk_fixed_rate container_of
free linux kernel development course
free embedded systems course

Of the six base clock types this chapter introduced, the linux fixed-rate
clock
is the simplest one to study first: it cannot gate, cannot change rate,
and cannot switch parents, which means every mandatory/optional rule from the last two
lectures reduces to almost nothing. That makes it the perfect first full case study
for seeing exactly how a base clock type’s internal wrapper struct, container_of
helper, and clk_ops set fit together in practice.

What You Will Learn

  • The struct clk_fixed_rate wrapper and its to_clk_fixed_rate() helper
  • Why a fixed-rate clock needs no clk_ops callbacks written by hand
  • The manual static-declaration style versus the modern registration helper
  • Building a clk_hw_onecell_data map for two fixed-rate clocks
  • Registering fixed-rate clocks end to end with build/run verification

Prerequisites

  • Linux clock flags reference guide (previous lecture)
  • CCF base clock types overview
  • clk_hw embedding and container_of pattern

The struct clk_fixed_rate Wrapper

A fixed-rate clock’s entire hardware-specific state is just a number: the rate itself.
The CCF represents that with a small wrapper struct, matching the embedding pattern from
an earlier lecture — a plain struct clk_hw plus whatever extra fields
the clock type needs:

struct clk_fixed_rate {
    struct clk_hw hw;
    unsigned long fixed_rate;
    unsigned long fixed_accuracy;
    unsigned long flags;
};

#define to_clk_fixed_rate(_hw) \
    container_of(_hw, struct clk_fixed_rate, hw)

Two fields that grew since older references

Current kernels carry a fixed_accuracy field (accuracy in parts per
billion) that some older material omits, and flags is now a full
unsigned long rather than a narrower u8, giving room for
additional per-clock hardware flags such as CLK_FIXED_RATE_PARENT_ACCURACY,
which tells the clock to inherit its parent’s accuracy instead of using
fixed_accuracy directly.

to_clk_fixed_rate() is exactly the container_of helper pattern from an
earlier lecture: hand it the clk_hw * any callback receives, and it hands
back a pointer to the full clk_fixed_rate struct, giving access to
fixed_rate.

Why No Custom clk_ops Are Needed

A fixed-rate clock cannot gate, cannot change rate, and has a single fixed parent, so
by the mandatory-callback table from two lectures ago, none of the gate, rate-change, or
multi-parent callback groups apply. The only callback the built-in
clk_fixed_rate_ops implements is recalc_rate, which simply
returns to_clk_fixed_rate(hw)->fixed_rate — there is nothing else
to report.

Manual Static Declaration vs the Modern Helper

Older code, and some still-maintained SoC clock drivers, declare fixed-rate clocks as
static structs directly, filling in clk_init_data by hand. It is worth
seeing this manual form once, because it makes the wrapper-struct-plus-clk_ops
relationship completely explicit:

/* ep_fixed_manual.c - manual static declaration style, for understanding the internals */
#include <linux/clk-provider.h>

static struct clk_fixed_rate ep_ref_clk = {
    .fixed_rate = 26000000,
    .hw.init = &(struct clk_init_data){
        .name = "ep_ref_clk",
        .ops = &clk_fixed_rate_ops,
        .num_parents = 0,
    },
};

In real driver code today, you almost never write this by hand. The CCF provides
clk_hw_register_fixed_rate() (and its managed devm_ variant),
which builds this same struct internally and registers it in one call — this is
the version you should reach for in new drivers.

/* ep_fixed_modern.c - modern registration helper, recommended for new drivers */
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk-provider.h>
#include <linux/of.h>

#define EP_CLKID_REF   0
#define EP_CLKID_PLL   1
#define EP_NR_CLKS     2

static int ep_fixed_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct clk_hw_onecell_data *hw_data;
    struct clk_hw *ref_hw, *pll_hw;

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

    ref_hw = devm_clk_hw_register_fixed_rate(dev, "ep_ref_clk", NULL, 0, 26000000);
    if (IS_ERR(ref_hw))
        return PTR_ERR(ref_hw);

    pll_hw = devm_clk_hw_register_fixed_rate(dev, "ep_fixed_pll", NULL, 0, 45000000);
    if (IS_ERR(pll_hw))
        return PTR_ERR(pll_hw);

    hw_data->hws[EP_CLKID_REF] = ref_hw;
    hw_data->hws[EP_CLKID_PLL] = pll_hw;

    return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, hw_data);
}

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

static struct platform_driver ep_fixed_driver = {
    .probe = ep_fixed_probe,
    .driver = {
        .name = "ep_clk_fixed_demo",
        .of_match_table = ep_fixed_of_match,
    },
};
module_platform_driver(ep_fixed_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala fixed-rate clock case study demo");

Matching device tree node:

ep_clk_fixed: clock-controller@5000 {
    compatible = "ep,clk-fixed-demo";
    #clock-cells = <1>;
};

Build and Run

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

Expected clk_summary excerpt:

ep_ref_clk                1        1        0    26000000          0
ep_fixed_pll              1        1        0    45000000          0

Manual Declaration vs Registration Helper

Aspect Manual Static Struct clk_hw_register_fixed_rate()
Lines of setup code More, written by hand One function call
Cleanup on driver removal Manual, if needed at all Automatic with the devm_ variant
Educational value High — shows every field explicitly Lower — details are hidden
Recommended for new drivers No Yes

Common Mistakes

  • Writing a custom recalc_rate for a fixed-rate clock when clk_fixed_rate_ops already provides one
  • Forgetting that fixed-rate clocks cannot implement set_rate at all
  • Manually declaring the struct in new code instead of using the registration helper
  • Mixing up fixed_rate and fixed_accuracy when only one of the two is relevant

Best Practices

  • Use clk_hw_register_fixed_rate() (or its devm_ variant) in new drivers
  • Reserve the manual struct style for understanding internals or unusual edge cases
  • Keep a fixed-rate clock’s name descriptive of its physical role (oscillator, reference, PLL output)
  • Use CLK_FIXED_RATE_PARENT_ACCURACY only when the parent’s accuracy is genuinely relevant

Summary and Key Takeaways

  • struct clk_fixed_rate embeds clk_hw plus fixed_rate, fixed_accuracy, and flags
  • clk_fixed_rate_ops implements only recalc_rate, since nothing else applies
  • clk_hw_register_fixed_rate() replaces manual static struct declarations in new drivers
  • The mandatory-callback rules from earlier lectures explain exactly why so little is needed here

Frequently Asked Questions

Can a fixed-rate clock ever change its rate at runtime?

No. By design it has no set_rate callback, so its rate is fixed for the lifetime of
the registration.

Why does struct clk_fixed_rate have a flags field of its own?

It carries hardware-type-specific flags such as CLK_FIXED_RATE_PARENT_ACCURACY,
separate from the framework-level flags stored in clk_init_data.flags.

Is the manual static declaration style still used anywhere?

Yes, in some older or still-unconverted SoC clock drivers, but new drivers should
prefer the registration helper functions instead.

Does a fixed-rate clock need a parent?

No, it commonly has none, representing something like an external crystal
oscillator that is the root of a clock tree.

What does clk_hw_register_fixed_rate() return on failure?

An ERR_PTR-encoded error, which should be checked with IS_ERR() and converted with
PTR_ERR() just like any other clk_hw registration helper in this chapter.

Suggested Images For This Lecture

  • Diagram: struct clk_fixed_rate fields and to_clk_fixed_rate() recovery
  • Side-by-side diagram: manual struct declaration vs registration helper call
  • Screenshot: clk_summary showing two registered fixed-rate clocks

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.

Leave a Reply

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