(三)從解析DTS到創建device_從device_node到併入設備驅動模型(結合源碼)

從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_initcallsdo_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了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章