设备树中的设备和平台驱动程序之间如何连接

11

我阅读了一些关于这个主题的文章,但是没有一个详细描述。

我所知道的是:

在设备树中声明 "compatible" 属性:

gpio0: gpio@44e07000 {
    compatible = "ti,omap4-gpio";
    ...
};

通过连接到平台驱动程序

static const struct of_device_id omap_gpio_match[] = {
    {
        .compatible = "ti,omap4-gpio",
    },
    { },
};
...
static struct platform_driver omap_gpio_driver = {
    .probe      = omap_gpio_probe,
    .driver     = {
        .name   = "omap_gpio",
        .pm = &gpio_pm_ops,
        .of_match_table = of_match_ptr(omap_gpio_match),
    },
};

结果,of_match_table会被用来匹配在设备树中声明的compatible属性。

platform_match函数(drivers/base/platform.c)执行匹配操作。我对此表示怀疑,因为我搜索了of_match_table的引用,唯一可能的结果位于of_device_get_match_data函数(drivers/of/device.c)中。

~/wk/linux$ find . -name '*.c'  | xargs grep  '\<of_match_table\>' | grep -v -E 'of_match_table\s+='
./drivers/dma/sirf-dma.c:               (of_match_device(op->dev.driver->of_match_table,
./drivers/macintosh/macio_asic.c:       const struct of_device_id * matches = drv->of_match_table;
./drivers/macintosh/macio_asic.c:       match = of_match_device(drv->driver.of_match_table, dev);
./drivers/nvmem/mxs-ocotp.c:    match = of_match_device(dev->driver->of_match_table, dev);
./drivers/reset/sti/reset-syscfg.c:     match = of_match_device(dev->driver->of_match_table, dev);
./drivers/mtd/devices/m25p80.c:  * matching for .of_match_table
./drivers/soc/rockchip/pm_domains.c:    match = of_match_device(dev->driver->of_match_table, dev);
./drivers/phy/phy-rockchip-usb.c:       match = of_match_device(dev->driver->of_match_table, dev);
./drivers/acpi/bus.c: * @of_match_table: List of device IDs to match against.
./drivers/acpi/bus.c:                            const struct of_device_id *of_match_table)
./drivers/acpi/bus.c:   if (!of_match_table || !of_compatible)
./drivers/acpi/bus.c:           for (id = of_match_table; id->compatible[0]; id++)
./drivers/acpi/bus.c:                                       drv->of_match_table);
./drivers/acpi/bus.c:                                drv->acpi_match_table, drv->of_match_table);
./drivers/pci/host/pcie-hisi.c: match = of_match_device(driver->of_match_table, &pdev->dev);
./drivers/pinctrl/stm32/pinctrl-stm32.c:        match = of_match_device(dev->driver->of_match_table, dev);
./drivers/of/device.c:  match = of_match_device(dev->driver->of_match_table, dev);
./drivers/mfd/axp20x.c:         of_id = of_match_device(dev->driver->of_match_table, dev);
./drivers/gpu/drm/armada/armada_crtc.c:         match = of_match_device(dev->driver->of_match_table, dev);
./arch/powerpc/kernel/ibmebus.c:        ibmebus_create_devices(drv->driver.of_match_table);
./arch/powerpc/kernel/ibmebus.c:        const struct of_device_id *matches = drv->of_match_table;
./sound/soc/qcom/lpass-cpu.c:   match = of_match_device(dev->driver->of_match_table, dev);

但是这个函数在一些常用的模块中没有被使用。

~/wk/linux$ find . -name '*.c'  | xargs grep of_device_get_match_data
./drivers/dma/sh/shdmac.c:              pdata = of_device_get_match_data(&pdev->dev);
./drivers/dma/tegra210-adma.c:  cdata = of_device_get_match_data(&pdev->dev);
./drivers/dma/tegra20-apb-dma.c:        cdata = of_device_get_match_data(&pdev->dev);
./drivers/usb/host/xhci-tegra.c:        tegra->soc = of_device_get_match_data(&pdev->dev);
./drivers/usb/phy/phy-msm-usb.c:        pdata->phy_type = (enum msm_usb_phy_type)of_device_get_match_data(&pdev->dev);
./drivers/mtd/spi-nor/fsl-quadspi.c:    q->devtype_data = of_device_get_match_data(dev);
./drivers/mtd/nand/qcom_nandc.c:        dev_data = of_device_get_match_data(dev);
./drivers/mtd/nand/atmel_nand.c:                of_device_get_match_data(host->dev);
./drivers/rtc/rtc-sunxi.c:      chip->data_year = of_device_get_match_data(&pdev->dev);
./drivers/spi/spi-mpc512x-psc.c:        mps->type = (int)of_device_get_match_data(dev);
./drivers/watchdog/mpc8xxx_wdt.c:       wdt_type = of_device_get_match_data(&ofdev->dev);
./drivers/phy/phy-exynos-mipi-video.c:  phy_dev = of_device_get_match_data(dev);
./drivers/phy/phy-sun4i-usb.c:  data->cfg = of_device_get_match_data(dev);
./drivers/pci/host/pci-imx6.c:          (enum imx6_pcie_variants)of_device_get_match_data(&pdev->dev);
./drivers/pci/host/pcie-qcom.c: pcie->ops = (struct qcom_pcie_ops *)of_device_get_match_data(dev);
./drivers/i2c/busses/i2c-rcar.c:        priv->devtype = (enum rcar_i2c_type)of_device_get_match_data(dev);
./drivers/i2c/busses/i2c-tegra.c:               i2c_dev->hw = of_device_get_match_data(&pdev->dev);
./drivers/tty/serial/imx.c:     sport->devdata = of_device_get_match_data(&pdev->dev);
./drivers/gpio/gpio-mpc8xxx.c:          of_device_get_match_data(&pdev->dev);
./drivers/gpio/gpio-tegra.c:    config = of_device_get_match_data(&pdev->dev);
./drivers/clk/clk-palmas.c:     match_data = of_device_get_match_data(&pdev->dev);
./drivers/input/misc/pmic8xxx-pwrkey.c: pwrkey->shutdown_fn = of_device_get_match_data(&pdev->dev);
./drivers/input/touchscreen/edt-ft5x06.c:       chip_data = of_device_get_match_data(&client->dev);
./drivers/pinctrl/sh-pfc/core.c:                info = of_device_get_match_data(&pdev->dev);
./drivers/thermal/rcar_thermal.c:       unsigned long of_data = (unsigned long)of_device_get_match_data(dev);
./drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c:      data = of_device_get_match_data(&pdev->dev);
./drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c:       data = of_device_get_match_data(&pdev->dev);
./drivers/net/ethernet/renesas/sh_eth.c:                mdp->cd = (struct sh_eth_cpu_data *)of_device_get_match_data(&pdev->dev);
./drivers/net/ethernet/renesas/ravb_main.c:     chip_id = (enum ravb_chip_id)of_device_get_match_data(&pdev->dev);
./drivers/of/device.c:const void *of_device_get_match_data(const struct device *dev)
./drivers/of/device.c:EXPORT_SYMBOL(of_device_get_match_data);
./drivers/gpu/drm/nouveau/nouveau_platform.c:   func = of_device_get_match_data(&pdev->dev);
./drivers/gpu/drm/exynos/exynos_drm_rotator.c:                          of_device_get_match_data(dev);
./drivers/gpu/drm/exynos/exynos_mixer.c:        drv = of_device_get_match_data(dev);
./drivers/gpu/drm/exynos/exynos5433_drm_decon.c:        ctx->out_type = (unsigned long)of_device_get_match_data(dev);
./drivers/gpu/drm/exynos/exynos_drm_fimd.c:     ctx->driver_data = of_device_get_match_data(dev);
./drivers/gpu/drm/exynos/exynos_hdmi.c: hdata->drv_data = of_device_get_match_data(dev);
./drivers/gpu/drm/exynos/exynos_drm_dsi.c:      dsi->driver_data = of_device_get_match_data(dev);
./drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c:      dc_ops = (struct kirin_dc_ops *)of_device_get_match_data(dev);
./drivers/gpu/drm/msm/hdmi/hdmi_phy.c:  phy->cfg = (struct hdmi_phy_cfg *)of_device_get_match_data(dev);
./drivers/gpu/drm/msm/hdmi/hdmi.c:                      of_device_get_match_data(dev);
./drivers/gpu/drm/msm/msm_drv.c:        return (int) (unsigned long) of_device_get_match_data(dev);
./drivers/gpu/drm/rockchip/rockchip_drm_vop.c:  vop_data = of_device_get_match_data(dev);
./sound/soc/sh/rcar/core.c:     priv->flags     = (unsigned long)of_device_get_match_data(dev);
./sound/soc/sh/rcar/rsrc-card.c:                of_data = of_device_get_match_data(dev);
./sound/soc/sh/rcar/rsrc-card.c:        const struct rsrc_card_of_data *of_data = of_device_get_match_data(dev);

有人能提供一些有用的信息吗?


3
所有的ID表(在您的情况下是OF表)都被打包在内核二进制文件的特殊部分中(因此在加载和解压缩时也在内存中)。在初始化期间,OF核心会接收DT blob并将DT中每个兼容字符串与已注册的驱动程序列表进行匹配(在内核编译期间进行注册)。除此之外,depmod数据库和设备助手(例如udev)还可以帮助解决依赖关系。 - 0andriy
1
未来的提示:使用 git grep 而不是你所使用的 shell。git grep -n -w of_device_get_match_data - 0andriy
1
谢谢Andy,这个git grep非常有用。 - jianing
3个回答

8

2
在驱动程序的探测函数中必须包含以下代码行:
probe(struct bus_client *client,
           const struct bus_device_id *id)
{

    const struct of_device_id *match;

    match = of_match_device(omap_gpio_match);
    if (!match)
            return -ENODEV;
    else
       //write the driver stuff for Probe

这就是设备树与你的驱动程序相关联的方式。 omap_gpio_match已经与你在设备树中定义的“兼容性标识符”链接在一起,这就是驱动程序与设备树相关联的方式。

3
不,这是错误的,因为核心已经处理了它。请参见我上面的评论。 - 0andriy

1
当您注册平台驱动程序时,需要使用platform_bus_type回调函数初始化driver.bus
int __platform_driver_register(struct platform_driver *drv, struct module *owner)
{
    drv->driver.owner = owner;
    drv->driver.bus = &platform_bus_type;

其中一个回调函数是platform_match:
struct bus_type platform_bus_type = {
    .name       = "platform",
    .dev_groups = platform_dev_groups,
    .match      = platform_match,

如果有设备树,此回调函数用于将驱动程序绑定到设备:

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);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接