平台:mini2440 + sst39vf1601 日期:2012-5-24
四、nor flash移植
分析源代码是为了更好理解芯片的操作时序。
flash_init函数,我们在分析u-boot启动代码时就看见过调用了这个函数,这个函数主要是将flash扇区的起始地址保存在一个全局数据区中,之所以其中有if判断语句判断,是因为该芯片的每个扇区大小不一样,sst39vf1601和这款芯片不同,它的每个扇区大小都是一样的,所以我们需要对这部分代码做部分修改。
sst39vf1601芯片知识补充:
sst39vf1601它是块2M大小的nofflash芯片,它有32个块,每块大小为64K。如果以扇区来看,它有512个扇区,每个扇区都是相同大小的,为4K。移植后的代码如下:
/*-----------------------------------------------------------------------
?*/
ulong flash_init (void)
{
? ? ? ? int i,j;
? ? ? ? ulong size = 0;
? ? ? ? for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {
? ? ? ? ? ? ? ? ulong flashbase = 0;
? ? ? ? ? ? ? ? flash_info[i].flash_id =
#if defined(CONFIG_SST_xF1601)
? ? ? ? ? ? ? ? ? ? ? ? (SST_MANUFACT & FLASH_VENDMASK) |
? ? ? ? ? ? ? ? ? ? ? ? (SST_ID_xF1601 & FLASH_TYPEMASK);
#else
#error "Unknown flash configured"
#endif
? ? ? ? ? ? ? ? flash_info[i].size = FLASH_BANK_SIZE;
? ? ? ? ? ? ? ? flash_info[i].sector_count = CONFIG_SYS_MAX_FLASH_SECT;
? ? ? ? ? ? ? ? memset (flash_info[i].protect,CONFIG_SYS_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++) {
? ? ? ? ? ? ? ? ? ? ? ? flash_info[i].start[j] = flashbase + j * MAIN_SECT_SIZE;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? size += flash_info[i].size;
? ? ? ? }
? ? ? ? flash_protect (FLAG_PROTECT_SET,
? ? ? ? ? ? ? ? ? ? ? ?CONFIG_SYS_FLASH_BASE,
? ? ? ? ? ? ? ? ? ? ? ?CONFIG_SYS_FLASH_BASE + monitor_flash_len - 1,
? ? ? ? ? ? ? ? ? ? ? ?&flash_info[0]);
? ? ? ? flash_protect (FLAG_PROTECT_SET,
? ? ? ? ? ? ? ? ? ? ? ?CONFIG_ENV_ADDR,
? ? ? ? ? ? ? ? ? ? ? ?CONFIG_ENV_ADDR + CONFIG_ENV_SIZE - 1,&flash_info[0]);
? ? ? ? return size;
}
修改flash_print_info函数,这个函数打印flash相关信息,修改后代码如下:
/*-----------------------------------------------------------------------
?*/
void flash_print_info (flash_info_t * info)
{
? ? ? ? int i;
? ? ? ? switch (info->flash_id & FLASH_VENDMASK) {
? ? ? ? case (SST_MANUFACT & FLASH_VENDMASK):
? ? ? ? ? ? ? ? printf ("SST: ");
? ? ? ? ? ? ? ? break;
? ? ? ? default:
? ? ? ? ? ? ? ? printf ("Unknown Vendor ");
? ? ? ? ? ? ? ? break;
? ? ? ? }
? ? ? ? switch (info->flash_id & FLASH_TYPEMASK) {
? ? ? ? case (SST_ID_xF1601 & FLASH_TYPEMASK):
? ? ? ? ? ? ? ? printf ("1x SST39VF1601 (2Mbit)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驱动的肯定看过这个表,这就是flash操作的命令表,看懂了这张表,那么你写flash驱动就没有问题了。
先说擦除,擦除有三种方式擦除,扇区擦除、块擦除和芯片擦除。所有的norflash操作都是发送上面的命令序列完成的,这三种方式擦除只是最后一个周期的命令不一样,扇区擦除对应的是30H,块擦除和芯片擦除分别对应的是50H和10H。扇区擦除对应的地址是扇区地址,块擦除对应的地址是块地址,而芯片擦除地址固定为5555H。
那么怎么判断擦除操作是否成功了呢?有三种判断方式,这三种方式也同样适用于写操作。
1.根据时间判断
操作命令发送之后,等待相应的一段时间,如果不出意外的话,操作就算成功。字编程、扇区擦除、块擦除和芯片擦除等待的时间分别为TBP、TSE、TBE和TSCE,它们的最大值分别为10us、25ms、25ms和50ms。也就是说这个最大等待时间都还没有完成操作的话,那么你就别等了,可能操作发送什么错误了。
2.Toggle Bit
这种方法是读取相同地址的字数据,判断DQ6是否相同,如果相同说明操作完成,如果不相同,说明操作没有完成。
3.Data# Polling
这种方法是读取DQ7值,如果值为1,操作完成,否则,操作没有完成。
还有就是需要说明的是,由于mini2440和norflash连线时是A1连接到norflash的A0这样错开一位连接的。所以在发送5555H和2AAA和这种地址的时候需要将地址左移一位,以便norflash能收到正确的地址。
在u-boot中我们可以修改驱动支持扇区擦除或者是块擦除,我们修改u-boot中的sector大小和数量即可实现。注意sector和norflash中的扇区概念是不同的,sector可以理解为擦除操作的一个基本单位。那么支持哪种擦除好呢,这就要看实际需求了,扇区擦除可以将norflash分的很细,但是块擦除操作速度更快。这里我们根据实际需要选择块作为u-boot中的基本操作单元。修改后代码如下。
支持块擦除需要修改上面的擦除命令:
//#define CMD_ERASE_CONFIRM ? ? 0x00000030
#define CMD_ERASE_CONFIRM ? ? ? 0x00000050
修改擦除函数如下:
/*-----------------------------------------------------------------------
?*/
int flash_erase (flash_info_t * info,int s_first,int s_last)
{
? ? ? ? ushort result;
? ? ? ? int iflag,cflag,prot,sect;
? ? ? ? int rc = ERR_OK;
? ? ? ? int chip;
? ? ? ? /* first look for protection bits */
? ? ? ? if (info->flash_id == FLASH_UNKNOWN)
? ? ? ? ? ? ? ? return ERR_UNKNOWN_FLASH_TYPE;
? ? ? ? if ((s_first < 0) || (s_first > s_last)) {
? ? ? ? ? ? ? ? return ERR_INVAL;
? ? ? ? }
? ? ? ? if ((info->flash_id & FLASH_VENDMASK) !=
? ? ? ? ? ? (SST_MANUFACT & FLASH_VENDMASK)) {
? ? ? ? ? ? ? ? return ERR_UNKNOWN_FLASH_VENDOR;
? ? ? ? }
? ? ? ? prot = 0;
? ? ? ? for (sect = s_first; sect <= s_last; ++sect) {
? ? ? ? ? ? ? ? if (info->protect[sect]) {
? ? ? ? ? ? ? ? ? ? ? ? prot++;
? ? ? ? ? ? ? ? }
? ? ? ? }
? ? ? ? if (prot)
? ? ? ? ? ? ? ? return ERR_PROTECTED;
? ? ? ? /*
? ? ? ? ?* Disable interrupts which might cause a timeout
? ? ? ? ?* here. Remember that our exception vectors are
? ? ? ? ?* at address 0 in the flash,and we don't want a
? ? ? ? ?* (ticker) exception to happen while the flash
? ? ? ? ?* chip is in programming mode.
? ? ? ? ?*/
? ? ? ? cflag = icache_status ();
? ? ? ? icache_disable ();
? ? ? ? iflag = disable_interrupts ();
? ? ? ? /* Start erase on unprotected sectors */
? ? ? ? for (sect = s_first; sect <= s_last && !ctrlc (); sect++) {
? ? ? ? ? ? ? ? printf ("Erasing sector %2d ... ",sect);
? ? ? ? ? ? ? ? /* arm simple,non interrupt dependent timer */
? ? ? ? ? ? ? ? reset_timer_masked ();
? ? ? ? ? ? ? ? if (info->protect[sect] == 0) { /* not protected */
? ? ? ? ? ? ? ? ? ? ? ? vu_short *addr = (vu_short *) (info->start[sect]);
? ? ? ? ? ? ? ? ? ? ? ? MEM_FLASH_ADDR1 = CMD_UNLOCK1;
? ? ? ? ? ? ? ? ? ? ? ? MEM_FLASH_ADDR2 = CMD_UNLOCK2;
? ? ? ? ? ? ? ? ? ? ? ? MEM_FLASH_ADDR1 = CMD_ERASE_SETUP;
? ? ? ? ? ? ? ? ? ? ? ? MEM_FLASH_ADDR1 = CMD_UNLOCK1;
? ? ? ? ? ? ? ? ? ? ? ? MEM_FLASH_ADDR2 = CMD_UNLOCK2;
? ? ? ? ? ? ? ? ? ? ? ? *addr = CMD_ERASE_CONFIRM;
? ? ? ? ? ? ? ? ? ? ? ? /* wait until flash is ready */
? ? ? ? ? ? ? ? ? ? ? ? chip = 0;
? ? ? ? ? ? ? ? ? ? ? ? do {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? result = *addr;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /* check timeout */
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (get_timer_masked () >
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? CONFIG_SYS_FLASH_ERASE_TOUT) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? MEM_FLASH_ADDR1 = CMD_READ_ARRAY;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? chip = TMO;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (!chip
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? && (result & 0xFFFF) & BIT_ERASE_DONE)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? chip = READY;
? ? ? ? ? ? ? ? ? ? ? ? } while (!chip);
? ? ? ? ? ? ? ? ? ? ? ? if (chip == ERR) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? rc = ERR_PROG_ERROR;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? goto outahere;
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? if (chip == TMO) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? rc = ERR_TIMOUT;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? goto outahere;
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? printf ("ok.n");
? ? ? ? ? ? ? ? } else { ? ? ? ?/* it was protected */
? ? ? ? ? ? ? ? ? ? ? ? printf ("protected!n");
? ? ? ? ? ? ? ? }
? ? ? ? }
? ? ? ? if (ctrlc ())
? ? ? ? ? ? ? ? printf ("User Interrupt!n");
? ? ? outahere:
? ? ? ? /* allow flash to settle - wait 10 ms */
? ? ? ? udelay_masked (10000);
? ? ? ? if (iflag)
? ? ? ? ? ? ? ? enable_interrupts ();
? ? ? ? if (cflag)
? ? ? ? ? ? ? ? icache_enable ();
? ? ? ? return rc;
}
主要修改的部分是对擦除成功的判断,我们这里用到了两个方法,一个是时间,如果超过一定时间则认为擦除失败。另一个是判断DQ7的值,如果操作成功,该位将被置1。
看了擦除,再来看写操作。
写操作只有字写,也就是说一次写两个字节,我们看u-boot中写函数很明显和sst39vf1601写的命令列表不一样,所以这部分肯定是要修改的。修改后代码如下:
/*-----------------------------------------------------------------------
?* Copy memory to flash
?*/
static int write_hword (flash_info_t * info,ulong dest,ushort data)
{
? ? ? ? vu_short *addr = (vu_short *) dest;
? ? ? ? ushort result;
? ? ? ? int rc = ERR_OK;
? ? ? ? int cflag,iflag;
? ? ? ? int chip;
? ? ? ? /*
? ? ? ? ?* Check if Flash is (sufficiently) erased
? ? ? ? ?*/
? ? ? ? result = *addr;
? ? ? ? if ((result & data) != data)
? ? ? ? ? ? ? ? return ERR_NOT_ERASED;
? ? ? ? /*
? ? ? ? ?* Disable interrupts which might cause a timeout
? ? ? ? ?* here. Remember that our exception vectors are
? ? ? ? ?* at address 0 in the flash,and we don't want a
? ? ? ? ?* (ticker) exception to happen while the flash
? ? ? ? ?* chip is in programming mode.
? ? ? ? ?*/
? ? ? ? cflag = icache_status ();
? ? ? ? icache_disable ();
? ? ? ? iflag = disable_interrupts ();
? ? ? ? MEM_FLASH_ADDR1 = CMD_UNLOCK1;
? ? ? ? MEM_FLASH_ADDR2 = CMD_UNLOCK2;
? ? ? ? //MEM_FLASH_ADDR1 = CMD_UNLOCK_BYPASS;
? ? ? ? //*addr = CMD_PROGRAM;
? ? ? ? MEM_FLASH_ADDR1 = CMD_PROGRAM;
? ? ? ? *addr = data;
? ? ? ? /* arm simple,non interrupt dependent timer */
? ? ? ? reset_timer_masked ();
? ? ? ? /* wait until flash is ready */
? ? ? ? chip = 0;
? ? ? ? do {
? ? ? ? ? ? ? ? result = *addr;
? ? ? ? ? ? ? ? /* check timeout */
? ? ? ? ? ? ? ? if (get_timer_masked () > CONFIG_SYS_FLASH_ERASE_TOUT) {
? ? ? ? ? ? ? ? ? ? ? ? chip = ERR | TMO;
? ? ? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if (!chip && ((result & 0x80) == (data & 0x80)))
? ? ? ? ? ? ? ? ? ? ? ? chip = READY;
? ? ? ? } while (!chip);
? ? ? ? if (chip == ERR || *addr != data)
? ? ? ? ? ? ? ? rc = ERR_PROG_ERROR;
? ? ? ? if (iflag)
? ? ? ? ? ? ? ? enable_interrupts ();
? ? ? ? if (cflag)
? ? ? ? ? ? ? ? icache_enable ();
? ? ? ? return rc;
}
修改了命令时序和对操作是否成功做检测。
至此,整个flash驱动修改完成。
最后,还有最重要的,在配置文件中定义该芯片相关的一些宏定义,这样整个norflash驱动才算移植完成。norflash相关修改如下:
#define PHYS_FLASH_1 ? ? ? ? ? ?0x00000000 /* Flash Bank #1 */
#define CONFIG_SYS_FLASH_BASE ? ? ? ? ? PHYS_FLASH_1
/*-----------------------------------------------------------------------
?* FLASH and environment organization
?*/
#define CONFIG_SST_xF1601 ? ? ? 1 ? ? ? /* sst39vf1601 */
#define CONFIG_SYS_MAX_FLASH_BANKS ? ? ?1 ? ? ? /* max number of memory banks */
#ifdef CONFIG_SST_xF1601
#define PHYS_FLASH_SIZE ? ? ? ? 0x00200000 /* 2MB */
#define CONFIG_SYS_MAX_FLASH_SECT ? ? ? (32) ? ?/* max number of sectors on one chip */
#define CONFIG_ENV_ADDR ? ? ? ? (CONFIG_SYS_FLASH_BASE + 0x040000) /* addr of environment */
#endif
/* timeout values are in ticks */
#define CONFIG_SYS_FLASH_ERASE_TOUT ? ? (3*CONFIG_SYS_HZ) /* Timeout for Flash Erase */
#define CONFIG_SYS_FLASH_WRITE_TOUT ? ? (CONFIG_SYS_HZ/200) /* Timeout for Flash Write */
#define CONFIG_ENV_IS_IN_FLASH ?1
#define CONFIG_ENV_SIZE ? ? ? ? 0x10000 /* Total Size of Environment Sector */
其中定义了sst39vf1601sector数目,环境变量起始地址和空间大小。
对超时检测的时间修改:
/* the PWM TImer 4 uses a counter of 15625 for 10 ms,so we need */
/* it to wrap 100 times (total 1562500) to get 1 sec. */
#define CONFIG_SYS_HZ ? ? ? ? ? ? ? ? ? 15625 //1562500
原来的1秒种太长了,还是修改成10毫秒合适。
附:完整nor flash驱动flash.c可以从这里下载flash.c