1. What is platform_device
platform_device is the structure used in the Linux kernel to describe a Platform device.
Its role is:
- Tell the kernel "what resources this hardware has" (register addresses, interrupt numbers, DMA channels, etc.)
- Serve as the "information carrier" between device and driver
- Build a bridge between device tree and driver
2. platform_device Structure Details
platform_device structure is defined in TaishanPi-3-Linux/kernel-6.1/include/linux/platform_device.h:
struct platform_device {
const char *name; // Device name
int id; // Device ID (used to distinguish devices with same name)
bool id_auto; // Whether ID is auto-allocated
struct device dev; // Embedded generic device structure
u64 platform_dma_mask; // DMA address mask
struct device_dma_parameters dma_parms; // DMA parameters
u32 num_resources; // Number of resources
struct resource *resource; // Resource array pointer
const struct platform_device_id *id_entry; // ID matching table entry
const char *driver_override; // Forced matching driver name
struct mfd_cell *mfd_cell; // MFD device pointer
struct pdev_archdata archdata; // Architecture-specific data
};2
3
4
5
6
7
8
9
10
11
12
13
14
Please pay special attention to the core parameters:
name: Device nameid: Device IDdev: Generic device structure (containsof_nodepointing to device tree)num_resourcesandresource: Hardware resource array
1. name (Device Name)
The device name, usually extracted from the device tree node name.
mychardev@0 {
compatible = "lckfb,mychardev";
// ...
};2
3
4
The kernel will create a platform_device with name = "mychardev".
2. id (Device ID)
Used to distinguish devices with the same name, usually extracted from the device tree node address suffix.
mychardev@0 {
// First mychardev device
};
mychardev@1 {
// Second mychardev device
};2
3
4
5
6
7
The kernel will create two platform_devices:
- First:
name = "mychardev",id = 0 - Second:
name = "mychardev",id = 1
If there is no @ address suffix, id defaults to -1.
3. id_auto (ID Auto-allocation Flag)
When id_auto = true, the kernel automatically assigns a unique ID to the device.
Usage scenarios:
- When manually creating
platform_deviceand not wanting to manually specify ID - Letting the kernel automatically manage device IDs
When using device tree, we usually don't need to care about this field.
4. dev.of_node
dev.of_node points to the device tree node; the driver reads device tree properties through it.
// Read device tree property in driver
u32 dev_id;
of_property_read_u32(pdev->dev.of_node, "dev-id", &dev_id);2
3
5. Resource Array
The resource array is used to describe the hardware resources of the device (register addresses, interrupt numbers, DMA channels, etc.).
Each resource is described by struct resource:
struct resource {
resource_size_t start; /* Resource start address/number */
resource_size_t end; /* Resource end address/number (inclusive) */
const char *name; /* Resource name */
unsigned long flags; /* Resource type and attribute flags */
unsigned long desc; /* Resource descriptor */
struct resource *parent; /* Parent resource (larger range) */
struct resource *sibling; /* Sibling resource (next at same level) */
struct resource *child; /* Child resource (nested subrange) */
};2
3
4
5
6
7
8
9
10
Flags - Main Resource Types:
Flag Value Meaning IORESOURCE_IO 0x00000100 IO port resource (x86) IORESOURCE_MEM 0x00000200 Memory-mapped resource IORESOURCE_IRQ 0x00000400 Interrupt resource IORESOURCE_DMA 0x00000800 DMA channel IORESOURCE_BUS 0x00001000 Bus address space Flags - Resource Attributes:
Flag Meaning IORESOURCE_PREFETCH Prefetchable (memory resource) IORESOURCE_READONLY Read-only IORESOURCE_CACHEABLE Cacheable IORESOURCE_BUSY Resource already in use IORESOURCE_EXCLUSIVE Exclusive access
Multiple flags can be combined:
struct resource mem = {
.flags = IORESOURCE_MEM | IORESOURCE_PREFETCH | IORESOURCE_CACHEABLE,
};2
3
3. Device Tree Conversion
1. Device Tree Node
We create the following device tree node
/ {
mychardev@0 {
compatible = "lckfb,mychardev";
reg = <0x12340000 0x1000>; // Register address
interrupts = <56>; // Interrupt number
dev-id = <0xFFA8>; // Custom property
status = "okay";
};
};2
3
4
5
6
7
8
9
2. Automatically Created by Kernel
Kernel automatically creates
platform_devicestructure declaration.
The following is all automatically generated by the kernel
struct platform_device my_pdev = {
.name = "mychardev",
.id = 0,
.num_resources = 2,
.resource = {
// reg property → Register resource
{
.start = 0x12340000,
.end = 0x12340FFF,
.flags = IORESOURCE_MEM,
},
// interrupts property → Interrupt resource
{
.start = 56,
.end = 56,
.flags = IORESOURCE_IRQ,
},
},
.dev.of_node = &mychardev_node, // Points to device tree node
};2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| Device Tree Property | Converted Resource Type |
|---|---|
reg | IORESOURCE_MEM |
interrupts | IORESOURCE_IRQ |
dev-id and other custom properties | Not converted, need to use of_property_read_*() to read |
4. How Driver Gets Device Resources
1. Get Register Address
static int my_probe(struct platform_device *pdev)
{
struct resource *res;
// Get the 0th memory resource
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
printk(KERN_ERR "Failed to get register resource!\n");
return -ENODEV;
}
return 0;
}2
3
4
5
6
7
8
9
10
11
12
13
2. Get Interrupt Number
static int my_probe(struct platform_device *pdev)
{
int irq;
// Get the 0th interrupt resource
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
printk(KERN_ERR "Failed to get interrupt number!\n");
return irq;
}
return 0;
}2
3
4
5
6
7
8
9
10
11
12
13
3. Get Custom Properties
In the example below, I choose to get the value named
dev-id
static int my_probe(struct platform_device *pdev)
{
u32 dev_id;
// Read dev-id property
if (of_property_read_u32(pdev->dev.of_node, "dev-id", &dev_id)) {
printk(KERN_ERR "Failed to read dev-id!\n");
return -EINVAL;
}
return 0;
}2
3
4
5
6
7
8
9
10
11
12