一、uboot與linux驅動
1、uboot是裸機程序
- 狹義的驅動的概念:操作系統中用來具體操控硬件的那部分代碼叫驅動。
- 裸機中沒有驅動的概念,因爲沒有操作系統。
- 裸機程序是直接操控硬件的,而操作系統中則是通過驅動來操控硬件。兩者的本質區別是分層。
2、uboot的虛擬地址對硬件操作的影響
(1)操作系統下,MMU是開啓的,即linux驅動使用的都是虛擬地址。純裸機程序不會開啓MMU,全部使用物理地址。
- 這是裸機下和驅動中操控硬件的一個重要區別。
(2)uboot早期也是純物理地址工作,但是現在的uboot開啓了MMU做了虛擬地址映射。
- 查uboot中的虛擬地址映射表,發現除了0x30000000-0x3FFFFFFF映射到了0xC0000000-0xCFFFFFFF之外,其餘的虛擬地址空間全是原樣映射的。
- 我們驅動中主要是操控硬件寄存器,而S5PV210的SFR都在0xExxxxxx地址空間,因此驅動中不必考慮虛擬地址。
3、uboot移植了linux驅動
(1)linux的驅動是模塊化設計。
- linux驅動本身和linux內核不是強耦合的,這是linux驅動可以被uboot移植的關鍵。
(2)uboot移植了linux驅動源代碼。
- uboot是從源代碼級別去移植linux驅動的,這就是linux系統的開源性。
(3)uboot中的硬件驅動比linux簡單。
- linux驅動本身有更復雜的框架,需要實現更多的附帶功能,而uboot本質上只是個裸機程序,uboot移植linux驅動時只是借用了linux驅動的一部分而已。
二、iNand/SD驅動解析
1、MMC驅動的初始化,是在start_armboot函數中,調用的是mmc_initialize函數
下面看一下mmc_initialize函數
(1)函數位於uboot/drivers/mmc/mmc.c。
(2)此函數主要是初始化開發板上MMC系統。
- SoC裏的MMC控制器初始化(MMC系統時鐘的初始化、SFR初始化);
- SoC裏MMC相關的GPIO的初始化;
- SD卡/iNand芯片的初始化。
(3)mmc_devices,鏈表全局變量,用來記錄系統中所有已經註冊的SD/iNand設備。
- 向系統中插入一個SD卡/iNand設備,則系統驅動就會向mmc_devices鏈表中插入一個數據結構表示這個設備。
(4)struct mmc類型的結構體指針
- 這個struct mmc類型的結構體非常重要,我們說的驅動主要就是構建這個結構體;
- 在這個結構體中構建了一些列變量、函數指針等;
- 這些變量記錄了mmc的一些信息,函數指針所指向的函數是用來向sd卡中發送命令、或者發送數據、直接操作最底層的特殊功能寄存器
下面看一下cpu_mmc_init函數
(1)函數位於uboot/cpu/s5pc11x/cpu.c中,通過調用3個函數來完成。
(2)setup_hsmmc_clock,在uboot/cpu/s5pc11x/setup_hsmmc.c中,用來初始化SoC中MMC控制器中的時鐘部分的。
(3)setup_hsmmc_cfg_gpio,在uboot/cpu/s5pc11x/setup_hsmmc.c中,用來配置SoC中MMC控制器相關的GPIO的。
下面看一下setup_hsmmc_clock函數,主要是選擇時鐘源、分頻
下面看一下setup_hsmmc_cfg_gpio函數,主要是初始化相關的GPIO
下面看一下smdk_s3c_hsmmc_init函數
(1)函數位於:uboot/drivers/mmc/s3c_hsmmc.c中。
(2)函數內部通過宏定義USE_MMCx來決定是否調用s3c_hsmmc_initialize來進行具體的初始化操作。
下面看一下s3c_hsmmc_initialize函數
(1)函數位於:uboot/drivers/mmc/s3c_hsmmc.c中。
(2)定義並且實例化一個struct mmc類型的對象
- 即定義了一個指針,給指針分配內存,然後填充它的各種成員,最後調用mmc_register函數來向驅動框架註冊這個mmc設備驅動。
(3)mmc_register功能是進行mmc設備的註冊,註冊方法其實就是將當前這個struct mmc使用鏈表連接到mmc_devices這個全局變量中去。
(4)在X210中定義了USE_MMC0和USE_MMC2
- 因此在我們的uboot初始化時,會調用2次s3c_hsmmc_initialize函數,傳遞參數分別是0和2;
- 因此完成之後系統中會註冊上2個mmc設備,表示當前系統中有2個mmc通道在工作。平常我們說的通道0和通道2?
(5)真正的操作寄存器的函數是s3c_hsmmc_send_command、s3c_hsmmc_set_ios、s3c_hsmmc_init;
- 發送命令、發送數據、初始化三個函數;
- 這三個函數是最底層的、直接操作GPIO進而特殊功能寄存器的函數;
- 這三個函數以及一些變量被封裝在struct mmc結構體中,因而操作系統對mmc設備進行操作的時候,到封裝以後的這個結構體中進行操作即可;
(6)至此cpu_mmc_init函數分析完成。
下面看一下find_mmc_device函數
(1)函數位於uboot/drivers/mmc/mmc.c中。
(2)通過mmc設備編號來在系統中查找對應的mmc設備(struct mmc的對象,根據上面分析系統中有2個,編號分別是0和2)。
- 通過遍歷mmc_devices鏈表,去依次尋找系統中註冊的mmc設備,然後對比其設備編號和我們當前要查找的設備編號,如果相同則就找到了要找的設備。
- 找到了後調用mmc_init函數來初始化它。
下面看一下mmc_init函數
(1)函數位於:drivers/mmc/mmc.c中。
(2)分析猜測這個函數應該要進行mmc卡的初始化了(前面已經進行了SoC端控制器的初始化)
(3)函數的調用關係爲:
mmc_init
mmc_go_idle
mmc_send_cmd
mmc_send_if_condmmc_send_cmd……
(4)分析可知,mmc_init函數通過依次向mmc卡發送命令碼(CMD0、CMD2那些)來初始化SD卡/iNand內部的控制器,以達到初始化SD卡的目的。
- 調用struct mmc 中的函數進行了一些時序操作
(5)send_cmd函數的細節找不到……
2、總結
(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卡發送命令的硬件操作的函數找不到。這就是學習驅動的麻煩之處。
(6)struct mmc結構體是關鍵。
- 上述兩部分初始化之間用mmc結構體來鏈接的;
- 初始化完了後對mmc卡的常規讀寫操作也是通過mmc結構體來鏈接的。
三、關於驅動的理解
1、驅動的關鍵數據結構
(1)驅動的設計中有一個關鍵數據結構。譬如MMC驅動的結構體就是struct mmc。
- 這些結構體中包含一些變量和一些函數指針,變量用來記錄驅動相關的一些屬性,函數指針用來記錄驅動相關的操作方法。
- 這些變量和函數指針加起來就構成了驅動。驅動就被抽象爲這個結構體。
(2)一個驅動工作時主要分兩部分
- 驅動構建(構建一個struct mmc然後填充它);
- 驅動運行時(調用這些函數指針指針的函數和變量);
2、分離思想
(1)分離思想,即在驅動中將操作方法和數據分開。
(2)操作方法就是函數,數據就是變量。
- 所謂操作方法和數據分離的意思就是,在不同的地方來存儲和管理驅動的操作方法和變量,這樣的優勢就是驅動便於移植。
3、分層思想
(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_hsmm。