uboot硬件驱动
uboot與linux驅(qū)動
1.uboot本身是裸機程序
(1)在裸機中本來是沒有驅(qū)動概念的(狹義的驅(qū)動概念是指在操作系統(tǒng)中用來具體操控硬件的那部分代碼叫驅(qū)動)。
(2)裸機程序是直接操控硬件的,操作系統(tǒng)必須通過驅(qū)動來操控硬件。這兩個有什么區(qū)別?本質(zhì)區(qū)別就是分層。
2.uboot虛擬地址對硬件操作的影響
(1)操作系統(tǒng)(指的是linux)下MMU肯定是開啟的,也就是說linux驅(qū)動中使用的都是虛擬地址,而純裸機程序中根本不會開MMU,全部使用的是物理地址。這是裸機和操作系統(tǒng)中操控硬件的一個重要區(qū)別。
(2)uboot早期也是工作在純物理地址下,但是現(xiàn)在的uboot開啟了MMU,做了虛擬地址映射,因此做驅(qū)動時必須考慮到這一點。
通過查看uboot中的虛擬地址映射表,發(fā)現(xiàn)除了0x30000000~0x3FFFFFFF映射到0xC0000000~0xCFFFFFFF之外,其余的地址空間全部是原樣映射,而在驅(qū)動中主要是操控硬件寄存器,而s5pv210的SFR都在0xEXXXXXXX地址空間,因此在驅(qū)動不需要考慮虛擬地址。
3.uboot借用(移植)linux驅(qū)動
(1)linux驅(qū)動本身做了模塊化設(shè)計,linux驅(qū)動本身和linux內(nèi)核不是強耦合的,這是linux驅(qū)動可以被uboot借用(移植)的關(guān)鍵。
(2)uboot移植了linux驅(qū)動源代碼,uboot是從源代碼級別去移植linux驅(qū)動的,這就是linux系統(tǒng)的開源性。
(3)uboot中的硬件驅(qū)動比linux簡單,linux驅(qū)動本身有復(fù)雜的框架,需要實現(xiàn)更多的附帶功能,而uboot本質(zhì)上只是個裸機程序,uboot移植linux驅(qū)動時,只是借用了linux驅(qū)動的一部分而已。
iNand/SD卡驅(qū)動
1.從start_armboot開始
(1)驅(qū)動整體比較龐大,涉及很多文件夾下的很多文件,函數(shù)很多,貿(mào)然插入根本不知道看哪里,學(xué)習(xí)時必須有時序。因此這里的iNand/SD卡驅(qū)動從start_armboot開始。
(2)在start_armboot函數(shù)調(diào)用mmc_initialize函數(shù)實現(xiàn)mmc的初始化,該函數(shù)在lib_arm/board.c的603行被調(diào)用。
2.mmc_initialize
代碼:1177 ~ 1208行(drivers/mmc/mmc.c)
int mmc_initialize(bd_t *bis) {struct mmc *mmc;int err;INIT_LIST_HEAD(&mmc_devices);cur_dev_num = 0;if (board_mmc_init(bis) < 0)cpu_mmc_init(bis);#if defined(DEBUG_S3C_HSMMC)print_mmc_devices(','); #endif#ifdef CONFIG_CHECK_X210CV3mmc = find_mmc_device(1);//lqm #elsemmc = find_mmc_device(0); #endifif (mmc) {err = mmc_init(mmc);if (err)err = mmc_init(mmc);if (err) {printf("Card init fail!\n");return err;}}printf("%ldMB\n", (mmc->capacity/(1024*1024/(1<<9))));return 0; }(1)從名字可以看出,這個函數(shù)的作用就是初始化開發(fā)板上的MMC系統(tǒng)。MMC系統(tǒng)的初始化應(yīng)該包含這幾部分:SoC中的MMC控制器初始化(MMC系統(tǒng)時鐘的初始化、SFR初始化)、SoC中的MMC相關(guān)的GPIO的初始化、iNand/SD卡芯片的初始化。
3.mmc_devices
(1)mmc_devices是一個鏈表(全局變量),用來記錄系統(tǒng)中所有已注冊的iNand/SD設(shè)備。所以向系統(tǒng)中插入一個iNand/SD設(shè)備,則系統(tǒng)驅(qū)動就會向mmc_devices鏈表中插入一個數(shù)據(jù)結(jié)構(gòu)表示這個設(shè)備。
4.cpu_mmc_init
代碼:232 ~ 241行(cpu/s5pc11x/cpu.c)
/** Initializes on-chip MMC controllers.* to override, implement board_mmc_init()*/ int cpu_mmc_init(bd_t *bis) { #ifdef CONFIG_S3C_HSMMCsetup_hsmmc_clock();setup_hsmmc_cfg_gpio();return smdk_s3c_hsmmc_init(); #elsereturn 0; #endif }(1)該函數(shù)調(diào)用了3個函數(shù):setup_hsmmc_clock、setup_hsmmc_cfg_gpio、smdk_s3c_hsmmc_init。
(2)setup_hsmmc_clock函數(shù):位于cpu/s5pc11x/setup_hsmmc.c中,從名字上可以看出該函數(shù)是用來初始化SoC中MMC控制器的時鐘部分的。
(3)setup_hsmmc_cfg_gpio函數(shù):位于cpu/s5pc11x/setup_hsmmc.c中,從名字上可以該函數(shù)是用來配置SoC中MMC相關(guān)的GPIO的。
(4)smdk_s3c_hsmmc_init函數(shù):位于drivers/mmc/s3c_hsmmc.c中,在它的內(nèi)部通過宏定義USE_MMCx來決定是否調(diào)用s3c_hsmmc_initialize函數(shù)來進行具體的初始化操作。
(5)s3c_hsmmc_initialize函數(shù):位于drivers/mmc/s3c_hsmmc.c中,代碼如下所示:
static int s3c_hsmmc_initialize(int channel) {struct mmc *mmc;mmc = &mmc_channel[channel];sprintf(mmc->name, "S3C_HSMMC%d", channel);mmc->priv = &mmc_host[channel];mmc->send_cmd = s3c_hsmmc_send_command;mmc->set_ios = s3c_hsmmc_set_ios;mmc->init = s3c_hsmmc_init;mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;mmc->host_caps = MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS; #if defined(USE_MMC0_8BIT) || defined(USE_MMC2_8BIT)mmc->host_caps |= MMC_MODE_8BIT; #endifmmc->f_min = 400000;mmc->f_max = 52000000;mmc_host[channel].clock = 0;switch(channel) {case 0:mmc_host[channel].ioaddr = (void *)ELFIN_HSMMC_0_BASE;break;case 1:mmc_host[channel].ioaddr = (void *)ELFIN_HSMMC_1_BASE;break;case 2:mmc_host[channel].ioaddr = (void *)ELFIN_HSMMC_2_BASE;break; #ifdef USE_MMC3case 3:mmc_host[channel].ioaddr = (void *)ELFIN_HSMMC_3_BASE;break; #endifdefault:printk("mmc err: not supported channel %d\n", channel);}return mmc_register(mmc); }函數(shù)解析:
第1點,定義并且實例化一個struct mmc類型的對象(定義了一個指針,并且給指針指向有意義的內(nèi)存,或者給指針分配內(nèi)存),然后填充它的各種成員,最后調(diào)用mmc_register函數(shù)來向驅(qū)動框架注冊這個mmc設(shè)備驅(qū)動。
第2點,mmc_register功能是進行mmc設(shè)備的注冊,注冊方法其實就是將當(dāng)前這個struct mmc使用鏈表連接到mmc_devices這個全局變量中去。
(6)在x210_sd.h中定義了USE_MMC0和USE_MMC2,因此在uboot初始化時,會調(diào)用2次s3c_hsmmc_initialize函數(shù),傳遞參數(shù)分別是0和2,因此在調(diào)用完成之后,系統(tǒng)中會注冊上2個mmc設(shè)備,表示當(dāng)前系統(tǒng)中有2個mmc通道在工作。
5.find_mmc_device
代碼:63 ~ 78行(drivers/mmc/mmc.c)
struct mmc *find_mmc_device(int dev_num) {struct mmc *m;struct list_head *entry;list_for_each(entry, &mmc_devices) {m = list_entry(entry, struct mmc, link);if (m->block_dev.dev == dev_num)return m;}printf("MMC Device %d not found\n", dev_num);return NULL; }(1)這個函數(shù)其實就是通過mmc設(shè)備編號在系統(tǒng)中查找對應(yīng)的mmc設(shè)備(struct mmc的對象,根據(jù)上面的分析可知,系統(tǒng)中有2個,分別是0和2)
(2)函數(shù)的工作原理就是通過遍歷mmc_devices鏈表,依次尋找系統(tǒng)中注冊的mmc設(shè)備,然后對比設(shè)備編號和我們當(dāng)前要查找的設(shè)備編號,如果相同,就表示找到要找的設(shè)備。找到了之后,調(diào)用mmc_init函數(shù)來初始化它。
6.mmc_init
代碼:1112 ~ 1144行(drivers/mmc/mmc.c)
int mmc_init(struct mmc *host) {int err;err = host->init(host);if (err)return err;/* Reset the Card */err = mmc_go_idle(host);if (err)return err;/* Test for SD version 2 */err = mmc_send_if_cond(host);/* Now try to get the SD card's operating condition */err = mmc_send_app_op_cond(host);/* If the command timed out, we check for an MMC card */if (err == TIMEOUT) {err = mmc_send_op_cond(host);if (err)return UNUSABLE_ERR;} elseif (err)return UNUSABLE_ERR;return mmc_startup(host); }(1)該函數(shù)的功能是初始化mmc卡。
(2)函數(shù)的調(diào)用關(guān)系為:
mmc_init
? ? ? ? mmc_go_idle
? ? ? ? ? ? ? ? mmc_send_cmd
? ? ? ? mmc_send_if_cond
? ? ? ? ? ? ? ? mmc_send_cmd
······
從以上的分析可以看出,mmc_init函數(shù)內(nèi)部就是依次通過向mmc卡發(fā)送命令碼(CMD0、CMD2...)來初始化iNand/SD卡內(nèi)部的控制器,以達到初始化SD卡的目的。
總結(jié)
1.整個MMC的初始化分為2部分:SoC這一端的MMC控制器的初始化、SD卡這一端的本身的初始化。前一部分主要是在cpu_mmc_init函數(shù)中完成,后一部分主要是在mmc_init函數(shù)中完成。
2.整個初始化完成后去使用iNand/SD卡時,操作方法和mmc_init函數(shù)中初始化SD卡的的操作一樣。讀寫SD卡時也是通過總線向SD卡發(fā)送命令、讀取/寫入數(shù)據(jù)來完成的。
3.順著操作追下去,到了mmc_send_cmd函數(shù)處就斷了,真正的向SD卡發(fā)送命令的硬件操作的函數(shù)找不到。這就是學(xué)習(xí)驅(qū)動的麻煩之處。
4.struct mmc結(jié)構(gòu)體是關(guān)鍵,兩部分初始化之間是用mmc結(jié)構(gòu)體來鏈接的,初始化完了之后對mmc卡的常規(guī)讀寫操作也是通過mmc結(jié)構(gòu)體來鏈接的。
驅(qū)動結(jié)構(gòu)體分析
1.數(shù)據(jù)結(jié)構(gòu)
(1)驅(qū)動設(shè)計中有一個關(guān)鍵數(shù)據(jù)結(jié)構(gòu),例如MMC驅(qū)動的結(jié)構(gòu)體struct mmc,這些結(jié)構(gòu)體中包含一些變量和一些函數(shù)指針,變量用來記錄驅(qū)動相關(guān)的一些屬性,函數(shù)指針用來記錄驅(qū)動相關(guān)的一些操作方法,這些變量和函數(shù)指針加起來就構(gòu)成了驅(qū)動,驅(qū)動就被抽象為這個結(jié)構(gòu)體。
(2)驅(qū)動工作的時候主要分為兩部分:驅(qū)動構(gòu)建(構(gòu)建一個struct mmc,然后填充它)、驅(qū)動運行(調(diào)用結(jié)構(gòu)體中變量和函數(shù)指針)
2.分離思想
(1)分離思想就是說在驅(qū)動中將操作方法和數(shù)據(jù)分開。
(2)操作方法就是函數(shù),數(shù)據(jù)就是變量。所謂操作方法和數(shù)據(jù)分離的意思是:在不同的地方來存儲和管理驅(qū)動的操作方法和變量,這樣的優(yōu)勢就是驅(qū)動便于移植。
3.分層思想
(1)分層思想是指一個整體的驅(qū)動分為好多個層次,簡單理解就是驅(qū)動分為很多個源文件,放在很多個文件夾中。例如本課程講的mmc的驅(qū)動設(shè)計到drivers/mmc下面的2個文件和cpu/s5pc11x下面的幾個文件。
(2)以mmc驅(qū)動為例來分析各個文件的作用:
drivers/mmc/mmc.c:本文件的主要內(nèi)容是和MMC卡操作有關(guān)的方法,例如MMC卡設(shè)置為空閑狀態(tài)、卡讀寫數(shù)據(jù)等,但是本文件中并沒有具體的硬件操作函數(shù),操作最終指向的是struct mmc結(jié)構(gòu)體中的函數(shù)指針,這些函數(shù)指針是在驅(qū)動構(gòu)建的時候和真正的硬件操作函數(shù)掛接的(真正的硬件操作函數(shù)在別的文件中)
drivers/mmc/s3c_hsmmc.c:本文件是SoC內(nèi)部MMC控制器的硬件操作方法,例如向SD卡發(fā)送命令的函數(shù)(s3c_hsmmc_send_command)、SD卡讀寫數(shù)據(jù)的函數(shù)(s3c_hsmmc_set_ios)等,這些函數(shù)就是具體操作硬件的函數(shù),也就是mmc.c中需要的那些硬件操作函數(shù)。這些函數(shù)在mmc驅(qū)動初始化構(gòu)建時(s3c_hsmmc_initialize函數(shù)中)和struct mmc掛接起來備用。
分析:mmc.c和s3c_hsmmc.c構(gòu)成了一個分層,mmc.c中調(diào)用了s3c_hsmmc.c中的函數(shù),所以mmc.c在上層,s3c_hsmmc.c在下層。有這個分成后,可以發(fā)現(xiàn)mmc.c中不涉及具體硬件的操作,s3c_hsmmc.c中不涉及時序操作。因此移植的時候就有好處:例如要把這一套mmc驅(qū)動移植到別的SoC上的,那mmc.c就不用動,s3c_hsmmc.c動就可以了;又例如SoC沒變,但是SD卡升級了,這時候只需要更換mmc.c,不需要更換s3c_hsmmc.c。
(3)cpu/s5pc11x下還有一個setup_hsmmc.c,也和MMC驅(qū)動有關(guān),但是這些代碼為什么不放到drivers目錄下,而是放到cpu目錄下?因為這里面的兩個函數(shù)(setup_hsmmc_clock和setup_hsmmc_cfg_gpio)都是和SoC有關(guān)的初始化函數(shù),這兩個函數(shù)不能放到drivers目錄下,實際上如果非把這兩個函數(shù)放在drivers/mmc/s3c_hsmmc.c中也能湊合說得過去。
總結(jié)
- 上一篇: 成为被 BAT 疯抢的数据分析师,要如何
- 下一篇: S5PV210裸机之时钟