How to Fixed-Factor Clock Driver Case Study-Free Linux Device Drivers Course

Fixed-Factor Clock Driver Case Study-Free Linux Device Drivers Course
Free Linux Kernel Development Course · Common Clock Framework · Kernel 6.x
Chapter 4 · Lecture 18
Reading Time: 14 min
Level: Intermediate

Keywords covered in this lecture

linux fixed factor clock
struct clk_fixed_factor
clk_hw_register_fixed_factor
fixed-factor-clock device tree
free linux kernel development course
free linux device drivers course

Some clocks in a system are not truly independent sources at all — they are
just a parent clock multiplied and divided by a fixed ratio, wired in purely to feed a
peripheral the exact frequency it needs. The CCF models this directly as the
linux fixed factor clock type, and it comes with a small but
important quirk in how its set_rate callback behaves that is worth
understanding before you ever write one.

What You Will Learn

  • The struct clk_fixed_factor wrapper and its mult/div fields
  • Why this clock type can change rate at all despite having no gate or divider hardware
  • Why its set_rate callback must be a deliberate no-op returning 0
  • Using clk_hw_register_fixed_factor() instead of building the struct by hand
  • The fixed-factor-clock DT binding that needs no driver at all

Prerequisites

  • Fixed and PWM clock bindings (previous lecture)
  • Linux clock flags reference guide (CLK_SET_RATE_PARENT)
  • Fixed-rate clock case study (struct clk_fixed_rate, container_of pattern)

The struct clk_fixed_factor Wrapper

A fixed-factor clock’s hardware-specific state is just two small integers: how much
to multiply the parent rate by, and how much to divide it by afterward.

struct clk_fixed_factor {
    struct clk_hw hw;
    unsigned int mult;
    unsigned int div;
};

#define to_clk_fixed_factor(_hw) \
    container_of(_hw, struct clk_fixed_factor, hw)

The resulting rate is always parent_rate * mult / div. This clock cannot
gate — there is no enable bit anywhere in this picture, only arithmetic on
whatever rate the parent happens to be running at.

Why set_rate Must Be a Deliberate No-Op

A fixed-factor clock has no way to change its own multiplier or divider at runtime
— those are fixed at registration time. The only way its output rate can change at
all is if the parent’s rate changes, which requires the
CLK_SET_RATE_PARENT flag from an earlier lecture to be set so that any
clk_set_rate() request is forwarded upstream instead of failing.

Here is the subtlety: once CLK_SET_RATE_PARENT is set and the request
succeeds upstream, the CCF still calls this clock’s own set_rate callback
as the final step of applying that rate change. If the callback were left NULL, the CCF
would treat this clock as entirely rate-immutable and refuse the operation outright. The
fix is to provide a callback that does nothing but signal success:

static int clk_factor_set_rate(struct clk_hw *hw, unsigned long rate,
                                unsigned long parent_rate)
{
    return 0;
}

This is a deliberate no-op: the actual rate change already happened at the parent, and
this function’s only job is to confirm to the CCF that nothing further needs to happen
locally. recalc_rate and round_rate are still implemented
normally, since the clock does need to report and validate rates — it is only the
act of “setting” a rate locally that has nothing left to do.

Where the No-Op set_rate Fits
clk_set_rate() on fixed-factor clock
forwarded to parent (CLK_SET_RATE_PARENT)
parent rate actually changes
local set_rate returns 0 (no-op)

The Built-In clk_fixed_factor_ops

You rarely need to write any of this yourself. The framework already ships a complete
ops struct for this exact type:

const struct clk_ops clk_fixed_factor_ops = {
    .round_rate  = clk_factor_round_rate,
    .set_rate    = clk_factor_set_rate,
    .recalc_rate = clk_factor_recalc_rate,
};
EXPORT_SYMBOL_GPL(clk_fixed_factor_ops);

Its round_rate and recalc_rate implementations already
account for CLK_SET_RATE_PARENT internally, so once you register through
the dedicated helper, you genuinely do not need to think about ops at all:

struct clk_hw *clk_hw_register_fixed_factor(struct device *dev,
                                             const char *name,
                                             const char *parent_name,
                                             unsigned long flags,
                                             unsigned int mult,
                                             unsigned int div);

This allocates a struct clk_fixed_factor internally, wires up
clk_fixed_factor_ops, and registers it in one call — the same
simplification pattern the fixed-rate clock lecture already introduced.

Original Demo: A x2/÷3 Fixed-Factor Clock

/* ep_fixed_factor_demo.c - registers a mult=2, div=3 clock derived from a parent */
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk-provider.h>
#include <linux/of.h>

static int ep_fixed_factor_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct clk_hw *hw;

    /* output rate = parent_rate * 2 / 3, tracks the parent via CLK_SET_RATE_PARENT */
    hw = devm_clk_hw_register_fixed_factor(dev, "ep_derived_clk", "ep_ext_osc",
                                            CLK_SET_RATE_PARENT, 2, 3);
    if (IS_ERR(hw))
        return PTR_ERR(hw);

    return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw);
}

static const struct of_device_id ep_fixed_factor_of_match[] = {
    { .compatible = "ep,clk-fixed-factor-demo" },
    { }
};
MODULE_DEVICE_TABLE(of, ep_fixed_factor_of_match);

static struct platform_driver ep_fixed_factor_driver = {
    .probe = ep_fixed_factor_probe,
    .driver = {
        .name = "ep_clk_fixed_factor_demo",
        .of_match_table = ep_fixed_factor_of_match,
    },
};
module_platform_driver(ep_fixed_factor_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala fixed-factor clock case study demo");

Matching device tree node, deriving from the fixed-clock node introduced in the
previous lecture:

ep_derived_ctrl: clock-controller@6000 {
    compatible = "ep,clk-fixed-factor-demo";
    #clock-cells = <0>;
    clocks = <&ep_ext_osc>;
};

Build and Run

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

With an 8 MHz parent, expected clk_summary excerpt (8,000,000 * 2 / 3 ≈ 5,333,333):

ep_ext_osc                1        1        0     8000000          0
   ep_derived_clk         1        1        0     5333333          0

The fixed-factor-clock DT-Only Binding

Just like the fixed-clock and pwm-clock types from the previous lecture, a
fixed-factor clock can also be described purely in the device tree with no driver at all
— the CCF core parses clock-mult/clock-div directly:

ep_derived_dtonly: ep_derived_dtonly {
    compatible = "fixed-factor-clock";
    #clock-cells = <0>;
    clock-div = <3>;
    clock-mult = <2>;
    clocks = <&ep_ext_osc>;
};

The required properties are #clock-cells (always 0),
compatible = "fixed-factor-clock", clock-div,
clock-mult, and clocks pointing at the parent. Choose this
DT-only form whenever the ratio is fixed by board wiring and no software control is ever
needed; reach for the driver-based form from this lecture only when the ratio needs to
be picked or adjusted by code at probe time.

Driver-Based vs DT-Only Fixed-Factor Clocks

Aspect devm_clk_hw_register_fixed_factor() compatible = “fixed-factor-clock”
Driver code required Yes, a probe function None
mult/div source Chosen in C code, can vary by compatible match Fixed directly in the DT node
Best fit Ratio depends on runtime/compatible-specific logic Ratio is fixed purely by board wiring

Common Mistakes

  • Leaving set_rate NULL on a custom fixed-factor-style clk_ops, breaking CLK_SET_RATE_PARENT
  • Forgetting CLK_SET_RATE_PARENT, so clk_set_rate() calls fail with nowhere to propagate
  • Writing a driver for a ratio that never changes and could have used the DT-only binding
  • Swapping mult and div, inverting the intended frequency relationship

Best Practices

  • Use clk_hw_register_fixed_factor()/devm_ variant instead of building the struct by hand
  • Always pair a fixed-factor clock with CLK_SET_RATE_PARENT unless rate propagation is intentionally unwanted
  • Prefer the DT-only fixed-factor-clock binding when the ratio never needs code-driven logic
  • Double check mult/div direction against the datasheet before wiring up the DT node

Summary and Key Takeaways

  • struct clk_fixed_factor holds just mult and div alongside the embedded clk_hw
  • set_rate must be a deliberate no-op returning 0 so CLK_SET_RATE_PARENT can do the real work
  • clk_hw_register_fixed_factor() wires up clk_fixed_factor_ops for you automatically
  • A DT-only fixed-factor-clock binding exists for boards where the ratio never needs code

Frequently Asked Questions

Can a fixed-factor clock gate on and off?

No, it has no enable bit or gating hardware at all — it only computes a
derived rate from its parent.

Why does set_rate need to exist if it does nothing?

Because a NULL set_rate would tell the CCF the clock’s rate can never change at all,
which would block the CLK_SET_RATE_PARENT forwarding mechanism from working.

What happens if I forget CLK_SET_RATE_PARENT?

A clk_set_rate() call against the fixed-factor clock will fail, since there is
nowhere for the request to go without that flag.

Is the DT-only fixed-factor-clock binding as capable as the driver-based version?

For a purely fixed ratio, yes — both end up using the same
clk_fixed_factor_ops underneath; the difference is only whether C code or a DT
property supplies mult/div.

Can mult and div be changed after registration?

No, both are fixed for the lifetime of the registration; only the parent’s rate can
still change, which this clock’s ratio then scales automatically.

Suggested Images For This Lecture

  • Diagram: parent rate flowing through mult/div into a fixed-factor clock
  • Flow diagram: CLK_SET_RATE_PARENT forwarding into a no-op local set_rate
  • Side-by-side diagram: driver-based vs DT-only fixed-factor-clock binding

Continue This Free Linux Kernel Development Course

Next, we move to the gate clock case study and implement a complete gate clk_ops
set from scratch, register by register.

Leave a Reply

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