最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

STM32MP157 Linux系統(tǒng)移植開發(fā)篇3:設備樹詳解

2021-08-04 10:04 作者:華清遠見研發(fā)中心  | 我要投稿


本文章為《STM32MP157 Linux系統(tǒng)移植開發(fā)篇》系列中的一篇,筆者使用的開發(fā)平臺為華清遠見FS-MP1A開發(fā)板(STM32MP157開發(fā)板)。stm32mp157是ARM雙核,2個A7核,1個M4核,A7核上可以跑Linux操作系統(tǒng),M4核上可以跑FreeRTOS、RT-Thread等實時操作系統(tǒng),STM32MP157開發(fā)板所以既可以學嵌入式linux,也可以學stm32單片機。


針對FS-MP1A開發(fā)板,除了Linux系統(tǒng)移植篇外,還包括其他多系列教程,包括Cortex-A7開發(fā)篇、Cortex-M4開發(fā)篇、擴展板驅動移植篇、Linux應用開發(fā)篇、FreeRTOS系統(tǒng)移植篇、Linux驅動開發(fā)篇、硬件設計篇、人工智能機器視覺篇、Qt應用編程篇、Qt綜合項目實戰(zhàn)篇等。歡迎關注,更多stm32mp157開發(fā)教程及視頻,可加技術交流Q群459754978,感謝關注。


關于FS-MP1A開發(fā)板:

手機淘寶分享碼:復制本行文字打開手淘?T4FPXn3YYJ2?

鏈接:https://item.taobao.com/item.htm?id=622457259672


設備樹詳解

簡介

在傳統(tǒng)Linux內(nèi)核中,ARM架構的板極硬件細節(jié)過多地被硬編碼在arch/arm/plat-xxx和arch/arm/mach-xxx,比如板上的platform設備、resource、i2c_board_info、spi_board_info以及各種硬件的platform_data,這些板級細節(jié)代碼對內(nèi)核來講只不過是垃圾代碼。而采用Device Tree后,許多硬件的細節(jié)可以直接透過它傳遞給Linux,而不再需要在kernel中進行大量的冗余編碼。導致ARM的merge工作量較大。

之后經(jīng)過一些討論,對ARM平臺的相關code做出如下相關規(guī)范調整,這個也正是引入DTS的原因。

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的代碼被移除,由DeviceTree機制來負責傳遞硬件拓撲和硬件資源信息。

本質上,Device Tree改變了原來用code方式將硬件配置信息嵌入到內(nèi)核代碼的方法,改用bootloader傳遞一個DB的形式。對于嵌入式系統(tǒng),在系統(tǒng)啟動階段,bootloader會加載內(nèi)核并將控制權轉交給內(nèi)核,此外,還需要把上述的三個參數(shù)信息傳遞給kernel,以便kernel可以有較大的靈活性。在linux kernel中,Device Tree的設計目標就是如此。

在devie tree中,可描述的信息包括:

1、CPU的數(shù)量和類別

2、內(nèi)存基地址和大小

3、總線和橋

4、外設連接

5、中斷控制器和中斷的使用情況

6、GPIO控制器和GPIO使用情況

7、clock控制器和clock使用情況

它基本就是一棵電路板上的CPU、總線、設備組成的樹,Bootloader會將這棵樹傳遞給內(nèi)核,然后內(nèi)核來識別這棵樹,并根據(jù)它展開出Linux內(nèi)核中的platform_device、i2c_client、spi_device等設備,而這些設備用到的內(nèi)存、IRQ等資源,也被傳遞給內(nèi)核,內(nèi)核會將這些資源綁定給展開的相應設備。

Linux內(nèi)核從3.x開始引入設備樹的概念,用于實現(xiàn)驅動代碼與設備信息相分離。在設備樹出現(xiàn)以前,所有關于設備的具體信息都要寫在驅動里,一旦外圍設備變化,驅動代碼就要重寫。引入了設備樹之后,驅動代碼只負責處理驅動的邏輯,而關于設備的具體信息存放到設備樹文件中,這樣,如果只是硬件接口信息的變化而沒有驅動邏輯的變化,驅動開發(fā)者只需要修改設備樹文件信息,不需要改寫驅動代碼。比如在ARM Linux內(nèi),一個.dts(device tree source)文件對應一個ARM的machine,一般放置在內(nèi)核的"arch/arm/boot/dts/"目錄內(nèi),比如stmp1a-dk1參考板的板級設備樹文件就是"arch/arm/boot/dts/ stm32mp157a-dk1.dts"。這個文件可以通過make dtbs命令編譯成二進制的.dtb文件供內(nèi)核驅動使用。

基于同樣的軟件分層設計的思想,由于一個SoC可能對應多個machine,如果每個machine的設備樹都寫成一個完全獨立的.dts文件,那么勢必相當一些.dts文件有重復的部分,為了解決這個問題,Linux設備樹目錄把一個SoC公用的部分或者多個machine共同的部分提煉為相應的.dtsi文件。這樣每個.dts就只有自己差異的部分,公有的部分只需要"include"相應的.dtsi文件, 這樣就是整個設備樹的管理更加有序。我這里用Linux4.19.94源碼自帶的goodix touchscreen觸摸芯片為例來分析設備樹的使用和移植。這個觸摸芯片的設備樹節(jié)點信息在"
Documentation/devicetree/bindings/input/touchscreen/goodix.txt"有詳細說明,其驅動源碼是"?drivers/input/touchscreen/goodix.c"。

基礎知識介紹

  • dts

硬件的相應信息都會寫在.dts為后綴的文件中,每一款硬件可以單獨寫一份例如stm32mp157a-dk1.dts,一般在Linux源碼中存在大量的dts文件,對于arm架構可以在arch/arm/boot/dts找到相應的dts,一個dts文件對應一個ARM的machie。

  • dtsi

值得一提的是,對于一些相同的dts配置可以抽象到dtsi文件中,然后類似于C語言的方式可以include到dts文件中,對于同一個節(jié)點的設置情況,dts中的配置會覆蓋dtsi中的配置。

  • dtc

dtc是編譯dts的工具,可以在Ubuntu系統(tǒng)上通過指令apt-get install device-tree-compiler安裝dtc工具,不過在內(nèi)核源碼scripts/dtc路徑下已經(jīng)包含了dtc工具;

  • dtb

dtb(Device Tree Blob),dts經(jīng)過dtc編譯之后會得到dtb文件,dtb通過Bootloader引導程序加載到內(nèi)核。所以Bootloader需要支持設備樹才行;Kernel也需要加入設備樹的支持;

DTS結構

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22


/dts-v1/;
/ {
node1 {
a-string-property =?"A string";
a-string-list-property =?"first string",?"second string";
// hex is implied in byte arrays. no '0x' prefix is required
a-byte-data-property = [01?23?34?56];
child-node1 {
first-child-property;
second-child-property = <1>;
a-string-property =?"Hello, world";
};
child-node2 {
};
};
node2 {
an-empty-property;
a-cell-property = <1?2?3?4>;?/* each number (cell) is a uint32 */
child-node1 {
};
};
};

device tree的基本單元是node。這些node被組織成樹狀結構,除了root node,每個node都只有一個parent。一個device tree文件中只能有一個root node。每個node中包含了若干的property/value來描述該node的一些特性。每個node用節(jié)點名字(node name)標識,節(jié)點名字的格式是node-name@unit-address。如果該node沒有reg屬性(后面會描述這個property),那么該節(jié)點名字中必須不能包括@和unit-address。unit-address的具體格式是和設備掛在那個bus上相關。例如對于cpu,其unit-address就是從0開始編址,以此加一。而具體的設備,例如以太網(wǎng)控制器,其unit-address就是寄存器地址。root node的node name是確定的,必須是“/”。

也就是說設備樹源文件的結構為:

  • 1個root節(jié)點”/”;

  • root節(jié)點下面含一系列子節(jié)點,“node1” and “node2”

  • 節(jié)點node1和下又含有一系列子節(jié)點,“child-node1” and “child-node2”

  • 各個節(jié)點都有一系列屬性

    • 這些屬性可能為空,如an-empty-property

    • 可能為字符串,如a-string-property

    • 可能為字符串樹組,如a-string-list-property

    • 可能為Cells(由u32整數(shù)組成),如second-child-property

DTS語法介紹

了解了基本的device tree的結構后,我們總要把這些結構體現(xiàn)在device tree source code上來。在linux kernel中,擴展名是dts的文件就是描述硬件信息的device tree source file,在dts文件中,一個node被定義成如下格式:

1
2
3
4


[label:] node-name[@unit-address] {
[properties definitions]
[child nodes]
}

“[]”表示option,因此可以定義一個只有node name的空節(jié)點,label方便在dts文件中引用。

基本數(shù)據(jù)類型:

  • text string(以null結束),以雙引號括起來,如:string-property = “a string”;

  • cells 是32位無符號整形數(shù),以尖括號括起來,如:cell-property = <0xbeef 123 0xabcd1234>;

  • binary data 以方括號括起來,如:binary-property = [0x01 0x23 0x45 0x67];

  • 不同類型數(shù)據(jù)可以在同一個屬性中存在,以逗號分格,如:mixed-property = “a string”, [0x01 0x23 0x45 0x67],<0x12345678>;

  • 多個字符串組成的列表也使用逗號分格,如:string-list = “red fish”,”blue fish”;

dts的組成

compatible

每一個dts文件都是由一個root的根節(jié)點組成,內(nèi)核通過根節(jié)點“/”的兼容性即可判斷它啟動的是什么設備,其代碼結構如下

C++ Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15


/ {
model =?"HQYJ FS-MP1A Discovery Board";
compatible =?"st,stm32mp157a-dk1",?"st,stm32mp157",?"hqyj,fsmp1a";
aliases {
ethernet0 = eernet0;
serial0 = &uart4;
serial5 = &usart3;
};
chosen {
stdout-path =?"serial0:115200n8";
};
... ...
};

model屬性值是,它指定制造商的設備型號。推薦的格式是:“manufacturer,model”,其中manufacturer是一個字符串描述制造商的名稱,而型號指定型號。

compatible屬性值是,指定了系統(tǒng)的名稱,是一個字符串列表,它包含了一個“<制造商>,<型號>”形式的字符串。重要的是要指定一個確切的設備,并且包括制造商的名字,以避免命名空間沖突。

chosen 節(jié)點不代表一個真正的設備,但功能與在固件和操作系統(tǒng)間傳遞數(shù)據(jù)的地點一樣,如根參數(shù),取代以前bootloader的啟動參數(shù),控制臺的輸入輸出參數(shù)等。

#address-cells和#size-cells

1
2
3
4
5
6
7
8
9


port {
#address-cells = <1>;
#size-cells = <0>;
ltdc_ep0_out: endpoint@0 {
reg = <0>;
remote-endpoint = <&sii9022_in>;
};
};

#address-cells = <1>: 基地址、片選號等絕對起始地址所占字長,單位uint32。

#size-cells = <1>: 長度所占字長,單位uint32。

CPU addressing

在討論尋址時,CPU節(jié)點代表了最簡單的情況。 每個CPU都分配有一個唯一的ID,并且沒有與CPU ID相關聯(lián)的大小。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24


cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu0: cpu@0 {
compatible =?"arm,cortex-a7";
device_type =?"cpu";
reg = <0>;
clocks = <&rcc CK_MPU>;
clock-names =?"cpu";
operating-points-v2 = <&cpu0_opp_table>;
nvmem-cells = <&part_number_otp>;
nvmem-cell-names =?"part_number";
};
cpu1: cpu@1 {
compatible =?"arm,cortex-a7";
device_type =?"cpu";
reg = <1>;
clocks = <&rcc CK_MPU>;
clock-names =?"cpu";
operating-points-v2 = <&cpu0_opp_table>;
};
};

在cpus節(jié)點,#address-cells被設置成了1,#size-cells被設置成了0。這是說子reg值是單獨的uint32,它用無大小字段表示地址。在此情況下,這兩個cpu分配到的地址為0和1。cpu節(jié)點的#size-cells是0因為每個cpu只分配到了一個單獨的地址。

你仍然需要注意reg值班需要與節(jié)點名的值相匹配。按照慣例,如果一個節(jié)點有一個reg屬性,那么這個節(jié)點名稱必須包括unit-address,這是reg屬性的第一個address值。

Memory Mapped Devices

與在cpu節(jié)點中單獨的address值不同,內(nèi)存映射設備被分配了一系列將要響應的地址,因此不僅需要包含內(nèi)存的基地址而且還需要映射地址的長度,因此需要使用#size-cells用來表示在每個子reg元組中長度字段的大小。在以下示例中,每個address值為1 cell(32 bits),每個長度值也是1 cell,這在32 bit系統(tǒng)是比較典型的。64 bit設備也許會為#address-cells和#size-cells使用數(shù)值2,在device tree中獲取64 bit addressing。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32


/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
...
timers2: timer@40000000 {
compatible =?"st,stm32-timers";
reg = <0x40000000 0x400>;
};
timers3: timer@40001000 {
compatible =?"st,stm32-timers";
reg = <0x40001000 0x400>;
};
spi2: spi@4000b000 {
compatible =?"st,stm32h7-spi";
reg = <0x4000b000 0x400>;
};
uart7: serial@40018000 {
compatible =?"st,stm32h7-uart";
reg = <0x40018000 0x400>;
};
sai1: sai@4400a000 {
compatible =?"st,stm32h7-sai";
reg = <0x4400a000 0x4>;
};
...
};

Non Memory Mapped Devices

處理器總線的其它設備為非內(nèi)存映射設備。他們有地址范圍,但不能被CPU直接尋址。母設備的驅動程序將代替CPU進行間接訪問。以i2c設備為例,每個設備都分配了一個地址,但沒有長度或范圍與之相匹配。這與CPU地址分配很相似。

C++ Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26


etf: etf@50092000 {
compatible =?"arm,coresight-tmc",?"arm,primecell";
reg = <0x50092000 0x1000>;
clocks = <&rcc CK_TRACE>;
clock-names =?"apb_pclk";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
etf_in_port: endpoint {
slave-mode;
remote-endpoint = <&funnel_out_port0>;
};
};
port@1 {
reg = <0>;
etf_out_port: endpoint {
remote-endpoint = <&tpiu_in_port>;
};
};
};
};

Ranges (Address Translation)

前面已經(jīng)討論過如何向設備分配地址,但此時這些地址只是本地設備節(jié)點,還沒有說明如何從那些地址里映射到cpu可以使用的地址。根節(jié)點經(jīng)常描述地址空間的CPU視圖。根節(jié)點的子節(jié)點已經(jīng)使用了CPU的address domain,所以不需要任何明確的映射。例如,serial@101f0000設備被直接分配了地址0x101f0000。

根節(jié)點的非直接子節(jié)點是無法使用CPU的address domain的。為了在deivce tree獲取內(nèi)存映射地址必須指定如何從一個域名將地址轉換到另一個。Ranges屬性就用于此目的。以下是添加了ranges屬性的device tree示例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21


m4_rproc: m4@0 {
compatible =?"st,stm32mp1-rproc";
#address-cells = <1>;
#size-cells = <1>;
ranges = <0x00000000 0x38000000 0x10000>,
<0x30000000 0x30000000 0x60000>,
<0x10000000 0x10000000 0x60000>;
resets = <&rcc MCU_R>;
reset-names =?"mcu_rst";
st,syscfg-pdds = <&pwr 0x014 0x1>;
st,syscfg-holdboot = <&rcc 0x10C 0x1>;
st,syscfg-tz = <&rcc 0x000 0x1>;
st,syscfg-rsc-tbl = <&tamp 0x144 0xFFFFFFFF>;
status =?"disabled";
m4_system_resources {
compatible =?"rproc-srm-core";
status =?"disabled";
};
};

ranges是一個地址轉換列表。每個輸入ranges表格的是包含子地址的元組,母地址和子地址空間的范圍大小。每個字段的大小都由獲取的子地址的#address-cells值,母地址的#address-cell值和子地址的#size-cells值而定。以外部總線為例,子地址是2 cells,母地址是1 cell,大小也為1 cell。

status

device tree中的status標識了設備的狀態(tài),使用status可以去禁止設備或者啟用設備,看下設備樹規(guī)范中的status可選值。

描述

“okay”

表示設備正在運行

“disabled”

表示該設備目前尚未運行,但將來可能會運行

“fail”

表示設備無法運行。在設備中檢測到嚴重錯誤,確實如此沒有修理就不可能投入運營

“fail-sss”

表示設備無法運行。在設備中檢測到嚴重錯誤,它是沒有修理就不可能投入運營。值的sss部分特定于設備并指示檢測到的錯誤情況。

中斷映射

與遵循樹的自然結構而進行的地址轉換不同,機器上的任何設備都可以發(fā)起和終止中斷信號。另外地址的編址也不同于中斷信號,前者是設備樹的自然表示,而后者者表現(xiàn)為獨立于設備樹結構的節(jié)點之間的鏈接。


下面顯示了ADC控制器中斷設備片段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75


adc: adc@48003000 {
compatible =?"st,stm32mp1-adc-core";
reg = <0x48003000 0x400>;
interrupts = <GIC_SPI?18?IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI?90?IRQ_TYPE_LEVEL_HIGH>;
clocks = <&rcc ADC12>, <&rcc ADC12_K>;
clock-names =?"bus",?"adc";
interrupt-controller;
st,syscfg-vbooster = <&syscfg 0x4 0x100>;
st,syscfg-vbooster-clr = <&syscfg 0x44 0x100>;
st,syscfg-anaswvdd = <&syscfg 0x4 0x200>;
st,syscfg-anaswvdd-clr = <&syscfg 0x44 0x200>;
#interrupt-cells = <1>;
#address-cells = <1>;
#size-cells = <0>;
status =?"disabled";
adc1: adc@0 {
compatible =?"st,stm32mp1-adc";
#io-channel-cells = <1>;
reg = <0x0>;
interrupt-parent = <&adc>;
interrupts = <0>;
dmas = <&dmamux1?9?0x400 0x05>;
dma-names =?"rx";
status =?"disabled";
};
adc2: adc@100 {
compatible =?"st,stm32mp1-adc";
#io-channel-cells = <1>;
reg = <0x100>;
interrupt-parent = <&adc>;
interrupts = <1>;
dmas = <&dmamux1?10?0x400 0x05>;
dma-names =?"rx";
/* temperature sensor */
st,adc-channels = <12>;
st,min-sample-time-nsecs = <10000>;
status =?"disabled";
};
jadc1: jadc@0 {
compatible =?"st,stm32mp1-adc";
st,injected;
#io-channel-cells = <1>;
reg = <0x0>;
interrupt-parent = <&adc>;
interrupts = <3>;
status =?"disabled";
};
jadc2: jadc@100 {
compatible =?"st,stm32mp1-adc";
st,injected;
#io-channel-cells = <1>;
reg = <0x100>;
interrupt-parent = <&adc>;
interrupts = <4>;
/* temperature sensor */
st,adc-channels = <12>;
st,min-sample-time-nsecs = <10000>;
status =?"disabled";
};
adc_temp: temp {
compatible =?"st,stm32mp1-adc-temp";
io-channels = <&adc2?12>;
nvmem-cells = <&ts_cal1>, <&ts_cal2>;
nvmem-cell-names =?"ts_cal1",?"ts_cal2";
#io-channel-cells = <0>;
#thermal-sensor-cells = <0>;
status =?"disabled";
};
};

下面顯示了ethernet控制器中斷設備片段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27


ethernet0: ethernet@5800a000 {
compatible =?"st,stm32mp1-dwmac",?"snps,dwmac-4.20a";
reg = <0x5800a000 0x2000>;
reg-names =?"stmmaceth";
interrupts-extended = <&intc GIC_SPI?61?IRQ_TYPE_LEVEL_HIGH>,
<&intc GIC_SPI?62?IRQ_TYPE_LEVEL_HIGH>,
<&exti?70?1>;
interrupt-names =?"macirq",
"eth_wake_irq",
"stm32_pwr_wakeup";
clock-names =?"stmmaceth",
"mac-clk-tx",
"mac-clk-rx",
"ethstp";
clocks = <&rcc ETHMAC>,
<&rcc ETHTX>,
<&rcc ETHRX>,
<&rcc ETHSTP>;
st,syscon = <&syscfg 0x4>;
snps,mixed-burst;
snps,pbl = <2>;
snps,en-tx-lpi-clockgating;
snps,axi-config = <&stmmac_axi_config_0>;
snps,tso;
power-domains = <&pd_core>;
status =?"disabled";
};

屬性

屬性值

描述

interrupts

prop-encoded-array

一個設備節(jié)點屬性,該屬性主要描述了中斷的HW interrupt ID以及類型

interrupt-parent

phandle

該屬性主要描述了該設備的interrupt request line連接到哪一個interrupt controller,那些沒有 interrupt-parent 的節(jié)點則從它們的父節(jié)點中繼承該屬性

interrupts-extended

phandle prop-encoded-array

列出了設備生成的中斷,當設備連接到多個中斷控制器

interrupt-cells

u32

這是中斷控制器節(jié)點的屬性,用來標識這個控制器需要幾個單位做中斷描述符(類似于 #address-cells 和 #size-cells),則子節(jié)點的interrupts一個cell三個32bits整型值: <中斷域 中斷 觸發(fā)方式>

interrupt-controller

empty

一個空屬性用來聲明這個node接收中斷信號

特殊節(jié)點

aliases節(jié)點為了解決節(jié)點路徑名過長的問題,引入了節(jié)點別名的概念,可以引用到一個全路徑的節(jié)點。如/external-bus/ethernet@0,0,但當用戶想知道具體內(nèi)容的時候顯得太累贅。

1
2
3
4
5


aliases {
ethernet0 = eernet0;
serial0 = &uart4;
serial5 = &usart3;
};

當為設備分配一個標識符的時候,操作系統(tǒng)更傾向于使用aliases。



STM32MP157 Linux系統(tǒng)移植開發(fā)篇3:設備樹詳解的評論 (共 條)

分享到微博請遵守國家法律
宁安市| 德保县| 白朗县| 虎林市| 合山市| 湖南省| 济宁市| 剑阁县| 黔西县| 平谷区| 元谋县| 蒙自县| 京山县| 枝江市| 连江县| 正镶白旗| 临猗县| 富锦市| 陵川县| 山阳县| 类乌齐县| 噶尔县| 逊克县| 蕲春县| 隆德县| 榕江县| 天柱县| 天峻县| 威信县| 都昌县| 西宁市| 古交市| 会同县| 广安市| 镇赉县| 通城县| 微山县| 武平县| 巴林右旗| 延川县| 新巴尔虎左旗|