What are Linux Common Clock Framework Guide in Linux Kernel

 

PREV_LEC  |  NEXT_LEC

Linux Common Clock Framework Guide-Free Linux Device Drivers Course
Lecture 1 of the Clock Framework chapter — Free Linux Kernel Development Course
Chapter 4
Lecture 1
Kernel 6.x

linux common clock framework
CCF linux kernel
clk_hw clk_ops
free linux kernel development course
free linux device drivers course
free embedded systems course
free embedded linux course

The Linux Common Clock Framework (CCF) is the subsystem that lets the kernel enable, disable, and change the rate of every clock line on a System on Chip using one uniform API, instead of a different hand-written API per platform. If you are learning Linux device driver development, understanding the CCF is essential, because almost every peripheral driver — I2C, SPI, UART, display, and audio controllers — depends on a clock being turned on before it can be used at all. This lecture is part of our free Linux kernel development course and free embedded Linux course, and it lays the conceptual foundation before we write a real clock provider driver in the next lecture.

What You Will Learn

  • Why embedded systems need a clock management subsystem
  • The problem the Linux Common Clock Framework was built to solve
  • The difference between a clock provider and a clock consumer
  • The core CCF data structures: clk_hw, clk_ops, clk_init_data, clk, and clk_core
  • How to build and test a minimal fixed-rate clock provider module
  • Common mistakes, best practices, and security considerations around clock drivers

Prerequisites

  • Comfortable writing and building basic Linux kernel modules
  • Familiarity with platform drivers and the Device Tree (covered earlier in this course)
  • A Linux kernel 6.x source tree, or a board/VM you can build and boot modules on
  • Basic C and pointer/struct handling

Why Embedded Systems Need Clock Management

Every digital circuit in a System on Chip — CPU cores, memory controllers, UARTs, timers, display engines — needs a clock signal to drive its internal logic. Clocks in embedded systems serve two main purposes. The first is synchronization: registers, state machines, and buses all need a common timing reference to move data correctly. The second is power management: an SoC can save a significant amount of power by gating off the clock to a block that is currently idle, and by scaling clock rates up or down depending on system load.

Because of this, the Linux kernel has always needed some way for driver code to ask “turn my clock on,” “what rate is my clock running at,” or “set my clock to this rate.” The question was never whether this API should exist — it was how to implement it without duplicating the same logic across dozens of SoC families.

The Problem Before the Linux Common Clock Framework

In the early days, each platform (each SoC family supported by the kernel) implemented its own version of the basic clock API — its own way to get a clock reference, enable or disable it, and get or set its rate. Since every machine directory wrote this logic independently, the kernel accumulated many near-identical files, each reinventing the same bookkeeping around a completely different piece of hardware underneath. This meant more code to maintain, more bugs to fix in parallel across platforms, and a driver-facing API that was not guaranteed to behave the same way from one SoC to the next.

What Is the Linux Common Clock Framework (CCF)?

The Common Clock Framework is the kernel subsystem that unifies clock management under one hardware-independent interface. It is enabled by the CONFIG_COMMON_CLK kernel configuration option, and it lives primarily in drivers/clk/clk.c, with its public data structures declared in include/linux/clk-provider.h (provider-facing) and include/linux/clk.h (consumer-facing).

Note: the CCF does not manage Real-Time Clocks (RTCs) or timekeeping devices — those belong to their own separate kernel subsystems. A “clock” in the CCF sense is a hardware clock line: an oscillator, PLL, divider, gate, or mux feeding a peripheral or CPU core.

CCF Provider and Consumer Model
Clock Provider Driver
(drivers/clk/*)
CCF Core
drivers/clk/clk.c
Consumer Driver
(I2C, SPI, UART…)

Clock Providers vs Clock Consumers

Role Who implements it What it does
Provider SoC clock driver author Connects to the CCF and exposes the real clock tree, matching the SoC datasheet
Consumer Peripheral driver author Requests a clock by name/index and calls the common clk_*() API on it
Both Some complex drivers Provides one or more clocks while also consuming a parent clock supplied by another provider

The Two Halves of the CCF

The CCF is deliberately split into two halves that never need to know the internal details of each other:

  • The framework core — generic code you never modify. It defines struct clk and implements every consumer-facing function (clk_get, clk_enable, clk_set_rate, and so on) on top of whatever clk_ops a provider supplies.
  • The hardware-specific half — the code you write for a new piece of clock hardware. It supplies a struct clk_ops with callbacks the core will invoke, plus a hardware-specific structure that wraps a struct clk_hw.

CCF Core Data Structures Explained

Structure Used by Purpose
struct clk_hw Provider Base handle every hardware clock type embeds; links a hardware-specific struct back into the core
struct clk_ops Provider Hardware-specific callbacks (enable, disable, recalc_rate, set_rate…) invoked by the core
struct clk_init_data Provider Static registration data (name, ops, parent names, flags) handed to the core once at registration time
struct clk Consumer What consumer drivers actually hold a pointer to; every consumer API call takes this
struct clk_core Internal The framework’s own internal representation of a clock; consumers and providers never touch it directly

Understanding struct clk_hw

struct clk_hw is never used on its own. A provider driver always embeds it inside its own hardware-specific structure, then uses the standard kernel container_of() pattern to get back from a clk_hw pointer to the full driver structure. This is the same embedding pattern used throughout the kernel for base/derived structures.

/* Every clock provider follows this embedding pattern */
struct ep_fixed_clk {
    struct clk_hw hw;      /* must be embedded, not pointed to */
    unsigned long rate;    /* our own hardware-specific data */
};

/* Recover our structure from the clk_hw pointer the core gives us */
#define to_ep_fixed_clk(_hw) container_of(_hw, struct ep_fixed_clk, hw)

Understanding struct clk_ops

Every callback in clk_ops receives a struct clk_hw * as its first argument — never a raw hardware pointer. Only a handful of callbacks are mandatory, and which ones depends on the clock type you are modeling (a fixed oscillator needs far fewer callbacks than a programmable PLL).

static unsigned long ep_fixed_clk_recalc_rate(struct clk_hw *hw,
                                               unsigned long parent_rate)
{
    struct ep_fixed_clk *fclk = to_ep_fixed_clk(hw);

    return fclk->rate;
}

static const struct clk_ops ep_fixed_clk_ops = {
    .recalc_rate = ep_fixed_clk_recalc_rate,
};

Original Example: A Minimal Fixed-Rate Clock Provider

To make this concrete, here is an original, minimal clock provider module — ep_fixed_clk — that registers one fixed-rate clock line with the CCF and prints its rate through the consumer API. This keeps the focus on how the pieces connect, rather than on real SoC register access.

#include <linux/module.h>
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/err.h>

#define EP_FIXED_RATE_HZ  24000000UL  /* 24 MHz, just for the demo */

struct ep_fixed_clk {
    struct clk_hw hw;
    unsigned long rate;
};

#define to_ep_fixed_clk(_hw) container_of(_hw, struct ep_fixed_clk, hw)

static unsigned long ep_fixed_clk_recalc_rate(struct clk_hw *hw,
                                               unsigned long parent_rate)
{
    struct ep_fixed_clk *fclk = to_ep_fixed_clk(hw);

    return fclk->rate;
}

static const struct clk_ops ep_fixed_clk_ops = {
    .recalc_rate = ep_fixed_clk_recalc_rate,
};

static struct ep_fixed_clk ep_clk = {
    .rate = EP_FIXED_RATE_HZ,
};

static struct clk_hw *ep_hw;

static int __init ep_fixed_clk_init(void)
{
    struct clk_init_data init = {};
    struct clk *consumer_clk;
    int ret;

    init.name = "ep_fixed_clk24m";
    init.ops = &ep_fixed_clk_ops;
    init.flags = 0;

    ep_clk.hw.init = &init;
    ep_hw = &ep_clk.hw;

    ret = clk_hw_register(NULL, ep_hw);
    if (ret) {
        pr_err("ep_fixed_clk: registration failed (%d)\n", ret);
        return ret;
    }

    /* Act as our own consumer, purely for this demo */
    consumer_clk = ep_hw->clk;
    pr_info("ep_fixed_clk: registered '%s' at %lu Hz\n",
            clk_hw_get_name(ep_hw), clk_get_rate(consumer_clk));

    return 0;
}

static void __exit ep_fixed_clk_exit(void)
{
    clk_hw_unregister(ep_hw);
    pr_info("ep_fixed_clk: unregistered\n");
}

module_init(ep_fixed_clk_init);
module_exit(ep_fixed_clk_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala minimal CCF fixed-rate clock demo");

Build and Run Steps

# Makefile
obj-m += ep_fixed_clk.o

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
$ make
$ sudo insmod ep_fixed_clk.ko
$ dmesg | tail -n 3

Expected dmesg Output

[ 1234.567890] ep_fixed_clk: registered 'ep_fixed_clk24m' at 24000000 Hz

Removing the module cleanly unregisters the clock:

$ sudo rmmod ep_fixed_clk
$ dmesg | tail -n 1
[ 1240.112233] ep_fixed_clk: unregistered

Common Mistakes When Working With the CCF

  • Pointing to a clk_hw instead of embedding itstruct clk_hw must be a member of your driver structure, not a separately allocated pointer, or container_of() will not work.
  • Forgetting to set hw.init before registration — the core reads clk_init_data only during clk_hw_register(); setting it afterward has no effect.
  • Implementing callbacks your clock type doesn’t need — a plain fixed clock never needs set_rate; adding it just invites inconsistent state.
  • Ignoring the return value of registration functions — a failed clk_hw_register() leaves no valid clock behind, and any consumer that depends on it should fail probing with -EPROBE_DEFER or a real error, not silently continue.

Best Practices for Clock Provider Drivers

  • Prefer the devres-managed variants, such as devm_clk_hw_register(), in real platform drivers so cleanup happens automatically on probe failure or driver removal.
  • Keep provider structures private to the driver file; never let consumer code reach into a clk_hw-embedding structure directly.
  • Register clocks with Device Tree–based lookup (of_clk_add_hw_provider()) rather than relying on global clock names wherever possible.
  • Implement only the clk_ops callbacks that map to real hardware behavior — an unimplemented callback should simply be absent, not a stub that does nothing.

Real-World Use Cases

The CCF is used across virtually every modern ARM, RISC-V, and MIPS SoC supported by Linux. A typical SoC clock tree — visible today under /sys/kernel/debug/clk/clk_summary — includes a crystal oscillator feeding one or more PLLs, which in turn feed dividers and gates for each peripheral block: UARTs, I2C/SPI controllers, display controllers, audio codecs, and CPU cores. Runtime PM frameworks and CPU frequency scaling drivers both call directly into the CCF consumer API to gate clocks off when a device is idle and to change CPU clock rates for performance scaling.

Performance Considerations

Clock rate changes and clock gating operations are not free — reparenting a clock or waiting for a PLL to lock can take microseconds to milliseconds depending on the hardware. Provider drivers should avoid unnecessary rate recalculations in hot paths, and consumer drivers should cache a clock reference obtained once at probe time rather than repeatedly calling clk_get().

Security Considerations

Because a misbehaving or malicious consumer could otherwise request a clock rate or state that damages hardware or destabilizes clocks shared by other devices, the CCF enforces rate constraints (via clk_set_rate_range() and similar APIs) and access through the driver core’s device model rather than exposing raw register access to userspace. Debugfs clock summary output is read-only and does not permit rate changes from a user without kernel driver access.

Summary and Key Takeaways

  • The Linux Common Clock Framework replaces duplicated, platform-specific clock code with one uniform API, enabled by CONFIG_COMMON_CLK.
  • Providers implement hardware access; consumers call the common clk_*() API — a driver can be both.
  • struct clk_hw ties the hardware-specific half to the framework core; it must always be embedded, never pointed to separately.
  • struct clk_ops supplies only the callbacks a given clock type actually needs.
  • struct clk_init_data is consumed once, at registration time.

Conclusion

This lecture built the conceptual foundation of the Linux Common Clock Framework: why it exists, how providers and consumers divide responsibility, and how the five core data structures fit together, backed by a minimal working fixed-rate clock provider you can build and test today. In the next lecture of this free Linux kernel development course, we will write a complete Device Tree–based clock provider driver with multiple clock outputs and explore the consumer-side API in depth.

Frequently Asked Questions

What is the Linux Common Clock Framework used for?

It provides one hardware-independent API for enabling, disabling, and changing the rate of clock lines on an SoC, replacing platform-specific clock code that used to be duplicated across machine directories.

Is the Common Clock Framework the same as RTC support?

No. Real-Time Clocks and timekeeping devices are handled by separate kernel subsystems; the CCF only manages hardware clock lines that drive digital logic.

What kernel config option enables the CCF?

CONFIG_COMMON_CLK must be enabled for CCF support to be compiled into the kernel.

What is the difference between a clock provider and a clock consumer?

A provider driver implements the hardware-specific callbacks for a real clock device; a consumer driver simply calls the common clk_*() API to use a clock it does not own.

Why must struct clk_hw be embedded instead of pointed to?

The kernel recovers your driver’s full structure from a clk_hw pointer using container_of(), which only works correctly when clk_hw is a direct member of that structure.

Do all clock providers need to implement every clk_ops callback?

No. Only the callbacks relevant to that clock type are required — a fixed-rate clock, for example, typically only needs recalc_rate.

What is struct clk_core used for?

It is the framework’s internal representation of a clock. Neither provider nor consumer drivers access it directly — it is private to drivers/clk/clk.c.

Where can I inspect the live clock tree on a running kernel?

Mount debugfs and read /sys/kernel/debug/clk/clk_summary, which dumps every registered clock, its parent, rate, and enable count.

Is this lecture part of a free course?

Yes, this is part of EmbeddedPathashala’s free Linux kernel development course, free Linux device drivers course, and free embedded systems course.

What will the next lecture in this chapter cover?

Writing a complete, Device Tree–registered clock provider driver with multiple outputs, followed by the full clock consumer API.

 

Continue the Free Linux Kernel Development Course

Explore more free Linux device drivers and embedded systems lectures on EmbeddedPathashala.

PREV_LEC  |  NEXT_LEC

 

Leave a Reply

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