首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >15.NAND FLASH驱动

15.NAND FLASH驱动

作者头像
嵌入式与Linux那些事
发布2021-05-20 15:25:07
2.3K0
发布2021-05-20 15:25:07
举报

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函数开始看起

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);
}

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

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
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也可以看下
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
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
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
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
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

在这里插入图片描述
在这里插入图片描述
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
//函数成员介绍:
//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
//创建一个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链表
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链表中

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结构体
static struct mtd_notifier notifier = {
	.add	= mtd_notify_add,
	.remove	= mtd_notify_remove,
};
13.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字符设备节点
}

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

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

14.先看字符设备的mtd_notify_add
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
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()函数中:
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
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()函数如下所示:
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
 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.完整驱动源码
/* 参考 
 * 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  ;
	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");
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-12-18 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、基本的问题
  • 二、NAND FLASH驱动程序层次分析
    • 1.S3c2410.c
      • 2.s3c2440_nand_probe
        • 3.mtd_info也可以看下
          • 4.s3c2410_nand_add_partition
            • 5.nand_scan
              • 6.nand_scan_ident
                • 7.nand_get_flash_type
                  • 8.add_mtd_partitions
                    • 9.add_mtd_device
                      • 10.mtd_notifiers链表
                        • 11.register_mtd_user
                          • 12.notifier结构体
                            • 13.mtd_nofify_add
                              • 14.先看字符设备的mtd_notify_add
                                • 15.再看块设备的blktrans_notify_add
                                  • 15.搜索blktrans_majors链表,看看mtd_blktrans_ops结构体在哪里添加进去的找到该链表在register_mtd_blktrans()函数中:
                                    • 16.mtdblock.c
                                      • 17.add_mtd_blktrans_dev()函数如下所示:
                                        • 18.mtd_blkdevs.c
                                          • 19.总结
                                          • 三、写程序
                                            • 1.参考内核的部分函数
                                              • 2.参考手册内容
                                                • 3.完整驱动源码
                                                领券
                                                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档