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
原文地址:https://www.cnblogs.com/053179hu/p/13997479.html