开发板:天嵌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 + 0x1F0000) /* 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。
首先检查主扇区的大小,如下所示(即大多数扇区的大小,我们的为64KB)
#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, 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;
}
flash_init函数主要的作用就是将我们在./board/TQ2440/TQ2440.h中定义的关于flash的内容添加flash_info结构体中,便于对flash空间进行规范的管理。首先我们需要修改要写入的flashid,如上面代码中第一处红色所示。接下来对于扇区起始地址的处理,虽然我们是以word形式访问存储单元(地址上表现为LADDR1接到了flash的A0),但是表示16kB还是用0x4000表示,8KB用0x2000表示,32KB用0x8000表示。我们在红色部分做了对应的修改。
接下我们修改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 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_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/