15.NAND FLASH驱动

2021-05-20 15:25:07 浏览数 (1)

NAND FLASH 原理以及操作详见:https://blog.csdn.net/qq_16933601/article/details/100001443

一、基本的问题

NAND FLASH是一个存储芯片 那么: 这样的操作很合理"读地址A的数据,把数据B写到地址A"

问1. 原理图上NAND FLASH和S3C2440之间只有数据线, 怎么传输地址? 答1.在DATA0~DATA7上既传输数据,又传输地址 当ALE为高电平时传输的是地址,

问2. 从NAND FLASH芯片手册可知,要操作NAND FLASH需要先发出命令 怎么传入命令? 答2.在DATA0~DATA7上既传输数据,又传输地址,也传输命令 当ALE为高电平时传输的是地址, 当CLE为高电平时传输的是命令 当ALE和CLE都为低电平时传输的是数据

问3. 数据线既接到NAND FLASH,也接到NOR FLASH,还接到SDRAM、DM9000等等 那么怎么避免干扰? 答3. 这些设备,要访问之必须"选中", 没有选中的芯片不会工作,相当于没接一样

问4. 假设烧写NAND FLASH,把命令、地址、数据发给它之后, NAND FLASH肯定不可能瞬间完成烧写的, 怎么判断烧写完成? 答4. 通过状态引脚RnB来判断:它为高电平表示就绪,它为低电平表示正忙

问5. 怎么操作NAND FLASH呢? 答5. 根据NAND FLASH的芯片手册,一般的过程是: 发出命令 发出地址 发出数据/读数据

     NAND FLASH              S3C2440 发命令    选中芯片       CLE设为高电平            NFCMMD=命令值       在DATA0~DATA7上输出命令值       发出一个写脉冲

发地址   选中芯片                NFADDR=地址值       ALE设为高电平       在DATA0~DATA7上输出地址值       发出一个写脉冲

发数据    选中芯片                NFDATA=数据值       ALE,CLE设为低电平       在DATA0~DATA7上输出数据值       发出一个写脉冲

读数据    选中芯片                val=NFDATA       发出读脉冲       读DATA0~DATA7的数据

二、NAND FLASH驱动程序层次分析

1.S3c2410.c

从init函数开始看起

代码语言:javascript复制
static int __init s3c2410_nand_init(void)
{
	printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronicsn");

	platform_driver_register(&s3c2412_nand_driver);//注册平台
	platform_driver_register(&s3c2440_nand_driver);
	return platform_driver_register(&s3c2410_nand_driver);
}

看下s3c2440_nand_driver这个平台有哪些内容

代码语言:javascript复制
static struct platform_driver s3c2440_nand_driver = {
	.probe		= s3c2440_nand_probe,//一如既往的probe函数。是:调用probe函数
	.remove		= s3c2410_nand_remove,
	.suspend	= s3c24xx_nand_suspend,
	.resume		= s3c24xx_nand_resume,
	.driver		= {
		.name	= "s3c2440-nand",//是否同名设备
		.owner	= THIS_MODULE,
	},
};

当与nand flash设备匹配时,就会调用s3c2440_nand_driver->probe()来初始化

2.s3c2440_nand_probe
代码语言:javascript复制
static int s3c24xx_nand_probe(struct platform_device *pdev,
			      enum s3c_cpu_type cpu_type)
{
	struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
	struct s3c2410_nand_info *info;//s3c2410 nand flash状态结构体 包括 nand 时钟 nand控制器等
	struct s3c2410_nand_mtd *nmtd;//控制器结构体和mtd的信息
	struct s3c2410_nand_set *sets;//不同的mtd注册的nand 芯片
	struct resource *res;
	int err = 0;
	int size;
	int nr_sets;
	int setno;
	.................................
	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
	if (nmtd->scan_res == 0) {
	s3c2410_nand_add_partition(info, nmtd, sets);//添加分区mtd分区
		}

通过上面代码和注释,得出:驱动主要调用内核的nand_scan()函数,add_mtd_partitions()函数,来完成注册nand flash 关于MTD设备的解释https://blog.csdn.net/u013562393/article/details/50868033

3.mtd_info也可以看下
代码语言:javascript复制
struct mtd_info {
	u_char type;	     /* MTD类型,包括MTD_NORFLASH,MTD_NANDFLASH等(可参考mtd-abi.h) */
	uint32_t flags;	     /* MTD属性标志,MTD_WRITEABLE,MTD_NO_ERASE等(可参考mtd-abi.h) */
	uint64_t size;	     /* mtd设备的大小 */
	uint32_t erasesize;	 /* MTD设备的擦除单元大小,对于NandFlash来说就是Block的大小 */
	uint32_t writesize;	 /* 写大小, 对于norFlash是字节,对nandFlash为一页 */
	uint32_t oobsize;    /* OOB字节数 */
	uint32_t oobavail;   /* 可用的OOB字节数 */
	unsigned int erasesize_shift;	/* 默认为0,不重要 */
	unsigned int writesize_shift;	/* 默认为0,不重要 */
	unsigned int erasesize_mask;	/* 默认为1,不重要 */
	unsigned int writesize_mask;	/* 默认为1,不重要 */
	const char *name;				/* 名字,   不重要*/
	int index;						/* 索引号,不重要 */
	int numeraseregions;			/* 通常为1 */
	struct mtd_erase_region_info *eraseregions;	/* 可变擦除区域 */
	
	void *priv;		/* 设备私有数据指针,对于NandFlash来说指nand_chip结构体 */
	struct module *owner;	/* 一般设置为THIS_MODULE */
	
	/* 擦除函数 */
	int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
 
	/* 读写flash函数 */
	int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
	int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
 
	/* 带oob读写Flash函数 */
	int (*read_oob) (struct mtd_info *mtd, loff_t from,
			 struct mtd_oob_ops *ops);
	int (*write_oob) (struct mtd_info *mtd, loff_t to,
			 struct mtd_oob_ops *ops);
 
	int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
	int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
	int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
	int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
	int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
	int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);
 
	int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
	int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
	/* Sync */
	void (*sync) (struct mtd_info *mtd);
 
	/* Chip-supported device locking */
	int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
	int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
 
	/* 电源管理函数 */
	int (*suspend) (struct mtd_info *mtd);
	void (*resume) (struct mtd_info *mtd);
 
	/* 坏块管理函数 */
	int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
	int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
 
	void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
	unsigned long (*get_unmapped_area) (struct mtd_info *mtd,
					    unsigned long len,
					    unsigned long offset,
					    unsigned long flags);
	struct backing_dev_info *backing_dev_info;
	struct notifier_block reboot_notifier;  /* default mode before reboot */
 
	/* ECC status information */
	struct mtd_ecc_stats ecc_stats;
	int subpage_sft;
	struct device dev;
	int usecount;
	int (*get_device) (struct mtd_info *mtd);
	void (*put_device) (struct mtd_info *mtd);
};
4.s3c2410_nand_add_partition
代码语言:javascript复制
static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
				      struct s3c2410_nand_mtd *mtd,
				      struct s3c2410_nand_set *set)
{
	if (set == NULL)
		return add_mtd_device(&mtd->mtd);//添加分区

	if (set->nr_partitions > 0 && set->partitions != NULL) {
		return add_mtd_partitions(&mtd->mtd, set->partitions, set->nr_partitions);
	}
5.nand_scan
代码语言:javascript复制
int nand_scan(struct mtd_info *mtd, int maxchips)
{
	int ret;

	/* Many callers got this wrong, so check for it for a while... */
	if (!mtd->owner && caller_is_module()) {
		printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!n");
		BUG();
	}

	ret = nand_scan_ident(mtd, maxchips);//识别
	if (!ret)
		ret = nand_scan_tail(mtd);//尾部
	return ret;
}

nand_scan会调用nand_scan()->nand_scan_ident()->nand_get_flash_type()来获取flash存储器的类型 以及nand_scan()->nand_scan_ident()->nand_tail()来构造mtd设备的成员(实现对nand flash的读,写,擦除等)

6.nand_scan_ident
代码语言:javascript复制
int nand_scan_ident(struct mtd_info *mtd, int maxchips)
{
	int i, busw, nand_maf_id;
	struct nand_chip *chip = mtd->priv;
	struct nand_flash_dev *type;

	/* Get buswidth to select the correct functions */
	busw = chip->options & NAND_BUSWIDTH_16;
	/* Set the default functions */
	nand_set_defaults(chip, busw);//设置默认函数
	/* Read the flash type */
	type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id);//获得flash类型
7.nand_get_flash_type
代码语言:javascript复制
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;

	/* Select the device */
	chip->select_chip(mtd, 0);调用nand_chip结构体的成员select_chip使能flash片选


	/* Send the command for reading device ID */
	chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);//发出命令读ID  NAND_CMD_READID 90 最后数据保存在mtd结构体里

	/* Read manufacturer and device IDs */
	*maf_id = chip->read_byte(mtd);//读厂家ID
	dev_id = chip->read_byte(mtd);//读设备ID

	/* Lookup the flash id */
	//for循环匹配nand_flash_ids[]数组,找到对应的nandflash设备ID信息
	for (i = 0; nand_flash_ids[i].name != NULL; i  ) {
		if (dev_id == nand_flash_ids[i].id) {
			type =  &nand_flash_ids[i];
			break;
		}
	}

从上面代码和注释得出,nand_chip结构体就是保存与硬件相关的函数(后面会讲这个结构体) nand_flash_ids数组 如下图所示,在芯片手册中,看到nand flash的设备ID=0xDA

代码语言:javascript复制
struct nand_flash_dev nand_flash_ids[] = {

#ifdef CONFIG_MTD_NAND_MUSEUM_IDS
	{"NAND 1MiB 5V 8-bit",		0x6e, 256, 1, 0x1000, 0},
	{"NAND 2MiB 5V 8-bit",		0x64, 256, 2, 0x1000, 0},
	{"NAND 4MiB 5V 8-bit",		0x6b, 512, 4, 0x2000, 0},
	{"NAND 1MiB 3,3V 8-bit",	0xe8, 256, 1, 0x1000, 0},
	{"NAND 1MiB 3,3V 8-bit",	0xec, 256, 1, 0x1000, 0},
	{"NAND 2MiB 3,3V 8-bit",	0xea, 256, 2, 0x1000, 0},
	{"NAND 4MiB 3,3V 8-bit",	0xd5, 512, 4, 0x2000, 0},
	{"NAND 4MiB 3,3V 8-bit",	0xe3, 512, 4, 0x2000, 0},
	{"NAND 4MiB 3,3V 8-bit",	0xe5, 512, 4, 0x2000, 0},
	{"NAND 8MiB 3,3V 8-bit",	0xd6, 512, 8, 0x2000, 0},

	{"NAND 8MiB 1,8V 8-bit",	0x39, 512, 8, 0x2000, 0},
	{"NAND 8MiB 3,3V 8-bit",	0xe6, 512, 8, 0x2000, 0},
	{"NAND 8MiB 1,8V 16-bit",	0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16},
	{"NAND 8MiB 3,3V 16-bit",	0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16},
	................................
		/* 2 Gigabit */
	{"NAND 256MiB 1,8V 8-bit",	0xAA, 0, 256, 0, LP_OPTIONS},
	{"NAND 256MiB 3,3V 8-bit",	0xDA, 0, 256, 0, LP_OPTIONS},//与手册信息相同
	{"NAND 256MiB 1,8V 16-bit",	0xBA, 0, 256, 0, LP_OPTIONS16},
	{"NAND 256MiB 3,3V 16-bit",	0xCA, 0, 256, 0, LP_OPTIONS16},
8.add_mtd_partitions
代码语言:javascript复制
//函数成员介绍:
//master:就是要创建的mtd设备
//parts:分区信息的数组,它的结构体是mtd_partition,该结构体如下所示:
int add_mtd_partitions(struct mtd_info *master,
		       const struct mtd_partition *parts,
		       int nbparts)
{
	struct mtd_part *slave;
	u_int32_t cur_offset = 0;
	int i;
	/* register our partition */
    add_mtd_device(&slave->mtd);
/*
    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的指针,不常用
    };
}
*/
9.add_mtd_device
代码语言:javascript复制
//创建一个mtd设备
int add_mtd_device(struct mtd_info *mtd)
{
	int i;

	BUG_ON(mtd->writesize == 0);
	mutex_lock(&mtd_table_mutex);

	for (i=0; i < MAX_MTD_DEVICES; i  )
		if (!mtd_table[i]) {
			struct list_head *this;
.......................................
	/* No need to get a refcount on the module containing
			   the notifier, since we hold the mtd_table_mutex */
			   //找mtd_notifiers链表里的list_head结构体
			list_for_each(this, &mtd_notifiers) {
			通过list_head找到struct mtd_notifier *not
				struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
				//最后调用mtd_notifier的add()函数
				not->add(mtd);
			}
10.mtd_notifiers链表
代码语言:javascript复制
void register_mtd_user (struct mtd_notifier *new)
{
	int i;

	mutex_lock(&mtd_table_mutex);

	list_add(&new->list, &mtd_notifiers);//注册mtd_notifiers

 	__module_get(THIS_MODULE);

	for (i=0; i< MAX_MTD_DEVICES; i  )
		if (mtd_table[i])
			new->add(mtd_table[i]);

	mutex_unlock(&mtd_table_mutex);
}
11.register_mtd_user

list_head在register_mtd_user()里放到mtd_notifiers链表中

代码语言:javascript复制
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驱动

12.notifier结构体
代码语言:javascript复制
static struct mtd_notifier notifier = {
	.add	= mtd_notify_add,
	.remove	= mtd_notify_remove,
};
13.mtd_nofify_add
代码语言:javascript复制
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字符设备节点
}

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

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

14.先看字符设备的mtd_notify_add
代码语言:javascript复制
static void mtd_notify_add(struct mtd_info* mtd)
{
	if (!mtd)
		return;

	class_device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
			    NULL, "mtd%d", mtd->index);

	class_device_create(mtd_class, NULL,
			    MKDEV(MTD_CHAR_MAJOR, mtd->index*2 1),
			    NULL, "mtd%dro", mtd->index);
}
15.再看块设备的blktrans_notify_add
代码语言:javascript复制
static void blktrans_notify_add(struct mtd_info *mtd)
{
	struct list_head *this;

	if (mtd->type == MTD_ABSENT)
		return;

	//链表里面的每个成员调用 add
	list_for_each(this, &blktrans_majors) {
		struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);

		tr->add_mtd(tr, mtd);
	}

}
15.搜索blktrans_majors链表,看看mtd_blktrans_ops结构体在哪里添加进去的找到该链表在register_mtd_blktrans()函数中:
代码语言:javascript复制
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

16.mtdblock.c
代码语言:javascript复制
static struct mtd_blktrans_ops mtdblock_tr = {
	.name		= "mtdblock",
	.major		= 31,
	.part_bits	= 0,
	.blksize 	= 512,
	.open		= mtdblock_open,
	.flush		= mtdblock_flush,
	.release	= mtdblock_release,
	.readsect	= mtdblock_readsect,
	.writesect	= mtdblock_writesect,
	.add_mtd	= mtdblock_add_mtd,
	.remove_dev	= mtdblock_remove_dev,
	.owner		= THIS_MODULE,
};

找到执行mtd_blktrans_ops结构体的add_mtd()函数,就是上图的mtdblock_add_mtd()函数 在mtdblock_add_mtd()函数中最终会调用add_blktrans_dev()

17.add_mtd_blktrans_dev()函数如下所示:
代码语言:javascript复制
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;
}
18.mtd_blkdevs.c
代码语言:javascript复制
 alloc_disk
 gd->queue = tr->blkcore_priv->rq; // tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
 add_disk       

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

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

19.总结

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

1)设置mtd_info结构体成员

2)设置nand_chip结构体成员

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

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

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

三、写程序

1.参考内核的部分函数
2.参考手册内容

2440手册

nand手册

3.完整驱动源码
代码语言:javascript复制
/* 参考 
 * driversmtdnands3c2410.c
 * driversmtdnandat91_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  ;
	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_chip *s3c_nand;
static struct mtd_info *s3c_mtd;
static struct s3c_nand_regs *s3c_nand_regs;

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  (struct mtd_info *mtd, int chipnr)
{
	if (chipnr == -1)
	{
		/* 取消选中: NFCONT[1]设为1 */
		s3c_nand_regs->nfcont |= (1<<1);		
	}
	else
	{
		/* 选中: NFCONT[1]设为0 */
		s3c_nand_regs->nfcont &= ~(1<<1);
	}
}

static void s3c2440_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
	if (ctrl & NAND_CLE)
	{
		/* 发命令: NFCMMD=dat */
		s3c_nand_regs->nfcmd = dat;
	}
	else
	{
		/* 发地址: NFADDR=dat */
		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);

	s3c_nand_regs = ioremap(0x4E000000, sizeof(struct s3c_nand_regs));
	
	/* 2. 设置nand_chip */
	/* 设置nand_chip是给nand_scan函数使用的, 如果不知道怎么设置, 先看nand_scan怎么使用 
	 * 它应该提供:选中,发命令,发地址,发数据,读数据,判断状态的功能
	 */
	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;
	
	/* 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   0
	s3c_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_scan(s3c_mtd, 1);  /* 识别NAND FLASH, 构造mtd_info */
	
	/* 5. add_mtd_partitions */
	add_mtd_partitions(s3c_mtd, s3c_nand_parts, 4);
	
	//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");

0 人点赞