設備樹Device Tree詳解

原文:https://www.cnblogs.com/aaronLinux/p/5496559.html

目錄

1. 設備樹(Device  Tree)基本概念及作用

2. 設備樹的組成和使用

  2.1. DTS和DTSI

  2.2. DTC

  2.3. DTB

  2.4. Bootloader

3. 設備樹中dts、dtsi文件的基本語法

  3.1. chosen node

  3.2. aliases node

  3.3. memory node

  3.4.  其他節點

      3.4.1. Reg屬性

      3.4.2. Compatible屬性

      3.4.3. Interrupts屬性

      3.4.4. Ranges屬性

4. DTB相關結構

  4.1. Header

  4.2. 字符串塊

  4.3. memory reserve map

5. 解析DTB的函數及相關數據結構

  5.1. machine_desc結構

  5.2. 設備節點結構體

  5.3. 屬性結構體

  5.4. uboot下的相關結構體

6. DTB加載及解析過程

7. OF的API接口

 

1. 設備樹(Device  Tree)基本概念及作用

在內核源碼中,存在大量對板級細節信息描述的代碼。這些代碼充斥在/arch/arm/plat-xxx和/arch/arm/mach-xxx目錄,對內核而言這些platform設備、resource、i2c_board_info、spi_board_info以及各種硬件的platform_data絕大多數純屬垃圾冗餘代碼。爲了解決這一問題,ARM內核版本3.x之後引入了原先在Power PC等其他體系架構已經使用的Flattened Device Tree。

“A data structure by which bootloaders pass hardware layout to Linux in a device-independent manner, simplifying hardware probing.”開源文檔中對設備樹的描述是,一種描述硬件資源的數據結構,它通過bootloader將硬件資源傳給內核,使得內核和硬件資源描述相對獨立(也就是說*.dtb文件由Bootloader讀入內存,之後由內核來解析)。

Device Tree可以描述的信息包括CPU的數量和類別、內存基地址和大小、總線和橋、外設連接、中斷控制器和中斷使用情況、GPIO控制器和GPIO使用情況、Clock控制器和Clock使用情況。

另外,設備樹對於可熱插拔的熱備不進行具體描述,它只描述用於控制該熱插拔設備的控制器。

設備樹的主要優勢:對於同一SOC的不同主板,只需更換設備樹文件.dtb即可實現不同主板的無差異支持,而無需更換內核文件。

注:要使得3.x之後的內核支持使用設備樹,除了內核編譯時需要打開相對應的選項外,bootloader也需要支持將設備樹的數據結構傳給內核。

2. 設備樹的組成和使用

設備樹包含DTC(device tree compiler),DTS(device tree source和DTB(device tree blob)。其對應關係如圖1-1所示:

 

 

圖1-1 DTS、DTC、DTB之間的關係

2.1. DTS和DTSI

*.dts文件是一種ASCII文本對Device Tree的描述,放置在內核的/arch/arm/boot/dts目錄。一般而言,一個*.dts文件對應一個ARM的machine。

*.dtsi文件作用:由於一個SOC可能有多個不同的電路板,而每個電路板擁有一個 *.dts。這些dts勢必會存在許多共同部分,爲了減少代碼的冗餘,設備樹將這些共同部分提煉保存在*.dtsi文件中,供不同的dts共同使用。*.dtsi的使用方法,類似於C語言的頭文件,在dts文件中需要進行include *.dtsi文件。當然,dtsi本身也支持include 另一個dtsi文件。

2.2. DTC

DTC爲編譯工具,它可以將.dts文件編譯成.dtb文件。DTC的源碼位於內核的scripts/dtc目錄,內核選中CONFIG_OF,編譯內核的時候,主機可執行程序DTC就會被編譯出來。 即scripts/dtc/Makefile中

hostprogs-y := dtc

always := $(hostprogs-y) 

在內核的arch/arm/boot/dts/Makefile中,若選中某種SOC,則與其對應相關的所有dtb文件都將編譯出來。在linux下,make dtbs可單獨編譯dtb。以下截取了TEGRA平臺的一部分。

ifeq ($(CONFIG_OF),y)

dtb-$(CONFIG_ARCH_TEGRA) += tegra20-harmony.dtb \

tegra30-beaver.dtb \

tegra114-dalmore.dtb \

tegra124-ardbeg.dtb 

2.3. DTB

DTC編譯*.dts生成的二進制文件(*.dtb),bootloader在引導內核時,會預先讀取*.dtb到內存,進而由內核解析。

2.4. Bootloader

Bootloader需要將設備樹在內存中的地址傳給內核。在ARM中通過bootm或bootz命令來進行傳遞。bootm [kernel_addr] [initrd_address] [dtb_address],其中kernel_addr爲內核鏡像的地址,initrd爲initrd的地址,dtb_address爲dtb所在的地址。若initrd_address爲空,則用“-”來代替。

3. 設備樹中dts、dtsi文件的基本語法

DTS的基本語法範例,如圖3-1 所示。

它包括一系列節點,以及描述節點的屬性。

“/”爲root節點。在一個.dts文件中,有且僅有一個root節點;在root節點下有“node1”,“node2”子節點,稱root爲“node1”和“node2”的parent節點,除了root節點外,每個節點有且僅有一個parent;其中子節點node1下還存在子節點“child-nodel1”和“child-node2”。

注:如果看過內核/arch/arm/boot/dts目錄的讀者看到這可能有一個疑問。在每個.dsti和.dts中都會存在一個“/”根節點,那麼如果在一個設備樹文件中include一個.dtsi文件,那麼豈不是存在多個“/”根節點了麼。其實不然,編譯器DTC在對.dts進行編譯生成dtb時,會對node進行合併操作,最終生成的dtb只有一個root node。Dtc會進行合併操作這一點從屬性上也可以得到驗證。這個稍後做講解。

在節點的{}裏面是描述該節點的屬性(property),即設備的特性。它的值是多樣化的:

1.它可以是字符串string,如①;也可能是字符串數組string-list,如②

2.它也可以是32 bit unsigned integers,如cell⑧,整形用<>表示

3.它也可以是binary data,如③,十六進制用[]表示

4.它也可能是空,如⑦


圖3-1  DTS的基本語法範例

在/arch/arm/boot/dts/目錄中有一個文件skeleton.dtsi,該文件爲各ARM vendor共用的一些硬件定義信息。以下爲skeleton.dtsi的全部內容。

/ {

#address-cells = <1>;

#size-cells = <1>;

chosen { };

aliases { };

memory { device_type = "memory"; reg = <0 0>; };

};

如上,屬性# address-cells的值爲1,它代表以“/”根節點爲parent的子節點中,reg屬性中存在一個address值;#size-cells的值爲1,它代表以“\” 根節點爲parent的子節點中,reg屬性中存在一個size值。即父節點的# address-cells和#size-cells決定了子節點的address和size的長度;Reg的組織形式爲reg = 

下面列舉例子,對一些典型節點進行具體描述。

3.1. chosen node

chosen {

bootargs = "tegraid=40.0.0.00.00 vmalloc=256M video=tegrafb console=ttyS0,115200n8 earlyprintk";

};

chosen node 主要用來描述由系統指定的runtime parameter,它並沒有描述任何硬件設備節點信息。原先通過tag list傳遞的一些linux kernel運行的參數,可以通過chosen節點來傳遞。如command line可以通過bootargs這個property來傳遞。如果存在chosen node,它的parent節點必須爲“/”根節點。

3.3. aliases node

aliases {

i2c6 = &pca9546_i2c0;

i2c7 = &pca9546_i2c1;

i2c8 = &pca9546_i2c2;

i2c9 = &pca9546_i2c3;

};

aliases node用來定義別名,類似C++中引用。上面是一個在.dtsi中的典型應用,當使用i2c6時,也即使用pca9546_i2c0,使得引用節點變得簡單方便。例:當.dts  include 該.dtsi時,將i2c6的status屬性賦值爲okay,則表明該主板上的pca9546_i2c0處於enable狀態;反之,status賦值爲disabled,則表明該主板上的pca9546_i2c0處於disenable狀態。如下是引用的具體例子:

&i2c6 {--------------這裏&i2c6到底是label還是alias???

status = "okay";

};------------------在*.dtsi中大多默認爲設備爲disable,然後在*.dts中將其enable,進行重寫使能。

3.3. memory node

memory {

device_type = "memory";

reg = <0x00000000 0x20000000>; /* 512 MB */

};

對於memory node,device_type必須爲memory,由之前的描述可以知道該memory node是以0x00000000爲起始地址,以0x20000000爲結束地址的512MB的空間。

一般而言,在.dts中不對memory進行描述,而是通過bootargs中類似521M@0x00000000的方式傳遞給內核。

3.4.  其他節點

由於其他設備節點依據屬性進行描述,具有類似的形式。接下來的部分主要分析各種屬性的含義及作用,並結合相關的例子進行闡述。

3.4.1. Reg屬性

在device node 中,reg是描述memory-mapped IO register的offset和length。子節點的reg屬性address和length長度取決於父節點對應的#address-cells和#size-cells的值。例:

在上述的aips節點中,存在子節點spda。spda中的中reg爲<0x70000000 0x40000 >,其0x700000000爲address,0x40000爲size。這一點在圖3-1下有作介紹。

這裏補充的一點是:設備節點的名稱格式node-name@unit-address,節點名稱用node-name唯一標識,爲一個ASCII字符串。其中@unit-address爲可選項,可以不作描述。unit-address的具體格式和設備掛載在哪個bus上相關。如:cpu的unit-address從0開始編址,以此加1;本例中,aips爲0x70000000。

3.4.2. compatible屬性

在①中,compatible屬性爲string list,用來將設備匹配對應的driver驅動,優先級爲從左向右。本例中spba的驅動優先考慮“fsl,aips-bus”驅動;若沒有“fsl,aips-bus”驅動,則用字符串“simple-bus”來繼續尋找合適的驅動。即compatible實現了原先內核版本3.x之前,platform_device中.name的功能,至於具體的實現方法,本文後面會做講解。

注:對於“/”root節點,它也存在compatible屬性,用來匹配machine type。具體說明將在後面給出。

3.4.3. interrupts屬性

設備節點通過interrupt-parent來指定它所依附的中斷控制器,當節點沒有指定interrupt-parent時,則從parent節點中繼承。上面例子中,root節點的interrupt-parent = <&mic>。這裏使用了引用,即mic引用了②中的inrerrupt-controller @40008000;root節點的子節點並沒有指定interrupt-controller,如ahb、fab,它們均使用從根節點繼承過來的mic,即位於0x40008000的中斷控制器。

若子節點使用到中斷(中斷號、觸發方法等等),則需用interrupt屬性來指定,該屬性的數值長度受中斷控制器中#inrerrupt-controller值③控制,即interrupt屬性<>中數值的個數爲#inrerrupt-controller的值;本例中#inrerrupt-controller=<2>,因而④中interrupts的值爲<0x3d 0>形式,具體每個數值的含義由驅動實現決定。

3.4.4. ranges屬性

ranges屬性爲地址轉換表,這在pcie中使用較爲常見,它表明了該設備在到parent節點中所對用的地址映射關係。ranges格式長度受當前節點#address-cell、parent節點#address-cells、當前節點#size-cell所控制。順序爲ranges=<前節點#address-cell, parent節點#address-cells , 當前節點#size-cell。在本例中,當前節點#address-cell=<1>,對應於⑤中的第一個0x20000000;parent節點#address-cells=<1>,對應於⑤中的第二個0x20000000;當前節點#size-cell=<1>,對應於⑤中的0x30000000。即ahb0節點所佔空間從0x20000000地址開始,對應於父節點的0x20000000地址開始的0x30000000地址空間大小。

注:對於相同名稱的節點,dtc會根據定義的先後順序進行合併,其相同屬性,取後定義的那個。

4. DTB相關結構

本節講下.dts編譯生成的dtb文件,其佈局結構。

DTB由三部分組成:頭(Header)、結構塊(device-tree structure)、字符串塊(string block)。下面將詳細介紹這三部分的內容。

4.1. Header

在\kernel\include\linux\of_fdt.h文件中有相關定義

 

4.2.device-tree structure

 

設備樹結構塊是一個線性化的結構體,是設備樹的主體,以節點的形式保存了主板上的設備信息。

在結構塊中,以宏OF_DT_BEGIN_NODE標誌一個節點的開始,以宏OF_DT_END_NODE標識一個節點的結束,整個結構塊以宏OF_DT_END (0x00000009)結束。在\kernel\include\linux\of_fdt.h中有相關定義,我們把這些宏稱之爲token。

(1)FDT_BEGIN_NODE (0x00000001)。該token描述了一個node的開始位置,緊挨着該token的就是node name(包括unit address)

(2)FDT_END_NODE (0x00000002)。該token描述了一個node的結束位置。

(3)FDT_PROP (0x00000003)。該token描述了一個property的開始位置,該token之後是兩個u32的數據,分別是length和name offset。length表示該property value data的size。name offset表示該屬性字符串在device tree strings block的偏移值。length和name offset之後就是長度爲length具體的屬性值數據。

(4)FDT_NOP (0x00000004)。

(5)FDT_END (0x00000009)。該token標識了一個DTB的結束位置。

一個節點的結構如下:

(1)節點開始標誌:一般爲OF_DT_BEGIN_NODE(0x00000001)。

(2)節點路徑或者節點的單元名(version<3以節點路徑表示,version>=0x10以節點單元名錶示)

(3)填充字段(對齊到四字節)

(4)節點屬性。每個屬性以宏OF_DT_PROP(0x00000003)開始,後面依次爲屬性值的字節長度(4字節)、屬性名稱在字符串塊中的偏移量(4字節)、屬性值和填充(對齊到四字節)。

(5)如果存在子節點,則定義子節點。

(6)節點結束標誌OF_DT_END_NODE(0x00000002)。

4.3. 字符串塊

通過節點的定義知道節點都有若干屬性,而不同的節點的屬性又有大量相同的屬性名稱,因此將這些屬性名稱提取出一張表,當節點需要應用某個屬性名稱時,直接在屬性名字段保存該屬性名稱在字符串塊中的偏移量。

4.4. memory reserve map

這個區域包括了若干的reserve memory描述符。每個reserve memory描述符是由address和size組成。其中address和size都是用U64來描述。

有些系統,我們也許會保留一些memory有特殊用途(例如DTB或者initrd image),或者在有些DSP+ARM的SOC platform上,有些memory被保留用於ARM和DSP進行信息交互。這些保留內存不會進入內存管理系統。

5. 解析DTB的函數及相關數據結構

5.1. machine_desc結構

內核將機器信息記錄爲machine_desc結構體(該定義在/arch/arm/include/asm/mach/arch.h),並保存在_arch_info_begin到_arch_info_end之間(_arch_info_begin,_arch_info_end爲虛擬地址,是編譯內核時指定的,此時mmu還未進行初始化。它其實通過彙編完成地址偏移操作)

machine_desc結構體用宏MACHINE_START進行定義,一般在/arch/arm/子目錄,與板級相關的文件中進行成員函數及變量的賦值。由linker將machine_desc聚集在.arch.info.init節區形成列表。

bootloader引導內核時,ARM寄存器r2會將.dtb的首地址傳給內核,內核根據該地址,解析.dtb中根節點的compatible屬性,將該屬性與內核中預先定義machine_desc結構體的dt_compat成員做匹配,得到最匹配的一個machine_desc。

在代碼中,內核通過在start_kernel->setup_arch中調用setup_machine_fdt來實現上述功能,該函數的具體實現可參見/arch/arm/kernel/devtree.c。 

5.2. 設備節點結構體

1.

記錄節點信息的結構體。.dtb經過解析之後將以device_node列表的形式存儲節點信息。

5.3. 屬性結構體

device_node結構體中的成員結構體,用於描述節點屬性信息。

5.4. uboot下的相關結構體

首先我們看下uboot用於記錄os、initrd、fdt信息的數據結構bootm_headers,其定義在/include/image.h中,這邊截取了其中與dtb相關的一小部分。

fit_hdr_fdt指向DTB設備樹鏡像的頭。

lmb爲uboot下的一種內存管理機制,全稱爲logical memory blocks。用於管理鏡像的內存。lmb所記錄的內存信息最終會傳遞給kernel。這裏對lmb不做展開描述。在/include/lmb.h和/lib/lmb.c中有對lmb的接口和定義的具體描述。有興趣的讀者可以看下,所包含的代碼量不多。

6. DTB加載及解析過程

先從uboot裏的do_bootm出發,根據之前描述,DTB在內存中的地址通過bootm命令進行傳遞。在bootm中,它會根據所傳進來的DTB地址,對DTB所在內存做一系列操作,爲內核解析DTB提供保證。上圖爲對應的函數調用關係圖。

在do_bootm中,主要調用函數爲do_bootm_states,第四個參數爲bootm所要處理的階段和狀態。 

在do_bootm_states中,bootm_start會對lmb進行初始化操作,lmb所管理的物理內存塊有三種方式獲取。起始地址,優先級從上往下:

1. 環境變量“bootm_low”

2. 宏CONFIG_SYS_SDRAM_BASE(在tegra124中爲0x80000000)

3. gd->bd->bi_dram[0].start

大小:

1. 環境變量“bootm_size”

2. gd->bd->bi_dram[0].size

經過初始化之後,這塊內存就歸lmb所管轄。接着,調用bootm_find_os進行kernel鏡像的相關操作,這裏不具體闡述。

還記得之前講過bootm的三個參數麼,第一個參數內核地址已經被bootm_find_os處理,而接下來的兩個參數會在bootm_find_other中執行操作。

首先,bootm_find_other根據第二個參數找到ramdisk的地址,得到ramdisk的鏡像;然後根據第三個參數得到DTB鏡像,同檢查kernel和ramdisk鏡像一樣,檢查DTB鏡像也會進行一系列的校驗工作,如果校驗錯誤,將無法正常啓動內核。另外,uboot在確認DTB鏡像無誤之後,會將該地址保存在環境變量“fdtaddr”中。

接着,uboot會把DTB鏡像reload一次,使得DTB鏡像所在的物理內存歸lmb所管理:①boot_fdt_add_mem_rsv_regions會將原先的內存DTB鏡像所在的內存置爲reserve,保證該段內存不會被其他非法使用,保證接下來的reload數據是正確的;②boot_relocate_fdt會在bootmap區域中申請一塊未被使用的內存,接着將DTB鏡像內容複製到這塊區域(即歸lmb所管理的區域)

注:若環境變量中,指定“fdt_high”參數,則會根據該值,調用lmb_alloc_base函數來分配DTB鏡像reload的地址空間。若分配失敗,則會停止bootm操作。因而,不建議設置fdt_high參數。

接下來,do_bootm會根據內核的類型調用對應的啓動函數。與linux對應的是do_bootm_linux。

① boot_prep_linux

爲啓動後的kernel準備參數

② boot_jump_linux

以上是boot_jump_linux的片段代碼,可以看出:若使用DTB,則原先用來存儲ATAG的寄存器R2,將會用來存儲.dtb鏡像地址。

boot_jump_linux最後將調用kernel_entry,將.dtb鏡像地址傳給內核。

 

下面我們來看下內核的處理部分:

在arch/arm/kernel/head.S中,有這樣一段:

_vet_atags定義在/arch/arm/kernel/head-common.S中,它主要對DTB鏡像做了一個簡單的校驗。

真正解析處理dbt的開始部分,是setup_arch->setup_machine_fdt。這部分的處理在第五部分的machine_mdesc中有提及。

如圖,是setup_machine_fdt中的解析過程。

解析chosen節點將對boot_command_line進行初始化。

解析根節點的{size,address}將對dt_root_size_cells,dt_root_addr_cells進行初始化。爲之後解析memory等其他節點提供依據。

解析memory節點,將會把節點中描述的內存,加入memory的bank。爲之後的內存初始化提供條件。

解析設備樹在函數unflatten_device_tree中完成,它將.dtb解析成device_node結構(第五部分有其定義),並構成單項鍊表,以供OF的API接口使用。

下面主要結合代碼分析:/drivers/of/fdt.c

 

 

 

 

 

 

 

 

 

 

 

 

 

總的歸納爲

① kernel入口處獲取到uboot傳過來的.dtb鏡像的基地址

② 通過early_init_dt_scan()函數來獲取kernel初始化時需要的bootargs和cmd_line等系統引導參數。

③ 調用unflatten_device_tree函數來解析dtb文件,構建一個由device_node結構連接而成的單向鏈表,並使用全局變量of_allnodes保存這個鏈表的頭指針。

④ 內核調用OF的API接口,獲取of_allnodes鏈表信息來初始化內核其他子系統、設備等。

 

7. OF的API接口

OF的接口函數在/drivers/of/目錄下,有of_i2c.c、of_mdio.c、of_mtd.c、Adress.c等等

這裏將列出幾個常用的API接口。

 

1. 用來查找在dtb中的根節點

unsigned long __init of_get_flat_dt_root(void)

 

2. 根據deice_node結構的full_name參數,在全局鏈表of_allnodes中,查找合適的device_node

struct device_node *of_find_node_by_path(const char *path)

例如:

struct device_node *cpus;

cpus=of_find_node_by_path("/cpus");

 

3. 若from=NULL,則在全局鏈表of_allnodes中根據name查找合適的device_node

struct device_node *of_find_node_by_name(struct device_node *from,const char *name)

例如:

struct device_node *np;

np = of_find_node_by_name(NULL,"firewire");

 

4. 根據設備類型查找相應的device_node

struct device_node *of_find_node_by_type(struct device_node *from,const char *type)

例如:

struct device_node *tsi_pci;

tsi_pci= of_find_node_by_type(NULL,"pci");

 

5. 根據compatible字符串查找device_node

struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compatible)

 

6. 根據節點屬性的name查找device_node

struct device_node *of_find_node_with_property(struct device_node *from,const char *prop_name)

 

7. 根據phandle查找device_node

struct device_node *of_find_node_by_phandle(phandle handle)

 

8. 根據alias的name獲得設備id號

int of_alias_get_id(struct device_node *np, const char *stem)

 

9. device node計數增加/減少

struct device_node *of_node_get(struct device_node *node)

void of_node_put(struct device_node *node)

 

10. 根據property結構的name參數,在指定的device node中查找合適的property

struct property *of_find_property(const struct device_node *np,const char *name,int *lenp)

 

11. 根據property結構的name參數,返回該屬性的屬性值

const void *of_get_property(const struct device_node *np, const char *name,int *lenp)

 

12. 根據compat參數與device node的compatible匹配,返回匹配度

int of_device_is_compatible(const struct device_node *device,const char *compat)

 

13. 獲得父節點的device node

struct device_node *of_get_parent(const struct device_node *node)

 

14. 將matches數組中of_device_id結構的name和type與device node的compatible和type匹配,返回匹配度最高的of_device_id結構

const struct of_device_id *of_match_node(const struct of_device_id *matches,const struct device_node *node)

 

15. 根據屬性名propname,讀出屬性值中的第index個u32數值給out_value

int of_property_read_u32_index(const struct device_node *np,const char *propname,u32 index, u32 *out_value)

 

16. 根據屬性名propname,讀出該屬性的數組中sz個屬性值給out_values

int of_property_read_u8_array(const struct device_node *np,const char *propname, u8 *out_values, size_t sz)

int of_property_read_u16_array(const struct device_node *np,const char *propname, u16 *out_values, size_t sz)

int of_property_read_u32_array(const struct device_node *np,const char *propname, u32 *out_values,size_t sz)

 

17. 根據屬性名propname,讀出該屬性的u64屬性值

int of_property_read_u64(const struct device_node *np, const char *propname,u64 *out_value)

 

18. 根據屬性名propname,讀出該屬性的字符串屬性值

int of_property_read_string(struct device_node *np, const char *propname,const char **out_string)

 

19. 根據屬性名propname,讀出該字符串屬性值數組中的第index個字符串

int of_property_read_string_index(struct device_node *np, const char *propname,int index, const char **output)

 

20. 讀取屬性名propname中,字符串屬性值的個數

int of_property_count_strings(struct device_node *np, const char *propname)

 

21. 讀取該設備的第index個irq號

unsigned int irq_of_parse_and_map(struct device_node *dev, int index)

 

22. 讀取該設備的第index個irq號,並填充一個irq資源結構體

int of_irq_to_resource(struct device_node *dev, int index, struct resource *r)

 

23. 獲取該設備的irq個數

int of_irq_count(struct device_node *dev)

 

24. 獲取設備寄存器地址,並填充寄存器資源結構體

int of_address_to_resource(struct device_node *dev, int index,struct resource *r)

const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,unsigned int *flags)

 

25. 獲取經過映射的寄存器虛擬地址

void __iomem *of_iomap(struct device_node *np, int index)

 

24. 根據device_node查找返回該設備對應的platform_device結構

struct platform_device *of_find_device_by_node(struct device_node *np)

 

25. 根據device node,bus id以及父節點創建該設備的platform_device結構

struct platform_device *of_device_alloc(struct device_node *np,const char *bus_id,struct device *parent)

static struct platform_device *of_platform_device_create_pdata(struct device_node *np,const char *bus_id,

void *platform_data,struct device *parent)

 

26. 遍歷of_allnodes中的節點掛接到of_platform_bus_type總線上,由於此時of_platform_bus_type總線上還沒有驅動,所以此時不進行匹配

int of_platform_bus_probe(struct device_node *root,const struct of_device_id *matches,struct device *parent)

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