Linux platform device 分析

1. Platform device 和dts

很多开发人员只知道配置好dts后,系统会自动匹配相应的compatible,然后调用驱动的probe,但是不明白其具体的创建过程。

以下是dts扫描platform device的调用流程。

 需要注意的是,此处只会creat&add其匹配的一级child。比如amba-bus下的i2c master是属于其一级child,会add。但是i2c master下的child,属于二级child,就不在这里creat&add了。I2C master下的child则是有其他驱动creat&add,以后分析。

device_add 时,设置其dev.bus为platform_bus_type,以标识这个设备是platform device。

device_add 之后,kernel device core 会匹配相关的driver,如果匹配成功,会调用 device_driver_attach,实现驱动probe,完成device的初始化(涉及到通用驱动框架,详细流程以后分析)。

2. paltform bus 驱动

以下是platform bus 驱动初始化调用流程。

以下为platform bus type的参数。

static const struct dev_pm_ops platform_dev_pm_ops = {
	.runtime_suspend = pm_generic_runtime_suspend,
	.runtime_resume = pm_generic_runtime_resume,
	USE_PLATFORM_PM_SLEEP_OPS
};

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_groups	= platform_dev_groups,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.dma_configure	= platform_dma_configure,
	.pm		= &platform_dev_pm_ops,
};
EXPORT_SYMBOL_GPL(platform_bus_type);

 需要特别关注的是.match,match会在device和driver_attach被调用,用于匹配driver和device。

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 */
    /*根据dts的compatible匹配driver的compatible*/
	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 */
    /*根据platform device的name匹配driver的platform_drv_data的id_table
    *如一个driver创建的另一个platform,也可考虑此处匹配:如pci glue添加devcie,或者mfd创建子device。
    */
	if (pdrv->id_table)
		return platform_match_id(pdrv->id_table, pdev) != NULL;

	/* fall-back to driver name match */
    /*根据platform device name和drv name匹配*/
	return (strcmp(pdev->name, drv->name) == 0);
}

3. platform driver

        以下是列举一个platform driver驱动的典型配置:

static const struct of_device_id axp20x_adc_of_match[] = {
	{ .compatible = "x-powers,axp209-adc", .data = (void *)&axp20x_data, },
	{ .compatible = "x-powers,axp221-adc", .data = (void *)&axp22x_data, },
	{ .compatible = "x-powers,axp813-adc", .data = (void *)&axp813_data, },
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, axp20x_adc_of_match);

static const struct platform_device_id axp20x_adc_id_match[] = {
	{ .name = "axp20x-adc", .driver_data = (kernel_ulong_t)&axp20x_data, },
	{ .name = "axp22x-adc", .driver_data = (kernel_ulong_t)&axp22x_data, },
	{ .name = "axp813-adc", .driver_data = (kernel_ulong_t)&axp813_data, },
	{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(platform, axp20x_adc_id_match);


static struct platform_driver axp20x_adc_driver = {
	.driver = {
		.name = "axp20x-adc",
		.of_match_table = of_match_ptr(axp20x_adc_of_match),
	},
	.id_table = axp20x_adc_id_match,
	.probe = axp20x_probe,
	.remove = axp20x_remove,
};

module_platform_driver(axp20x_adc_driver);

其中.of_match_table和.id_table可以任意配置一个。

以上流程便是通过dts来创建platform的流程。

4. platform device 初始参数。

        dts参数

/**
 * of_device_alloc - Allocate and initialize an of_device
 * @np: device node to assign to device
 * @bus_id: Name to assign to the device.  May be null to use default name.
 * @parent: Parent device.
 */
struct platform_device *of_device_alloc(struct device_node *np,
				  const char *bus_id,
				  struct device *parent)
{
	struct platform_device *dev;
	int rc, i, num_reg = 0, num_irq;
	struct resource *res, temp_res;

	dev = platform_device_alloc("", PLATFORM_DEVID_NONE);
	if (!dev)
		return NULL;

	/* count the io and irq resources */
    /*对应dts的reg = <addr len> 
    *addr和len的宽度,根据#address-cells 和 #size-cells而定;
    */
	while (of_address_to_resource(np, num_reg, &temp_res) == 0)
		num_reg++;
	num_irq = of_irq_count(np);

	/* Populate the resource table */
	if (num_irq || num_reg) {
		res = kcalloc(num_irq + num_reg, sizeof(*res), GFP_KERNEL);
		if (!res) {
			platform_device_put(dev);
			return NULL;
		}

		dev->num_resources = num_reg + num_irq;
		dev->resource = res;
		for (i = 0; i < num_reg; i++, res++) {
			rc = of_address_to_resource(np, i, res);
			WARN_ON(rc);
		}
		if (of_irq_to_resource_table(np, res, num_irq) != num_irq)
			pr_debug("not all legacy IRQ resources mapped for %pOFn\n",
				 np);
	}

    /*
    * 设置dts的node, 以及设置其父节点,如果没有父节点则挂在root platform bus上。
    */
	dev->dev.of_node = of_node_get(np);
	dev->dev.fwnode = &np->fwnode;
	dev->dev.parent = parent ? : &platform_bus;

	if (bus_id)
		dev_set_name(&dev->dev, "%s", bus_id);
	else
		of_device_make_bus_id(&dev->dev);

	return dev;
}
EXPORT_SYMBOL(of_device_alloc);

以上的dev->resource在driver probe时,可用以下接口组合,将device的resource map到内核空间,以供driver直接read/write.

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

base = devm_ioremap_resource(dev, res );

pdev->dev->platform_data初始化:

platform_data主要用于区别各device的差异或创建者想要传递的数据。

如果platform device是从kernel初始时的of_platform_default_populate来创建的,那么此时platform_data则为空。 如果是module_init什么的函数手动创建的,则通常会传入一些参数。如:

static int __init palmtx_pcmcia_init(void)
{
	int ret;

	if (!machine_is_palmtx())
		return -ENODEV;

	palmtx_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
	if (!palmtx_pcmcia_device)
		return -ENOMEM;

	ret = platform_device_add_data(palmtx_pcmcia_device, &palmtx_pcmcia_ops,
					sizeof(palmtx_pcmcia_ops));

	if (!ret)
		ret = platform_device_add(palmtx_pcmcia_device);

	if (ret)
		platform_device_put(palmtx_pcmcia_device);

	return ret;
}

static void __exit palmtx_pcmcia_exit(void)
{
	platform_device_unregister(palmtx_pcmcia_device);
}

module_init(palmtx_pcmcia_init);
module_exit(palmtx_pcmcia_exit);

或者使用platform_device_register_full 接口配置platform_data数据。

platform_data使用时,可调用接口:dev_get_platdata(&spi->dev)获取。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奔跑的蜗牛87

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值