Chinaunix首页 | 论坛 | 博客
  • 博客访问: 169729
  • 博文数量: 26
  • 博客积分: 1427
  • 博客等级: 上尉
  • 技术积分: 310
  • 用 户 组: 普通用户
  • 注册时间: 2009-04-28 12:00
文章分类

全部博文(26)

文章存档

2012年(1)

2009年(25)

我的朋友

分类:

2009-04-30 10:09:02

    前不久买了阳初2440超值板,价格算是便宜,但基本没有技术服务,原来还有个论坛现在都关了,真是不责任啊!
    我前面已经尝试过将板子上的64M Flash换为大容量的,发现2440不支持MLC的ECC,而同为SLC结构的8位NAND Flash,128M以下的主要是512字节页面,128M以上的为2K页面,就是因为页面大小的不同,导致换上去的512M Flash不能正常引导了。
    查看vivi2440_src/include/mtd/nand_ids.h中是包含对512M Flash支持的,只是pagesize, pageadrlen, erasesize对应的域均为0。开始以为是这个地方的问题,后来发现vivi2440_src/drivers/mtd/nand/smc_core.c中的函数int smc_scan(struct mtd_info *mtd)有:

#ifdef USE_256BYTE_NAND_FLASH
            if (!mtd->size) {
                mtd->name = nand_flash_ids[i].name;
                mtd->erasesize = nand_flash_ids[i].erasesize;
                mtd->size = (1 << nand_flash_ids[i].chipshift);
                mtd->eccsize = 256;
                if (nand_flash_ids[i].pagesize == PAGESIZE256) {
                    mtd->oobblock = 256;
                    mtd->oobsize = 8;
                    this->page_shift = 8;
                } else {
                    mtd->oobblock = 512;
                    mtd->oobsize = 16;
                    this->page_shift = 9;
                }
                this->dev = &nand_smc_info[GET_DI_NUM(nand_flash_ids[i].chipshift)];
            }
#else
         if (!(nand_flash_ids[i].pagesize)) {
         int extid;
                /* The 3rd id byte contains non relevant data ATM */
                extid = this->read_data();
                /* The 4th id byte is the important one */
                extid = this->read_data();
                /* Calc pagesize */
                mtd->oobblock = 1024 << (extid & 0x3);
                extid >>= 2;
                /* Calc oobsize */
                mtd->oobsize = (8 << (extid & 0x01)) * (mtd->oobblock >> 9);
                extid >>= 2;
                /* Calc blocksize. Blocksize is multiples of 64KiB */
                mtd->erasesize = (64 * 1024) << (extid & 0x03);
                extid >>= 2;
                /* Get buswidth information */
                //busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;

                mtd->name = nand_flash_ids[i].name;
                mtd->size = (1 << nand_flash_ids[i].chipshift);
                mtd->eccsize = 256;
                this->page_shift = 11;
                this->dev = &nand_smc_info[GET_DI_NUM(nand_flash_ids[i].chipshift)];
                this->badblockpos = 0;
         } else if (!(mtd->size) && (nand_flash_ids[i].pagesize != PAGESIZE256)) {
                mtd->name = nand_flash_ids[i].name;
                mtd->erasesize = nand_flash_ids[i].erasesize;
                mtd->size = (1 << nand_flash_ids[i].chipshift);
                mtd->eccsize = 256;
                mtd->oobblock = 512;
                mtd->oobsize = 16;
                this->page_shift = 9;
                this->dev = &nand_smc_info[GET_DI_NUM(nand_flash_ids[i].chipshift)];
                this->badblockpos = 5;
            }
#endif
            /* Do not replace user supplied command function ! */
            if ((mtd->oobblock>512) && (this->cmdfunc == nand_command))
                this->cmdfunc = nand_command_lp;

    在预编译指令#ifdef USE_256BYTE_NAND_FLASH#else处检查nand_flash_dev的结构的pagesize为0,表示pagesize等域要从Flash芯片内扩展id读出,具体细节查看芯片手册。

    为追根溯源,干脆直接读vivi2440_src/arch/s3c2440/head.S源码,从Reset标签往下读,CPU执行完一系列的初始化工作(如内存管理、串口设备初始化)之后,终于执行到一个关键的封地方:

    bl    copy_myself

这里可以看作为bootloader的Stage1和Stage2的分界点,实际上应为这个函数执行后紧接着的跳转,即:

    @ jump to ram
    ldr    r1, =on_the_ram
    add    pc, r1, #0

    从字面就可以看出,copy_myself函数把vivi自身从启动设备(Nor Flash或NAND Flash)拷贝到内存中。再查看copy_myself,执行了一系列初始化Flash接口的初始化之后:

#ifndef CONFIG_S3C2440_NAND_BOOT
    ldr    r2, =__bss_start
    ldr    r0, =__ro_start
    mov r1, sl
    sub    r2,r2,r0
    ldmfd sp!,{lr}
    b    mem_copy
#else @ #ifdef CONFIG_S3C2440_NAND_BOOT
    @ copy vivi to RAM
    @ldr    r0, =VIVI_RAM_BASE
    @mov r1, #0x0
    @mov    r2, #0x20000
    @mov    r0, #'R'
    @bl PrintChar
    
    ldr    r2, =__bss_start
    ldr    r0, =__ro_start
    movs r1, sl
    @mov r1, #0
    sub    r2,r2,r0
    ldmnefd sp!,{lr}
    bne mem_copy
    bl    nand_read_ll

    会根据配置文件vivi2440_src/include/autocong.h中的宏CONFIG_S3C2440_NAND_BOOT是否定义来决定执行mem_copy(如从Nor Flash中拷贝)还是nand_read_ll,只是没有太弄明白__bss_start和__ro_start这两个内置标签的地址是怎么被确定的,因为即便在定义了宏CONFIG_S3C2440_NAND_BOOT的情况下,也有可能执行mem_copy的。这种情况就是板子设置为上电从Nor Flash中启动,而且vivi也保存在Nor Flash中,而操作系统内核保存在Nand Flash中,这也是很常见的情况。所以一般即使我们把板子的跳线设为从Nor Flash启动,实际上还是应该配置vivi为Support NAND Boot,因为我们不太可能将操作系统内核放在Nor Flash中的。但是问题是怎么控制执行到

bne mem_copy

这个地方还不是很清楚。

   我们可以先来看nand_read_ll函数,它定义在vivi2440_src/arch/nand_read.c中,这个函数注释为:/* low level nand read function */,也就是vivi在Stage1中会用到,所以要注意这个函数中最好不要调用非low level的函数,比如printf,因为我们可以考虑一下,如果vivi存放在NAND中,在vivi的Stage1只有4K的代码在内部RAM中,你调用的非low level函数极可能不在RAM中。不过,还没有弄清楚怎么控制编译器将这些low level函数放入目标文件的前4K位置。为了替代printf来帮助调试,head.S中定义了PrintChar、PrintWord和PrintHexWord等函数用来向串口终端打印字符,调试的printk应该也不能用,我们可以查看vivi2440_src/vivi.map文件,可以看到目标文件的所有函数入口点的地址(这个地址是加载到内存之后的地址),Stage1中可用的函数入口点都位于33f00000和33f00fff地址之间(反之不成立)。

    这个版本的vivi实际上支持2k页面的NAND Flash的引导,只需要开启nand_read.c页面顶部LARGEPAGE_FLASH的宏定义:

//#define LARGEPAGE_FLASH

不过读取逻辑实现得不是很好,坏块检查只能在读取之前进行一次,读取过程中没有再检查。原代码如下:

#define PAGESPERSBLOCK (32)
#define BYTESPERSPAGE (512)
#define NAND_BLOCK_SMASK (512 - 1)
#define PAGESPERLBLOCK (64)
#define NAND_BLOCK_LMASK (2048 - 1)

#ifndef LARGEPAGE_FLASH
#define NAND_SECTOR_SIZE 512
#else
#define NAND_SECTOR_SIZE 2048
#endif
#define NAND_BLOCK_MASK (NAND_SECTOR_SIZE - 1)

void PrintChar(char msg);

static int maf_id,dev_id;
/* low level nand read function */
int nand_block_checkbad_ll(unsigned long addr);
void PrintChar(char msg);

/* low level nand read function */
int
nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
{
  int j, n;
  unsigned long addr,baddr;

  //PrintChar('r');

  
  if ((start_addr & NAND_BLOCK_MASK)/* || (size & NAND_BLOCK_MASK)*/) {
    return -1; /* invalid alignment */
  }

  NAND_CHIP_ENABLE;

  baddr=start_addr;
  while(size>0) {
    if (!nand_block_checkbad_ll(baddr)) {
      for (j=0;j<size;j+=NAND_SECTOR_SIZE) {
        /* READ0 */
        NAND_CLEAR_RB;
        NFCMD = NAND_CMD_READ0;
    
        addr = baddr+j;
        /* Write Address */
#ifndef LARGEPAGE_FLASH
        NFADDR = addr & 0xff;
        NFADDR = (addr >> 9) & 0xff;
        NFADDR = (addr >> 17) & 0xff;
        NFADDR = (addr >> 25) & 0xff;
#else
        NFADDR = addr & 0xff;
        NFADDR = (addr >> 8) & 0x07;
        NFADDR = (addr >> 11) & 0xff;
        NFADDR = (addr >> 19) & 0xff;
        NFADDR = (addr >> 27) & 0x3;
  
        NFCMD = NAND_CMD_READSTART;
#endif
        NAND_DETECT_RB;
    
        for(n=0; n<NAND_SECTOR_SIZE; n++) {
          *buf = (NFDATA & 0xff);
          buf++;
        }
      }
      size -= j;
    } //else PrintChar('e');

    baddr += (BYTESPERSPAGE*PAGESPERSBLOCK);
  }
  NAND_CHIP_DISABLE;
  return 0;
}

本人修改改为如下编译后能正常使用:

#ifndef LARGEPAGE_FLASH
#define NAND_SECTOR_SIZE 512
#define PAGESPERBLOCK        32
#else
#define NAND_SECTOR_SIZE 2048
#define PAGESPERBLOCK        64
#endif
#define    NAND_BLOCK_SIZE        (NAND_SECTOR_SIZE*PAGESPERBLOCK)
#define NAND_BLOCK_MASK        (NAND_BLOCK_SIZE - 1)

void PrintChar(char msg);
void PrintWord(unsigned int msg);

static int maf_id,dev_id;
/* low level nand read function */
int nand_block_checkbad_ll(unsigned long addr);
void PrintChar(char msg);

/* low level nand read function */
int
nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
{
    int j, n;
    unsigned long addr,baddr;

#ifdef CONFIG_DEBUG_LL
    PrintWord(*((unsigned int *)"NFRD"));
#endif

    if ((start_addr & NAND_BLOCK_MASK)/* || (size & NAND_BLOCK_MASK)*/) {
        return -1; /* invalid alignment */
    }

    NAND_CHIP_ENABLE;
    
    baddr=start_addr;
    for( ;size>0; baddr+=NAND_BLOCK_SIZE) {
        if (nand_block_checkbad_ll(baddr)){
#ifdef CONFIG_DEBUG_LL
            PrintChar('e');
#endif
            continue;
        }
        for (j=0;j<NAND_BLOCK_SIZE && j<size;j+=NAND_SECTOR_SIZE) {
            /* READ0 */
            NAND_CLEAR_RB;
            NFCMD = NAND_CMD_READ0;
            
            addr = baddr+j;
        /* Write Address */
    #ifndef LARGEPAGE_FLASH
            NFADDR = addr & 0xff;
            NFADDR = (addr >> 9) & 0xff;
            NFADDR = (addr >> 17) & 0xff;
            NFADDR = (addr >> 25) & 0xff;
    #else
            NFADDR = addr & 0xff;
            NFADDR = (addr >> 8) & 0x07;
            NFADDR = (addr >> 11) & 0xff;
            NFADDR = (addr >> 19) & 0xff;
            NFADDR = (addr >> 27) & 0x3;
    
            NFCMD = NAND_CMD_READSTART;
    #endif
            NAND_DETECT_RB;
            
            for(n=0; n<NAND_SECTOR_SIZE; n++) {
                *buf = (NFDATA & 0xff);
                buf++;
            }
        }
        size -= NAND_BLOCK_SIZE;    
    }
    NAND_CHIP_DISABLE;
    return 0;
}


    本人将2k页面的支持加下了menuconfig脚本中,在Menuconfig->Main menu->Memory Technology Devices(MTD)->NAND Flash Device Drivers中选中Large Page Device Support即会开启2K页面NAND Flash支持,以上是本人升级Flash的最终成果,附上修改后的vivi源代码。

文件:vivi2440_src.zip
大小:836KB
下载:下载
阅读(2333) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~