zoukankan      html  css  js  c++  java
  • Nand Flash驱动程序

    学习目的

    • 掌握Nand Flash驱动程序的编写

    通过前面对MTD系统层次进行分析,理清了内核中MTD系统框架。内核MTD系统已经实现了设备节点、MTD设备层、MTD原始设备层构建,并预留接口用于硬件驱动层的注册。驱动程序编写者只需完成硬件相关的操作,如提供硬件初始化和访问函数、填充mtd_info结构体、填充用于分区描述的mtd_partition结构体、向上注册MTD硬件设备等。有了前面的基础,这一节就开始编写Nand Flash驱动程序,驱动程序的编写参考内核源码树中的drivers/mtd/nand/at91_nand.c

    1、驱动入口函数

    int s3c_nand_drv_init(void)
    {
        struct clk *clk;
    
        /* nand flash寄存器ioremap */
        nand_reg = ioremap(S3C_NAND_REG_BASE, sizeof(struct s3c_nand_ctl_reg));--------------------------->①    
    
        /* 分配nand_chip结构体 */
        chip_op = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);------------------------------------------>②
    
        /* 设置nand_chip结构体 */
        chip_op->select_chip = s3c_nand_select_chip;
        chip_op->cmd_ctrl    = s3c_nand_cmd_ctrl;
        chip_op->IO_ADDR_R   = &nand_reg->NFDATA;
        chip_op->IO_ADDR_W   = &nand_reg->NFDATA;
        chip_op->dev_ready   = s3c_nand_dev_ready;
        chip_op->ecc.mode    = NAND_ECC_SOFT;    /* enable ECC */
        chip_op->chip_delay  = 20;        /* 20us command delay time */
    
        /* 硬件相关设置 */
        clk = clk_get(NULL, "nand"); /* 使能NAND Flash控制器时钟 */--------------------------------------->③
        clk_enable(clk);
        
    #define TACLS     0
    #define TWRPH0    1
    #define TWRPH1    0
        nand_reg->NFCONF     = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);--------------------------------->④
        nand_reg->NFCONT     = (1<<1) | (1<<0);
        
        /* 分配设置mtd_info结构体 */
        nand_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);---------------------------------------->⑤
        nand_mtd->priv = chip_op;
        nand_mtd->owner = THIS_MODULE;
    
        /* 获取nand flash信息 */
        nand_scan(nand_mtd, 1);------------------------------------------------------------------------->⑥
    
        //add_mtd_partitions(nand_mtd, s3c_nand_parts,  ARRAY_SIZE(s3c_nand_parts));
        mtd_device_parse_register(nand_mtd, NULL, NULL, s3c_nand_parts, ARRAY_SIZE(s3c_nand_parts));---->⑦
        return 0;
    }

    ① s3c2440芯片NAND Flash控制器相关的寄存器物理地址到虚拟地址映射

    ② 分配并设置nand_chip结构体

    nand_chip结构体是MTD系统为描述nand flash硬件而抽象出结构体,它主要包含三方面内容:

    • 提供函数指针,指向用于操作NAND Flash硬件的函数
    • 包含用于表示Nand Flash芯片特性变量,如芯片位宽、容量等等
    • 包含用于坏块管理结构体,如ecc、oob和bbt等

    MTD提供了默认的nand_chip结构体操作函数,如果驱动编写者没对nand_chip结构体的某些函数指针成员进行设置,在调用nand_scan函数时获取NAND Flash信息前,会使用nand_set_defaults函数将它被设置成默认函数。

    虽然MTD为nand_chip结构体提供了一些默认函数,但这些默认函数是针对NAND Flash的共同特性抽象出的,如这些函数知道发哪些命令去读取数据,但不知道怎样去发命令,这样底层的操作还是需要我们根据硬件的特性自己去设置。

    这里我们设置了nand_chip结构体成员.select_chip、.cmd_ctrl、.IO_ADDR_R、.IO_ADDR_W、.dev_ready、.ecc.mode

        chip_op->select_chip = s3c_nand_select_chip; ----------------->片选操作函数
        chip_op->cmd_ctrl    = s3c_nand_cmd_ctrl;--------------------->命令控制函数,根据传入参数,确认是发命令还是发地址
        chip_op->IO_ADDR_R   = &nand_reg->NFDATA;--------------------->读数据地址
        chip_op->IO_ADDR_W   = &nand_reg->NFDATA;--------------------->写数据地址
        chip_op->dev_ready   = s3c_nand_dev_ready;-------------------->获取NAND Flash芯片是否处于忙状态
        chip_op->ecc.mode    = NAND_ECC_SOFT;------------------------->软件方式进行坏块校验

    以上操作函数都是硬件相关的,针对主控芯片Nand Flash控制器编写

    ③ 使能NAND Flash控制器时钟

    ④ 根据NAND Flash芯片特性,设置2440芯片NAND Flash控制器参数

    针对NAND Flash芯片的时序图,设置NAND Flash控制器的寄存器,读写时序设置

    ⑤ 分配和设置mtd_info结构体

    mtd_info结构体是MTD设备层操作注册的MTD硬件的接口。这里我们仅需要设置mtd_info结构体成员的priv指针指向nand_chip结构体,对于硬件操作时,会取出mtd_info结构体priv成员,最终调用到nand_chip结构体中硬件操作函数。

    不同厂商的NAND Flash芯片,虽然芯片特性有所不同,但他们的读写操作规范都遵守相同的规范,因此它的读写操作函数可以有内核来实现。在NAND Flash驱动中mtd_info结构体中的erase、read、write等函数,是nand_scan函数里调用nand_scan_tail函数设置。

    ⑥ 调用nand_scan函数

    nand_scan函数首先调用nand_scan_ident函数获取NAND Flash信息,根据读取信息设置nand_chip结构体中一些描述硬件芯片特性的变量,然后调用nand_scan_tail函数,使用默认值设置所有未初始化的指针,读取NAND Flash中坏块,设置mtd_info结构体成员。

    ⑦ 注册MTD设备

    将mtd_info结构体存放到mtd_table数组未使用的一项里,解析并设置分区,调用设备层已经注册的notifier结构体的add成员函数,设置gendisk结构体,创建设备节点等。

    s3c_nand_parts是对NAND Flash分区进行描述,在Nand Flash创建分别创建了bootloader、params、kernel、root等四个分区,分区范围描述如下:

    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,
        }
    };

    其中offset是分区开始的偏移地址,MTDPART_OFS_APPEND,表示紧接着上一个分区,MTD Core会自动计算和处理分区地址;

    size是分区的大小,在最后一个分区我们设为MTDPART_SIZ_FULL,表示这个NADN剩下的所有部分。

    2、驱动出口函数

    void s3c_nand_drv_exit(void)
    {
        nand_release(nand_mtd);------------>①
        kfree(nand_mtd);------------------->②
        kfree(chip_op);-------------------->③
        iounmap(nand_reg);----------------->④
    }

    ① 取消注册NAND Flash设备

    ② 释放动态分配nand_info结构体内存

    ③ 释放动态分配nand_chip结构体内存

    ④ 取消s3c2440的NAND Flash控制器相关寄存器虚拟地址到物理地址映射

    3、驱动测试

    1)make menuconfig配置不让内核自带的s3c2440 nand flash驱动编译进内核

    Device Drivers  --->
        <*> Memory Technology Device (MTD) support  ---> 
                <*>   NAND Device Support  --->
                        < >   NAND Flash support for Samsung S3C SoCs

    2)make uImage重新编译内核

    3)设置uboot内核启动参数,挂接网络文件系统作为根文件系统

    bootargs=noinitrd root=/dev/nfs nfsroot=192.168.1.100:/work/fs_digital_photo_self ip=192.168.1.99:192.168.1.100:192.168.1.1:255.255.255.0::eth0:off init=linuxrc console=ttySAC0,115200

    4)烧写并重启内核,insmode加载驱动程序

    5)挂载设备节点到mnt目录测试(若nand flash上无文件系统,须使用mtd-utils-05.07.23.tar.bz2源码编译工具,用于设备格式化)

    mount /dev/mtdblock3 /mnt
    cd mnt
    echo "Hello World" > hello.txt
    cat hello.txt

    完整驱动程序

    #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/cpufreq.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 <plat/regs-nand.h>
    #include <plat/nand.h>
    
    
    
    #define S3C_NAND_REG_BASE 0x4E000000
    
    struct s3c_nand_ctl_reg
    {
        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 mtd_info *nand_mtd;
    static struct nand_chip *chip_op;
    
    static struct s3c_nand_ctl_reg *nand_reg;
    
    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 void s3c_nand_select_chip(struct mtd_info *mtd, int chipnr)
    {
    
        if (chipnr == -1)
        {
            /* 取消选中: NFCONT[1]设为1 */
            nand_reg->NFCONT |= (1<<1);
        }
        else
        {
            /* 选中:  NFCONT[1]设为0 */
            nand_reg->NFCONT &= ~(1<<1);
            
        }
    }
    
    static void s3c_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
    {
    
        if (cmd == NAND_CMD_NONE)
            return;
    
        if (ctrl & NAND_CLE)
        {
            /* 发命令:    NFCMD=cmd */
            nand_reg->NFCMD  = cmd;
            
        }
        else
        {
            /* 发命令:    NFADDR=cmd */
            nand_reg->NFADDR = cmd;
        }
    }
    
    static int s3c_nand_dev_ready(struct mtd_info *mtd)
    {
        return (nand_reg->NFSTAT & (1<<0));
    }
    
    
    
    int s3c_nand_drv_init(void)
    {
        struct clk *clk;
    
        /* nand flash寄存器ioremap */
        nand_reg = ioremap(S3C_NAND_REG_BASE, sizeof(struct s3c_nand_ctl_reg));    
    
        /* 分配nand_chip结构体 */
        chip_op = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
    
        /* 设置nand_chip结构体 */
        chip_op->select_chip = s3c_nand_select_chip;
        chip_op->cmd_ctrl    = s3c_nand_cmd_ctrl;
        chip_op->IO_ADDR_R   = &nand_reg->NFDATA;
        chip_op->IO_ADDR_W   = &nand_reg->NFDATA;
        chip_op->dev_ready   = s3c_nand_dev_ready;
        chip_op->ecc.mode    = NAND_ECC_SOFT;    /* enable ECC */
        chip_op->chip_delay  = 20;        /* 20us command delay time */
    
        /* 硬件相关设置 */
        clk = clk_get(NULL, "nand"); /* 使能NAND Flash控制器时钟 */
        clk_enable(clk);
        
    #define TACLS     0
    #define TWRPH0    1
    #define TWRPH1    0
        nand_reg->NFCONF     = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
        nand_reg->NFCONT     = (1<<1) | (1<<0);
        
        /* 分配设置mtd_info结构体 */
        nand_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
        if(nand_mtd == NULL)
            printk("alloc nand_mtd failed...
    ");
        nand_mtd->priv = chip_op;
        nand_mtd->owner = THIS_MODULE;
    
        /* 获取nand flash信息 */
        nand_scan(nand_mtd, 1);
    
        //add_mtd_partitions(nand_mtd, s3c_nand_parts,  ARRAY_SIZE(s3c_nand_parts));
        mtd_device_parse_register(nand_mtd, NULL, NULL, s3c_nand_parts, ARRAY_SIZE(s3c_nand_parts));
        return 0;
    }
    
    void s3c_nand_drv_exit(void)
    {
        nand_release(nand_mtd);
        kfree(nand_mtd);
        kfree(chip_op);
        iounmap(nand_reg);
    }
    
    module_init(s3c_nand_drv_init);
    module_exit(s3c_nand_drv_exit);
    
    MODULE_LICENSE(
    "GPL");
    s3c_nand_drv.c
  • 相关阅读:
    hdu 5119 Happy Matt Friends
    hdu 5128 The E-pang Palace
    hdu 5131 Song Jiang's rank list
    hdu 5135 Little Zu Chongzhi's Triangles
    hdu 5137 How Many Maos Does the Guanxi Worth
    hdu 5122 K.Bro Sorting
    Human Gene Functions
    Palindrome(最长公共子序列)
    A Simple problem
    Alignment ( 最长上升(下降)子序列 )
  • 原文地址:https://www.cnblogs.com/053179hu/p/13997479.html
Copyright © 2011-2022 走看看