首页 > 其他分享 >nand flash驱动

nand flash驱动

时间:2023-03-22 14:47:10浏览次数:53  
标签:chip flash nand unsigned mtd 驱动 buf s3c

 

编写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)

 

   

标签:chip,flash,nand,unsigned,mtd,驱动,buf,s3c
From: https://www.cnblogs.com/kernelx/p/17243674.html

相关文章

  • python详解事件驱动event实现
    所有的计算机程序都可以大致分为两类:脚本型(单次运行)和连续运行型(直到用户主动退出)。脚本型:脚本型的程序包括最早的批处理文件以及使用Python做交易策略回测等等,这类......
  • 基于HDF驱动框架的温度传感器驱动开发
    概述​温度传感器(Temperature)Sensor驱动,对温度传感器进行上电,通过驱动入口,将温度传感器注册到HDF驱动框架,对温度传感器驱动进行初始化,探测器件是否在位,并解析配......
  • STM32F103 高级定时器的PWM驱动电机注意事项
    此前一直用通用定时器的PWM驱动电机,因此初始化结构体只有如下几项,今天用TB6612驱动电机,用TIM1-CH4的PWM,结果发现设置完毕后电机不转。//OCInitStructTIM_OC......
  • Go语言:利用 TDD 驱动开发测试 学习结构体、方法和接口
    环境安装:(新手向)在Linux中使用VScode编写"Hello,world"程序,并编写测试-Ubuntu20.4上一篇相关随笔:Go语言:利用TDD测试驱动开发帮助理解数组与动态数组(切片)的区别......
  • 外设驱动库开发笔记52:PM3003S激光粉尘仪驱动
      空气质量是现代日常生活中人们所关注的事情,也是生存环境好坏的一种体现。其中粉尘数量监测更是空气质量检测中最常见的对象,在我们的检测设备中也经常会有这种需求。检......
  • Linux 网络设备驱动整理
      注意:拥有硬件探测机制的总线,例如USB,PCI总线上的设备不需要dts描述;没有探测机制的总线,如I2C设备应该用dts描述。PCIE网卡因为属于pci设备,不需要dts描述;但是PCIE控......
  • 04期:领域驱动设计与微服务
    这里记录的是学习分享内容,文章维护在Github:studeyang/leanrning-share。如何理解领域驱动设计?随着微服务的兴起,你一定听说过领域驱动设计DDD(domain-drivendesign),但是如......
  • Go语言:利用 TDD 测试驱动开发帮助理解数组与动态数组(切片)的区别
    ArrayVSSlice数组允许你以特定的顺序在变量中存储相同类型的多个元素。对于数组来说,最常见的就是迭代数组中的元素。我们创建一个Sum函数,它使......
  • 四轮驱动,助力元服务澎湃动力
    黎明前的暗夜下,巍巍昆仑侧广袤草原上,一道激光无声闪过,一辆黑马披星戴月疾驰向北。天边泛起朦朦金光,远处黑马的合金轮毂激射出金色炫光。鸿蒙元服务(简称元服务)是华为提供的......
  • mysql-connector-python驱动和django-mysql-pool连接池
    ##################### 为了设计一个公用的、安全的连接池接口,可以考虑以下几个方面:封装连接池初始化和获取连接的逻辑,使得调用者不需要了解连接池的具体实现细节,只需要调......