linux裝置樹dts之powerpc 平臺解析過程
一. 在linux中,對dtb檔案解析的整個過程式如下:
1)首先將從u-boot 傳遞過來的映像基地址和dtb 檔案映像基地址儲存通用暫存器r30,r31;
2)通過呼叫machine_init()、early_init_devtree()函式來獲取核心前期初始化所需的bootargs,cmd_line等系統引導引數;
3)呼叫start_kernel()、setup_arch()、unflatten_device_tree()函式來解析dtb 檔案,構建一個由device_node 結構連線而成的單項鍊表,並使用全域性變數allnodes 指標來儲存這個連結串列的頭指標;如下在此函式執行過後,在記憶體中會存在一個如下的連結串列,後面所有的函式,如果需要從of
tree結構上讀取裝置資料的,都將從這個連結串列中遍歷並讀取。
4)核心呼叫OF 提供的API 函式獲取allnodes連結串列資訊來初始化核心其他子系統、裝置等。(of_flatform_device)
二、dts檔案首地址解析
在進行DTS檔案解析之前先要從bootm啟動命令中獲取dtb檔案所在的地址。而這一步的開始是從arch/powerpc/kernel/head_32.s檔案開始中的。
1、arch/powerpc/kernel/head_32.s
* This is where the main kernel code starts.
*/
start_here:
/* ptr to current */
lisr2,[email protected]
orir2,r2,[email protected]
/* Set up for using our exception vectors */
/* ptr to phys current thread */
tophys(r4,r2)
addir4,r4,THREAD/* init task's THREAD */
CLR_TOP32(r4)
mtsprSPRN_SPRG_THREAD,r4
lir3,0
mtsprSPRN_SPRG_RTAS,r3/* 0 => not in RTAS */
/* stack */
lisr1,[email protected]
addir1,r1,[email protected]
lir0,0
stwur0,THREAD_SIZE-STACK_FRAME_OVERHEAD(r1)
/*
* Do early platform-specific initialization,
* and set up the MMU.
*/
mrr3,r31
mrr4,r30
blmachine_init //在這個函式中
bl__save_cpu_setup
blMMU_init
notrace void __init machine_init(unsigned long dt_ptr)
{
lockdep_init();
/* Enable early debugging if any specified (see udbg.h) */
udbg_early_init();
/* Do some early initialization based on the flat device tree */
early_init_devtree(__va(dt_ptr));
probe_machine();
setup_kdump_trampoline();
#ifdef CONFIG_6xx
if (cpu_has_feature(CPU_FTR_CAN_DOZE) ||
cpu_has_feature(CPU_FTR_CAN_NAP))
ppc_md.power_save = ppc6xx_idle;
#endif
#ifdef CONFIG_E500
if (cpu_has_feature(CPU_FTR_CAN_DOZE) ||
cpu_has_feature(CPU_FTR_CAN_NAP))
ppc_md.power_save = e500_idle;
#endif
if (ppc_md.progress)
ppc_md.progress("id mach(): done", 0x200);
}
void __init early_init_devtree(void *params)
{
phys_addr_t limit;
DBG(" -> early_init_devtree(%p)\n", params);
/* Setup flat device-tree pointer */
initial_boot_params = params;
#ifdef CONFIG_PPC_RTAS
/* Some machines might need RTAS info for debugging, grab it now. */
of_scan_flat_dt(early_init_dt_scan_rtas, NULL);
#endif
#ifdef CONFIG_PHYP_DUMP
/* scan tree to see if dump occured during last boot */
of_scan_flat_dt(early_init_dt_scan_phyp_dump, NULL);
#endif
/* Retrieve various informations from the /chosen node of the
* device-tree, including the platform type, initrd location and
* size, TCE reserve, and more ...
*/
of_scan_flat_dt(early_init_dt_scan_chosen, NULL);
/* Scan memory nodes and rebuild LMBs */
lmb_init();
of_scan_flat_dt(early_init_dt_scan_root, NULL);
of_scan_flat_dt(early_init_dt_scan_memory, NULL);
/* Save command line for /proc/cmdline and then parse parameters */
strlcpy(boot_command_line, cmd_line, COMMAND_LINE_SIZE);
parse_early_param();
/* Reserve LMB regions used by kernel, initrd, dt, etc... */
lmb_reserve(PHYSICAL_START, __pa(klimit) - PHYSICAL_START);
/* If relocatable, reserve first 32k for interrupt vectors etc. */
if (PHYSICAL_START > MEMORY_START)
lmb_reserve(MEMORY_START, 0x8000);
reserve_kdump_trampoline();
reserve_crashkernel();
early_reserve_mem();
phyp_dump_reserve_mem();
limit = memory_limit;
if (! limit) {
phys_addr_t memsize;
/* Ensure that total memory size is page-aligned, because
* otherwise mark_bootmem() gets upset. */
lmb_analyze();
memsize = lmb_phys_mem_size();
if ((memsize & PAGE_MASK) != memsize)
limit = memsize & PAGE_MASK;
}
lmb_enforce_memory_limit(limit);
lmb_analyze();
lmb_dump_all();
DBG("Phys. mem: %llx\n", lmb_phys_mem_size());
/* We may need to relocate the flat tree, do it now.
* FIXME .. and the initrd too? */
move_device_tree();
DBG("Scanning CPUs ...\n");
/* Retreive CPU related informations from the flat tree
* (altivec support, boot CPU ID, ...)
*/
of_scan_flat_dt(early_init_dt_scan_cpus, NULL);
DBG(" <- early_init_devtree()\n");
}
三、解析
main.c-------------->arch/pwerpc/kernel/setup_32.c-------------->arch/powerpc/kernel/prom.c
start_kernel()------>setup_arch()---------->unflatten_device_tree()
/**
* unflattens the device-tree passed by the firmware, creating the
* tree of struct device_node. It also fills the "name" and "type"
* pointers of the nodes so the normal device-tree walking functions
* can be used (this used to be done by finish_device_tree)
*/
void __init unflatten_device_tree(void)
{
unsigned long start, mem, size;
struct device_node **allnextp = &allnodes;
DBG(" -> unflatten_device_tree()\n");
/* First pass, scan for size */
start = ((unsigned long)initial_boot_params) +
initial_boot_params->off_dt_struct;
size = unflatten_dt_node(0, &start, NULL, NULL, 0);
size = (size | 3) + 1;
DBG(" size is %lx, allocating...\n", size);
/* Allocate memory for the expanded device tree */
mem = lmb_alloc(size + 4, __alignof__(struct device_node));
mem = (unsigned long) __va(mem);
((u32 *)mem)[size / 4] = 0xdeadbeef;
DBG(" unflattening %lx...\n", mem);
/* Second pass, do actual unflattening */
start = ((unsigned long)initial_boot_params) +
initial_boot_params->off_dt_struct;
unflatten_dt_node(mem, &start, NULL, &allnextp, 0);
if (*((u32 *)start) != OF_DT_END)
printk(KERN_WARNING "Weird tag at end of tree: %08x\n", *((u32 *)start));
if (((u32 *)mem)[size / 4] != 0xdeadbeef)
printk(KERN_WARNING "End of tree marker overwritten: %08x\n",
((u32 *)mem)[size / 4] );
*allnextp = NULL;
/* Get pointer to OF "/chosen" node for use everywhere */
of_chosen = of_find_node_by_path("/chosen");
if (of_chosen == NULL)
of_chosen = of_find_node_by_path("/[email protected]");
DBG(" <- unflatten_device_tree()\n");
}
四. 將allnodes顯示在/proc/device-tree目錄下
start_kernel() -->#ifdef CONFIG_PROC_FS
proc_root_init(); -->
#endif
#ifdef CONFIG_PROC_DEVICETREE
proc_device_tree_init(); -->
#endif
proc_device_tree_add_node(root, proc_device_tree);
此時將在/proc/device-tree目錄下生成裝置節點
# ls -al /proc/device-tree
五. of_platform_bus_type的註冊/初始化
-------------------------------------------------------------------arch/powerpc/kernel/of_platform.c
struct bus_type of_platform_bus_type = {
.uevent = of_device_uevent,
};
EXPORT_SYMBOL(of_platform_bus_type);
static int __init of_bus_driver_init(void)
{
return of_bus_type_init(&of_platform_bus_type, "of_platform");
}
postcore_initcall(of_bus_driver_init);
of_platform_bus_type匯流排註冊完畢。
此時/sys/bus/目錄下將會有of_platform
六. 將allnodes裝置節點新增到匯流排of_platform_bus_type上
-------------------------------------------------------------------
arch/powerpc/platform/85xx/mpc85Xx_ads.c
static int __init declare_of_platform_devices(void)
{
of_platform_bus_probe(NULL, of_bus_ids, NULL);
return 0;
}
machine_device_initcall(mpc85xx_ads, declare_of_platform_devices);
declare_of_platform_devices() --> of_platform_bus_probe(NULL, of_bus_ids, NULL)
arch/powerpc/kernel/of_platform.c
遍歷第一步中在記憶體中生成連結串列的所有soc的子節點,將所有的soc子節點裝置新增到of_platform匯流排。
int of_platform_bus_probe(struct device_node *root,
const struct of_device_id *matches,
struct device *parent)
{
struct device_node *child;
struct of_device *dev;
int rc = 0;
if (matches == NULL)
matches = of_default_bus_ids;
if (matches == OF_NO_DEEP_PROBE)
return -EINVAL;
if (root == NULL)
root = of_find_node_by_path("/");
else
of_node_get(root);
pr_debug("of_platform_bus_probe()\n");
pr_debug(" starting at: %s\n", root->full_name);
/* Do a self check of bus type, if there's a match, create
* children
*/
if (of_match_node(matches, root)) {
pr_debug(" root match, create all sub devices\n");
dev = of_platform_device_create(root, NULL, parent);
if (dev == NULL) {
rc = -ENOMEM;
goto bail;
}
pr_debug(" create all sub busses\n");
rc = of_platform_bus_create(root, matches, &dev->dev);
goto bail;
}
for_each_child_of_node(root, child) {
if (!of_match_node(matches, child))
continue;
pr_debug(" match: %s\n", child->full_name);
dev = of_platform_device_create(child, NULL, parent);
if (dev == NULL)
rc = -ENOMEM;
else
rc = of_platform_bus_create(child, matches, &dev->dev);
if (rc) {
of_node_put(child);
break;
}
}
bail:
of_node_put(root);
return rc;
}
EXPORT_SYMBOL(of_platform_bus_probe);
--> of_platform_device_create--> of_device_register() --> device_add()
of_platform總線上的所有裝置新增完畢,e0024000.ethernet,e0024520.mdio等裝置現在都在總線上。至此裝置節點將出現在/sys/devices/platform/
七、例項
1、mdio匯流排的註冊
/driver/net/phy_device.csubsys_initcall(phy_init)
phy_init --> mdio_bus_init --> bus_register(&mdio_bus_type)
匯流排註冊後,在總線上註冊了一個預設的phy的驅動 genphy_driver:
.phy_id = 0xffffffff,
.phy_id_mask = 0xffffffff,
.name = "Generic phy",
mdio匯流排註冊完畢。
/sys/bus/mdio
2、 mdio總線上驅動的新增
--------------------------------------------
module_init(marvell_init)
marvell_init() -->
phy_driver_register(&marvell_drivers[i]) -->
driver_register()
前面第三步,註冊mdio匯流排後,已經添加了一個預設的phy的驅動,現在要將所有的phy驅動新增到總線上,這裡將所有的marvell的phy都新增。
這步過後,核心的/sys/bus/mdio/driver裡面就有了各種phy的驅動,但這時還沒有和具體的裝置繫結。
3、 of_platform總線上mdio裝置驅動(該驅動的目的是在mdio總線上新增phy裝置)的新增,並繫結裝置:e0024520.mdio和e0025520.mdio
/driver/net/fsl_pq_mdio.cmodule_init(fsl_pq_mdio_init)
fsl_pq_mdio_init --> of_register_platform_driver(&fsl_pq_mdio_driver) --> of_register_driver --> driver_register --> bus_add_driver --> driver_attach
遍歷整個of_platform匯流排,尋找與之相匹配的裝置,找到e0024520.mdio
driver_attach --> __driver_attach --> driver_match_device
將driver的match_table裡的資訊和dev_nod中的做比較,若符合就進入driver的probe,也就是fsl_pq_mdio_probe。
現在of_platform總線上的裝置e0024520.mdio和e0025520.mdio已經綁定了驅動。
4、 mdio總線上的裝置的新增,尋找並繫結相應的驅動。
/driver/net/fsl_pq_mdio.c
fsl_pq_mdio_probe --> of_mdiobus_register --> phy_device_register --> device_register(&phydev->dev) --> device_add --> bus_probe_device --> device_attach -->bus_for_each_drv
掃描mdio總線上的所有的驅動,若找到匹配的,就繫結,並probe。
__device_attach --> driver_probe_device --> really_probe --> phy_probe
將所有的phy和tbi-phy的裝置都新增到mdio總線上,並且兩個phy裝置和兩個tbi-phy裝置都會根據其自己的phyID找到各自的驅動
相關推薦
linux裝置樹dts之powerpc 平臺解析過程
一. 在linux中,對dtb檔案解析的整個過程式如下: 1)首先將從u-boot 傳遞過來的映像基地址和dtb 檔案映像基地址儲存通用暫存器r30,r31;2)通過呼叫machine_init()、early_init_devtree()函式來獲取核心前期初始化所需的bo
linux裝置樹dts一之移植詳解
支援包的開發,使得單個核心映象能支援多個系統。作為U-Boot 和Linux 核心之間的動態 介面,本文闡述了裝置樹的資料儲存格式以及原始碼描述語法,進而分析了U-Boot 對扁平設 備樹的支援設定,Linux 核心對裝置樹的解析流程。 關鍵詞:扁平裝置樹; DTS; PowerPC; Linux IBM、S
Linux裝置驅動模型之platform(平臺)匯流排詳解
1 int driver_attach(struct device_driver *drv) 2 { 3 return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); // 這個函式的功能就是: 依次去匹配b
linux裝置樹dts移植詳解
摘 要:裝置樹的引入減少了核心為支援新硬體而需要的改變,提高程式碼重用,加速了Linux支援包的開發,使得單個核心映象能支援多個系統。作為U-Boot 和Linux 核心之間的動態介面,本文闡述了裝置樹的資料儲存格式以及原始碼描述語法,進而分析了U-Boot 對扁平設備樹的
基於Android7.1 8953 高通平臺下零死角玩轉裝置樹DTS
【基於Android7.1 8953 高通平臺下零死角玩轉裝置樹DTS】 更新內容: 【創科之龍_安卓開發】第01課_為什麼引用linux裝置樹和對比優勢 【創科之龍_安卓開發】第02課_如何在Linux-3.x核心
第18章 ARM Linux裝置樹之四(常用的OF API)
18.4 常用的OF API除了前文介紹的of_machine_is_compatible()、of_device_is_compatible()等常用函式以外,在Linux的BSP和驅動程式碼中,經常會使用到一些Linux中其他裝置樹的API,這些API通常被冠以of_字首
linux裝置樹中的dts與dtsi
1. ARM Device Tree起源 Linus Torvalds在2011年3月17日的ARM Linux郵件列表宣稱“this whole ARM thing is a f*cking pain in the ass”,引發ARM Linux社群的地震
Linux 3.14的裝置樹-ARM架構-4412平臺,最詳細的實戰開發程式碼(三)
本章節主要講解程式碼中如何使用裝置樹對應的介面 視訊講解,請點解如下連結: Linux裝置樹驅動開發視訊-of解析dts節點的API-8 Linux ARM裝置樹驅動開發視訊-程式碼中獲取節點(9) Linux ARM裝置樹驅動開發視訊-程式碼中獲取屬
第二課:linux裝置樹的規範(dts和dtb)
轉載請註明文章地址 http://wiki.100ask.org/Linux_devicetree 第01節_DTS格式 dts檔案通過編譯生成dtb格式檔案 屬性的定義 value取值型別 屬性名=值只有三種取值 第一種 <1 0x3
宋牧春: Linux裝置樹檔案結構與解析深度分析(1)
本文轉載自微信公眾號linuxer 作者簡介 宋牧春,linux核心愛好者,喜歡閱讀各種開原始碼(uboot、linux、ucos、rt-thread等),對於優秀的程式碼框架及其痴迷。現就職於一家手機研發公司,任職Android BSP開發工程師。 正文開始 1. Device Tree簡介 裝置樹就是描
宋牧春: Linux裝置樹檔案結構與解析深度分析(2)
作者簡介 宋牧春,linux核心愛好者,喜歡閱讀各種開原始碼(uboot、linux、ucos、rt-thread等),對於優秀的程式碼框架及其痴迷。現就職於一家手機研發公司,任職Android BSP開發工程師。 正文開始 前情提要: 6. platform_device和device_node
tiny4412學習(四)之移植linux-裝置樹(1)裝置樹基礎知識及GPIO中斷
#include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/gpio.h> #include <linux/of.
Linux裝置驅動模型之platform匯流排深入淺出(加入裝置樹)
在Linux2.6以後的裝置驅動模型中,需關心匯流排,裝置和驅動這三種實體,匯流排將裝置和驅動繫結。在系統每註冊一個裝置的時候,會尋找與之匹配的驅動;相反,在系統每註冊一個驅動的時候,會尋找與之匹配的裝置,而匹配由匯流排完成。 對於依附在USB、PCI、I2C
linux裝置樹之gpio
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/version.h> #include &
linux設備驅動之platform平臺總線工作原理(三)
linux設備和驅動設備為數據,驅動為加工著1、以led-s3c24xx.c為例來分析platform設備和驅動的註冊過程其中關於led的驅動數據結構為:static struct platform_driver s3c24xx_led_driver = { .probe = s3c24xx_led_pr
ARM Linux裝置樹
1 ARM裝置樹 DT: Device Tree FDT: Flattened DeviceTree OF: Open Firmware(開啟韌體,這個字首在後面的api中會用到) DTS : device tree souke DTSI: device tree sourc
Linux裝置樹語法詳解【轉】
轉自:https://www.cnblogs.com/xiaojiang1025/p/6131381.html 概念 Linux核心從3.x開始引入裝置樹的概念,用於實現驅動程式碼與裝置資訊相分離。在裝置樹出現以前,所有關於裝置的具體資訊都要寫在驅動裡,一旦外圍裝置變化,驅動程式碼就要重寫。引入了裝置樹之
linux裝置驅動模型之Kobject、kobj_type、kset
一、sysfs檔案系統簡介: 1、sysfs概述 sysfs檔案系統是核心物件(kobject)、屬性(kobj_type)、及它們相互關係的一種表現。 sysfs非常重要的特徵:使用者可以從sysfs中讀出核心資料,也可以將使用者資料寫入核心。 2、核心結構與sysfs對應關係:
LINUX裝置驅動模型之class
轉自 https://blog.csdn.net/qq_20678703/article/details/52754661 1、LINUX裝置驅動模型中的bus、device、driver,。其中bus:實際的匯流排,device:實際的裝置和介面,而driver:對應存在的驅動。 2、但
04-Linux裝置樹系列-GPIO驅動實踐
1. 前言 GPIO驅動開發可能算是Linux核心裝置驅動開發中最為簡單、最常見的一個方向,對於開發板的按鍵、LED、蜂鳴器、電源控制等模組,可能都是使用GPIO實現的。Linux核心的GPIO子系統在核心不斷的演進過程中進行了多次的重構,本文的第二