Chinaunix首页 | 论坛 | 博客
  • 博客访问: 394989
  • 博文数量: 138
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1620
  • 用 户 组: 普通用户
  • 注册时间: 2013-03-10 16:55
个人简介

当你比别人优秀一点点,别人会嫉妒你。当你比别人优秀很多,别人会羡慕你。

文章分类

全部博文(138)

文章存档

2016年(2)

2015年(2)

2014年(15)

2013年(119)

我的朋友

分类: LINUX

2013-05-02 06:49:45

内容:
        1, 裸机 nandflash
        2,ubootnandflash
        3, linux nandflash

QQ2440V3  S3C2440

========================================================================================

   第一层, 裸机nandflash
    
    nandflash 组织结构是  芯片,块 ,页, 
    一个页内有 512 +16 B 也就是   528×8bit

    页内地址,首先通过三个命令分别访问三个区, A B C
    前面512个字节的数据需要用9位来访问。我们使用 第九位来决定访问A 还是B但是第九位不是通过地址线来赋值的,而是不同的命令赋予不同的值。    
    命令00 赋予第八位地址为0, 命令01赋予第八位地址为1. 这样就可以访问一个页的上半页,下半页。0-7位来访问半页内的数据。
    同时命令50来访问C区。 C区只有16B 所以地址部分只使用了0-3位。

    页的上一个单位是块,一个块含有32个页,所以块内地址应该是5。

    我们当前操作的芯片有4096块,我们姑且把这个叫做芯片的范畴吧,那么芯片内块地址就是12位。

    好了,访问地址解释清楚了。
---------------------------------------------------------------------------------------------------------------------------------
    说说s3c2440 的NAND  flash控制器:

    一共有这么几个寄存器可以用 (2440 )
    NFCONF
                这个寄存器,主要是用于配置时序,等等,在芯片初始化的时候,一次行配置好就不用惯了。
    NFCONT 
                这个寄存器,主要是用于使能/ 禁能我们的flash芯片。 这里要有一个意识: 操作flash芯片的流程是这样的: 使能芯片,操作芯片,禁能芯片。这个要有一个意识。
                 还有就是,韦东山书上一直说这个是选择芯片,窃以为说使能比说选择来的容易理解,但是linux里面也是使用了select这个名词。是不是因为我们很多时候flash芯片不止一种,那么我们选择哪个芯片就把芯片对应的这一位使能,所以我们说这一位是选择芯片的位。到底什么原因,且不深究,回头再说。知道意思即可。

    NFCMD
                命令寄存器,往这个地址写命令就会发送到flash芯片上去。
                主要命令有这么几个:
                读命令:前面介绍了,有三种读命令。
                reset命令:芯片初始化完成后,我们往往要reset下芯片,他会终止当前芯片正在进行的所有,读,写,擦出等命令。
    NFADDR
                地址寄存器。我们写一个地址的时候,地址是24位的,这个寄存器只有8位,所以我们要分次写。0-7 一次。 8位前面提到了通过命令来设置。 9-16 17-24  25-32(只有25有效)

    NFDATA
                数据寄存器,我们写数据的时候,就8位为单位,不断往里面写就行。 读数据的时候,一直读,读够数就OK。

    NFSTAT
                状态寄存器,使用他的第0位,我们可以判断前面的命令执行完成了没有,目前的方法是死循环检查。死循环检查???效率不低???看看linux如何实现了,按道理该用中断哈

---------------------------------------------------------------------------------------------------------------------------------
命令序列

        重置flash芯片:
                NFCONT &=~(1<<1)                       使能/选择芯片    
                NFCMD = 0xff                                        发送重置命令
                while (NFSTAT 第0位为1)                 说明重置命令执行完毕了
                NFCONT |= 0x2                                        禁能flash

        
         读数据
            
             NFCONT &=~(1<<1)                       使能/选择芯片    
             NFCMD = 0                                            发送读命令

              写入地址:
              NFADDR = addr & 0x ff                        把地址的0-7位写入地址寄存器
              NFADDR = (addr>> 9 )                        把地址的8-16位写入地址寄存器
              NFADDR = (addr>> 17 )                        把地址的17-24位写入地址寄存器
              NFADDR = (addr>> 25 )                        把地址的25-31位写入地址寄存器

              while (NFSTAT 第0位为1)                 说明写地址命令执行完毕了

            
              连续读取寄存器NFDATA512次,因为我们读取是按照页为单位的。
        
               NFCONT |= (1<<1)         


            其他的命令序列,可以参考韦东山那本书上的章节。都是差不多的东西。


           
    操作nandflash的方法就是这样的:使能芯片--》   发送命令 ---》  发送地址-----》读/写 数据------》禁能芯片

   
==================================================================================
第一次升华:把对寄存器的直接引用转换为结构体的方式使用



       我们看uboot的代码可以分析的出,以前曾经提过 http://blog.chinaunix.net/uid-28708203-id-3570133.html

        那里讲到了头文件的作用,我们这里再叙述一遍。
    
        我们在实际的工程中,不会直接使用一个寄存器地址,而是会把寄存器的地址以 结构体加 函数的方式来使用,比如说这里我们的nandflash,他的结构体就这么来管理:
        结构体在 include/s3c24x0.h中添加(结构提结构来自s3c2440 芯片手册的nandflash部分):

点击(此处)折叠或打开

  1. /* NAND FLASH (see S3C2440 manual) */
  2. typedef struct {
  3. S3C24X0_REG32    NFCONF;
  4. S3C24X0_REG32    NFCONT;
  5. S3C24X0_REG32    NFCMD;
  6. S3C24X0_REG32    NFADDR;
  7. S3C24X0_REG32    NFDATA;
  8. S3C24X0_REG32    NFMECCD0;
  9. S3C24X0_REG32    NFMECCD1;
  10. S3C24X0_REG32    NFSECCD;
  11. S3C24X0_REG32    NFSTAT;
  12. S3C24X0_REG32    NFESTAT0;
  13. S3C24X0_REG32    NFESTAT1;
  14. S3C24X0_REG32    NFMECC0;
  15. S3C24X0_REG32    NFMECC1;
  16. S3C24X0_REG32    NFSECC;
  17. S3C24X0_REG32    NFSBLK;
  18. S3C24X0_REG32    NFEBLK
  19. } /*__attribute__((__packed__))*/ S3C2440_NAND;

        include/config/s3c2440.h 中添加函数
点击(此处)折叠或打开
  1. #define S3C2410_NAND_BASE     0x4E000000 来自s3c2440 芯片手册的nandflash部分。

  2. static inline S3C2440_NAND * const S3C2440_GetBase_NAND(void)
  3.     {
  4.         return (S3C2440_NAND * const)S3C2440_NAND_BASE;
  5.     }
------------------------------------------------------------------------------------------------------------------------------


第一次升华 函数的使用。

上面的改造是把寄存器的使用,从直接使用寄存器的地址转变为使用结构体的方式。

下面要改造命令序列,把他们改造成为函数。

        选择/使能    芯片函数,chip为-1的时候禁能,其余的时候,使能芯片。    

点击(此处)折叠或打开

  1. static void s3c2440_nand_select_chip( int chip)
  2. {
  3.     S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
  4.  
  5.     if (chip == -1) {
  6.         s3c2440nand->NFCONT |= S3C2440_NFCONT_nFCE;
  7.     } else {
  8.         s3c2440nand->NFCONT &= ~S3C2440_NFCONT_nFCE;
  9.     }
  10. }

        查询NFSTAT寄存器第0位,以判断上一次命令是否执行完毕,的函数。
    

点击(此处)折叠或打开

  1. static int s3c2440_nand_devready()
  2. {
  3.     S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
  4.  
  5.     return (s3c2440nand->NFSTAT & S3C2440_NFSTAT_READY);
  6. }

        其余读,写等等的操作,也都可以转换为函数。

==================================================================================

    下面谈谈uboot中nandflash的工作系统。
      从上面我们可以的出,给uboot添加nandflash支持需要做这么几个操作。

      1,添加相应的结构体和那个函数,以使用nandflash的寄存器。
      2,构造各个函数,完成操作nandflash的各个操作。同时我们不能直接使用这些个函数,还需要一个结构提,这个结构体的成员都是函数指针,只想刚刚提到的函数们,把结构体成员(函数指针)指向函数的过程可以理解为软件的初始化。
      3,同时,一直没有提到的是,nandflash初始化的时候,需要配置时序。这个需要一个额外的函数,这个是真正的硬件初始。
    
    ---------------------------------------------------------------------------------------------------------------------------------
    
    看看uboot的flash初始化流程:
    lib_arm/board.c  : void start_armboot (void)   ---> 
    
    drivers/nand/nand.c:  void nand_init(void)     --->

    ---------同上--------------:  static void nand_init_chip(     ----->

    cpu/arm920t/s3c24x0/nand.c: board_nand_init(

    board_nand_init 这个函数就是我们要自己实现的。前面三个函数都是uboot管理nandflash的架构函数。下面分析下下。

    第一个 地方不用分析了,就是系统启动的时候挨个调用初始化函数。

点击(此处)折叠或打开

  1. void nand_init(void)
  2. {
  3.         int i;
  4.         unsigned int size = 0;
  5.         for (i = 0; i < CFG_MAX_NAND_DEVICE; i++) { //这个地方是遍历初始化所有的nandflash芯片
  6.                 nand_init_chip(&nand_info[i], &nand_chip[i], base_address[i]);
  7.                 size += nand_info[i].size;
  8.                 if (nand_curr_device == -1)
  9.                         nand_curr_device = i;
  10.         }
  11.         printf("%lu MiB\n", size / (1024 * 1024));

  12. #ifdef CFG_NAND_SELECT_DEVICE
  13.         /*
  14.          * Select the chip in the board/cpu specific driver
  15.          */
  16.         board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device);
  17. #endif
  18. }

        这里这个结构体nand_info的结构定义是这样的,uboot借鉴了linux管理nandflash对象的方式:

        使用一个结构体struct mtd_info 来表示一个nandflash设备。就我目前的分析来看,真正起作用的是mtd->priv 这个成员变量。考虑到以前的看内核的经验,这个成员变量就是private的那部分。
        从下面代码中我们可以看到,我们为这个成员变量赋值了这么个结构 struct nand_chip 。
        

点击(此处)折叠或打开

  1. static void nand_init_chip(struct mtd_info *mtd, struct nand_chip *nand,
  2.              ulong base_addr)
  3. {
  4.     mtd->priv = nand;

  5.     nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr;
  6.     board_nand_init(nand);

  7.     if (nand_scan(mtd, 1) == 0) {
  8.         if (!mtd->name)
  9.             mtd->name = (char *)default_nand_name;
  10.     } else
  11.         mtd->name = NULL;

  12. }
      然后以 struct nand_chip为参数,调用我们自己的nand初始化函数。这个函数说白了,就是定义一个struct nand_chip,然后关联到mtd这个对象上去,然后调用我们自己的函数,来初始化函数。

      看看这个定义在include/linux/mtd/nand.h中的这个结构体:

点击(此处)折叠或打开

  1. struct nand_chip {
  2.     void __iomem    *IO_ADDR_R;
  3.     void __iomem    *IO_ADDR_W;

  4.     u_char        (*read_byte)(struct mtd_info *mtd);
  5.     void        (*write_byte)(struct mtd_info *mtd, u_char byte);
  6.     u16        (*read_word)(struct mtd_info *mtd);
  7.     void        (*write_word)(struct mtd_info *mtd, u16 word);

  8.     void        (*write_buf)(struct mtd_info *mtd, const u_char *buf, int len);
  9.     void        (*read_buf)(struct mtd_info *mtd, u_char *buf, int len);
  10.     int        (*verify_buf)(struct mtd_info *mtd, const u_char *buf, int len);
  11.     void        (*select_chip)(struct mtd_info *mtd, int chip);
  12.     int        (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
  13.     int        (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
  14.     void        (*hwcontrol)(struct mtd_info *mtd, int cmd);
  15.     int        (*dev_ready)(struct mtd_info *mtd);
  16.     void        (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
  17.     int        (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state);
  18.     int        (*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
  19.     int        (*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
  20.     void        (*enable_hwecc)(struct mtd_info *mtd, int mode);
  21.     void        (*erase_cmd)(struct mtd_info *mtd, int page);
  22.     int        (*scan_bbt)(struct mtd_info *mtd);
  23.     int        eccmode;
  24.     int        eccsize;
  25.     int        eccbytes;
  26.     int        eccsteps;
  27.     int        chip_delay;
  28. #if 0
  29.     spinlock_t    chip_lock;
  30.     wait_queue_head_t wq;
  31.     nand_state_t    state;
  32. #endif
  33.     int        page_shift;
  34.     int        phys_erase_shift;
  35.     int        bbt_erase_shift;
  36.     int        chip_shift;
  37.     u_char        *data_buf;
  38.     u_char        *oob_buf;
  39.     int        oobdirty;
  40.     u_char        *data_poi;
  41.     unsigned int    options;
  42.     int        badblockpos;
  43.     int        numchips;
  44.     unsigned long    chipsize;
  45.     int        pagemask;
  46.     int        pagebuf;
  47.     struct nand_oobinfo    *autooob;
  48.     uint8_t        *bbt;
  49.     struct nand_bbt_descr    *bbt_td;
  50.     struct nand_bbt_descr    *bbt_md;
  51.     struct nand_bbt_descr    *badblock_pattern;
  52.     struct nand_hw_control    *controller;
  53.     void        *priv;
  54. }

    丫的这么长,肯定 又是从linux那边抄过来的。

    说白了,就是把上面我们提到的那些个函数,赋值到这个结构体中的成员变量中去。

    我们这个初始化函数是这么写的:
    

点击(此处)折叠或打开

  1. void board_nand_init(struct nand_chip *chip)
  2. {
  3.     S3C2410_NAND * const s3c2410nand = S3C2410_GetBase_NAND();
  4.     S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
  5.  
  6.     s3c24x0_nand_inithw();
  7.  
  8.     if (gd->bd->bi_arch_number == MACH_TYPE_SMDK2410) {
  9.         chip->IO_ADDR_R = (void *)&s3c2410nand->NFDATA;
  10.         chip->IO_ADDR_W = (void *)&s3c2410nand->NFDATA;
  11.         chip->hwcontrol = s3c2410_nand_hwcontrol;
  12.         chip->dev_ready = s3c2410_nand_devready;
  13.         chip->select_chip = s3c2410_nand_select_chip;
  14.         chip->options = 0;
  15.     } else {
  16.         chip->IO_ADDR_R = (void *)&s3c2440nand->NFDATA;
  17.         chip->IO_ADDR_W = (void *)&s3c2440nand->NFDATA;
  18.         chip->hwcontrol = s3c2440_nand_hwcontrol;
  19.         chip->dev_ready = s3c2440_nand_devready;
  20.         chip->select_chip = s3c2440_nand_select_chip;
  21.         chip->options = 0;
  22.     }
  23.  
  24.     chip->eccmode = NAND_ECC_SOFT;
  25. }
    这个函数,首先执行的是前面提到的硬件初始化函数,调用 s3c24x0_nand_inithw(); 来进行时序等的配置。

    然后把读,写,重置,等待等等的函数,赋值到 struct nand_chip 这个结构体的成员变量中去。至于这些个函数怎么实现,大多是参考了linux的代码。其实仔细看看也不难理解,

    挨个分析一下下:
        chip->IO_ADDR_R    = (void *)&s3c2440nand->NFDATA;
        这个成员变量是为了提供我们读外设存储的地址。我们读取外设的内容的时候,肯定是从NFDATA中读取。

        chip->IO_ADDR_W    = (void *)&s3c2440nand->NFDATA;
         我们往外设的存储空间写内容的时候,有这么几种情况,从功能流程上我们就可以看出,首先是写命令,写入命令寄存器。然后是写地址,写入地址寄存器,最后是写数据,写入数据寄存器。所以这个成员变量可以取得值有三个。分别是命令寄存器NFCMD ,地址寄存器 NFADDR, 数据寄存器 NFDATA

        chip->hwcontrol    = s3c2440_nand_hwcontrol;
        这个函数稍微有点特殊,他是配合上面那个成员变量IO_ADDR_W来配合使用的。
        上面提到我们写外设的时候有三种情况,可以分别实现三个函数,写命令函数,写地址函数,写数据函数。如果我们想想这三个函数的共同点,都是有一个“写”的操作,他们的区别是,写的地方不同,那么我们就可以把这个相同点抽象出来。
        本来是三个函数:
        cmd_write()
        address_wirte()
        data_write()

        现在变为两个函数:

        s3c2440_nand_hwcontrol
        {
            switch (写入功能)
            case 写入命令 :chip->IO_ADDR_W = NFCMD

            case 写入地址: chip->IO_ADDR_W = NFADDR
            case 写入数据 :chip->IO_ADDR_W
NFDATA
            
        }    

----------------------------------------------------------------------------------

        output_write()
        {
            
                    *(chip->IO_ADDR_W) =数据内容(命令/地址/数据)

        }



        


        chip->dev_ready    = s3c2440_nand_devready;
        这个成员是查询NFSTAT这个结构体的0位,查看上一次的执行结果有没有结束。
        chip->select_chip  = s3c2440_nand_select_chip;
        这个函数就是为了使能/禁能flash芯片。
        chip->options      = 0;
       

---------------------------------------------------------------------------------------------------------------------------------
现在解释完了,我们只需要把这些个函数的实现放到文件中,编译到uboot中(修改makefile吗)即可。
cpu/arm920t/s3c24x0/nand_flash.c
 

文件内容分析见:http://blog.chinaunix.net/uid-28708203-id-3641000.html

同时我们还需要修改头文件的几个内容:


include/configs/s3c2440.h

添加nandflash命令

点击(此处)折叠或打开

  1. #define CONFIG_COMMANDS \
  2.                         ((CONFIG_CMD_DFL | \
  3.                         CFG_CMD_CACHE | \
  4.                          CFG_CMD_PING | \
  5.                         CFG_CMD_JFFS2 | \
  6.                         CFG_CMD_NAND | \
  7. …………


前面代码分析的时候看到了,我们遍历所有的flash芯片,一一初始化,这里我们定义下设备,芯片的个数。

点击(此处)折叠或打开

  1. #define CFG_NAND_BASE 0
  2. #define CFG_MAX_NAND_DEVICE 1
  3. #define NAND_MAX_CHIPS 1 
1,3 还没找到哪里用了。



阅读(550) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~