先来看看DTB在系统中被使用的过程。
setup_machine_fdt()
直接在dtb中解析根节点的一些属性和子节点给系统早期使用。
解析”/”节点的model”属性给machine_desc赋值;
解析”/chosen”node中的”bootargs”属性给boot_command_line;
解析”/”节点的”#size-cells”、”#address-cells”属性;
解析”/memory”node中的”reg”属性,并将memory区域加入到系统;
unflatten_device_tree()
将DTB完全递归解析为内核使用的的device_node、property结构:
struct device_node,struct property
/* node和node之间树形结构的创建 */
if (dad != NULL) {
np->parent = dad;
np->sibling = dad->child;
dad->child = np;
}
}
property->name:指针指向dtb strings blcok区域中的属性name
property->value:指针指向dtb stucture block区域中的属性data
根据"name"、 "device_type"属性,来给device_node结构中的name、type成员赋值
如果还有子node的存在,递归解析
of_platform_populate()
递归创建platform_device。
root节点下的第1级子节点创建成platform device
对root节点下的第1级子节点,如果有”compatible”属性创建对应platform device;
如果”compatible”属性等于of_default_bus_match_table(“simple-bus”/”simple-mfd”/”arm,amba-bus”)中任意一种,继续对其子节点进行platform device创建。
const struct of_device_id of_default_bus_match_table[]
获取dts中的root node
对root node的child node进行platform device创建
struct platform_device *dev;
void *platform_data = NULL;
递归对本node的child node进行platform device创建
of_platform_device_create_pdata()
分配node对应的platform_device结构,并且解析node中的"reg"、"interrupts"属性,作为platform_device->resource
device对应的bus为platform_bus_type
struct platform_device *dev;
dev->dev.bus = &platform_bus_type;
dev->dev.platform_data = platform_data;
注册platform_device->dev为标准的device
platform_device_put(dev);
根据device能够找到of node
struct device_node *np;
struct platform_device *dev;
dev->dev.of_node = of_node_get(np);
给resource分配空间并解析值
dev->num_resources = num_reg + num_irq;
dev->resource = res;
因为第1级子节点会被注册成platform device,例如i2c/spi控制器,那么对应也需要注册platform driver。
i2c控制器驱动为例:
首先会创建对应platform driver,把adapter注册成i2c device;
adapter的probe过程中,会调用of_i2c_register_devices()函数遍历控制器下挂的i2c设备的DTS节点,并将其注册成i2c_client;
static const struct of_device_id mtk_i2c_of_match[]
static struct platform_driver mt_i2c_driver = {
.probe = mt_i2c_probe,
.remove = mt_i2c_remove,
.driver = {
.name = I2C_DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(mtk_i2c_of_match),
},
};
static int mt_i2c_probe(struct platform_device *pdev)
{
struct platform_device *pdev;
struct mt_i2c *i2c;
struct resource *res;
const struct of_device_id *of_id;
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);
i2c->irqnr = platform_get_irq(pdev, 0);
i2c_set_adapdata(&i2c->adap, i2c);
ret = i2c_add_numbered_adapter(&i2c->adap);
if (ret) {
dev_err(&pdev->dev, "Failed to add i2c bus to i2c core\n");
return ret;
}
}
注册adapter为i2c_bus上的device
static int i2c_register_adapter(struct i2c_adapter *adap)
{
struct i2c_adapter *adap;
res = device_register(&adap->dev);
}
遍历adapter node下的子node,并创建标准的i2c bus的device
static void of_i2c_register_devices(struct i2c_adapter *adap)
{
struct device_node *node;
of_i2c_register_device(adap, node);
}
static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
struct device_node *node)
{
struct i2c_client *result;
struct i2c_board_info info;
struct dev_archdata dev_ad;
addr_be = of_get_property(node, "reg", &len);
addr = be32_to_cpup(addr_be);
dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n";
}
具体的I2c设备驱动,在总线驱动使用of_i2c_register_devices()创建设备以后,就可以适配工作了。
i2c1: i2c@11090000 {
compatible = "mediatek,mt6799-i2c";
id = <1>;
reg = <0 0x11090000 0 0x1000>,
<0 0x11000100 0 0x80>;
interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_LOW>;
clocks = <&pericfg CLK_PERICFG_RG_I2C1_BCLK>, <&pericfg CLK_PERICFG_RG_AP_DM>;
clock-names = "main", "dma";
clock-div = <5>;
};
&i2c1 {
apds9922:apds9922@53 {
compatible = "mediatek,apds9922";
interrupt-parent = <&eintc>;
interrupts = < 8 IRQ_TYPE_EDGE_FALLING>;
debounce = <8 0>;
gpio = < 8 >;
reg = <0x53>;
status = "okay";
};
}
static const struct i2c_device_id mz_mag_id[] = {
{"mediatek,mmc3530", 0 },
{"mediatek,akm09911", 1 },
{ }
};
static struct i2c_driver mz_mag_driver = {
.probe = mz_mag_probe,
.id_table = mz_mag_id,
.driver = {
.name = MZ_MAG_DEV_NAME,
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = of_match_ptr(msensor_of_match),
#endif
},
};