• s3c6410 开发板Linux系统支持 K9GAG08U0E的方法


    由于NandFlash硬件升级比较快,公司去年一直在使用三星的K9GAG08U0D,现在MLC NandFlash 升级到了第二代,K9GAG08U0D 很快就会处在停产的状态,未雨绸缪,公司选型了K9GAG08U0E 来替代原有的NandFlash芯片。起初。本以为把新Nandflash ID信息增加到Uboot 和 Linux内核的NandFlash ID 列表文件中即可,也就是Uboot 和Linux源码中的nand_ids.c 文件中,但是仔细看了 K9GAG08U0E 的DataSheet,发现自己的想法错了。

    下面记录了NandFlash更换时面临的几个问题,及解决方法:

    第一个问题:ID 冲突

      K9GAG08U0E 的 Product ID与 K9GAG08U0D 的Product ID 是相同的,都是 0xd5,而在Uboot 代码和Linux内核代码中,是根据ID信息来获取或者计算NandFlash的页大小和块大小这些关键信息的。

     现在这两款的 Flash ID相同,而Page,OOB ,Block大小都是不同的。 K9GAG08U0D  Page,OOB,Block 关键参数:




     


    而K9GAG08U0E 的 Page,OOB,Block  关键参数:




    可见他们是不同的,U0E 每页大小是U0D的两倍。

    针对相同ID的两款不同Flash是怎样区分的呢?仔细看DataSheet,发现了可以快速区分两种设备的方法:


    U0D Page=4K,U0D Page=8K,我们只需判断Read第四个字节的第0位和第1位即可。

    下面列出关键代码段:


         //这里必须有下面两行代码,因为U0E的DataSheet要求系统上电后第一条指令必须是NAND_CMD_RESET,而U0D没有这要求,如果不发送这条指令是无法读取到NandFlash的任何信息的,因为这个问题查找了很长时间,在这里提醒给大家。

         // K9GAG08U0E must add below codes     

           {
              s3c_nand_hwcontrol(0, NAND_CMD_RESET, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
              s3c_nand_device_ready(0);
            }


            s3c_nand_hwcontrol(0, NAND_CMD_READID, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
            s3c_nand_hwcontrol(0, 0x00, NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE);
            s3c_nand_hwcontrol(0, 0x00, NAND_NCE | NAND_ALE);
            s3c_nand_hwcontrol(0, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);


            s3c_nand_device_ready(0);
           tmp = readb(nand->IO_ADDR_R); /* Maf. ID */

          //  printf("Manufactor ID:%x ",tmp);
           tmp = dev_id = readb(nand->IO_ADDR_R); /* Device ID */


            //printf("dev_id ID:%x ",dev_id);
         for (i = 0; nand_flash_ids[i].name != NULL; i++) 

         {
            if (tmp == nand_flash_ids[i].id) {
            type = &nand_flash_ids[i];
           break;
        }

          nand->cellinfo = readb(nand->IO_ADDR_R);/* 3rd byte */
         tmp = readb(nand->IO_ADDR_R);/* 4th byte */

         int childType=tmp & 0x03; //Page size
         //  printf("dev_id=%x,childType=%x ",dev_id,childType);

        if(dev_id == 0xd5 && childType==0x01) //U0D

        {   

        }else  if(dev_id == 0xd5 && childType==0x02) //U0E

       {


        }

    这样针对不同的NandFlash做不同的初始化了,以上代码来自uboot1.1.6/cpu/s3c64xx/nand.c 文件中的void board_nand_init(struct nand_chip *nand) 函数。


    在上一篇中我们重点介绍了K9GAG08U0D 和 K9GAG08U0E的区别,除了这些外还有一个需要重点关注的地方,下面我们详细讲解。

            Uboot从NandFlash启动第一步就是把NandFlash的前8K代码拷贝到s3c6410内部的SRAM中,然后运行,这8K的代码会从NandFlash中把完整的Uboot代码拷贝到6410开发板片外内存中,我这里使用的是256M的DDR,Uboot的存放地址为0x5FE00000,虚拟地址为 0xCFE00000.

    注意:关键的8K代码不是在U0D(每页4K)的前两页存放,也不是在U0E(每页8K)的第一页存放,而是在U0E或者U0D的             前四页中存放的,前四页分为每页2K,总共8K,这是由s3c6410芯片本身所规定的。这8K字节外后面的数据就是按实             际的页数存放了,U0D 每页存放4K字节,U0E每页存放8K字节。

                  s3c6410支持从SD卡启动,从SD卡启动后就可以把Uboot.bin文件写入NandFlash了,可以认真的看一下Uboot 里面 的 nand write.uboot 命令是如何把数据写入NandFlash前4页 的。写入成功后就可以从NandFlash启动Uboot。

           nand write.uboot 的关键代码 位于uboot1.1.6/common/cmd_nand.c文件中:

                if (!read && s != NULL && (!strcmp(s, ".uboot")) && nand->writesize == 4096) //U0D 

               {    

                size=4096;
                nand_write(nand, off, &size, (u_char *)addr);
                off+=4096;
                addr+=2048;
               nand_write(nand, off, &size, (u_char *)addr);
                off+=4096;
                addr+=2048;
               nand_write(nand, off, &size, (u_char *)addr);
               off+=4096;
               addr+=2048;
               nand_write(nand, off, &size, (u_char *)addr);
               off+=4096;
               addr+=2048;

                size=1024*1024-4*4096;
                ret = nand_write(nand, off, &size, (u_char *)addr);

                }else if(!read && s != NULL && (!strcmp(s, ".uboot")) && nand->writesize == 8192) //U0E
                    {
                        size=8192;  
                        ret=nand_write(nand, off, &size, (u_char *)addr);

                        off+=8192;
                        addr+=2048;
                        ret=nand_write(nand, off, &size, (u_char *)addr);
                        off+=8192;
                        addr+=2048;
                        ret=nand_write(nand, off, &size, (u_char *)addr);
                        off+=8192;
                        addr+=2048;
                        ret=nand_write(nand, off, &size, (u_char *)addr);

                        off+=8192;
                        addr+=2048;
    //写剩余的页面,这里的Uboot占用1M的NandFlash地址空间,在U0E里面占用128个页面(128Page=1M),上面已经写了四个页面了,这里写剩余的124个页,124个页面足够存放Uboot的有效数据了。
                        size=1024*1024-4*8192;
                        ret = nand_write(nand, off, &size, (u_char *)addr);
                    }

    现在我们重点要看 Uboot代码,uboot1.1.6/cpu/s3c64xx/nand_cp.c 文件:

     //该函数在Uboot.bin前8K代码中,这个函数实现把Uboot从Nandflash拷贝到外部内存中,是Uboot从Nandflash启动的关键地方

    int copy_uboot_to_ram (void)

    {
          int large_block = 0;
          int i;
          vu_char id;

            NAND_ENABLE_CE();
            NFCMD_REG=NAND_CMD_RESET;
            NF_TRANSRnB();


            NFCMD_REG = NAND_CMD_READID;
            NFADDR_REG =  0x00;
            NF_TRANSRnB();
    /* wait for a while */
            for (i=0; i<200; i++);


            int factory = NFDATA8_REG;
            id = NFDATA8_REG;

            int cellinfo=NFDATA8_REG;
            int tmp= NFDATA8_REG;

            int childType=tmp & 0x03; //Page size

            if (id > 0x80)
            {
                large_block = 1;
            }
            if(id == 0xd5 && childType==0x01 ) //K9GAG08U0D
            {
                large_block = 2;
            }else if(id == 0xd5 && childType==0x02 ) //K9GAG08U0D
            {
                large_block = 3;
            }

        /* read NAND Block.
        * 128KB ->240KB because of U-Boot size increase. by scsuh
        * So, read 0x3c000 bytes not 0x20000(128KB).
        */
       return nandll_read_blocks(CFG_PHY_UBOOT_BASE, 0x3c000, large_block);

    }

    static int nandll_read_blocks (ulong dst_addr, ulong size, int large_block)
    {
           uchar *buf = (uchar *)dst_addr;
           int i;
           uint page_shift = 9;

          if (large_block==1)
            page_shift = 11;

          if(large_block==2)
              page_shift = 12;

          if(large_block==3)
                    page_shift =13;

          if(large_block == 2) // K9GAG08U0D

           {
                    /* Read pages */
             for (i = 0; i < 4; i++, buf+=(1<<(page_shift-1))) 

              {
                      nandll_read_page(buf, i, large_block);
              }

           /* Read pages */
          for (i = 4; i < (0x3c000>>page_shift); i++, buf+=(1<<page_shift)) 

          {
           nandll_read_page(buf, i, large_block);
         }
          }else if(large_block == 3)  //K9GAG08U0E
            {
                /* Read pages */
                for (i = 0; i < 4; i++, buf+=(1<<(page_shift-2))) 

                {
                        nandll_read_page(buf, i, large_block);
                }
                /* Read pages */
                for (i = 4; i < (0x3c000>>page_shift); i++, buf+=(1<<page_shift)) 

                {
                        nandll_read_page(buf, i, large_block);
                }
            }

        else
          {
                for (i = 0; i < (0x3c000>>page_shift); i++, buf+=(1<<page_shift)) 

                 {
                       nandll_read_page(buf, i, large_block);
                   }
           }
            return 0;
    }

    static int nandll_read_page (uchar *buf, ulong addr, int large_block)
    {
         int i;
         int page_size = 512;

         if (large_block==1)
             page_size = 2048;
         if (large_block==2)
             page_size = 4096;
         if(large_block==3)
             page_size = 8192;

           NAND_ENABLE_CE();
           NFCMD_REG = NAND_CMD_READ0;


            /* Write Address */ //5 个寻址周期,参看下图
            NFADDR_REG = 0;
            if (large_block)
           NFADDR_REG = 0;
           NFADDR_REG = (addr) & 0xff;
           NFADDR_REG = (addr >> 8) & 0xff;
           NFADDR_REG = (addr >> 16) & 0xff;


            if (large_block)
               NFCMD_REG = NAND_CMD_READSTART;

            NF_TRANSRnB(); //等待NandFlash 状态引脚可读。


           /* for compatibility(2460). u32 cannot be used. by scsuh */
           for(i=0; i < page_size; i++) 

            {
                    *buf++ = NFDATA8_REG;
            }

            NAND_DISABLE_CE();
            return 0;
    }

    寻址周期图:

    具体的说明请参考K9GAG08U0E  DataSheet 第9页。

    这里用两片文章写了s3c6410支持K9GAG08U0E 的关键地方,当然还有一些具体的细节了,比如给关键结构体 nand  chip, mtd_device 等赋值Page大小,Block大小,OOB大小的操作,硬件ECC 8bit 纠错等功能,当然这些不是K9GAG08U0E特有的功能,K9GAG08U0D也需要这样的操作,这里就不详细的介绍了。

    希望这两篇文章对需要了解NandFlash如何通过软件工作的朋友有参考价值。


  • 相关阅读:
    第三次作业
    第二次作业
    第一次作业
    软件工程第0次作业
    第四次作业
    第三次作业
    第二次作业
    第一次作业
    第零次作业
    第四次软件工程作业
  • 原文地址:https://www.cnblogs.com/LoongEmbedded/p/5298521.html
Copyright © 2020-2023  润新知