Keywords covered in this lecture
container_of clock driver
clock provider driver
clk_ops private data
free linux device drivers course
free embedded systems course
So far in this chapter we have only looked at the device tree side of the Common
Clock Framework — how a provider is exposed and how a consumer looks a clock up.
None of that code actually controls real hardware. From this lecture onward we switch
to the driver side, starting with the single most important structural decision in any
clock driver: the linux clk_hw embedding pattern, where you place a
full struct clk_hw inside your own private data structure instead of
pointing to one.
What You Will Learn
- Why clk_ops callbacks only ever receive a struct clk_hw pointer
- Why clk_hw should be embedded, not referenced through a pointer
- How container_of() recovers your private structure from that pointer
- Writing a reusable to_<driver>_clk() helper
- Structuring a multi-clock private data type around this pattern
Prerequisites
- CCF data structures: struct clk_hw, struct clk_ops, struct clk_init_data
- Basic C: structs, pointers, and the container_of macro
- Dynamic onecell clock allocation pattern (previous lecture)
Writing a Clock Provider Driver
A device tree node only describes hardware — it never runs any code. The actual
work of enabling a clock gate, changing a divider, or reading back a rate has to live in
a driver, and that driver is built around the callbacks in struct clk_ops.
Every one of those callbacks receives exactly one piece of context: a
struct clk_hw *. If your driver manages more state than a bare
clk_hw can hold — a register base, an enable bit, a spinlock, a parent
device pointer — you need a reliable way to get from that pointer back to your own
private structure.
Why Embedding Beats a Separate Pointer
You could store a pointer to your private data inside clk_hw, but the CCF does not
provide a spare field for that. The accepted approach instead is the opposite direction:
embed the struct clk_hw itself as a plain member inside your private struct.
Because the address of that member is a fixed, known offset from the start of your
struct, the container_of() macro can walk backwards from the
clk_hw * the callback receives to the enclosing structure, with no extra
allocation and no extra pointer chasing.
This is exactly the same technique used throughout the kernel to recover a parent
structure from an embedded member — list_head and
work_struct are two familiar examples that use it the same way.
Building the Embedding Pattern
Here is an original private clock structure for a small gated clock block, followed by
the driver-wide context structure that holds several of them, and the helper that
recovers a pointer to one instance from its clk_hw:
/* ep_gate_clk.h - original clk_hw embedding example for a gated clock block */
#include <linux/clk-provider.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#define EP_GATE_MAX_CLKS 4
struct ep_gate_driver_data;
struct ep_gate_clk_hw {
struct clk_hw hw;
struct clk_init_data init;
u8 enable_bit;
struct ep_gate_driver_data *drvdata;
};
struct ep_gate_driver_data {
void __iomem *base;
spinlock_t lock;
struct clk *parent_clk;
struct ep_gate_clk_hw clks[EP_GATE_MAX_CLKS];
};
static inline struct ep_gate_clk_hw *to_ep_gate_clk(struct clk_hw *hw)
{
return container_of(hw, struct ep_gate_clk_hw, hw);
}
Notice three design choices in this header, each worth calling out:
- struct clk_hw hw is embedded by value inside ep_gate_clk_hw, not a pointer
- ep_gate_clk_hw stores a back-pointer to the shared driver data (register base, lock, parent clock)
- ep_gate_driver_data holds a fixed array of ep_gate_clk_hw, so all clocks share one allocation
With this layout, any clk_ops callback can recover everything it needs in
one line:
static int ep_gate_clk_enable(struct clk_hw *hw)
{
struct ep_gate_clk_hw *eclk = to_ep_gate_clk(hw);
struct ep_gate_driver_data *drv = eclk->drvdata;
unsigned long flags;
u32 val;
spin_lock_irqsave(&drv->lock, flags);
val = readl(drv->base);
val |= BIT(eclk->enable_bit);
writel(val, drv->base);
spin_unlock_irqrestore(&drv->lock, flags);
return 0;
}
Notice how to_ep_gate_clk(hw) is the only place the container_of logic
appears. Every other callback (disable, is_enabled,
recalc_rate, and so on) can reuse it the exact same way, which keeps the
bookkeeping in one place instead of repeating a raw container_of() call in
every function.
Why this scales to multi-clock providers
Recall from the dynamic onecell lecture that a clk_hw_onecell_data array
just stores an array of struct clk_hw *. With this pattern, each entry in
that array simply points at the embedded hw field inside one element of
ep_gate_driver_data.clks[] — the two patterns are designed to fit
together directly, with no adapter code needed in between.
Embedding vs a Separate Pointer
| Approach | Extra Allocation | Lookup Cost | Used In Practice |
|---|---|---|---|
| Embed struct clk_hw by value | None | One container_of(), O(1) | Yes — the standard kernel pattern |
| Store a separate pointer to private data elsewhere | Yes, plus bookkeeping to keep it in sync | Extra indirection or table lookup | Rare, and generally discouraged |
Common Mistakes
- Declaring struct clk_hw *hw instead of struct clk_hw hw in the private struct
- Writing container_of() inline in every callback instead of one shared helper
- Forgetting to set init.ops and init.name before registering each embedded clk_hw
- Mixing up which struct member name container_of() should reference
Best Practices
- Always define a single to_<driver>_clk() helper right next to your private struct
- Keep shared resources (register base, lock, parent clock) in one driver-wide struct
- Store a back-pointer to that shared struct inside each embedded clk_hw wrapper
- Prefer a fixed or dynamically sized array of these wrapper structs over separate allocations per clock
Summary and Key Takeaways
- clk_ops callbacks only ever receive a struct clk_hw pointer as context
- Embedding clk_hw by value inside your own struct avoids any extra allocation
- container_of() recovers the enclosing private struct from that embedded member
- A single to_<driver>_clk() helper keeps this logic in one place
- This pattern connects directly to the onecell array from the previous lecture
Frequently Asked Questions
Why can’t clk_ops callbacks just take a void *data parameter?
The CCF API fixes the callback signature to take a struct clk_hw pointer so that every
clock in the system, regardless of driver, can be manipulated through one uniform interface.
Is embedding clk_hw the only valid pattern?
It is the pattern used throughout mainline clock drivers because it needs no extra
allocation or bookkeeping, which is why this course teaches it as the default approach.
What does container_of() actually compute?
It subtracts the compile-time byte offset of the named member from the pointer you pass
in, giving you the address of the structure that contains that member.
Can I embed more than one clk_hw in the same struct?
Not in the same struct directly, since container_of() needs a single unambiguous member
name. Instead, use an array of wrapper structs, each embedding exactly one clk_hw, as shown
in this lecture.
Where does struct clk_init_data fit into this pattern?
It is typically embedded alongside clk_hw inside the same wrapper struct and populated
with the clock’s name, ops, and flags before the clk_hw is registered.
Suggested Images For This Lecture
- Diagram: struct clk_hw embedded inside a larger private driver struct
- Diagram: container_of() pointer arithmetic from clk_hw back to private struct
- Diagram: array of embedded clk_hw wrapper structs feeding a onecell array
Continue This Free Linux Kernel Development Course
Next, we implement the actual clk_ops callbacks — enable, disable, recalc_rate,
and set_rate — for a real gated clock driver.
