1、uboot借用(移植)了linux驅動
(1)linux驅動本身做了模塊化設計。linux驅動本身和linux內核不是強耦合的,這是linux驅動可以被uboot借用(移植)的關鍵。
(2)uboot移植了linux驅動源代碼。uboot是從源代碼級別去移植linux驅動的,這就是linux系統的開源性。
(3)uboot中的硬件驅動比linux簡單。linux驅動本身有更復雜的框架,需要實現更多的附帶功能,而uboot本質上只是個裸機程序,uboot移植linux驅動時只是借用了linux驅動的一部分而已。
2、iNand/SD驅動解析
2.1、soc內部驅動MMC控制器的初始化
(1)mmc_initialize函數位於:uboot/drivers/mmc/mmc.c。
(2)從名字可以看出,這個函數的作用就是初始化開發板上MMC系統。MMC系統的初始化應該包含這么幾部分:SoC里的MMC控制器初始化(MMC系統時鍾的初始化、SFR初始化)、SoC里MMC相關的GPIO的初始化、SD卡/iNand芯片的初始化。
(3)mmc_devices鏈表全局變量,用來記錄系統中所有已經注冊的SD/iNand設備。所以向系統中插入一個SD卡/iNand設備,則系統驅動就會向mmc_devices鏈表中插入一個數據結構表示這個設備。
(4)cpu_mmc_init函數位於:uboot/cpu/s5pc11x/cpu.c中。實質是通過調用3個函數來完成的。
(5)setup_hsmmc_clock,在uboot/cpu/s5pc11x/setup_hsmmc.c中。看名字函數是用來初始化SoC中MMC控制器中的時鍾部分的。
(6)setup_hsmmc_cfg_gpio,在uboot/cpu/s5pc11x/setup_hsmmc.c中。看名字函數是用來配置SoC中MMC控制器相關的GPIO的。
(7)smdk_s3c_hsmmc_init函數位於:uboot/drivers/mmc/s3c_hsmmc.c中。
(8)函數內部通過宏定義USE_MMCx來決定是否調用s3c_hsmmc_initialize來進行具體的初始化操作。
(9)s3c_hsmmc_initialize函數位於:uboot/drivers/mmc/s3c_hsmmc.c中。
(10)定義並且實例化一個struct mmc類型的對象(定義了一個指針,並且給指針指向有意義的內存,或者說給指針分配內存),然后填充它的各種成員,最后調用mmc_register函數來向驅動框架注冊這個mmc設備驅動。
(11)mmc_register功能是進行mmc設備的注冊,注冊方法其實就是將當前這個struct mmc使用鏈表連接到mmc_devices這個全局變量中去。
(12)我們在X210中定義了USE_MMC0和USE_MMC2,因此在我們的uboot初始化時會調用2次s3c_hsmmc_initialize函數,傳遞參數分別是0和2,因此完成之后系統中會注冊上2個mmc設備,表示當前系統中有2個mmc通道在工作。
(5)至此cpu_mmc_init函數分析完成。
(13)find_mmc_device這個函數位於:uboot/drivers/mmc/mmc.c中。
(14)這個函數其實就是通過mmc設備編號來在系統中查找對應的mmc設備(struct mmc的對象,根據上面分析系統中有2個,編號分別是0和2)。
(15)函數工作原理就是通過遍歷mmc_devices鏈表,去依次尋找系統中注冊的mmc設備,然后對比其設備編號和我們當前要查找的設備編號,如果相同則就找到了要找的設備。找到了后調用mmc_init函數來初始化它。
2.2、SD卡/iNand內部的控制器初始化
(1)函數位於:drivers/mmc/mmc.c中。
(2)分析猜測這個函數應該要進行mmc卡的初始化了(前面已經進行了SoC端控制器的初始化)
(3)函數的調用關系為:
mmc_init
mmc_go_idle
mmc_send_cmd
mmc_send_if_cond
mmc_send_cmd
······
具體分析可以看出,mmc_init函數內部就是依次通過向mmc卡發送命令碼(CMD0、CMD2那些)來初始化SD卡/iNand內部的控制器,以達到初始化SD卡的目的。
3、總結
(1)至此整個MMC系統初始化結束。
(2)整個MMC系統初始化分為2大部分:SoC這一端的MMC控制器的初始化,SD卡這一端卡本身的初始化。前一步主要是在cpu_mmc_init函數中完成,后一部分主要是在mmc_init函數中完成。
(3)整個初始化完成后去使用sd卡/iNand時,操作方法和mmc_init函數中初始化SD卡的操作一樣的方式。讀寫sd卡時也是通過總線向SD卡發送命令、讀取/寫入數據來完成的。
(4)順着操作追下去,到了mmc_send_cmd函數處就斷了,真正的向SD卡發送命令的硬件操作的函數找不到。這就是學習驅動的麻煩之處。
(5)struct mmc結構體是關鍵。兩部分初始化之間用mmc結構體來鏈接的,初始化完了后對mmc卡的常規讀寫操作也是通過mmc結構體來鏈接的。
4、linux驅動前奏
(1)驅動的設計中有一個關鍵數據結構struct mmc。譬如MMC驅動的結構體就是struct mmc這些結構體中包含一些變量和一些函數指針,變量用來記錄驅動相關的一些屬性,函數指針用來記錄驅動相關的操作方法。這些變量和函數指針加起來就構成了驅動。驅動就被抽象為這個結構體。
(2)一個驅動工作時主要就分幾部分:驅動構建(構建一個struct mmc然后填充它)、驅動運行時(調用這些函數指針指針的函數和變量)
4.1、分離思想
(1)分離思想就是說在驅動中將操作方法和數據分開。
(2)操作方法就是函數,數據就是變量。所謂操作方法和數據分離的意思就是:在不同的地方來存儲和管理驅動的操作方法和變量,這樣的優勢就是驅動便於移植。
4.2、分層思想
(1)分層思想是指一個整個的驅動分為好多個層次。簡單理解就是驅動分為很多個源文件,放在很多個文件夾中。譬如本課程講的mmc的驅動涉及到drivers/mmc下面的2個文件和cpu/s5pc11x下的好幾個文件。
(2)以mmc驅動為例來分析各個文件的作用:
uboot/drivers/mmc/mmc.c:本文件的主要內容是和MMC卡操作有關的方法,譬如MMC卡設置空閑狀態的、卡讀寫數據等。但是本文件中並沒有具體的硬件操作函數,操作最終指向的是struct mmc結構體中的函數指針,這些函數指針是在驅動構建的時候和真正硬件操作的函數掛接的(真正的硬件操作的函數在別的文件中)。
uboot/drivers/mmc/s3c_hsmmc.c:本文件中是SoC內部MMC控制器的硬件操作的方法,譬如向SD卡發送命令的函數(s3c_hsmmc_send_command),譬如和SD卡讀寫數據的函數(s3c_hsmmc_set_ios),這些函數就是具體操作硬件的函數,也就是mmc.c中需要的那些硬件操作函數。這些函數在mmc驅動初始化構建時(s3c_hsmmc_initialize函數中)和struct mmc掛接起來備用。
分析:mmc.c和s3c_hsmmc.c構成了一個分層,mmc.c中調用了s3c_hsmmc.中的函數,所以mmc.c在上層,s3c_hsmmc.c在下層。這兩個分層后我們發現mmc.c中不涉及具體硬件的操作,s3c_hsmmc.c中不涉及驅動工程時的時序操作。因此移植的時候就有好處:譬如我們要把這一套mmc驅動移植到別的SoC上mmc.c就不用動,s3c_hsmmc.c動就可以了;譬如SoC沒變但是SD卡升級了,這時候只需要更換mmc.c,不需要更換s3c_hsmmc.即可。
(3)cpu/s5pc11x/下面還有一個setup_hsmmc.c,也和MMC驅動有關。但是這些代碼為什么不能放到drivers目錄下去,而要放到cpu目錄下去?因為這里面的2個函數(setup_hsmmc_clock和setup_hsmmc_cfg_gpio)都是和SoC有關的初始化函數,這兩個函數不能放到drivers目錄下去。實際上如果非把這兩個函數放在uboot/drivers/mmc/s3c_hsmmc.c文件中也湊活能說過去。