從功能上來說,nand flash與norflash並無太大差異,主要區別在於操作介面和方式。nand基於非sram匯流排介面,使用nand介面,所以一般需要mcu具有nand控制器才可與其連線。在讀取時,以頁為單位;擦除和寫入時,以塊為單位。
將nand視作乙個mtd裝置
uboot將nand視作乙個mtd裝置,所以使用mtd機制對nand裝置進行管理。單個nand裝置用nand_info_t來描述。而nand_info_t實際上就是mtd結構。最多支援的nand裝置數config_sys_max_nand_device可由開發者自行配置。不過一般目標板上只有一塊nand裝置,所以取值通常為1。
typedef struct mtd_info nand_info_t;但是僅使用mtd結構來描述不夠,因為mtd只是乙個通用的儲存描述結構,而nand裝置特定的某些屬性,如ecc布局等不能簡單的新增到mtd結構中。所以,uboot定義了nand_chip結構。nand_info_t nand_info[config_sys_max_nand_device];
struct nand_chip ;然後,分配了config_sys_max_nand_device個nand_chip結構。
struct nand_chip nand_chip[config_sys_max_nand_device];最後,在nand_init_chip()中通過以下**關聯nand_info結構和nand_chip結構:
mtd->priv = nand;這樣做使得乙個mtd結構既可以描述mtd結構的通用性,又能在需要時訪問nand裝置特定的屬性和操作函式。
基本的nand操作演算法
uboot提供了nand裝置的通用操作演算法,這些操作演算法集中在nand_base.c。nand驅動層的初始化由nand_init()完成,該函式呼叫nand_init_chip()逐個初始化nand_info表中的各項。而nand_init_chip()則呼叫board_nand_init()完成目標板nand控制器初始化,mtd結構中驅動介面函式初始化,之後呼叫nand_scan()自動檢測該控制器上的nand晶元資訊。檢測完畢後,再呼叫nand_register()向mtd管理器中註冊該裝置。
其中nand_scan最為關鍵,它負責檢測nand的晶元型號,並根據型號填充nand操作演算法介面。
nand_scan操作分為兩步。
第一步,nand_scan_ident()會嘗試讀取晶元id,並根據id計算出器件的屬性,然後將該屬性及對應的操作介面函式填充到nand_chip結構中。
第二步,nand_scan_tail()負責設定ecc布局,並且根據設定的ecc模式填充ecc相關演算法函式,以及使用nand操作函式填充mtd結構中操作介面函式。
具體的演算法函式比較多,且多與硬體裝置有關,所以這裡不深究細節,不再詳細分析。
驅動移植最基本的介面
對於移植而言,開發者需要做的是重新定義board_nand_init(),在該函式中完成nand裝置中的mtd結構部分介面函式和屬性的設定。其中某些屬性是必須設定的,如下所示:
int board_nand_init(struct nand_chip *nand)如果具體到乙個特定的nand裝置,為了能訪問該裝置,驅動**能夠向nand裝置傳送命令、讀寫資料、檢測器件是否忙。雖然nand型號非常多;但是其基本介面都一致,並且讀寫nand塊、頁中資料的演算法都是類似的。所以,uboot在nand_base.c中提供了與nand控制器無關的操作演算法,而與硬體相關的部分則由使用者驅動實現。這樣做的好處便是使用者不需要自己再實現這些操作。nand->io_addr_r = (void __iomem *)(nfdata);
nand->io_addr_w = (void __iomem *)(nfdata);
nand->cmd_ctrl = s3c_nand_hwcontrol;
nand->dev_ready = s3c_nand_device_ready;
nand->ecc.mode = nand_ecc_hw;
ecc管理機制
由於nand本身的特性,在讀寫過程中容易出現錯誤,即儲存器中的某乙個或多個位可能會失效,而無法再寫入。為了處理方便,這些錯誤位的所在的整個頁或者整個塊就不能再用,常見的做法是將這個塊標記為壞塊,不再使用。而nand塊通常為幾十k到幾百k,為了避免僅僅因為錯誤幾個位導致整個塊浪費,所以使用了ecc機制。借助儲存在oob區的ecc碼,可以檢測出讀取的資料是否有錯誤位。而當出現的錯誤位數在容許的範圍內時,可對資料進行糾正,保證讀取資料正確,避免丟棄整個塊。
當然,ecc碼只能糾正錯誤位數較少的情況,錯誤太多時用ecc也是無法糾正的,只能丟棄整個塊。即便如此,ecc的存在能減少浪費。
uboot使用nand_ecc_ctrl結構來定義ecc相關的操作模式和介面。
struct nand_ecc_ctrl ;對於ecc操作而言,最主要的兩個操作為根據輸入計算ecc碼(calculate)和根據ecc碼對資料進行糾錯(correct)。
在board_nand_init()中初始化nand驅動介面時,可以根據需要初始化ecc操作介面。s5pv210的部分設定**如下:
nand->ecc.mode = nand_ecc_hw;ecc碼計算方式有兩種,一種是純粹通過uboot軟體計算得出;二是呼叫nand控制器由硬體計算出ecc。通常情況下,以使用硬體ecc計算為主。nand->ecc.hwctl = s3c_nand_enable_hwecc;
nand->ecc.calculate = s3c_nand_calculate_ecc;
nand->ecc.correct = s3c_nand_correct_data;
nand->ecc.read_page = s3c_nand_read_page_8bit;
nand->ecc.write_page = s3c_nand_write_page_8bit;
nand->ecc.read_oob = s3c_nand_read_oob_8bit;
nand->ecc.write_oob = s3c_nand_write_oob_8bit;
nand->ecc.layout = &s3c_nand_oob_128;
nand->ecc.hwctl = s3c_nand_enable_hwecc_8bit;
nand->ecc.calculate = s3c_nand_calculate_ecc_8bit;
nand->ecc.correct = s3c_nand_correct_data_8bit;
nand->ecc.size = 512;
nand->ecc.bytes = 13;
nand->options |= nand_no_subpage_write;
ecc的分組處理
通常ecc碼需要分組計算,即將整個nand頁劃分為成多個部分,每部分資料計算一組ecc碼,最後統一放置到oob區。例如,s5pv210的8bit硬體ecc計算,每512位元組生成一組ecc,單個頁的ecc計算**如下:
for (i = 0; eccsteps; eccsteps–, i += eccbytes, p += eccsize)軟體ecc
使用軟體ecc時,需要自行實現ecc碼檢錯和糾錯的演算法。最新的uboot中提供了一種ecc計算方法,在nand_ecc.c中。其中nand_calculate_ecc()根據輸入的源資料計算出ecc碼,nand_correct_data根據輸入的ecc碼對資料進行檢錯和糾錯。
具體的演算法細節這裡不關注。
硬體ecc
s5pv210支援1位、4位、8位、12位、16位的硬體ecc,相應的ecc檢錯和糾錯**已經包含在nand驅動原始碼中。
壞塊管理
uboot對nand裝置的壞塊管理有兩種:一種是讀寫時跳過壞塊;一種是基於壞塊表。
跳過壞塊是最簡單也是最常見處理方式,在nand_util.c中nand_read_skip_bad和nand_write_skip_bad提供了該種處理方式。
而基於壞塊表的方式,暫沒有見到過。
原文**:
uboot原始碼分析(3)
正式開始了第二階段 relocate部分的 負責把u boot stage2的 從flash儲存器載入到記憶體,如下 163 ifndef config skip relocate uboot 164relocate 165 adr r0,start 獲取當前 存放位址 00000000 166 l...
uboot原始碼 MMC分析
1 作業系統下,mmu是開啟的,即linux驅動使用的都是虛擬位址。純裸機程式不會開啟mmu,全部使用實體地址。2 uboot早期也是純實體地址工作,但是現在的uboot開啟了mmu做了虛擬位址對映。1 linux的驅動是模組化設計。2 uboot移植了linux驅動源 3 uboot中的硬體驅動比...
u boot原始碼配置原理分析
華清遠見嵌入式學院講師。u boot的源 預設是不針對任何目標平台的,當我們要移植u boot到乙個特定的目標平台時,需要生成針對目標平台的配置檔案。u boot目前已經支援的晶元可以在include configs 下面找到。比如我們要編譯針對s3c2410晶元的u boot.bin,那麼我們就需...