平台: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, 0, 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 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操作的命令表,看懂了这张表,那么你写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大小和数量即可实现。注意u-boot中的sector和norflash中的扇区概念是不同的,u-boot中的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毫秒合适。