一、什么是platform_device
platform_device 是 Linux 内核中用来描述一个 Platform 设备的结构体。
它的作用是:
- 告诉内核"这个硬件有什么资源"(寄存器地址、中断号、DMA 通道等)
- 作为设备和驱动之间的"信息载体"
- 在设备树和驱动之间搭建桥梁
二、platform_device结构体详解
platform_device 结构体定义在 TaishanPi-3-Linux/kernel-6.1/include/linux/platform_device.h 中:
c
struct platform_device {
const char *name; // 设备名称
int id; // 设备ID(用于区分同名设备)
bool id_auto; // ID是否自动分配
struct device dev; // 内嵌的通用设备结构体
u64 platform_dma_mask; // DMA地址掩码
struct device_dma_parameters dma_parms; // DMA参数
u32 num_resources; // 资源数量
struct resource *resource; // 资源数组指针
const struct platform_device_id *id_entry; // ID匹配表条目
const char *driver_override; // 强制匹配的驱动名
struct mfd_cell *mfd_cell; // MFD设备指针
struct pdev_archdata archdata; // 架构相关数据
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
特别注意一下核心参数:
name:设备名称id:设备IDdev:通用设备结构体(包含of_node指向设备树)num_resources和resource:硬件资源数组
1、name(设备名称)
设备的名称,通常从设备树节点名提取。
c
mychardev@0 {
compatible = "lckfb,mychardev";
// ...
};1
2
3
4
2
3
4
内核会创建一个 platform_device,其 name = "mychardev"。
2、id(设备ID)
用于区分同名设备,通常从设备树节点地址后缀提取。
c
mychardev@0 {
// 第一个 mychardev 设备
};
mychardev@1 {
// 第二个 mychardev 设备
};1
2
3
4
5
6
7
2
3
4
5
6
7
内核会创建两个 platform_device:
- 第一个:
name = "mychardev",id = 0 - 第二个:
name = "mychardev",id = 1
如果没有 @ 地址后缀,id 默认为 -1。
3、id_auto(ID自动分配标志)
当 id_auto = true 时,内核会自动为设备分配一个唯一的 ID。
使用场景:
- 手动创建
platform_device时,不想手动指定 ID - 让内核自动管理设备ID
我们使用设备树时,通常不需要关心这个字段。
4、dev.of_node
dev.of_node 指向设备树节点,驱动通过它读取设备树属性。
c
// 驱动中读取设备树属性
u32 dev_id;
of_property_read_u32(pdev->dev.of_node, "dev-id", &dev_id);1
2
3
2
3
5、资源数组
resource 数组用于描述设备的硬件资源(寄存器地址、中断号、DMA 通道等)。
每个资源由 struct resource 结构体描述:
c
struct resource {
resource_size_t start; /* 资源起始地址/编号 */
resource_size_t end; /* 资源结束地址/编号(包含) */
const char *name; /* 资源名称 */
unsigned long flags; /* 资源类型和属性标志 */
unsigned long desc; /* 资源描述符 */
struct resource *parent; /* 父资源(所属的更大范围) */
struct resource *sibling; /* 兄弟资源(同级的下一个) */
struct resource *child; /* 子资源(嵌套的子范围) */
};1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
Flags - 主要资源类型:
标志 值 含义 IORESOURCE_IO 0x00000100 IO 端口资源(x86) IORESOURCE_MEM 0x00000200 内存映射资源 IORESOURCE_IRQ 0x00000400 中断资源 IORESOURCE_DMA 0x00000800 DMA 通道 IORESOURCE_BUS 0x00001000 总线地址空间 Flags - 资源属性:
标志 含义 IORESOURCE_PREFETCH 可预取(内存资源) IORESOURCE_READONLY 只读 IORESOURCE_CACHEABLE 可缓存 IORESOURCE_BUSY 资源已被占用 IORESOURCE_EXCLUSIVE 独占访问
可以有多个标志组合:
c
struct resource mem = {
.flags = IORESOURCE_MEM | IORESOURCE_PREFETCH | IORESOURCE_CACHEABLE,
};1
2
3
2
3
三、设备树转换
1、设备树节点
我们创建以下的设备树节点
c
/ {
mychardev@0 {
compatible = "lckfb,mychardev";
reg = <0x12340000 0x1000>; // 寄存器地址
interrupts = <56>; // 中断号
dev-id = <0xFFA8>; // 自定义属性
status = "okay";
};
};1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
2、内核自动创建
内核自动创建
platform_device结构体声明。
以下都是内核自动生成的
c
struct platform_device my_pdev = {
.name = "mychardev",
.id = 0,
.num_resources = 2,
.resource = {
// reg 属性 → 寄存器资源
{
.start = 0x12340000,
.end = 0x12340FFF,
.flags = IORESOURCE_MEM,
},
// interrupts 属性 → 中断资源
{
.start = 56,
.end = 56,
.flags = IORESOURCE_IRQ,
},
},
.dev. of_node = &mychardev_node, // 指向设备树节点
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| 设备树属性 | 转换成的资源类型 |
|---|---|
reg | IORESOURCE_MEM |
interrupts | IORESOURCE_IRQ |
dev-id 等自定义属性 | 不转换,需用 of_property_read_*() 读取 |
四、驱动如何获取设备资源
1、获取寄存器地址
c
static int my_probe(struct platform_device *pdev)
{
struct resource *res;
// 获取第0个内存资源
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
printk(KERN_ERR "获取寄存器资源失败!\n");
return -ENODEV;
}
return 0;
}1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
2、获取中断号
c
static int my_probe(struct platform_device *pdev)
{
int irq;
// 获取第0个中断资源
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
printk(KERN_ERR "获取中断号失败!\n");
return irq;
}
return 0;
}1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
3、获取自定义属性
我在下面的例子中选择获取名为
dev-id的值
c
static int my_probe(struct platform_device *pdev)
{
u32 dev_id;
// 读取 dev-id 属性
if (of_property_read_u32(pdev->dev.of_node, "dev-id", &dev_id)) {
printk(KERN_ERR "读取 dev-id 失败!\n");
return -EINVAL;
}
return 0;
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12