Keywords covered in this lecture
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.
