u-boot移植--2、EN29LV160AB Nor Flash移植
常常说自己是老年人记忆,因为看过的东西很快就忘。秉承着看自己写的东西肯定要比看别人写东西要好理解的原则。写下这个系列的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/ (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |