What are Linux Divider Clock Driver Basics-Free Linux Device Drivers Course

Linux Divider Clock Driver Basics-Free Linux Device Drivers Course

Part of EmbeddedPathashala’s free Linux kernel development course — Common Clock Framework series

What You Will Learn

  • Why a divider clock is a rate-changing, non-gating clock type
  • Every field of struct clk_divider explained
  • All CLK_DIVIDER_* flags with a worked 2-bit example
  • clk_hw_register_divider() vs clk_hw_register_divider_table()
  • struct clk_div_table for irregular divisor ratios
  • clk_divider_ops vs clk_divider_ro_ops and how CLK_DIVIDER_READ_ONLY picks one
  • Writing and testing an original MMIO divider clock driver

Prerequisites

  • struct clk_ops and the provider/consumer model
  • The mandatory-callbacks-by-clock-type table (gate / rate-changing / mux)
  • Comfort reading clk_hw embedding and container_of()-based drivers
  • A working gate clock driver (bit-field register access, spinlock usage)

Understanding the Divider Clock in the Linux Common Clock Framework

Every driver author writing a linux divider clock driver eventually runs into the same hardware pattern: a small field inside a control register that scales a parent clock down to a slower rate. Unlike a gate clock, a divider clock cannot turn its output on or off — it only changes how fast that output ticks relative to its parent. Because the output rate is adjustable, the Common Clock Framework treats rate calculation as mandatory for this clock type: any driver built around it must be able to report the current rate, propose a rate for a requested value, and actually program a new divisor into hardware.

This lecture continues EmbeddedPathashala’s free Linux kernel development course by walking through the divider clock the same way earlier lectures walked through gate and mux clocks — the wrapper structure first, then the flags that change its behavior, then the registration helpers, and finally the ops the framework attaches based on how you configure it.

struct clk_divider — Field by Field

A divider clock is represented by embedding a clk_hw inside a wrapper structure, exactly like the gate and mux clocks covered earlier in this chapter. The wrapper carries everything the divider-specific callbacks need to read and write the correct bits of the correct register.

clk_divider Field Layout

struct clk_divider fields
hw → embedded clk_hw, the provider-side handle
reg → MMIO register controlling the divisor
shift → bit offset of the divider field inside reg
width → bit width of the divider field
flags → CLK_DIVIDER_* behavior flags
table → optional clk_div_table for irregular ratios
lock → spinlock protecting concurrent register access

The reg, shift, and width fields together describe exactly which bits inside the hardware register hold the divisor. The framework isolates those bits internally using a mask built from width, so a 4-bit-wide field, for instance, can only ever hold divisor-related values from 0 to 15 before any flag-driven interpretation is applied.

What those raw bits actually mean is where flags comes in. By default the framework treats the raw register value plus one as the divisor — a field value of 0 divides the parent rate by 1, a field value of 3 divides it by 4, and so on. This default keeps a full range of divisors reachable even though the all-zero register state is a common power-on default. Flags exist to override this default relationship for hardware that behaves differently.

CLK_DIVIDER_* Flags Reference

The divider clock flags are one of the richer flag sets in the Common Clock Framework because real hardware encodes divisor ratios in several genuinely different ways. Each flag below changes how the raw register value is interpreted before it becomes an actual division ratio.

Flag Effect
CLK_DIVIDER_ONE_BASED Register value itself is the divisor (no implicit +1); a value of 0 is invalid unless ALLOW_ZERO is also set
CLK_DIVIDER_ROUND_CLOSEST round_rate picks the closest achievable divisor instead of always rounding the rate up
CLK_DIVIDER_POWER_OF_TWO Divisor is 2 raised to the power of the register value, useful for binary prescalers
CLK_DIVIDER_ALLOW_ZERO Permits a register value of 0 to mean “no change” instead of being treated as invalid
CLK_DIVIDER_HIWORD_MASK Same single-write, self-masking register pattern used by gate and mux clocks; upper 16 bits carry the write-enable mask, lower 16 bits carry the value
CLK_DIVIDER_READ_ONLY Clock is preconfigured by hardware/firmware; the framework must never attempt to reprogram it, which also changes which ops struct gets attached
CLK_DIVIDER_MAX_AT_ZERO A register value of 0 encodes the maximum possible divisor rather than the minimum

CLK_DIVIDER_MAX_AT_ZERO is easiest to understand with a concrete example. Picture a 2-bit divider field. Under this flag, an all-zero field does not mean “divide by 1” — it means “divide by the largest value representable by the field width plus one”:

Register Value Effective Divisor
0 4
1 1
2 2
3 3

This kind of quirky, hardware-specific mapping is exactly why the table-based registration path exists, which we’ll get to shortly.

Registering a Divider Clock: clk_hw_register_divider()

For the common case — a linear or near-linear relationship between register value and divisor that one of the flags above can fully describe — clk_hw_register_divider() is the interface to reach for. It builds the clk_divider wrapper, fills in clk_init_data, and calls clk_hw_register() internally so you never touch struct clk_divider directly for straightforward hardware.

struct clk_hw *clk_hw_register_divider(struct device *dev,
                                        const char *name,
                                        const char *parent_name,
                                        unsigned long flags,
                                        void __iomem *reg,
                                        u8 shift, u8 width,
                                        u8 clk_divider_flags,
                                        spinlock_t *lock);

Every parameter here maps directly onto a field of struct clk_divider — reg, shift, width, and lock are passed through unchanged, while clk_divider_flags becomes the wrapper’s flags field. flags (the unsigned long parameter) is the generic CLK_* framework-level flag set covered earlier in this chapter — CLK_SET_RATE_PARENT and friends — and is a completely separate namespace from the CLK_DIVIDER_* flags above. Mixing the two up is one of the most common bugs newcomers hit, and we’ll return to it in the mistakes section.

Current mainline kernels also provide a managed devm_clk_hw_register_divider() wrapper that ties the divider’s lifetime to the device, releasing it automatically on driver unbind — the same devm_ convention already used by the fixed-rate, fixed-factor, gate, and mux registration helpers earlier in this chapter.

Irregular Ratios: struct clk_div_table and clk_hw_register_divider_table()

Some divider hardware simply does not follow any arithmetic rule at all — the mapping from register value to actual divisor is an arbitrary lookup table baked into the silicon. For that case the framework provides a second registration interface, one that accepts an explicit table of value/divisor pairs instead of relying on a flag-driven formula.

struct clk_div_table {
    unsigned int val;
    unsigned int div;
};

val is the raw register value and div is the actual division ratio it represents. The table is a plain array, and the framework expects the final entry to have div set to 0 as a terminator — the same “sentinel last entry” convention you’ll see elsewhere in the kernel for variable-length static tables.

struct clk_hw *clk_hw_register_divider_table(struct device *dev,
                                              const char *name,
                                              const char *parent_name,
                                              unsigned long flags,
                                              void __iomem *reg,
                                              u8 shift, u8 width,
                                              u8 clk_divider_flags,
                                              const struct clk_div_table *table,
                                              spinlock_t *lock);

Every parameter matches clk_hw_register_divider() with one addition — table, which is stored directly into the wrapper’s table field. Once a table is supplied, the divider-to-register lookup functions inside clk-divider.c prefer the table over any arithmetic flag-based interpretation, which lets a single divider clock type cover both regular and irregular hardware through the same core ops. A managed devm_clk_hw_register_divider_table() variant is also available on current kernels.

clk_divider_ops vs clk_divider_ro_ops

Exactly like the gate and mux registration paths, both divider registration functions look at CLK_DIVIDER_READ_ONLY internally and pick one of two exported ops structures on your behalf:

Condition ops struct assigned Callbacks present
CLK_DIVIDER_READ_ONLY not set clk_divider_ops recalc_rate, round_rate, set_rate
CLK_DIVIDER_READ_ONLY set clk_divider_ro_ops recalc_rate, round_rate only — no set_rate

Both structs are exported with EXPORT_SYMBOL_GPL, which means — just like clk_gate_ops and clk_mux_ops from earlier lectures — you can reuse individual callbacks from clk_divider_ops inside your own custom ops struct when only one piece of divider behavior needs to be hardware-specific. This is the same “borrow the built-in callback, override just one” pattern this chapter’s lecture on registration helpers introduced.

Divider Registration Decision Path

Regular arithmetic ratio → clk_hw_register_divider() → flags interpreted directly
Irregular / lookup-table ratio → clk_hw_register_divider_table() → clk_div_table consulted
CLK_DIVIDER_READ_ONLY set → clk_divider_ro_ops attached (no set_rate)
CLK_DIVIDER_READ_ONLY clear → clk_divider_ops attached (set_rate available)

Kernel Updates Beyond the Source Material

Current mainline clk-divider.c has grown a few things not present when this material was originally written, worth knowing before you copy code from an older reference. The internal flags field has widened past the original 8 bits on recent kernels to make room for additional behavior flags, and two register-access flags — CLK_DIVIDER_BIG_ENDIAN and register-width flags for narrow 8-bit/16-bit MMIO access — have been added to support divider hardware that doesn’t use a plain native-endian 32-bit register. The managed devm_ registration variants mentioned above are also a post-source-book addition. None of this changes the core concepts above; it’s the same divisor-in-a-register model with a slightly wider set of edge cases covered.

Writing an Original Divider Clock Driver

To make this concrete, here’s an original platform driver — ep_mmio_divider_demo — that registers a 3-bit MMIO divider clock as a child of an existing parent clock. It uses CLK_DIVIDER_ONE_BASED so the raw register value is the divisor directly, which keeps the demo’s dmesg output easy to follow.

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/spinlock.h>

#define EP_DIV_SHIFT   4
#define EP_DIV_WIDTH   3

struct ep_divider_priv {
    void __iomem *reg;
    spinlock_t lock;
    struct clk_hw *div_hw;
};

static int ep_mmio_divider_probe(struct platform_device *pdev)
{
    struct ep_divider_priv *priv;
    struct resource *res;
    const char *parent_name;

    priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    priv->reg = devm_ioremap_resource(&pdev->dev, res);
    if (IS_ERR(priv->reg))
        return PTR_ERR(priv->reg);

    spin_lock_init(&priv->lock);

    parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0);

    priv->div_hw = devm_clk_hw_register_divider(&pdev->dev,
                                    "ep_div_out", parent_name, 0,
                                    priv->reg, EP_DIV_SHIFT, EP_DIV_WIDTH,
                                    CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
                                    &priv->lock);
    if (IS_ERR(priv->div_hw))
        return PTR_ERR(priv->div_hw);

    platform_set_drvdata(pdev, priv);

    return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_simple_get,
                                        priv->div_hw);
}

static const struct of_device_id ep_mmio_divider_of_match[] = {
    { .compatible = "ep,mmio-divider-clock" },
    { }
};
MODULE_DEVICE_TABLE(of, ep_mmio_divider_of_match);

static struct platform_driver ep_mmio_divider_driver = {
    .probe = ep_mmio_divider_probe,
    .driver = {
        .name = "ep_mmio_divider_demo",
        .of_match_table = ep_mmio_divider_of_match,
    },
};
module_platform_driver(ep_mmio_divider_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala original divider clock demo");

Because CLK_DIVIDER_ALLOW_ZERO is set alongside CLK_DIVIDER_ONE_BASED here, a register value of 0 is legal and simply passes the parent rate through unchanged instead of being flagged invalid — a common combination for hardware where the divider defaults to “bypass” at reset.

Adding a Table-Based Variant

The same driver can be extended to demonstrate the irregular-ratio path with a small addition — a static clk_div_table and a call to the table-registration helper instead:

static const struct clk_div_table ep_div_table[] = {
    { .val = 0, .div = 1 },
    { .val = 1, .div = 3 },
    { .val = 2, .div = 6 },
    { .val = 3, .div = 12 },
    { /* sentinel */ }
};

priv->div_hw = devm_clk_hw_register_divider_table(&pdev->dev,
                                "ep_div_out_irregular", parent_name, 0,
                                priv->reg, EP_DIV_SHIFT, EP_DIV_WIDTH,
                                0, ep_div_table, &priv->lock);

Notice the ratios here — 1, 3, 6, 12 — have no consistent arithmetic relationship to the register values 0, 1, 2, 3. That non-linearity is precisely what clk_div_table exists to express; no combination of CLK_DIVIDER_* flags could describe this mapping.

Build and Test Walkthrough

Build the module against your kernel headers, load it against a matching device tree node, and confirm the divider clock registered correctly through the standard clk debugfs interface:

$ make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
$ sudo insmod ep_mmio_divider_demo.ko
$ cat /sys/kernel/debug/clk/ep_div_out/clk_rate
$ sudo dmesg | tail
[  912.114203] ep_mmio_divider_demo ep_div_out: registered divider clock, parent=ep_pll_out
[  912.114211] clk: ep_div_out: rate initialized to 24000000 Hz (divisor=1)
[  912.118440] ep_mmio_divider_demo: probe of ep-div0 completed

Changing the divisor at runtime through the standard clk consumer API — clk_set_rate() from a consumer driver, or writing to the debugfs rate file if your kernel build exposes it — should trigger clk_divider_set_rate() internally, reprogram the three-bit field at the configured shift, and report the new recalculated rate on the next read.

Common Mistakes with Divider Clocks

  • Confusing the generic CLK_* framework flags (5th parameter) with the CLK_DIVIDER_* hardware-interpretation flags (7th parameter) — they occupy different namespaces and different parameter positions
  • Setting CLK_DIVIDER_ONE_BASED without CLK_DIVIDER_ALLOW_ZERO on hardware that can legitimately read back 0, causing spurious WARN() splats
  • Forgetting the sentinel {0} entry at the end of a clk_div_table array, which causes the table-walking helpers to read past the array
  • Using CLK_DIVIDER_HIWORD_MASK on a divider field wider than 16 bits, which the registration helper explicitly rejects
  • Expecting set_rate to be callable on a clock registered with CLK_DIVIDER_READ_ONLY — clk_divider_ro_ops has no set_rate at all

Best Practices

  • Prefer the devm_ registration variants so divider lifetime is tied cleanly to the owning device
  • Reach for clk_hw_register_divider_table() the moment a datasheet’s divisor table stops being arithmetic, rather than trying to force a flag combination to fit
  • Always pass a real spinlock when the same register is shared with other clock bits (a mux or gate field in the same word), to avoid read-modify-write races
  • Use CLK_DIVIDER_ROUND_CLOSEST whenever downstream consumers care about hitting the nearest achievable rate rather than always rounding up
  • Document the flag combination you chose in a comment near the registration call — CLK_DIVIDER_* combinations are not always self-explanatory months later

Summary and Key Takeaways

The divider clock rounds out this chapter’s tour of rate-changing clock types alongside fixed-factor clocks. struct clk_divider wraps a clk_hw around a register/shift/width triple, and the CLK_DIVIDER_* flags decide how the raw bits in that field translate into an actual divisor — from the simple register-plus-one default, through power-of-two scaling, to a fully arbitrary lookup table via clk_div_table. Two registration entry points, clk_hw_register_divider() and clk_hw_register_divider_table(), cover the arithmetic and table-driven cases respectively, and both quietly select between clk_divider_ops and clk_divider_ro_ops based on whether CLK_DIVIDER_READ_ONLY is set. Getting comfortable with this linux divider clock driver pattern completes the base clock type set this free Linux kernel development course has been building toward — fixed-rate, gate, mux, fixed-factor, and now divider — which is exactly the foundation needed for the composite clock type coming up next in this chapter.

Frequently Asked Questions

What is the difference between a gate clock and a divider clock?

A gate clock only turns its output on or off and cannot change frequency; a divider clock only changes frequency by dividing the parent rate and cannot gate its output. They are separate base clock types, and hardware that needs both behaviors either combines them at the composite clock level or exposes two separate register fields.

Why is providing round_rate or set_rate mandatory for a divider clock?

Because the divisor is adjustable, any consumer requesting a specific rate needs the framework to be able to compute what rate is actually achievable and then program it. A clock type that can change rate must implement rate-negotiation callbacks; a clock type that can only gate does not need them.

What does CLK_DIVIDER_ONE_BASED actually change?

By default the framework treats the register value plus one as the divisor. With CLK_DIVIDER_ONE_BASED set, the raw register value is used as the divisor with no implicit addition — which also means a register value of 0 becomes invalid unless CLK_DIVIDER_ALLOW_ZERO is set alongside it.

When should I use clk_hw_register_divider_table() instead of clk_hw_register_divider()?

Use the table-based variant whenever the hardware’s register-value-to-divisor mapping is not a consistent arithmetic formula — for example, non-sequential or non-linear divisor sets that no combination of CLK_DIVIDER_* flags can describe. The table’s terminating {div: 0} sentinel entry tells the framework where the array ends.

Can a divider clock registered with CLK_DIVIDER_READ_ONLY be reprogrammed later?

No. CLK_DIVIDER_READ_ONLY causes the registration function to attach clk_divider_ro_ops, which implements recalc_rate and round_rate but deliberately omits set_rate, so the framework can report the rate but never change it.

Is CLK_DIVIDER_HIWORD_MASK the same mechanism used by gate and mux clocks?

Yes. It’s the identical single-write, self-masking register convention covered in the gate clock lecture — the upper 16 bits of the write carry a mask of which bits to change, and the lower 16 bits carry the new value — applied here to the divider field instead of a gate bit or mux select field.

What happens if I mix up the generic CLK_* flags with the CLK_DIVIDER_* flags?

They are separate parameters at separate positions in the registration call and control unrelated behavior — generic CLK_* flags affect framework-level reparenting and rate-propagation policy, while CLK_DIVIDER_* flags affect how this specific clock’s register bits are interpreted. Passing one set where the other belongs typically compiles fine but produces silently wrong clock behavior, which makes it worth double-checking during review.

Does the divider clock support device tree registration with zero driver code, like fixed-clock or pwm-clock did?

No. Unlike the DT-only fixed-clock and pwm-clock compatibles covered earlier in this chapter, a divider clock’s register layout is too hardware-specific for a generic DT-only binding — you still need a driver that knows the register offset, shift, and width for your specific SoC or chip.

Continue the Free Linux Kernel Development Course

Composite clocks, chained callbacks, and the rest of the Common Clock Framework chapter are next.

 

 

Leave a Reply

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