Skip to content

Platform Bus Internals

Overview

The Linux Platform Bus is the most commonly used bus type for SoC-integrated peripherals in Embedded Linux systems.

Examples:

  • GPIO Controller
  • UART Controller
  • SPI Controller
  • I2C Controller
  • Watchdog
  • RTC
  • PWM
  • DMA
  • PCIe Controller

The Platform Bus is built on top of the Linux Driver Model.


Relationship with Driver Core

The Platform Bus is not a separate driver framework.

It is a specialization of the Linux Driver Model.

Driver Core

bus_type
device
device_driver

Platform Bus extends these structures:

platform_bus_type
bus_type

platform_device
device

platform_driver
device_driver

Platform Bus Architecture

Platform Bus

platform_device
platform_match()
platform_driver

The Platform Bus is responsible for matching devices and drivers.

A successful match triggers the driver's probe callback.


Device Creation

Platform devices can be created in multiple ways.


Device Tree

Most modern ARM systems use Device Tree.

Device Tree
OF Subsystem
of_platform_populate()
platform_device

Example:

uart0 {
    compatible = "vendor,uart";
};

The kernel creates a corresponding platform device.


Manual Registration

A platform device may also be created manually.

platform_device_register_simple(
    "demo_platform",
    -1,
    NULL,
    0);

This is commonly used for:

  • Testing
  • Legacy code
  • Virtual devices

Driver Registration

Platform drivers are registered through:

platform_driver_register()

Internally:

platform_driver_register()
driver_register()

The driver becomes part of the Platform Bus.


Matching Flow

The Platform Bus provides its own match callback:

platform_match()

Conceptually:

platform_device
platform_match()
platform_driver

A successful match allows probing to proceed.


Device Tree Matching

The most common matching method on ARM systems uses Device Tree.

Driver:

static const struct of_device_id demo_of_match[] = {
    { .compatible = "demo,platform-bus" },
    {}
};
.driver = {
    .of_match_table = demo_of_match,
}

Device Tree:

demo-platform {
    compatible = "demo,platform-bus";
};

Matching:

compatible
of_match_table
platform_match()

Name Matching

If Device Tree matching is unavailable, Platform Bus may fall back to name matching.

Device:

platform_device_register_simple(
    "demo_platform",
    ...
);

Driver:

.driver = {
    .name = "demo_platform",
}

Matching:

platform_device.name
platform_driver.name

Probe Flow

The complete flow is:

platform_driver_register()
driver_register()
driver_attach()
platform_match()
really_probe()
platform_probe()
driver probe()

The driver's probe callback is not called directly by Device Tree.


Device Tree and Probe

A common misunderstanding is:

Device Tree
probe()

This is incorrect.

Actual flow:

Device Tree
platform_device
platform_match()
platform_driver
probe()

Device Tree creates devices.

The Platform Bus performs matching.


MODULE_DEVICE_TABLE(of)

Example:

MODULE_DEVICE_TABLE(of, demo_of_match);

This macro generates OF module aliases.

Example:

alias: of:N*T*Cdemo,platform-busC*

Purpose:

Module auto-loading
Module alias generation

It is not responsible for runtime matching.

Runtime matching uses:

.of_match_table

Sysfs Representation

Platform Bus objects are visible through sysfs.

Bus:

/sys/bus/platform

Devices:

/sys/bus/platform/devices

Drivers:

/sys/bus/platform/drivers

Device Tree association:

device
of_node
Device Tree node

Driver association:

device
driver
platform_driver

Key Takeaways

Platform Bus is built on top of Driver Core.

Device Tree creates platform_device objects.

Platform Bus performs matching.

Successful matching triggers probe().

Device Tree does not call probe() directly.

Related Topics

  • Linux Driver Model Fundamentals
  • Device Tree Fundamentals
  • Platform Driver and Device Tree Binding
  • Driver Probe and Remove Lifecycle

Related APIs

  • platform_driver_register()
  • platform_driver_unregister()
  • platform_device_register_simple()
  • platform_device_unregister()
  • MODULE_DEVICE_TABLE()