設備樹學習:內核對設備樹的處理


內核版本:linux-4.19

之前系統的學習了有關設備樹的一些知識,時間長了總會有忘記的時候,所以現在把所學到的知識記錄下來。

系統啟動后,內核會執行一段匯編代碼,匯編代碼暫不分析,我們從 start_kernel 開始。

一、優先被初始化的信息

調用流程:

start_kernel
	-->setup_arch
		-->setup_machine_fdt
			-->early_init_dt_verify			/* 驗證設備樹文件 */
			-->of_flat_dt_match_machine		/* 與內核中注冊的 machine_desc 進行比較, 最終獲取到與之匹配的 machine_desc */
				-->arch_get_next_mach		/* 獲取到 machine_desc 的 dt_compat 屬性 */
			-->early_init_dt_scan_nodes		/* 獲取到設備樹中 chosen、{size,address}-cells、memory 信息 */

early_init_dt_verify 代碼:

bool __init early_init_dt_verify(void *params)
{
	if (!params)
		return false;

	/* 驗證設備樹的 magic */
	if (fdt_check_header(params))
		return false;

	/* 設置 device-tree 指針 */
	initial_boot_params = params;
	of_fdt_crc32 = crc32_be(~0, initial_boot_params,
				fdt_totalsize(initial_boot_params));
	return true;
}

of_flat_dt_match_machine 代碼:

獲取到最為匹配的 machine_desc。

const void * __init of_flat_dt_match_machine(const void *default_match,
		const void * (*get_next_compat)(const char * const**))
{
    ...
    
    while ((data = get_next_compat(&compat))) {
    		score = of_flat_dt_match(dt_root, compat);
    		if (score > 0 && score < best_score) {
    			best_data = data;
    			best_score = score;
    		}
    	}
	...
	
	return best_data;
}

early_init_dt_scan_nodes 代碼:

/* 掃描 /chosen 節點,處理 bootargs 並保存至 boot_command_line */
of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);

/* 獲取 {size,address}-cells 信息 */
of_scan_flat_dt(early_init_dt_scan_root, NULL);

/* 設置 memeory */
of_scan_flat_dt(early_init_dt_scan_memory, NULL);

通過 early_init_dt_scan_memory 函數,最后調用 memblock_add 來完成 memory 的設置。

二、設備樹展開

接下來,內核會展開設備樹,並將節點構建為 device_node。便於系統管理、使用。

調用流程:

start_kernel
	-->setup_arch
		-->unflatten_device_tree
			-->__unflatten_device_tree
				-->unflatten_dt_nodes(blob, NULL, dad, NULL);		/* First pass, scan for size */
					-->fdt_next_node								/* 獲取每個 node 的 offsize, 並統計整體大小 */
				-->unflatten_dt_nodes(blob, mem, dad, mynodes);		/* Second pass, do actual unflattening */
					-->populate_node

device_node 結構:

struct device_node {
	const char *name;   /* 節點的 name 屬性 */
	const char *type;   /* 節點的 device_type 屬性 */
	phandle phandle;
	const char *full_name;
	struct fwnode_handle fwnode;

	struct	property *properties;   /* 節點的屬性 */
	struct	property *deadprops;	/* removed properties */
	struct	device_node *parent;
	struct	device_node *child;
	struct	device_node *sibling;
#if defined(CONFIG_OF_KOBJ)
	struct	kobject kobj;
#endif
	unsigned long _flags;
	void	*data;
#if defined(CONFIG_SPARC)
	const char *path_component_name;
	unsigned int unique_id;
	struct of_irq_controller *irq_trans;
#endif
};

property 結構:

struct property {
	char	*name;  /* 屬性名字 */
	int	length;     /* 屬性值長度 */
	void	*value; /* 屬性值指針 */
	struct property *next;
#if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC)
	unsigned long _flags;
#endif
#if defined(CONFIG_OF_PROMTREE)
	unsigned int unique_id;
#endif
#if defined(CONFIG_OF_KOBJ)
	struct bin_attribute attr;
#endif
};

這些 device_node 構成一棵樹,根節點為: of_root。

三、device_node 轉換為 platform_device

調用流程:

arch_initcall_sync(of_platform_default_populate_init);
	-->of_platform_default_populate
		-->of_platform_populate
			-->of_platform_bus_create
				-->of_platform_device_create_pdata
					-->of_device_alloc
					-->of_device_add

這時涉及到 initcall 調用問題,應該會在下一篇文章總結。

of_device_alloc 代碼:

struct platform_device *of_device_alloc(struct device_node *np, const char *bus_id, struct device *parent)
{
    ...
    platform_device_alloc       /* 分配 platform_device */
    ...
    of_address_to_resource      /* 解析 address 資源 */
    ...
    of_irq_to_resource_table    /* 解析 irq 資源 */
    ...
}

of_device_add 代碼:

int of_device_add(struct platform_device *ofdev)
{
	BUG_ON(ofdev->dev.of_node == NULL);

	ofdev->name = dev_name(&ofdev->dev);
	ofdev->id = PLATFORM_DEVID_NONE;

	set_dev_node(&ofdev->dev, of_node_to_nid(ofdev->dev.of_node));

	return device_add(&ofdev->dev);     /* 添加 device */
}
哪些 device_node 可以轉換為 platform_device?
	1. 根節點下含有 compatile 屬性的子節點
	2. 如果一個結點的 compatile 屬性含有這些特殊的值 ("simple-bus", "simple-mfd", "isa", "arm,amba-bus") 之一,
	   那么它的子結點(需含 compatile 屬性)也可以轉換為 platform_device
    3. i2c, spi 等總線節點下的子節點,應該交給對應的總線驅動程序來處理,它們不應該被轉換為 platform_device。

of_default_bus_match_table 表:

const struct of_device_id of_default_bus_match_table[] = {
	{ .compatible = "simple-bus", },
	{ .compatible = "simple-mfd", },
	{ .compatible = "isa", },
#ifdef CONFIG_ARM_AMBA
	{ .compatible = "arm,amba-bus", },
#endif /* CONFIG_ARM_AMBA */
	{} /* Empty terminated list */
};


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM