Platform Devices in Linux Kernel: A Complete Beginner’s Guide-Linux Device Drivers Course

Platform Devices in Linux Kernel: A Complete Beginner’s Guide

Understand how the Linux kernel represents on-chip peripherals using the platform bus, updated for modern 6.x kernels

Free Linux Kernel Course
Free Device Drivers Course
Beginner Friendly

« Previous Lecture  |  Next Lecture »

If you are following a free Linux kernel programming course or a free Linux device drivers course, one of the very first roadblocks you hit is understanding platform devices in the Linux kernel. Most peripherals on an embedded board or a System on Chip (SoC) do not sit on a real, discoverable bus like PCI or USB. Instead, the kernel needs a software-only bus to represent them, and that is exactly what the platform bus does. In this tutorial you will learn what a platform device is, why it exists, and how modern kernels register and expose these devices.

What You Will Learn

What a platform device is Why SoC peripherals need the platform bus Device Tree basics Registering a platform driver on modern kernels Inspecting devices through sysfs Common mistakes and best practices

Prerequisites

  • Basic C programming knowledge
  • A Linux system (Ubuntu 22.04 or later recommended) with kernel headers installed
  • Comfort building and inserting kernel modules with make and insmod
  • Familiarity with the Linux Device Model is helpful but not mandatory

What Is a Platform Device?

A platform device is the kernel’s way of representing hardware that cannot announce its own presence. Buses such as USB or PCI are enumerable: you can plug in a device and the bus hardware itself tells the kernel that something new has appeared. Many chips soldered directly onto an SoC, such as a UART controller, an I2C controller, or a watchdog timer, have no such enumeration mechanism. The kernel still needs a uniform struct device to hang driver logic, power management, and sysfs entries off, so it invents a virtual bus called the platform bus and attaches these components to it as platform devices.

SoC to Platform Bus Mapping
UART Controller I2C Controller Watchdog Timer
↓ registered on ↓
Platform Bus (software-only)
↓ exposed via ↓
/sys/devices/platform/

Why the Platform Bus Exists

An SoC packs processing cores together with dozens of peripherals inside a single piece of silicon. None of these internal peripherals sit on a physical, discoverable bus, so the driver core needs a placeholder bus to keep the device model consistent. Every device in Linux, real or virtual, must belong to some bus so that driver matching, power management callbacks, and sysfs directory creation all work the same way regardless of the underlying hardware.

Bus TypeDiscoverable?Typical Devices
PCIYes, hardware enumeratedGraphics cards, NVMe controllers
USBYes, hot-pluggableKeyboards, storage, webcams
PlatformNo, software describedSoC UART, I2C, SPI, RTC, GPIO blocks

Device Tree vs Legacy Board Files

Older kernels described SoC hardware in architecture-specific “board files” compiled directly into the kernel source tree. This approach did not scale as the number of supported boards grew, so the community moved to the Device Tree, a hardware description format stored in separate .dts and .dtsi files. On modern kernels, the vast majority of platform devices are created automatically by the Device Tree parser during boot, matching a compatible string in the tree against a driver’s of_match_table. Manually calling a registration function is now mostly reserved for test drivers, virtual devices, or ACPI-described hardware on non-Device-Tree platforms.

Registering a Platform Driver on Modern Kernels

Rather than manually creating a bare platform device, current kernel practice favors writing a full platform driver that binds through either the Device Tree or ACPI. Below is an original, updated-style skeleton showing the modern probe and remove callback structure used on 6.x kernels.

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>

struct epdemo_data {
    struct device *dev;
    int debug_level;
};

static int epdemo_probe(struct platform_device *pdev)
{
    struct epdemo_data *data;

    data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
    if (!data)
        return -ENOMEM;

    data->dev = &pdev->dev;
    platform_set_drvdata(pdev, data);

    dev_info(&pdev->dev, "epdemo platform driver probed\n");
    return 0;
}

static void epdemo_remove(struct platform_device *pdev)
{
    dev_info(&pdev->dev, "epdemo platform driver removed\n");
}

static const struct of_device_id epdemo_of_match[] = {
    { .compatible = "embeddedpathashala,epdemo" },
    { }
};
MODULE_DEVICE_TABLE(of, epdemo_of_match);

static struct platform_driver epdemo_driver = {
    .probe  = epdemo_probe,
    .remove = epdemo_remove,
    .driver = {
        .name = "epdemo_platform_driver",
        .of_match_table = epdemo_of_match,
    },
};
module_platform_driver(epdemo_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala platform driver demo");

Note: Notice the use of devm_kzalloc() instead of plain kzalloc(). Device-managed allocations are automatically freed when the device is removed, which is the modern recommended pattern and eliminates a whole class of memory-leak bugs in driver cleanup paths.

Verifying Platform Devices Through sysfs

Once a platform device is bound, the driver core automatically creates a directory for it under /sys/devices/platform/. You can confirm this from the command line:

$ ls /sys/devices/platform/
$ cat /sys/devices/platform/<your-device>/uevent

Every entry you see here corresponds to a struct device created by the driver core, whether it came from a real bus or the virtual platform bus.

Real-World Use Cases

  • Board-specific LED and GPIO drivers on embedded Linux boards
  • SoC-integrated UART, I2C, and SPI controller drivers
  • Watchdog timers and hardware random number generators baked into the SoC
  • Virtual test devices used to prototype sysfs or debugfs interfaces before real hardware is available

Common Mistakes and Troubleshooting

MistakeFix
Forgetting to unregister the device in the remove pathAlways pair every register call with the matching unregister call
Using plain kzalloc() for driver private dataPrefer devm_kzalloc() so cleanup happens automatically
Hardcoding board details instead of using Device TreeMatch against a compatible string via of_match_table
Assuming probe order across devicesNever rely on probe ordering; use deferred probing (-EPROBE_DEFER) when a dependency is not ready

Best Practices

  • Always prefer Device Tree matching over manual, hardcoded registration on modern boards
  • Use device-managed (devm_*) APIs wherever possible
  • Return -EPROBE_DEFER from probe() if a required resource is not yet available
  • Keep probe() lightweight; move heavy initialization to workqueues if it can be deferred

Security Considerations

Platform devices are typically trusted, board-described hardware, but any sysfs or debugfs files a platform driver exposes are attack surface. Always validate any user-writable attribute in the driver, and default to the narrowest sysfs file permissions that are actually needed rather than opening files world-writable.

Performance Considerations

Because platform devices are usually the backbone of core SoC peripherals, keep probe() functions fast, avoid blocking calls during early boot probing, and defer non-critical initialization to a workqueue so that overall boot time is not impacted.

Summary: Key Takeaways

  • Platform devices represent SoC peripherals that cannot self-announce on a real bus
  • The platform bus is a purely software construct maintained by the driver core
  • Modern kernels create platform devices automatically from the Device Tree
  • Use devm_* managed APIs and of_match_table matching in new drivers

Frequently Asked Questions

Q1. What is a platform device in the Linux kernel?
A platform device is a software representation of hardware, typically SoC-integrated, that has no physical discoverable bus of its own.

Q2. Is the platform bus a real hardware bus?
No, it exists purely in software inside the kernel’s driver core.

Q3. Do I still need to manually register platform devices on modern kernels?
Rarely. Most production drivers rely on the Device Tree to create and bind platform devices automatically.

Q4. What replaced board files?
The Device Tree, described using .dts/.dtsi source files, replaced most architecture-specific board files.

Q5. Where can I see platform devices on a running system?
Under /sys/devices/platform/ using the ls and cat commands.

Q6. What is -EPROBE_DEFER used for?
It tells the kernel to retry probing later because a dependency, such as a clock or regulator, is not yet available.

Q7. Is this course free?
Yes, this is part of a free Linux kernel programming and free Linux device drivers course.

Continue Learning Linux Kernel Programming

This lecture is part of EmbeddedPathashala’s free Linux kernel development course covering device drivers, embedded systems, and Bluetooth/BLE programming.

« Previous Lecture  |  Next Lecture »

2 Comments

Leave a Reply

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