1. 程式人生 > >(三)從解析DTS到建立device_從device_node到併入裝置驅動模型(結合原始碼)

(三)從解析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了。