Linux Clock Tree Device Tree Example-Free Linux Device Drivers Course
Modeling a real multi-clock hierarchy end to end, and debugging it with clk_summary
What You Will Learn
device tree modeling with multiple clocks properties
of_clk_get_parent_name() for multi-parent muxes
clk_register_mux() with several candidate parents
reading /sys/kernel/debug/clk/clk_summary
Prerequisites
This lecture assumes familiarity with fixed-rate clocks, mux clocks, and the of_clk_get_parent_name() phandle-parsing helper covered earlier in this series, since we will be combining all three into a single worked example rather than introducing new registration APIs.
Designing the Example Clock Tree
Consider a small SoC-style clock controller with the following topology: a single crystal oscillator feeds three PLL-style derived clocks. Two of those PLLs, together with the raw oscillator itself, are candidate inputs to a multiplexer whose output drives one hardware block. This is a deliberately realistic shape — it is extremely common for a downstream peripheral clock to be selectable between a high-accuracy PLL output and the raw, low-power oscillator, so firmware or a driver can trade accuracy for power depending on the use case.
Example Clock Tree
| PLL1 |—————->| /1,/2,.. |————> BLOCK1
+——–+ +———+Crystal/Osc +——–+ ep_pll2_clk
ep_osc_clk —–> PLL2 |——————+
| +——–+ |
| v
| +———+ ep_out3_clk
+———————————-> MUX |————> BLOCK3
| +———+
| +——–+ ep_pll3_clk ^
+———> PLL3 |——————+
+——–+
ep_out3_clk, the mux output, can be sourced from ep_pll2_clk, ep_pll3_clk, or directly from ep_osc_clk — giving a downstream consumer three selectable options with a single clk_set_parent() call, a capability we covered in the parent functions lecture earlier in this chapter.
Modeling the Tree in the Device Tree
Each node in the diagram becomes its own device tree node, using #clock-cells = <0> since every clock here has exactly one output:
ep_osc: ep-oscillator {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <24000000>;
clock-output-names = "ep_osc_clk";
};
ep_pll2: ep-pll2 {
#clock-cells = <0>;
compatible = "ep,pll-clock";
clocks = <&ep_osc>;
clock-output-names = "ep_pll2_clk";
};
ep_pll3: ep-pll3 {
#clock-cells = <0>;
compatible = "ep,pll-clock";
clocks = <&ep_osc>;
clock-output-names = "ep_pll3_clk";
};
ep_out3: ep-out3-mux {
#clock-cells = <0>;
compatible = "ep,mux-clock";
clocks = <&ep_pll2>, <&ep_pll3>, <&ep_osc>;
clock-output-names = "ep_out3_clk";
};
Notice that ep_out3’s clocks property lists all three candidate parents in a fixed order — that ordering is what the mux driver’s index-based selection will refer back to at runtime, and it must match the order the driver expects when it later calls of_clk_get_parent_name() for each index.
Driver-Side: Registering the Mux With Its Three Parents
The driver for the ep_out3 node reads its own clock-output-names property for the output name, then walks its clocks entries by index to recover each parent’s name before registering itself as a mux:
static int ep_mux_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
const char *clk_name;
const char *parent_names[3];
void __iomem *base;
struct clk_hw *hw;
int i;
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
of_property_read_string(node, "clock-output-names", &clk_name);
for (i = 0; i < 3; i++) parent_names[i] = of_clk_get_parent_name(node, i); hw = devm_clk_hw_register_mux(&pdev->dev, clk_name,
parent_names, ARRAY_SIZE(parent_names),
0, base, 0, 2, 0, NULL);
if (IS_ERR(hw))
return PTR_ERR(hw);
return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_simple_get, hw);
}
Each call to of_clk_get_parent_name(node, i) resolves index i of the clocks property back to that referenced clock’s registered name — index 0 gives “ep_pll2_clk”, index 1 gives “ep_pll3_clk”, index 2 gives “ep_osc_clk” — precisely because those clocks each declared their own name via clock-output-names. This is the same core/downstream-provider relationship covered when we first introduced of_clk_get_parent_name() and struct of_phandle_args earlier in this chapter, now applied across an entire tree rather than a single node.
Verifying the Tree at Runtime With clk_summary
Once every node in the tree has probed, the Common Clock Framework exposes the entire hierarchy — including live rates, enable counts, and parent relationships — through a debugfs file, without needing any extra driver code:
$ sudo mount -t debugfs none /sys/kernel/debug
$ cat /sys/kernel/debug/clk/clk_summary
enable prepare protect duty
clock count count count rate accuracy phase cycle
------------------------------------------------------------------------------------------------
ep_osc_clk 0 0 0 24000000 0 0 50000
ep_pll2_clk 0 0 0 48000000 0 0 50000
ep_out3_clk 1 1 0 48000000 0 0 50000
ep_pll3_clk 0 0 0 96000000 0 0 50000
The indentation in clk_summary mirrors the parent/child tree structure we designed: ep_out3_clk appears nested directly under whichever parent it is currently selecting — in this snapshot, ep_pll2_clk — and its enable count of 1 confirms a consumer currently holds it enabled. If you later call clk_set_parent() to switch ep_out3_clk to ep_pll3_clk or ep_osc_clk, re-reading clk_summary immediately reflects the new indentation and the new inherited rate, making it an extremely fast way to confirm a parent switch actually took effect in hardware rather than only in software state.
Common Mistakes
- Listing parents in the clocks property in a different order than the driver’s parent_names array expects — the mux will select the wrong physical input even though the code compiles and probes cleanly.
- Forgetting that of_clk_get_parent_name() only returns a valid name when the referenced node’s clock-output-names property is present; without it, the lookup silently fails or returns a generic auto-generated name.
- Reading clk_summary before debugfs has been mounted and assuming the clock framework itself is broken — the file simply does not exist until debugfs is mounted.
- Confusing clk_summary’s “rate” column (the currently active rate given the current parent and divider settings) with a clock’s theoretical maximum rate — they are not the same thing once a divider or mux selection is in play.
Best Practices
- Design your device tree clocks/clock-output-names layout on paper (or as a diagram, like the one above) before writing driver code — most clock tree bugs are ordering mistakes, not registration API mistakes.
- Use clk_summary as your first debugging step whenever a downstream peripheral reports the wrong effective clock rate — it is almost always faster than adding printk statements to a probe function.
- Keep clock-output-names consistent with the driver’s own compatible-derived naming scheme so the clk_summary tree stays human-readable as your clock hierarchy grows.
Real-World Use Case
This selectable-PLL-vs-oscillator pattern is exactly how many SoC peripheral blocks — UARTs, timers, and certain communication controllers — let firmware trade accuracy for power. A UART block, for instance, might default to a low-power oscillator-derived clock when idle and switch to a tightly-toleranced PLL-derived clock only when high-baud-rate communication is required, all without touching any code outside a single clk_set_parent() call from the consumer driver.
Summary and Key Takeaways
Building one complete clock tree — from a fixed-rate oscillator root, through PLL-style derived clocks, into a multi-parent mux — ties together nearly every clock provider concept covered so far in this free linux device drivers course: fixed-clock modeling, clocks/clock-output-names device tree conventions, of_clk_get_parent_name() index resolution, and mux registration. Just as important is knowing how to verify that tree at runtime: /sys/kernel/debug/clk/clk_summary gives you the entire live hierarchy — rates, enable counts, and current parent selections — without writing a single line of debug code. With the provider side of the Common Clock Framework now complete, the next lecture shifts focus entirely to the consumer side: how a peripheral driver actually requests, prepares, enables, and uses these clocks once a provider has exposed them.
FAQ
Does the order of entries in a clocks property matter?
Yes — it must exactly match the index order the driver expects when it calls of_clk_get_parent_name(node, i) for each parent, since mux selection is index-based.
Do I need to write any extra code to get clk_summary output?
No — clk_summary is a built-in debugfs file provided automatically by the Common Clock Framework core for every registered clock, once debugfs is mounted.
Why does of_clk_get_parent_name() fail for one of my clocks?
The most common cause is a missing clock-output-names property on the referenced parent node — without it, the name lookup cannot resolve.
Can I see the effect of a clk_set_parent() call without rebooting?
Yes — re-read /sys/kernel/debug/clk/clk_summary immediately after the call; the tree’s indentation and inherited rate update live.
Is clk_summary safe to read on a production system?
Reading it is safe and side-effect free; it is a read-only debugfs snapshot of the current clock state.
What does the enable count column in clk_summary mean?
It reflects how many active clk_enable() references currently exist on that clock, letting you spot clocks left enabled unexpectedly.
Keep Building Real Kernel Skills
This lecture is part of EmbeddedPathashala’s free linux kernel development course covering the Common Clock Framework end to end.
