nand flash驱动

发布时间 2023-03-22 21:16:36作者: 迷人的危险~

 

编写Nand Flash驱动

由于MTD设备驱动已经帮我实现了MTD块设备、以及MTD字符设备驱动的编写。而我们要做的主要就是:

    • 分配nand_chip内存;
    • 根据SOC Nand控制器初始化nand_chip成员,比如:chip->legacy(成员write_buf、read_buf、select_chip、cmd_ctrl、dev_ready、IO_ADDR_R、IO_ADDR_W)、chip->controller;
    • 设置chip->priv为mtd_info;
    • 以mtd_info为参数调用nand_scan探测Nand Flash,nand_scan会读取nand芯片ID:

      • 初始化chip->base.mtd(成员writesize、oobsize、erasesize等);
      • 初始化chip->base.memorg(成员bits_per_cell、pagesize、oobsize、pages_per_eraseblock、planes_per_lun、luns_per_target、ntatgets等);
      • 初始化chip->options、chip->base.eccreq;
      • chip成员中所有未初始化函数指针则使用nand_base.c中的默认函数;
      • 初始化chip->ecc各个成员(设置ecc模式及处理函数);
    • mtd_info和mtd_partition为参数调用mtd_device_register()进行MTD设备注册;

 

1 项目结构

我们在/work/sambashare/drivers路径下创建项目17.nand_flash_dev,创建为nand_flash_dev.c。

 

2 模块入口函数

    • 使用kzalloc函数为nand_chip动态申请内存,其中nand_chip.base.mtd包含了mtd_info结构体成员;

    • 使用ioremap将Nand Flash控制器寄存器地址映射到虚地址空间;

    • 设置mtd_info结构体成员;

    • 设置nand_chip结构体成员;

    • 获取nand时钟,设置info->clk;并使能nand时钟、Nanfd Flash控制器;

    • 以mtd_info为参数调用nand_scan探测Nand Flash;

    • mtd_info和mtd_partition为参数调用mtd_device_register()进行MTD设备注册;

 

3 模块出口函数

    • 卸载MTD设备;
    • 取消寄存器地址映射;
    • 释放nand_chip;

 

nand_flash_dev.c

#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/io.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/rawnand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>


/*
 * Nand Flash控制器相关寄存器
 */
struct  nand_regs {
    unsigned long nfconf  ;             //0x4E000000
    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_regs  *s3c_regs;     
static struct mtd_info   *s3c_mtd;
static struct nand_chip  *s3c_nand_chip;      


/* 分区信息 */
static struct mtd_partition s3c_nand_part[] = {
   [0] = {
        .name    = "u-boot",
        .size    = SZ_256K,
        .offset  = 0,
    },
    [1] = {
        .name    = "params",
        .size    = SZ_128K,
        .offset  = MTDPART_OFS_APPEND,
    },
    [2] = {
        .name    = "kernel",
        /* 5 megabytes, for a kernel with no modules
         * or a uImage with a ramdisk attached */
        .size    = SZ_4M,
        .offset  = MTDPART_OFS_APPEND,
    },
    [3] = {
        .name    = "rootfs",
        .offset  = MTDPART_OFS_APPEND,
        .size    = MTDPART_SIZ_FULL,
    },
};

/* 
 * 使能/禁止片选 
 */
static void s3c_nand_select_chip(struct nand_chip *this, int chip)
{
    if(chip==-1)          //CE Disable
   {
        s3c_regs->nfcont|=(0x01<<1);               //bit1置1
   }
    else                         //CE Enable
   {
        s3c_regs->nfcont&=~(0x01<<1);        //NFCONT寄存器片选位设置位0,使能片选  
   }            
}

/*
 * 用于向nand芯片发送命令/地址,第三个参数用来区分是发送的是命令还是地址。
 */
static void s3c_nand_cmd_ctrl(struct nand_chip *chip, int cmd, unsigned int ctrl)
{
    if (ctrl & NAND_CLE)               //当前为command状态 ,   
        s3c_regs->nfcmd=cmd;   
    else                               //当前为地址状态
        s3c_regs->nfaddr=cmd;
}

/*
 * 用于获取nand的状态,0表示繁忙,1表示就绪:
 */
static int s3c_nand_devready(struct mtd_info *mtd)
{
    return (s3c_regs->nfstat&0x01);     //获取RnB状态,0:busy       1:ready
}

/*
 * 用于从nand读取len个长度字节,并保存到buf缓冲区中:
 */
static void s3c_nand_read_buf(struct nand_chip *this, u_char *buf, int len)
{
    readsl(&s3c_regs->nfdata, buf, len >> 2);  // 读取NFDATA寄存器的值,读取长度位len >> 2,按字访问

    /* cleanup if we've got less than a word to do */
    if (len & 3) {  // 处理长度非4整数倍情况
        buf += len & ~3;
        for (; len & 3; len--)
            *buf++ = readb(&s3c_regs->nfdata);  // 按字节读取
    }
}

/*
 * 用于将缓冲区buf中len个长度字节写入到nand
 */
static void s3c_nand_write_buf(struct nand_chip *this, const u_char *buf,int len)
{
    writesl(&s3c_regs->nfdata, buf, len >> 2);  //写入NFDATA寄存器,写入长度为len >> 2,按字写入

    /* cleanup any fractional write */
    if (len & 3) {   // 处理长度非4整数倍情况
        buf += len & ~3;
        for (; len & 3; len--, buf++)  // 按字节写入
             writeb(*buf, s3c_regs->nfdata);
    }
}


/* 
 *init入口函数 
*/
static int s3c_nand_init(void)
{
    struct clk *nand_clk;
    int res;
    
    /*1.分配结构体:nand_chip */  
    s3c_nand_chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
    s3c_mtd = &s3c_nand_chip->base.mtd;
    
    /*2.获取nand flash 寄存器虚拟地址*/
    s3c_regs = ioremap(0x4E000000, sizeof(struct nand_regs));

    /*3.设置mtd_info*/
    s3c_mtd->owner = THIS_MODULE;
    s3c_mtd->priv = s3c_nand_chip;                //私有数据

    /*4.设置nand_chip*/
    s3c_nand_chip->legacy.IO_ADDR_R = &s3c_regs->nfdata;         //设置读data
    s3c_nand_chip->legacy.IO_ADDR_W = &s3c_regs->nfdata;         //设置写data
    s3c_nand_chip->legacy.select_chip = s3c_nand_select_chip;    //片选/取消片选
    s3c_nand_chip->legacy.cmd_ctrl  = s3c_nand_cmd_ctrl;
    s3c_nand_chip->legacy.dev_ready = s3c_nand_devready;
    s3c_nand_chip->legacy.read_buf = s3c_nand_read_buf;
    s3c_nand_chip->legacy.write_buf = s3c_nand_write_buf;
    s3c_nand_chip->ecc.mode = NAND_ECC_NONE;                   //关闭ECC
    s3c_nand_chip->legacy.chip_delay   = 50;


    /*5.设置硬件相关*/    
    /*5.1使能nand flash 时钟*/
    nand_clk = clk_get(NULL, "nand");  // 获取nand时钟,并赋值给GATE(HCLK_NAND, "nand", "hclk", CLKCON, 4, 0, 0)
                                                 // CLKCON寄存器bit[4],控制进入Nand FLash控制器模块的HCLK
    clk_prepare_enable(nand_clk); 

    /*5.2设置时序*/
    #define TACLS    0                       //0nS
    #define TWRPH0   1                       //15nS
    #define TWRPH1   0                       //5nS
    s3c_regs->nfconf = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);

    /*5.3 bit1:关闭片选 bit0:开启nand flash 控制器*/
    s3c_regs->nfcont = (1<<1) | (1<<0);
    
    /*6.扫描NAND*/
    if (nand_scan(s3c_nand_chip, 1)) 
    {  
        res = -ENXIO;
        goto out;
    }
            
    /*7.行MTD设备注册*/
    res = mtd_device_register(s3c_mtd, s3c_nand_part, 4); 
    if(!res)
    {
        return 0;
    }
out:
    mtd_device_unregister(s3c_mtd);                      //MTD设备卸载
    iounmap(s3c_regs);                                //释放Nand Flash控制器寄存器
    kfree(s3c_nand_chip);                             //释放nand_chip
    return 0;  
}

/*
 * exit出口函数
 */
static void s3c_nand_exit(void)
{
    mtd_device_unregister(s3c_mtd);       //行MTD设备卸载
    iounmap(s3c_regs);                    //释放nand flash寄存器
    kfree(s3c_nand_chip);                 //释放nand_chip
}

module_init(s3c_nand_init);
module_exit(s3c_nand_exit);
MODULE_LICENSE("GPL");

 

Makefile

KERN_DIR :=/work/sambashare/linux-5.2.8
all:
        make -C $(KERN_DIR) M=`pwd` modules
clean:
        make -C $(KERN_DIR) M=`pwd` modules clean
        rm -rf modules.order

obj-m += nand_flash_dev.o

 

 

linux驱动移植-linux块设备驱动Nand Flash - 大奥特曼打小怪兽 - 博客园 (cnblogs.com)