What are Linux Syscon Framework Regmap Basics in Linux Kernel

Linux Syscon Framework Regmap Basics -Free Linux Device Drivers Course
Free Linux Device Drivers Course — MFD Subsystem, Lecture 7
Lecture 7
Kernel 6.x
~14 min read

free linux kernel development course
free linux device drivers course
free embedded systems course
linux syscon framework regmap
system controller driver

The linux syscon framework regmap mechanism exists for exactly one reason: SoCs often expose a block of miscellaneous MMIO registers that don’t belong to any single functional device. This free linux kernel development course lecture explains what syscon actually is, how it wraps regmap, and how to declare one correctly in the device tree on a modern kernel.

What You Will Learn

  • What a syscon (system controller) node represents on real hardware
  • The internal struct syscon and why you should never touch it directly
  • The default regmap configuration syscon applies automatically
  • The mandatory and optional device tree properties for a syscon node
  • How to declare your own syscon node with a hardware spinlock reference

Prerequisites

What Problem Does Syscon Solve?

Some SoC register blocks genuinely don’t belong to any one IP block — a handful of scattered control bits for clocking, resets, or pinmux quirks that several unrelated drivers each need to poke a few bits in. Writing a dedicated functional driver for such a register block makes no sense, because there is no cohesive device behind it. The syscon framework — “system controller” — exists to let any number of unrelated drivers share safe, serialized access to that register space through the standard regmap API, without anyone having to write a custom driver for it.

Multiple Drivers Sharing One Syscon Regmap
clock driver
pinctrl driver
reset driver
↓↓↓
syscon regmap (shared MMIO block)

The struct syscon Internal Structure

Syscon is implemented in drivers/mfd/syscon.c. Its core data structure exists purely for internal bookkeeping — application drivers never allocate or touch it directly, they only ever receive a struct regmap * back from a lookup call:

struct syscon {
    struct device_node *np;
    struct regmap *regmap;
    struct list_head list;
};

np identifies which device tree node this syscon represents, regmap is the shared register-map handle every consumer eventually gets a pointer to, and list links every syscon on the system into one global list so lookups by node or by compatible string can walk through it.

Required Header Files

#include <linux/mfd/syscon.h>
#include <linux/regmap.h>

The regmap header is mandatory alongside the syscon header, since every syscon API ultimately hands back or consumes a struct regmap *.

The Default Regmap Configuration

Any device tree node that lists "syscon" in its compatible string gets its reg region ioremapped and bound to an MMIO regmap automatically during early boot, using this built-in default configuration:

static const struct regmap_config syscon_regmap_config = {
    .reg_bits  = 32,
    .val_bits  = 32,
    .reg_stride = 4,
};

This assumes standard 32-bit registers spaced 4 bytes apart, which matches the vast majority of SoC MMIO blocks. Two optional DT properties, described below, let you override parts of this default when the hardware doesn’t match it.

Mandatory and Optional Device Tree Properties

Property Required? Meaning
compatible Mandatory Must include the string “syscon”
reg Mandatory The register region syscon should expose
reg-io-width Optional Overrides the default access width in bytes
hwlocks Optional Phandle to a hardware spinlock provider node

Declaring a Syscon Node

The following original device tree excerpt declares a syscon register block guarded by a hardware spinlock, a common pattern on SoCs where a co-processor can also touch the same registers:

ep_ctrl: ep-ctrl-block@44000 {
    compatible = "ep,ctrl-block", "syscon";
    reg = <0x44000 0x100>;
    hwlocks = <&ep_hwlock 3>;
};

ep_hwlock: hwspinlock@48000 {
    compatible = "ep,hwspinlock";
    reg = <0x48000 0x1000>;
    #hwlock-cells = <1>;
};

Listing both a vendor-specific compatible string (“ep,ctrl-block”) and the generic “syscon” string is the standard convention — it lets a dedicated driver bind to the node for anything syscon can’t express, while still letting any other driver reach the same registers generically through syscon lookups.

How Registration Happens Internally

During early boot, the OF core calls into syscon’s own registration path for every node advertising the “syscon” compatible string. Internally this looks roughly like the following (simplified for clarity):

static DEFINE_SPINLOCK(syscon_list_slock);
static LIST_HEAD(syscon_list);

static struct syscon *of_syscon_register(struct device_node *np, bool check_res)
{
    struct syscon *syscon;

    if (!of_device_is_compatible(np, "syscon"))
        return ERR_PTR(-EINVAL);

    /* ... allocate syscon, build the regmap, ioremap reg ... */

    spin_lock(&syscon_list_slock);
    list_add_tail(&syscon->list, &syscon_list);
    spin_unlock(&syscon_list_slock);

    return syscon;
}

This is exactly why any node that merely lists “syscon” in its compatible property gets picked up automatically — no explicit driver registration is needed for the syscon side of things.

Build and Check the Demo Binding

Since syscon registration happens automatically from the device tree, you can verify a syscon node came up correctly without writing any driver code at all, just by checking the debugfs regmap listing:

$ sudo mount -t debugfs none /sys/kernel/debug
$ ls /sys/kernel/debug/regmap/ | grep 44000

Expected output:

44000.ep-ctrl-block

Seeing an entry here confirms the syscon regmap was created; the next lecture covers the exact API calls a consumer driver uses to look that regmap up.

Common Mistakes

  • Forgetting to add “syscon” alongside the vendor-specific compatible string
  • Assuming reg-io-width changes the register spacing rather than just the access width
  • Manually allocating a struct syscon instead of only ever holding a struct regmap * returned from a lookup call
  • Adding a hwlocks phandle to a spinlock provider node that has no #hwlock-cells property

Best Practices

  • Always list a vendor-specific compatible string first, then “syscon” second
  • Use hwlocks whenever the same register block can also be touched by firmware or a co-processor
  • Never store or dereference struct syscon directly in a consumer driver — treat it as fully opaque

Summary

Syscon is a thin, automatic wrapper around regmap for MMIO register blocks that don’t belong to any single functional device. Adding “syscon” to a node’s compatible list is enough to get a shared, safely accessed regmap for that region during early boot, with no dedicated driver required on the syscon side.

FAQ

What exactly does adding “syscon” to a compatible string do?

It tells the OF core to ioremap that node’s reg region and bind it to an MMIO regmap automatically during early boot, using the default syscon_regmap_config.

Can I ever allocate or modify a struct syscon myself?

No, it is an internal bookkeeping structure. Consumer drivers only ever receive a struct regmap pointer from a lookup call.

What does reg-io-width actually change?

It overrides the access width (in bytes) used for reads and writes, letting you override the default 32-bit access assumption for hardware that needs narrower or wider accesses.

When should I add a hwlocks property to a syscon node?

When the same register block can also be accessed by firmware, a co-processor, or another core outside Linux’s control, so accesses need a hardware spinlock rather than just a software one.

Do I need a dedicated driver to use syscon?

No, syscon registration itself is automatic from the device tree; you only need a driver if you also want vendor-specific behavior beyond what generic regmap access provides.

Where can I verify a syscon regmap was created?

Mount debugfs and check /sys/kernel/debug/regmap/ for an entry matching the syscon node’s register address.

 

Continue the Free Linux Device Drivers Course

Leave a Reply

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