目录
4. 修改源码之识别NOR Flash与NAND Flash
4.1 修改源码之识别NOR Flash
4.1.1 打开DEBUG调试
4.1.2 分析flash_init()函数
4.1.3 分析flash_detect_legacy()函数
4.1.4 分析jedec_flash_match()函数
4.1.5 修改代码
4.1.6 测试
4.1.7 修改命令行名称
4.2 修改源码之识别NAND Flash
4.2.1 修改相关宏
4.2.2 添加NAND Flash操作文件
4.2.3 分析调用流程
4.2.4 修改s3c2440_nand.c(drivers/mtd/nand/目录下)
4.2.4.1 将该文件所有的2410都替换为2440
4.2.4.2 添加s3c2440_nand_select()函数
4.2.4.3 修改board_nand_init()函数
4.2.4.4 修改s3c2440_hwcontrol()函数
4.2.5 测试
在上一节移植u-boot-2012.04.01到JZ2440(三:修改源码之实现NOR启动与NAND启动)中开发板启动后串口有如下输出:
查找源码知道“Flash: ***failed ***”是在board_init_r()函数里输出的,代码如下所示(arch/arm/lib/board.c文件中):
/* 参数1:gd指针;参数2:重定位地址 */
void board_init_r(gd_t *id, ulong dest_addr)
{
... ...
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 ### */
}
... ...
}
显然是由于flash_init()函数返回值不对导致出错的。
我们首先打开debug,方便查看程序执行到了哪里,在include/configs/jz2440.h中添加宏定义“#define DEBUG”,重新编译后按照上一节的3.2.5 测试NOR Flash启动内容进行烧写启动,串口输出如下:
进入flash_init()函数查看(drivers/mtd/cfi_flash.c文件中),去掉无用代码如下:
unsigned long flash_init (void)
{
unsigned long size = 0;
int i;
/* 在jz2440.h中默认定义宏CONFIG_SYS_MAX_FLASH_BANKS为1 */
for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i) {
flash_info[i].flash_id = FLASH_UNKNOWN;
... ...
if (!flash_detect_legacy(cfi_flash_bank_addr(i), i)) /* 检测NOR Flash */
flash_get_size(cfi_flash_bank_addr(i), i);
size += flash_info[i].size;
... ...
}
flash_protect_default();
return (size);
}
显然是flash_info[i].size的值不对导致返回的size不大于0出错的。
进入flash_detect_legacy()函数如下:
static int flash_detect_legacy(phys_addr_t base, int banknum)
{
flash_info_t *info = &flash_info[banknum];
/* 执行if里的内容 */
if (board_flash_get_legacy(base, banknum, info)) {
/* board code may have filled info completely. If not, we
use JEDEC ID probing. */
if (!info->vendor) {
int modes[] = { /* 定义两种生产厂商,AMD与INTEL */
CFI_CMDSET_AMD_STANDARD,
CFI_CMDSET_INTEL_STANDARD
};
int i;
/* 循环两次 */
for (i = 0; i < sizeof(modes) / sizeof(modes[0]); i++) {
info->vendor = modes[i];
info->start[0] =
(ulong)map_physmem(base,
info->portwidth,
MAP_NOCACHE);
if (info->portwidth == FLASH_CFI_8BIT
&& info->interface == FLASH_CFI_X8X16) {
info->addr_unlock1 = 0x2AAA;
info->addr_unlock2 = 0x5555;
} else {
info->addr_unlock1 = 0x5555;
info->addr_unlock2 = 0x2AAA;
}
flash_read_jedec_ids(info); /* 读取芯片ID信息 */
debug("JEDEC PROBE: ID %x %x %x\n",
info->manufacturer_id,
info->device_id,
info->device_id2);
/* 使用jedec规范,通过ID比较是否支持该NOR Flash */
if (jedec_flash_match(info, info->start[0]))
break;
else
unmap_physmem((void *)info->start[0],
MAP_NOCACHE);
}
}
... ...
info->flash_id = FLASH_MAN_CFI;
return 1;
}
return 0; /* use CFI */
}
显然在4.1.1 打开DEBUG调试里输出的JEDEC PROBE: ID c2 2249 0信息是在这里打印的,含义分别为机器ID:c2,设备ID:2249(flash_read_jedec_ids函数里读取ID,具体的读ID过程看十八、Linux驱动之nor flash驱动),然后调用jedec_flash_match()函数使用jedec规范匹配NOR Flash,源码里该函数对开发板使用的NOR Flash没有匹配成功,返回值为0,进行第二次for循环。
jedec_flash_match()函数代码如下:
int jedec_flash_match(flash_info_t *info, ulong base)
{
int ret = 0;
int i;
ulong mask = 0xFFFF;
if (info->chipwidth == 1)
mask = 0xFF;
/* 与jedec_table数组的成员比较机器ID、设备ID,匹配成功则调用fill_info填充到flash_info_t结构体 */
for (i = 0; i < ARRAY_SIZE(jedec_table); i++) {
if ((jedec_table[i].mfr_id & mask) == (info->manufacturer_id & mask) &&
(jedec_table[i].dev_id & mask) == (info->device_id & mask)) {
fill_info(info, &jedec_table[i], base); /* 填充info */
ret = 1;
break;
}
}
return ret;
}
与jedec_table全局数组的成员比较机器ID、设备ID,匹配成功则调用fill_info()函数填充该数组成员到flash_info_t结构体,其中就包括info->size的填充,这样最终flash_init()函数里的size += flash_info[i].size就会大于0,否则uboot就会打印错误信息并进入死循环。
显然源码jedec_table全局数组里没有与之前读到的ID信息匹配项,所以没有调用fill_info()函数,接下来便向jedec_table[]数组里添加我们的NOR Flash: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:too many flash sectors”,查找发现在fill_info()函数里有如下代码:
CONFIG_SYS_MAX_FLASH_SECT宏在include/configs/jz2440.h中默认定义为19,显然小于我们jedec_table[]数组中定义的NOR Flash扇区个数,修改jz2440.h文件中宏CONFIG_SYS_MAX_FLASH_SECT的定义如下:
可以再把之前定义的DEBUG调试宏去掉。
重新编译,按照上一节的3.2.5 测试NOR Flash启动内容进行烧写启动,串口输出如下:
正确识别出了NOR Flash,并且进入了命令行操作。输入“flinfo”命令如下:
可以看到已经正确读出NOR Flash的信息了,再输入以下命令测试NOR Flash:
protect off all
erase 80000 +7ffff
cp.b 30000000 80000 1000 //烧写在另一个位置
cmp.b 30000000 80000 1000 //比较,是否读写正确
效果如下:
可以看到命令行的名称为“SMDK2410 #”,只需修改include/configs/jz2440.h中CONFIG_SYS_PROMPT宏如下:
重新编译,按照上一节的3.2.5 测试NOR Flash启动内容进行烧写启动,串口输出如下:
由于2440在NAND启动时,不支持NOR Flash,也就是说NOR Flash识别不出,导致识别不了NOR Flash出错,所以修改board_init_r()函数如下(arch/arm/lib/board.c文件中):
下面开始修改源码使U-Boot能识别出NAND Flash。
在上一节3.2.4 解决编译错误中将CONFIG_CMD_NAND宏屏蔽了,打开该宏并修改(include/configs/jz2440.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
拷贝drivers/mtd/nand/s3c2410_nand.c为s3c2440_nand.c到同目录,同时修改该目录下的Makefile如下:
从board_init_r()函数继续往下分析(arch/arm/lib/board.c文件中):
定义了CONFIG_CMD_NAND宏后就会调用到nand_init()函数,去掉无用代码如下(driver/mtd/nand/nand.c文件中):
void nand_init(void)
{
int i;
for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)
nand_init_chip(i);
printf("%lu MiB\n", total_nand_size / 1024);
}
nand_init_chip()函数代码如下(driver/mtd/nand/nand.c文件中):
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);
}
其中board_nand_init()函数与nand_scan()函数最终都会调用drivers/mtd/nand/s3c2440_nand.c里的函数,所以需要修改s3c2440_nand.c文件来支持我们的NAND Flash。
在board_nand_init()函数前添加s3c2440_nand_select()函数,函数代码如下:
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();
}
}
board_nand_init()函数修改如下:
1. 将如下代码:
改为(修改时序):
cfg= (tacls-1<<12)|(twrph0-1<<8)|(twrph1-1<<4);
writel(cfg, &nand_reg->nfconf);
writel((1<<4)|(1<<1)|(1<<0), &nand_reg->nfcont);
2. 将如下代码:
改为(指定片选函数):
3. 将如下代码:
改为(屏蔽硬件ECC):
修改s3c2440_hwcontrol()函数如下:
static void s3c2440_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct s3c2440_nand *nand = s3c2440_get_base_nand();
debug("hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);
if (ctrl & NAND_CLE) {
/* 发命令 */
writeb(cmd, &nand->nfcmd);
}
else if (ctrl & NAND_ALE) {
/* 发地址 */
writeb(cmd, &nand->nfaddr);
}
}
重新编译,按照上一节的3.3.7 测试NAND Flash启动内容进行烧写启动,串口输出如下:
可以看到NAND启动时,NOR Flash无效,识别出了NAND Flash。输入以下命令测试NAND Flash:
nand erase 0 2000 //擦除
mw.b 30000000 0x55 2000
nand write 30000000 0 2000 //将0x55写入nand
nand dump 0 2000 //打印
串口打印如下: