ARM devicetree的來源
在過去的arm linux中,存在大量的冗余代碼。這些設(shè)備代碼與特定公司的單板啟動或運行細節(jié)緊密耦合,無法被重用或移植。同時,內(nèi)核缺乏引導(dǎo)標準,導(dǎo)致代碼不斷膨脹。最終,由于tony lindgren向linus發(fā)送了一封郵件,請求提交omap平臺代碼的修改,并附上了修改內(nèi)容以及如何解決合并沖突的方法,使得linus怒不可遏地抱怨道:“該死。伙計們,這整個arm的事情真是個討厭的麻煩。”(linus對arm的代碼肯定已經(jīng)忍耐了很久了)。
經(jīng)過討論后,對ARM平臺相關(guān)代碼做出了一些規(guī)范:
- ARM的核心代碼仍然存放在arch/arm目錄下;
- ARM SoC核心架構(gòu)代碼存放在arch/arm目錄下;
- ARM SoC周邊外設(shè)模塊的驅(qū)動存放在drivers目錄下;
- ARM SoC特定的代碼存放在arch/arm/mach-xxx目錄下;
- ARM SoC板級特定的代碼被移除,由Device Tree機制來傳遞硬件拓撲和硬件資源信息。
從本質(zhì)上講,Device Tree改變了以前將硬件設(shè)備配置信息硬編碼到內(nèi)核代碼中的方式,改為使用引導(dǎo)加載程序傳遞一個描述性的數(shù)據(jù)結(jié)構(gòu)。
DTS知識介紹
Arm系統(tǒng)啟動,硬件設(shè)備可以通過DTS(devicetree)或ACPI引導(dǎo)初始化,這里只講DTS方式,ACPI是由BIOS配置。
如上圖,一般來說,arm內(nèi)核通過dts引導(dǎo)啟動,需要內(nèi)核Image、dtb和Filesystem,其中dtb是由dts通過dtc工具生成,里面包括初始化設(shè)備的硬件信息。內(nèi)核Image啟動過程中會解析dtb中內(nèi)容,并根據(jù)信息初始化設(shè)備平臺。這里提一句,dts由雖然由用戶配置,但是配置必須與硬件信息相匹配,否則會出現(xiàn)初始化失敗或設(shè)備部分功能不正常的問題。
DTS描述
Device Tree由一系列被命名的結(jié)點(node)和屬性(Property)組成,而結(jié)點本身可包含子結(jié)點。所謂屬性,其實就是成對出現(xiàn)的name和value。在Device Tree中,可描述的信息包括(原先這些信息大多被hard code到kernel中),CPU的數(shù)量和類別、內(nèi)存基地址和大、timer時鐘、外設(shè)連接、中斷配置、串口等。內(nèi)核在啟動過程中會解析每個node的硬件配置信息,根據(jù)這些信息初始化設(shè)備。
舉例,如下是arm gicv3中斷控制器的節(jié)點配置信息(來源Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt):
gic:?interrupt-controller@2cf00000?{ ?compatible?=?"arm,gic-v3"; ?#interrupt-cells?=?; ?#address-cells?=?; ?#size-cells?=?; ?ranges; ?interrupt-controller; ?reg?=?,??//?GICD ?,?//?GICR ?,?//?GICC ?,?//?GICH ?;??//?GICV ?interrupts?=?; ? ?msi-controller; ?mbi-ranges?=?; ? ?gic-its@2c200000?{ ?compatible?=?"arm,gic-v3-its"; ?msi-controller; ?#msi-cells?=?; ?reg?=?; ?}; ?};
gic: interrupt-controller@2cf00000:表示node節(jié)點信息,interrupt-controller@2cf00000是node節(jié)點的名稱,一般命名規(guī)范是 設(shè)備名@基地址或設(shè)備名,其中冒號前面gic可以看過node的“小名”,后續(xù)關(guān)聯(lián)node節(jié)點可以直接”&gic”;
{}中內(nèi)容:interrupt-controller的屬性,包括:
1)compatible = “arm,gic-v3”; compatible名稱,gicv3驅(qū)動代碼匹配”arm,gic-v3”后才會執(zhí)行初始化probe代碼;
2)#interrupt-cells = ; 表示interrupt由3部分組成,對應(yīng)interrupts = ,分別是中斷類型(1表示PPI),中斷號9和中斷觸發(fā)方式(4表示上升沿觸發(fā));
3)#address-cells = ;
#size-cells = ; 表示地址信息用64位表示,比如reg = 表示GICD的基地址是0x02f000000(其中0x是高32bit,0x2f000000是低32bit),size是0x10000;
4)msi-controller; 表示msi,此字符串含義可以具體查看驅(qū)動代碼,驅(qū)動會get到這個字符串,然后做判斷,有會走A,沒有就跳過。
5)gic-its@2c200000 表示its節(jié)點的配置,gic-v3引入了its來接收SPI中斷;
如上,便是dts的基本配置,更加詳細的介紹可以參考linux內(nèi)核源碼:Documentation/devicetree/,每個硬件設(shè)備配置格式和屬性都有描述。
重要點
一般來說,linux菜鳥級別只要會看dts即可,Linux驅(qū)動開發(fā)才需要掌握dts的每行含義,因為任一行出錯,都可能導(dǎo)致驅(qū)動某項功能失效。下面我列一些經(jīng)常遇到的問題:
1.dts和dtb如何轉(zhuǎn)換
內(nèi)核源碼下scripts/dtc/dtc,dtc工具只有內(nèi)核編譯時才會編譯,所以如果希望得到,先編譯一遍內(nèi)核即可。
轉(zhuǎn)換命令:
dts轉(zhuǎn)dtb:dtc –I dts –O dtb test.dts –o test.dtb
dtb轉(zhuǎn)dts: dtc –I dtb –O dts test.dtb –o test.dts
2.dts中node節(jié)點如何查看,每行又是什么意義
這個問題經(jīng)常遇到,不知道設(shè)備節(jié)點配置的意義,其實每個配置都可以根據(jù)compatible在Documentation/devicetree/中查到說明,只是不搞內(nèi)核不知道這個方法,比如dts中看到:
v2m_serial0:?uart@090000?{ ??compatible?=?"arm,pl011",?"arm,primecell"; ?reg?=?; ?interrupts?=?; ?clocks?=?,?; ?clock-names?=?"uartclk",?"apb_pclk"; ?};
到linux內(nèi)核源碼運行:
cuibixuan@ubuntu:~/git/linux/Documentation/devicetree/bindings$?cd?Documentation/devicetree/bindings/ cuibixuan@ubuntu:~/git/linux/Documentation/devicetree/bindings$?grep?"arm,pl011"?-rn?* clock/hi3660-clock.txt:41:?compatible?=?"arm,pl011",?"arm,primecell"; clock/hi3670-clock.txt:37:?compatible?=?"arm,pl011",?"arm,primecell"; clock/lsi,axm5516-clks.txt:22:?compatible?=?"arm,pl011",?"arm,primecell"; clock/hix5hd2-clock.txt:25:?compatible?=?"arm,pl011",?"arm,primecell"; dma/ste-dma40.txt:130:?compatible?=?"arm,pl011",?"arm,primecell"; dma/snps-dma.txt:65:?compatible?=?"arm,pl011",?"arm,primecell"; pinctrl/axis,artpec6-pinctrl.txt:71:?compatible?=?"arm,pl011",?"arm,primecell"; pinctrl/axis,artpec6-pinctrl.txt:80:?compatible?=?"arm,pl011",?"arm,primecell"; pinctrl/ste,nomadik.txt:141:?compatible?=?"arm,pl011",?"arm,primecell"; serial/pl011.txt:4:-?compatible:?must?be?"arm,primecell",?"arm,pl011",?"zte,zx296702-uart" serial/pl011.txt:44:?compatible?=?"arm,pl011",?"arm,primecell"; vim?serial/pl011.txt
如下圖,有說明,有舉例,很清晰
如果還不懂,或者找不到,那么恭喜你,你要看驅(qū)動代碼了
cuibixuan@ubuntu:~/git/linux$?cd?drivers/tty/serial/ cuibixuan@ubuntu:~/git/linux/drivers/tty/serial$?grep?"arm,pl011"?-rn?* amba-pl011.c:2473:OF_EARLYCON_DECLARE(pl011,?"arm,pl011",?pl011_early_console_setup);
3.內(nèi)核初始化設(shè)備驅(qū)動,根據(jù)compatible來決定是否初始化
compatible的字符串,是驅(qū)動匹配的關(guān)鍵,如果匹配不到,那么就不會初始化。這點設(shè)計非常棒,在編譯Image完畢后,用戶還可以根據(jù)dts選配啟動哪些硬件。
一直有人有疑問,既然內(nèi)核都有config選項來決定了,為什么還要dts來再加一道門禁呢。你可以設(shè)想下,如果內(nèi)核啟動配置了哪些config就初始化哪些功能,那么啟動要多么繁瑣呀,大部分都是你不知道的功能都在啟動,萬一失敗了,還得查看哪里問題,至少編譯一次內(nèi)核。尤其是嵌入式設(shè)備,要精簡,更要達到“我只關(guān)心我配置的設(shè)備”的目的。
好了扯遠了,以上面串口驅(qū)動代碼舉例(提示:驅(qū)動代碼的開頭是probe函數(shù),一般翻到代碼底部即可,上面都是功能的實現(xiàn)),
static?const?Struct?of_device_id?sbsa_uart_of_match[]?=?{ ?{?.compatible?=?"arm,sbsa-uart",?}, ?{}, }; MODULE_DEVICE_TABLE(of,?sbsa_uart_of_match); ? static?struct?platform_driver?arm_sbsa_uart_platform_driver?=?{ ?.probe?=?sbsa_uart_probe, ?.remove??=?sbsa_uart_remove, ?.driver?=?{ ?.name?=?"sbsa-uart", ?.of_match_table?=?of_match_ptr(sbsa_uart_of_match), ?.acpi_match_table?=?ACPI_PTR(sbsa_uart_acpi_match), ?.suppress_bind_attrs?=?IS_BUILTIN(CONFIG_SERIAL_AMBA_PL011), ?}, }; ? … ? static?int?__init?pl011_init(void) { ?printk(KERN_INFO?"Serial:?AMBA?PL011?UART?driver "); ? ?if?(platform_driver_register(&arm_sbsa_uart_platform_driver)) ?pr_warn("could?not?register?SBSA?UART?platform?driver "); ?return?amba_driver_register(&pl011_driver); }
內(nèi)核通過platform_driver_register()來注冊設(shè)備,arm_sbsa_uart_platform_driver是初始化成struct platform_driver的結(jié)構(gòu)體,結(jié)構(gòu)體指定了設(shè)備的probe,remove等鉤子函數(shù),.driver記錄設(shè)備的name,.compatible = “arm,sbsa-uart”(of_match_table來匹配),這里多提一句,.of_match_table是dts啟動匹配字符串”arm,sbsa-uart”,.acpi_match_table是ACPI啟動匹配PTR。
如上,如果dts中node節(jié)點有compatible帶”arm,sbsa-uart”,那么就會執(zhí)行指定的鉤子函數(shù).probe = sbsa_uart_probe,函數(shù)再進行node節(jié)點其他參數(shù)的解析(說是解析,就是get字符串或數(shù)值,在進行對應(yīng)初始化或讀寫寄存器)。
4.reg=和interrupts=里面數(shù)值代表什么?
reg和interrupt數(shù)值都有具體的含義,上文提到:
#interrupt-cells = ; 表示interrupt由3部分組成,對應(yīng)interrupts = ,分別是中斷類型(1表示PPI),中斷號9和中斷觸發(fā)方式(4表示高電平觸發(fā));
這里再補充一下:
interrupts = ,分別是
中斷類型:0表示SPI,1表示PPI
中斷號9,其中PPI是[0-15],SPI范圍[32-1019]
中斷觸發(fā)方式:
1 = low-to-high edge triggered
2 = high-to-low edge triggered
4 = active high edge triggered
8 = active low edge triggered
恩,翻譯一下就時上升沿觸發(fā)、下降沿觸發(fā)、高電平觸發(fā)、低電平觸發(fā)。
#address-cells = ;
#size-cells = ; 表示地址信息用64位表示,比如reg = 表示GICD的基地址是0x02f000000(其中0x是高32bit,0x2f000000是第32bit),size是0x10000;
舉例, = 0x12f000000,= 0x100000001
5.dts支持include
對于可復(fù)用的描述節(jié)點,支持以include方式被多個dts包含,可放在dtsi文件,在dts中以
#include “uart.dtsi”包含使用。
dts文件:Foundation-platform.dts
默認變量
一般dts首個{}前面的信息表示全局默認變量,意思即node無特殊配置,則默認采用這里的配置
????????#address-cells?=?;?//地址長度64bit ????????#size-cells?=?;??//size長度32bit ????????model?=?"V2P-AARCH64";?//model名稱 ????????compatible?=?"arm,vexpress,v2p-aarch64",?"arm,vexpress"; ????????interrupt-parent?=?;?//中斷parent是gic
CPU配置
????????cpus?{ ????????????????#address-cells?=?;? ????????????????#size-cells?=?; //?配置cpu0-cpu3的信息,最終啟動4核 ????????????????cpu@0?{ ????????????????????????device_type?=?"cpu";??//設(shè)備類型:cpu ????????????????????????compatible?=?"arm,armv8";?//表示armv8的cpu ????????????????????????reg?=?;?//cpu信息 ????????????????????????enable-method?=?"spin-table";?//采用spintable方式拉起從核 ????????????????????????cpu-release-addr?=?;?//cpu初啟動的pc指針存放位置 ????????????????}; ????????????????cpu@1?{ ????????????????????????device_type?=?"cpu"; ????????????????????????compatible?=?"arm,armv8"; ????????????????????????reg?=?; ????????????????????????enable-method?=?"spin-table"; ????????????????????????cpu-release-addr?=?; ????????????????}; ????????????????cpu@2?{ ????????????????????????device_type?=?"cpu"; ????????????????????????compatible?=?"arm,armv8"; ????????????????????????reg?=?; ????????????????????????enable-method?=?"spin-table"; ????????????????????????cpu-release-addr?=?; ????????????????}; ????????????????cpu@3?{ ????????????????????????device_type?=?"cpu"; ????????????????????????compatible?=?"arm,armv8"; ????????????????????????reg?=?; ????????????????????????enable-method?=?"spin-table"; ????????????????????????cpu-release-addr?=?; ????????????????}; ????????};
內(nèi)存配置
????????memory@80000000?{ ????????????????device_type?=?"memory"; //此節(jié)點配置內(nèi)存,內(nèi)存2塊: //起始地址:0x80000000,?長度0x80000000; //起始地址:0x880000000,?長度0x80000000; ????????????????reg?=?; ????????};
中斷控制器
????????gic:?interrupt-controller@2c001000?{ //?中斷控制器使用?cortex?a15,gic ????????????????compatible?=?"arm,cortex-a15-gic"; ????????????????#interrupt-cells?=?; ????????????????#address-cells?=?; ????????????????interrupt-controller; //?GICD,GICH等信息,具體參考Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt //-?reg?:?Specifies?base?physical?address(s)?and?size?of?the?GIC?registers.?The ??first?region?is?the?GIC?distributor?register?base?and?size.?The?2nd?region?is ??the?GIC?cpu?interface?register?base?and?size. ????????????????reg?=?, ??????????????????????, ??????????????????????, ??????????????????????; //?虛擬化使用,-?interrupts?:?VGIC?maintenance?interrupt. //*?GIC?virtualization?extensions?(VGIC) ????????????????interrupts?=?; ????????};
PMU
????????pmu?{ //?使用armv8?pmuv3 ????????????????compatible?=?"arm,armv8-pmuv3"; //?pmu的中斷配置,這段配置完畢,即可以使用arm?cpu的pmu功能 ????????????????interrupts?=?; ????????};
Timer:
????????timer?{ //定時器配置 ????????????????compatible?=?"arm,armv8-timer"; ????????????????interrupts?=?, ?????????????????????????????, ?????????????????????????????, ?????????????????????????????; ????????????????clock-frequency?=?; ????????};
串口配置
????????????????iofpga@3,00000000?{ ????????????????????????compatible?=?"arm,amba-bus",?"simple-bus"; ????????????????????????#address-cells?=?; ????????????????????????#size-cells?=?; ????????????????????????ranges?=?; ????????????????????????sysreg@010000?{ ????????????????????????????????compatible?=?"arm,vexpress-sysreg"; ????????????????????????????????reg?=?; ????????????????????????}; ????????????????????????v2m_serial0:?uart@090000?{ ????????????????????????????????compatible?=?"arm,pl011",?"arm,primecell";?//串口驅(qū)動 ????????????????????????????????reg?=?;?//串口基地址和長度 ????????????????????????????????interrupts?=?;??//串口中斷號 ????????????????????????????????clocks?=?,?;?//串口波特率 ????????????????????????????????clock-names?=?"uartclk",?"apb_pclk"; ????????????????????????}; ????????????????????????v2m_serial1:?uart@0a0000?{ ????????????????????????????????compatible?=?"arm,pl011",?"arm,primecell"; ????????????????????????????????reg?=?; ????????????????????????????????interrupts?=?; ????????????????????????????????clocks?=?,?; ????????????????????????????????clock-names?=?"uartclk",?"apb_pclk"; ????????????????????????}; ????????????????????????v2m_serial2:?uart@0b0000?{ ????????????????????????????????compatible?=?"arm,pl011",?"arm,primecell"; ????????????????????????????????reg?=?; ????????????????????????????????interrupts?=?; ????????????????????????????????clocks?=?,?; ????????????????????????????????clock-names?=?"uartclk",?"apb_pclk"; ????????????????????????}; ????????????????????????v2m_serial3:?uart@0c0000?{ ????????????????????????????????compatible?=?"arm,pl011",?"arm,primecell"; ????????????????????????????????reg?=?; ????????????????????????????????interrupts?=?; ????????????????????????????????clocks?=?,?; ????????????????????????????????clock-names?=?"uartclk",?"apb_pclk"; ????????????????????????}; ????????????????????????virtio_block@0130000?{ ????????????????????????????????compatible?=?"virtio,mmio"; ????????????????????????????????reg?=?; ????????????????????????????????interrupts?=?; ????????????????????????}; ????????????????}; ????????}; ????????/*?chosen?*/ };
從device_node中獲取信息:
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);
從設(shè)備結(jié)點np中讀取屬性名為propname,類型為8、16、32、位整型數(shù)組的屬性值,并放入out_values,sz指明了要讀取的個數(shù)。
static?inline?int?of_property_read_u8(const?struct?device_node?*np,const?char?*propname,u8?*out_value)? static?inline?int?of_property_read_u16(const?struct?device_node?*np,const?char?*propname,u8?*out_value)? static?inline?int?of_property_read_u32(const?struct?device_node?*np,const?char?*propname,u8?*out_value)
從設(shè)備結(jié)點np中讀取屬性名為propname,類型為8、16、32位的屬性值,并放入out_values。實際上這里調(diào)用的就是sz為1的XXX_array函數(shù)。
int?of_property_read_u32_index(const?struct?device_node?*np,const?char*propname,u32?index,?u32?*out_value)
從設(shè)備結(jié)點np中讀取屬性名為propname的屬性值中第index個u32數(shù)值給out_value
int?of_property_read_u64(conststruct?device_node?*np,?const?char?*propname,u64?*out_value)
從設(shè)備結(jié)點np中讀取屬性名為propname,類型為64位的屬性值,并放入out_values
int?of_property_read_string(struct?device_node?*np,?const?char?*propname,const?char**out_string)
從設(shè)備結(jié)點np中讀取屬性名為propname的字符串型屬性值
int?of_property_read_string_index(struct?device_node?*np,?const?char?*propname,intindex,?const?char?**output)
從設(shè)備結(jié)點np中讀取屬性名為propname的字符串型屬性值數(shù)組中的第index個字符串
int?of_property_count_strings(struct?device_node?*np,?const?char?*propname)
從設(shè)備結(jié)點np中讀取屬性名為propname的字符串型屬性值的個數(shù)
unsigned?int?irq_of_parse_and_map(struct?device_node?*dev,?int?index)
從設(shè)備節(jié)點dev中讀取第index個irq號
int?of_irq_to_Resource(struct?device_node?*dev,?int?index,?struct?resource?*r)
從設(shè)備節(jié)點dev中讀取第index個irq號,并填充一個irq資源結(jié)構(gòu)體
int?of_irq_count(struct?device_node?*dev)
獲取設(shè)備節(jié)點dev的irq個數(shù)
static?inline?bool?of_property_read_bool(const?struct?device_node?*np,const?char?*propname);
如果設(shè)備結(jié)點np含有propname屬性,則返回true,否則返回false。一般用于檢查空屬性是否存在。
struct?property*?of_find_property(const?struct?device_node?*np,const?char?*name,int?*lenp)
根據(jù)name參數(shù),在指定的設(shè)備結(jié)點np中查找匹配的property,并返回這個property
const?void?*?of_get_property(const?struct?device_node?*np,?const?char?*name,int?*lenp)
根據(jù)name參數(shù),在指定的設(shè)備結(jié)點np中查找匹配的property,并返回這個property的屬性值
struct?device_node*?of_get_parent(const?struct?device_node?*node)
獲得node節(jié)點的父節(jié)點的device node
int?of_device_is_compatible(const?struct?device_node?*device,const?char?*compat);
判斷設(shè)備結(jié)點device的compatible屬性是否包含compat指定的字符串
從of_allnodes中查找信息:
struct?device_node*?of_find_node_by_path(const?char?*path) 根據(jù)路徑參數(shù),在全局鏈表of_allnodes中,查找匹配的device_node
struct?device_node*?of_find_node_by_name(struct?device_node?*from,const?char?*name) 則根據(jù)name在全局鏈表of_allnodes中查找匹配的device_node,若from=NULL表示從頭開始查找
struct?device_node*?of_find_node_by_type(struct?device_node?*from,const?char?*type)
根據(jù)設(shè)備類型在全局鏈表of_allnodes中查找匹配的device_node
struct?device_node?*?of_find_compatible_node(struct?device_node?*from,?const?char*type,?const?char,*compatible);
根據(jù)compatible的屬性值在全局鏈表of_allnodes中查找匹配的device_node,大多數(shù)情況下,from、type為NULL。
struct?device_node*?of_find_node_with_property(struct?device_node?*from,const?char?*prop_name)
根據(jù)節(jié)點屬性的name在全局鏈表of_allnodes中查找匹配的device_node
struct?device_node*?of_find_node_by_phandle(phandle?handle)
根據(jù)phandle在全局鏈表of_allnodes中查找匹配的device_node
雜:
void?__iomem*?of_iomap(struct?device_node?*node,?int?index);
通過設(shè)備結(jié)點直接進行設(shè)備內(nèi)存區(qū)間的 ioremap(),index是內(nèi)存段的索引。若設(shè)備結(jié)點的reg屬性有多段,可通過index標示要ioremap的是哪一段,只有1段的情況,index為0
unsigned?long?__init?of_get_flat_dt_root(void)
用來查找在dtb中的根節(jié)點,好像返回的都是0
int?of_alias_get_id(struct?device_node?*np,?const?char?*stem)
獲取節(jié)點np對應(yīng)的aliasid號
struct?device_node*?of_node_get(struct?device_node?*node) void?of_node_put(struct?device_node?*node)
device node計數(shù)增加/減少
const?struct?of_device_id*?of_match_node(const?struct?of_device_id?*matches,const?struct?device_node*node)
將matches數(shù)組中of_device_id結(jié)構(gòu)的name和type與device node的compatible和type匹配,返回匹配度最高的of_device_id結(jié)構(gòu)
platform_device和resource相關(guān):
int?of_address_to_resource(struct?device_node?*dev,?int?index,struct?resource?*r)
根據(jù)設(shè)備節(jié)點dev的reg屬性值,填充資源結(jié)構(gòu)體r。Index參數(shù)指明了使用reg屬性中第幾個屬性值,一般設(shè)置為0,表示第一個。
struct?platform_device*?of_device_alloc(struct?device_node?*np,const?char?*bus_id,struct?device?*parent)
根據(jù)device node,bus_id以及父節(jié)點創(chuàng)建該設(shè)備的platform_device結(jié)構(gòu),同時會初始化它的resource成員。
int?of_platform_bus_probe(struct?device_node?*root,const?struct?of_device_id?*matches,struct?device?*parent)
遍歷of_allnodes中的節(jié)點掛接到of_platform_bus_type總線上,由于此時of_platform_bus_type總線上還沒有驅(qū)動,所以此時不進行匹配
int?of_platform_populate(struct?device_node?*root,const?struct?of_device_id?*matches,const?struct?of_dev_auxdata?*lookup,struct?device?*parent)
遍歷of_allnodes中的所有節(jié)點,生成并初始化所以節(jié)點的platform_device結(jié)構(gòu)
struct?platform_device*?of_find_device_by_node(struct?device_node?*np)
根據(jù)device_node查找返回該設(shè)備對應(yīng)的platform_device結(jié)構(gòu)