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

1675

被折叠的 条评论
为什么被折叠?



