代码语言: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驱动加到内核中)
*/