linux设备注册

本文详细介绍了Linux内核4.9版本中设备注册的过程,以arm64平台为例。首先,根设备及其子设备的注册通过of_platform_default_populate()函数递归创建,检查compatible属性并调用of_device_add()进行注册。接着,分析了i2c设备的注册,从平台设备到i2c适配器的注册,包括i2c控制器设备及其子设备的注册。通过对dts中设备节点的遍历和匹配,完成设备的初始化和注册工作。

作为一个linux驱动开发者,大家肯定会写过形形色色的驱动。一般的流程是首先在dts配置设备的相关属性,然后编写相应的driver,基于linux内核device-bus-driver架构,device和driver会在某个时候match上,然后执行driver的probe函数,完成设备的相关初始化工作。如果打开一个项目的dts,会发现里面很多设备节点,那么这些设备是以什么样的原则给注册到系统中呢?

 下文以4.9内核版本,平台为arm64 为例来讲解具体的设备注册过程。

1. 根设备及其子设备的注册

路径:drivers/of/platform.c
static int __init of_platform_default_populate_init(void)
{
        struct device_node *node;

        if (!of_have_populated_dt())
                return -ENODEV;

        /*
         * Handle ramoops explicitly, since it is inside /reserved-memory,
         * which lacks a "compatible" property.
         */
        node = of_find_node_by_path("/reserved-memory");
        if (node) {
                node = of_find_compatible_node(node, NULL, "ramoops");
                if (node)
                        of_platform_device_create(node, NULL, NULL);
        }

        /* Populate everything else. */
        of_platform_default_populate(NULL, NULL, NULL);

        return 0;
}
arch_initcall_sync(of_platform_default_populate_init);

首先单独处理了reserved-memory节点下属性为ramoops的节点,该节点主要用于保存kernel oops信息, 然后接下来调用 of_platform_default_populate()函数来处理剩余的节点。

路径:drivers/of/platform.c
int of_platform_default_populate(struct device_node *root,
                                 const struct of_dev_auxdata *lookup,
                                 struct device *parent)
{
        return of_platform_populate(root, of_default_bus_match_table, lookup,
                                    parent);
}
EXPORT_SYMBOL_GPL(of_platform_default_populate);

int of_platform_populate(struct device_node *root,
                        const struct of_device_id *matches,
                        const struct of_dev_auxdata *lookup,
                        struct device *parent)
{
        struct device_node *child;
        int rc = 0;

        root = root ? of_node_get(root) : of_find_node_by_path("/");
        if (!root)
                return -EINVAL;

        pr_debug("%s()\n", __func__);
        pr_debug(" starting at: %s\n", root->full_name);

        for_each_child_of_node(root, child) {
                rc = of_platform_bus_create(child, matches, lookup, parent, true);
                if (rc) {
                        of_node_put(child);
                        break;
                }
        }
        of_node_set_flag(root, OF_POPULATED_BUS);

        of_node_put(root);
        return rc;
}
EXPORT_SYMBOL_GPL(of_platform_populate);

 首先获取root节点的device_node, 在根节点初始化的过程中,root节点即为dts中的"/", 然后一次遍历"/"的子节点,并依次调用of_platform_bus_create函数递归的创建匹配上match的设备及其子设备。

路径:drivers/of/platform.c
static int of_platform_bus_create(struct device_node *bus,
                                  const struct of_device_id *matches,
                                  const struct of_dev_auxdata *lookup,
                                  struct device *parent, bool strict)
{
        const struct of_dev_auxdata *auxdata;
        struct device_node *child;
        struct platform_device *dev;
        const char *bus_id = NULL;
        void *platform_data = NULL;
        int rc = 0;

        /* Make sure it has a compatible property */
        if (strict && (!of_get_property(bus, "compatible", NULL))) {
                pr_debug("%s() - skipping %s, no compatible prop\n",
                         __func__, bus->full_name);
                return 0;
        }

        if (of_node_check_flag(bus, OF_POPULATED_BUS)) {
                pr_debug("%s() - skipping %s, already populated\n",
                        __func__, bus->full_name);
                return 0;
        }

        auxdata = of_dev_lookup(lookup, bus);
        if (auxdata) {
                bus_id = auxdata->name;
                platform_data = auxdata->platform_data;
        }

        if (of_device_is_compatible(bus, "arm,primecell")) {
                /*
                 * Don't return an error here to keep compatibility with older
                 * device tree files.
                 */
                of_amba_device_create(bus, bus_id, platform_data, parent);
                return 0;
        }

        dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
        if (!dev || !of_match_node(matches, bus))
                return 0;
 
        for_each_child_of_node(bus, child) {
                pr_debug("   create child: %s\n", child->full_name);
                rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
                if (rc) {
                        of_node_put(child);
                        break;
                }
        }
        of_node_set_flag(bus, OF_POPULATED_BUS);
        return rc;
}

该函数首先检查当前处理的节点是否有 compatible属性,如果没有直接返回;接着of_dev_lookup函数获取该节点的某些信息,最后调用of_platform_device_create_pdata函数注册相应的设备。

路径:drivers/of/platform.c 
static struct platform_device *of_platform_device_create_pdata(
                                        struct device_node *np,
                                        const char *bus_id,
                                        void *platform_data,
                                        struct device *parent)
{
        struct platform_device *dev;

        if (!of_device_is_available(np) ||
            of_node_test_and_set_flag(np, OF_POPULATED))
                return NULL;

        dev = of_device_alloc(np, bus_id, parent);
        if (!dev)
                goto err_clear_flag;

        dev->dev.bus = &platform_bus_type;
        dev->dev.platform_data = platform_data;
        of_dma_configure(&dev->dev, dev->dev.of_node);
        of_msi_configure(&dev->dev, dev->dev.of_node);
        of_reserved_mem_device_init_by_idx(&dev->dev, dev->dev.of_node, 0);

        if (of_device_add(dev) != 0) {
                of_dma_deconfigure(&dev->dev);
                platform_device_put(dev);
                goto err_clear_flag;
        }

        return dev;

err_clear_flag:
        of_node_clear_flag(np, OF_POPULATED);
        return NULL;
}

该函数首先调用of_device_alloc()函数分配内存,并初始化相应的信息;紧接着设置device的bus为platform_bus_type及相应的platform_data等等,然后调用of_device_add()函数(最终调用device_add())将设备注册到相应总线上。

该函数 of_platform_device_create_pdata()执行完成后,返回到of_platform_bus_create()函数中,在of_platform_bus_create()函数中,接着判断已注册设备的compatible属性是否和match中的compatible想匹配,如果匹配上,则继续注册该节点的子节点,如果没有匹配上,则退出该函数。如此递归调用下去,就会注册节点compatible属性为of_default_bus_match_table中的设备并注册其子设备,这样就完成了根设备及其子设备的注册工作。

const struct of_device_id of_default_bus_match_table[] = {
        { .compatible = "simple-bus", },
        { .compatible = "simple-mfd", },
        { .compatible = "isa", },
#ifdef CONFIG_ARM_AMBA
        { .compatible = "arm,amba-bus", },
#endif /* CONFIG_ARM_AMBA */
        {} /* Empty terminated list */
};

2. 其他设备的注册

在第一部分讲解了根设备及其子设备的注册过程,接下来讲解挂接在具体总线上的设备的注册过程,比如i2c设备,spi设备等等,下来以i2c设备为例,讲解具体注册过程。

i2c设备作为i2c 控制器的子设备,而挂在i2c总线上。如下例:

路径:arch/arm64/boot/dts/mediatek/mt8173.dtsi
soc {
         #address-cells = <2>;
         #size-cells = <2>;
         compatible = "simple-bus";
         ranges;
.............
此处省略
.............
         i2c1: i2c@11008000 {
                compatible = "mediatek,mt8173-i2c";
                reg = <0 0x11008000 0 0x70>,
                      <0 0x11000180 0 0x80>;
                interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_LOW>;
                clock-div = <16>;
                clocks = <&pericfg CLK_PERI_I2C1>,
                         <&pericfg CLK_PERI_AP_DMA>;
                clock-names = "main", "dma";
                pinctrl-names = "default";
                pinctrl-0 = <&i2c1_pins_a>;
                #address-cells = <1>;
                #size-cells = <0>;
                status = "disabled";
        };
}


路径:arch/arm64/boot/dts/mediatek/mt8173-evb.dts
&i2c1 {
        status = "okay";

        buck: da9211@68 {
                compatible = "dlg,da9211";
                reg = <0x68>;

                regulators {
                        da9211_vcpu_reg: BUCKA {
                                regulator-name = "VBUCKA";
                                regulator-min-microvolt = < 700000>;
                                regulator-max-microvolt = <1310000>;
                                regulator-min-microamp  = <2000000>;
                                regulator-max-microamp  = <4400000>;
                                regulator-ramp-delay = <10000>;
                                regulator-always-on;
                        };

                        da9211_vgpu_reg: BUCKB {
                                regulator-name = "VBUCKB";
                                regulator-min-microvolt = < 700000>;
                                regulator-max-microvolt = <1310000>;
                                regulator-min-microamp  = <2000000>;
                                regulator-max-microamp  = <3000000>;
                                regulator-ramp-delay = <10000>;
                        };
                };
        };
};


有dts的拓扑结构可知,i2c控制器设备节点i2c@11008000是作为soc节点的子节点,而soc节点的compatible属性值是"simple-bus", 依据第一部分的分析,i2c@11008000节点作为platform device已经注册到系统中,当该设备的driver注册时,device和driver match上,就开始调用driver的probe函数,具体如下:

路径:drivers/i2c/busses/i2c-mt65xx.c

static int mtk_i2c_probe(struct platform_device *pdev)
{
        const struct of_device_id *of_id;
        int ret = 0;
        struct mtk_i2c *i2c;
        struct clk *clk;
        unsigned int clk_src_div;
        struct resource *res;
        int irq;

        i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
        if (!i2c)
                return -ENOMEM;

        ret = mtk_i2c_parse_dt(pdev->dev.of_node, i2c, &clk_src_div);
        if (ret)
                return -EINVAL;

        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        i2c->base = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(i2c->base))
                return PTR_ERR(i2c->base);

        res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
        i2c->pdmabase = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(i2c->pdmabase))
                return PTR_ERR(i2c->pdmabase);

        irq = platform_get_irq(pdev, 0);
        if (irq <= 0)
                return irq;

        init_completion(&i2c->msg_complete);

        of_id = of_match_node(mtk_i2c_of_match, pdev->dev.of_node);
        if (!of_id)
                return -EINVAL;

         i2c->dev_comp = of_id->data;
         i2c->adap.dev.of_node = pdev->dev.of_node;
         i2c->dev = &pdev->dev;
         i2c->adap.dev.parent = &pdev->dev;
         i2c->adap.owner = THIS_MODULE;
         i2c->adap.algo = &mtk_i2c_algorithm;
         i2c->adap.quirks = i2c->dev_comp->quirks;
         i2c->adap.timeout = 2 * HZ;
         i2c->adap.retries = 1;
 
         if (i2c->have_pmic && !i2c->dev_comp->pmic_i2c)
                 return -EINVAL;
 
         i2c->clk_main = devm_clk_get(&pdev->dev, "main");
         if (IS_ERR(i2c->clk_main)) {
                 dev_err(&pdev->dev, "cannot get main clock\n");
                 return PTR_ERR(i2c->clk_main);
         }
 
         i2c->clk_dma = devm_clk_get(&pdev->dev, "dma");
         if (IS_ERR(i2c->clk_dma)) {
                 dev_err(&pdev->dev, "cannot get dma clock\n");
                 return PTR_ERR(i2c->clk_dma);
         }
 
         clk = i2c->clk_main;
         if (i2c->have_pmic) {
                 i2c->clk_pmic = devm_clk_get(&pdev->dev, "pmic");
                 if (IS_ERR(i2c->clk_pmic)) {
                         dev_err(&pdev->dev, "cannot get pmic clock\n");
                         return PTR_ERR(i2c->clk_pmic);
                 }
                 clk = i2c->clk_pmic;
         }
         strlcpy(i2c->adap.name, I2C_DRV_NAME, sizeof(i2c->adap.name));
 
         ret = mtk_i2c_set_speed(i2c, clk_get_rate(clk), clk_src_div);
         if (ret) {
                 dev_err(&pdev->dev, "Failed to set the speed.\n");
                 return -EINVAL;
         }
 
         if (i2c->dev_comp->support_33bits) {
                 ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(33));
                 if (ret) {
                         dev_err(&pdev->dev, "dma_set_mask return error.\n");
                         return ret;
                 }
         }
 
         ret = mtk_i2c_clock_enable(i2c);
         if (ret) {
                 dev_err(&pdev->dev, "clock enable failed!\n");
                 return ret;
         }
         mtk_i2c_init_hw(i2c);
         mtk_i2c_clock_disable(i2c);
 
         ret = devm_request_irq(&pdev->dev, irq, mtk_i2c_irq,
                                IRQF_TRIGGER_NONE, I2C_DRV_NAME, i2c);
         if (ret < 0) {
                 dev_err(&pdev->dev,
                         "Request I2C IRQ %d fail\n", irq);
                 return ret;
         }
 
         i2c_set_adapdata(&i2c->adap, i2c);
         ret = i2c_add_adapter(&i2c->adap);
         if (ret)
                 return ret;
 
         platform_set_drvdata(pdev, i2c);

         return 0;
}

在其probe函数中做了一系列的初始化工作后, 调用i2c_add_adapter()函数完成i2c适配器的注册工作,也会完成i2c控制器设备及其子设备的注册工作,具体一下代码:

路径:drivers/i2c/i2c-core.c
int i2c_add_adapter(struct i2c_adapter *adapter)
{
        struct device *dev = &adapter->dev;
        int id;

        if (dev->of_node) {
                id = of_alias_get_id(dev->of_node, "i2c");
                if (id >= 0) {
                        adapter->nr = id;
                        return __i2c_add_numbered_adapter(adapter);
                }
        }

        mutex_lock(&core_lock);
        id = idr_alloc(&i2c_adapter_idr, adapter,
                       __i2c_first_dynamic_bus_num, 0, GFP_KERNEL);
        mutex_unlock(&core_lock);
        if (WARN(id < 0, "couldn't get idr"))
                return id;

        adapter->nr = id;

        return i2c_register_adapter(adapter);
}
EXPORT_SYMBOL(i2c_add_adapter);

 如果在dts中的alias节点中有该i2c控制器的描述,则依据该描述确定其在i2c_adapter_idr的id值,否则动态确定。最终都会调用i2c_register_adapter()函数,具体如下:

路径:driver/i2c/i2c-core.c
static int i2c_register_adapter(struct i2c_adapter *adap)
{
	int res = -EINVAL;
.............
	of_i2c_register_devices(adap);
.............
}

在函数 i2c_register_adapter()函数,首先会进行一些属性检查以及初始化工作,最后会调用of_i2c_register_devices()函数,具体如下:

路径:driver/i2c/i2c-core.c
static void of_i2c_register_devices(struct i2c_adapter *adap)
{
	struct device_node *bus, *node;
	struct i2c_client *client;

	/* Only register child devices if the adapter has a node pointer set */
	if (!adap->dev.of_node)
		return;

	dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");

	bus = of_get_child_by_name(adap->dev.of_node, "i2c-bus");
	if (!bus)
		bus = of_node_get(adap->dev.of_node);

	for_each_available_child_of_node(bus, node) {
		if (of_node_test_and_set_flag(node, OF_POPULATED))
			continue;

		client = of_i2c_register_device(adap, node);
		if (IS_ERR(client)) {
			dev_warn(&adap->dev,
				 "Failed to create I2C device for %s\n",
				 node->full_name);
			of_node_clear_flag(node, OF_POPULATED);
		}
	}

	of_node_put(bus);
}

 在该函数中,首先获取该adapter的device node节点,然后遍历该device node的子节点,依次调用of_i2c_register_device()注册该子设备, 这样就完成了i2c控制器子设备的注册工作。

至此分析了,i2c设备的注册工作。其他设备的注册流程大体类似, 可以参照i2c设备的注册过程来分析。

 

参考博客:http://www.wowotech.net/device_model/dt-code-analysis.html

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值