• 嵌入式Linux驱动学习之路(二十三)NAND FLASH驱动程序


    NAND FLASH是一个存储芯片。

    在芯片上的DATA0~DATA7上既能传输数据也能传输地址。

      当ALE为高电平时传输的是地址。

      当CLE为高电平时传输的是命令。

      当ALE和CLE都为低电平时传输的是数据。

    将数据发给nand Flash后,在发送第二次数据之前还要判断芯片是否处于空闲状态。一般是通过引脚RnB来判断,一般是高电平代表就绪,低电平代表正忙。

    操作Nand Flash的一般步骤是:

      1. 发命令

        选中芯片

        CLE设置为高电平

        在DATA0~DATA7上输出命令值

        发出一个写脉冲

      2. 发地址

        选中芯片

        ALE为高电平

        在DATA0~DATA7上传输数据

        发出一个写脉冲

      3. 发数据

        选中芯片

        发出读脉冲

        读取DATA0~DATA7上的数据。

    使用UBOOT来体验NAND FLASH的操作:

    读ID

        选中        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

        退出读ID的状态      NFCMMD=0XFF      mw.b  0x4e000008  0xff

    NAND FLASH驱动程序层次

    看内核启动信息

    S3C24XX NAND Driver, (c) 2004 Simtec Electronics
    s3c2440-nand s3c2440-nand: Tacls=3, 30ns Twrph0=7 70ns, Twrph1=3 30ns
    NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)
    Scanning device for bad blocks
    Bad eraseblock 256 at 0x02000000
    Bad eraseblock 257 at 0x02020000
    Bad eraseblock 319 at 0x027e0000
    Bad eraseblock 606 at 0x04bc0000
    Bad eraseblock 608 at 0x04c00000
    Creating 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_inithw
    s3c2410_nand_init_chip
    nand_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            

    驱动程序代码:

    /*
    * driversmtd
    ands3c2410.c
    * driversmtd
    andat91_nand.c
    */
    
    #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 nand_chip *s3c_nand;
    struct mtd_info *s3c_mtd;
    struct clk *clk;
    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 nfeecc0;
        unsigned long nfeecc1;
        unsigned long nfsecc;
        unsigned long nfsblk;
        unsigned long nfeblk;
    };
    
    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 volatile struct s3c_nand_regs *nand_regs;
    
    static void s3c2440_select_chip(struct mtd_info *mtd, int chipnr )
    {
        if(chipnr == -1 )
        {
            /* 取消选中 */
            nand_regs->nfcont |= (1<<1); 
        }
        else
        {
            /* 选中 NFCONT^1 设置为1 */
            nand_regs->nfcont &= ~(1<<1);
        }
    }
    
    static void s3c2440_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
    {
    
        if (cmd == NAND_CMD_NONE)
            return;
    
        if (ctrl & NAND_CLE)
        {
            /* 发命令 */
            nand_regs->nfcmd = cmd;
        }
        else
        {
            /* 发地址 */
            nand_regs->nfaddr = cmd;
        }
    }
    
    static int s3c2440_dev_ready(struct mtd_info *mtd)
    {
        /*  */
        return (nand_regs->nfstat & (1<<0));
    }
    
    static int s3c_nand_init(void)
    {
        /* 分配一个nand_chip结构体 */
        s3c_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
        
        nand_regs = ioremap(0x4e000000,sizeof(struct s3c_nand_regs));
        
        clk = clk_get(NULL,"nand");
        clk_enable(clk);
        /* 设置 */
        s3c_nand->select_chip = s3c2440_select_chip;
        s3c_nand->cmd_ctrl = s3c2440_nand_cmd_ctrl;
        s3c_nand->IO_ADDR_R = (void *)&nand_regs->nfdata;
        s3c_nand->IO_ADDR_W = (void *)&nand_regs->nfdata;
        s3c_nand->dev_ready = s3c2440_dev_ready;
        s3c_nand->ecc.mode  = NAND_ECC_SOFT;
    
        /* 硬件相关的操作:根据nand Flash的手册设置时间参数 */
        /* HCLK = 100MHz */
        /* TACLS: 发出cle/ale之后多长时间才发出nWE信号 */
        /* TWRPHO: nWE的脉冲宽度 */
        nand_regs->nfconf = (1<<8);
        nand_regs->nfcont = 0x03;
        
        /* 使用:nand_scan */
        s3c_mtd = kzalloc( sizeof(struct mtd_info), GFP_KERNEL );
        s3c_mtd->owner = THIS_MODULE;
        s3c_mtd->priv = s3c_nand;
        nand_scan(s3c_mtd,1); /* 扫描识别 */
    
        /* add_mtd_partitions */
        add_mtd_partitions(s3c_mtd,s3c_nand_parts,4);
        
        //add_mtd_device(s3c_mtd);  //整个flash只有一个分区的话可以用这个
        return 0;
    }
    
    static void s3c_nand_exit(void)
    {
        del_mtd_partitions(s3c_mtd);
        kfree(s3c_mtd);
        iounmap(nand_regs);
        kfree(s3c_nand);
    
    }
    
    module_init(s3c_nand_init);
    module_exit(s3c_nand_exit);
    MODULE_LICENSE("GPL");

    在添加这个内核模块的时候,首先卸载内核中的nand Flash驱动。

    ->Device Drivers

      ->Memory Technology Device (MTD) support

        ->NAND Device Support

    sd

  • 相关阅读:
    如何使Linux系统上的程序开机后自动运行 (转)
    Makefile详解 (转--不错就是有点长)
    ./configure && make && make install详解 (转)
    从程序员角度看ELF | Linux-Programming (转)
    动态符号链接的细节 (转)
    GCC编译动态和静态链接库例子
    gcc编译静态库和动态库
    udhcp源码详解(五) 之DHCP包--options字段
    一个炒鸡好用的pdf阅读器
    Linux 创建用户 用户组 用户权限
  • 原文地址:https://www.cnblogs.com/ynxf/p/6041069.html
Copyright © 2020-2023  润新知