S3C2440对NORflash芯片EN29LV160AB的操作详解

最近在研究自己的GT2440开发板时,在u-boot使用flinfo命令显示NORFLASH信息时,发现打印出的信息与datasheet不符合:

 

00000000 (RO) 00004000 (RO) 00006000 (RO) 00008000 (RO) 00010000 (RO)
00020000 (RO) 00030000 (RO) 00040000      00050000      00060000     
00070000      00080000      00090000      000A0000      000B0000      
000C0000      000D0000      000E0000      000F0000      00100000
00110000      00120000      00130000      00140000      00150000     
00160000      00170000      00180000      00190000      001A0000     
001B0000      001C0000      001D0000      001E0000      001F0000 

 

此处为什么选择Bottom Boot,我也不明白,只知道这个与芯片有关系,S3C2440芯片只能使用Bottom Boot,如果有哪位知道的话,希望赐教。这里我们对比不难发现,我们的板子上使用的是word Mode,但是u-boot下打印出的分区信息却是Byte Mode,难道是官方的u-boot出现BUG。于是,我自己修改了u-boot代码,添加一些打印信息,然后重新编译,并利用u-boot自带的烧写功能将u-boot重新少些进去,发现u-boot能够正常读写,并启动。因此判断官方的代码没有问题,于是怀疑是否在操作flash时对地址作了处理。于是自己仔细分析了下u-boot中NORFLASH的代码,其中对NORFLASH操作的核心代码子在(u-boot/board/gtstudio/flash.c)文件中:

 

ulong flash_init (void)
{
	int i, j;
	ulong size = 0;

	for (i = 0; i < CFG_MAX_FLASH_BANKS; i++) {
		ulong flashbase = 0;

		flash_info[i].flash_id =
#if defined(CONFIG_AMD_LV400)
			(AMD_MANUFACT & FLASH_VENDMASK) |
			(AMD_ID_LV400B & FLASH_TYPEMASK);
#elif defined(CONFIG_AMD_LV800)
			(AMD_MANUFACT & FLASH_VENDMASK) |
			(AMD_ID_LV800B & FLASH_TYPEMASK);
#else
#error "Unknown flash configured"
#endif
			flash_info[i].size = FLASH_BANK_SIZE;
		flash_info[i].sector_count = CFG_MAX_FLASH_SECT;
		memset (flash_info[i].protect, 0, CFG_MAX_FLASH_SECT);
		if (i == 0)
			flashbase = PHYS_FLASH_1;
		else
			panic ("configured too many flash banks!\n");
		for (j = 0; j < flash_info[i].sector_count; j++) {
			if (j <= 3) {
				/* 1st one is 16 KB */
				if (j == 0) {
					flash_info[i].start[j] =
						flashbase + 0;
				}

				/* 2nd and 3rd are both 8 KB */
				if ((j == 1) || (j == 2)) {
					flash_info[i].start[j] =
						flashbase + 0x4000 + (j -
								      1) *
						0x2000;
				}

				/* 4th 32 KB */
				if (j == 3) {
					flash_info[i].start[j] =
						flashbase + 0x8000;
				}
			} else {
				flash_info[i].start[j] =
					flashbase + (j - 3) * MAIN_SECT_SIZE;
			}
		}
		size += flash_info[i].size;
	}

	flash_protect (FLAG_PROTECT_SET,
		       CFG_FLASH_BASE,
		       CFG_FLASH_BASE + monitor_flash_len - 1,
		       &flash_info[0]);

	flash_protect (FLAG_PROTECT_SET,
		       CFG_ENV_ADDR,
		       CFG_ENV_ADDR + CFG_ENV_SIZE - 1, &flash_info[0]);

	return size;
}

这个是NORFLASH初始化文件,在u-boot检测硬件信息时被调用,其中我们不难发现flash[i].start[j]是在对FLASH扇区地址赋值,这块的赋值与我们前面打印出来的信息一致。同时还发现这里面还有一个函数flash_print_info,这个函数就是我们输入flinfo时,通过调用do_flinfo,然后调用它的:

(u-boot/board/gtstudio/flash.c)
void flash_print_info (flash_info_t * info)
{
	int i;

	switch (info->flash_id & FLASH_VENDMASK) {
	case (AMD_MANUFACT & FLASH_VENDMASK):
		printf ("AMD: ");
		break;
	default:
		printf ("Unknown Vendor ");
		break;
	}

	switch (info->flash_id & FLASH_TYPEMASK) {
	case (AMD_ID_LV400B & FLASH_TYPEMASK):
		printf ("1x Amd29LV400BB (4Mbit)\n");
		break;
	case (AMD_ID_LV800B & FLASH_TYPEMASK):
		printf ("1x Amd29LV800BB (8Mbit)\n");
		break;
	default:
		printf ("Unknown Chip Type\n");
		goto Done;
		break;
	}

	printf ("  Size: %ld MB in %d Sectors\n",
		info->size >> 20, info->sector_count);

	printf ("  Sector Start Addresses:");
	for (i = 0; i < info->sector_count; i++) {
		if ((i % 5) == 0) {
			printf ("\n   ");
		}
		printf (" %08lX%s", info->start[i],
			info->protect[i] ? " (RO)" : "     ");
	}
	printf ("\n");

      Done:;
}

开始我怀疑可能它在擦出和写入flash时对flash的扇区信息作了处理,才没有在重新烧写u-boot代码时出错,于是对erase命令和cp.b命令跟踪,主要此处的cp.b命令中的.b代码字节,也即是拷贝字节。

 

erase命令跟踪如下,do_flerase:

 

<pre name="code" class="html">(common/com_flash.c)

int do_flerase (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]){flash_info_t *info;ulong bank, addr_first, addr_last;int n, sect_first, sect_last;#if (CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE)struct mtd_device *dev;struct part_info *part;u8 dev_type, dev_num, pnum;#endifint rcode = 0;if (argc < 2) {printf ("Usage:\n%s\n", cmdtp->usage);return 1;}if (strcmp(argv[1], "all") == 0) {for (bank=1; bank<=CFG_MAX_FLASH_BANKS; ++bank) {printf ("Erase Flash Bank # %ld ", bank);info = &flash_info[bank-1];rcode = flash_erase (info, 0, info->sector_count-1);}return rcode;}if ((n = abbrev_spec(argv[1], &info, §_first, §_last)) != 0) {if (n < 0) {puts ("Bad sector specification\n");return 1;}printf ("Erase Flash Sectors %d-%d in Bank # %d ",sect_first, sect_last, (info-flash_info)+1);rcode = flash_erase(info, sect_first, sect_last);return rcode;}rcode = flash_sect_erase(addr_first, addr_last);return rcode;}

 

 

do_flerase主要调用flash_sect_erase函数:

<pre name="code" class="html">(common/com_flash.c)

int flash_sect_erase (ulong addr_first, ulong addr_last){flash_info_t *info;ulong bank;#ifdef CFG_MAX_FLASH_BANKS_DETECTint s_first[CFG_MAX_FLASH_BANKS_DETECT], s_last[CFG_MAX_FLASH_BANKS_DETECT];#elseint s_first[CFG_MAX_FLASH_BANKS], s_last[CFG_MAX_FLASH_BANKS];#endifint erased = 0;int planned;int rcode = 0;rcode = flash_fill_sect_ranges (addr_first, addr_last,s_first, s_last, &planned );if (planned && (rcode == 0)) {for (bank=0,info=&flash_info[0]; (bank < CFG_MAX_FLASH_BANKS) && (rcode == 0); ++bank, ++info) {if (s_first[bank]>=0) {erased += s_last[bank] - s_first[bank] + 1;debug ("Erase Flash from 0x%08lx to 0x%08lx ""in Bank # %ld ",info->start[s_first[bank]],(s_last[bank] == info->sector_count) ?info->start[0] + info->size - 1:info->start[s_last[bank]+1] - 1,bank+1);rcode = flash_erase (info, s_first[bank], s_last[bank]);}}printf ("Erased %d sectors\n", erased);} else if (rcode == 0) {puts ("Error: start and/or end address"" not on sector boundary\n");rcode = 1;}return rcode;

 

而flash_sect_erase()函数则调用flash_fill_sect_rangs()函数确定需要擦除的扇区,然后再调用flash_erase进行擦除。因此关键在flash_fill_sect_rangs()函数:

 

 

(common/com_flash.c)
static int
flash_fill_sect_ranges (ulong addr_first, ulong addr_last,
			int *s_first, int *s_last,
			int *s_count )
{
	flash_info_t *info;
	ulong bank;
	int rcode = 0;

	*s_count = 0;

	for (bank=0; bank < CFG_MAX_FLASH_BANKS; ++bank) {
		s_first[bank] = -1;	/* first sector to erase	*/
		s_last [bank] = -1;	/* last  sector to erase	*/
	}

	for (bank=0,info=&flash_info[0];
	     (bank < CFG_MAX_FLASH_BANKS) && (addr_first <= addr_last);
	     ++bank, ++info) {
		ulong b_end;
		int sect;
		short s_end;

		if (info->flash_id == FLASH_UNKNOWN) {
			continue;
		}

		b_end = info->start[0] + info->size - 1;	/* bank end addr */
		s_end = info->sector_count - 1;			/* last sector   */


		for (sect=0; sect < info->sector_count; ++sect) {
			ulong end;	/* last address in current sect	*/

			end = (sect == s_end) ? b_end : info->start[sect + 1] - 1;

			if (addr_first > end)
				continue;
			if (addr_last < info->start[sect])  
				continue;

			if (addr_first == info->start[sect]) {
				s_first[bank] = sect;
			}
			if (addr_last  == end) {
				s_last[bank]  = sect;
			}
		}
		if (s_first[bank] >= 0) {
			if (s_last[bank] < 0) {
				if (addr_last > b_end) {
					s_last[bank] = s_end;
				} else {
					puts ("Error: end address"
						" not on sector boundary\n");
					rcode = 1;
					break;
				}
			}
			if (s_last[bank] < s_first[bank]) {
				puts ("Error: end sector"
					" precedes start sector\n");
				rcode = 1;
				break;
			}
			sect = s_last[bank];
			addr_first = (sect == s_end) ? b_end + 1: info->start[sect + 1];
			(*s_count) += s_last[bank] - s_first[bank] + 1;
		} else if (addr_first >= info->start[0] && addr_first < b_end) {
			puts ("Error: start address not on sector boundary\n");
			rcode = 1;
			break;
		} else if (s_last[bank] >= 0) {
			puts ("Error: cannot span across banks when they are"
			       " mapped in reverse order\n");
			rcode = 1;
			break;
		}
	}

	return rcode;
}

此处flash_fill_sect_rangs()函数主要通过将起始地址和尾地址与flash的sector起始地址比较来确定需要擦除的扇区,因此根本没有对flash的扇区信息做任何处理。flash的烧写函数也是一样,首先调用do_mem_cp()函数:

 

 

(common/cmd_mem.c)
int do_mem_cp ( cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	ulong	addr, dest, count;
	int	size;

	if (argc != 4) {
		printf ("Usage:\n%s\n", cmdtp->usage);
		return 1;
	}

	/* Check for size specification.
	*/
	if ((size = cmd_get_data_size(argv[0], 4)) < 0)
		return 1;

	addr = simple_strtoul(argv[1], NULL, 16);
	addr += base_address;

	dest = simple_strtoul(argv[2], NULL, 16);
	dest += base_address;

	count = simple_strtoul(argv[3], NULL, 16);

	if (count == 0) {
		puts ("Zero length ???\n");
		return 1;
	}

#ifndef CFG_NO_FLASH
	/* check if we are copying to Flash */
	if ( (addr2info(dest) != NULL)
#ifdef CONFIG_HAS_DATAFLASH
	   && (!addr_dataflash(addr))
#endif
	   ) {
		int rc;

		puts ("Copy to Flash... ");

		rc = flash_write ((char *)addr, dest, count*size);
		if (rc != 0) {
			flash_perror (rc);
			return (1);
		}
		puts ("done\n");
		return 0;
	}
#endif
	while (count-- > 0) {
		if (size == 4)
			*((ulong  *)dest) = *((ulong  *)addr);
		else if (size == 2)
			*((ushort *)dest) = *((ushort *)addr);
		else
			*((u_char *)dest) = *((u_char *)addr);
		addr += size;
		dest += size;
	}
	return 0;
}

 

do_mem_cp()函数将参数传递给flash_write()函数,然后通过该函数对原地址和目标地址作简单处理,最终调用write_buffer()函数实现对flash的编程,此处我们应该注意下flash_write()函数:

 

int
flash_write (char *src, ulong addr, ulong cnt)
{
#ifdef CONFIG_SPD823TS
	return (ERR_TIMOUT);	/* any other error codes are possible as well */
#else
	int i;
	ulong         end        = addr + cnt - 1;
	flash_info_t *info_first = addr2info (addr);
	flash_info_t *info_last  = addr2info (end );
	flash_info_t *info;

	if (cnt == 0) {
		return (ERR_OK);
	}

	if (!info_first || !info_last) {
		return (ERR_INVAL);
	}

	for (info = info_first; info <= info_last; ++info) {
		ulong b_end = info->start[0] + info->size;	/* bank end addr */
		short s_end = info->sector_count - 1;
		for (i=0; i<info->sector_count; ++i) {
			ulong e_addr = (i == s_end) ? b_end : info->start[i + 1];

			if ((end >= info->start[i]) && (addr < e_addr) &&
			    (info->protect[i] != 0) ) {
				return (ERR_PROTECTED);
			}
		}
	}

	/* finally write data to flash */
	for (info = info_first; info <= info_last && cnt>0; ++info) {
		ulong len;

		len = info->start[0] + info->size - addr;
		if (len > cnt)
			len = cnt;
		if ((i = write_buff(info, (uchar *)src, addr, len)) != 0) {
			return (i);
		}
		cnt  -= len;
		addr += len;
		src  += len;
	}
	return (ERR_OK);
#endif /* CONFIG_SPD823TS */
}

flash_write()函数此处作了地址对齐处理,这不难让我们想起S3C2440连接NORFLASH是,将ADDR1连接在ADDR0上,仔细想下,我这才明白了。原来,当S3C2440在写数据时,地址将会右移移位,比如:sector1地址0x004000>>1等于0x002000,sector35地址1F0000>>1等于F8000,所以flash读写和擦除的地址都正确。因此,如果想显示与datasheet中对应的sector地址,其实只需要改写flash_print_info()函数,将地址右移移位,然后显示就行了。顺便指出下GT2440配置的flash扇区数量为19,不正确,应该是35。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值