What are Mux Table Index Mapping Internals in Linux Kernel-Free Linux Device Drivers Course

Mux Table Index Mapping Internals-Free Linux Device Drivers Course
Free Linux Kernel Development Course · Common Clock Framework · Kernel 6.x
Chapter 4 · Lecture 22
Reading Time: 14 min
Level: Advanced

Keywords covered in this lecture

clk_register_mux_table linux
mux index to register value
CLK_MUX_INDEX_BIT CLK_MUX_INDEX_ONE
irregular clock mux mapping
free linux kernel development course
free embedded systems course

The previous lecture registered a mux clock and mentioned that
clk_hw_register_mux() assumes a fairly regular relationship between a
parent’s index and the value written to the register. Real hardware is not always
that cooperative. This lecture goes inside the CCF’s own
clk_register_mux_table() variant, the internal index-to-value
translation logic that backs every mux clock, and how to handle a genuinely irregular
register mapping.

What You Will Learn

  • How the CCF turns a parent index into an actual register value internally
  • The precedence order between table, CLK_MUX_INDEX_BIT, and CLK_MUX_INDEX_ONE
  • Why clk_hw_register_mux() alone cannot express an irregular value table
  • Using clk_register_mux_table() to register a mux with non-sequential register values
  • Building an original irregular-mapping mux clock demo

Prerequisites

  • Mux clock driver case study (previous lecture)
  • struct clk_mux fields, especially table/mask/shift

How a Parent Index Becomes a Register Value

Whenever clk_set_parent() is called against a mux clock, the CCF has to
translate the requested parent index into whatever bit pattern that specific piece of
hardware actually expects in its selector register. The logic follows a clear
precedence order:

Index-to-Value Precedence Order
table set?
yes: value = table[index]
no table, CLK_MUX_INDEX_BIT set?
value = 1 << index
CLK_MUX_INDEX_ONE also set?
value += 1
none of the above
value = index (plain sequential)

Whatever value comes out of that logic is then left-shifted by shift and
masked before being written into reg. In plain terms: an explicit
table always wins if one is provided; failing that,
CLK_MUX_INDEX_BIT and CLK_MUX_INDEX_ONE can adjust a
power-of-two or off-by-one encoding; and with nothing set at all, the index is used
directly as the register value.

Why clk_hw_register_mux() Cannot Pass a Table

Looking back at the previous lecture’s registration prototype, there is no parameter
anywhere for supplying a table array —
clk_hw_register_mux() only covers the sequential, power-of-two, and
off-by-one cases through its flags. For a genuinely irregular mapping, where register
values do not follow any of those three predictable patterns, the CCF provides a
separate registration interface instead:

struct clk *clk_register_mux_table(struct device *dev,
                                    const char *name,
                                    const char **parent_names,
                                    u8 num_parents,
                                    unsigned long flags,
                                    void __iomem *reg, u8 shift, u32 mask,
                                    u8 clk_mux_flags, u32 *table,
                                    spinlock_t *lock);

This is the same registration underneath — the same
clk_mux_ops/clk_mux_ro_ops are still used regardless of which
registration interface you called. The only difference is that this variant lets you
supply the table array directly, for hardware whose selector values simply
do not follow any formula.

Original Demo: A Mux With an Irregular Value Table

Some real clock-selector registers use scattered values for historical or
layout reasons — imagine a 3-input mux where the hardware designer wired parent 0
to register value 0x0, parent 1 to 0x2, and parent 2 to 0x5, with no arithmetic
relationship between the index and the value at all. A plain
clk_hw_register_mux() call has no flag that can express this; a table is
the only correct answer.

/* ep_mux_table_demo.c - three parents mapped to non-sequential register values */
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/spinlock.h>

static const char * const ep_mux_table_parents[] = {
    "ep_bus_clk", "ep_ext_osc", "ep_derived_clk"
};

/* index 0 -> 0x0, index 1 -> 0x2, index 2 -> 0x5 (no arithmetic pattern) */
static u32 ep_mux_table[] = { 0x0, 0x2, 0x5 };

struct ep_mux_table_data {
    void __iomem *base;
    spinlock_t lock;
    struct clk *mux_clk;
};

static int ep_mux_table_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct ep_mux_table_data *data;

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

    spin_lock_init(&data->lock);

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

    data->mux_clk = clk_register_mux_table(dev, "ep_mux_table_out",
                                            ep_mux_table_parents,
                                            ARRAY_SIZE(ep_mux_table_parents),
                                            0, data->base, 0, 0x7,
                                            0, ep_mux_table, &data->lock);
    if (IS_ERR(data->mux_clk))
        return PTR_ERR(data->mux_clk);

    return devm_add_action_or_reset(dev, (void (*)(void *))clk_unregister,
                                     data->mux_clk);
}

static const struct of_device_id ep_mux_table_of_match[] = {
    { .compatible = "ep,clk-mux-table-demo" },
    { }
};
MODULE_DEVICE_TABLE(of, ep_mux_table_of_match);

static struct platform_driver ep_mux_table_driver = {
    .probe = ep_mux_table_probe,
    .driver = {
        .name = "ep_clk_mux_table_demo",
        .of_match_table = ep_mux_table_of_match,
    },
};
module_platform_driver(ep_mux_table_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala irregular mux table demo");

Notice the cleanup here uses devm_add_action_or_reset() paired with
clk_unregister(), since clk_register_mux_table() is a
struct clk *-returning legacy-style interface without its own managed
devm_ variant — a good reminder that not every CCF registration
function has a managed counterpart, and unmanaged ones still need explicit teardown
wiring.

Build and Run

make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
sudo insmod ep_mux_table_demo.ko
cat /sys/kernel/debug/clk/clk_summary | grep ep_mux_table_out

Expected clk_summary excerpt, showing the mux registered and reporting whichever
parent’s rate its current register value maps back to:

ep_mux_table_out          1        1        0   100000000          0

Choosing Between the Two Mux Registration Variants

Situation Use
Register value equals the parent index directly clk_hw_register_mux(), no special flags
Register value is a power of two, or off-by-one clk_hw_register_mux() with CLK_MUX_INDEX_BIT/CLK_MUX_INDEX_ONE
Register values follow no arithmetic formula at all clk_register_mux_table() with an explicit table

Common Mistakes

  • Trying to force an irregular mapping through CLK_MUX_INDEX_BIT/CLK_MUX_INDEX_ONE instead of using a table
  • Getting the table array order out of sync with parent_names order
  • Forgetting that clk_register_mux_table() has no built-in devm_ variant
  • Setting mask too narrow to hold the largest value actually present in the table

Best Practices

  • Reach for clk_hw_register_mux() and its flags first; only use a table when the mapping is genuinely irregular
  • Keep the table array and parent_names array visually aligned in source, index by index
  • Wire up devm_add_action_or_reset() with clk_unregister() whenever using a non-managed registration function
  • Document the register-to-parent mapping in a comment next to the table for future maintainers

Summary and Key Takeaways

  • An explicit table always takes precedence over CLK_MUX_INDEX_BIT/CLK_MUX_INDEX_ONE
  • With no table and no flags, the register value simply equals the parent index
  • clk_register_mux_table() is the only way to express a genuinely irregular register mapping
  • Both mux registration variants share the same underlying clk_mux_ops/clk_mux_ro_ops

Frequently Asked Questions

What takes priority if both a table and CLK_MUX_INDEX_BIT are set?

The table always wins; CLK_MUX_INDEX_BIT and CLK_MUX_INDEX_ONE are only consulted
when no table is provided.

Does clk_register_mux_table() use different clk_ops than clk_hw_register_mux()?

No, both ultimately assign the same clk_mux_ops or clk_mux_ro_ops depending on the
CLK_MUX_READ_ONLY flag; only the registration-time parameters differ.

Can I combine CLK_MUX_INDEX_ONE with a table?

Once a table is supplied it fully determines the value, so index-adjustment flags
like CLK_MUX_INDEX_ONE have no additional effect at that point.

Why doesn’t clk_register_mux_table() have a devm_ variant?

It is an older, struct clk *-returning interface that predates some of the managed
registration helpers; pairing it with devm_add_action_or_reset() and clk_unregister()
achieves the same automatic cleanup.

How large should the mask parameter be for a table-based mux?

Large enough to hold the largest value that actually appears in the table, not just
enough bits for the number of parents.

Suggested Images For This Lecture

  • Flowchart: table vs INDEX_BIT vs INDEX_ONE vs plain index precedence
  • Diagram: three parents mapped to non-sequential register values via a table
  • Table graphic: choosing clk_hw_register_mux() vs clk_register_mux_table()

Continue This Free Linux Kernel Development Course

Next, we move to the divider clock case study and cover selectable divisors, divider
tables, and rate rounding.

Leave a Reply

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