Keywords covered in this lecture
struct clk_gate
clk_hw_register_gate
CLK_GATE_HIWORD_MASK
free linux kernel development course
free linux device drivers course
Of the base clock types this chapter has covered, the linux gate clock
driver is the one you will write most often in real MMIO-based clock
controllers — it does exactly one job: turn a clock signal on or off through a
single register bit. This lecture works through struct clk_gate field by field, the
hiword-mask register trick some SoCs require, and the registration helper that wraps
it all up.
What You Will Learn
- The struct clk_gate wrapper and its reg/bit_idx/lock fields
- CLK_GATE_SET_TO_DISABLE and what it means to invert gate polarity
- CLK_GATE_HIWORD_MASK and the hiword-mask register write pattern
- clk_hw_register_gate() and the built-in clk_gate_ops it wires up
- Registering an original MMIO gate clock end to end
Prerequisites
- Fixed-factor clock driver case study (previous lecture)
- Mandatory clk_ops by clock type (Gate column)
- clk_hw embedding and container_of pattern
The struct clk_gate Wrapper
A gate clock’s entire hardware-specific state is: which register to write, which bit
in it controls the gate, and what locking is needed to write it safely.
struct clk_gate {
struct clk_hw hw;
void __iomem *reg;
u8 bit_idx;
u8 flags;
spinlock_t *lock;
};
#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw)
- reg: the MMIO virtual address of the register controlling this gate
- bit_idx: which bit in that register turns the clock on or off
- flags: gate-specific behavior flags, covered next
- lock: a spinlock shared with any sibling clocks that share the same register
This structure assumes the gate register is plain MMIO — a detail the next
lecture builds directly on when covering gate clocks that live behind I2C or SPI
instead.
Gate-Specific Flags
Two flags control how the register write itself behaves:
- CLK_GATE_SET_TO_DISABLE: inverts the usual polarity. With this flag set, writing 1 turns the clock off and writing 0 turns it on, instead of the default the other way around
- CLK_GATE_HIWORD_MASK: uses a hiword-mask register instead of a plain read-modify-write
Understanding Hiword Mask
Some hardware registers cannot be safely read-modify-written — writing one bit
might unintentionally reset others due to how the hardware latches the value. Hiword
mask solves this within a single 32-bit register: the lower 16 bits (0-15) hold the
actual gate bit values, while the upper 16 bits (16-31) act as a per-bit write-enable
mask. To change bit b1, you set bit b1 in the lower half to
the new value and set bit b1 << 16 in the upper half to
signal that this particular bit should actually be applied. Every other bit position is
left alone automatically. Because only 16 bits are available for the actual gate
values, bit_idx must be 15 or lower whenever this flag is used.
Registering With clk_hw_register_gate()
struct clk_hw *clk_hw_register_gate(struct device *dev, const char *name,
const char *parent_name,
unsigned long flags,
void __iomem *reg, u8 bit_idx,
u8 clk_gate_flags, spinlock_t *lock);
flags here are the framework-level flags from an earlier lecture —
it is common to set CLK_SET_RATE_PARENT on a gate clock that has a parent,
so rate-change requests are forwarded up one level, since a plain gate has no rate logic
of its own. clk_gate_flags corresponds to the wrapper struct’s own
flags field (CLK_GATE_SET_TO_DISABLE,
CLK_GATE_HIWORD_MASK).
The framework assigns its own built-in ops automatically before registration:
const struct clk_ops clk_gate_ops = {
.enable = clk_gate_enable,
.disable = clk_gate_disable,
.is_enabled = clk_gate_is_enabled,
};
EXPORT_SYMBOL_GPL(clk_gate_ops);
Exactly the three callbacks the Gate column of the mandatory-callbacks table two
lectures ago said were required — nothing more, since a gate clock cannot change
rate or select between parents.
Original Demo: An MMIO Gate Clock
/* ep_mmio_gate_demo.c - a gate clock controlled through a single register bit */
#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>
struct ep_gate_demo_data {
void __iomem *base;
spinlock_t lock;
struct clk_hw *gate_hw;
};
static int ep_mmio_gate_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct ep_gate_demo_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);
/* bit 3 of the gate register controls this peripheral clock */
data->gate_hw = devm_clk_hw_register_gate(dev, "ep_periph_gate", "ep_bus_clk",
CLK_SET_RATE_PARENT,
data->base, 3, 0, &data->lock);
if (IS_ERR(data->gate_hw))
return PTR_ERR(data->gate_hw);
return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, data->gate_hw);
}
static const struct of_device_id ep_mmio_gate_of_match[] = {
{ .compatible = "ep,clk-gate-demo" },
{ }
};
MODULE_DEVICE_TABLE(of, ep_mmio_gate_of_match);
static struct platform_driver ep_mmio_gate_driver = {
.probe = ep_mmio_gate_probe,
.driver = {
.name = "ep_clk_gate_demo",
.of_match_table = ep_mmio_gate_of_match,
},
};
module_platform_driver(ep_mmio_gate_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala MMIO gate clock demo");
Matching device tree node, deriving from the critical bus clock introduced in an
earlier lecture:
ep_gate_ctrl: clock-controller@7000 {
compatible = "ep,clk-gate-demo";
reg = <0x7000 0x4>;
#clock-cells = <0>;
clocks = <&ep_bus_clk>;
};
Build and Run
make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
sudo insmod ep_mmio_gate_demo.ko
cat /sys/kernel/debug/clk/clk_summary | grep ep_periph_gate
Expected clk_summary excerpt, showing the gate initially enabled (a driver would call
clk_prepare_enable()/clk_disable_unprepare() to control it at
runtime):
ep_periph_gate 1 1 0 100000000 0
Registering the Same Gate With Hiword Mask
If the same bit lived in a register that only supports hiword-mask writes, only the
clk_gate_flags argument changes — nothing else about the driver:
data->gate_hw = devm_clk_hw_register_gate(dev, "ep_periph_gate", "ep_bus_clk",
CLK_SET_RATE_PARENT,
data->base, 3,
CLK_GATE_HIWORD_MASK, &data->lock);
Internally, clk_gate_enable()/clk_gate_disable() then write
both the target bit and its corresponding mask bit in the upper 16 bits, instead of doing
a plain read-modify-write on the register.
Plain vs Hiword-Mask Gate Registers
| Aspect | Plain Gate Register | Hiword-Mask Gate Register |
|---|---|---|
| Write style | Read-modify-write | Direct write with an inline mask |
| Max bit_idx | Up to register width | 15 |
| Flag needed | None | CLK_GATE_HIWORD_MASK |
Common Mistakes
- Using CLK_GATE_HIWORD_MASK with a bit_idx above 15
- Forgetting CLK_GATE_SET_TO_DISABLE on hardware where writing 1 actually disables the clock
- Sharing a register across multiple gate clocks without a common spinlock
- Assuming struct clk_gate works for a gate register behind I2C or SPI
Best Practices
- Use clk_hw_register_gate()/devm_ variant instead of building struct clk_gate by hand
- Share one spinlock across every gate clock backed by the same register
- Check the datasheet for hiword-mask support before assuming plain read-modify-write is safe
- Set CLK_SET_RATE_PARENT whenever the gate sits beneath a rate-changing parent
Summary and Key Takeaways
- struct clk_gate holds reg, bit_idx, flags, and lock alongside the embedded clk_hw
- CLK_GATE_SET_TO_DISABLE inverts polarity; CLK_GATE_HIWORD_MASK changes the write style entirely
- clk_hw_register_gate() wires up clk_gate_ops (enable/disable/is_enabled) automatically
- This structure assumes an MMIO register, which the next lecture explicitly does not have
Frequently Asked Questions
What does CLK_GATE_SET_TO_DISABLE actually change?
It inverts the gate’s write polarity: writing 1 disables the clock and writing 0
enables it, the opposite of the default behavior.
Why is bit_idx limited to 15 under hiword mask?
Because only the lower 16 bits of the 32-bit register carry actual gate values; the
upper 16 bits are reserved entirely for the write-enable mask.
Can multiple gate clocks share the same spinlock?
Yes, and they should whenever they share the same underlying register, to avoid a
race between two gate bits being modified at once.
Does a gate clock need CLK_SET_RATE_PARENT?
Only if it sits beneath a parent that can change rate and you want rate-change
requests on the gate to propagate upward; a gate has no rate logic of its own either
way.
Where is the built-in gate clock implementation defined?
drivers/clk/clk-gate.c in the kernel source tree, which implements clk_gate_ops and
the registration helper covered in this lecture.
Suggested Images For This Lecture
- Diagram: struct clk_gate fields (reg, bit_idx, flags, lock)
- Diagram: hiword mask register layout, lower 16 bits vs upper 16 bits
- Screenshot: clk_summary showing a registered MMIO gate clock
Continue This Free Linux Kernel Development Course
Next, we look at gate clocks that live behind I2C or SPI instead of MMIO, and the
DT-only gpio-gate-clock alternative.
