Chinaunix首页 | 论坛 | 博客
  • 博客访问: 299494
  • 博文数量: 76
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 715
  • 用 户 组: 普通用户
  • 注册时间: 2015-05-20 20:38
文章分类
文章存档

2016年(20)

2015年(56)

分类: 嵌入式

2015-06-09 12:52:07

NAND FLASH驱动程序
15年6月3日10:50:55
首先在u-boot中体验nand flash的读写:
其中u-boot的内置函数md.l   mw.b等可以在u-boot中通过help md或者help mw来查看。
(1)读ID:

                S3C2440            u_boot
选中            NFCONT的bit1设为0     md.l 0x4E000004 1       mw.l 0x4E000004 1
发出命令0x90             NFCMMD = 0x90         mw.b 0x4E000008 0x90
发出地址0x00        NFADDR = 0x00        mw.b 0x4E00000C 0x00
读数据得到0xEC    val = NFDATA        md.b 0x4E000010 1
读数据得到devices code                md.b 0x4E000010 1
退出读ID的状态     NFCMMD = 0xff        mw.b 0x4E000008 0xff

(2)读内容,读0地址的数据:
使用u_boot的命令:nand dump 0:
Page 00000000 dump:                                                             
        01 da a0 e3 09 00 00 eb  0c 00 00 eb 26 00 00 eb

                S3C2440            u_boot
选中            NFCONT的bit1设为0     md.l 0x4E000004 1       mw.l 0x4E000004 1
发出命令0x00             NFCMMD = 0x00         mw.b 0x4E000008 0x00

发出地址0x00        NFADDR = 0x00        mw.b 0x4E00000C 0x00
发出地址0x00        NFADDR = 0x00        mw.b 0x4E00000C 0x00
发出地址0x00        NFADDR = 0x00        mw.b 0x4E00000C 0x00
发出地址0x00        NFADDR = 0x00        mw.b 0x4E00000C 0x00
发出地址0x00        NFADDR = 0x00        mw.b 0x4E00000C 0x00

发出命令0x30             NFCMMD = 0x30         mw.b 0x4E000008 0x30
读数据得到0x01    val = NFDATA        md.b 0x4E000010 1
读数据得到0xda    val = NFDATA        md.b 0x4E000010 1
读数据得到0xa0    val = NFDATA        md.b 0x4E000010 1
读数据得到0xe3    val = NFDATA        md.b 0x4E000010 1
…............
退出读状态     NFCMMD = 0xff        mw.b 0x4E000008 0xff

结果如下图所示:

NAND FLASH的驱动程序如下:
  1
  2 #include
  3 #include
  4 #include
  5 #include
  6 #include
  7 #include
  8 #include
  9 #include
 10 #include
 11 #include
 12 #include
 13 #include
 14 #include
 15 #include
 16 #include
 17 #include
 18 #include
 19 #include
 20
 21 struct s3c_nand_regs {
 22     unsigned long nfconf  ;
 23     unsigned long nfcont  ;
 24     unsigned long nfcmd   ;
 25     unsigned long nfaddr  ;
 26     unsigned long nfdata  ;
 27     unsigned long nfeccd0 ;
 28     unsigned long nfeccd1 ;
 29     unsigned long nfeccd  ;
 30     unsigned long nfstat  ;
 31     unsigned long nfestat0;
 32     unsigned long nfestat1;
 33     unsigned long nfmecc0 ;
 34     unsigned long nfmecc1 ;
 35     unsigned long nfsecc  ;
 36     unsigned long nfsblk  ;
 37     unsigned long nfeblk  ;
 38 };
 39
 40 static struct nand_chip *s3c_nand;
 41 static struct mtd_info *s3c_mtd;
 42 static struct s3c_nand_regs *s3c_nand_regs;
 43
 44 static struct mtd_partition s3c_nand_parts[] = {
 45     [0] = {
 46         .name   = "bootloader",
 47         .size   = 0x00040000,
 48         .offset = 0,
 49     },
 50     [1] = {
 51         .name   = "params",
 52         .offset = MTDPART_OFS_APPEND,
 53         .size   = 0x00020000,
 54     },
 55     [2] = {
 56         .name   = "kernel",
 57         .offset = MTDPART_OFS_APPEND,
 58         .size   = 0x00200000,
 59     },
 60     [3] = {
 61         .name   = "root",
 62         .offset = MTDPART_OFS_APPEND,
 63         .size   = MTDPART_SIZ_FULL,
 64     }
 65 };
 66
 67 static void s3c2440_select_chip(struct mtd_info *mtd, int chip)
 68 {
 69     if (chip == -1)
 70     {
 71         s3c_nand_regs->nfcont |= (1<<1);
 72     }
 73     else
 74     {
 75         s3c_nand_regs->nfcont &= ~(1<<1);
 76     }
 77     
 78 }
 79
 80 static void s3c2440_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
 81 {
 82     if (ctrl & NAND_CLE)
 83     {
 84         s3c_nand_regs->nfcmd = dat;
 85     }
 86     else
 87     {
 88         s3c_nand_regs->nfaddr = dat;
 89     }
 90 }
 91
 92 static int s3c2440_dev_ready(struct mtd_info *mtd)
 93 {
 94     return (s3c_nand_regs->nfstat & (1<<0));
 95 }
 96
 97 static int s3c_nand_init(void)
 98 {
 99     struct clk *clk;
100     
101     /* 1. 分配一个 nand_chip 结构体 */
102     s3c_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
103
104     s3c_nand_regs = ioremap(0x4E000000, sizeof(s3c_nand_regs));
105     
106     /* 2. 设置  */
107     s3c_nand->select_chip = s3c2440_select_chip;
108     s3c_nand->cmd_ctrl    = s3c2440_cmd_ctrl;
109     s3c_nand->IO_ADDR_R   = &s3c_nand_regs->nfdata;
110     s3c_nand->IO_ADDR_W   = &s3c_nand_regs->nfdata;
111     s3c_nand->dev_ready   = s3c2440_dev_ready;
112     s3c_nand->ecc.mode    = NAND_ECC_SOFT;
113
114     /* 3. 硬件相关操作 */
115     /* she zhi zong shi zhong */
116     clk = clk_get(NULL, "nand");
117     clk_enable(clk);
118     
119     /*设置 nand 的时钟参数 */
120     s3c_nand_regs->nfconf = (0<<12) | (1<<8) | (0<<4);
121     s3c_nand_regs->nfcont = (1<<1) | (1<<0);
122     
123     
124     /* 4. 使用 nand_scan */
125     s3c_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
126     s3c_mtd->owner = THIS_MODULE;
127     s3c_mtd->priv  = s3c_nand;
128
129     nand_scan(s3c_mtd, 1);
130
131     /* 5. add_mtd_partitions */
132     add_mtd_partitions(s3c_mtd, s3c_nand_parts, 4);
133
134     return 0;
135 }
136
137 static void s3c_nand_exit(void)
138 {
    del_mtd_partitions(s3c_mtd);    
139     kfree(s3c_mtd);
140     iounmap(s3c_nand_regs);
141     kfree(s3c_nand);
142 }
143
144 module_init(s3c_nand_init);
145 module_exit(s3c_nand_exit);
146
147 MODULE_LICENSE("GPL");
148

(一)在linux系统中提供了MTD系统来建立Flash针对llinux的统一抽象的接口。MTD将文件系统与底层的flash存储器进行隔离,这样,在写nand flash驱动的时候就不用关心flash作为字符设备和块设备与linux内核的接口,直接操作MTD即可。

MTD的数据结构是mtd_info,定义了MTD的数据和操作函数。mtd_info是表示MTD原始设备的结构体,每个分区也被认为是一个mtd_info。例如,有两个MTD原始设备,而每个上面有3个分区,在系统中就有6个mtd_info结构体,这些mtd_info的指针放在名为mtd_table的数组里面。

不过在NOR或者NAND的驱动程序中,几乎看不到mtd_info的成员函数,这时因为MTD的下层实现了针对NOR和NAND的通用mtd_info成员,我们只需要分别关心NOR或者NAND在mtd_info结构体中的私有函数即可。

Flash驱动程序中使用下面两个函数来注册和注销MTD设备:
int add_mtd_device(struct mtd_info *mtd);
int del_mtd_device (struct mtd_info *mtd);

(二)如果不想让Flash分区的话,用上面两个函数注册就行,如果想分区,需要用到下面的函数:
int add_mtd_partitions(struct mtd_info *master, const struct mtd_partition *parts,  int nbparts);
int del_mtd_partitions(struct mtd_info *master);
看第一个注册函数的参数,第一个参数为 mtd_info结构体,第二个参数是一个 mtd_partition结构体,这个结构体显示了想要把Flash分成几个分区,其中每个分区怎么分的信息:
struct mtd_partition {
    char *name;            /* identifier string */
    u_int32_t size;            /* partition size */
    u_int32_t offset;        /* offset within the master MTD space */
    u_int32_t mask_flags;        /* master MTD flags to mask out for this partition */
    struct nand_ecclayout *ecclayout;    /* out of band layout for this partition (NAND only)*/
    struct mtd_info **mtdp;        /* pointer to store the MTD object */
};
在调用注册函数之前,需要将这个结构体初始化了。第三个参数是指分区的个数。

(三)注册了mtd_info结构体以后,就需要使用它, nand_scan( )函数探测nand flash的存在,并去读取NAND芯片的厂商,ID等信息,函数如下所示:
int nand_scan(struct mtd_info *mtd, int maxchips);


(四)上面提到了MTD的下层实现了通用的NAND的驱动,我们需要关心的就是NAND的私有函数,这个私有的主体就是nand_chip结构体。这个结构体如下所示:
struct nand_chip {
    void  __iomem    *IO_ADDR_R;
    void  __iomem    *IO_ADDR_W;

    uint8_t        (*read_byte)(struct mtd_info *mtd);
    u16        (*read_word)(struct mtd_info *mtd);
    void        (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
    void        (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
    int        (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
    void        (*select_chip)(struct mtd_info *mtd, int chip);
    int        (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
    int        (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
    void        (*cmd_ctrl)(struct mtd_info *mtd, int dat,
                    unsigned int ctrl);
    int        (*dev_ready)(struct mtd_info *mtd);
    void        (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
    int        (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
    void        (*erase_cmd)(struct mtd_info *mtd, int page);
    int        (*scan_bbt)(struct mtd_info *mtd);
    int        (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
    int        (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
                      const uint8_t *buf, int page, int cached, int raw);

    int        chip_delay;
    unsigned int    options;

    int        page_shift;
    int        phys_erase_shift;
    int        bbt_erase_shift;
    int        chip_shift;
    int        numchips;
    unsigned long    chipsize;
    int        pagemask;
    int        pagebuf;
    int        subpagesize;
    uint8_t        cellinfo;
    int        badblockpos;

    nand_state_t    state;

    uint8_t        *oob_poi;
    struct nand_hw_control  *controller;
    struct nand_ecclayout    *ecclayout;

    struct nand_ecc_ctrl ecc;
    struct nand_buffers *buffers;
    struct nand_hw_control hwcontrol;

    struct mtd_oob_ops ops;

    uint8_t        *bbt;
    struct nand_bbt_descr    *bbt_td;
    struct nand_bbt_descr    *bbt_md;

    struct nand_bbt_descr    *badblock_pattern;

    void        *priv;
};

在写NAND驱动程序的时候,首先需要动态分配这个nand_chip结构体,然后分别初始化设置这个结构体里面我们需要用到的数据或函数。

(五)下面总结写NAND FLASH驱动的步骤:
(1)定义一个nand_chip结构体,并动态分配。
(2)设置nand_chip结构体中一些相关的函数。
(3)硬件相关的操作,包括设置总的nand时钟,设置一些寄存器的参数等。
(3)定义mtd_info结构体,并且将它的priv 函数指向我们刚才注册的nand_chip结构体:
    s3c_mtd->priv  = s3c_nand;
    并且调用nand_scan函数,来探测nand flash的存在,并读取NAND芯片的厂商,ID等信息。
(4)如果要分区,就先定义mtd_partition数组,将分区信息记录在里面,然后调用add_mtd_partitions()函数来分区。
(5)在出口函数中做相反的事情。

测试:需要从新配置内核,去掉MTD中的NAND驱动。
Make menuconfig
        Device Drivers  --->
        <*> Memory Technology Device (MTD) support  --->
             <*>   NAND Device Support  --->
                 < >   NAND Flash support for S3C2410/S3C2440 SoC
然后从新“make uImage”,用新内核启动以后测试:

可以看到,没有mtd设备:

装载驱动以后,可以看到有分区信息输出:


这时候,可以看到,设备节点都已经建立起来了:

用分区工具格式化:


格式化以后挂载:

阅读(1582) | 评论(0) | 转发(0) |
0

上一篇:块设备驱动程序

下一篇:NOR FLASH驱动程序

给主人留下些什么吧!~~