在上一章,我們添加了nor,nand啟動後,uboot啟動出如下圖所示: 上面的Flash: *** failed *** 是屬於uboot第二階段函數board_init_r()里的代碼, 代碼如下所示(位於arch/arm/lib/board.c): 從上面代碼看出, board_init_r( ...
- 在上一章,我們添加了nor,nand啟動後,uboot啟動出如下圖所示:
上面的Flash: *** failed *** 是屬於uboot第二階段函數board_init_r()里的代碼, 代碼如下所示(位於arch/arm/lib/board.c):
/*第二階段*/ void board_init_r(gd_t *id, ulong dest_addr) //gd uboot重定位地址 { ... ... puts("Flash: "); //列印flash: flash_size = flash_init(); //初始化nor_flash if (flash_size > 0) { ... ... print_size(flash_size, "\n"); //列印nor_flash的大小 } else { puts(failed); //列印數組failed[]="*** failed ***\n"; hang(); //進入while中,並列印: ### ERROR ### Please RESET the board ### } #if defined(CONFIG_CMD_NAND) puts("NAND: "); //列印NAND: nand_init(); //初始化nand_flah ... ... }
從上面代碼看出, board_init_r()會來初始化nor,由於新的uboot不支持nor,所以flash_init()初始失敗,然後列印一串錯誤代碼後,等待複位.
由於2440在nand啟動時,會自動裝載nand的前4k內容,所以不支持norflash,因為nor的前4k內容被nand占用.
所以修改上面代碼,避免nand啟動一直卡住,將:
else { puts(failed); //列印數組failed[]="*** failed ***\n"; hang(); //進入while中,並列印: ### ERROR ### Please RESET the board ### }
改為:
else { puts("0 KB\r\n"); //列印0 KB }
1.接下來,下章便來修改代碼,使uboot支持讀寫norflash
1.1首先在include/common.h中添加:
#define DEBUG //調試模式
然後使用nor啟動新的uboot,列印出調試信息:
列印出norflash的廠家ID=0xC2,設備ID=0x2249,顯然uboot匹配讀出的ID沒有成功.
搜索JEDEC PROBE欄位,找到位於board_init_r()->flash_init()->flash_detect_legacy():
如上圖所示,該函數會進入board_init_r()->flash_init()->flash_detect_legacy()->jedec_flash_match(),裡面會通過兩個ID來匹配jedec_table[].
1.2接下來向jedec_table[]里添加norflash:MT29LV160DB(位於drivers/mtd/jedec_flash.c)
代碼如下:
/*MX29LV160DB*/ { .mfr_id = (u16)MX_MANUFACT, //廠家ID0x00C200C2 (讀nor,便是0xc2) .dev_id = 0x2249, //設備ID .name = "MXIC MX29LV160DB", .uaddr = { [1] = MTD_UADDR_0x0555_0x02AA /* 數組[1]表示是16位nor,解鎖地址為:0x555,0x2AA */ }, .DevSize = SIZE_2MiB, .CmdSet = P_ID_AMD_STD, .NumEraseRegions= 4, //4種不同的扇區規格 .regions = { ERASEINFO(16*1024, 1), ERASEINFO(8*1024, 2), ERASEINFO(32*1024, 1), ERASEINFO(64*1024, 31), } },
重新燒寫看列印信息,出現這麼一段ERROR:
ERROR:too many flash sectors
說flash的扇區太多了,搜索找到位於drivers/mtd/jedec_flash.c中:
顯然是CONFIG_SYS_MAX_FLASH_SECT巨集小於我們flash的扇區,所以列印ERROR。
所以修改CONFIG_SYS_MAX_FLASH_SECT巨集定義(位於include/configs/smdk2440.h),並去掉之前定義的DEBUG調試巨集(位於include/common.h)
1.3然後重新燒寫
輸入flinfo命令(flash info),就能查看flash的信息了:
然後通過uboot命令,檢測nor的讀寫是否正確:
protect off all erase 80000 +7ffff cp.b 30000000 80000 1000 //燒寫在另一個位置 cmp.b 30000000 80000 1000 //比較,是否讀寫正確
2.接下來繼續修改代碼,使uboot支持NandFlash
2410的NandFlash位於drivers/mtd/nand/s3c2410_nand.c
2.1 首先複製s3c2410_nand.c,改為s3c2440_nand.c
改Makefile,如下圖所示:
2.2 在上一章分析過CONFIG_NAND_S3C2410巨集,位於include/configs/smdk2440.h:
如上圖所示,其中CONFIG_CMD_NAND巨集:表示uboot是否支持nand,在上章里,我們把它屏蔽了,接下來便取消屏蔽CONFIG_CMD_NAND巨集。
2.3繼續添加對CONFIG_NAND_S3C2440巨集的支持,將:
#ifdef CONFIG_CMD_NAND #define CONFIG_NAND_S3C2410 #define CONFIG_SYS_S3C2410_NAND_HWECC #define CONFIG_SYS_MAX_NAND_DEVICE 1 #define CONFIG_SYS_NAND_BASE 0x4E000000 #endif
改為:
#ifdef CONFIG_CMD_NAND #ifdef CONFIG_S3C2410 #define CONFIG_NAND_S3C2410 #define CONFIG_SYS_S3C2410_NAND_HWECC #else // CONFIG_S3C2440 #define CONFIG_NAND_S3C2440 #define CONFIG_SYS_S3C2440_NAND_HWECC #endif #define CONFIG_SYS_MAX_NAND_DEVICE 1 #define CONFIG_SYS_NAND_BASE 0x4E000000 #endif
由於smdk2410.h中定義的是CONFIG_S3C2410,而smdk2440.h中定義的是CONFIG_S3C2440,所以便會根據上面的#ifdef來動態定義巨集
2.4 然後來看看nand的流程(和linux的nand驅動有很多相似的地方):
1)uboot重定位後進入第二階段board_init_r():
void board_init_r(gd_t *id, ulong dest_addr) //gd uboot重定位地址 { ... ... #if defined(CONFIG_CMD_NAND) //需要定義CONFIG_CMD_NAND巨集 puts("NAND: "); nand_init(); /* go init the NAND */ #endif ... ... }
2)進入nand_init():
void nand_init(void) { ... ... for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) //在2.3小節里,該巨集為1 nand_init_chip(i); printf("%lu MiB\n", total_nand_size / 1024); ... ... }
3)進入nand_init()->nand_init_chip(0):
static void nand_init_chip(int i) { struct mtd_info *mtd = &nand_info[i]; //mtd_info屬於軟體的一部分,實現用戶層讀寫等操作 struct nand_chip *nand = &nand_chip[i]; //屬於底層,保存對nand的硬體相關操作,它是mtd_info結構體的priv私有成員 ulong base_addr = base_address[i]; //獲取nand寄存器基地址,等於0x4E000000 int maxchips = CONFIG_SYS_NAND_MAX_CHIPS; if (maxchips < 1) maxchips = 1; mtd->priv = nand; //設置私有成員nand_chip ... ... if (board_nand_init(nand)) //位於s3c2440_nand.c,該函數會設置nand_chip結構體的成員 return; if (nand_scan(mtd, maxchips)) //通過mtd->priv來開啟nand片選,來獲取nand的型號,類型等.並填充mtd結構體下其它的成員. return; nand_register(i); //註冊nand,使uboot支持對nand的讀寫操作 }
這個nand_chip結構體和我們之前學的linux下的nand驅動章節里的nand_chip一摸一樣,流程也非常相似.
由於在2.1小節里,該函數所在的文件s3c2440_nand.c是從s3c2410_nand.c複製過來的,所以接下來便修改s3c2440_nand.c (位於drivers/mtd/nand目錄下)
2.5 修改s3c2440_nand.c(參考2410數據手冊和2440數據手冊)
1)首先將所有帶2410字的變數都替換為2440
2)修改board_nand_init()
參考以前寫的nand驅動,將
tacls = 4; twrph0 = 8; twrph1 = 8; cfg = S3C2440_NFCONF_EN; //啟動nand控制器 cfg |= S3C2440_NFCONF_TACLS(tacls - 1); cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1); cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1); writel(cfg, &nand_reg->nfconf);
改為:
tacls = 0; //10ns twrph0 = 1; //20ns twrph1 = 0; //10ns nand_reg->nfconf = (tacls<<12) | (twrph0<<8) | (twrph1<<4); //設置時序 nand_reg->nfcont=(1<<1)|(1<<0); // bit1:關閉片選(), bit0:開啟nand flash 控制器
2)添加nand_chip結構體成員
nand->select_chip=s3c2440_select_chip; //設置CE
然後並寫一個s3c2440_select_chip()函數
/*nand flash :CE */ static void s3c2440_select_chip(struct mtd_info *mtd, int chipnr) { if(chipnr==-1) //CE Disable { my_regs->nfcont|=(0x01<<1); //bit1置1 } else //CE Enable { my_regs->nfcont&=~(0x01<<1); //bit1置0 } }
3)屏蔽帶硬體ECC的相關操作
將:
#ifdef CONFIG_S3C2410_NAND_HWECC nand->ecc.hwctl = s3c2440_nand_enable_hwecc; nand->ecc.calculate = s3c2440_nand_calculate_ecc; nand->ecc.correct = s3c2440_nand_correct_data; nand->ecc.mode = NAND_ECC_HW; nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE; nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES; #else nand->ecc.mode = NAND_ECC_SOFT; #endif
改為:
nand->ecc.mode = NAND_ECC_SOFT; //使用軟體ECC
2.6 修改s3c2440_hwcontrol()函數
改為:
static void s3c2440_hwcontrol(struct mtd_info *mtd,int cmd,unsigned int ctrl) { struct nand_chip *chip = mtd->priv; struct s3c2440_nand *nand = s3c2440_get_base_nand(); //獲取nand寄存器地址 if (ctrl & NAND_CLE) // 傳輸的是命令 nand->nfcmd=cmd; else // 傳輸的是地址 nand->nfaddr=cmd; }
s3c2440_hwcontrol()函數的ctrl是個標誌位:
- bit[1]==1: 表示要發送的dat是命令
- bit[2]==1: 表示要發送的dat是地址
- bit[0]==1:表示使能nand , ==0:表示禁止nand
(PS:具體可以參考內核的nand_command_lp()函數,它會調用這個cmd_crtl函數來實現功能 )
2.7編譯燒寫
如下圖所示,可以看到已支持Nand Flash:
試驗nand是否能讀寫:
nand erase 0 2000 //擦除 mw.b 30000000 0x55 2000 nand write 30000000 0 2000 //將0x55寫入nand nand dump 0 2000 //列印
如下圖所示, 可以看到讀寫nand都沒問題
下章便來學習:讓uboot支持DM9000網卡,便可以通過網路來傳輸文件