clinux-kerneldriver

Are I²C child nodes instantiated as platform devices during of_platform_populate()?


I'm trying to understand how platform_devicesare instantiated from the Device Tree during early kernel boot.

I noticed that the function of_platform_default_populate_init() is called via arch_initcall_sync() and eventually invokes:

of_platform_default_populate(NULL, NULL, NULL);

This sets up a match_table containing entries like "simple-bus", "simple-mfd", and so on. It then calls:

of_platform_populate(NULL, match_table, NULL, NULL);

Inside of_platform_populate(), the root node ("/") is retrieved using of_find_node_by_path("/"). Then, for each of its child nodes, the function of_platform_bus_create() is called. This function performs several checks:

If the node passes these checks, the function of_platform_device_create_pdata() is called to instantiate a platform_device.

At this point, we have:

dev = of_platform_device_create_pdata(...);
if (!dev || !of_match_node(matches, bus))
    return 0;

This means:

Now, here's my question:

I know that platform_devices are meant for non-discoverable hardware. However, in embedded systems, it's common to describe I²C devices as children of an I²C controller node, and the controller itself is often a platform_device.

So in this case:

Any clarification on this behavior would be appreciated.

EDIT: Here is an example Device Tree snippet that illustrates how I understand the mechanisms. Is it correct ?

/ {
    /* Root‑level node: becomes a platform_device
     * because it is an immediate child of “/” and
     * of_platform_populate() will create a device for it.
     */
    i2c0: i2c@40066000 {
        compatible = "vendor,foo-i2c";   /* NOT "simple-bus" */
        reg        = <0x40066000 0x1000>;
        interrupts = <23>;

        /* of_platform_populate()
         * will not recurse into them because the parent’s
         * compatible string is not in match_table.
         */

        /* ---- I²C child devices (NOT platform_devices) ---- */

        temperature-sensor@48 {
            compatible = "ti,tmp102";
            reg = <0x48>;
        };

        eeprom@50 {
            compatible = "atmel,24c02";
            reg = <0x50>;
        };
    };
};


Solution

  • Child nodes of I2C adapter nodes in the device tree or ACPI tables are instantiated as I2C client devices (not platform devices) by the calls to of_i2c_register_devices() (in "drivers/i2c/i2c-core-i2c.c") or i2c_acpi_register_devices() (in "drivers/i2c/i2c-core-acpi.c") from i2c_register_adapter() (in "drivers/i2c/i2c-core-base.c") when the I2C adapter is registered. These ultimately call i2c_new_client_device() (in "drivers/i2c/i2c-core.c") for each child node.

    The call to i2c_new_client_device() uses information from a struct i2c_board_info object that is filled in with details extracted from the device tree or ACPI tables, such as the I2C address and driver alias string. (For device tree nodes, the driver alias is extracted from the "compatible" property by stripping the vendor portion of the string.)

    Legacy platforms from before the introduction of device trees describe the known I2C devices in C code as arrays of struct i2c_board_info containing details of the devices such as their I2C address and driver alias string, and register these to an I2C bus number by calling i2c_register_board_info() (in "drivers/i2c/i2c-boardinfo.c"), where each entry is appended to a list of static I2C devices. When an I2C adapter is registered by the previously mentioned i2c_register_adapter(), that calls i2c_scan_static_board_info() (in "drivers/i2c/i2c-core-base.c") to scan the list of static I2C devices for entries with the same bus number as the adapter, and that calls i2c_new_client_device() to instantiate the I2C client devices for each matching entry in the list.

    Other drivers may call i2c_new_client_device() on an ad hoc basis.