Every mux clock lecture so far in this free linux device drivers course has assumed one thing: that the multiplexer’s select bits live in a memory-mapped I/O register the CPU can read and write directly with clk_readl()/clk_writel(). That assumption breaks down in two common real-world situations — clock generator chips that sit behind an I2C or SPI bus, and simple hardware where a single GPIO line is literally the select signal. This lecture covers a linux non-mmio clock multiplexer approach for both, and shows exactly how the kernel’s own GPIO-controlled clock mux driver has evolved well beyond what older reference material describes.
I2C clock mux
gpio-mux-clock
clk_parent_data
free linux device drivers course
What You Will Learn
- Why
clk_hw_register_mux()andclk_register_mux_table()cannot be used for bus-controlled (I2C/SPI) mux chips - How to build a custom low-level mux driver on top of plain
clk_hw_register(), reusingclk_mux_val_to_index()from the previous lecture - The GPIO-controlled clock mux alternative, its two-parent limitation, and how it is implemented today
- How the kernel’s GPIO clock mux driver has been restructured since older reference books were written — and what that means if you were expecting an exported registration function
Prerequisites
This lecture builds directly on the struct clk_mux/clk_mux_ops lectures earlier in this chapter, the index-to-value encoding and decoding lectures just before this one, and the sleep-vs-atomic .prepare/.enable distinction covered back when this chapter discussed I2C/SPI gate clocks.
Why MMIO Mux Helpers Don’t Fit Bus-Controlled Hardware
clk_hw_register_mux() and its table-based sibling assume the register is reachable with a plain load/store instruction, and their internal read-modify-write is protected with a spinlock — spinlock-protected sections must never sleep. An I2C or SPI transaction, however, virtually always sleeps: it waits on a bus controller, a DMA completion, or an interrupt. That single constraint rules out the generic mux helpers entirely for chips like clock generator ICs — you cannot call a sleeping bus transfer from inside a spinlock-protected callback.
I2C / SPI transaction → can sleep, waits on bus controller
↓
Conclusion: a bus-controlled mux cannot reuse the MMIO mux ops directly
The Low-Level Approach: clk_hw_register() Plus Custom Ops
The way around this is the same pattern this chapter has used before for I2C/SPI gate clocks: skip the type-specific registration helper entirely, embed struct clk_hw by value inside your own driver-private structure, and register it with the generic clk_hw_register(), supplying your own clk_ops. The kernel’s own driver for the Si5351a/b/c programmable I2C clock generator from Silicon Labs follows exactly this shape — it is worth reading as a real-world reference once you understand the pattern, though this lecture builds its own original example rather than walking through that file line by line.
At minimum, each mux input clock must be registered as a parent of the mux clock, and the mux’s clk_ops must supply .get_parent and .set_parent — every other callback is optional and only needed if your chip supports rate changes, gating, or similar on that same clock node.
Original Demo: ep_i2c_mux_clk
Here is an original driver for a fictitious I2C-controlled 2-parent clock select chip. Its select register is written and read through i2c_smbus calls, so both callbacks are implemented as .prepare/.unprepare-adjacent sleeping operations rather than atomic .enable/.disable-style ones, matching the sleep-safe pattern from the earlier I2C/SPI gate clock lecture:
struct ep_i2c_mux_clk {
struct clk_hw hw;
struct i2c_client *client;
};
#define to_ep_i2c_mux_clk(_hw) container_of(_hw, struct ep_i2c_mux_clk, hw)
#define EP_MUX_SELECT_REG 0x04
static u8 ep_i2c_mux_get_parent(struct clk_hw *hw)
{
struct ep_i2c_mux_clk *emux = to_ep_i2c_mux_clk(hw);
int val;
val = i2c_smbus_read_byte_data(emux->client, EP_MUX_SELECT_REG);
if (val < 0) return 0; return clk_mux_val_to_index(hw, NULL, 0, val); } static int ep_i2c_mux_set_parent(struct clk_hw *hw, u8 index) { struct ep_i2c_mux_clk *emux = to_ep_i2c_mux_clk(hw); return i2c_smbus_write_byte_data(emux->client, EP_MUX_SELECT_REG, index);
}
static const struct clk_ops ep_i2c_mux_ops = {
.get_parent = ep_i2c_mux_get_parent,
.set_parent = ep_i2c_mux_set_parent,
.determine_rate = __clk_mux_determine_rate,
};
static int ep_i2c_mux_probe(struct i2c_client *client)
{
struct ep_i2c_mux_clk *emux;
struct clk_init_data init = {};
static const char * const parents[] = { "ep_ref_a", "ep_ref_b" };
emux = devm_kzalloc(&client->dev, sizeof(*emux), GFP_KERNEL);
if (!emux)
return -ENOMEM;
init.name = "ep_i2c_mux_clk";
init.ops = &ep_i2c_mux_ops;
init.parent_names = parents;
init.num_parents = ARRAY_SIZE(parents);
init.flags = CLK_SET_RATE_PARENT;
emux->client = client;
emux->hw.init = &init;
return devm_clk_hw_register(&client->dev, &emux->hw);
}
Notice this reuses clk_mux_val_to_index() directly from the previous lecture rather than reimplementing any decoding — since our chip has no exotic bit-encoding, calling it with a NULL table and no flags is just a bounds-checked passthrough.
Build and load against a devicetree node describing this I2C client, then exercise it:
$ echo ep_ref_b > /sys/kernel/debug/clk/ep_i2c_mux_clk/clk_parent
Expected dmesg output:
ep_i2c_mux_clk: probed on i2c bus, 2 parents (ep_ref_a, ep_ref_b)
ep_i2c_mux_clk: set_parent(index=1) -> i2c write reg 0x04 = 0x01
ep_i2c_mux_clk: get_parent() -> i2c read reg 0x04 = 0x01, index = 1
The GPIO Mux Clock Alternative
For the simplest possible case — exactly two parent clocks, switched by toggling one GPIO line high or low — the kernel provides a dedicated, self-contained driver rather than requiring you to write one. This is the most restrictive kind of multiplexer in the framework: it only ever supports two inputs, since a single GPIO can only represent two states.
parent1 (fixed-clock, e.g. 24.576 MHz) → select input 1
select GPIO (0 or 1) → chooses active input → CLK out
How It Is Implemented in Current Kernels
This is the part worth slowing down on, because the implementation has moved on considerably from how older reference material describes it. In current kernels, drivers/clk/clk-gpio.c is a single self-contained module built entirely around device tree matching — there is no longer a public, exported registration function that another driver’s probe routine can call directly with a clock name, a parent-name array, and a flags value.
Instead, the module registers its own platform_driver matching two compatible strings, "gpio-mux-clock" and "gpio-gate-clock". When the driver core matches either compatible against a device tree node, the module’s own probe function does all the work:
- it determines whether the node is a mux or a gate from the compatible string;
- for a mux, it reads the parent count with
of_clk_get_parent_count()and rejects anything other than exactly two parents; - it requests the GPIO itself with
devm_gpiod_get(), using the property name"select"for a mux or"enable"for a gate; - it registers the clock through an internal helper and adds a hardware clock provider with
devm_of_clk_add_hw_provider().
Internally the two parent clocks are no longer wired up by passing a parent_names string array at all — they are referenced positionally through struct clk_parent_data entries carrying just an index field (0 and 1), letting the core clock code resolve each parent from the device tree’s own clocks property order instead of from a name the driver would otherwise have to know in advance.
The practical consequence: this clock type is genuinely DT-instantiable only, with no code-level entry point for a custom driver to call — a meaningful modernization compared to older material that shows an exported function taking an explicit name and parent-name array as parameters. If your hardware fits the two-parent GPIO-select shape, you write a device tree node and nothing else; there is no C code to author for this clock at all.
Device Tree Example
clocks {
ep_osc_22: oscillator-22 {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <22579200>;
};
ep_osc_24: oscillator-24 {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <24576000>;
};
ep_clk_mux: multiplexer {
compatible = "gpio-mux-clock";
clocks = <&ep_osc_22>, <&ep_osc_24>;
#clock-cells = <0>;
select-gpios = <&gpio 17 GPIO_ACTIVE_HIGH>;
};
};
Original Consumer Demo: ep_clkmux_consumer
Since there is no registration API left to call directly, the meaningful original demo here is a consumer driver that requests the resulting clock and reports which oscillator is active:
static int ep_clkmux_consumer_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct clk *mux_clk;
unsigned long rate;
mux_clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(mux_clk))
return dev_err_probe(dev, PTR_ERR(mux_clk),
"failed to get gpio mux clock\n");
rate = clk_get_rate(mux_clk);
dev_info(dev, "ep_clkmux_consumer: active parent rate = %lu Hz\n", rate);
return 0;
}
Expected dmesg output, toggling the select GPIO between probes:
ep_clkmux_consumer: active parent rate = 22579200 Hz
… (select GPIO driven high) …
ep_clkmux_consumer: active parent rate = 24576000 Hz
One further evolution worth knowing about: current kernels add a third compatible string in this same file, "gated-fixed-clock", for a fixed-rate clock that is gated by an optional GPIO and an optional supply regulator together — a combination the older single mux/gate split didn’t cover. It isn’t a mux at all, but it shows this driver has kept growing well past what the original two compatibles offered.
Comparison: Three Ways to Build a Clock Mux
| Approach | Registration | Control Mechanism | Max Parents | Can Sleep? |
|---|---|---|---|---|
| MMIO mux | clk_hw_register_mux() / clk_register_mux_table() |
Memory-mapped register | Any | No — spinlock-protected |
| I2C/SPI mux | Plain clk_hw_register() + custom ops |
Bus transaction | Any (driver-defined) | Yes — must use prepare/unprepare |
| GPIO mux clock | DT-only, "gpio-mux-clock" compatible |
Single GPIO line | Exactly 2 | Yes — gpiod_set_value_cansleep() |
Common Mistakes and Troubleshooting
- Trying to call a GPIO mux registration function from your own driver. There is no exported entry point to call in current kernels — the whole thing is DT-driven from inside
clk-gpio.citself. If you need custom logic around a GPIO-selected clock, you’ll need to write your own low-level driver rather than reusing this one. - Assuming I2C/SPI mux callbacks can run atomically. As with I2C/SPI gate clocks earlier in this chapter, bus transactions must happen in
.prepare/.unprepare-safe context, never in something the framework may call from atomic context. - Forgetting the two-parent restriction for gpio-mux-clock. Any device tree node with a different parent count for this compatible will fail to probe with an explicit error rather than silently truncating to two.
- Reimplementing value-to-index decoding by hand in a bus-controlled mux driver. Reuse
clk_mux_val_to_index()from the previous lecture instead — it works for any driver, not just MMIO ones.
Best Practices
- Choose the GPIO mux clock only when your hardware genuinely has exactly two parents selected by one line — for anything more complex, write a low-level driver instead.
- For bus-controlled mux chips, keep all sleeping bus I/O out of
.enable/.disable/.get_parent/.set_parentonly where those callbacks are documented as sleep-safe for your chosen registration path, and otherwise route hardware access through.prepare/.unprepare. - Reuse framework helpers like
clk_mux_val_to_index()and__clk_mux_determine_ratewherever your custom mux ops need the same semantics as the built-in ones, instead of duplicating logic.
Summary and Key Takeaways
Not every clock multiplexer lives behind a memory-mapped register. This lecture covered the two standard alternatives the Common Clock Framework supports: a hand-written low-level driver built on plain clk_hw_register() for I2C/SPI-controlled mux chips, and the kernel’s own dedicated, DT-only GPIO mux clock for the simplest two-parent case. You also saw how that GPIO driver has been restructured since older reference material was written — from an exported, name-and-flags-driven registration function into a fully self-contained, compatible-string-matched platform driver using positional parent references. Understanding both paths means you’re no longer limited to MMIO-only hardware when designing a clock provider driver.
Frequently Asked Questions
Why can’t I use clk_hw_register_mux() for an I2C-controlled clock generator chip?
Its internal read-modify-write is protected by a spinlock, and spinlock-protected code must never sleep, while I2C transactions virtually always sleep waiting on the bus controller.
Is there still an exported function to register a GPIO mux clock from my own driver?
No — in current kernels the GPIO mux registration helper is a static, internal function used only by clk-gpio.c’s own platform driver, which is matched entirely from the device tree compatible string.
How many parents can a gpio-mux-clock have?
Exactly two — the driver explicitly rejects any other parent count during probe, since a single GPIO can only represent two states.
How are the two parent clocks identified if there’s no parent-names array anymore?
Through struct clk_parent_data entries carrying just a positional index (0 and 1), which the core clock code resolves against the device tree node’s own clocks property order.
Can I reuse clk_mux_val_to_index() in a custom I2C/SPI mux driver?
Yes — it’s exported specifically so any driver managing its own mux register or bus access can reuse the same index-decoding rules instead of writing new logic.
What is the gated-fixed-clock compatible mentioned alongside the GPIO mux driver?
It’s a related but separate clock type in the same driver file for a fixed-rate clock gated by an optional GPIO and an optional supply regulator together — it is not a multiplexer.
Continue Learning Linux Kernel Clock Drivers
This lecture is part of EmbeddedPathashala’s free linux device drivers course covering the Common Clock Framework in depth.
