http://www.cnblogs.com/lifexy/p/7701181.html

1、本节使用的nand flash型号为K9F2G08U0C,它的命令如下:

1.1 我们以上图的Read ID(读ID)为例,它的时序图如下:

首先需要使用CE片选

1)使能CLE

2)发出0x90命令,并发出WE写脉冲

3)复位CLE,然后使能ALE

4)发出0x00地址,并发出WE写脉冲

5)设置CLE和ALE为低电平

6)while判断nRE(读使能)是否为低电平

7)读出8个I/O的数据,并发出RE上升沿脉冲

(我们的nand flash为8个I/O口)

1.2 nand flash 控制器介绍

在2440中有个nand flash 控制器,它会自动控制CLE,ALE那些控制引脚,我们只需要配置控制器,就可以直接写命令,写地址,读写数据到它的寄存器中便能完成(读写数据之前需要判断RnB脚),如下图所示:

若在nand flash 控制器下,我们读ID就只需要如下几步(非常方便):

1)将寄存器NFCONT(0x4E000004)的bit1 = 0,来使能片选

2)写入寄存器NFCMMD(0x4E000008) = 0x90,发送命令

3)写入寄存器NFADDR(0x4E00000C) = 0x00,发送地址

4)while判断nRE(读使能)是否为低电平

5)读寄存器NFDATA(0x4E000010),来读取数据

1.3 我们在uboot中测试,通过md和mw命令来实现读id(x要小写)

刚好对用了我们nand flash手册里的数据(0xEC表示厂家ID,0xDA表示设备ID):

若我们要退出读ID命令时,只需要reset就行,同样地,要退出读数据/写数据时,也是reset。

1.4 reset的命令为0xff,它的时序图如下所示:

1.5 同样地,参考读地址时序图来看看:

其中Column Address 对应 列地址,表示某页里的2k地址

Row Address 对应 行地址,表示具体的某一页

5个地址的周期图,如下所示:

因为我们的nand flash = 256MB = (2k*128M)b

所以row Address = 128M = 2^17(A27~A11)

所以column Adress = 2k = 2^11(A10~A0)

1.7 我们现在读0地址的内容

使用命令 nand dump 0读出nand flash 0地址的内容

从这一节当中的第一张图,可以发现Read,先发出00h命令,再发出30h命令。

(md.b 后面的1表示读1次)

2、接下来我们来参考自带的nand flash,位于drivers/mtd/nand/s3c2410.c中

2.1 为什么nand在mtd目录下

因为mtd(memory technology device 存储技术设备)是用于访问memory设备(ROM、flash)的Linux子系统。MTD的主要目的是为了使新的memory设备的驱动更加简单,为此它在硬件和上层之间提供了一个抽象的接口

2.2 首先来看s3c2440.c的入口函数

static int __init s3c2410_nand_init(void)
{printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");platform_driver_register(&s3c2412_nand_driver);platform_driver_register(&s3c2440_nand_driver);return platform_driver_register(&s3c2410_nand_driver);
}

在入口函数中,注册了一个platform平台设备驱动,也是说当与nand flash设备匹配时,就会调用s3c2440_nand_driver->probe()来初始化

我们进入probe函数中,看看是如何初始化

static int s3c24xx_nand_probe(struct platform_device *pdev,enum s3c_cpu_type cpu_type)
{... ...err = s3c2410_nand_inithw(info, pdev);    //初始化硬件hardware,设置TACLS、TWRPH0、TWRPH1通信时序等... ...s3c2410_nand_init_chip(info, nmtd, sets);    //初始化芯片... ...nmtd->scan_res = nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1);    //扫描nand flash... ...s3c2410_nand_add_partition(info, nmtd, sets);    //来添加mtd分区... ...
}

通过上面代码和注释,得出:驱动主要调用内核的nand_scan()函数,add_mtd_partitions()函数,来完成注册nand flash

3 上面probe()里的nand_scan()扫描函数 位于 drivers/mtd/nand/nand_base.c

它会调用nand_scan()->nand_scan_ident()->nand_get_flash_type()来获取flash存储器的类型

以及nand_scan()->nand_scan_ident()->nand_tail()来构造mtd设备的成员(实现对nand flash的读,写,擦除等)

3.1 其中nand_get_flash_type()函数如下所示:

static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip, int busw, int *maf_id)
{struct nand_flash_dev *type = NULL;int i, dev_id, maf_idx;chip->select_chip(mtd, 0);//调用nand_chip结构体的成员select_chip使能flash片选chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);//3.2调用nand_chip结构体的成员cmdfunc,发送读id命令,最后数据保存在mtd结构体里*maf_id = chip->read_byte(mtd);//获取厂家IDdev_id = chip->read_byte(mtd);//获取设备ID/* 3.3for循环匹配nand_flash_ids[]数组,找到对应的nandflash信息 */for (i = 0; nand_flash_ids[i].name != NULL; i++) {if (dev_id == nand_flash_ids[i].id) {//匹配设备idtype =  &nand_flash_ids[i];break;}}//3.4 匹配成功,便打印nand flash参数printk(KERN_INFO "NAND device: Manufacturer ID:"" 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,dev_id, nand_manuf_ids[maf_idx].name, mtd->name);... ...
}

从上面代码和注释得出,nand_chip结构体就是保存与硬件相关的函数(后面会讲这个结构体)

3.2 其中NAND_CMD_READID定义为0x90,也就是发送0x90命令,和0x00地址来读id,最后放到mtd中

3.3 nand_flash_ids[]数组就是个全局变量,这里通过匹配设备ID,来确定我们nand flash是个多大的存储器

如下图所示,在芯片手册中,看到nand flash的设备ID=0xDA

所以就匹配到nand_flash_ids[]里的0xDA:

3.4 然后打印出nand flash 参数,我们启动内核就可以看到:

4.probe()里的s3c2410_nand_add_partition()函数主要是注册mtd设备的nand_flash

最后它调用了s3c2410_nand_add_partition()->add_mtd_partitions()->add_mtd_device(),其中add_mtd_partitions()函数主要实现多个分区创建,也就是多次调用add_mtd_device(),当只设置nand_flash为一个分区时,就直接调用add_mtd_device()即可。

4.1 add_mtd_partitions()函数原型如下:

int add_mtd_partitions(struct mtd_info *master, const struct mtd_partition *parts,
int nbparts)
//函数成员介绍:
//master:就是要创建的mtd设备
//parts:分区信息的数组,它的结构体是mtd_partition,该结构体如下所示:/*struct mtd_partition {char *name;            //分区名u_int32_t size;        //分区大小u_int32_t offset; //分区所在的偏移值u_int32_t mask_flags; //掩码标志struct nand_ecclayout *ecclayout; //OOB布局struct mtd_info **mtdp;      //MTD的指针,不常用};}
*///nbparts:等于分区信息的数组个数,表示要创建分区的个数

比如我们启动内核时,也能找到内核自带的nand flash的分区信息

4.2 其中add_mtd_device()函数如下所示:

int add_mtd_device(struct mtd_info *mtd)//创建一个mtd设备
{struct list_head *this;list_for_each(this, &mtd_notifiers) //4.3找mtd_notifiers链表里的list_head结构体{//通过list_head找到struct mtd_notifier *notstruct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);not->add(mtd);//最后调用mtd_notifier的add()函数}... ...
}

4.3 我们搜索上面函数里的mtd_notifiers链表

看看里面的list_head结构体,在哪里放入的,就能找到执行的add()是什么了

4.4 如下图,发现list_head在register_mtd_user()里放到mtd_notifiers链表中

4.5 继续搜索register_mtd_user(),被哪个调用

如上图,找到被drivers/mtd/mtdchar.c、drivers/mtd/mtd_blkdevs.c调用(4.6节和4.7节会分析)

是因为mtd层既提供了字符设备的操作接口(mtdchar.c),也实现了块设备的操作接口(mtd_blkdevs.c)

我们在控制台输入ls -l /dev/mtd*,也能找到块MTD设备节点和字符MTD设备节点,如下图所示:

上图中,可以看到共创建了4个分区的设备,每个分区都包含了两个字符设备(mtd%d、mtd%d ro)、一个块设备(mtdblock0)。

其中MTD的块设备的主设备号为31,MTD的字符设备的主设备号为90(后面会讲到在哪里创建)

4.6 我们进入上面搜索到的drivers/mtd/mtdchar.c,找到它的入口函数init_mtdchar():


static int __init init_mtdchar(void)
{//创建字符设备mtd,主设备号为90,cat /proc/devices 可以看到if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) {printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",MTD_CHAR_MAJOR);return -EAGAIN;}mtd_class = class_create(THIS_MODULE, "mtd");    //创建类if (IS_ERR(mtd_class)) {printk(KERN_ERR "Error creating mtd class.\n");unregister_chrdev(MTD_CHAR_MAJOR, "mtd");return PTR_ERR(mtd_class);}register_mtd_user(&notifier);//将notifier添加到mtd_notifiers链表中return 0;
}

之所以上面没有创建设备节点,是因为此时没有nand flash驱动

4.6.1 发现上面的notifiers是mtd_notifier结构体的:

4.6.2 如上图,我们进入notifiers是mtd_nofify_add()函数看看:


static void mtd_notify_add(struct mtd_info* mtd)
{if (!mtd)return;/* 其中MTD_CHAR_MAJOR主设备定义为90 */class_device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2),NULL, "mtd%d", mtd->index);//创建mtd%d字符设备节点class_device_create(mtd_class, NULL,MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),NULL, "mtd%dro", mtd->index);//创建mtd%dro字符设备节点
}

该函数创建了两个字符设备(mtd%d,mtd%dro),其中ro的字符设备表示为只读

总结出:

mtdchar.c的入口函数将notifie添加到mtd_notifiers链表中。

然后在add_mtd_device()函数中,当查找到mtd字符设备的list_head时,就调用mtd_notifiers->add()来创建两个字符设备(mtd%d,mtd%dro)

4.7 同样,我们进入mtd_blkdevs.c(MTD块设备)中,找到注册到mtd_nitifiers链表是blktrans_notifier变量:

4.7.1 然后进入blktrans_notifier变量的blktrans_notify_add()函数


static void blktrans_notify_add(struct mtd_info *mtd)
{struct list_head *this;if (mtd->type == MTD_ABSENT)return;list_for_each(this, &blktrans_majors) {//找blktrans_majors链表里的list_head结构体struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);tr->add_mtd(tr, mtd);//执行mtd_blktrans_ops结构体add_mtd()}}

从上面的代码和注释得出:块设备的add()是查找blktrans_majors链表,然后执行mtd_blktrans_ops结构体的add_mtd()

4.7.2 我们搜索blktrans_majors链表,看看mtd_blktrans_ops结构体在哪里添加进去的

找到该链表在register_mtd_blktrans()函数中:


int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
{... ...ret = register_blkdev(tr->major, tr->name);//注册块设备... ...tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);//分配一个请求队列... ...list_add(&tr->list, &blktrans_majors);//将tr->list添加到blktrans_majors链表... ...
}

继续搜索register_mtd_blktrans(),如下图,找到被drivers/mtd/Mtdblock.c、Mtdblock_ro.c调用

4.7.3 我们进入drivers/mtd/Mtdblock.c函数中,如下图所示

找到执行mtd_blktrans_ops结构体的add_mtd()函数,就是上图的mtdblock_add_mtd()函数

在mtdblock_add_mtd()函数中最终会调用add_blktrans_dev()

4.7.4 add_mtd_blktrans_dev()函数如下所示:


int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
{... ...gd = alloc_disk(1 << tr->part_bits);//分配一个gendisk结构体... ...gd->major = tr->major;//设置gendisk的主设备号gd->first_minor = (new->devnum) << tr->part_bits;//设置gendisk的起始此设备号gd->fops = &mtd_blktrans_ops;//设置操作函数... ...gd->queue = tr->blkcore_priv->rq;//设置请求队列... ...add_disk(gd);//向内核注册gendisk结构体return 0;
}

总结出:

mtd_blkdevs()块设备的入口函数 将 blktrans_notifier添加到mtd_notifiers链表中,并创建设备,请求队列。

然后在add_mtd_device()函数中,当查找到有blktrans_notifier时,就调用blktrans_notifier->add()来分配设置注册结构体

5、显然在内核中,mtd已经帮我们做了整个框架,而我们的nand flash驱动只需要以下几步即可:

1)设置mtd_info结构体成员

2)设置nand_chip结构体成员

3)设置硬件相关(设置nand控制器时序等)

4)通过nand_scan()来扫描nand flash

5)通过add_mtd_partitions()来添加分区,创建MTD字符/块设备

5.1 mtd_info结构体介绍:

主要是实现对nand flash的read()、write()、read_oob()、write_oob(),erase()等操作,属于软件部分,它会通过它的成员priv来找到对应的nand_chip结构体,来调用与硬件相关的操作

5.2nand_chip结构体介绍:

它是mtd_info结构体的priv成员,主要是对MTD设备中的nand flash硬件相关的描述。

当我们不设置nand_chip的成员时,以下的成员就会被mtd自动设为默认值,代码位于:nand_scan()->nand_scan_ident()->nand_set_defaults()

struct nand_chip {void  __iomem      *IO_ADDR_R;         /* 需要读出数据的nandflash地址 */void  __iomem      *IO_ADDR_W;        /* 需要写入数据的nandflash地址 */ /* 从芯片中读一个字节 */uint8_t    (*read_byte)(struct mtd_info *mtd);           /* 从芯片中读一个字 */u16         (*read_word)(struct mtd_info *mtd);         /* 将缓冲区内容写入nandflash地址, len:数据长度*/void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); /* 读nandflash地址至缓冲区, len:数据长度   */void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);/* 验证芯片和写入缓冲区中的数据 */int          (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);/* 选中芯片,当chip==0表示选中,chip==-1时表示取消选中 */void (*select_chip)(struct mtd_info *mtd, int chip);/* 检测是否有坏块 */int          (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);/* 标记坏块 */int          (*block_markbad)(struct mtd_info *mtd, loff_t ofs);/* 命令、地址控制函数 ,  dat :要传输的命令/地址 *//*当ctrl的bit[1]==1: 表示要发送的dat是命令bit[2]==1: 表示要发送的dat是地址bit[0]==1:表示使能nand , ==0:表示禁止nand具体可以参考内核的nand_command_lp()函数,它会调用这个cmd_crtl函数实现功能*/void (*cmd_ctrl)(struct mtd_info *mtd, int dat,unsigned int ctrl);/* 设备是否就绪,当该函数返回的RnB引脚的数据等于1,表示nandflash已就绪 */int (*dev_ready)(struct mtd_info *mtd);/* 实现命令发送,最终调用nand_chip -> cmd_ctrl来实现  */void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);/*等待函数,通过nand_chip ->dev_ready来等待nandflash是否就绪 */int          (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);/* 擦除命令的处理 */void (*erase_cmd)(struct mtd_info *mtd, int page);/* 扫描坏块 */int          (*scan_bbt)(struct mtd_info *mtd);int          (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);/* 写一页 */int          (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,const uint8_t *buf, int page, int cached, int raw);int          chip_delay;                   /* 由板决定的延迟时间 *//* 与具体的NAND芯片相关的一些选项,默认为8位宽nand,比如设置为NAND_BUSWIDTH_16,表示nand的总线宽为16 */unsigned int   options; /* 用位表示的NAND芯片的page大小,如某片NAND芯片* 的一个page有512个字节,那么page_shift就是9*/int          page_shift;/* 用位表示的NAND芯片的每次可擦除的大小,如某片NAND芯片每次可* 擦除16K字节(通常就是一个block的大小),那么phys_erase_shift就是14*/int          phys_erase_shift;/* 用位表示的bad block table的大小,通常一个bbt占用一个block,* 所以bbt_erase_shift通常与phys_erase_shift相等*/int          bbt_erase_shift;/* 用位表示的NAND芯片的容量 */int          chip_shift;/* NADN FLASH芯片的数量 */int          numchips;/* NAND芯片的大小 */uint64_t chipsize;int          pagemask;int          pagebuf;int          subpagesize;uint8_t    cellinfo;int          badblockpos;nand_state_t   state;uint8_t           *oob_poi;struct nand_hw_control  *controller;struct nand_ecclayout   *ecclayout;     /* ECC布局 */
/* ECC校验结构体,若不设置, ecc.mode默认为NAND_ECC_NONE(无ECC校验) */
/*可以为硬件ECC和软件ECC校验,比如:设置ecc.mode=NAND_ECC_SOFT(软件ECC校验)*/struct nand_ecc_ctrl ecc;      struct nand_buffers *buffers;struct nand_hw_control hwcontrol;struct mtd_oob_ops ops;uint8_t           *bbt;struct nand_bbt_descr   *bbt_td;struct nand_bbt_descr   *bbt_md;struct nand_bbt_descr   *badblock_pattern;void        *priv;
};

5.3 本节驱动我们需要设置nand_chip的成员如下:
IO_ADDR_R(提供读数据)

IO_ADDR_W(提供写数据)

select_chip(提供片选使能/禁止)

cmd_ctrl(提供写命令/地址)

dev_ready(提供nand flash的RnB引脚,来判断是否就绪)

ecc.mode(设置ECC为硬件校验/软件校验)

其它成员会通过nand_scan()->nand_scan_ident()->nand_set_defaults()来设置为默认值

6、接下来我们就来写nand_flash块设备驱动

参考:drivers/mtd/nand/at91_nand.c

drivers/mtd/nand/s3c2410.c

6.1 本节需要用到的函数如下所示:

int nand_scan(struct mtd_info *mtd, int maxchips);   //扫描nandflash,扫描成功返回0int add_mtd_partitions(struct mtd_info *master,const struct mtd_partition *parts,int nbparts);
//将nandflash分成nbparts个分区,会创建多个MTD字符/块设备,成功返回0
//master:就是要创建的mtd设备
//parts:分区信息的数组,它的结构体是mtd_partition
//nbparts:要创建分区的个数,比如上图,那么就等于4int del_mtd_partitions(struct mtd_info *master);
//卸载分区,并会卸载MTD字符/块设备

6.2 在init入口函数中

1)通过kzalloc()来分配结构体:mtd_info和nand_chip

2)通过ioremap()来分配获取nand flash 寄存器虚拟地址

3)设置mtd_info结构体成员

4)设置nand_chip结构体成员

5)设置硬件相关

5.1)通过clk_get()和clk_enable()来使能nand flash时钟

5.2)设置时序

5.3)关闭片选,并开启nand flash控制器

6)通过nand_scan()来扫描nand flash

7)通过add_mtd_partitions()来添加分区,创建MTD字符/块设置

6.3 在exit入口函数中

1)卸载分区,卸载字符/块设备

2)释放mtd

3)释放nand flash寄存器

4)释放nand_chip

驱动代码如下:


/* 参考* drivers\mtd\nand\s3c2410.c* drivers\mtd\nand\at91_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 s3c_nand_regs {unsigned long nfconf  ;   0x4E000000unsigned 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_chip *s3c_nand;
static struct mtd_info *s3c_mtd;
static struct s3c_nand_regs *s3c_nand_regs;//nand寄存器//分区,在common-smdk.c里
static struct mtd_partition s3c_nand_part[] = {[0] = {.name   = "bootloader",//分区名.size   = 0x00040000,//分区大小.offset  = 0,//分区偏移值},[1] = {.name   = "params",.offset = MTDPART_OFS_APPEND,//APPEND紧跟上一个的分区.size   = 0x00020000,},[2] = {.name   = "kernel",.offset = MTDPART_OFS_APPEND,//APPEND紧跟上一个的分区.size   = 0x00200000,},[3] = {.name   = "root",.offset = MTDPART_OFS_APPEND,//APPEND紧跟上一个的分区.size   = MTDPART_SIZ_FULL,//剩下的所有空间}
};//片选
static void s3c2440_select_chip(struct mtd_info *mtd, int chipnr)
{if (chipnr == -1){/* 取消选中CE:NFCONT[1]设为1        */s3c_nand_regs->nfcont |= (1<<1);}else{/* 选中CE:NFCONT[1]设为0 */s3c_nand_regs->nfcont &= ~(1<<1);}
} //发命令或地址
static void s3c2440_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
{/* 使用ctrl分辨是命令还是地址,dat是数据值 */if (ctrl & NAND_CLE){/* 发命令:NFCOMD=ctrl     */s3c_nand_regs->nfcmd = dat;}else{/* 发地址:NFADDR=ctrl     */s3c_nand_regs->nfaddr = dat;}
}//判断状态
static int s3c2440_dev_ready(struct mtd_info *mtd)
{return (s3c_nand_regs->nfstat & (1<<0));
}static int s3c_nand_init(void)
{struct clk *clk;/* 1.分配一个nand_chip结构体 */s3c_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);//从0x4E000000开始映射s3c_nand_regs = ioremap(0x4E000000, sizeof(struct s3c_nand_regs));/* 2.设置nand_chip结构体 *//* 设置nand_chip是给nand_scan函数使用的,如果不知道怎么设置,先看nand_scan怎么使用 * 它应该提供:选中,发命令,发地址,发数据,读数据,判断状态的功能*///默认的default sekect函数不适合我们使用,所以我们自己重新设置s3c_nand->select_chip = s3c2440_select_chip;  //选中s3c_nand->cmd_ctrl    = s3c2440_cmd_ctrl;       //发命令/地址s3c_nand->IO_ADDR_R = &s3c_nand_regs->nfdata;    //读数据s3c_nand->IO_ADDR_W = &s3c_nand_regs->nfdata;   //写数据s3c_nand->dev_ready = s3c2440_dev_ready;       //判断状态s3c_nand->ecc.mode = NAND_ECC_SOFT;   /* enable ECC *///设置软件ECC/* 3.硬件相关设置:根据NAND FLASH的手册设置时间参数 */   /* 使能NAND FLASH控制器的时钟,总开关 */clk = clk_get(NULL, "nand");clk_enable(clk);  /* CLKCON'bit[4] *//* HCLK=100MHz * TACLS:发出CLE/ALE之后多长时间才发出nWE信号,从NAND手册可知CLE/ALE与nWE可以同时发出,所以TACLS=0* TWRPH0:nWE的脉冲宽度,HCLK x (TWRPH0 + 1),从NAND手册可知,它要>=12ns,所以TWRPH0>=1* TWRPH1:nWE变为高电平后多长时间CLE/ALE才能变为低电平,从NAND手册可知它要>=5ns,所以TWRPH1>=0*/
#define TACLS  0
#define TWRPH0 1
#define TWRPH1 0s3c_nand_regs->nfconf = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);/* NFCONT:*bit1-设为1,取消片选 *bit0-设为1,使能NAND FLASH控制器,开关*/s3c_nand_regs->nfcont = (1<<1) | (1<<0);/* 4.nand_scan扫描 */s3c_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);s3c_mtd->owner = THIS_MODULE;s3c_mtd->priv = s3c_nand;//私有数据指向nand_chipnand_scan(s3c_mtd, 1);/* 识别NAND FLASH,构造mtd_info(mtd_info有读/写/擦除的函数) *//* 5.add_mtd_partitions 增加mtd分区 */add_mtd_partitions(s3c_mtd, s3c_nand_part, 4);//如果想把整个flash,只有一个分区//add_mtd_device(s3c_mtd);return 0;
}static void s3c_nand_exit(void)
{del_mtd_partitions(s3c_mtd);kfree(s3c_mtd);iounmap(s3c_nand_regs); kfree(s3c_nand);
}module_init(s3c_nand_init);
module_exit(s3c_nand_exit);MODULE_LICENSE("GPL");

7、编译启动内核

7.1 重新设置编译内核(去掉默认的nand flash驱动)

make menuconfig,进入menu菜单重新设置内核参数,去掉内核自带的NAND  FLASH驱动:

进入->Device Drivers->Memory Technology Device (MTD)support->NAND Device Support

< >   NAND Flash support for S3C2410/S3C2440 SoC

7.2 然后 make uImage 编译内核

7.3 将新的nand flash驱动模块(s3c_nand.ko)和在arch/arm/boot/uImage(uImage)拷贝到nfs文件系统目录中(/work/nfsroot/first_fs)

cp arch/arm/boot/uImage /work/nfsroot/uImage_nonand

cp s3c_nand.ko /work/nfsroot/first_fs

PS:因为之前的根文件系统在Nand Flash上,现在把NAND FLASH驱动去掉了,所以使用NFS(网络文件系统 first_fs)作为根文件系统。

7.4 先进入uboot,设置uboot启动参数(参考  Documentation/nfsroot.txt,它会告诉你怎样设置启动参数)

nfsroot=[<server-ip>:](服务器ip)<root-dir>(根文件目录)[,<nfs-options>]

ip=<client-ip>(客户端ip):<server-ip>(服务器ip):<gw-ip>(网关):<netmask>(子网掩码):<hostname>(主机名):<device>(设备):<autoconf>(自动配置)


set bootargs console=ttySAC0 root=/dev/nfs nfsroot=192.168.1.3:/work/nfsroot/first_fs ip=192.168.1.17:192.168.1.3:192.168.1.255:255.255.255.0::eth0:off  (设置uboot启动参数)save  (保存)nfs 30000000 192.168.1.3:/work/nfsroot/uImage_nonand (装载新内核)bootm 30000000  (启动)

8、挂载nand flash 驱动:insmod s3c_nand.ko

8.1 如下图,可以看到共4个分区:bootloader、params、kernel、root

刚好对应程序中的my_nand_part数组里面的分区信息

8.2 如下图,可以看到/dev下共创建了4个MTD块设备(mtdblock%d),4个MTD字符设备(mtd%d、mtd%dro只读)

8.3 如下图,使用cat /proc/partitions,可以看到分区信息

其中blocks表示分区的容量。

9、使用mount来挂载mtd块设备:mount /dev/mtdblock3 /mnt

10、mtd-util格式化(擦除),使用mtd_util工具擦除mtdblock3(使用nand之前最好擦除一次)

因为flash的特性如下:
写入,只能把数据(bit)从1改为0;擦除,只能把所有数据(bit)从0改为1.

所以,要想写入数据之前必须先擦除。因为flash只能写0,写1时其实是保持原来的状态。

10.1 使用mtd_util工具步骤如下:

tar -xjf mtd-utils-05.07.23.tar.bz2           //解压mtd-util工具
cd mtd-utils-05.07.23/util /                   //进入util目录
vi Makefile                                     //修改交叉编译改为: CROSS=arm-linux-
make                                          //编译,生成flashcp 、flash_erase、flash_eraseall等命令
cp  flash_erase  flash_eraseall  /work/nfsroot/first_fs/bin   //复制命令

10.2 mtd_util工具的常用命令介绍

命令:flashcp

作用:copy数据到flash中

实例:

./flashcp fs.yaffs2 /dev/mtd0    //将文件系统yaffs2复制到mtd0中

命令:flash_erase

常用参数:

-j    使用jiffs2来格式化分区

-q    不打印过程信息

作用:擦除某个分区的指定范围(其中指定位置必须以0x20000(128K)为倍数)

实例:

./ flash erase    /dev/mtd0 0x20000 5    //擦除mtd0从0x20000开始的5块数据,128K/块

命令:flash_eraseall

常用参数:
-j    使用jiffs2来格式化分区(对于nor flash才加该参数)

-q    不打印过程信息

作用:擦除整个分区的内容

实例:

./flash_eraseall -q /dev/mtd0    //擦除mtd0,并不打印过程信息

10.3 为什么这里的实例都是对mtd字符设备进行操作,而不是mtdblock块设备?

因为每个分区的字符设备,其实就是对应着每个分区设备。即/dev/mtd3对应/dev/mtdclock

flash_eraseall,flash_erase那些命令是以ioctl等基础而实现,而块设备不支持ioctl,只有字符设备支持

10.4 使用flash_eraseall来擦除分区3mtdblock3

umount /mnt                                             //擦除之前需要使用umount mnt来取消之前的挂载
./flash_eraseall  /dev/mtd3                             //擦除mtd3
mount -t yaffs /dev/mtdblock3 /mnt/                     //使用yaffs类型来挂载mtdblock3块设备//因为当前的mtdblock3为空,mount命令无法自动获取mtdblock3的文件类型

S3C2440 Nand Flash驱动(分析MTD层并制作NAND驱动)(二十三)相关推荐

  1. 24.Linux-Nand Flash驱动(分析MTD层并制作NAND驱动)

    1.本节使用的nand flash型号为K9F2G08U0M,它的命令如下: 1.1我们以上图的read id(读ID)为例,它的时序图如下: 首先需要使能CE片选 1)使能CLE 2)发送0X90命 ...

  2. MTD 设备驱动 和 NAND Flash 驱动程序分析。

    硬件环境: 飞凌OK6410,256MB DDR,2GB NAND Flash.   NAND Flash 型号:K9G8G08U9A   .     分析源码:Linux 2.6.36.2 内核源码 ...

  3. Linux NAND FLASH驱动程序分析(mini2440)

    Linux NAND FLASH驱动程序分析(mini2440) 一.Linux-MTD Subsystem介绍 FLASH在嵌入式系统中是必不可少的,它是bootloader.linux内核和文件系 ...

  4. nand flash 经典 全面 ------如何编写Linux下Nand Flash驱动

    Crifan Li 摘要 本文先解释了Nand Flash相关的一些名词,再从Flash硬件机制开始,介绍到Nand Flash的常见的物理特性,且深入介绍了Nand Flash的一些高级功能,然后开 ...

  5. 【SemiDrive源码分析】【MailBox核间通信】46 - Android侧 RPMSG_IPCC_RPC驱动分析(下) 之 RPMSG_IPCC_RPC驱动初始化、数据收发流程分析

    [SemiDrive源码分析][MailBox核间通信]46 - Android侧 RPMSG_IPCC_RPC驱动分析(下) 之 RPMSG_IPCC_RPC驱动初始化.数据收发流程分析 三. rp ...

  6. Nand Flash原理分析与编程

    NAND Flash 在嵌入式系统中的地位与PC机上的硬盘是类似的.用于保存系统运行所必需的操作系统,应用程序,用户数据,运行过程中产生的各类数据,系统掉电后数据不会护丢失.现在的Flash主要有两咱 ...

  7. linux内核spi总线驱动分析,Linux下的SPI总线驱动(三)

    版权所有,转载请说明转自 原创作者:南京邮电大学  通信与信息系统专业 研二 魏清 五.SPI测试代码 对于SPI总线驱动,我们可以分为SPI控制设备驱动和SPI接口设备驱动.而作为驱动开发人员主要是 ...

  8. mtd驱动分析-硬件驱动层

    http://blog.chinaunix.net/uid-28236237-id-3989135.html 分析uboot中的nand flash.查找了一些资料,看了韦东山移植.也不是很懂,nan ...

  9. Linux MTD架构下的nand flash驱动详解

    转载自:http://blog.csdn.net/wang_zheng_kai/article/details/18988521 有了前面的基础(Nandflash详解:https://blog.cs ...

最新文章

  1. 【MATLAB】符号数学计算(四):符号表达式操作
  2. java B2B2C Springcloud电子商城系统-Ribbon设计原理
  3. PHP统计网站pv(访问量)
  4. apache 不解释php,apache-2.2 – Apache不解释.PHP文件
  5. 怎么查看电脑内存和配置_电脑内存条如何选择?老司机带你如何选择电脑内存条...
  6. Node.js 启动调试方式
  7. 图片去字工具_这些免费工具轻松提取图片中的文字,别再傻傻地手工去输了
  8. C#LeetCode刷题-并查集
  9. 小说搜索站快速搭建:2.内容页解析
  10. skinmagic对VC中程序窗口的换肤
  11. AWK中BEGIN和END的使用理解
  12. Python中对一个数组各个数进行累加(反差分计算) cumsum()函数
  13. 仿QQ计步器效果的实现
  14. 字节跳动打响「教育战」
  15. FSOS森林模拟优化模型学习笔记
  16. 如何用 VS Code remote 插件编辑远程 linux 服务器上的文件
  17. 抽象类和接口的相同点和不同点
  18. Array和Arrays
  19. 《上海市居住证》积分申请基本流程
  20. 佳佳GIS学习笔记2

热门文章

  1. dy极速版2-艳云脚本云控系统
  2. Left_Leaning RedBlack Tree
  3. 智能宠物喂食器方案设计PCBA控制板
  4. stm32+增量式pid+max6675 PWM温度控制
  5. python模拟正方教务系统登录
  6. 全球与中国先进暖通空调(HVAC)控制市场深度研究分析报告
  7. latex编辑器的基本使用
  8. asp.net 页面代码 if_代码适配对百度友好代码详解
  9. kubelet运行机制及架构分析
  10. 蓝桥杯软件类Java语言IDE(Eclipse)安装