MTD系统架构和yaffs2使用、Nandflash驱动设计

一、MTD系统架构

1.MTD设备体验

FLASH在嵌入式系统中是必不可少的,它是bootloader、linux内核和文件系统的最佳载体。

在Linux内核中引入了MTD子系统为NORFLASH和NAND FLASH设备提供统一的接口,从而使得FLASH驱动的设计大为简化。

cat /proc/mtd

每个分区对应一个块设备

ls -l /dev/mtd*

crw-rw—- 1 0 0 90, 0 Jan 1 00:00 /dev/mtd0

crw-rw—- 1 0 0 90, 1 Jan 1 00:00 /dev/mtd0ro

crw-rw—- 1 0 0 90, 2 Jan 1 00:00 /dev/mtd1

crw-rw—- 1 0 0 90, 3 Jan 1 00:00 /dev/mtd1ro

crw-rw—- 1 0 0 90, 4 Jan 1 00:00 /dev/mtd2

crw-rw—- 1 0 0 90, 5 Jan 1 00:00 /dev/mtd2ro

brw-rw—- 1 0 0 31, 0 Jan 1 00:00 /dev/mtdblock0

brw-rw—- 1 0 0 31, 1 Jan 1 00:00 /dev/mtdblock1

brw-rw—- 1 0 0 31, 2 Jan 1 00:00 /dev/mtdblock2

2.块设备驱动系统架构

二、YAFFS2文件系统应用

1.MTD分区设置
配置linux内核支持mtd,找到mtd接口文件,设置空间大小。
2.Yaffs2文件系统制作
将rootfs格式化生成yaffs文件系统。

/home/win/mkyaffs2image ./rootfs/ rootfs.img

3.Uboot参数设置
在uboot_tq2440includeconfigsTQ2440.h中有uboot的启动配置选项

#define CONFIG_BZIP2

#define CONFIG_LZO

#define CONFIG_LZMA

#define CONFIG_CMD_NAND_YAFFS

#define CONFIG_BOOTARGS “console=ttySAC0 root=/dev/mtdblock3”

#define CONFIG_BOOTCOMMAND “nand read 0x30000000 kernel;bootm 0x30000000”

4.下载烧写与启动

在uboot中用dnw下载

三、Nandflash驱动设计
s3c2410.c/s3c24xx_nand_probe:

static int s3c24xx_nand_probestruct platform_device *pdev,

             enum s3c_cpu_type cpu_type)

{

    struct s3c2410_platform_nand *plat = to_nand_platpdev);

    struct s3c2410_nand_info *info;

    struct s3c2410_nand_mtd *nmtd;

    struct s3c2410_nand_set *sets;

    struct resource *res;

    int err = 0;

    int size;

    int nr_sets;

    int setno;

    pr_debug“s3c2410_nand_probe%p)
, pdev);

    info = kmallocsizeof*info), GFP_KERNEL);

    if info == NULL) {

        dev_err&pdev>dev, “no memory for flash info
);

        err = ENOMEM;

        goto exit_error;

    }

    memsetinfo, 0, sizeof*info));

    platform_set_drvdatapdev, info);

    spin_lock_init&info>controller.lock);

    init_waitqueue_head&info>controller.wq);

    /* get the clock source and enable it */

    info>clk = clk_get&pdev>dev, “nand”);                                  //获取时钟,并使能

    if IS_ERRinfo>clk)) {

        dev_err&pdev>dev, “failed to get clock
);

        err = ENOENT;

        goto exit_error;

    }

    clk_enableinfo>clk);

    /* allocate and map the resource */

    /* currently we assume we have the one resource */

    res = pdev>resource;

    size = res>end res>start + 1;

    info>area = request_mem_regionres>start, size, pdev>name);                         //地址转换

    if info>area == NULL) {

        dev_err&pdev>dev, “cannot reserve register region
);

        err = ENOENT;

        goto exit_error;

    }

    info>device = &pdev>dev;

    info>platform = plat;

    info>regs = ioremapres>start, size);

    info>cpu_type = cpu_type;

    if info>regs == NULL) {

        dev_err&pdev>dev, “cannot reserve register region
);

        err = EIO;

        goto exit_error;

    }

    dev_dbg&pdev>dev, “mapped registers at %p
, info>regs);

    /* initialise the hardware */

    err = s3c2410_nand_inithwinfo);                                                        //初始化硬件

    if err != 0)

        goto exit_error;

    sets = plat != NULL) ? plat>sets : NULL;

    nr_sets = plat != NULL) ? plat>nr_sets : 1;

    info>mtd_count = nr_sets;

    /* allocate our information */

    size = nr_sets * sizeof*info>mtds);

    info>mtds = kmallocsize, GFP_KERNEL);

    if info>mtds == NULL) {

        dev_err&pdev>dev, “failed to allocate mtd storage
);

        err = ENOMEM;

        goto exit_error;

    }

    memsetinfo>mtds, 0, size);

    /* initialise all possible chips */

    nmtd = info>mtds;

    for setno = 0; setno < nr_sets; setno++, nmtd++) {

        pr_debug“initialising set %d %p, info %p)
, setno, nmtd, info);

        s3c2410_nand_init_chipinfo, nmtd, sets);                                               //里面有校验nandflash

        nmtd>scan_res = nand_scan_ident&nmtd>mtd,                                            //搜索nandflash

                         sets) ? sets>nr_chips : 1);

        if nmtd>scan_res == 0) {

            s3c2410_nand_update_chipinfo, nmtd);

            nand_scan_tail&nmtd>mtd);

            s3c2410_nand_add_partitioninfo, nmtd, sets);                                        //注册分区信息

        }

        if sets != NULL)

            sets++;

    }

    err = s3c2410_nand_cpufreq_registerinfo);

    if err < 0) {

        dev_err&pdev>dev, “failed to init cpufreq support
);

        goto exit_error;

    }

    if allow_clk_stopinfo)) {

        dev_info&pdev>dev, “clock idle support enabled
);

        clk_disableinfo>clk);

    }

    pr_debug“initialised ok
);

    return 0;

 exit_error:

    s3c2410_nand_removepdev);

    if err == 0)

        err = EINVAL;

    return err;

}

MTD通用驱动部分nand_base.c(nand_read:

static int nand_readstruct mtd_info *mtd, loff_t from, size_t len,

         size_t *retlen, uint8_t *buf)

{

    struct nand_chip *chip = mtd>priv;

    int ret;

    /* Do not allow reads past end of device */

    if from + len) > mtd>size)

        return EINVAL;

    if !len)

        return 0;

    nand_get_devicechip, mtd, FL_READING);

    chip>ops.len = len;

    chip>ops.datbuf = buf;

    chip>ops.oobbuf = NULL;

    ret = nand_do_read_opsmtd, from, &chip>ops);                                       //进行读操作的代码

    *retlen = chip>ops.retlen;

    nand_release_devicemtd);

    return ret;

}

nand_do_read_ops:

/**

 * nand_do_read_ops [Internal] Read data with ECC

 *

 * @mtd:    MTD device structure

 * @from:    offset to read from

 * @ops:    oob ops structure

 *

 * Internal function. Called with chip held.

 */

static int nand_do_read_opsstruct mtd_info *mtd, loff_t from,

             struct mtd_oob_ops *ops)

{

    int chipnr, page, realpage, col, bytes, aligned;

    struct nand_chip *chip = mtd>priv;

    struct mtd_ecc_stats stats;

    int blkcheck = 1 << chip>phys_erase_shift chip>page_shift)) 1;

    int sndcmd = 1;

    int ret = 0;

    uint32_t readlen = ops>len;

    uint32_t oobreadlen = ops>ooblen;

    uint8_t *bufpoi, *oob, *buf;

    stats = mtd>ecc_stats;

    chipnr = int)from >> chip>chip_shift);

    chip>select_chipmtd, chipnr);

    realpage = int)from >> chip>page_shift);

    page = realpage & chip>pagemask;

    col = int)from & mtd>writesize 1));

    buf = ops>datbuf;

    oob = ops>oobbuf;

    while1) {

        bytes = minmtd>writesize col, readlen);

        aligned = bytes == mtd>writesize);

        /* Is the current page in the buffer ? */

        if realpage != chip>pagebuf || oob) {

            bufpoi = aligned ? buf : chip>buffers>databuf;

            if likelysndcmd)) {

                chip>cmdfuncmtd, NAND_CMD_READ0, 0x00, page);                                              //实际对应了nand_command_lp,cmd命令是0

                sndcmd = 0;

            }

        ………

}

nand_command_lp:

static void nand_command_lpstruct mtd_info *mtd, unsigned int command,

             int column, int page_addr)

{

    register struct nand_chip *chip = mtd>priv;

    /* Emulate NAND_CMD_READOOB */

    if command == NAND_CMD_READOOB) {

        column += mtd>writesize;

        command = NAND_CMD_READ0;

    }

    /* Command latch cycle */

    chip>cmd_ctrlmtd, command & 0xff,

         NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);                                   //cmd_ctrl来源于底层驱动,在s3c2410_nand_init_chip中赋值了。

    …….

}

s3c2410_nand_hwcontrol:

/* s3c2410_nand_hwcontrol

 *

 * Issue command and address cycles to the chip

*/

static void s3c2410_nand_hwcontrolstruct mtd_info *mtd, int cmd,

                 unsigned int ctrl)

{

    struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfomtd);

    if cmd == NAND_CMD_NONE)

        return;

    if ctrl & NAND_CLE)

        writebcmd, info>regs + S3C2410_NFCMD);                                     //往NFCONT寄存器中写入cmd,cmd来自于nand_command,往上回溯为nand_read.其实就是发送了命令0x00

    else

        writebcmd, info>regs + S3C2410_NFADDR);

}

继续回到nand_command_lp:

        ……………

        if column != 1 || page_addr != 1) {

        int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;

        /* Serially input address */

        if column != 1) {

            /* Adjust columns for 16 bit buswidth */

            if chip>options & NAND_BUSWIDTH_16)

                column >>= 1;

            chip>cmd_ctrlmtd, column, ctrl);                                   //紧接着发送列地址

            ctrl &= ~NAND_CTRL_CHANGE;

            chip>cmd_ctrlmtd, column >> 8, ctrl);

        }

        if page_addr != 1) {

            chip>cmd_ctrlmtd, page_addr, ctrl);                                //发送行地址

            chip>cmd_ctrlmtd, page_addr >> 8,

                 NAND_NCE | NAND_ALE);

            /* One more address cycle for devices > 128MiB */

            if chip>chipsize > 128 << 20))

                chip>cmd_ctrlmtd, page_addr >> 16,

                     NAND_NCE | NAND_ALE);

        }

    }

    chip>cmd_ctrlmtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);

    /*

     * program and erase have their own busy handlers

     * status, sequential in, and deplete1 need no delay

     */

    switch command) {

    case NAND_CMD_CACHEDPROG:

    case NAND_CMD_PAGEPROG:

    case NAND_CMD_ERASE1:

    case NAND_CMD_ERASE2:

    case NAND_CMD_SEQIN:

    case NAND_CMD_RNDIN:

    case NAND_CMD_STATUS:

    case NAND_CMD_DEPLETE1:

        return;

        /*

         * read error status commands require only a short delay

         */

    case NAND_CMD_STATUS_ERROR:

    case NAND_CMD_STATUS_ERROR0:

    case NAND_CMD_STATUS_ERROR1:

    case NAND_CMD_STATUS_ERROR2:

    case NAND_CMD_STATUS_ERROR3:

        udelaychip>chip_delay);

        return;

    case NAND_CMD_RESET:

        if chip>dev_ready)

            break;

        udelaychip>chip_delay);

        chip>cmd_ctrlmtd, NAND_CMD_STATUS,

             NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);

        chip>cmd_ctrlmtd, NAND_CMD_NONE,

             NAND_NCE | NAND_CTRL_CHANGE);

        while !chip>read_bytemtd) & NAND_STATUS_READY)) ;

        return;

    case NAND_CMD_RNDOUT:

        /* No ready / busy check necessary */

        chip>cmd_ctrlmtd, NAND_CMD_RNDOUTSTART,

             NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);

        chip>cmd_ctrlmtd, NAND_CMD_NONE,

             NAND_NCE | NAND_CTRL_CHANGE);

        return;

    case NAND_CMD_READ0:

        chip>cmd_ctrlmtd, NAND_CMD_READSTART,                                                    //这里发送了0x30命令

             NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);

        chip>cmd_ctrlmtd, NAND_CMD_NONE,

             NAND_NCE | NAND_CTRL_CHANGE);

        /* This applies to read commands */

    default:

        /*

         * If we dont have access to the busy pin, we apply the given

         * command delay

         */

        if !chip>dev_ready) {

            udelaychip>chip_delay);

            return;

        }

    }

    /* Apply this short delay always to ensure that we do wait tWB in

     * any case on any machine. */

    ndelay100);

    nand_wait_readymtd);                                                                          //wait等待

}

无欲速,无见小利。欲速,则不达;见小利,则大事不成。

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注