Nand Flash驱动程序分析

2022-05-08 16:13:42 浏览数 (1)

代码语言:javascript复制
/*Nand Flash驱动分析*/

/*首先: 市面上的开发板很多,Nand Flash差不多都一样。先说说Nand Flash的特性*/


/* 上图是OK6410开发板的Nand Flash原理图,从上图可知:
1. 数据线和地址线明显是公用的。因为只看见了DATA0-DATA7没看见地址线。作为一个存储芯片当然要写数据,读数据。当然需要地址线。
2. DATA0-DATA7在好多地址被使用了,那怎么区分当前是那个芯片。那当然要CSN2和CSN3来控制当前选中那个芯片,也就是让那个芯片工作。
3. FWEN和FREN是写使能信号,和读使能信息。来控制读写的操作
4. 当FCLE为高电平时传输的是命令, FALE为高电平时传输的是地址,当FCLE和FALE都为低电平时传输的是数据。(可以从Nand Flash芯片手册上获取到)


既然了解这么多,就该知道Nand Flash一般的工作流程了:
1. 先发命令,
2. 再发地址(可能需要发送好几个周期),
3. 发出数据或者读取数据

*/


/*其次,我们来分析三星公司自带的Nand Flash驱动程序。路径: drivers/mtd/nand/s3c2410.c*/

//老套路了还是平台驱动程序,既然是平台驱动程序,就有平台设备的存在
static int __init s3c2410_nand_init(void)
{
	printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronicsn");

	return platform_driver_register(&s3c24xx_nand_driver);
}


//平台设备与驱动想匹配时,就会调用probe函数

/* s3c24xx_nand_probe  //注释写的是多么的清楚
 *
 * called by device layer when it finds a device matching	//先是当发现设备后调用设备层,接着代码检测是否能分配足够的资源
 * one our driver can handled. This code checks to see if //然后调用Nand 层去寻找设备。
 * it can allocate all necessary resources then calls the //注释中出现了Device 层和 Nand层的概念,以后会说到
 * nand layer to look for devices
*/
static int s3c24xx_nand_probe(struct platform_device *pdev)
{
	struct s3c2410_nand_info *info;
	
	//分配info结构
	info = kzalloc(sizeof(*info), GFP_KERNEL);
	platform_set_drvdata(pdev, info);
	
	//设置时钟,使能时钟
	info->clk = clk_get(&pdev->dev, "nand");
	s3c2410_nand_clk_set_state(info, CLOCK_ENABLE);
	
	//初始化硬件
	s3c2410_nand_inithw(info);
	
	//初始化所有可能的芯片
	s3c2410_nand_init_chip
	
	//nand 寻找设备
	nand_scan_ident();
	
	//如果没有找到,
	if (nmtd->scan_res == 0) {
		s3c2410_nand_update_chip(info, nmtd);
		nand_scan_tail(&nmtd->mtd);//寻找设备
		s3c2410_nand_add_partition(info, nmtd, sets);//增加分区
	}
}

//寻找Nand Flash设备
int nand_scan_ident(struct mtd_info *mtd, int maxchips,struct nand_flash_dev *table)
{
	/* 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, //读取flash的类型
				&nand_maf_id, &nand_dev_id, table);

}

/*所谓设置默认函数,就是设置flash的读,写,发送命令,发送数据,等待等函数。*/
static void nand_set_defaults(struct nand_chip *chip, int busw)
{
	/* check for proper chip_delay setup, set 20us if not */
	if (!chip->chip_delay)	//如果没有设置延迟时间,那就设置
		chip->chip_delay = 20;

	/* check, if a user supplied command function given */
	if (chip->cmdfunc == NULL)  //如果没有设置发送命令的函数,那就需要我们自己设置
		chip->cmdfunc = nand_command;  

	/* check, if a user supplied wait function given */
	if (chip->waitfunc == NULL)  //如果没有设置等待函数,那就需要在驱动程序中设置
		chip->waitfunc = nand_wait;

	if (!chip->select_chip)
		chip->select_chip = nand_select_chip;  //片选函数设置
	if (!chip->read_byte)
		chip->read_byte = busw ? nand_read_byte16 : nand_read_byte; //读flash函数
	if (!chip->read_word)
		chip->read_word = nand_read_word;
	if (!chip->block_bad)
		chip->block_bad = nand_block_bad;
	if (!chip->block_markbad)
		chip->block_markbad = nand_default_block_markbad;
	if (!chip->write_buf)
		chip->write_buf = busw ? nand_write_buf16 : nand_write_buf; //写flash函数
	if (!chip->read_buf)
		chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
	if (!chip->verify_buf)
		chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
	if (!chip->scan_bbt)
		chip->scan_bbt = nand_default_bbt;
	}
}

/* 取得Flash和厂家id,寻找是否支持该设备
 * Get the flash and manufacturer id and lookup if the type is supported
 */
static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
						  struct nand_chip *chip,
						  int busw,
						  int *maf_id, int *dev_id,
						  struct nand_flash_dev *type)
{
		/* Select the device */
	chip->select_chip(mtd, 0); //先选中芯片

	/*
	 * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
	 * after power-up
	 */
	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);	//reset芯片

	/* Send the command for reading device ID */
	chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); //发送读取ID的命令90h,

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

s3c2410_nand_add_partition -> mtd_device_register ->add_mtd_partitions -> add_mtd_device->
{
	list_for_each_entry(not, &mtd_notifiers, list)//遍历mtd_notifiers中每个调用add函数
		not->add(mtd);	
}

//那mtd_notifiers在那里设置了?
void register_mtd_user (struct mtd_notifier *new)
{
	list_add(&new->list, &mtd_notifiers);//在这里设置了mtd_notifiers

	__module_get(THIS_MODULE);

	mtd_for_each_device(mtd)
		new->add(mtd);
}

//看看谁调用register_mtd_user函数

//一个文件在: drivers/mtd/mtdchar.c
register_mtd_user(&mtdchar_notifier);

//一个文件在: drivers/mtd/mtd_blkdevs.c
register_mtd_user(&blktrans_notifier);


//先看mtdchar_notifier中的add函数

static void mtd_notify_add(struct mtd_info *mtd)
{
}

//再看blktrans_notifier中的add函数
static void blktrans_notify_add(struct mtd_info *mtd)
{
	//遍历blktrans_majors链表,调用add_mtd函数
	list_for_each_entry(tr, &blktrans_majors, list)
		tr->add_mtd(tr, mtd);
}

//看blktrans_majors链表在那里设置?
int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
{
	list_add(&tr->list, &blktrans_majors); //此函数中有设置	
}

//init_mtdblock函数调用了register_mtd_blktrans函数
static int __init init_mtdblock(void)
{
	mutex_init(&mtdblks_lock);

	return register_mtd_blktrans(&mtdblock_tr);
}

//看mtdblock_tr结构中的add_mtd函数
static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
{
		
}
int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
{
	//穿件gendisk结构
	gd = alloc_disk(1 << tr->part_bits);
	
	//初始化gendisk
	new->disk = gd;
	gd->private_data = new;
	gd->major = tr->major;
	gd->first_minor = (new->devnum) << tr->part_bits;
	gd->fops = &mtd_blktrans_ops;
	
	set_capacity(gd, (new->size * tr->blksize) >> 9);

	//初始化请求队列
	new->rq = blk_init_queue(mtd_blktrans_request, &new->queue_lock);

	//注册gendisk
	add_disk(gd);
	
}

/*在这里可以发现,是不是块设备的基本操作等。 其实是内核帮我们已经做好了这些东西。
  这样做的好处是把Nand Flash相关的操作都抽象出来,放在nand层。 而把和硬件相关的,
  经常需要变化的留给设备层,而设备层就是由我们程序员编写,因为设备层的差别各异,
  很难抽象成统一的整体。
  
  所以我们编写块设备的话,只需要做设备层相关的操作,其余的操作内核已经帮我们做好了。
*/



/*上面的分析是对自带的程序分析: 那我们如何写驱动程序同时也能融合到内核为我们提供好的nand层*/

/*
	1. 分配一个nand_chip结构  
	2. 设置nand_chip结构
	   设置cmdfunc, waitfunc, select_chip等函数
	3. 硬件相关的代码
		 使能时钟, 设置时钟,选择芯片
	4. 使用nand_scan识别nand flash
	5. 添加分区(这样就会将nand flash驱动加到内核中)
*/

0 人点赞