Platform Data In Linux Drivers- Free Linux Device Drivers Course

PREV_LEC  |  NEXT_LEC

Platform Data In Linux Drivers

Free Linux Device Drivers Course — Free Linux Kernel Development Course — Free Embedded Systems Course

Platform data is how a Linux platform device carries board-specific settings — a GPIO number, a chip variant, a calibration value — over to the driver that will use them. In this free linux device drivers course lecture we explain platform data in the Linux kernel, show the modern platform_device_register_data() helper, and build a small original driver that reads its platform data inside probe() and drives a GPIO line with it. Everything here targets kernel 6.x; no code is copied from any book, and every example is written fresh for EmbeddedPathashala.

What You Will Learn

  • What platform data is, and how it differs from a resource (memory region, IRQ)
  • The struct device.platform_data field and why it exists
  • Why manually filling a struct platform_device by hand is now considered legacy
  • How to register a device with data safely using platform_device_register_data()
  • How to read platform data back in probe() with dev_get_platdata()
  • A complete, runnable original demo driver plus expected dmesg output

Prerequisites

  • Completed our platform bus concept lecture (platform_driver, probe/remove)
  • Comfortable building and loading kernel modules (insmod/rmmod, dmesg)
  • Basic familiarity with GPIO numbering on your test board

What Is Platform Data?

A platform device carries two very different kinds of information to its driver. The first kind is resources — memory-mapped register ranges, interrupt lines — things the kernel already understands and can validate through struct resource. The second kind is everything else: a GPIO line number, a string identifying which hardware variant is fitted, a default configuration value. This second kind is platform data, and the kernel has no built-in meaning for it — it is opaque, driver-specific data that you define yourself.

Platform data lives at platform_device.dev.platform_data, a plain void * pointer. Because it is untyped from the kernel’s point of view, the device and the driver must agree privately on what structure that pointer actually points to. That agreement is usually a shared header file included by both the code that registers the device and the driver that binds to it.

Resource vs Platform Data
resource[]
IORESOURCE_MEM, IORESOURCE_IRQ — the kernel already understands these
dev.platform_data (void *) — only your driver understands this

The Legacy Way: Hand-Built platform_device

Older board files declared a static struct platform_device and pointed its dev.platform_data field directly at a statically allocated data structure, then called platform_device_register(). This works, but it has two problems on a modern kernel. First, the data structure has to stay alive for as long as the device exists — a static global works, but anything allocated on the stack or freed early will corrupt the driver’s view of its own configuration. Second, almost no modern platform declares devices this way anymore; device tree or ACPI does it instead. You will still see this pattern in a handful of older in-tree board files and stub platforms, so it is worth recognising, but it is not how you should write new code.

The Modern Way: platform_device_register_data()

Kernel 6.x provides a small, safe helper for exactly this job:

struct platform_device *platform_device_register_data(
    struct device *parent,
    const char *name,
    int id,
    const void *data,
    size_t size);

Unlike the hand-built approach, this helper copies your data into kernel-managed memory instead of just storing your pointer. That means the buffer you pass in only needs to be valid for the duration of the call — no lifetime tricks required, and no risk of the driver reading freed memory. This single change is the main reason it has replaced manual struct platform_device declarations for platform data use cases in modern code.

Reading Platform Data Back: dev_get_platdata()

On the driver side, inside probe(), you retrieve the pointer with:

void *dev_get_platdata(const struct device *dev);

This is preferred over reading pdev->dev.platform_data directly, since it keeps driver code independent of the exact field layout of struct device and reads clearly as “give me this device’s platform data.”

Original Demo: ep_led_demo Driver

We will register one platform device carrying a small configuration structure — a GPIO number and a label string — and write a driver that reads that structure in probe() and drives the GPIO line. Save the shared structure in a header both files include.

ep_led_data.h

#ifndef EP_LED_DATA_H
#define EP_LED_DATA_H

struct ep_led_config {
    int gpio_num;
    const char *label;
};

#endif

ep_led_board.c — registers the device

#include <linux/module.h>
#include <linux/platform_device.h>
#include "ep_led_data.h"

static struct ep_led_config ep_led_cfg = {
    .gpio_num = 21,
    .label    = "ep-status-led",
};

static struct platform_device *ep_led_pdev;

static int __init ep_led_board_init(void)
{
    ep_led_pdev = platform_device_register_data(NULL, "ep-led-demo", -1,
                                                  &ep_led_cfg,
                                                  sizeof(ep_led_cfg));
    return PTR_ERR_OR_ZERO(ep_led_pdev);
}

static void __exit ep_led_board_exit(void)
{
    platform_device_unregister(ep_led_pdev);
}

module_init(ep_led_board_init);
module_exit(ep_led_board_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala platform data board demo");

ep_led_driver.c — reads platform data

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio.h>
#include "ep_led_data.h"

static struct gpio_desc *ep_led_desc;

static int ep_led_probe(struct platform_device *pdev)
{
    struct ep_led_config *cfg = dev_get_platdata(&pdev->dev);

    if (!cfg)
        return -EINVAL;

    dev_info(&pdev->dev, "platform data: gpio=%d label=%s\n",
             cfg->gpio_num, cfg->label);

    ep_led_desc = gpio_to_desc(cfg->gpio_num);
    if (!ep_led_desc)
        return -ENODEV;

    return gpiod_direction_output(ep_led_desc, 0);
}

static void ep_led_remove(struct platform_device *pdev)
{
    dev_info(&pdev->dev, "ep_led_demo removed\n");
}

static struct platform_driver ep_led_driver = {
    .driver = {
        .name = "ep-led-demo",
    },
    .probe  = ep_led_probe,
    .remove = ep_led_remove,
};

module_platform_driver(ep_led_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala platform data driver demo");

Building And Running The Demo

Build both modules against your kernel headers, then load the driver first so it is registered and waiting, followed by the board module that creates the device.

$ make
$ sudo insmod ep_led_driver.ko
$ sudo insmod ep_led_board.ko
$ dmesg | tail -5

Expected output

ep-led-demo ep-led-demo.-1: platform data: gpio=21 label=ep-status-led

The device name string, "ep-led-demo", is what let the kernel’s platform bus match this device to this driver — we cover that matching mechanism in the next lecture. Unload in reverse order with rmmod ep_led_board ep_led_driver.

Common Mistakes

Mistake Why It Breaks Fix
Passing a stack-allocated struct to a hand-built platform_device Data becomes garbage once the function returns Use platform_device_register_data(), which copies it
Forgetting the NULL check on dev_get_platdata() Device may exist without data attached, causing a NULL dereference Always check before use
Loading the board module before the driver module No functional bug, but probe() runs later once the driver registers, which can confuse debugging Load driver first for predictable dmesg ordering

Best Practices

  • Prefer platform_device_register_data() over hand-filled struct platform_device for any new code
  • Keep the platform data structure definition in a header shared by both sides
  • Treat platform data as read-only inside the driver unless you explicitly designed it to be mutable
  • On modern boards, prefer device tree properties over platform data where the board actually boots from device tree — platform data is mainly for non-DT platforms and simple in-kernel test setups like this one

Security Considerations

Because platform_device_register_data() copies the buffer, there is no dangling-pointer risk from the registration side. The remaining responsibility is on the driver: never trust platform data to contain user-controlled values without validating ranges, since it is still just memory that a bug elsewhere in the kernel could corrupt.

Summary

Platform data is the mechanism for attaching driver-specific, kernel-opaque configuration to a platform device. The modern, safe way to attach it is platform_device_register_data(), and the modern way to read it back is dev_get_platdata(). Our ep_led_demo pair shows the full round trip: a board module registers a device with a GPIO number and label, and a driver module reads that data in probe() and configures the GPIO accordingly.

FAQ

What is the difference between platform data and a device tree property?

Platform data is a C structure passed in-kernel at registration time; a device tree property is text describing hardware, parsed by the driver using of_property functions. Device tree is the modern approach for real hardware; platform data is still used for non-DT platforms and in-kernel test devices like the one in this lecture.

Can platform data be modified by the driver?

Technically yes, since it’s just memory the driver can write to, but this is not recommended practice — treat it as configuration input, not shared mutable state.

Why not just keep using pdev->dev.platform_data directly?

You can, but dev_get_platdata() is the documented kernel API for this and keeps your code readable and consistent with the rest of the kernel tree.

Does platform_device_register_data() work with dynamic device IDs?

Yes — pass -1 for the id parameter and the kernel assigns one automatically, as shown in the demo.

Is the legacy manual platform_device declaration still supported in kernel 6.x?

Yes, it still compiles and works, but it is not recommended for new drivers because of the data-lifetime risk described in this lecture.

What happens if dev_get_platdata() returns NULL?

It means no platform data was attached to this device instance — your probe() should check for this and fail gracefully with -EINVAL rather than dereferencing a NULL pointer.

platform data linux device driver
free linux device drivers course
free linux kernel development course
free embedded systems course
free embedded linux course

Continue The Free Linux Device Drivers Course

Next: bus matching and how the kernel decides which driver binds to which platform device.

PREV_LEC  |  NEXT_LEC

 

 

Leave a Reply

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