移植u-boot-2012.04.01到JZ2440(四:修改源码之支持NOR Flash与NAND Flash)

目录

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 测试


4. 修改源码之识别NOR Flash与NAND Flash

4.1 修改源码之识别NOR Flash

    在上一节移植u-boot-2012.04.01到JZ2440(三:修改源码之实现NOR启动与NAND启动)中开发板启动后串口有如下输出:
    移植u-boot-2012.04.01到JZ2440(四:修改源码之支持NOR Flash与NAND Flash)_第1张图片
    查找源码知道“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()函数返回值不对导致出错的。

4.1.1 打开DEBUG调试

    我们首先打开debug,方便查看程序执行到了哪里,在include/configs/jz2440.h中添加宏定义“#define DEBUG”,重新编译后按照上一节的3.2.5 测试NOR Flash启内容进行烧写启动,串口输出如下:
    移植u-boot-2012.04.01到JZ2440(四:修改源码之支持NOR Flash与NAND Flash)_第2张图片

4.1.2 分析flash_init()函数

    进入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出错的。

4.1.3 分析flash_detect_legacy()函数

    进入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循环。

4.1.4 分析jedec_flash_match()函数

    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.5 修改代码

    显然源码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调试宏去掉。

4.1.6 测试

    重新编译,按照上一节的3.2.5 测试NOR Flash启内容进行烧写启动,串口输出如下:
    移植u-boot-2012.04.01到JZ2440(四:修改源码之支持NOR Flash与NAND Flash)_第3张图片       
    正确识别出了NOR Flash,并且进入了命令行操作。输入“flinfo”命令如下:
    移植u-boot-2012.04.01到JZ2440(四:修改源码之支持NOR Flash与NAND Flash)_第4张图片
    可以看到已经正确读出NOR Flash的信息了,再输入以下命令测试NOR Flash
      protect off all
      erase 80000 +7ffff
      cp.b 30000000 80000 1000   
//烧写在另一个位置
      cmp.b 30000000 80000 1000      //比较,是否读写正确
   
效果如下:
    移植u-boot-2012.04.01到JZ2440(四:修改源码之支持NOR Flash与NAND Flash)_第5张图片

4.1.7 修改命令行名称

    可以看到命令行的名称为“SMDK2410 #”,只需修改include/configs/jz2440.hCONFIG_SYS_PROMPT宏如下:
 
    重新编译,按照上一节的3.2.5 测试NOR Flash启内容进行烧写启动,串口输出如下:
    移植u-boot-2012.04.01到JZ2440(四:修改源码之支持NOR Flash与NAND Flash)_第6张图片

4.2 修改源码之识别NAND Flash

    由于2440NAND启动时,不支持NOR Flash,也就是说NOR Flash识别不出,导致识别不了NOR Flash出错,所以修改board_init_r()函数如下(arch/arm/lib/board.c文件中)
    移植u-boot-2012.04.01到JZ2440(四:修改源码之支持NOR Flash与NAND Flash)_第7张图片
    下面开始修改源码使U-Boot能识别出NAND Flash

4.2.1 修改相关宏

    在上一节3.2.4 解决编译错误中将CONFIG_CMD_NAND宏屏蔽了,打开该宏并修改(include/configs/jz2440.h文件中)
    移植u-boot-2012.04.01到JZ2440(四:修改源码之支持NOR Flash与NAND Flash)_第8张图片
    改为:

#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

4.2.2 添加NAND Flash操作文件

    拷贝drivers/mtd/nand/s3c2410_nand.cs3c2440_nand.c到同目录,同时修改该目录下的Makefile如下:
    移植u-boot-2012.04.01到JZ2440(四:修改源码之支持NOR Flash与NAND Flash)_第9张图片

4.2.3 分析调用流程

    从board_init_r()函数继续往下分析(arch/arm/lib/board.c文件中)

    移植u-boot-2012.04.01到JZ2440(四:修改源码之支持NOR Flash与NAND Flash)_第10张图片

    定义了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。

4.2.4 修改s3c2440_nand.c(drivers/mtd/nand/目录下)

4.2.4.1 将该文件所有的2410都替换为2440

4.2.4.2 添加s3c2440_nand_select()函数

    在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();
	}
}

4.2.4.3 修改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. 将如下代码:   
  
    改为(指定片选函数)
   
    3. 将如下代码:
    移植u-boot-2012.04.01到JZ2440(四:修改源码之支持NOR Flash与NAND Flash)_第11张图片
    改为屏蔽硬件ECC)
   

4.2.4.4 修改s3c2440_hwcontrol()函数

    修改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);
    }
}

4.2.5 测试

    重新编译,按照上一节的3.3.7 测试NAND Flash启动内容进行烧写启动,串口输出如下:
    移植u-boot-2012.04.01到JZ2440(四:修改源码之支持NOR Flash与NAND Flash)_第12张图片
    可以看到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    //打印
   
串口打印如下:
    移植u-boot-2012.04.01到JZ2440(四:修改源码之支持NOR Flash与NAND Flash)_第13张图片

你可能感兴趣的:(嵌入式杂谈,uboot移植)