歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux技術 >> 基於linux 3.10.49內核 從dts文件裡注冊platform_device流程分析

基於linux 3.10.49內核 從dts文件裡注冊platform_device流程分析

日期:2017/3/3 12:24:06   编辑:Linux技術

基於linux 3.10.49內核 從dts文件裡注冊platform_device流程分析

linux kernel 3.10.49+在這裡, 我們說說linux 是怎麼通過dts進行設備(platform_device)注冊和初始化板載信息.

在arch/arm/mach-******/******.c找到DT_MACHINE_START 和 MACHINE_END 宏, 如下:

DT_MACHINE_START(******_DT, "************* SoC (Flattened Device Tree)")

.atag_offset = 0x100,

.dt_compat = ******_dt_compat, // 匹配dts

.map_io = ******_map_io, // 板級地址內存映射, linux mmu

.init_irq = irqchip_init, // 板級中斷初始化.

.init_time = ******_timer_and_clk_init, // 板級時鐘初始化,如ahb,apb等

.init_machine = ******_dt_init, // 這裡是解析dts文件入口.

.restart = ******_restart, // 重啟, 看門狗寄存器相關可以在這裡設置

MACHINE_END

其中.dt_compat = ******_dt_compat 這個結構體是匹配是哪個dts文件, 如:

static const char * const ******_dt_compat[] = {

"******,******-soc",

NULL

};

這個"******,******-soc" 字符串可以在我們的dts的根節點下可以找到.

好了, 我們來看看 init_machine = ******_dt_init 這個回調函數.

1. arch/arm/mach-******/******.c : void __init ******_dt_init(void)

******_dt_init(void) --> of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);

of_default_bus_match_table 這個是struct of_device_id的全局變量.

const struct of_device_id of_default_bus_match_table[] = {

{ .compatible = "simple-bus", },

#ifdef CONFIG_ARM_AMBA

{ .compatible = "arm,amba-bus", },

#endif /* CONFIG_ARM_AMBA */

{} /* Empty terminated list */

};

我們設計dts時, 把一些需要指定寄存器基地址的設備放到以compatible = "simple-bus"為匹配項的設備節點下. 下面會有介紹為什麼.

2. drivers/of/platform.c : int of_platform_populate(...)

of_platform_populate(...) --> of_platform_bus_create(...)

// 在這之前, 會有of_get_property(bus, "compatible", NULL)

// 檢查是否有compatible, 如果沒有, 返回, 繼續下一個, 也就是說沒有compatible, 這個設備不會被注冊

for_each_child_of_node(root, child) {

printk("[%s %s %d] child->name = %s, child->full_name = %s\n", __FILE__, __func__, __LINE__, child->name, child->full_name);

rc = of_platform_bus_create(child, matches, lookup, parent, true);

if (rc)

break;

}

論詢dts根節點下的子設備, 每個子設備都要of_platform_bus_create(...);

全部完成後, 通過 of_node_put(root); 釋放根節點, 因為已經處理完畢;

3. drivers/of/platform.c : of_platform_bus_create(bus, ...)

dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent); // 我們跳到 3-1-1步去運行

if (!dev || !of_match_node(matches, bus)) // 就是匹配

// dt_compat = ******_dt_compat, 也就是 compatible = "simple-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); // dev->dev以本節點為父節點, 我們跳到 3-2-1步去運行

if (rc) {

of_node_put(child);

break;

}

}

3-1-1. drivers/of/platform.c : of_platform_device_create_pdata(...)

if (!of_device_is_available(np)) // 查看節點是否有效, 如果節點有'status'屬性, 必須是okay或者是ok, 才是有效, 沒有'status'屬性, 也有效

return NULL;

dev = of_device_alloc(np, bus_id, parent); // alloc設備, 設備初始化. 返回dev, 所有的設備都可認為是platform_device, 跳到3-1-1-1看看函數做了什麼事情

if (!dev)

return NULL;

#if defined(CONFIG_MICROBLAZE)

dev->archdata.dma_mask = 0xffffffffUL;

#endif

dev->dev.coherent_dma_mask = DMA_BIT_MASK(32); // dev->dev 是 struct device. 繼續初始化

dev->dev.bus = &platform_bus_type; //

dev->dev.platform_data = platform_data;

printk("[%s %s %d] of_device_add(device register) np->name = %s\n", __FILE__, __func__, __LINE__, np->name);

if (of_device_add(dev) != 0) { // 注冊device, of_device_add(...) --> device_add(...) // This is part 2 of device_register()

platform_device_put(dev);

return NULL;

}

3-1-1-1. drivers/of/platform.c : of_device_alloc(...)

1) alloc platform_device *dev

2) 如果有reg和interrupts的相關屬性, 運行of_address_to_resource 和 of_irq_to_resource_table, 加入到dev->resource

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);

/* printk("[%s %s %d] res->name = %s, res->start = 0x%X, res->end = 0x%X\n", __FILE__, __func__, __LINE__, res->name, res->start, res->end); */

WARN_ON(rc);

}

WARN_ON(of_irq_to_resource_table(np, res, num_irq) != num_irq);

3) dev->dev.of_node = of_node_get(np);

// 這個node屬性裡有compatible屬性, 這個屬性從dts來, 後續driver匹配device時, 就是通過這一屬性進匹配

// 我們可以通過添加下面一句話來查看compatible.

// printk("[%s %s %d] bus->name = %s, of_get_property(...) = %s\n", __FILE__, __func__, __LINE__, np->name, (char*)of_get_property(np, "compatible", NULL));

// node 再給dev, 後續給驅動注冊使用.

4) 運行 of_device_make_bus_id 設定device的名字, 如: soc.2 或 ac000000.serial 等

3-2-1. drivers/of/platform.c :

以 compatible = "simple-bus"的節點的子節點都會以這個節點作為父節點在這步注冊設備.

這是實際的板載設備, 也是最終目的.

下一篇分析,我們來講講platform_driver的注冊, 是怎麼匹配剛才我們注冊過的platform_device的

Copyright © Linux教程網 All Rights Reserved