What are MFD Subdevice Compatible Matching Rule in Linux Kernel

MFD Subdevice Compatible Matching Rules-Free Linux Device Drivers Course
Free Linux Device Drivers Course — MFD Subsystem, Lecture 6
Lecture 6
Kernel 6.x
~15 min read

free linux device drivers course
free linux kernel development course
free embedded systems course
mfd subdevice compatible matching linux
of_compatible

Getting mfd subdevice compatible matching linux right is what actually makes an MFD device tree node bind to the correct platform driver — get it wrong and your subdevice simply never probes, with no obvious error message. This free linux device drivers course lecture walks through exactly how the kernel matches an MFD subdevice’s device tree node to its platform driver, using a real PMIC binding as the running example.

What You Will Learn

  • Where MFD subdevice nodes belong inside the device tree
  • The two valid ways a subdevice’s compatible property can match its driver
  • How mfd_cell.of_compatible connects an MFD cell to a DT node
  • How to clean up an MFD driver’s resources with mfd_remove_devices()
  • A complete, original device tree binding plus matching platform driver

Prerequisites

Cleaning Up a Manually Added MFD Device

Before looking at device tree binding, it’s worth closing the loop on driver teardown. When subdevices were added with the non-devm mfd_add_devices(), they must be removed manually, and any dummy I2C clients created for them (see Lecture 5) must be unregistered in the same call:

static void ep_mfd_remove(struct i2c_client *client)
{
    struct ep_mfd_chip *chip = i2c_get_clientdata(client);

    mfd_remove_devices(chip->dev);
    i2c_unregister_device(chip->rtc_client);
    i2c_unregister_device(chip->gpio_client);
}

mfd_remove_devices() walks every child platform device that was registered against chip->dev and unregisters each one, mirroring what mfd_add_devices() did at probe time.

Why the Device Tree Needs to Describe MFD Devices

A device tree node’s only job is to describe hardware that exists on the board — it says nothing about drivers. For an MFD chip, that description should include every internal subdevice, declared as a child node of the parent chip’s node. This is not just a style choice: many subdevices share register ranges or interrupt lines that belong to the parent, so nesting the nodes correctly is what lets the kernel resolve those shared resources at all.

Subdevice Nodes Nested Under Their MFD Parent
i2c-bus node
pmic0 (MFD parent node)
rtc child node
watchdog child node
onkey child node

Two Valid Ways to Match a Subdevice Node

Every child node’s compatible string has to line up with the platform driver that should bind to it. The kernel accepts exactly two matching strategies for an MFD subdevice, and mixing them accidentally is a common source of silent probe failures:

Matching style DT side Driver side
Compatible-string matching node’s compatible property platform_driver.of_match_table entries
Name matching (legacy, rare) mfd_cell.name platform_driver.driver.name

Compatible-string matching is by far the more common and more robust choice, mainly because a single platform driver can then bind to several closely related chip variants (for example, two PMIC generations that share an “onkey” block) simply by listing more than one string in its match table.

Example: A PMIC With Regulator, RTC, Watchdog and Onkey Children

The following original device tree excerpt places every internal block of a PMIC as a child node beneath the PMIC itself:

&i2c2 {
    status = "okay";

    pmic0: ep_pmic@58 {
        compatible = "ep,pmic-demo";
        reg = <0x58>;
        interrupt-parent = <&gpio3>;
        interrupts = <7 IRQ_TYPE_LEVEL_LOW>;
        interrupt-controller;

        ep_rtc: rtc {
            compatible = "ep,pmic-demo-rtc";
        };

        ep_watchdog: watchdog {
            compatible = "ep,pmic-demo-watchdog";
        };

        ep_onkey: onkey {
            compatible = "ep,pmic-demo-onkey";
        };
    };
};

Declaring the Matching MFD Cell

On the driver side, each cell in the MFD core carries an of_compatible string that must equal one of the child node’s compatible strings:

static const struct mfd_cell ep_pmic_devs[] = {
    {
        .name = "ep-pmic-demo-onkey",
        .of_compatible = "ep,pmic-demo-onkey",
    },
    {
        .name = "ep-pmic-demo-rtc",
        .of_compatible = "ep,pmic-demo-rtc",
    },
    {
        .name = "ep-pmic-demo-watchdog",
        .of_compatible = "ep,pmic-demo-watchdog",
    },
};

Declaring the Matching Platform Driver

The subdevice’s own platform driver then lists that same compatible string in its of_match_table, and can list more than one if it wants to support related chip variants:

static const struct of_device_id ep_onkey_of_match[] = {
    { .compatible = "ep,pmic-demo-onkey" },
    { .compatible = "ep,pmic-legacy-onkey" },
    { }
};
MODULE_DEVICE_TABLE(of, ep_onkey_of_match);

static struct platform_driver ep_onkey_driver = {
    .probe  = ep_onkey_probe,
    .driver = {
        .name = "ep-pmic-demo-onkey",
        .of_match_table = ep_onkey_of_match,
    },
};
module_platform_driver(ep_onkey_driver);

Build and Run the Demo

$ make -C /lib/modules/$(uname -r)/build M=$PWD modules
$ sudo insmod ep_pmic_demo.ko
$ sudo insmod ep_pmic_demo_onkey.ko
$ dmesg | tail -n 6

Expected output when the compatible strings line up correctly:

[ 300.10021] ep_pmic_demo: probed pmic at 0x58, 3 subdevices added
[ 300.11540] ep-pmic-demo-onkey ep-pmic-demo-onkey.0: probed via DT compatible match
[ 300.11560] ep-pmic-demo-onkey ep-pmic-demo-onkey.0: onkey ready

If the compatible strings are mismatched, the platform device is still created by the MFD core, but no driver ever probes it — dmesg stays silent and /sys/bus/platform/devices/ shows the device with no bound driver.

Common Mistakes

  • Setting mfd_cell.of_compatible to a string that doesn’t exist anywhere in the device tree
  • Declaring the subdevice node as a sibling of the MFD parent instead of a child
  • Mixing name-matching and compatible-matching for the same cell
  • Forgetting MODULE_DEVICE_TABLE(of, ...), which breaks module autoloading even though manual insmod still works

Best Practices

  • Always prefer compatible-string matching over name matching for new drivers
  • Keep MFD subdevice nodes nested under their parent node in the device tree, never flattened
  • List every supported chip variant’s compatible string in the same of_match_table rather than duplicating the driver

Summary

An MFD subdevice’s device tree node has to be declared as a child of its parent MFD node, and its compatible property has to line up with either the subdevice driver’s of_match_table (via mfd_cell.of_compatible) or, more rarely, with the driver’s name. Getting this pairing right is what turns a correctly wired-up device tree into a subdevice that actually probes.

FAQ

Why must a subdevice node be a child of the MFD parent node?

Subdevices often share the parent’s register range or interrupt line, so nesting the node lets the kernel resolve those shared resources; it also reflects the real hardware relationship.

What happens if of_compatible doesn’t match any DT node?

The MFD core still creates the platform device, but no driver binds to it — it shows up unbound in sysfs with no probe message in dmesg.

Can one platform driver match multiple compatible strings?

Yes, list every supported string as separate entries in the same of_match_table array; this is how one driver supports several related chip variants.

Is name-based matching ever a better choice than compatible-string matching?

Rarely — it’s mostly a legacy mechanism and becomes confusing once a single driver is reused across multiple similar chips.

Does mfd_remove_devices() also unregister dummy I2C clients?

No, it only removes the platform devices added via mfd_add_devices(); dummy I2C clients from Lecture 5 must be unregistered separately with i2c_unregister_device().

Where is this documented in the kernel source tree?

Documentation/devicetree/bindings/mfd/mfd.txt describes the general MFD binding conventions used throughout the kernel.

 

Continue the Free Linux Device Drivers Course

Leave a Reply

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