Linux Syscon And Simple-MFD- Free Linux Device Drivers Tutorial

Linux Syscon And Simple-MFD
Free Linux Kernel Development Course · Free Linux Device Drivers Course · Free Embedded Linux Course
Lecture 3 of 3
Kernel 6.x
Zero-Conf Subdevice Demo
linux syscon api
simple-mfd driver
free linux kernel development course
free linux device drivers course
regmap syscon

This final lecture of the MFD mini-series covers the Linux syscon API and the simple-mfd device tree binding — the zero-configuration path for sharing a device’s register region between subdevices, without writing a dedicated MFD core driver at all. If you followed the free linux kernel development course lectures on regmap, this is where that API is put to direct use for miscellaneous, cross-cutting SoC registers.

What You Will Learn

  • What “syscon” registers are and why they don’t fit a normal driver model
  • The syscon lookup API: by phandle, by compatible string, and by platform device name
  • What the simple-mfd compatible string does and when to use it instead of a core driver
  • How to write an original consumer driver that reads a syscon-backed regmap

Prerequisites

  • Completed Lectures 1 and 2 of this MFD mini-series
  • Familiarity with the regmap API from this course’s regmap chapter

Why Syscon Exists

Many SoCs have a block of miscellaneous registers that configure unrelated features — pin muxing bits, reset lines, clock gating, boot-mode straps — packed together in one MMIO region simply because that is how the silicon was laid out. None of these bits belong to one cohesive “device” in the driver-model sense, so writing a dedicated subsystem driver for them makes no sense. The syscon framework, implemented in drivers/mfd/syscon.c, solves this by wrapping the region in a plain regmap that any other driver in the kernel can look up and share safely.

Syscon Sharing One Register Region
syscon node (compatible = “syscon”)
shared regmap
↑ ↑
reset driver
pinctrl driver

The Syscon Lookup API

The header for consumers is <linux/mfd/syscon.h>, and since it is regmap-based you also need <linux/regmap.h>. Three lookup styles cover most use cases:

Function Looks up by
syscon_regmap_lookup_by_phandle(np, property) A phandle property on the consumer’s own DT node
syscon_regmap_lookup_by_compatible(s) A global search for a node with the given compatible string
syscon_regmap_lookup_by_pdevname(s) A platform device name, useful for board files without DT

Every lookup function returns a normal struct regmap *, so once you have it, every regmap read/write helper from the earlier regmap lectures works unchanged.

The simple-mfd Binding

When an MFD node’s only job is to expose its register range to its own children through a shared regmap — no enable/disable hooks, no interrupt demultiplexing, nothing beyond “share this address range” — you don’t need a custom core driver at all. Adding "simple-mfd" alongside your vendor compatible string tells the generic simple-mfd driver to create a regmap for the node and register every child node as its own platform device automatically.

/* ep-syscon-demo.dtsi — original EmbeddedPathashala teaching overlay */
soc {
	#address-cells = <1>;
	#size-cells = <1>;

	ep_syscon: system-controller@1c000000 {
		compatible = "ep,ep-syscon", "simple-mfd", "syscon";
		reg = <0x1c000000 0x1000>;
	};

	ep_consumer: ep-consumer@0 {
		compatible = "ep,ep-consumer";
		ep-syscon = <&ep_syscon>;
	};
};

Listing "syscon" as a compatible fallback is what lets syscon_regmap_lookup_by_phandle() find this exact node; listing "simple-mfd" is what tells the kernel to auto-register any children of this node without a custom core driver.

Original Coding Example: ep_syscon_demo

This consumer driver looks up the syscon regmap through the ep-syscon phandle property and reads one 32-bit register from it at probe time.

// ep_syscon_demo.c — original EmbeddedPathashala syscon consumer example
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>

#define EP_STATUS_REG_OFFSET   0x04

static int ep_syscon_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct regmap *regmap;
	u32 val;
	int ret;

	regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "ep-syscon");
	if (IS_ERR(regmap)) {
		dev_err(dev, "failed to get syscon regmap: %ld\n",
			PTR_ERR(regmap));
		return PTR_ERR(regmap);
	}

	ret = regmap_read(regmap, EP_STATUS_REG_OFFSET, &val);
	if (ret) {
		dev_err(dev, "regmap_read failed: %d\n", ret);
		return ret;
	}

	dev_info(dev, "ep_syscon_demo: status register = 0x%08x\n", val);
	return 0;
}

static const struct of_device_id ep_syscon_of_match[] = {
	{ .compatible = "ep,ep-consumer" },
	{ }
};
MODULE_DEVICE_TABLE(of, ep_syscon_of_match);

static struct platform_driver ep_syscon_driver = {
	.driver = {
		.name           = "ep-syscon-demo",
		.of_match_table = ep_syscon_of_match,
	},
	.probe = ep_syscon_probe,
};
module_platform_driver(ep_syscon_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala syscon regmap consumer example");

Build and Run Steps

obj-m += ep_syscon_demo.o
make -C /lib/modules/$(uname -r)/build M=$PWD modules

sudo insmod ep_syscon_demo.ko
dmesg | tail -3
sudo rmmod ep_syscon_demo

Expected dmesg Output

[  340.221100] ep_syscon_demo: status register = 0x00000000

On real hardware this value reflects whatever the actual system-controller register contains; on a QEMU or emulated platform without backing memory for that offset it commonly reads back as zero, which is still a successful demonstration of the lookup-and-read path.

simple-mfd vs a Custom Core Driver

Aspect simple-mfd Custom MFD core driver
Code required None — DT compatible string only A full probe/remove core driver
Enable/disable hooks Not supported Supported via mfd_cell callbacks
Best fit Zero-conf register sharing (syscon-style) Chips needing IRQ demux, power sequencing, or custom setup

Common Mistakes

  • Forgetting the plain "syscon" fallback compatible, which breaks syscon_regmap_lookup_by_phandle() for consumers
  • Using simple-mfd on a node that actually needs custom enable/disable or IRQ handling logic
  • Calling a syscon lookup function before the syscon node’s driver has probed, without handling -EPROBE_DEFER

Best Practices

  • Always check for -EPROBE_DEFER from syscon lookups and let the driver core retry probing
  • Keep a dedicated header of register offsets shared between the syscon node and its consumer drivers
  • Use syscon_regmap_lookup_by_phandle_optional() when the syscon reference in DT is genuinely optional

Real-World Use Cases

Reset controllers, pin controllers, and clock drivers on many ARM SoCs read and write shared “system control” registers through exactly this syscon pattern rather than mapping the same physical memory twice from two separate drivers.

Summary / Key Takeaways

  • Syscon wraps a miscellaneous register region in a shareable regmap
  • Three lookup functions cover phandle, compatible-string, and platform-device-name based discovery
  • simple-mfd removes the need for a custom core driver in the zero-configuration case
  • Consumer drivers should always handle -EPROBE_DEFER from syscon lookups

Conclusion

Across this three-lecture mini-series you moved from a full custom MFD core driver, through device tree binding, to the zero-conf syscon/simple-mfd shortcut — covering the complete spectrum of multi-function device support in the modern Linux kernel. This closes out the MFD subsystem chapter of the free linux kernel development course.

Frequently Asked Questions

Is syscon only used for SoC-internal registers?

Mostly yes — syscon is primarily used for on-SoC miscellaneous register blocks, though the same mechanism works for any shared MMIO region described in device tree.

Do I still need struct mfd_cell with simple-mfd?

No. simple-mfd auto-registers every DT child node as a platform device without you defining any mfd_cell array in C code.

What does syscon_node_to_regmap() do differently from the phandle lookup?

It takes a device_node directly instead of parsing a phandle property, which is useful when you already have the target node from another lookup.

Can simple-mfd and a custom core driver be combined?

No, a node is handled by either the generic simple-mfd driver or your custom core driver, not both, since they both bind against the parent compatible string.

What error code indicates the syscon node has not probed yet?

syscon_node_to_regmap() returns -EPROBE_DEFER when no matching syscon device has been found yet, signaling the kernel to retry your driver’s probe later.

Where can I see simple-mfd used in mainline device trees?

Search arch/arm/boot/dts and arch/arm64/boot/dts in the kernel source tree for the string “simple-mfd” to find many real vendor examples.

You Completed the MFD Subsystem Chapter

Continue with the Free Linux Kernel Development Course

 

Leave a Reply

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