加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

u-boot移植--2、EN29LV160AB Nor Flash移植

发布时间:2020-12-15 19:51:00 所属栏目:百科 来源:网络整理
导读:常常说自己是老年人记忆,因为看过的东西很快就忘。秉承着看自己写的东西肯定要比看别人写东西要好理解的原则。写下这个系列的u-boot移植,同时也加深自己的理解。其实到现在网上大神很多,给出了很多的解决方案。我也以他们为参考。与之不同我会加上一些我
常常说自己是老年人记忆,因为看过的东西很快就忘。秉承着看自己写的东西肯定要比看别人写东西要好理解的原则。写下这个系列的u-boot移植,同时也加深自己的理解。其实到现在网上大神很多,给出了很多的解决方案。我也以他们为参考。与之不同我会加上一些我自己对某些地方的理解来进行处理。希望对大家有帮助。

开发板:天嵌TQ2440

u-boot:u-boot-1.1.6

手里的开发板是天嵌的TQ2440,看的书是韦东山老师的《嵌入式linux开发完全手册》,有些许不同的地方,增加了移植困得,当应该也更加深自己的理解。


这一讲是u-boot移植的Nor Flash移植。

我使用的开发板Nor Flash型号为 EN29LV160AB

首先看一波电路


通过net label可以知道Nor flash接的是S3C2440的bank0,Nor flash数据手册的第47位为BYTE#,当其接高电平时,表示16位模式,即word模式。如图中所示我们所用的就是16为模式,所以图中数据接口D0-D15接的是S3C2440数据线LDATA0-LDATA15。地址接口A0-A19接S3C2440地址线的LADDR1-LADDR20。这里解释一下,为什么是从LADDR1开始接,原因就是我们使用了16位数据线模式,一次地址写两个字节。将地址线错一位接,使通过S3C2440访问读写的地址与实际的地址相符。举例来说就是你读0x00地址的数据与0x01地址的数据得到的是相同的结果。真正的差异是从LADDR1位开始的。(我也不知道自己解释的清不清楚。。)看电路图中接了LADDR21和LADDR22到NC管脚,据说这是做芯片扩展用,这里我们不用理会。实际上S3C2440可访问的存储空间为2^20*16bit=2Mbyte,这与芯片容量相符。

分析完芯片,我们来看看如何修改u-boot能够与之相匹配呢?

1、修改./linclude/configs/TQ2440.h

在TQ2440.h文件中有关于flash芯片的定义与支持,读取里面的代码我们发现之前2410支持两种片子即AMD_LV400与AMD_LV800。我们需要修改将对上面两个片子的支持屏蔽掉,并添加自己的代码。具体修改后的样子如下图所示。

/*-----------------------------------------------------------------------
 * FLASH and environment organization
 */

#if 0
#define CONFIG_AMD_LV400	1	/* uncomment this if you have a LV400 flash */
#define CONFIG_AMD_LV800	1	/* uncomment this if you have a LV800 flash */
#endif

#define CONFIG_EON_LV160 	1	/* uncomment this if you have a LV160 flash */

#define CFG_MAX_FLASH_BANKS	1	/* max number of memory banks */

#ifdef CONFIG_EON_LV160
#define PHYS_FLASH_SIZE		0x00200000 /* 2MB */
#define CFG_MAX_FLASH_SECT	(35)	/* max number of sectors on one chip 35 is define in datasheet */
#define CFG_ENV_ADDR		(CFG_FLASH_BASE + 0x0F0000) /* addr of environment the CFG_FLASH_BASE is define in front 0x00000000 */
#endif

#ifdef CONFIG_AMD_LV800
#define PHYS_FLASH_SIZE		0x00100000 /* 1MB */
#define CFG_MAX_FLASH_SECT	(19)	/* max number of sectors on one chip */
#define CFG_ENV_ADDR		(CFG_FLASH_BASE + 0x0F0000) /* addr of environment */
#endif

#ifdef CONFIG_AMD_LV400
#define PHYS_FLASH_SIZE		0x00080000 /* 512KB */
#define CFG_MAX_FLASH_SECT	(11)	/* max number of sectors on one chip */
#define CFG_ENV_ADDR		(CFG_FLASH_BASE + 0x070000) /* addr of environment */
#endif


上面说到2410支持两种片子即AMD_LV400与AMD_LV800,实际上官方也为其编写了完善的驱动代码(其路径为./board/TQ2440/flash.c,这个驱动支持AMD_LV400与AMD_LV800)。但是我们使用的EN29LV160AB显然不在其支持之列。但是其本质是大同小异的(LV400支持512KB,LV800支持1MB,EN29LV160AB支持2MB)。所以我们需要做相应的修改。首先需要明确的是我们需要在flash.c所对应的头文件中添加对EN29LV160AB的支持。其对应头文件为./include/flash.h。我们需要添加芯片制造商ID和芯片型号ID。添加的代码如下所示(这一部分的添加就是宏定义,添加位置没有要求,符合C语言规范就好,有人说要放到181行,大概是想和#define AMD_ID_LV160B放一起吧)

#define EON_ID_LV160AB   0x22492249    
#define EON_MANUFACT     0x001c001c
至于这两个参数怎么来的,我们可以看下面的EN29LV160AB的命令定义


首先我们配置的是word模式因此上表中我们只看word行,另外我们的Nor Flash型号为EN29LV160AB,它是Bottom Boot类型。通过以上两点我们可以知道manufacturer ID为0x001C,Device ID为0x2249。至于为什么要在定义的时候写两遍?是因为方便处理,将manufacturer ID(高16位)与Device ID(低16位)组合成一个32位的信息赋值给flash_id(这部分操作在./board/TQ2440/flash.c中完成)。

在flash.h中完成了对于flash芯片的厂商和型号ID的定义,接下来我们要修改对应的源文件来适配我们的EN29LV160AB。在修改之前我们来深入的了解一下EN29LV160AB结构。查看datasheet,我们找到了如下对我们有用的信息。



我们从中挖掘两条有用的信息

1、芯片存储空间的结构为1个8Kword,2个4Kword,1个16Kword,31个32Kword扇区,共35个扇区(word模式)

2、35个扇区的排列顺序为第0扇区大小为8K,第1、2为4K,第3为16K,后面31扇区为32K。前面4个扇区加起来刚好是32K

根据这些信息我们去处理./board/TQ2440/flash.c源文件。在u-boot中对Nor Flash的操作分别有初始化、擦除和写入,所以我们主要修改与硬件密切相关的三个函数flash_init、flash_erase、write_hword。

首先修改主扇区的大小,如下所示(即大多数扇区的大小)

 #define MAIN_SECT_SIZE  0x8000  /*32K */
接下来这两句话我们做一下解释(并没有修改)
 44 #define MEM_FLASH_ADDR1         (*(volatile u16 *)(CFG_FLASH_BASE + (0x00000555 << 1)))
 45 #define MEM_FLASH_ADDR2         (*(volatile u16 *)(CFG_FLASH_BASE + (0x000002AA << 1)))

根据上面的Table9中的命令我们可以看到对于word模式下给的地址是555和2AA,即flash芯片希望从A0-A19收到的是555和2AA。当我们通过S3C2440写地址时,我们需要注意前面我们是将LADDR1接到了flash的A0,(为了保持存储空间访问的一致性)因此为了传输正确地址,我们需要在S3C2440写地址之前把地址左移一下。

好接下来我们先先修改flash_init函数。修改后的函数如下所示,涉及到的修改我们以红色标示。

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);
#elif defined(CONFIG_EON_LV160)
			(EON_MANUFACT & FLASH_VENDMASK) |
			(EON_ID_LV160AB & 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,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 + 0x2000 + (j -
								      1) *
						0x1000;
				}

				/* 4th 32 KB */
				if (j == 3) {
					flash_info[i].start[j] =
						flashbase + 0x4000;
				}
			} 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;
}

flash_init函数主要的作用就是将我们在./board/TQ2440/TQ2440.h中定义的关于flash的内容添加flash_info结构体中,便于对flash空间进行规范的管理。首先我们需要修改要写入的flashid,如上面代码中第一处红色所示。接下来对于扇区起始地址的处理,由于我们是以word形式访问存储单元(地址上表现为LADDR1接到了flash的A0),因此表示16kB用0x2000表示,8KB用0x1000表示,32KB用0x4000表示。我们在红色部分做了对应的修改。需要注意的,以第0扇区为例,虽然它的寻址空间为0-0x2000,但是它每个地址访问1word,因此它表示的实际存储空间为16KByte。

接下我们修改flash_print_info函数,这个函数就是打印芯片信息用的。

void flash_print_info (flash_info_t * info)
{
	int i;

	switch (info->flash_id & FLASH_VENDMASK) {
	case (AMD_MANUFACT & FLASH_VENDMASK):
		printf ("AMD: ");
		break;
        case (EON_MANUFACT & FLASH_VENDMASK):
                printf ("EON: ");
                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;
        case (EON_ID_LV160AB & FLASH_TYPEMASK):
                printf ("1x EN29LV160AB (16Mbit)n");
                 break;
	default:
		printf ("Unknown Chip Typen");
		goto Done;
		break;
	}

	printf ("  Size: %ld MB in %d Sectorsn",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_erase。

	if ((info->flash_id & FLASH_VENDMASK) !=
	    (AMD_MANUFACT & FLASH_VENDMASK)) {
		return ERR_UNKNOWN_FLASH_VENDOR;
	}
改为
	if ((info->flash_id & FLASH_VENDMASK) !=
	    (EON_MANUFACT & FLASH_VENDMASK)) {
		return ERR_UNKNOWN_FLASH_VENDOR;
	}
至此关于flash支持的修改已经修改完毕。

我们make all,下载然后查看串口的效果。


可以看到Flash处已经变成了2MB,通过flinfo命令,我们也看到了存储单元信息与扇区信息,说明我们的移植成功了。


最后说一下,我们的移植仅限于兼容性移植,没有对命令时序等做过多的关注,有兴趣的同学可以研究一下。



参考文献:1、《嵌入式linux开发完全手册》、韦东山

2、http://blog.csdn.net/zhaocj (这位博主下面的u-boot系列)

3、http://blog.163.com/cailing_07%40126/blog/static/3391508720116610246756/

4、http://blog.csdn.net/xsckernel/article/details/38868205

5、http://tanatseng.blog.163.com/blog/static/1749916292010102411193391/

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读