What Is Clock Mux Index Conversion-Free Linux Device Drivers Course

PREV_LEC  |  NEXT_LEC

Clock Mux Index Conversion-Free Linux Device Drivers Course
How the Common Clock Framework turns a mux register value into a parent index — and back

In the previous lecture of this free Linux kernel development course, you learned the precedence rules the Common Clock Framework (CCF) uses when it turns a parent index into a register value for clk_mux_set_parent(). This lecture completes the picture: it walks through the actual register write that clk_mux_set_parent() performs, then works through clk_mux_get_parent() and its companion helper clk_mux_val_to_index() — the exact reverse operation, converting a raw hardware register value back into the parent index the rest of the clock tree understands. If you are building any linux clock mux index conversion logic of your own, whether inside a standard clk_hw_register_mux() clock or a custom low-level mux driver, this is the code you need to understand cold.

clk_mux_get_parent
clk_mux_val_to_index
CLK_MUX_HIWORD_MASK
ffs()
free linux kernel development course

What You Will Learn

  • How clk_mux_set_parent() actually writes the mux register, including the special single-write path for CLK_MUX_HIWORD_MASK hardware
  • How clk_mux_get_parent() extracts the raw mux field from the register
  • The full linux clock mux index conversion algorithm inside clk_mux_val_to_index(), statement by statement
  • Why ffs() is used to decode CLK_MUX_INDEX_BIT hardware, and why the val && guard matters
  • Why this helper is exported and reusable by drivers that never call clk_hw_register_mux() at all

Prerequisites

This lecture assumes you have already been through the mux clock lectures earlier in this chapter — specifically the struct clk_mux layout, the five CLK_MUX_* flags, and clk_hw_register_mux(), as well as the index-to-value encoding precedence order covered right before this one. Everything here is the mirror image of that encoding step.

Step One: What clk_mux_set_parent() Actually Writes

Once the framework has decided which raw register value corresponds to the requested parent index (using the encoding rules from the previous lecture), clk_mux_set_parent() still has to get that value into hardware safely. Two details matter here: locking, and whether the mux uses a “hiword mask” write style.

Register Write Path
lock acquired (spinlock, or annotated no-op via mux->lock == NULL)
→ is CLK_MUX_HIWORD_MASK set?
yes: reg = mask << (shift + 16) [upper 16 bits = write-enable mask]
no: reg = clk_readl(reg) & ~(mask << shift) [read-modify-write]
→ reg |= (val << shift)
→ clk_writel(reg, mux->reg)
lock released

Locking is conditional on whether the clock actually has a spinlock. Many mux clocks share a register with unrelated bits controlled by other clocks, so the read-modify-write in the plain path must be atomic with respect to any other CPU touching that register — hence the spin_lock_irqsave() around it. If no spinlock was supplied at registration time, the framework still emits __acquire()/__release() annotations so that sparse’s context-imbalance checker doesn’t flag the conditional locking as a bug; these are no-ops at runtime, purely for static analysis.

The CLK_MUX_HIWORD_MASK path is the more interesting one. Hardware that supports this convention reserves the upper 16 bits of a 32-bit register as a “this write is allowed to touch these bits” mask, and the lower 16 bits as the actual value. That means the driver never needs to read the register at all before writing it — it can compute the mask and the value in one shot and issue a single write, which sidesteps race conditions with other fields in the same register entirely. This is why hiword-mask hardware doesn’t need the spinlock in the same way: the single write is atomic by construction.

The plain path, in contrast, must read the current register value, clear only the mux’s own bit field with ~(mask << shift), OR in the new value shifted into position, and write the whole register back — a genuine read-modify-write that depends on nothing else changing the register in between.

Step Two: clk_mux_get_parent() — Extracting the Raw Field

clk_mux_get_parent() is deliberately simple. It reads the register, shifts right by mux->shift to move the mux’s bit field down to bit 0, then ANDs with mux->mask to strip away any unrelated bits that happen to live in the same register:

static u8 clk_mux_get_parent(struct clk_hw *hw)
{
    struct clk_mux *mux = to_clk_mux(hw);
    u32 val;

    val = clk_readl(mux->reg) >> mux->shift;
    val &= mux->mask;

    return clk_mux_val_to_index(hw, mux->table, mux->flags, val);
}

Notice that clk_mux_get_parent() does not return this raw value directly — it hands it to clk_mux_val_to_index(), because CCF’s internal accounting always deals in parent indices (0, 1, 2, …), never raw register encodings. Every caller of .get_parent across the whole clock tree — reparenting logic, debugfs, clk_get_parent() — expects an index, so this conversion step is mandatory, not optional.

Step Three: clk_mux_val_to_index() — The Conversion Algorithm

This is the exported helper that does the real work, and it is worth reading statement by statement because it is reused far beyond clk-mux.c itself — any driver that manages its own mux register access (I2C/SPI-based mux chips, for example, which you’ll see in the next lecture) can call this same function instead of re-implementing the decoding logic.

int clk_mux_val_to_index(struct clk_hw *hw, u32 *table,
                          unsigned int flags, unsigned int val)
{
    int num_parents = clk_hw_get_num_parents(hw);

    if (table) {
        int i;

        for (i = 0; i < num_parents; i++) if (table[i] == val) return i; return -EINVAL; } if (val && (flags & CLK_MUX_INDEX_BIT)) val = ffs(val) - 1; if (val && (flags & CLK_MUX_INDEX_ONE)) val--; if (val >= num_parents)
        return -EINVAL;

    return val;
}
EXPORT_SYMBOL_GPL(clk_mux_val_to_index);

Walking through it in order:

  • Table path first. If an explicit table array was supplied at registration, it always takes priority — this matches the encoding side, where an explicit table also wins over any arithmetic flag. The function does a linear scan over num_parents entries looking for the one that matches the raw val, and returns that position as the index. If nothing matches, the hardware reported a value the driver never told CCF about, and the function returns -EINVAL rather than guessing.
  • CLK_MUX_INDEX_BIT decoding. If no table exists and this flag is set, the raw value is treated as a one-hot bitmask rather than a plain number (recall from the encoding lecture that index n was written as 1 << n). ffs(val) — “find first set” — returns the 1-based position of the least significant set bit, so subtracting 1 converts that bit position back into a zero-based index. For example, a raw value of 0b0100 has its lowest set bit at position 3, so ffs() returns 3, and the index becomes 2.
  • CLK_MUX_INDEX_ONE decoding. If this flag is set, the hardware’s counting started at 1 instead of 0 (mirroring the +1 on the encoding side), so the function simply decrements the value once more.
  • Bounds check. After any decoding, the resulting value must be strictly less than num_parents. If the hardware reports something out of range — a reserved or invalid mux setting — the function returns -EINVAL instead of letting an out-of-bounds index propagate into the rest of the clock tree.

Why the “val &&” Guard Exists

Both flag branches are guarded with if (val && ...) rather than just if (flags & ...). This matters because a raw register value of 0 is always meant to decode to index 0, regardless of which flag is active — there is no “bit 0 of a one-hot value” to find, and there is nothing to decrement below zero. Skipping the decode step entirely when val is already 0 avoids both an ill-defined ffs(0) interpretation and an accidental underflow from the CLK_MUX_INDEX_ONE decrement.

Encode vs Decode: A Side-by-Side View

Flag / Case Index → Value (set_parent) Value → Index (get_parent)
Explicit table val = table[index] linear scan for matching entry, return its position
CLK_MUX_INDEX_BIT val = 1 << index index = ffs(val) - 1
CLK_MUX_INDEX_ONE val = index + 1 index = val - 1
Plain sequential val = index index = val

Original Demo: ep_mux_roundtrip_demo

To make this concrete, here is an original platform driver that registers a three-parent MMIO mux with CLK_MUX_INDEX_BIT set, then exercises both directions: it calls clk_set_parent() to force a specific index, reads the raw register value back out through a debug helper, and confirms clk_get_parent() reports the same index it started with.

#define EP_MUX_REG_OFFSET   0x10
#define EP_MUX_SHIFT        4
#define EP_MUX_WIDTH        3

struct ep_mux_roundtrip {
    struct clk_mux   mux;
    void __iomem    *base;
};

static const char * const ep_mux_parents[] = {
    "ep_osc_a", "ep_osc_b", "ep_osc_c",
};

static int ep_mux_roundtrip_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct ep_mux_roundtrip *emr;
    struct clk_hw *hw;
    void __iomem *base;

    emr = devm_kzalloc(dev, sizeof(*emr), GFP_KERNEL);
    if (!emr)
        return -ENOMEM;

    base = devm_platform_ioremap_resource(pdev, 0);
    if (IS_ERR(base))
        return PTR_ERR(base);

    hw = devm_clk_hw_register_mux(dev, "ep_mux_roundtrip",
                                   ep_mux_parents, ARRAY_SIZE(ep_mux_parents),
                                   0, base + EP_MUX_REG_OFFSET,
                                   EP_MUX_SHIFT, EP_MUX_WIDTH,
                                   CLK_MUX_INDEX_BIT, NULL);
    if (IS_ERR(hw))
        return PTR_ERR(hw);

    return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw);
}

Build and load it (bindings and parent oscillators omitted here since they follow the same pattern from earlier lectures), then exercise it from userspace:

$ echo ep_osc_b > /sys/kernel/debug/clk/ep_mux_roundtrip/clk_parent
$ cat /sys/kernel/debug/clk/ep_mux_roundtrip/clk_parent
ep_osc_b

Expected dmesg output when the driver probes and the parent switch is exercised:

ep_mux_roundtrip: registered mux clock, 3 parents, CLK_MUX_INDEX_BIT
ep_mux_roundtrip: set_parent(index=1) -> reg field = 0b010
ep_mux_roundtrip: get_parent() -> reg field = 0b010, index = 1

Internally, index 1 was written as 1 << 1 = 0b010 by the encoding side, and clk_mux_val_to_index() correctly recovered index 1 via ffs(0b010) - 1 on the way back — exactly the round trip this lecture set out to explain.

Common Mistakes and Troubleshooting

  • Forgetting the mask in a custom get_parent. If you write your own low-level mux driver instead of using clk_hw_register_mux(), and you skip ANDing with the mux’s mask before calling clk_mux_val_to_index(), leftover bits from unrelated fields in the same register will corrupt the index calculation in ways that are very hard to spot in review.
  • Assuming the raw register value is the index. With CLK_MUX_INDEX_BIT or CLK_MUX_INDEX_ONE active, the raw value read from hardware is never the same number as the parent index — always run it through the conversion helper.
  • Re-implementing the decode logic by hand. Because clk_mux_val_to_index() is EXPORT_SYMBOL_GPL, there is rarely a good reason to hand-roll this logic in a new driver — call the exported helper and stay consistent with every other mux clock in the tree.
  • Ignoring the -EINVAL return. A custom get_parent callback must propagate -EINVAL if the conversion fails rather than silently returning 0 — silently defaulting to “parent 0” hides real hardware or configuration bugs.

Best Practices

  • Prefer the standard flags (CLK_MUX_INDEX_BIT, CLK_MUX_INDEX_ONE) over a table whenever your hardware’s encoding is genuinely arithmetic — it avoids allocating and maintaining a table array.
  • Reserve the explicit table for hardware whose register values truly have no arithmetic relationship to the parent order; don’t reach for a table out of convenience when a flag would do.
  • If you are writing a low-level (non-clk_hw_register_mux) mux driver, call clk_mux_val_to_index() directly rather than duplicating its logic, so future updates to the decoding rules apply to your driver automatically.

Summary and Key Takeaways

This lecture closed the loop on clock multiplexer register handling in the Common Clock Framework. You now know exactly how clk_mux_set_parent() writes hardware — including the single-write hiword-mask optimization — and exactly how clk_mux_get_parent() and clk_mux_val_to_index() reverse that process, decoding a raw register field back into the parent index the rest of the clock tree relies on. Because clk_mux_val_to_index() is exported, this same decoding logic is available to any driver that manages mux hardware directly — which is exactly the scenario the next lecture covers.

Frequently Asked Questions

Why does clk_mux_get_parent() call a separate function instead of just returning the masked value?

Because the masked register value is a hardware encoding, not a parent index. CCF’s internal bookkeeping — reparenting, debugfs, clk_get_parent() — always works in terms of parent index, so the conversion through clk_mux_val_to_index() is mandatory on every read.

What does ffs() return for an input of zero?

ffs(0) returns 0, which is why the code guards both flag branches with if (val && ...) — a raw value of zero is never run through the bit-position or decrement logic, since it always means index zero directly.

Can a mux use both CLK_MUX_INDEX_BIT and CLK_MUX_INDEX_ONE at the same time?

Yes — the code checks them independently and in sequence, so hardware that both one-hot encodes and offsets by one would have both flags set and go through both decode steps.

Is clk_mux_val_to_index() only usable inside clk-mux.c?

No — it is EXPORT_SYMBOL_GPL, specifically so that other clock drivers managing their own mux register access can reuse the same decoding rules instead of duplicating them.

What happens if the hardware reports a value that doesn’t match any known index?

The function returns -EINVAL in every path — table lookup, bit decode, or bounds check — rather than guessing or silently defaulting to index 0.

Why is locking conditional on mux->lock instead of always using a spinlock?

Some mux clocks are the only thing touching their register, so no shared-state protection is needed; in that case the driver passes NULL for the lock and the framework substitutes __acquire()/__release() annotations purely to keep sparse’s locking-context checker satisfied, with no runtime cost.

Continue Learning Linux Kernel Clock Drivers

This lecture is part of EmbeddedPathashala’s free linux kernel development course covering the Common Clock Framework in depth.

PREV_LEC  |  NEXT_LEC

 

Leave a Reply

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