u-boot移植到mini2440之四

平台: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,块擦除和芯片擦除分别对应的是50H10H。扇区擦除对应的地址是扇区地址,块擦除对应的地址是块地址,而芯片擦除地址固定为5555H

那么怎么判断擦除操作是否成功了呢?有三种判断方式,这三种方式也同样适用于写操作。

1.根据时间判断

操作命令发送之后,等待相应的一段时间,如果不出意外的话,操作就算成功。字编程、扇区擦除、块擦除和芯片擦除等待的时间分别为TBPTSETBETSCE,它们的最大值分别为10us25ms25ms50ms。也就是说这个最大等待时间都还没有完成操作的话,那么你就别等了,可能操作发送什么错误了。

2.Toggle Bit

这种方法是读取相同地址的字数据,判断DQ6是否相同,如果相同说明操作完成,如果不相同,说明操作没有完成。

3.Data# Polling

这种方法是读取DQ7值,如果值为1,操作完成,否则,操作没有完成。

还有就是需要说明的是,由于mini2440norflash连线时是A1连接到norflashA0这样错开一位连接的。所以在发送5555H2AAA和这种地址的时候需要将地址左移一位,以便norflash能收到正确的地址。

u-boot中我们可以修改驱动支持扇区擦除或者是块擦除,我们修改u-boot中的sector大小和数量即可实现。注意u-boot中的sectornorflash中的扇区概念是不同的,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毫秒合适。


附:完整nor flash驱动flash.c可以从这里下载flash.c


posted @ 2012-06-01 23:05  移动应用开发  阅读(214)  评论(0编辑  收藏  举报