目录
4. 修改源码之识别NOR Flash与NAND Flash
4.1 修改源码之识别NOR Flash
4.1.1 分析源码之NOR Flash
4.1.2 修改源码
4.1.3 测试
4.2 修改源码之识别NAND Flash
4.2.1 分析源码之NAND Flash
4.2.2 修改相关宏
4.2.3 添加s3c2440_nand.c文件
4.2.4 修改s3c2440_nand.c(drivers/mtd/nand/目录下)
4.2.4.1 添加s3c2440_nand_select()函数
4.2.4.2 修改board_nand_init()函数
4.2.4.3 修改s3c24x0_hwcontrol()函数
4.2.5 解决编译错误
4.2.6 测试
4. 修改源码之识别NOR Flash与NAND Flash
4.1 修改源码之识别NOR Flash
只有当uboot从NOR Flash启动时,NOR Flash才能被检测到。
经过上一节移植u-boot-2016.11到JZ2440(三:修改源码之实现NOR启动与NAND启动)的修改后,再将include/configs/jz2440.h中打开DEBUG调试(添加#define DEBUG),重新编译(此时的uboot有0x805ae大小,烧写时注意大小)烧到NOR Flash(使用上一节3.2.3.3 测试里的烧写方式),开发板NOR启动后串口有如下输出:
4.1.1 分析源码之NOR Flash
查找源码知道“Flash:”是在第二阶段调用board_init_r()函数时循环调用init_sequence_r数组里的函数,调用到initr_flash函数时输出的,代码如下所示(common/board_r.c文件中):
static int initr_flash(void)
{
ulong flash_size = 0;
bd_t *bd = gd->bd;
puts("Flash: "); /* 打印 Flash: */
if (board_flash_wp_on()) /* 空函数 */
printf("Uninitialized - Write Protect On\n");
else
flash_size = flash_init();
print_size(flash_size, "");
... ...
}
显然NOR Flash大小是通过调用flash_init()函数获得的,该函数代码如下(drivers/mtd/cfi_flash.c文件中):
unsigned long flash_init (void)
{
unsigned long size = 0;
int i;
/* Init: no FLASHes known */
for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i) {
flash_info[i].flash_id = FLASH_UNKNOWN;
/* Optionally write flash configuration register */
cfi_flash_set_config_reg(cfi_flash_bank_addr(i),
cfi_flash_config_reg(i));
if (!flash_detect_legacy(cfi_flash_bank_addr(i), i))
flash_get_size(cfi_flash_bank_addr(i), i);
size += flash_info[i].size;
if (flash_info[i].flash_id == FLASH_UNKNOWN) {
... ...
}
}
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 */
}
显然输出的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就会打印错误信息并进入死循环。
4.1.2 修改源码
显然源码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()函数里有如下代码(drivers/mtd/jedec_flash.c文件中):
CONFIG_SYS_MAX_FLASH_SECT宏在include/configs/jz2440.h中默认定义为19,显然小于我们jedec_table[]数组中定义的NOR Flash扇区个数,修改jz2440.h文件中宏CONFIG_SYS_MAX_FLASH_SECT的定义如下:
可以再把之前定义的DEBUG调试宏去掉。
4.1.3 测试
重新编译,按照上一节3.2.3.3 测试内容进行烧写新uboot到NOR Flash,重新NOR启动开发板,串口输出如下:
正确识别出了NOR Flash,并且进入了命令行操作。输入“flinfo”命令如下:
可以看到已经正确读出NOR Flash的信息了,再输入以下命令测试NOR Flash:
protect off all
erase 90000 +7ffff
cp.b 30000000 90000 1000 //烧写在另一个位置
cmp.b 30000000 90000 1000 //比较是否读写正确
效果如下:
4.2 修改源码之识别NAND Flash
下面开始修改源码使U-Boot能识别出NAND Flash。
4.2.1 分析源码之NAND Flash
查找源码知道“NAND:”是在第二阶段调用board_init_r()函数时循环调用init_sequence_r数组里的函数,调用到initr_nand函数时输出的,代码如下所示(common/board_r.c文件中):
static int initr_nand(void)
{
puts("NAND: ");
nand_init();
return 0;
}
nand_init函数去掉无用代码后如下(drivers/mtd/nand/nand.c文件中):
#define CONFIG_SYS_MAX_NAND_DEVICE 1 /* jz2440.h中定义 */
/* drivers/mtd/nand/nand.c文件中 */
static unsigned long total_nand_size; /* total_nand_size初始化为0 */
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函数得到NAND Flash大小,然后打印出来,进入该函数如下(drivers/mtd/nand/nand.c文件中):
static void nand_init_chip(int i)
{
struct nand_chip *nand = &nand_chip[i];
struct mtd_info *mtd = nand_to_mtd(nand);
ulong base_addr = base_address[i];
int maxchips = CONFIG_SYS_NAND_MAX_CHIPS;
if (maxchips < 1)
maxchips = 1;
nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr;
if (board_nand_init(nand)) /* 单板NAND Flash初始化 */
return;
if (nand_scan(mtd, maxchips))
return;
nand_register(i, mtd);
}
其中调用的board_nand_init函数定义在drivers/mtd/nand/s3c2410_nand.c文件中,显然该文件里的函数只支持s3c2410,下面开始修改源码。
4.2.2 修改相关宏
S3C2440与S3C2410在NAND Flash上区别较大,首先修改单板配置头文件如下(include/configs/jz2440.h文件中):
在jz2440.h文件中去掉宏CONFIG_NAND_S3C2410与CONFIG_SYS_S3C2410_NAND_HWECC,添加宏CONFIG_NAND_S3C2440。
4.2.3 添加s3c2440_nand.c文件
拷贝drivers/mtd/nand/s3c2410_nand.c为s3c2440_nand.c到同目录,同时修改该目录下的Makefile如下:
作用是将编译s3c2410_nand.c文件选项改成编译s3c2440_nand.c文件。
4.2.4 修改s3c2440_nand.c(drivers/mtd/nand/目录下)
4.2.4.1 添加s3c2440_nand_select()函数
在board_nand_init()函数前添加s3c2440_nand_select()函数,函数代码如下:
static void s3c2440_nand_select(struct mtd_info *mtd, int chipnr)
{
struct s3c24x0_nand *nand = s3c24x0_get_base_nand();
switch (chipnr) {
case -1: //取消选择
nand->nfcont |= (1<<1);
break;
case 0: /*选中*/
nand->nfcont&=~(1<<1);
break;
default:
BUG();
}
}
4.2.4.2 修改board_nand_init()函数
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. 将如下代码:
改为(指定片选函数):
4.2.4.3 修改s3c24x0_hwcontrol()函数
修改s3c24x0_hwcontrol()函数如下:
static void s3c24x0_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct s3c24x0_nand *nand = s3c24x0_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);
}
}
4.2.5 解决编译错误
重新编译,有如下错误:
Error: You must add new CONFIG options using Kconfig
The following new ad-hoc CONFIG options were detected:
CONFIG_NAND_S3C2440Please add these via Kconfig instead. Find a suitable Kconfig
file and add a 'config' or 'menuconfig' option.
make: *** [all] Error 1
在源码搜索CONFIG_NAND_S3C2410如下(grep -nR "CONFIG_NAND_S3C2410" *):
我们修改了宏CONFIG_NAND_S3C2410为CONFIG_NAND_S3C2440,同样将该宏修改到scripts/config_whitelist.txt文件,重新编译,成功。
4.2.6 测试
按照上一节的3.3.7 测试NAND Flash启动内容进行烧写新uboot到NAND Flash,NAND启动开发板,串口输出如下:
可以看到NAND启动时,NOR Flash无效,识别出了NAND Flash。输入以下命令测试NAND Flash:
nand erase 0 2000 //擦除nand flash 0~2000地址
mw.b 30000000 0x55 2000 //写2000个0x55到30000000地址
nand write 30000000 0 2000 //将30000000地址数据写入nand flash的0地址,大小为2000
nand dump 0 2000 //打印
串口打印如下: