platform总线匹配方式

重要结构体

struct platform_device

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct platform_device {
const char *name;
int id;
bool id_auto;
struct device dev;
u32 num_resources;
struct resource *resource;

const struct platform_device_id *id_entry;
char *driver_override; /* Driver name to force a match 第一种匹配方式:匹配优先级最高*/

/* MFD cell pointer */
struct mfd_cell *mfd_cell;

/* arch specific additions */
struct pdev_archdata archdata;
};

struct platform_driver

1
2
3
4
5
6
7
8
9
10
11
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;//第二种匹配方式:.of_match_table成员用于compatible属性匹配,类型为const struct of_device_id *of_match_table;推荐方式
//第三种匹配方式.acpi_match_table成员用于acpi方式匹配
const struct platform_device_id *id_table;//第四种匹配方式:用于ID表匹配,传统方式
bool prevent_deferred_probe;
};

先讨论platform_match这个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
* platform_match - bind platform device to platform driver.
* @dev: device.
* @drv: driver.
*
* Platform device IDs are assumed to be encoded like this:
* "<name><instance>", where <name> is a short description of the type of
* device, like "pci" or "floppy", and <instance> is the enumerated
* instance of the device, like '0' or '42'. Driver IDs are simply
* "<name>". So, extract the <name> from the platform_device structure,
* and compare it against the name of the driver. Return whether they match
* or not.
*/
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);

/* When driver_override is set, only bind to the matching driver */
if (pdev->driver_override)
return !strcmp(pdev->driver_override, drv->name);

/* Attempt an OF style match first */
if (of_driver_match_device(dev, drv))
return 1;

/* Then try ACPI style match */
if (acpi_driver_match_device(dev, drv))
return 1;

/* Then try to match against the id table */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;

/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);
}

函数按照以下顺序进行匹配,一旦某个匹配成功就返回:

  1. driver_override(强制绑定)
  2. 设备树匹配
  3. ACPI匹配
  4. ID表匹配
  5. 名称匹配(兜底方案)

详细分析

数据类型转化:

1
2
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);

将通用的devicedevice_driver结构转换为platform特定的结构体,方便访问platform特有的字段。

to_platform_device用于通过struct device类型的dev指针,得到包含它的struct platform_device类型的指针。

to_platform_driver用于通过struct device_driver类型的drv指针,得到包含它的struct platform_driver类型的指针。

这两个宏定义由contain_of宏定义实现

1
2
#define to_platform_device(x) container_of((x), struct platform_device, dev)
#define to_platform_driver(drv) (container_of((drv), struct platform_driver, driver))

driver_override匹配

1
2
if (pdev->driver_override)
return !strcmp(pdev->driver_override, drv->name);
  • 检查设备是否指定了强制绑定的驱动名称
  • 如果指定,则只与该名称完全匹配的驱动绑定
  • 这通常用于调试或强制特定设备使用特定驱动

设备树匹配

1
2
if (of_driver_match_device(dev,drv))
return 1;
  • 调用设备树匹配函数
  • 通过设备节点的”compatible”属性与驱动的of_match_table进行匹配
  • 这是现代嵌入式系统中最常用的匹配方式

ACPI匹配

1
2
if (acpi_driver_match_device(dev, drv))
return 1;
  • 在ACPI系统中,通过ACPI_hid和驱动进行匹配
  • 主要用于x86系统

ID表匹配

1
2
if (pdrv->id_table)
return platform_match_id(pdrv->id_table,pdev) != NULL;
  • 使用驱动中定义的id_table与设备名称进行匹配
  • 遍历驱动的ID表,查找与设备名称匹配的项

名称匹配

1
return (strcmp(pdev->name,drv->name) == 0)
  • 简单地比较设备名称和驱动名称是否相同
  • 这是最基本的匹配方式

区分设备树匹配和ID表匹配

1. of_driver_match_device

  • 用于**设备树(Device Tree)**匹配机制
  • 通过设备树中的compatible属性进行匹配

工作原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 在设备树中
my_device: my-device@10000000 {
compatible = "vendor,device-name", "generic-name";
reg = <0x10000000 0x1000>;
};

// 在驱动中
static const struct of_device_id my_of_match[] = {
{ .compatible = "vendor,device-name", .data = &my_data },
{ .compatible = "generic-name", .data = &generic_data },
{ }
};

static struct platform_driver my_driver = {
.driver = {
.name = "my-driver",
.of_match_table = my_of_match,
},
.probe = my_probe,
};

匹配过程

  1. 遍历驱动的of_match_table数组
  2. 将设备树节点的compatible属性与表中条目逐一比较
  3. 找到第一个匹配项即成功

2. platform_match_id

  • 用于传统platform设备ID表匹配机制
  • 通过设备和驱动的名称进行匹配

工作原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
c// 在驱动中定义ID表
static const struct platform_device_id my_id_table[] = {
{ "my-device-name", .driver_data = MY_DATA_1 },
{ "my-old-device", .driver_data = MY_DATA_2 },
{ }
};

static struct platform_driver my_driver = {
.driver = {
.name = "my-driver",
},
.id_table = my_id_table, // 使用ID表匹配
.probe = my_probe,
};

// 在板级文件或设备注册时
static struct platform_device my_device = {
.name = "my-device-name", // 与ID表中的名称匹配
.id = -1,
// ...
};

匹配过程

  1. 遍历驱动的id_table数组
  2. 将设备的name字段与表中条目的name字段比较
  3. 找到匹配项即成功,并将匹配的表项保存到设备的id_entry字段

设备树匹配和ID表匹配不同点

维度 of_driver_match_device(设备树匹配) platform_match_id(ID 表匹配)
匹配依据 基于设备树(Device Tree)中的 compatible 属性。 基于驱动中定义的 id_tablestruct platform_device_id 数组)。
适用场景 主要用于使用设备树的系统(现代嵌入式系统主流,如 ARM、RISC-V 平台)。 主要用于不使用设备树的系统(如部分 x86 平台、老的嵌入式系统),或作为设备树匹配的补充。
实现逻辑 1. 设备在设备树中通过 compatible 属性声明自己的「兼容标识」(如 vendor,chip-name); 2. 驱动通过 of_match_table 成员声明自己支持的 compatible 列表; 3. 函数比较设备的 compatible 与驱动的 of_match_table,若有一致则匹配成功。 1. 驱动通过 id_table 定义支持的设备名称列表(name 字段)和设备 ID(id 字段); 2. 设备通过 nameid 成员标识自己; 3. 函数遍历 id_table,若设备的 nameid 与表中某条目一致,则匹配成功。
灵活性 更高。compatible 属性可包含多个值(如 vendor,chip-v2vendor,chip-v1),支持「向下兼容」(驱动可匹配多个版本的设备)。 较低。依赖精确的名称和 ID 匹配,通常只能匹配固定名称或 ID 的设备。
在匹配链中的优先级 较高(在 platform_match 中先于 platform_match_id 执行)。 较低(在设备树、ACPI 匹配失败后才执行)。

3. 实例

文件地址:drivers\gpio\gpio-mxc.c

1
2
3
4
5
6
7
static const struct of_device_id mxc_gpio_dt_ids[] = {
{ .compatible = "fsl,imx1-gpio", .data = &mxc_gpio_devtype[IMX1_GPIO], },
{ .compatible = "fsl,imx21-gpio", .data = &mxc_gpio_devtype[IMX21_GPIO], },
{ .compatible = "fsl,imx31-gpio", .data = &mxc_gpio_devtype[IMX31_GPIO], },
{ .compatible = "fsl,imx35-gpio", .data = &mxc_gpio_devtype[IMX35_GPIO], },
{ /* sentinel */ }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static const struct platform_device_id mxc_gpio_devtype[] = {
{
.name = "imx1-gpio",
.driver_data = IMX1_GPIO,
}, {
.name = "imx21-gpio",
.driver_data = IMX21_GPIO,
}, {
.name = "imx31-gpio",
.driver_data = IMX31_GPIO,
}, {
.name = "imx35-gpio",
.driver_data = IMX35_GPIO,
}, {
/* sentinel */
}
};
1
2
3
4
5
6
7
8
9
static struct platform_driver mxc_gpio_driver = {
.driver = {
.name = "gpio-mxc",
.pm = &mxc_gpio_dev_pm_ops,
.of_match_table = mxc_gpio_dt_ids,//支持设备树匹配模式
},
.probe = mxc_gpio_probe,
.id_table = mxc_gpio_devtype,//支持ID表匹配模式
};