從device_node到併入設備驅動模型
此篇博客有很多參考其他文章的內容,由於參考內容繁雜,不一一標註角標了,在末尾會貼上所有參考博客的link,如有侵權,請聯繫本人處理,謝謝。
深入,並且廣泛
-沉默犀牛
上一篇文章已經詳細的分析了兩個問題:
1.如何根據Device Tree的信息,找到最適合的machine_desc
2.如何將DTB轉換成節點是device_node的樹狀結構
那麼爲什麼要做這兩件事情呢?
我們現在要做的事情是把DTS中描述的節點(status = okay的)註冊到kernel中,DTS的信息我們的kernel沒法直接識別啊,就得通過上述的2把DTS中描述的每一個節點,轉換成device_node結構體,我們可以認爲DTS中寫的每一個節點在這裏都被解析爲一個device_node。(該結構體中有parent、child、sibling成員,通過這些成員把device_node給連接成了樹狀結構)好,現在我們有了完全代表了DTS信息,又能被kernel識別的device_node,現在就要把這些device_node一個個的創建起來(比如有的device_node要註冊到platform總線上,有的要註冊到i2c總線…),這個創建的過程,就需要我們上述1中的machine_desc了!在定義machine_desc結構體的時候,會定義一個回調函數:xxxx_init(大部分情況)就是這個xxxx_init把device_node都註冊起來。
再補充上面一段話的三個點
1.xxxx_init函數只是註冊了platform總線上的device_node,那其他的呢?比如i2c上的device呢?在註冊i2c總線時,調用qup_i2c_probe(),這個接口會添加i2c適配器,適配器添加完成後會調用of_i2c_register_devices()接口來i2c總線節點的子節點,然後調用i2c_new_device(),生成i2c設備。
2.我在定義回調函數後面寫了個大部分情況,也就代表有的時候不會定義這個回調函數,那麼kernel怎麼註冊device_node呢?事實上kernel會檢測machine_desc有沒有init函數,如果有,則調用;如果沒有,則調用of提供的接口直接註冊所有platform的device,這裏就有一個問題了:這個machine_desc如果沒有定義init函數,那我還要它幹嘛呢?我去看(根據DTS選擇最適配的machine_desc)那一段代碼時發現,如果沒有找到合適的machine_desc的話,有一行註釋:does not return! 這似乎意味着,找不到合適的machine_desc也沒啥關係,kernel還是會繼續運行下去,platform上的device還是可以註冊上。如果這一點的理解有誤,請留言糾正,大恩不言謝!
3.DTS中的節點都轉換成了device_node,但是DTS中描述的節點並不都是device啊,比如cpus node,memory node,choose node等。對於這些節點的處理,我會在下一篇文章中說明。
經過前面的介紹和解釋,我們明確了這一篇文章的主題:
那些platform總線下的device_node如何註冊到platform總線上的?
執行流程
進入kernel的入口後,會按照如下的調用流程start_kernel
->rest_init
->kernel_init
->kernel_init_freeable
->
do_basic_setup
->do_initcalls
,do_initcalls
函數中,在do_initcalls函數中,kernel會依次執行各個initcall函數。
代碼驗證
在上述的調用流程中,會調用到調用customize_machine
,也正是這個函數,檢測了machine_desc是否有xxxx_init函數,代碼如下:
static int __init customize_machine(void)
{
if (machine_desc->init_machine) //如果有init_machine()函數,則執行
machine_desc->init_machine();
else //否則直接調用of提供的接口來註冊device
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
return 0;
}
arch_initcall(customize_machine);
以高通msm8953爲例,看看它有沒有定義init_machine函數:
DT_MACHINE_START(MSM8953_DT,
"Qualcomm Technologies, Inc. MSM8953 (Flattened Device Tree)")
.init_machine = msm8953_init, //這裏定義了init函數
.dt_compat = msm8953_dt_match, //這個參數就是之前匹配machine_desc用的
//這個參數將於DTS中的compatible屬性值來比較
MACHINE_END
再看看這個函數的內容:
static void __init msm8953_init(void)
{
board_dt_populate(NULL);
}
void __init board_dt_populate(struct of_dev_auxdata *adata)
{
//傳入NULL參數表示從root node開始scan
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
/* Explicitly parent the /soc devices to the root node to preserve
* the kernel ABI (sysfs structure, etc) until userspace is updated
*/
//從soc節點開始scan
of_platform_populate(of_find_node_by_path("/soc"),
of_default_bus_match_table, adata, NULL);
}
看來of_platform_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;
//之前傳入的NULL,就是從/節點下開始,即根節點
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) { //這裏面是一個for循環,如果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;
}
看來of_platform_bus_create()
這個函數是個重要函數,仔細分析一下:
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;
//確保device node有compatible屬性的代碼
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;
}
//在傳入的lookup table尋找和該device node匹配的附加數據
auxdata = of_dev_lookup(lookup, bus);
if (auxdata) {
bus_id = auxdata->name;
platform_data = auxdata->platform_data;
}
/*ARM公司提供了CPU core,除此之外,它設計了AMBA的總線來連接SOC內的各個block。
符合這個總線標準的SOC上的外設叫做ARM Primecell Peripherals。
如果一個device node的compatible屬性值是arm,primecell的話,
可以調用of_amba_device_create來向amba總線上增加一個amba device。*/
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;
}
//如果不是ARM Primecell Peripherals,那麼我們就需要向platform bus上增加一個platform device了
dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
if (!dev || !of_match_node(matches, bus))
return 0;
// 一個device node可能是一個橋設備,因此要重複調用of_platform_bus_create來把所有的device node處理掉。
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;
}
具體增加platform device的代碼在of_platform_device_create_pdata()
中,代碼如下:
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)) //check status屬性,和是否已經註冊過
return NULL;
// of_device_alloc除了分配struct platform_device的內存,還分配了該platform device需要的resource的內存
//這裏就根據struct device_node創建了struct platform_device
dev = of_device_alloc(np, bus_id, parent);
if (!dev)
goto err_clear_flag;
//設定platform_device 中的其他成員
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) { //把這個platform device加入統一設備模型系統中
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;
}
到這裏我們就很清楚device_node是如何最終成爲platform_device了。