《12.uboot的移植2-從uboot官方標準uboot開始移植》
《12.uboot的移植2-從uboot官方標準uboot開始移植》
第一部分、章節目錄
2.12.1.選擇合適的官方原版uboot
2.12.2.先初步瀏覽官方原版uboot
2.12.3.mkconfig指令碼分析
2.12.4.先解決官方版本uboot的燒錄執行
2.12.5.start.S檔案分析與移植1
2.12.6.start.S檔案分析與移植2
2.12.7.新增DDR初始化1
2.12.8.新增DDR初始化2
2.12.9.新增uboot第二階段重定位1
2.12.10.新增uboot第二階段重定位2
2.12.11.CPU時鐘資訊顯示移植1
2.12.12.CPU時鐘資訊顯示移植2
2.12.13.CPU時鐘資訊顯示移植3
2.12.14.board和DDR配置顯示移植
2.12.15.board_init_r移植
2.12.16.uboot2013.10中SD/MMC驅動瀏覽
2.12.17.SD卡驅動移植1
2.12.18.SD卡驅動移植2
2.12.19.SD卡驅動移植3
2.12.20.環境變數的移植
2.12.21.環境變數的測試和配置移植
2.12.22.網絡卡驅動的移植1
2.12.23.網絡卡驅動的移植2
第二部分、章節介紹
2.12.1.選擇合適的官方原版uboot
本節課講解uboot的版本差異,並且最終選擇一個合適的官方uboot版本作為我們移植工作的起點。
2.12.2.先初步瀏覽官方原版uboot
本節對官方uboot進行大概瀏覽和結構分析、工程建立等,並將該uboot和我們之前分析過的移植好的uboot進行大概對比分析。
2.12.3.mkconfig指令碼分析
本節分析2013.10版本的uboot的mkconfig,重點解析了8個傳參與符號連線建立等,這些在後續分析程式碼時都會有一定幫助。
2.12.4.先解決官方版本uboot的燒錄執行
本節首先移植sd_fusing資料夾,然後分析並修改程式碼使編譯得到的u-boot.bin能夠在SD卡中執行起來。
2.12.5.start.S檔案分析與移植1
本節分析start.S檔案流程,並且新增開發板置鎖和串列埠輸出字元的程式碼。
2.12.6.start.S檔案分析與移植2
本節接上節來排除問題,講解了如何使用LED點亮方式進行除錯,並最終解決問題,成功輸出字元"O"
2.12.7.新增DDR初始化1
本節開始新增DDR初始化程式碼,主要是分析了DDR初始化程式碼新增的位置、相關檔案的移植等。
2.12.8.新增DDR初始化2
本節繼續解決DDR初始化部分功能,新增除錯資訊列印以驗證DDR初始化是否成功。
2.12.9.新增uboot第二階段重定位1
本節首先分析uboot程式碼流程,找到應該新增uboot重定位功能的程式碼段,然後分析程式碼重定位的思路。
2.12.10.新增uboot第二階段重定位2
本節接上節繼續新增重定位程式碼,並且解決編譯中的各種問題,成功實現重定位,並且看到了uboot啟動第二階段的資訊。
2.12.11.CPU時鐘資訊顯示移植1
本節分析uboot第二階段的cpu資訊輸出,並且進行程式碼移植和更改,解決主頻顯示不正確的問題。
2.12.12.CPU時鐘資訊顯示移植2
本節接上節繼續解決主頻顯示不正確的問題。
2.12.13.CPU時鐘資訊顯示移植3
本節最終解決了主頻顯示不正確的問題,並且更正了以前一些不正確的認識,對210的iROM中時鐘設定更加明確。
2.12.14.board和DDR配置顯示移植
本節主要解決開發板名稱、DDR配置值的初始化等修改,這些都是init_sequences中的東西。
2.12.15.board_init_r移植
本節開始移植board_init_r中的一些函式,主要內容是去掉原來的oneNand支援,加上SD/MMC的支援並將環境變數修改到SD/MMC中。
2.12.16.uboot2013.10中SD/MMC驅動瀏覽
本節進行SD/MMC驅動的瀏覽和錯誤問題定位分析,最終分析得到解決方案思路。
2.12.17.SD卡驅動移植1
本節開始移植SD卡驅動,主要進行驅動檔案的逐個分析、檔案關係的對應複製Makefile的修改等。
2.12.18.SD卡驅動移植2
本節繼續進行SD卡驅動移植實驗,將移植的驅動檔案整理然後進行編譯、修改等使之可以通過編譯。
2.12.19.SD卡驅動移植3
本節繼續進行SD卡驅動移植實驗,主要在編譯指令碼cp.sh中新增程式碼使之選擇性清理配置編譯,這樣可以提升效率。
2.12.20.環境變數的移植
本節分析環境變數分割槽的問題,通過程式碼分割槽確認了當前環境變數被放在何處,並且通過修改程式碼將環境變數放在合適的位置。
2.12.21.環境變數的測試和配置移植
本節對環境變數進行測試和效果確認,並且將uboot中預設的環境變量表重新設定以使該uboot更方便使用。
2.12.22.網絡卡驅動的移植1
本節開始移植網絡卡驅動,主要是分析網絡卡驅動初始化程式碼,然後進行網絡卡初始化的新增、程式碼實踐、效果檢視。
2.12.23.網絡卡驅動的移植2
本節接上節繼續進行網絡卡驅動的移植。主要是分析網絡卡驅動不工作的原因並且進行解決方案分析、實踐及效果確認。
第三部分、隨堂記錄
2.12.1.選擇合適的官方原版uboot
2.12.1.1、官方原版uboot的版本
(1)版本號。剛開始是1.3.4,後來變成2009.08
(2)新版和舊版的差別。uboot的架構很早就定下來了,然後裡面普遍公用的東西(common目錄下、drivers目錄下、fs目錄下等···)在各個版本之間幾乎是完全一樣的。差別最大的是board和cpu目錄,這兩個目錄正是單板(開發板)相關的。越新的uboot版本支援越多的開發板(CPU),所以越新的uboot越龐大。
(3)並不是越新的版本就越好。越新的uboot中會多出更多的開發板的支援程式碼,如果我們的開發板並不是很新,就沒必要去用很新版本的uboot。因為多出來的程式碼自己也用不到而且還會成為累贅。
2.12.1.2、官方原版uboot的來源
(1)從uboot官方網站ftp下載
(2)從一些映象網站下載
2.12.1.3、新版uboot配置體系的改變
(1)在最新的uboot版本(準確的說是2013.10到2014.10中的某個版本)中,uboot的檔案體系發生了一個很大的變化。這個變化就是uboot引入了linux kernel的配置體系(Kbuild、Kconfig、menuconfig),從而讓我們可以在圖形介面下,像配置核心一樣配置uboot。
(2)所以新版本的uboot配置時和我們之前的課程講的就不同了。我們移植時不能選擇這種配置方式更改之後的uboot版本。我們要選擇更改之前的。
(3)新版本的配置方式本質上和linux kernel一樣的,所以在學完linux kernel移植後自己就能看懂,因此不用擔心。
2.12.1.4、結論:選擇合適的官方原版uboot進行移植
(1)結合以上,選擇2013.10版本進行實驗移植是比較合理的。
2.12.1.5、注意:實踐工作中一般是從SoC廠商的uboot出發移植的
(1)在工作中一般是不需要從uboot官方版本出發去做移植的,而是從SoC廠商提供的開發板配套的uboot去做移植的。
2.12.2.先初步瀏覽官方原版uboot
2.12.2.1、資料夾結構瀏覽
(1)資料夾結構分析、主要檔案檢視
總的來說,資料夾結構和以前基本一樣。不同的主要是lib,以前是lib_arm和lib_generic,現在是arch和lib。arch目錄下放的是和cpu架構有關的東西。
總的來說,2013.10版本的uboot在結構上和1.3.4版本的uboot還是有所不同的。
(2)參照物開發板的選擇
我們開發板使用的CPU是S5PV210,所以要找uboot中針對S5PV210或者S5PC110進行移植的作為參考。
根據規律,我們應該參考include/configs/s5p_goni.h,對應的board在uboot/board/samsung/goni這個目錄。
(3)刪除無關檔案和資料夾
其實不刪除也可以,但是刪除更好。
2.12.2.2、建立SI工程並預解析
2.12.2.3、主Makefile瀏覽及boards.cfg檔案
(1)2013.10版本的uboot的Makefile中使用了boards.cfg檔案,因此在配置uboot時make xxx_config,這個xxx要到boards.cfg檔案中查詢。
(2)其實就相當於把以前的版本的uboot中各種開發板的配置部分規則抽離出來寫到了Makefile中,然後把配置資訊部分寫到了一個獨立檔案boards.cfg。
2.12.2.4、mkconfig指令碼瀏覽及符號連線的分析
(1)下節課詳細分析,給出結論。
2.12.2.5、結論:
(1)參照物開發板為:55p_goni
(2)配置對應的cpu、board資料夾分別為:
cpu: u-boot-2013.10\arch\arm\cpu\armv7
board: u-boot-2013.10\board\samsung\goni
2.12.3.mkconfig指令碼分析
2.12.3.1、指令碼功能瀏覽
(1)首先我們在命令列配置uboot時,是:make s5p_goni_config,對應Makefile中的一個目標。
(2)新版本的Makefile中:
%_config:: unconfig
@$(MKCONFIG) -A $(@:_config=)
從這裡分析得出結論,實際配置時是呼叫mkconfig指令碼,然後傳參2個:-A和s5p_goni
(3)到了mkconfig指令碼中了。在24到35行中使用awk正則表示式將boards.cfg中與剛才$1(s5p_goni)能夠匹配上的那一行截取出來賦值給變數line,然後將line的內容以空格為間隔依次分開,分別賦值給$1、$2···$8。
(4)注意在解析完boards.cfg之後,$1到$8就有了新的值。
$1 = Active
$2 = arm
$3 = armv7
$4 = s5pc1xx
$5 = samsung
$6 = goni
$7 = s5p_goni
$8 = -
2.12.3.2、幾個傳參和其含義
(1)幾個很重要的變數
arch=arm
cpu=armv7
vendor=samsung
soc=s5pc1xx
2.12.3.3、符號連結
(1)include/asm -> arch/arm/include/asm
(2)include/asm/arch -> include/asm/arch-s5pc1xx
(3)include/asm/proc -> include/asm/proc-armv
最後建立了include/config.h檔案。
2.12.3.4、Makefile中新增交叉編譯工具鏈
(1)官方原版的uboot中CROSS_COMPLIE是沒有定義的,需要自己去定義。如果沒定義就直接去編譯,就會用gcc編譯。
(2)新增一行:
CROSS_COMPILE = /usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-
2.12.3.5、配置編譯測試
(1)編譯過程:
make distclean
make s5p_goni_config
make
(2)結果:得到u-boot.bin即可
2.12.4.先解決官方版本uboot的燒錄執行
2.12.4.1、如何燒錄uboot
(1)燒錄u-boot.bin到SD卡中有2種方法:windows下用燒錄軟體;linux下用dd命令燒錄指令碼來燒錄。因為windows下的工具不開源,出了問題沒法除錯,所以不推薦。推薦linux下用燒錄指令碼來燒錄(實質是dd命令進行sd卡扇區寫入)
(2)移植原來的版本的uboot中的sd_fusing資料夾到官方uboot版本中,使用這個資料夾中的sd_fusing.sh指令碼來進行燒錄。
2.12.4.2、分析:為什麼燒錄執行不正確?
(1)串列埠接串列埠2,串列埠有輸出。但是這個串列埠輸出不是uboot輸出的,而是內部iROM中的BL0執行時輸出的。
(2)輸出錯誤資訊分析:
第一個SD checksum Error:是第一順序啟動裝置SD0(iNand)啟動時校驗和失敗打印出來的;
第二個SD checksum Error:是第二順序啟動裝置SD2(外部SD卡)啟動時校驗和失敗打印出來的;
剩下的是串列埠啟動和usb啟動的東西,可以不管。
總結:從兩個SD checksum Error,可以看出:外部SD卡校驗和失敗了。
分析:SD卡燒錄出錯了,導致SD卡校驗和會失敗。
2.12.4.3、解決方案分析
(1)為什麼SD卡燒錄會出錯?可能原因:燒錄方法錯誤、燒錄原材料錯誤。
(2)經過分析,sd_fusing這個資料夾下的mkbl1這個程式肯定沒錯,上一層目錄下的u-boot.bin是存在的,校驗和失敗不失敗和u-boot.bin無關。
(3)經過分析和查詢,發現是mkbl1程式和start.S中前16個位元組校驗和的處理上面不匹配造成的,解決方法是在start.S最前面加上16個位元組的佔位。
2.12.4.4、程式碼實踐
(1)重新編譯燒錄執行,發現結果只顯示一個SD checksum Error。這一個就是內部SD0通道的inand啟動校驗和失敗打印出來的。剩下的沒有了說明外部SD卡校驗和成功了,只是SD卡上的uboot是錯誤的,沒有串列埠輸出內容,所以沒有輸出了。
2.12.5.start.S檔案分析與移植1
2.12.5.1、start.S流程分析
(1)#define CONFIG_SYS_TEXT_BASE 0x34800000 可以看出我們的uboot的連線地址是在0x34800000位置。
(2)save_boot_params是個空函式,裡面直接返回的。
(3)cpu_init_cp15這個函式功能是設定MMU、cache等。這個版本的uboot中未使用虛擬地址,因此MMU在這裡直接關掉。
(4)cpu_init_crit,這個函式裡只有一句跳轉指令,短跳轉到lowlevel_init函式。
注意:uboot中有2個lowlevel_init.S檔案(檔案中還都有lowlevel_init函式),憑一般分析無法斷定2箇中哪個才是我們想要的。通過分析兩個檔案所在資料夾下面的Makefile可以判定board/samsung/goni目錄下的才是真正包含進來的,arch/arm/cpu/armv7目錄下的並沒有被包含進來。
還可以通過實踐驗證的方法來輔助判斷。通過檢視之前已經編譯過的uboot原始碼目錄,看哪個被程式設計為.o檔案了,就知道哪個是真正被使用的了。
(5)lowlevel_init函式在board/samsung/goni目錄下,主要作用是時鐘設定、串列埠設定、復位狀態判斷···這個函式是S5PC100和S5PC110兩個CPU共用的。
(6)經過瀏覽,發現lowlevel_init函式中做的有意義的事情有:關看門狗、呼叫uart_asm_init來初始化串列埠、並沒有做時鐘初始化(下面有時鐘初始化的函式,但是實際沒呼叫。如果uboot中沒有初始化時鐘,那麼時鐘就是iROM中初始化的那種配置)
2.12.5.2、新增開發板制鎖和串列埠列印字元"O"
(1)我們為了除錯uboot的第一階段,就要看到現象。為了看到現象,我們向lowlevel_init函式中新增2個程式碼,一個是開發板制鎖,一個是串列埠列印"O"
(2)這兩段程式碼可以直接從ARM裸機全集課程中的程式碼中來。其實也可以從三星移植版本的uboot中來,但是因為三星移植版本中用到了很多暫存器定義,涉及到標頭檔案的,所以移植過來不方便。
(3)實踐新增。
2.12.5.3、實踐結果及分析
(1)實驗結果是:沒看到開發板制鎖,串列埠也沒有輸出任何東西。實驗失敗。
(2)結論:因為開發板制鎖沒有成功,所以我們判定,在開發板制鎖程式碼執行之前uboot就已經掛掉了。下面就是去跟蹤程式碼執行,然後判定問題點再去解決。
2.12.6.start.S檔案分析與移植2
2.12.6.1、新增LED點亮程式碼跟蹤程式執行
(1)在基礎程式碼階段,串列埠還沒有執行,串列埠除錯工具還無法使用時,使用LED點亮的方式來除錯程式就是一個有力的手段。
(2)有些情況下可以用Jlink等除錯工具來除錯這種基礎程式碼。
(3)從程式的基本執行路徑端出發,隔一段給他新增一個LED點亮程式碼,然後執行時根據現象來觀察,判定哪裡執行了哪裡沒執行。從而去定位問題。
(4)從以前的裸機程式碼中組織出一個標準的LED點亮然後延時一段的一個標準程式碼段:
ldr r0, =0x11111111
ldr r1, =0xE0200240
str r0, [r1]
ldr r0, =((1<<3) | (0<<4) | (1<<5)) // 1是滅,0是亮
ldr r1, =0xE0200244
str r0, [r1]
ldr r2, =9000000
ldr r3, =0x0
delay_loop:
sub r2, r2, #1
cmp r2, r3
bne delay_loop
(5)之前做實驗時發現一個現象:我們的uboot執行時按住電源開關時所有4顆LED都是亮的。所以我們做實驗時給LED點亮是看不到現象的,所以我們的程式碼關鍵是要熄滅某些LED來判斷。
(6)我們將熄滅LED的函式在start.S中隔一段的關鍵部位放上1個,然後執行時通過觀察LED的點亮熄滅狀態,就知道程式執行到哪裡了。
(7)經過判斷我們發現:start.S中工作一切正常,但是函式一旦放到lowlevel_init.S中就完全不工作了。通過分析得出結論:b lowlevel_init這句程式碼出了問題。
2.12.6.2、修改u-boot.lds將lowlevel_init.S放到前部
(1)問題分析:跳轉程式碼出了問題。分析問題出在程式碼的連線上。
(2)三星S5PV210要求BL1大小為8KB,因此uboot第一階段程式碼必須在整個uboot映象的前8KB內,否則跳轉不到。
(3)對比三星移植版本的uboot的u-boot.lds和官方版本uboot的連線指令碼u-boot.lds(注意這兩個版本的uboot的連線指令碼的位置是不同的),就發現lowlevel_init.S的程式碼段沒有被放在前面。
(4)在u-boot.lds中start.o後面新增board/samsung/goni/lowlevel_init.o (.text*),這個就保證了lowlevel_init函式被連線到前面8kb中去。
(5)報錯,lowlevel_init重複定義了。
2.12.6.3、修改board/samsung/goni/Makefile解決編譯問題
(1)問題分析:為什麼會重複定義。因為lowlevel_init這個函式被連線時連線了2次。一次是board/samsung/goni這個目錄下生成libgoni.o時連線了1次,第2次是連線指令碼最終在連線生成u-boot時又連線了一次,所以重複定義了。
(2)這個錯誤如何解決?思路是在libgoni.o中不要讓他連線進lowlevel_init,讓他只在最終連線u-boot時用1次,就可以避免重複定義。
(3)參考當前版本的uboot的start.S檔案的處理技巧,解決了這個問題。
2.12.6.4、實踐驗證。
結果是開發板制鎖和串列埠輸出’O’都成功了。
2.12.7.新增DDR初始化1
2.12.7.1、分析下一步移植路線
(1)cpu_init_crit函式成功初始化串列埠、時鐘後,轉入_main函式,函式在arch/arm/lib/crt0.S檔案中。
(2)在crt0.S中首先設定棧,將sp指向DDR中的棧地址;然後呼叫board_init_f函式進行板級初始化。函式在arch/arm/lib/board.c中。
(3)在這個版本的uboot中,把以前uboot的第二階段start_armboot函式分成了2部分:board_init_f和board_init_r。所以在這裡就和以前版本的uboot接軌上了,推測board_init_f中肯定是做了板級初始化,board_init_r中進入了uboot的命令列。
(4)分析到這裡,在uboot2013.10版本中思路已經很清晰了:uboot的第二階段就在crt0.S檔案中,第二階段的入口就是_main函式。第一階段工作主要就是cpu_init_crit函式,所以我們要在cpu_init_crit函式中新增DDR初始化和uboot的重定位。
(5)分析到這裡,下一步工作方向就確定了。我們要先在cpu_init_crit函式中新增DDR初始化,然後在start.S中bl _main之前新增uboot的重定位,然後將bl _main改成ldr pc, __main(__main: .word _main)長跳轉。然後在crt0.S中board_init_f後刪除那些重定位程式碼,至此uboot的第二階段就應該能啟動起來了。後續的移植就是第二階段了。
2.12.7.2、分析DDR初始化程式碼移植思路
(1)如果本來uboot中有DDR初始化程式碼,那我們可以就著這些程式碼來修改。但是問題是這個uboot2013.10中根本沒有DDR初始化,所以我們需要完全從頭去另外新增DDR初始化程式碼。
(2)我們的思路就是從三星版本的uboot中直接移植DDR初始化程式碼過來即可。三星版本的uboot中DDR初始化函式在cpu/s5pc11x/s5pc110/cpu_init.S檔案中,直接將這個檔案移植過來即可。
2.12.7.3、動手移植
(1)新增cpu_init.S檔案到uboot2013.10中。注意,這裡的程式碼必須保證在前8kb內,所以必須和lowlevel_init.S檔案一樣的連結處理。主要是在board/samsung/goni/Makefile中和arch/arm/cpu/u-boot.lds檔案中做修改新增。
(2)新增標頭檔案s5pc110.h到include目錄下。
(3)對cpu_init.S檔案程式碼進行修整,把一些無用的程式碼去掉,把一些相關的條件編譯人工處理一下。
(4)在SourceInsigt工程中新增入這兩個檔案。然後重新解析一遍。然後對新新增的程式碼進行分析修整,把裡面一些明顯的巨集定義缺失給補上。
2.12.8.新增DDR初始化2
2.12.8.1、移植必要的巨集定義
(1)DDR配置引數,從三星版本的smdkv210single.h中複製到s5p_goni.h中。
(2)s5pc110.h中進行修整。
2.12.8.2、程式碼同步、編譯、再修整
2.12.8.3、新增除錯資訊,驗證DDR初始化完成。
(1)除錯資訊有LED點亮和串列埠輸出兩種。優先選用串列埠除錯的方法。
(2)在DDR初始化完成後,新增串列埠輸出字元"K",這樣啟動時如果看到了"OK"就說明DDR已經被成功初始化了。
(3)結果:看到了"OK"標誌,說明DDR新增實驗成功。
2.12.9.新增uboot第二階段重定位1
2.12.9.1、在重定位程式碼前加除錯資訊定位
(1)邏輯上來說,重定位部分程式碼應該在DDR初始化之後和uboot第二階段來臨前之間。
(2)uboot的第一階段和第二階段的劃分並不是絕對的,唯一必須遵循的原則就是第一階段不能大於8KB。所以uboot的第一階段最少要完成DDR初始化和重定位,最多不能超過8KB。在滿足這些條件時,第一階段和第二階段的接點可以隨便挑。
(3)找到合適的地方來寫重定位程式碼,重定位之後遠跳轉到第二階段的入口。
(4)
2.12.9.2、重定位程式碼移植
2.12.9.3、清bss段移植
2.12.9.4、movi_bl2_copy函式移植
(1)從三星版本的uboot中賦值movi.c和movi.h到uboot2013.10中。
(2)改makefile和u-boot.lds。
2.12.10.新增uboot第二階段重定位2
2.12.10.1、_mian函式中基本處理
(1)主要就是把裡面的重定位程式碼部分給刪除掉。剩下就是:設定棧、呼叫board_init_f函式和board_init_r函式。
2.12.10.2、程式碼同步及編譯
(1)主要是crt0.S和movi.h。
2.12.10.3、編譯中出現問題解決
(1)movi.h中巨集定義出錯,最後在s5p_goni.h中添加了 CONFIG_EVT1這個巨集解決了
(2)連線錯誤:u-boot contains relocations other than R_ARM_RELATIVE
在uboot下用grep “R_ARM_RELATIVE” -nR *搜尋,發現Makefile中有一個檢查重定位的規則,遮蔽掉這個規則後編譯連線成功。
2.12.10.4、結果驗證及下階段展望
(1)看到了uboot啟動打印出來的一系列資訊,但是uboot沒有進入命令列。
(2)這說明uboot中的DDR初始化和重定位功能都已經完美實現,後面就是第二階段的繼續移植了。
2.12.11.CPU時鐘資訊顯示移植1
2.12.11.1、小問題:banner資訊補全
2.12.11.2、CPU ID的確定
2.12.11.3、CPU各種頻率的自動計算
2.12.11.4、程式碼實踐
(1)arch/arm/include/asm/arch-s5pc1xx/cpu.h,和arch/arm/cpu/armv7/s5p-common/cpu_info.c檔案同步一下
2.12.12.CPU時鐘資訊顯示移植2
2.12.13.CPU時鐘資訊顯示移植3
2.12.13.1、問題分析
(1)時鐘顯示ARMCLK是400MHz。
(2)除錯,把m、p、s和apll_ratio打印出來後,發現這幾個值的設定和之前的uboot的設定是不同的。原因在於我們當前版本的uboot中並未對SoC的時鐘進行過設定,當前uboot中的時鐘是iROM程式碼預設設定的。
(3)我自己之前一直認為iROM中把210的時鐘設定為了1000MHz,然後三星版本的uboot中設定的時鐘也是按照這個資料手冊356頁推薦的這個最佳效能配置時鐘設定的。所以以前認為uboot中可以沒有時鐘設定也是一樣的。
(4)但是實際上不是這樣的,實際上內部iROM中設定的時鐘APLL輸出是800MHz,ARMCLK是400MHz。如果uboot中不做時鐘的設定實際得到的就是這個時鐘。所以我們之前程式碼得到的結果是400MHz。
(5)所以要解決這個時鐘不對的問題,要在lowlevel_init.S中新增上時鐘初始化的程式碼即可。
2.12.13.2、時鐘初始化函式的新增
(1)在lowlevel_init.S中移植system_clock_init函式,並且在s5p_goni.h中新增相關的巨集定義引數,然後在lowlevel_init函式中呼叫system_clock_init函式。
2.12.13.1、總結和感悟
2.12.14.board和DDR配置顯示移植
2.12.14.1、board名稱更改
2.12.14.2、DDR配置值修改
2.12.14.3、MACH_TYPE定義
2.12.14.4、DDR列印資訊更改
2.12.14.5、程式碼實踐
2.12.14.6、關於MACH_TYPE的定義問題。
(1)在uboot2013.10中和uboot1.3.4中設計有所不同。在uboot1.3.4中這個東西是分散定義在各個配置標頭檔案當中的。但是在uboot2013.10中我們把MACH_TYPE集中定義在一個檔案arch/arm/include/asm/mach-types.h中了。
(2)集中定義其實是uboot從linux核心中學來的。在linux kernel中MACH_TYPE就是在檔案中集中定義的。集中定義的好處是方便查閱,不容易定義重複。
(3)這個MACH_TYPE是和開發板繫結的,原則上每一個開發板型號都有一個MACH_TYPE,這個機器碼由linux核心管理者來分配的,如果需要應該向這些人申請。
2.12.15.board_init_r移植
2.12.15.1、去掉oneNand支援
2.12.15.2、新增SD/MMC支援
2.12.16.uboot2013.10中SD/MMC驅動瀏覽
2.12.16.1、從初始化程式碼開始瀏覽
(1)
2.12.16.2、相關函式和檔案
(1)
drivers/mmc/mmc.c、
drivers/mmc/sdhci.c
board/samsung/goni/goni.c
arch/arm/include/asm/arch-s5pc1xx/mmc.h
2.12.16.3、當前錯誤定位及解決方案分析
(1)錯誤發生路徑定位
board_init_r
mmc_initialize
do_preinit
mmc_start_init
mmc_go_idle
mmc_send_cmd
sdhci_send_command
sdhci_transfer_data 錯誤在這個函式中
(2)錯誤原因分析
sdhic.c中的所有函式構成了三星210CPU的SD/MMC控制器的驅動。這裡面的函式是三星公司的工程師寫的,內容就是用來控制210CPU的內部的SD/MMC控制器和外部的SD卡通訊的。這就是所謂的驅動。
sdhci_transfer_data函數出錯,說明是SoC的SD/MMC控制器和外部SD卡(其實現在用的是SD0的iNand)的資料傳輸出了問題。(細節分析發現是控制器內部有一箇中斷狀態錯誤標誌被置位了。)
(3)解決方案分析:
兩條思路:第一是去逐行的分析SD卡驅動實現(分析中要對SD卡通訊協議和210這個SoC的SD控制器非常熟悉),然後發現錯誤所在,然後修改程式碼解決問題;第二個是投機取巧的方法,就是把原來三星移植版本的uboot中的SD/MMC驅動整個移植過來替換掉uboot2013.10中的MMC驅動。其實還有第三條折中思路,就是綜合第一種和第二種,譬如參考三星移植版本的uboot中的驅動實現來修補uboot2013.10中的驅動實現。
2.12.17.SD卡驅動移植1
2.12.17.1、分析兩個版本的uboot中SD卡驅動差異
(1)uboot2013.10中:驅動相關的檔案主要有:
drivers/mmc/mmc.c
drivers/mmc/sdhci.c
drivers/mmc/s5p_sdhci.c
board/samsung/goni/goni.c
(2)三星移植版本中,驅動相關的檔案主要有:
drivers/mmc/mmc.c
drivers/mmc/s3c_hsmmc.c
cpu/s5pc11x/cpu.c
cpu/s5pc11x/setup_hsmmc.c
(3)經過分析發現:SD卡驅動要工作要包含2部分內容,一部分是drivers/mmc目錄下的是驅動,另外一部分是uboot自己提供的初始化程式碼(譬如GPIO初始化、時鐘初始化)
2.12.17.2、複製必要的檔案並修改相應Makefile
(1)首先解決drivers/mmc目錄下的檔案替換。
(2)修改初始化程式碼。
2.12.17.3、程式碼瀏覽及修補
(1)按照程式碼執行時的流程來逐步瀏覽程式碼,看哪裡需要修補。
2.12.18.SD卡驅動移植2
2.12.18.1、繼續修補驅動程式碼
(1)include/mmc.h
(2)include/s3c_hsmmc.h
2.12.18.2、同步及編譯、問題解決
(1)出錯1:cmd_mmc.c中出錯。原因是cmd_mmc.c和mmc驅動密切相關,所以改了驅動後這個實現檔案也要跟著改,解決方法是從三星版本的直接同名檔案複製過來替換
(2)出錯2:drivers/mmc/mmc_write.c編譯出錯。原因是這個檔案和本來版本中的mmc.c檔案相關,但是mmc.c被替換掉了所以這個檔案編譯報錯。解決方案就是修改makefile去掉這個檔案的依賴,讓他不被編譯。
(3)出錯3:#include<regs.h>註釋掉,然後新增#include <s5pc110.h>
2.12.19.SD卡驅動移植3
2.12.19.1、解決每次編譯時間都很長的問題。
(1)每次編譯指令碼cp.sh執行時都會先cp同步程式碼,然後make distclean···所以每次都會清空後從頭編譯,這就很費時間了。
(2)但是實際上有時候是不會make distclean的,只需要先cp然後直接make即可(當更改沒有涉及到配置標頭檔案s5p_goni.h,沒有涉及到makefile檔案,或者其他專案配置檔案,也就是說我們的更改只是普通程式碼檔案的更改時)
2.12.19.2、效果測試
(1)讀寫測試均成功
2.12.20.環境變數的移植
2.12.20.1、iNand分割槽表檢查-env究竟應該放在哪
(1)測試環境變數是否可以儲存,通過開機set設定環境變數然後save,然後關機後重啟來測試環境變數的儲存是否成功。
(2)我們的環境變數究竟儲存到哪裡去了?這個就要去分析程式碼中的分割槽表。
(3)環境變數應該被放在哪裡?雖然無法確定ENV一定要放在哪裡,但是有一些地方肯定是不能放的,否則將來會出問題。原則是同一個SD卡扇區只能放一種東西,不能疊加,否則就會被覆蓋掉。uboot燒錄時使用的扇區數是:SD2的扇區1-16和49-x(x-49大於等於uboot的大小)
(3)從uboot的燒錄情況來看,SD2的扇區0空閒,扇區1-16被uboot的BL1佔用,扇區17-48空閒,扇區49-x被uboot的BL2佔用。再往後就是核心、rootfs等映象的分割槽了。系統移植工程師可以根據kernel映象大小、rootfs大小等來自由給SD分割槽。
(4)從uboot的分割槽情況來看,ENV不能往扇區1-16或者49-x中來放置,其他地方都可以商量。ENV的大小是16K位元組也就是32個扇區。
2.12.20.2、環境變數相關程式碼瀏覽
(1)目前情況是uboot在SD2中,而ENV在SD0中,所以現在ENV不管放在哪個扇區都能工作,不會有問題。但是我們還是得找到ENV分割槽所在並且改到不會和uboot衝突,因為將來部署系統時我們會將uboot和kernel、rootfs等都燒錄到iNnand中去,那時候也要確保不會衝突。
(2)static inline int write_env(struct mmc *mmc, unsigned long size,
unsigned long offset, const void *buffer)
類似於這種函式,在程式碼分析中,關鍵是弄明白各種引數的意義。mmc表示要寫的mmc裝置,size表示要寫的大小,offset表示要寫到SD卡的哪個扇區去,buffer是要寫的內容。
(3)CONFIG_ENV_OFFSET這個巨集決定了我們的ENV在SD卡中相對SD卡扇區0的偏移量,也就是ENV寫到SD卡的哪裡去了。經過分析發現這個巨集的值為0.所以我們的ENV
被寫到了0扇區開始的32個扇區中。
(4)寫到這裡肯定不行,因為和uboot的BL1衝突了。解決方案是改變這個CONFIG_ENV_OFFSET的值,將ENV寫到別的空閒扇區去。
(5)#define MOVI_BL2_POS ((eFUSE_SIZE / MOVI_BLKSIZE) + MOVI_BL1_BLKCNT + MOVI_ENV_BLKCNT) 後面這三個其實分別是1+16+32=49
其中的1就是扇區0(空閒的),16是就是扇區1-16(uboot的BL1),32就是扇區17-48(存放ENV的),49自然就是uboot的BL2開始扇區了。這種安排是三星移植的uboot版本中推薦的SD卡的分割槽方式,不一定是唯一的。
(6)我們參考這個設計,即可實現環境變數不衝突。所以只要將ENV放到17扇區起始的地方即可。
2.12.21.環境變數的測試和配置移植
2.12.21.1、如何測試環境變數的儲存是否正確
(1)程式修改重新編譯後啟動,啟動後要注意iNand中本來有沒有環境變數。為了保險起見對iNand的前49個扇區進行擦除,然後就可以確保裡面沒有之前儲存過的環境變量了。使用命令:mmc write 0 30000000 0# 49來擦除SD0的扇區0-48,保證以前的環境變數都沒有了。
(2)重新開機後先set隨便改一個環境變數作為標記然後saveenv然後重啟。
(3)測試方法是,使用:mmc read 0 30000000 17# 32命令將iNand的17開始的32個扇區讀出來到記憶體30000000處,然後md檢視。找到顯示區域裡面的各個環境變數,看讀出來的和自己剛才修改的值是否一樣。
2.12.21.2、常用環境變數的配置移植
(1)常用的環境變數就是網路相關的那幾個,和CONFIG_BOOTCOMMAND、CONFIG_BOOTARGS等。
2.12.22.網絡卡驅動的移植1
2.12.22.1、新增網路支援
(1)uboot中對各種功能也是一個條件編譯可以配置可以裁剪的設計(從linux核心學來的),預設情況下我們的uboot沒有選擇支援網路。
(2)在配置標頭檔案中新增一行 #define CONFIG_CMD_NET
(3)添加了網路支援巨集之後,在uboot初始化時就會執行eth_initialize函式,從而網路相關程式碼初始化就會被執行,將來網路就有可能能用。
2.12.22.2、新增ping和tftp命令
(1)在linux系統中網路底層驅動被上層應用呼叫的介面是socket,是一個典型的分層結構,底層和上層是完全被socket介面隔離的。
(2)但是在uboot中網路底層驅動和上層應用是黏在一起的,不分層。意思就是上層網路的每一個應用都是自己去呼叫底層驅動中的操作硬體的程式碼來實現的。
(3)uboot中有很多預先設計的需要用到網路的命令,和我們直接相關的就是ping和tftp這兩個命令。這兩個命令在uboot中也是需要用相應的巨集開關來開啟或者關閉的。
(4)經過程式碼檢查,發現ping命令開關巨集為CONFIG_CMD_PING,而tftp命令的開關為CONFIG_CMD_NET,確認新增。
2.12.22.3、程式碼實踐。結果是ping和tftp命令都被識別了,但是都提示no ethernet found`````網路不通。為什麼不通?因為還沒做初始化等移植
2.12.22.4、移植網絡卡初始化程式碼
2.12.23.網絡卡驅動的移植2
2.12.23.1、實驗現象分析
(1)因為我們沒有自定義的網絡卡初始化函式(board_eth_init或者cpu_eth_init),所以uboot啟動時初始化網絡卡時列印:Net: Net Initialization Skipped
(2)eth.c中有2個很重要的全域性變數:eth_devices(用來指向一個連結串列,這個連結串列中儲存了當前系統中所有的網絡卡資訊)和eth_current(eth_current指標指向當前我們正在操作的那個網絡卡)。
(3)在linux的網絡卡驅動體系中,有一個數據結構(struct eth_device)用來表示(封裝)一個網絡卡的所有資訊,系統中註冊一個網絡卡時就是要建立一個這個結構體的例項,然後填充這個例項中的各個元素,最後將這個結構體例項加入到eth_devices這個連結串列上,就完成了註冊。瞭解了這些之後,你就明白了網絡卡驅動在初始化時必須負責將自己註冊到系統的網絡卡驅動體系中(其實就是把自己的eth_device結構體例項新增到eth_devices連結串列中)。如果你不做這個過程就會出現:網絡卡找不到的錯誤。
(4)分析當前的問題是:在305行判斷eth_devices是否為NULL之前沒有去做網絡卡驅動的註冊,所以這裡為NULL,所以打印出了“No ethernet found.”
2.12.23.2、DM9000驅動瀏覽
(1)想解決這個問題,就是要在305行之前去註冊網絡卡驅動。註冊網絡卡驅動的程式碼不能隨便亂寫,一定要遵守linux網絡卡驅動架構的要求。這一塊的程式碼一般屬於網絡卡驅動的一部分,像這裡就在dm9000x.c中。
(2)dm9000x.c中的最後一個函式int dm9000_initialize(bd_t *bis),這個函式就是用來註冊dm9000網絡卡驅動的。
2.12.24.3、問題修復
(1)根據之前分析uboot函式,發現前面有2個函式預留的可以用來放網絡卡初始化函式的,經過對比感覺board_eth_init函式稍微合適點,於是乎去新增。
2.12.24.4、對比和總結
2.12.23.uboot啟動核心的移植