各位工程師,你們好,我是alan,今天就瑞芯微平臺和北京君正平臺下的linux系統(tǒng)中關(guān)于SD NAND的使用做一些經(jīng)驗的分享,如有不正,請聯(lián)系我們批評指正;
采用的開發(fā)板是RK3568和x2600e,ubuntu版本是20.04,交叉編譯工具鏈?zhǔn)莂arch64-linux-gnu-和mips-linux-gnu-;
下面將從五個板塊來進行介紹,分別是操作SD NAND的常用命令、SD底層協(xié)議簡要介紹、對SD NAND進行讀寫操作的三大方式、SD的驅(qū)動框架介紹以及SD NAND啟動,前三個板塊沒有瑞芯微和君正平臺之分,只要是跑linux系統(tǒng),差別不大,第四塊以RK平臺為例,第五塊同時以君正平臺和RK平臺為例。
一:操作SD NAND的常用命令
1.查看SD設(shè)備:lsblk或fdisk -l,設(shè)備節(jié)點一般為/dev/mmcblkX或/dev/sdX,eg:mmcblk0;
2.掛載分區(qū):mount 設(shè)備分區(qū) 掛載點 eg:mount /dev/mmcblk0p1 /mnt/sdcard
3.卸載分區(qū):umount 設(shè)備分區(qū)/掛載點 eg:umount /dev/mmcblk0p1 或 umount /mnt/sdcard
4.分區(qū)管理:fdisk 設(shè)備節(jié)點eg:fdisk /dev/mmcblk0
進入交互頁面后常用命令:
p 打印分區(qū)表
n 創(chuàng)建新分區(qū)
d 刪除分區(qū)
t 更改分區(qū)類型
w 將更改寫入磁盤并退出
q 不保存更改退出
m 顯示幫助菜單
l 列出已知的分區(qū)類型
v 驗證分區(qū)表
g 創(chuàng)建新的空GPT分區(qū)表
o 創(chuàng)建新的空DOS分區(qū)表
在進行分區(qū)管理前請務(wù)必備份重要數(shù)據(jù),因為在更改生效后會丟失原來數(shù)據(jù);
5.格式化分區(qū):
格式化為FAT32:mkfs.vfat 設(shè)備分區(qū);eg: mkfs.vfat /dev/mmcblk0p1
格式化為ext4:mkfs.ext4 設(shè)備分區(qū);eg:mkfs.ext4 /dev/mmcblk0p2
6.簡單讀寫:cp,cat,echo等命令
eg:cp /usr/data/1.txt /mnt/sdcard/2.txt
cat /mnt/sdcard/test.c
echo "測試內(nèi)容" > /mnt/sdcard/test.txt
二:SD底層協(xié)議簡要介紹(由于SD NAND和SD卡遵守相同協(xié)議,并且標(biāo)準(zhǔn)協(xié)議中使用SD卡來描述,因此以下用詞使用SD卡代替SD NAND)
此處是為下面介紹讀寫方式準(zhǔn)備協(xié)議的理論知識,僅做簡要介紹以及提醒一些需要注意的點,完整的協(xié)議內(nèi)容較多,詳細(xì)請參考SD2.0協(xié)議標(biāo)準(zhǔn)完整版(參考附件);
SD驅(qū)動中最重要的部分就是初始化,這里描述一下初始化流程,(需要區(qū)別四類卡,SDHC卡,SDSC卡,SD1.X卡,mmc卡)先是給SD卡上電,通常這一步在將卡插入卡槽就會自動完成,然后是發(fā)送CMD0進行軟復(fù)位,進入空閑模式,再發(fā)送CMD8,主機詢問SD卡是否支持電壓范圍,(SD1.X和mmc卡不會對CMD8產(chǎn)生響應(yīng),只有SDHC和SDSC卡會對CMD8回復(fù)R1響應(yīng));如果不響應(yīng)再區(qū)分SD1.X和mmc卡,發(fā)送ACMD41(先發(fā)送CMD55告訴SD卡接下來發(fā)送的是應(yīng)用命令),如果不響應(yīng)就說明是mmc卡(此時發(fā)送CMD1激活mmc卡,mmc卡響應(yīng)后即完成mmc卡的初始化),如果回復(fù)R1相應(yīng)就說明是V1.X卡;
收到R1響應(yīng)再區(qū)分SDHC和SDSC,然后發(fā)送ACMD41,主機告訴SD卡支持高容量,根據(jù)返回的R3響應(yīng)中的OCR寄存器來判斷是標(biāo)準(zhǔn)還是高容量,只有當(dāng)busy位置1時CCS位才有效,CCS位為1是高容量V2.0卡(即SDHC卡),CCS位為0是標(biāo)準(zhǔn)V2.0卡(即SDSC卡),至此區(qū)分出了V1.X,標(biāo)準(zhǔn)V2.0,高容量V2.0卡,這三種卡接下來的步驟一致:
發(fā)送CMD2獲取CID寄存器的值,SD卡會回復(fù)R2響應(yīng),再發(fā)送CMD3,SD卡獲得相對地址,流程圖如下圖所示:

然后是數(shù)據(jù)傳輸模式,初始化完成后進行數(shù)據(jù)傳輸就相對簡單了,只需要發(fā)送對應(yīng)的命令即可,流程圖如下:

下面附上常用命令以及響應(yīng)的圖:


最后提醒幾個需要注意的點:
第一是上電之后有一個時間段叫“供電上升時間”,這個是電壓上升到操作的總線電平以及等到能發(fā)送第一條命令的時間,這個時間需要在1ms,74個時鐘周期以及供電上升時間這三者中取最大值,其實這點在驅(qū)動源碼中也有體現(xiàn),在mmc_power_up函數(shù)(drivers/mmc/core/core.c)如下圖所示,同時在協(xié)議的6.4.1章節(jié)也有說明,這個時間如果沒有等待而直接開始發(fā)送命令進行初始化,可能也能通過初始化,但是在后面的數(shù)據(jù)傳輸階段就有概率會出現(xiàn)問題了,如果沒有注意到這個供電上升時間,那么其實出現(xiàn)了異常是很難定位問題的;

第二是SD卡的兩種數(shù)據(jù)包格式,分別是常規(guī)數(shù)據(jù)和寬位數(shù)據(jù),常規(guī)數(shù)據(jù)是指普通的8bit字節(jié)數(shù)據(jù),發(fā)送規(guī)則是先發(fā)低字節(jié)再發(fā)高字節(jié),每個字節(jié)是先發(fā)高位后發(fā)低位;而寬位數(shù)據(jù)一般指SD卡存儲寄存器,規(guī)則是先發(fā)高位,后發(fā)低位, 這點在解析SD卡寄存器時需要格外注意,否則就會發(fā)現(xiàn)解析的數(shù)據(jù)明顯不符規(guī)范;

三:對SD NAND進行讀寫操作的三大方式
SD NAND作為一種存儲設(shè)備,不外乎就是讀和寫,同時這也是最重要的,熟悉讀寫方式對于使用SD NAND開發(fā)非常有幫助,因此在這一塊會詳細(xì)介紹;
第一:使用dd命令:
1.使用前先掛載分區(qū)的文件系統(tǒng)(常用于寫入普通文件)
sudo dd if=輸入文件 of=輸出文件 bs=塊大小 status=狀態(tài)信息 其他選項參數(shù)
eg:sudo dd if=/mnt/sdcard/test.doc of=backup.doc bs=1M count=1
//將/mnt/sdcard/test.doc的前1M字節(jié)寫入backup.doc;
2.使用前不掛載分區(qū)的文件系統(tǒng)(常用于寫入鏡像文件,備份或擦除整個SD NAND等)
sudo dd if=輸入設(shè)備 of=輸出設(shè)備 bs=塊大小 status=狀態(tài)信息 其他選項參數(shù)
eg:sudo dd if=sd_back_up.img of=/dev/mmcblk0 bs=4M status=progress
//將sd_back_up.img以4MB的塊大小寫入/dev/mmcblk0,同時顯示進度和速度
sudo dd if=/dev/mmcblk1p1 of=/root/zboot.img bs=4M status=progress
//將mmcblk1p1設(shè)備中的所有數(shù)據(jù)讀取到/root/zboot.img文件下
如果要對讀取的數(shù)據(jù)進行限制,只讀取部分?jǐn)?shù)據(jù),那么使用參數(shù)skip或者count
sudo dd if=/dev/sdX of=/path/to/output.img bs=4M skip=10 status=progress
//skip:跳過前10個塊再開始讀取數(shù)據(jù)
sudo dd if=/dev/sdX of=/path/to/output.img bs=4M count=20 status=progress
//count:只讀取20個塊的數(shù)據(jù)
也可以結(jié)合skip和count,實現(xiàn)從特定位置讀取指定數(shù)量的數(shù)據(jù)
;
!!!注意:若使用前不掛載分區(qū),那么在寫入之前請備份SD NAND內(nèi)重要數(shù)據(jù),因為不掛載是繞過文件系統(tǒng)對原始存儲設(shè)備直接操作,會完全忽略文件系統(tǒng)結(jié)構(gòu),很可能會覆蓋已有數(shù)據(jù),甚至可能會損壞文件系統(tǒng),嚴(yán)重的話只能重新格式化;
如果是先掛載了分區(qū),那么dd命令會通過文件系統(tǒng)進行操作,相對安全;
第二:使用塊設(shè)備的標(biāo)準(zhǔn)接口,通過文件io或標(biāo)準(zhǔn)io進行讀寫,無論是掛載文件系統(tǒng)還是不掛載文件系統(tǒng),都能使用文件io或標(biāo)準(zhǔn)io對SD NAND進行操作,區(qū)別是前者通過文件系統(tǒng)較為安全且效率稍低而后者直接操作硬件存儲設(shè)備效率更高同時對于數(shù)據(jù)的寫入需要更加注意防止覆蓋重要數(shù)據(jù),下面分別提供兩個示例;
第一個示例:使用標(biāo)準(zhǔn)io(fopen,fwrite,fread,fseek,fclose等)在已掛載文件系統(tǒng)的SD NAND上進行讀寫,總所周知,在linux里流傳著一句話,那就是“一切皆文件”,在這種情況下操作掛載點的文件其實與操作一般文件基本無異;示例實現(xiàn)的功能是在SD卡掛載目錄/mnt/sdcard/目錄下創(chuàng)建test_data.txt文件,然后再往里面寫入4次0~255,然后再將數(shù)據(jù)讀取出來驗證是否寫入成功并打印測試結(jié)果;
編譯命令是aarch64-linux-gnu-gcc test2.c -o test2,編譯成功后使用sftp root@ip將test2發(fā)送至開發(fā)板運行測試,運行結(jié)果以及程序見下面兩張圖;


第二個示例:使用文件io(open,write,read,lseek,close)對未掛載文件系統(tǒng)的SD NAND直接操作;實現(xiàn)的功能是從SD NAND的第1000塊開始寫入512字節(jié)數(shù)據(jù),數(shù)據(jù)內(nèi)容為0~255,0~255,寫入后再把數(shù)據(jù)讀取出來比較是否成功并打印結(jié)果;
編譯命令是:aarch64-linux-gnu-gcc test.c -o test,編譯成功后使用sftp root@ip將test2發(fā)送至開發(fā)板運行測試,運行結(jié)果和程序也如下面兩張圖所示


第三:使用ioctl產(chǎn)生系統(tǒng)調(diào)用,陷入內(nèi)核進行處理,前兩種方式都容易實現(xiàn),而且不需要關(guān)注底層發(fā)送的命令,而ioctl可以對SD NAND進行精細(xì)的命令控制來約束NAND的行為,并且在ioctl使用過程中還有一些需要注意的點,如果想要對SD NAND進行最為底層的命令操作,那么ioctl必定是是首選,因此著重介紹這種方式;
使用ioctl來對SD NAND發(fā)送命令,其中最為重要的就是填充struct mmc_ioc_cmd結(jié)構(gòu)體,結(jié)構(gòu)體的詳細(xì)定義位于kernel/include/uapi/linux/mmc/ioctl.h,其中關(guān)于flags即命令標(biāo)志位掩碼的定義位于/include/linux/mmc/core.h文件中的struct mmc_command結(jié)構(gòu)體中,下面對struct mmc_ioc_cmd結(jié)構(gòu)體的各成員作詳細(xì)介紹,如下圖所示:

介紹完struct mmc_ioc_cmd結(jié)構(gòu)體后細(xì)心的小伙伴會發(fā)現(xiàn)了這里面幾乎所有的參數(shù)都好填充,唯獨flags,命令標(biāo)志位掩碼的定義在core.h中,如下圖所示:

那么在編寫ioctl的程序時,填充flags需要用到core.h里面的內(nèi)容,總所周知,linux里面的隔離是很嚴(yán)重的,在應(yīng)用層調(diào)用內(nèi)核層的定義,這是不允許的,編譯會報錯,之前剛開發(fā)的時候因為這個編譯報錯還折騰了不少時間,最終的解決辦法就是把這些標(biāo)志位的宏定義復(fù)制到自己寫的應(yīng)用程序中,這樣就沒有報錯了,下面提供一份程序
程序較長,文件名是init.c(見附件),編譯命令是:aarch64-linux-gnu-gcc init.c -o init,編譯成功后使用sftp root@ip(若有adb功能也可使用adb push)將可執(zhí)行程序init發(fā)送至開發(fā)板進行運行測試,文件中包含ioctl使用的詳細(xì)解釋,下面三張圖是運行結(jié)果截圖(前兩張)以及使用邏輯分析儀抓取的命令(第三張圖),波形文件名是:init.TLW,見附件,兩者都與程序一致,運行過程中沒有報錯,這就是使用ioctl向SD NAND發(fā)送底層命令的詳細(xì)過程;



關(guān)于ioctl的使用,有下面幾點需要格外注意,這些都是小編親身走過的坑,所謂前人栽樹,后人乘涼, 也算方便大家“乘涼”了哈哈;
第一是如果進行的是寫操作,例如發(fā)送CMD24,CMD25等命令,那么struct mmc_ioc_cmd結(jié)構(gòu)體的write_flag必須要賦值為非零值,如果是0值,那么很容易導(dǎo)致發(fā)送命令失敗,得到“errno=110 (Connection timed out)”的錯誤信息,這個錯誤信息相對常見,意為連接超時,分析函數(shù)調(diào)用鏈可知:在__mmc_blk_ioctl_cmd(drivers/mmc/core/block.c)函數(shù)中有

根據(jù)write_flag的值會進一步增加MMC_DATA_WRITE或MMC_DATA_READ的標(biāo)志再傳到驅(qū)動中,因此發(fā)送寫命令時請務(wù)必給write_flag賦非零值;
第二是在SD卡初始化完成后,開始數(shù)據(jù)傳輸前,需要發(fā)送ACMD6定義數(shù)據(jù)總線的寬度,分析源碼可知:在mmc_sd_init_card(drivers/mmc/core/sd.c)函數(shù)中有如下圖所示部分

SD和主機通常都是同時支持4位數(shù)據(jù)總線寬度的,在不修改SD驅(qū)動的情況下,主機控制器是4位總線寬度,所以如果不發(fā)送ACMD6,那么卡會保持默認(rèn)的1位總線寬度,此時主機和NAND的總線寬度不一致,就會出現(xiàn)“errno=84 (Invalid or incomplete multibyte or wide character)”的錯誤信息,含義是無效或不完整的多字節(jié)或?qū)捵址鉀Q辦法有兩種,一種是修改驅(qū)動源碼,將主機控制器的總線寬度固定為1位,此時可以不發(fā)送ACMD6,主機和 SD NAND都使用1位數(shù)據(jù)總線通信,第二種是發(fā)送ACMD6通知卡改變總線寬度為4位,兩者都用4位總線傳輸數(shù)據(jù),ACMD6的參數(shù)說明如下圖,很明顯要第二種方式更簡單,因此如果遇到errno=84的錯誤碼,檢查一下主機和SD NAND的總線寬度是否一致;

第三是頻率問題,如果使用ioctl發(fā)送cmd時,檢查了命令和各項參數(shù)都沒有錯,也沒犯上面兩種錯誤,但是運行程序發(fā)現(xiàn)就是會報各種錯,例如下圖(運行的是示例程序init)

那么很大概率就是頻率太高,SD NAND接受不了,此時需要降頻,如下圖

降低頻率后再次運行,運行結(jié)果見下圖

只要多加注意以上三點,相信使用ioctl就沒有什么大問題;
四:SD NAND的驅(qū)動框架介紹(以瑞芯微平臺的RK3568驅(qū)動源碼為例)
第一:MMC/SD驅(qū)動在linux中的結(jié)構(gòu)層次
通常在linux系統(tǒng)中,MMC/SD設(shè)備都是被抽象成塊設(shè)備來處理,在kernel的頂層目錄下的drivers/mmc目錄下通常有三個文件夾分別是core、card和host,有些驅(qū)動會將core和card合并成一個core,例如RK3568就是只有core和host,這個驅(qū)動框架就是以RK平臺的SD驅(qū)動來介紹的,下面解釋三個文件夾的作用;
1.card層:要把操作的數(shù)據(jù)以塊設(shè)備的處理方式寫到存儲設(shè)備上或從存儲設(shè)備上讀取;因為SD NAND屬于塊設(shè)備,那么必然要提供塊設(shè)備的驅(qū)動程序,這部分就是解決了一個問題,即如何將你的SD NAND實現(xiàn)為塊設(shè)備的。
2.core 層:則是將數(shù)據(jù)以何種格式,何種方式在 MMC/SD主機控制器與MMC/SD卡的記 憶體(即塊設(shè)備)之間進行傳遞,這種格式、方式被稱之為規(guī)范或協(xié)議.
這部分完成了不同協(xié)議和規(guī)范的實現(xiàn),抽離出不同SD主機控制器的共性,并為HOST 層的驅(qū)動提供了接口函數(shù)
3.host 層:是這個文件夾屬于 Linux 內(nèi)核中 MMC/SD 子系統(tǒng) 的 硬件驅(qū)動層,直接負(fù)責(zé)與 MMC/SD 主機控制器(Host Controller) 的硬件交互。它的核心作用是向上提供統(tǒng)一的接口供 Core 層 調(diào)用(如發(fā)送命令、讀寫數(shù)據(jù)),向下為不同廠商的 Host 控制器提供驅(qū)動實現(xiàn),將上層(Core 層)的協(xié)議請求轉(zhuǎn)換為具體的寄存器操作、時鐘控制、DMA 傳輸?shù)扔布袨椤?/p>
core層根據(jù)協(xié)議規(guī)范來構(gòu)造各種命令,那么命令是怎么發(fā)送給SD NAND呢?通過主機控制器。
主機控制器通過設(shè)置SD需要的gpio資源,注冊中斷資源,使能控制器等等,然后再向上面的核心層增加一個host,這樣核心層就能調(diào)用具體的硬件操作函數(shù)來和SD卡通信了;
card和core是封裝好的共性以及規(guī)范,通常是不需要修改的,而host層是直接與硬件打交道,需要控制底層寄存器的,不同的host控制器硬件資源也不一樣,因此驅(qū)動SD NAND,host層才是應(yīng)該需要修改開發(fā)的,MMC/SD驅(qū)動在linux中的結(jié)構(gòu)層次見下圖:

第二:SD驅(qū)動中核心的數(shù)據(jù)結(jié)構(gòu)舉例(SD NAND和SD卡在驅(qū)動中使用的數(shù)據(jù)結(jié)構(gòu)和調(diào)用的函數(shù)是一致的,并且有不少數(shù)據(jù)結(jié)構(gòu)命名或函數(shù)功能注釋翻譯過來用SD卡描述更貼切,因此下面描述用詞使用SD卡代替SD NAND,不再贅述)
1.struct mmc_host
功能:表示一個MMC/SD卡主機控制器,它是驅(qū)動程序和內(nèi)核MMC子系統(tǒng)之間的主要接口;
重要成員:
const struct mmc_host_ops *ops:包含操作該主機的各種函數(shù)指針,包括發(fā)送命令,設(shè)置時鐘和電源,請求操作等,用于和硬件交互
struct device class_dev:代表該主機控制器的設(shè)備對象,可用于設(shè)備模型的注冊和管理
unsigned int f_min:主機控制器支持的最小時鐘頻率
unsigned int f_max:主機控制器支持的最大時鐘頻率,對于SD卡的操作頻率非常重要
struct mmc_card *card:指向插入該主機的SD卡設(shè)備
struct mmc_ios ios:包含了當(dāng)前IO的狀態(tài)信息,例如時鐘頻率,電壓范圍,電源模式等
const struct mmc_bus_ops *bus_ops:指向struct mmc_bus_ops結(jié)構(gòu)體,定義了和MMC卡通信的操作集,例如讀寫,卡檢測等;
u32 ocr_avail:存儲MMC主機可用的操作條件寄存器OCR的值,
u32 caps:表示MMC主機的能力和特性,通過不同的位來標(biāo)記主機的各種模式;
2. struct mmc_card
功能:表示一個插入到MMC主機控制器的SD卡設(shè)備
重要成員:
struct mmc_host *host:指向SD卡所連接的主機控制器
unsigned int rca:相對卡地址,是SD卡的重要標(biāo)識符,用于在總線上唯一標(biāo)識該卡
unsigned int type:卡類型,例如MMC,SD,SDIO等,用于區(qū)分不同的設(shè)備類型
u32 ocr:操作條件寄存器,包含了SD卡的操作條件信息,例如支持的電壓范圍,電源模式等;
struct mmc_cid cid:包含卡的CID信息,例如制造商id,產(chǎn)品名稱等;
3. struct mmc_ios
功能:包含SD主機控制器的IO狀態(tài)信息
重要成員:
unsigned int clock:當(dāng)前的時鐘頻率,驅(qū)動可以根據(jù)需要調(diào)整此時鐘頻率,來滿足不同操作的需求;
unsigned char power_mode:電源模式,例如MMC_POWER_OFF,MMC_POWER_UP,用于控制SD卡的電源狀態(tài)
unsigned char bus_width:總線寬度,可以選擇1線,4線,8線,根據(jù)SD卡的能力和操作需求進行調(diào)整
4. struct mmc_host_ops
功能:包含了操作MMC主機控制器的一系列函數(shù)指針,是驅(qū)動程序與硬件交互的接口;
重要成員舉例:
void (*request)(struct mmc_host *host, struct mmc_request *req):函數(shù)指針,用于將一個操作請求添加到主機控制器的請求隊列中
void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios):用于設(shè)置主機控制器的IO狀態(tài),例如調(diào)整時鐘頻率,電源模式和總線寬度等等;
int (*start_signal_voltage_switch)(struct mmc_host *host, struct mmc_ios *ios):在需要切換SD卡的操作電壓時調(diào)用此函數(shù);
int (*get_ro)(struct mmc_host *host):檢查MMC/SD卡是否被寫保護了;
int (*get_cd)(struct mmc_host *host):檢查SD卡的插入和拔出;
5. struct mmc_request
功能:表示一個對SD卡的操作請求,通常包括命令,數(shù)據(jù)傳輸?shù)刃畔ⅲ?/p>
重要成員:
struct mmc_command *cmd:指向要執(zhí)行的命令;
struct mmc_data *data:指向要傳輸?shù)臄?shù)據(jù)對象,用于數(shù)據(jù)塊的讀寫操作;
第三:host層函數(shù)調(diào)用關(guān)系解析

這里主要做了兩件事,其一是mmc_alloc_host分配一個mmc_host,其二是mmc_add_host添加一個mmc_host;
第四:卡的檢測

我們用的SD卡只是一張卡,要操作卡還得通過主機控制器才行,因此會有struct mmc_card,struct mmc_host之分,截至這里再回憶一下dw_mci_init_slot做的事情,大概就是準(zhǔn)備一個 mmc_host 結(jié)構(gòu),然后添加一個主控制器設(shè)備到內(nèi)核,最后又調(diào)用了一下 mmc_rescan 來檢測是不是有卡插入了,有人會問如果此時卡沒有插入呢,那么不就是白白調(diào)用一次mmc_rescan,那下次卡插入又是怎么檢測到的呢,很明顯,這種不能確定觸發(fā)時間的完全未知的動作,肯定是需要通過中斷機制來處理,檢測到卡插入,觸發(fā)中斷,掃描卡,開始初始化等流程,檢測到卡拔出觸發(fā)中斷,清理各類資源,釋放空間;
第五:掃描流程


struct mmc_card 結(jié)構(gòu)里面包含了一個 struct device 結(jié)構(gòu), mmc_alloc_card 不但申請了內(nèi)存,而且還填充了 struct device 中的幾個成員,尤其 card->dev.bus = &mmc_bus_type; 這一句要重點對待,mmc_bus_type的定義如下圖

申請一個 mmc_card 結(jié)構(gòu),并簡單初始化后, mmc_init_card 的使命就完成了,然后再調(diào)用 mmc_add_card 將這個 card 設(shè)備添加到內(nèi)核。 mmc_add_card 其實很簡單,就是調(diào)用 device_add 將 card->dev 添加到內(nèi)核當(dāng)中去;
device_add 里面,設(shè)備對應(yīng)的總線會拿著你這個設(shè)備和掛在這個總線上的所有驅(qū)動程序去匹配( match ),此時會調(diào)用 match 函數(shù)(如下圖),如果匹配到了就會調(diào)用總線的 probe 函數(shù)或驅(qū)動的 probe 函數(shù);

所以match永遠(yuǎn)不會失敗,匹配成功就會執(zhí)行mmc_bus_probe(定義見下圖)

追蹤到這里,probe由調(diào)用了drv->probe(),這就需要知道drv的定義了,struct mmc_driver*drv=to_mmc_driver(dev->driver);match 函數(shù)總是返回 1 ,說明掛在這條總線上的 driver 都有可能跑到這里來了,事實的確也是這樣的,不過好在掛在這條總線上的 driver 只有一個,定義見下圖:

因此跳轉(zhuǎn)到mmc_bllk_probe函數(shù)執(zhí)行,函數(shù)定義如下:

mmc_blk_probe是 MMC 塊設(shè)備驅(qū)動的核心探測函數(shù),負(fù)責(zé)將 MMC/SD 卡注冊為塊設(shè)備,使其能夠被系統(tǒng)訪問,看到這里已經(jīng)把core,host目錄下的文件都牽扯進來了,慢慢再捋一下就能看出從host到core的聯(lián)系了;
第五:數(shù)據(jù)的讀寫
在驅(qū)動中,向SD卡發(fā)送cmd都是通過請求隊列來完成的,所有的cmd都會被封裝成請求,然后由內(nèi)核的I/O調(diào)度器統(tǒng)一排序,合并或拆分,再下發(fā)給硬件驅(qū)動,這樣避免了驅(qū)動直接操作硬件,而是通過標(biāo)準(zhǔn)接口解耦,增強了安全性和可維護性,所以分析cmd的發(fā)送首先要定位到request請求隊列,關(guān)鍵函數(shù)調(diào)用流程如下圖:

順便附上上圖中提到的兩個ops操作集合的賦值:


分析到這里,整個SD驅(qū)動框架做的事情說簡單一些其實就干了兩件事,一是卡的掃描檢測,而是數(shù)據(jù)的讀寫,不過這個過程中涉及到的數(shù)據(jù)結(jié)構(gòu)和函數(shù)調(diào)用確實相對復(fù)雜,捋清楚驅(qū)動框架還是需要靜下心來花費時間的;
五:SD NAND啟動
相同的板卡廠商一般SD NAND啟動的流程固定,大致講一下流程,首先是要制作SD啟動卡,即將啟動鏡像燒錄進SD NAND,一般板卡廠商都會有專門的制作工具,只需按照使用方法來制作啟動卡即可,如果沒有制作工具,那么一般使用dd命令將系統(tǒng)鏡像寫入卡內(nèi)(寫入時可能有地址參數(shù)要求,需要咨詢板卡廠商的技術(shù)支持),然后是選擇啟動方式,一般是有幾個boot引腳,啟動時系統(tǒng)會根據(jù)boot引腳的電平組合來選擇啟動方式,有些開發(fā)板可能在選擇SD NAND啟動時還需要配置其他內(nèi)核參數(shù),只需咨詢對應(yīng)的技術(shù)即可,最后就是驗證系統(tǒng)是否能正常從SD啟動,數(shù)據(jù)讀寫是否正常;
首先是君正平臺,開發(fā)板是x2600e,SD NAND是采用CS256G-AOW;
第一:制作SD啟動卡
君正的燒錄工具支持將啟動鏡像燒錄到SD NAND,先編譯準(zhǔn)備好啟動鏡像,然后按照下圖順序進行制作啟動卡;





第二:選擇啟動方式
查詢芯片手冊得知啟動方式的選擇依據(jù)如下圖

再查看原理圖如下:

可知目前的電平組合是001,即選擇的是SFC@PD06_3.3V(默認(rèn)從SPI NAND FLASH啟動),現(xiàn)在需要改為從SD啟動,那么就要將BOOT_SET0即PD14接低電平,使得電平組合為000,然后就是查看PCB,找到PD14的gpio,再修改硬件將其接地即可;
第三:驗證SD啟動

通過上圖可知,系統(tǒng)成功從SD NAND啟動,然后使用一些簡單的讀寫命令驗證是否正常即可,一般而言只要能正常啟動系統(tǒng)讀寫就沒問題,因為啟動過程中本身就已經(jīng)包含了對SD進行讀寫。
然后就是瑞芯微平臺,開發(fā)板是RK3568,SD NAND采用CS256G-AOW,也采取相同步驟;
第一:制作SD啟動卡
首先編譯準(zhǔn)備好鏡像文件,并將其拷貝到windows端(可以將鏡像放置共享目錄下),然后打開瑞芯微的制作工具SDDiskTool,按照下圖所示進行配置:



正常來說制作過程是2~3分鐘,但是也遇到過十分鐘左右的,這個可能因SD NAND而異,但是只要沒報錯,就耐心等待制作完成;
第二:選擇啟動方式
由于咨詢技術(shù)支持得到的答復(fù)是RK對于啟動引導(dǎo)不開源,原理也是靠硬件選boot腳,然后在芯片手冊上也沒找到有關(guān)boot引腳配置的部分,所以說明在RK上我們不需要修改硬件,額外配置boot引腳來選擇啟動方式,后面在rockchip-common.h文件中找到了關(guān)于啟動方式的優(yōu)先順序,如下圖:

因此只要啟動時在卡槽檢測到SD NAND,那么就會優(yōu)先從SD啟動;
第三:驗證SD啟動


從上面兩張圖中可以相互印證,系統(tǒng)成功從SD啟動;
啟動卡的分區(qū)情況如下圖:

從mmcblk1p1至mmcblk1p6共6個分區(qū),依次是uboot,misc,boot,recovery,backup和rootfs,前面5個分區(qū)一般存儲特定數(shù)據(jù),不建議用戶將數(shù)據(jù)寫入,而mmcblk1p6是根文件系統(tǒng)分區(qū),存儲所有系統(tǒng)文件、應(yīng)用程序、用戶數(shù)據(jù)等,進行用戶操作時就是基于這個分區(qū),因此進行讀寫測試或者存儲用戶數(shù)據(jù)建議在此分區(qū)進行。
最后分享一些使用的軟件:
串口工具:Windows下:MobaXterm;Ubuntu下:Minicom
分析內(nèi)核源碼:sourceinsight4
邏輯分析儀:ATK-Logic 或Acute TravelLogic Analyzer
除了軟件,硬件上的邏輯分析儀,示波器,萬用表也可輔助調(diào)試驗證。
附件:

親愛的卡友們,歡迎光臨雷龍官網(wǎng),如果看完文章之后還是有疑惑或不懂的地方,請聯(lián)系我們
審核編輯 黃宇
-
SD
+關(guān)注
關(guān)注
1文章
174瀏覽量
36499 -
瑞芯微
+關(guān)注
關(guān)注
27文章
808瀏覽量
54527
發(fā)布評論請先 登錄
99.99% 穩(wěn)定性:CS 創(chuàng)世 SD NAND 在精密儀表中的落地應(yīng)用
野外數(shù)據(jù)采集系統(tǒng)中的穩(wěn)定存儲:CS 創(chuàng)世 SD NAND 應(yīng)用分析
CS創(chuàng)世 SD NAND測試報告
RK3588?平臺?MPP?編譯?+ VPU?格式測試
瑞芯微 RK3588 平臺系統(tǒng)啟動卡制作及 eMMC 固化操作手冊
瑞芯微 RK3588 平臺 Debian 系統(tǒng)開發(fā)案例與使用說明
CS創(chuàng)世SD NAND在北京君正平臺和瑞芯微RK平臺的應(yīng)用
評論