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 sysconand 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
- Regmap fundamentals (covered in our Regmap API series)
- Device tree binding rules for MFD subdevices (covered in Lecture 6)
- Basic MMIO concepts (covered in our Kernel Memory Management series)
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.
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-widthchanges the register spacing rather than just the access width - Manually allocating a
struct sysconinstead of only ever holding astruct regmap *returned from a lookup call - Adding a
hwlocksphandle to a spinlock provider node that has no#hwlock-cellsproperty
Best Practices
- Always list a vendor-specific compatible string first, then “syscon” second
- Use
hwlockswhenever the same register block can also be touched by firmware or a co-processor - Never store or dereference
struct syscondirectly 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
