Device Tree(一):背景介紹

http://www.wowotech.net/device_model/why-dt.html

一、前言

作爲一個多年耕耘在linux 2.6.23內核的開發者,各個不同項目中各種不同周邊外設驅動的開發以及各種瑣碎的、扯皮的俗務佔據了大部分的時間。當有機會下載3.14的內核並準備學習的時候,突然發現linux kernel對於我似乎變得非常的陌生了,各種新的機制,各種framework、各種新的概念讓我感到閱讀內核代碼變得舉步維艱。 還好,剖析內核的熱情還在,剩下的就交給時間的。首先進入視線的是Device Tree機制,這是和porting內核非常相關的機制,如果想讓將我們的硬件平臺遷移到高版本的內核上,Device Tree是一個必須要掃清的障礙。

我想從下面三個方面來了解Device Tree:

1、爲何要引入Device Tree,這個機制是用來解決什麼問題的?(這是本文的主題)

2、Device Tree的基礎概念(請參考DT基礎概念

3、ARM linux中和Device Tree相關的代碼分析(請參考DT代碼分析

閱讀linux內核代碼就像欣賞冰山,有看得到的美景(各種內核機制及其代碼),也有埋在水面之下看不到的基礎(機制背後的源由和目的)。沉醉於各種內核機制的代碼固然有無限樂趣,但更重要的是注入更多的思考,思考其背後的機理,真正理解軟件抽象。這樣才能舉一反三,並應用在具體的工作和生活中。

本文主要從下面幾個方面闡述爲何ARM linux會引入Device Tree:

1、沒有Device Tree的ARM linux是如何運轉的?

2、混亂的ARM architecture代碼和存在的問題

3、新內核的解決之道

 

二、沒有Device Tree的ARM linux是如何運轉的?

我曾經porting內核到兩個ARM-based的平臺上。一個是小的芯片公司的應用處理器,公司自己購買了CPU core,該CPU core使用ARM兼容的指令集(但不是ARM)加上各種公司自行設計的多媒體外設整合成公司的產品進行銷售。而我的任務就是porting 2.4.18內核到該平臺上。在黑白屏幕的手機時代,那顆AP(application process)支持了彩屏、camera、JPEG硬件加速、2D/3D加速、MMC/SD卡、各種音頻加速(內置DSP)等等特性,功能強大到無法直視。另外一次移植經歷是讓2.6.23內核跑在一個大公司的冷門BP(baseband processor)上。具體porting的方法是很簡單的:

1、自己撰寫一個bootloader並傳遞適當的參數給kernel。除了傳統的command line以及tag list之類的,最重要的是申請一個machine type,當拿到屬於自己項目的machine type ID的時候,當時心情雀躍,似乎自己已經是開源社區的一份子了(其實當時是有意願,或者說有目標是想將大家的代碼併入到linux kernel main line的)。

2、在內核的arch/arm目錄下建立mach-xxx目錄,這個目錄下,放入該SOC的相關代碼,例如中斷controller的代碼,時間相關的代碼,內存映射,睡眠相關的代碼等等。此外,最重要的是建立一個board specific文件,定義一個machine的宏:

MACHINE_START(project name, "xxx公司的xxx硬件平臺")
    .phys_io    = 0x40000000,
    .boot_params    = 0xa0000100,  
    .io_pg_offst    = (io_p2v(0x40000000) >> 18) & 0xfffc,
    .map_io        = xxx_map_io,
    .init_irq    = xxx_init_irq,
    .timer        = &xxx_timer,
    .init_machine    = xxx_init,
MACHINE_END

在xxx_init函數中,一般會加入很多的platform device。因此,伴隨這個board specific文件中是大量的靜態table,描述了各種硬件設備信息。

3、調通了system level的driver(timer,中斷處理,clock等)以及串口terminal之後,linux kernel基本是可以起來了,後續各種driver不斷的添加,直到系統軟件支持所有的硬件。

綜上所述,在linux kernel中支持一個SOC平臺其實是非常簡單的,讓linux kernel在一個特定的平臺上“跑”起來也是非常簡單的,問題的重點是如何優雅的”跑”。

 

三、混亂的ARM architecture代碼和存在的問題

每次正式的linux kernel release之後都會有兩週的merge window,在這個窗口期間,kernel各個部分的維護者都會提交各自的patch,將自己測試穩定的代碼請求併入kernel main line。每到這個時候,Linus就會比較繁忙,他需要從各個內核維護者的分支上取得最新代碼並merge到自己的kernel source tree中。Tony Lindgren,內核OMAP development tree的維護者,發送了一個郵件給Linus,請求提交OMAP平臺代碼修改,並給出了一些細節描述:

1、簡單介紹本次改動

2、關於如何解決merge conficts。有些git mergetool就可以處理,不能處理的,給出了詳細介紹和解決方案

一切都很平常,也給出了足夠的信息,然而,正是這個pull request引發了一場針對ARM linux的內核代碼的爭論。我相信Linus一定是對ARM相關的代碼早就不爽了,ARM的merge工作量較大倒在其次,主要是他認爲ARM很多的代碼都是垃圾,代碼裏面有若干愚蠢的table,而多個人在維護這個table,從而導致了衝突。因此,在處理完OMAP的pull request之後(Linus並非針對OMAP平臺,只是Tony Lindgren撞在槍口上了),他發出了怒吼:

Gaah. Guys, this whole ARM thing is a f*cking pain in the ass.

負責ARM linux開發的Russell King臉上掛不住,進行了反駁:事情沒有那麼嚴重,這次的merge conficts就是OMAP和IMX/MXC之間一點協調的問題,不能抹殺整個ARM linux團隊的努力。其他的各個ARM平臺維護者也加入討論:ARM平臺如何複雜,如何龐大,對於arm linux code我們已經有一些思考,正在進行中……一時間,討論的氣氛有些尖銳,但總體是坦誠和友好的。

對於一件事情,不同層次的人有不同層次的思考。這次爭論涉及的人包括:

1、內核維護者(CPU體系結構無關的代碼)

2、維護ARM系統結構代碼的人

3、維護ARM sub architecture的人(來自各個ARM SOC vendor)

維護ARM sub architecture的人並沒有強烈的使命感,作爲公司的一員,他們最大的目標是以最快的速度支持自己公司的SOC,儘快的佔領市場。這些人的軟件功力未必強,對linux kernel的理解未必深入(有些人可能很強,但是人在江湖身不由己)。在這樣的情況下,很多SOC specific的代碼都是通過copy and paste,然後稍加修改代碼就提交了。此外,各個ARM vendor的SOC family是一長串的CPU list,每個CPU多多少少有些不同,這時候#ifdef就充斥了各個源代碼中,讓ARM mach-和plat-目錄下的代碼有些不忍直視。

作爲維護ARM體系結構的人,其能力不容置疑。以Russell King爲首的team很好的維護了ARM體系結構的代碼。基本上,除了mach-和plat-目錄,其他的目錄中的代碼和目錄組織是很好的。作爲ARM linux的維護者,維護一個不斷有新的SOC加入的CPU architecture code的確是一個挑戰。在Intel X86的架構一統天下的時候,任何想正面攻擊Intel的對手都敗下陣來。想要擊倒巨人(或者說想要和巨人並存)必須另闢蹊徑。ARM的策略有兩個,一個是focus在嵌入式應用上,也就意味着要求低功耗,同時也避免了和Intel的正面對抗。另外一個就是博採衆家之長,採用license IP的方式,讓更多的廠商加入ARM建立的生態系統。毫無疑問,ARM公司是成功的,但是這種模式也給ARM linux的維護者帶來了噩夢。越來越多的芯片廠商加入ARM陣營,越來越多的ARM platform相關的代碼被加入到內核,不同廠商的周邊HW block設計又各不相同……

內核維護者是真正對操作系統內核軟件有深入理解的人,他們往往能站在更高的層次上去觀察問題,發現問題。Linus注意到每次merge window中,ARM的代碼變化大約佔整個ARCH目錄的60%,他認爲這是一個很明顯的符號,意味着ARM linux的代碼可能存在問題。其實,60%這個比率的確很誇張,因爲unicore32是在2.6.39 merge window中第一次全新提交,它的代碼是全新的,但是其代碼變化大約佔整個ARCH目錄的9.6%(需要提及的是unicore32是一箇中國芯)。有些維護ARM linux的人認爲這是CPU市場佔用率的體現,不是問題,直到內核維護者貼出實際的代碼並指出問題所在。內核維護者當然想linux kernel支持更多的硬件平臺,但是他們更願意爲linux kernel制定更長遠的規劃。例如:對於各種繁雜的ARM平臺,用一個kernel image來支持。

經過爭論,確定的問題如下:

1、ARM linux缺少platform(各個ARM sub architecture,或者說各個SOC)之間的協調,導致arm linux的代碼有重複。值得一提的是在本次爭論之前,ARM維護者已經進行了不少相關的工作(例如PM和clock tree)來抽象相同的功能模塊。

2、ARM linux中大量的board specific的源代碼應該踢出kernel,否則這些垃圾代碼和table會影響linux kernel的長期目標。

3、各個sub architecture的維護者直接提交給Linux併入主線的機制缺乏層次。

 

四、新內核的解決之道

針對ARM linux的現狀,最需要解決的是人員問題,也就是如何整合ARM sub architecture(各個ARM Vendor)的資源。因此,內核社區成立了一個ARM sub architecture的team,該team主要負責協調各個ARM廠商的代碼(not ARM core part),Russell King繼續負責ARM core part的代碼。此外,建立一個ARM platform consolidation tree。ARM sub architecture team負責review各個sub architecture維護者提交的代碼,並在ARM platform consolidation tree上維護。在下一個merge window到來的時候,將patch發送給Linus。

針對重複的代碼問題,如果不同的SOC使用了相同的IP block(例如I2C controller),那麼這個driver的code要從各個arch/arm/mach-xxx中獨立出來,變成一個通用的模塊供各個SOC specific的模塊使用。移動到哪個目錄呢?對於I2C或者USB OTG而言,這些HW block的驅動當然應該移動到kernel/drivers目錄。因爲,對於這些外設,可能是in-chip,也可能是off-chip的,但是對於軟件而言,它們是沒有差別的(或者說好的軟件抽象應該掩蓋底層硬件的不同)。對於那些system level的code呢?例如clock control、interrupt control。其實這些也不是ARM-specific,應該屬於linux kernel的核心代碼,應該放到linux/kernel目錄下,屬於core-Linux-kernel frameworks。當然對於ARM平臺,也需要保存一些和framework交互的code,這些code叫做ARM SoC core architecture code。OK,總結一下:

1、ARM的核心代碼仍然保存在arch/arm目錄下

2、ARM SoC core architecture code保存在arch/arm目錄下

3、ARM SOC的周邊外設模塊的驅動保存在drivers目錄下

4、ARM SOC的特定代碼在arch/arm/mach-xxx目錄下

5、ARM SOC board specific的代碼被移除,由Device Tree機制來負責傳遞硬件拓撲和硬件資源信息。

OK,終於來到了Device Tree了。本質上,Device Tree改變了原來用hardcode方式將HW 配置信息嵌入到內核代碼的方法,改用bootloader傳遞一個DB的形式。對於基於ARM CPU的嵌入式系統,我們習慣於針對每一個platform進行內核的編譯。但是隨着ARM在消費類電子上的廣泛應用(甚至桌面系統、服務器系統),我們期望ARM能夠象X86那樣用一個kernel image來支持多個platform。在這種情況下,如果我們認爲kernel是一個black box,那麼其輸入參數應該包括:

1、識別platform的信息

2、runtime的配置參數

3、設備的拓撲結構以及特性

對於嵌入式系統,在系統啓動階段,bootloader會加載內核並將控制權轉交給內核,此外,還需要把上述的三個參數信息傳遞給kernel,以便kernel可以有較大的靈活性。在linux kernel中,Device Tree的設計目標就是如此。

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