移植uboot第六步:支持NANDFlash
寫在前面:
我的博客已遷移至自建服務器:博客傳送門,CSDN博客暫時停止,如有機器學習方面的興趣,歡迎來看一看。
此外目前我在gitHub上準備一些李航的《統計學習方法》的實現算法,目標將書內算法全部手打實現,歡迎參觀并打星。GitHib傳送門
正文
前面已經讓板子能夠支持NORFlash了,還沒有支持NANDFlash。
一. 找到之前第一步注釋掉的NAND相關的宏定義
//#define CONFIG_S3C2410 /* specifically a SAMSUNG S3C2410 SoC */ #ifdef CONFIG_S3C2410 #define CONFIG_NAND_S3C2410 #define CONFIG_SYS_S3C2410_NAND_HWECC #define CONFIG_SYS_MAX_NAND_DEVICE 1 #define CONFIG_SYS_NAND_BASE 0x4E000000 #endif為了程序的兼容性,不直接把宏定義取消注釋。在宏定義邊上再宏定義一個
//#define CONFIG_S3C2410 /* specifically a SAMSUNG S3C2410 SoC */ #define CONFIG_S3C2440 /* specifically a SAMSUNG S3C2410 SoC */然后將NAND的相關宏定義格式改為
#ifdef CONFIG_S3C2410 #define CONFIG_NAND_S3C2410 #define CONFIG_SYS_S3C2410_NAND_HWECC #else #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這樣這個uboot以后對2410還是有很好的支持性的。
編譯,解決錯誤
二. 報錯:s3c2410_nand.c的第72行出現錯誤。(一般出現一堆錯誤的時候,解決第一個錯誤,解決完再重新編譯, 再解決第一個錯誤,直到沒有錯誤。因為很多錯誤是有連帶性的,一個錯誤解決可能會連帶解決一些錯誤)
找到代碼段,是調用了一個結構體,結構體的定義是這樣的
struct s3c2410_nand *nand = s3c2410_get_base_nand();
定位到這個結構體的初始化
這里可以明白,我們使用的宏是2440,所以2410這個結構體并沒有,在代碼中把2410改為2440。
我們干脆就重新做一個s3c2440_nand.c文件,找到目錄drivers\mtd\nand里面的s3c2410_nand.c,復制一份改為s3c2440_nand.c,要將其加入uboot,所以找到該文件目錄里的Makefile,找到里面有一條語句
說明原先的2410也是條件編譯進入uboot。在include\configs 目錄內找到smdk2440.h。
#ifdef CONFIG_CMD_NAND#ifdef CONFIG_S3C2410 #define CONFIG_NAND_S3C2410 #define CONFIG_SYS_S3C2410_NAND_HWECC #else #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宏定義在這里,這段代碼段就是我們剛才修改過的,也就是說s3c2410_nand.c本身就已經不參與編譯了,我們再邊上加上一句
COBJS-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o COBJS-$(CONFIG_NAND_S3C2440) += s3c2440_nand.o如果定義了CONFIG_NAND_S3C2440,那么就編譯s3c2440_nand.c
這樣就可以了。
進入s3c2440_nand.c目錄。將剛才發現錯誤的地方修改如下
struct s3c2440_nand *nand = s3c2440_get_base_nand();三.查看nand相關函數是否支持2440
在board_init_r中找到
進入函數
void nand_init(void) { #ifdef CONFIG_SYS_NAND_SELF_INITboard_nand_init(); #elseint i;for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)nand_init_chip(i); #endifprintf("%lu MiB\n", total_nand_size / 1024);#ifdef CONFIG_SYS_NAND_SELECT_DEVICE/** Select the chip in the board/cpu specific driver*/board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device); #endif }查看得知CONFIG_SYS_NAND_SELF_INIT這個宏定義是否定義由另一個宏定義決定
#if defined(CONFIG_NAND_FSL_ELBC) #define CONFIG_SYS_NAND_SELF_INIT #endif而CONFIG_NAND_FSL_ELBC沒有被定義,也就是說程序是執行的是#else下面的內容,進入nand_init_chip(i);函數。
#ifndef CONFIG_SYS_NAND_SELF_INIT static void nand_init_chip(int i) {struct mtd_info *mtd = &nand_info[i];struct nand_chip *nand = &nand_chip[i];ulong base_addr = base_address[i];int maxchips = CONFIG_SYS_NAND_MAX_CHIPS;if (maxchips < 1)maxchips = 1;mtd->priv = nand;nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr;if (board_nand_init(nand))return;if (nand_scan(mtd, maxchips))return;nand_register(i); } #endif進入board_nand_init看看
int board_nand_init(struct nand_chip *nand) {u_int32_t cfg;u_int8_t tacls, twrph0, twrph1;struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();struct s3c2440_nand *nand_reg = s3c2440_get_base_nand();debug("board_nand_init()\n");writel(readl(&clk_power->clkcon) | (1 << 4), &clk_power->clkcon);/* initialize hardware */ #if defined(CONFIG_S3C24XX_CUSTOM_NAND_TIMING)tacls = CONFIG_S3C24XX_TACLS;twrph0 = CONFIG_S3C24XX_TWRPH0;twrph1 = CONFIG_S3C24XX_TWRPH1; #elsetacls = 4;twrph0 = 8;twrph1 = 8; #endifcfg = S3C2410_NFCONF_EN;cfg |= S3C2410_NFCONF_TACLS(tacls - 1);cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);writel(cfg, &nand_reg->nfconf);/* initialize nand_chip data structure */nand->IO_ADDR_R = (void *)&nand_reg->nfdata;nand->IO_ADDR_W = (void *)&nand_reg->nfdata;nand->select_chip = s3c2440_nand_select;/* read_buf and write_buf are default *//* read_byte and write_byte are default */ #ifdef CONFIG_NAND_SPLnand->read_buf = nand_read_buf; #endif/* hwcontrol always must be implemented */nand->cmd_ctrl = s3c2440_hwcontrol;nand->dev_ready = s3c2440_dev_ready;#ifdef CONFIG_S3C2410_NAND_HWECCnand->ecc.hwctl = s3c2410_nand_enable_hwecc;nand->ecc.calculate = s3c2410_nand_calculate_ecc;nand->ecc.correct = s3c2410_nand_correct_data;nand->ecc.mode = NAND_ECC_HW;nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES; #elsenand->ecc.mode = NAND_ECC_SOFT; #endif#ifdef CONFIG_S3C2410_NAND_BBTnand->options = NAND_USE_FLASH_BBT; #elsenand->options = 0; #endifdebug("end of nand_init\n");return 0; }下面這段代碼應該是設置NAND控制器的時間參數的。我當時寫的時候三個時間參數設置是
tacls = 0; twrph0 = 1; twrph1 = 0;但是無所謂,只要它的時間參數比我的久就行了,久是肯定可以直接用的,比我的時間短那就得看手冊支不支持怎么短的時間了。
tacls = 4; twrph0 = 8; twrph1 = 8; #endifcfg = S3C2410_NFCONF_EN; cfg |= S3C2410_NFCONF_TACLS(tacls - 1); cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1); cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1); writel(cfg, &nand_reg->nfconf);查看手冊比對寄存器設置的時候這些代碼是否是對的,我就不想管這些了,直接粗暴一點,反正之前這塊設置的代碼還在,我就直接改成了
#elsetacls = 4;twrph0 = 8;twrph1 = 8; #endif#if 0cfg = S3C2410_NFCONF_EN;cfg |= S3C2410_NFCONF_TACLS(tacls - 1);cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1); #else/* 設置時序 */ cfg = ((twrph1 - 1)<<12)|((twrph0 - 1)<<8)|((tacls - 1)<<4); #endifwritel(cfg, &nand_reg->nfconf);設置完時序以后 初始化ECC, 禁止片選還沒有做,我也自己給他在后面加上
/* 使能NAND Flash控制器, 初始化ECC, 禁止片選 */ writel( (1<<4)|(1<<1)|(1<<0), &nand_reg->nfcont);函數剩下的部分是結構體參數的賦值,應該是NAND的底層操作函數在這里上報。
nand->select_chip = NULL;/* read_buf and write_buf are default *//* read_byte and write_byte are default */ #ifdef CONFIG_NAND_SPLnand->read_buf = nand_read_buf; #endif/* hwcontrol always must be implemented */nand->cmd_ctrl = s3c2440_hwcontrol;nand->dev_ready = s3c2440_dev_ready;#ifdef CONFIG_S3C2410_NAND_HWECCnand->ecc.hwctl = s3c2410_nand_enable_hwecc;nand->ecc.calculate = s3c2410_nand_calculate_ecc;nand->ecc.correct = s3c2410_nand_correct_data;nand->ecc.mode = NAND_ECC_HW;nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES; #elsenand->ecc.mode = NAND_ECC_SOFT; #endif#ifdef CONFIG_S3C2410_NAND_BBTnand->options = NAND_USE_FLASH_BBT; #elsenand->options = 0; #endif我們要看現在uboot的nand能不能支持2440了,就一個個把這些函數看下去,看每一個函數里面的代碼是否和2440匹配。
a.select_chip函數設置的是null,就先不管,CONFIG_NAND_SPL沒有宏定義,所以read_buf也先不管,先看nand->cmd_ctrl = s3c2410_hwcontrol;這個函數
先把名字都從2410改成2440。
這個函數應該是負責寫命令或者寫數據,直接將其修改為我們自己的
static void s3c2440_hwcontrol(struct mtd_info *mtd, int dat, unsigned int ctrl) {struct s3c2440_nand *nand = s3c2440_get_base_nand();if (ctrl & NAND_CLE){/* 發命令 */writeb(dat, &nand->nfcmd);}else if (ctrl & NAND_ALE){/* 發數據 */writeb(dat, &nand->nfaddr);} }接下來就是比對每一個操作函數里面的操作寄存器的方式和我們的是否一樣,同時把所有的2440修改為2410。
之前代碼里看到過select_chip 函數是空,那么怎么選中片子呢?
nand->select_chip = null;一層層函數下去找到nand_set_defaults函數,看名字是設置默認值
void board_init_r(gd_t *id, ulong dest_addr) nand_init(); /* go init the NAND */ nand_init_chip(i); nand_scan(mtd, maxchips) nand_scan_ident(mtd, maxchips, NULL); nand_set_defaults(chip, busw);這個函數的作用是,查看之前上報用的結構體,如果某項為空,就使用程序自帶的默認函數。
/** Set default functions*/ static void nand_set_defaults(struct nand_chip *chip, int busw) {/* check for proper chip_delay setup, set 20us if not */if (!chip->chip_delay)chip->chip_delay = 20;/* check, if a user supplied command function given */if (chip->cmdfunc == NULL)chip->cmdfunc = nand_command;/* check, if a user supplied wait function given */if (chip->waitfunc == NULL)chip->waitfunc = nand_wait;if (!chip->select_chip)chip->select_chip = nand_select_chip;if (!chip->read_byte)chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;if (!chip->read_word)chip->read_word = nand_read_word;if (!chip->block_bad)chip->block_bad = nand_block_bad;if (!chip->block_markbad)chip->block_markbad = nand_default_block_markbad;if (!chip->write_buf)chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;if (!chip->read_buf)chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;if (!chip->verify_buf)chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;if (!chip->scan_bbt)chip->scan_bbt = nand_default_bbt;if (!chip->controller)chip->controller = &chip->hwcontrol; }找到它默認的選擇片子的函數
/*** nand_select_chip - [DEFAULT] control CE line* @mtd: MTD device structure* @chipnr: chipnumber to select, -1 for deselect** Default select function for 1 chip devices.*/ static void nand_select_chip(struct mtd_info *mtd, int chipnr) {struct nand_chip *chip = mtd->priv;switch (chipnr) {case -1:chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);break;case 0:break;default:BUG();} }-1表示取消選中,0表示選中,這里只寫了取消選中,沒寫選中,顯然這個函數我們是用不了的。自己在s3c2440_nand.c建一個函數
static void s3c2440_nand_select(struct mtd_info *mtd, int chipnr) {struct s3c2440_nand *nand = s3c2440_get_base_nand();switch (chipnr) {case -1: /* 取消選中 */nand->nfcont |= (1 << 1); break;case 0:nand->nfcont &= ~(1 << 1); break;default:BUG();} }在結構體上報中修改一下
nand->select_chip = s3c2440_nand_select;編譯,燒寫,運行。
沒有錯誤且已經能識別NAND了,因為燒寫在NAND里,0地址對應的是片內內存,無法去訪問NOR,所以NOR識別出來應該是0k。但是NOR啟動的話,兩個都能識別。
后記:
boot有四五百K,我使用的是oflash,燒寫速度很慢,提速的方式可以使用先燒寫下的好用的uboot,我使用了韋東山的,兩百多K,然后再使用好用的uboot去驅動dnw下載。
現在我們寫的boot已經能使用串口了,因此可以使用串口下載,速度也還可以。
輸入help查看指令,有loady下載方式
我使用的Xshell沒找到loady模式,(load有好幾種模式,loadx,loady,loadz,大多數都不支持loady)。百度下載了一個XP的超級終端,提供loady。終端中串口連接上,輸入命令。
loady 0x30000000后面的是燒寫地址。板子就在等待文件,終端中選擇boot路徑,選擇loady模式,點確定就能下載。發送文件這個步驟手速要快,如果失敗了,就是時間間隔太久了,可以復制文件路徑,進去直接粘貼點確定,要快一點。
燒寫進去以后是在板子的內存里,輸入
總結
以上是生活随笔為你收集整理的移植uboot第六步:支持NANDFlash的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 移植uboot第五步:支持NORFlas
- 下一篇: 移植uboot第七步:支持DM9000