Welcome 微信登录

首页 / 操作系统 / Linux / Nand Flash驱动程序编写

NAND FLASH是一个存储芯片那么: 这样的操作很合理"读地址A的数据,把数据B写到地址A"问1. 原理图上NAND FLASH和S3C2440之间只有数据线,   怎么传输地址?答1.在DATA0~DATA7上既传输数据,又传输地址   当ALE为高电平时传输的是地址,问2. 从NAND FLASH芯片手册可知,要操作NAND FLASH需要先发出命令   怎么传入命令?答2.在DATA0~DATA7上既传输数据,又传输地址,也传输命令   当ALE为高电平时传输的是地址,   当CLE为高电平时传输的是命令   当ALE和CLE都为低电平时传输的是数据问3. 数据线既接到NAND FLASH,也接到NOR FLASH,还接到SDRAM、DM9000等等   那么怎么避免干扰?答3. 这些设备,要访问之必须"选中",   没有选中的芯片不会工作,相当于没接一样问4. 假设烧写NAND FLASH,把命令、地址、数据发给它之后,   NAND FLASH肯定不可能瞬间完成烧写的,   怎么判断烧写完成?答4. 通过状态引脚RnB来判断:它为高电平表示就绪,它为低电平表示正忙问5. 怎么操作NAND FLASH呢?答5. 根据NAND FLASH的芯片手册,一般的过程是:   发出命令   发出地址   发出数据/读数据          NAND FLASH                      S3C2440发命令    选中芯片                            CLE设为高电平                 NFCMMD=命令值              在DATA0~DATA7上输出命令值          发出一个写脉冲            发地址    选中芯片                        NFADDR=地址值          ALE设为高电平          在DATA0~DATA7上输出地址值          发出一个写脉冲发数据    选中芯片                        NFDATA=数据值          ALE,CLE设为低电平          在DATA0~DATA7上输出数据值          发出一个写脉冲读数据    选中芯片                        val=NFDATA          发出读脉冲          读DATA0~DATA7的数据用UBOOT来体验NAND FLASH的操作: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读数据得到device code          val=NFDATA          md.b 0x4E000010 1          0xda退出读ID的状态               NFCMMD=0xff       mw.b 0x4E000008 0xff     2. 读内容: 读0地址的数据使用UBOOT命令:nand dump 0Page 00000000 dump:        17 00 00 ea 14 f0 9f e5  14 f0 9f e5 14 f0 9f e5                             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读数据得到0x17               val=NFDATA          md.b 0x4E000010 1读数据得到0x00               val=NFDATA          md.b 0x4E000010 1读数据得到0x00               val=NFDATA          md.b 0x4E000010 1读数据得到0xea               val=NFDATA          md.b 0x4E000010 1退出读状态                   NFCMMD=0xff       mw.b 0x4E000008 0xffNAND FLASH驱动程序层次看内核启动信息S3C24XX NAND Driver, (c) 2004 Simtec Electronicss3c2440-nand s3c2440-nand: Tacls=3, 30ns Twrph0=7 70ns, Twrph1=3 30nsNAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)Scanning device for bad blocksBad eraseblock 256 at 0x02000000Bad eraseblock 257 at 0x02020000Bad eraseblock 319 at 0x027e0000Bad eraseblock 606 at 0x04bc0000Bad eraseblock 608 at 0x04c00000Creating 4 MTD partitions on "NAND 256MiB 3,3V 8-bit":0x00000000-0x00040000 : "bootloader"0x00040000-0x00060000 : "params"0x00060000-0x00260000 : "kernel"0x00260000-0x10000000 : "root"搜"S3C24XX NAND Driver"S3c2410.c (driversmtd and)s3c2410_nand_inithws3c2410_nand_init_chipnand_scan  // drivers/mtd/nand/nand_base.c 根据nand_chip的底层操作函数识别NAND FLASH,构造mtd_info    nand_scan_ident        nand_set_defaults            if (!chip->select_chip)                chip->select_chip = nand_select_chip; // 默认值不适用            if (chip->cmdfunc == NULL)                chip->cmdfunc = nand_command;                                    chip->cmd_ctrl(mtd, command, ctrl);            if (!chip->read_byte)                chip->read_byte = nand_read_byte;                                    readb(chip->IO_ADDR_R);            if (chip->waitfunc == NULL)                chip->waitfunc = nand_wait;                                    chip->dev_ready                        nand_get_flash_type            chip->select_chip(mtd, 0);            chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);            *maf_id = chip->read_byte(mtd);            dev_id = chip->read_byte(mtd);    nand_scan_tail            mtd->erase = nand_erase;            mtd->read = nand_read;            mtd->write = nand_write;                                    s3c2410_nand_add_partition    add_mtd_partitions        add_mtd_device            list_for_each(this, &mtd_notifiers) { // 问. mtd_notifiers在哪设置                                                  // 答. drivers/mtd/mtdchar.c,mtd_blkdev.c调用register_mtd_user                struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);                not->add(mtd);                // mtd_notify_add  和 blktrans_notify_add                先看字符设备的mtd_notify_add                        class_device_create                        class_device_create                再看块设备的blktrans_notify_add                    list_for_each(this, &blktrans_majors) { // 问. blktrans_majors在哪设置                                                            // 答. driversmtdmdblock.c或mtdblock_ro.c register_mtd_blktrans                        struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);                                     tr->add_mtd(tr, mtd);                                mtdblock_add_mtd (driversmtdmdblock.c)                                    add_mtd_blktrans_dev                                        alloc_disk                                        gd->queue = tr->blkcore_priv->rq; // tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);                                        add_disk           测试4th:1. make menuconfig去掉内核自带的NAND FLASH驱动-> Device Drivers  -> Memory Technology Device (MTD) support    -> NAND Device Support < > NAND Flash support for S3C2410/S3C2440 SoC2. make uImage 使用新内核启动, 并且使用NFS作为根文件系统3. insmod s3c_nand.ko4. 格式化 (参考下面编译工具) flash_eraseall  /dev/mtd3  // yaffs   5. 挂接 mount -t yaffs /dev/mtdblock3 /mnt6. 在/mnt目录下建文件  编译工具:1. tar xjf mtd-utils-05.07.23.tar.bz22. cd mtd-utils-05.07.23/util修改Makefile:#CROSS=arm-linux-改为CROSS=arm-linux-3. make4. cp flash_erase flash_eraseall /work/nfs_root/first_fs/bin/<br><br><br>
 

复制代码
struct nand_chip {
/*8 位NAND 芯片的读写地址*/
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);
/*mark(标记)bad block*/
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;
struct nand_ecc_ctrl ecc;
struct nand_buffers *buffers;
...................
};
struct nand_ecc_ctrl {
nand_ecc_modes_t    mode;
int    steps;
int    size;
int    bytes;
int    total;
int    prepad;
int    postpad;
struct nand_ecclayout    *layout;/*hwctl函数:
*这个函数用来控制硬件产生ecc
*其实它主要的工作就是控制NAND controller 向 NAND 芯片发出NAND_ECC_READ 、NAND_ECC_WRITE 和NAND_ECC_READSYN *等命令,与struct nand_chip 结构体中的cmdfunc 类似
*/
void    (*hwctl)(struct mtd_info *mtd, int mode);/*根据data 计算ecc 值*/
int    (*calculate)(struct mtd_info *mtd,const uint8_t *dat,uint8_t *ecc_code);
/*根据ecc 值,判断读写数据时是否有错误发生,若有错,则立即试着纠正,纠正失败则返回错误*/
int    (*correct)(struct mtd_info *mtd, uint8_t *dat,uint8_t *read_ecc,uint8_t *calc_ecc);
/*read_page_raw write_page_raw函数
*从NAND 芯片中读取一个page 的原始数据和向NAND 芯片写入一个page 的原始数据,所谓的原始数据,即不对读写的数据做ecc处理 *该读写什么值就读写什么值。另外,这两个函数会读写整个page 中的所有内容,即不但会读写一个page 中MAIN部分,还会读写OOB 部分。
*/
int    (*read_page_raw)(struct mtd_info *mtd,struct nand_chip *chip,uint8_t *buf);
void    (*write_page_raw)(struct mtd_info *mtd,struct nand_chip *chip,const uint8_t *buf);/*
*read_page 和write_page 在读写过程中会加入ecc 的计算,校验,和纠正等处理。
*/
int    (*read_page)(struct mtd_info *mtd,struct nand_chip *chip,uint8_t *buf);
void    (*write_page)(struct mtd_info *mtd,struct nand_chip *chip,const uint8_t *buf);/*
*读写oob 中的内容,不包括MAIN 部分。
*/
int    (*read_oob)(struct mtd_info *mtd,struct nand_chip *chip,int page,int sndcmd);
int    (*write_oob)(struct mtd_info *mtd,struct nand_chip *chip,int page);
};
其实,以上提到的这几个read_xxx 和write_xxx 函数,最终都会调用struct nand_chip 中的read_buf 和write_buf 这两个函数,所以如果没有特殊需求的话,我认为不必自己实现,使用MTD 提供的default 的函数即可。--------------------------------------------------------#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/clk.h>#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>#include <asm/io.h>#include <asm/arch/regs-nand.h>
#include <asm/arch/nand.h>struct s3c_nand_regs {
    unsigned long nfconf  ;
    unsigned long nfcont  ;
    unsigned long nfcmd ;
    unsigned long nfaddr  ;
    unsigned long nfdata  ;
    unsigned long nfeccd0 ;
    unsigned long nfeccd1 ;
    unsigned long nfeccd  ;
    unsigned long nfstat  ;
    unsigned long nfestat0;
    unsigned long nfestat1;
    unsigned long nfmecc0 ;
    unsigned long nfmecc1 ;
    unsigned long nfsecc  ;
    unsigned long nfsblk  ;
    unsigned long nfeblk  ;
};static struct nand_chip *s3c_nand_chip;
static struct mtd_info *s3c_mtd;
static struct s3c_nand_regs *s3c_nand_regs;static struct mtd_partition s3c_nand_parts[] = {
    [0] = {
        .name = "bootloader",
        .size = 0x00040000,
        .offset    = 0,
    },
    [1] = {
        .name = "params",
        .offset = MTDPART_OFS_APPEND,
        .size = 0x00020000,
    },
    [2] = {
        .name = "kernel",
        .offset = MTDPART_OFS_APPEND,
        .size = 0x00200000,
    },
    [3] = {
        .name = "root",
        .offset = MTDPART_OFS_APPEND,
        .size = MTDPART_SIZ_FULL,
    }
};
/*判断忙*/
static int s3c_dev_ready(struct mtd_info *mtd)
{
    /*返回"NFSTAT的bit[0]";*/
    return (s3c_nand_regs->nfstat & (1<<0));
}static void s3c_cmd_ctrl(struct mtd_info *mtd, int dat,unsigned int ctrl)
{    if (ctrl & NAND_CLE)
    {
        /* 发命令: NFCMMD=dat */
        s3c_nand_regs->nfcmd = dat;
        //writeb(cmd, host->io_base + (1 << host->board->cle));/*命令*/
    }
    else
    {
        /* 发地址: NFADDR=dat */
        s3c_nand_regs->nfaddr = dat;
        //writeb(cmd, host->io_base + (1 << host->board->ale));/*地址*/
    }
}
static void s3c_select_chip(struct mtd_info *mtd, int chip)
{
    if(chip ==-1)
    {
        /*表示取消选中 NFCONT[1]设为1  */
        s3c_nand_regs->nfcont |=(1<<1);   
    }
    else
    {
        s3c_nand_regs->nfcont &=~(1<<1);
        /*选中芯片 NFCONT[1]设为0 */
    }   
}
static int s3c_nand_init(void)
{    struct clk *clk;
   
    /*1.分配一个nand_chip结构体*/
    s3c_nand_chip =  kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
   
    s3c_nand_regs = ioremap(0x4E000000, sizeof(struct s3c_nand_regs));
   
    /*2.设置nandc_chip结构体*/
    s3c_nand_chip->IO_ADDR_R = &s3c_nand_regs->nfdata;
    s3c_nand_chip->IO_ADDR_W = &s3c_nand_regs->nfdata;
    s3c_nand_chip->cmd_ctrl = s3c_cmd_ctrl;
    s3c_nand_chip->dev_ready = s3c_dev_ready;
    s3c_nand_chip->select_chip = s3c_select_chip;
    s3c_nand_chip->ecc.mode    = NAND_ECC_SOFT;
    //s3c_nand_chip->chip_delay = 20;    /* 使能NAND FLASH控制器的时钟 */
    clk = clk_get(NULL, "nand");
    clk_enable(clk);              /* CLKCON"bit[4] */
       /*初始化nand控制器 设置寄存器*/
#define TACLS 0
#define TWRPH0  3
#define TWRPH1  0
    /* HCLK=100MHz
   * TACLS:  发出CLE/ALE之后多长时间才发出nWE信号, 从NAND手册可知CLE/ALE与nWE可以同时发出,所以TACLS=0
   * TWRPH0: nWE的脉冲宽度, HCLK x ( TWRPH0 + 1 ), 从NAND手册可知它要>=12ns, 所以TWRPH0>=1
   * TWRPH1: nWE变为高电平后多长时间CLE/ALE才能变为低电平, 从NAND手册可知它要>=5ns, 所以TWRPH1>=0
   */
    s3c_nand_regs->nfconf=(TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);    /* NFCONT:
   * BIT1-设为1, 取消片选
   * BIT0-设为1, 使能NAND FLASH控制器
   * BIT4-设为0, 未初始化hardware ECC
   */
    s3c_nand_regs->nfcont = (1<<1) | (1<<0);
   
    /*分配一个mtd_info结构体*/
    s3c_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);    /*设置s3c_mtd结构体*/
    s3c_mtd->owner = THIS_MODULE;
    s3c_mtd->priv = s3c_nand_chip;    /* 识别NAND FLASH, 构造mtd_info */
    nand_scan(s3c_mtd, 1);    /*设置分区表*/
    add_mtd_partitions(s3c_mtd, s3c_nand_parts, 4);
   
    add_mtd_device(s3c_mtd);
    return 0;
}static void s3c_nand_exit(void)
{
    kfree(s3c_mtd);
    kfree(s3c_nand_chip);
    iounmap(s3c_nand_regs);
}module_init(s3c_nand_init);
module_exit(s3c_nand_exit);
MODULE_LICENSE("GPL");本文永久更新链接地址:http://www.linuxidc.com/Linux/2016-12/138152.htm