做网站的公司怎么拓展业务/产品推广的目的和意义
作为一个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.cstatic 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