1本节使用的nand flash的命令如下:
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上升沿脉冲
1.2nand 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要小写)
如下图所示,最终读取出0XEC 0XDA 0X10 0X95
刚好对应了我们nand flash手册里的数据(其中0XEC表示厂家ID, 0X。
2 接下来我们来参考自带的nand flash驱动,位于drivers/mtd/nand/s3c2410.c中
2.1 为什么nand在mtd目录下?
因为mtd(memory technology device 存储 技术设备 ) 是用于访问 memory 设备( ROM 、 flash )的Linux 的子系统。 MTD 的主要目的是为了使新的 memory 设备的驱动更加简单,为此它在硬件和上层之间提供了一个抽象的接口。
2.2 首先来看s3c2410.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平台设备驱动,也是说当与nandflash设备匹配时,就会调用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); //3.扫描nandflash
... ...
s3c2410_nand_add_partition(info, nmtd, sets); //4.调用add_mtd_partitions()来添加mtd分区
... ...
}
通过上面代码和注释,得出:驱动主要调用内核的nand_scan()函数,add_mtd_partitions()函数,来完成注册nandflash。
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_scan_tail()来构造mtd设备的成员(实现对nandflash的读,写,擦除等)
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); // 获取厂家ID,
dev_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) //匹配设备ID
{
type = &nand_flash_ids[i];
break;}
}
... ...
/* 3.4 匹配成功,便打印nandflash参数 */
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.1 add_mtd_partitions()函数原型如下:
int add_mtd_partitions(struct mtd_info *master, const struct mtd_partition *parts,int nbparts); //创建多个分区mtd设备
//函 数 成 员 介 绍 :
//master:就是要创建的mtd设备
//parts:分区信息的数组,它的结构体是mtd_partition,该结构体如下所示:
/*
struct mtd_partition {
char *name; //分区名,比如bootloader、params、kernel、root
u_int32_t size; //分区大小
u_int32_t offset; //分区所在的偏移值
u_int32_t mask_flags; //掩码标志
struct nand_ecclayout *ecclayout; //OOB布局
struct mtd_info **mtdp; //MTD的指针,不常用
};
*/
//nbparts:等于分区信息的数组个数,表示要创建分区的个数
</