写块驱动程序的框架:
- 分配 gendisk 结构体,使用alloc_disk
- 设置:队列,属性
- 注册
Nandflash芯片操作:
1、NandFlash和S3c2440怎么硬件怎么连接?
DATA0 ~DATA7上既传输数据,又传输地址,也传输命令
当ALE为高电平时传输的是地址
当CLE为高电平时传输的是数据
当ALE和CLE都为地电平时传输的是数据
2、数据线既接到NAND FLASH也接到 NOR FLASH,还接到SDRAM,DM9000等等。怎么避免干扰?
这些设备在访问之前,必须选中,没有选中的芯片不会工作。
3、假设,烧写 NAND FLASH把命令、地址、数据发给它之后,NANDFLASH肯定不可能瞬间完成烧写的。怎么判断烧写完成?
通过状态因叫RnB来判断,它为高电平,表示ready,它为低电平表示busy。
4、怎么操作 NandFlash?
根据NandFlash的datasheet,思路为:
发出命令 --- 发出地址 ---- 发出数据/读数据
NANDFLASH | S3C2440 | |
发命令 | 选中芯片 CLE设为高电平 在DATA0~DATA8上输出命令值 发出一个写脉冲 | NFCMMD=命令 |
发地址 | 选中芯片 ALE设为高电平 在DATA0~DATA8上输出地址 发出一个写脉冲 | NFADDR=地址 |
发数据 | 选中芯片 ALE,CLE设为高电平 在DATA0~DATA8上输出数据 发出一个写脉冲 | NFDATA=数据 |
读数据 | 选中芯片 ALE,CLE设为高电平 读DATA0~DATA8的数据 | val = NFDATA |
裸奔:
读ID的方法(参照datasheet的时序)
选中 | NFCONT的bit1 设为0 | 0x4E000004 |
发出命令0X90 | NFCMMD = 0x90 | 0x4E000008 |
发出地址0X00 | NFADDR = 0x00 | 0x4E00000C |
读数据得到0XEC | val = NFDATA | 0x4E000010 |
都数据得到device_code | val = NFDATA | 0x4E000010 |
============================================================================================================
驱动思路:
1、分配nandchip结构体
2、设置这个结构体
3、硬件的相关操作
4、使用 nand_scan
5、add_mtd_partitions
1、分配一个nandchip结构体:
static struct nand_chip *s3c_nand;
s3c_nand = kzalloc(sizeof(struct nand_chip),GFP_KERNEL);
2、设置这个nand_chip结构体
关于怎么设置,可以通过察看nandscan这个方法使用了什么来反推需要怎么配置这个nand_chip结构体的
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;//ECC校验
对于 s3c2440_select_chip等操作函数内核是可以自动分配一个默认的函数的,但是这个默认你的函数是不能真正工作的。因此需要我们自己去定义。
s3c2440_select_chip、s3c2440_cmd_ctrl、s3c2440_dev_ready 涉及了硬件的相关操作,因此要配置下硬件。
这里的ecc.mode 是使用ecc校验功能。这是nand_flash的没一页的OOB区都会保存这位校验的ECC用于检验是否有位翻转。
3、硬件的相关操作:
首次还是要映射相关的寄存器,在这个驱动函数中,寄存器的定义:
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 ;
};
然后ioremap一下:
s3c_nand_regs = ioremap(0x4E000000,sizeof(struct s3c_nand_regs));
接下来完成s3c2440_select_chip、s3c2440_cmd_ctrl、s3c2440_dev_ready这三个函数.这三个函数的定义根据datasheet可作如下的定义:
s3c2440_select_chip
static void s3c2440_select_chip(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);
}
}
s3c2440_cmd_ctrl
static void s3c2440_cmd_ctrl(struct mtd_info *mtd, int data,unsigned int ctrl)
{
if (ctrl & NAND_CLE)
{
/*发命令
* NFCMMD = dat*/
s3c_nand_regs->nfcmd = data;
}
else
{
/*发数据
* NFADDR = dat*/
s3c_nand_regs->nfaddr = data;
}
}
s3c2440_dev_ready
static int s3c2440_dev_ready(struct mtd_info *mtd)
{
/*NFSTAT[0]
* */
return s3c_nand_regs->nfstat & (1<<0);
}
在硬件配置中,要注意一点就是时钟的配置,每个模块的时钟默认都是关闭的,因此如果我们要使用nandflash的主控制器,那么就要开启其clk:
struct clk *clk;
clk = clk_get(NULL,"nand");
clk_enable(clk); //clkcon的bit4
说到时钟,我们没有理由不去考虑时序问题,在s3c2440里面的的nandflash控制器的相关寄存器有这个配置:nfconf
*HCLOK = 100MHz 10ns
* TACLS:发出cle/ale之后多长时间才发出 nWE信号,从NAND手册可知cle/ale与nWE可以同时发出,所以TACLS值可以为0
* TWRPH0 nWE信号的脉冲宽度 从手册上: HCLK *(TWRPH0 + 1) >=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);
4、接下来使用nand_scan来识别这个nandflash。
这个函数的原型: int nand_scan (struct mtd_info *mtd, int max_chips);
因此我们还要定义一个mtd结构体,并且简单的设置它
static struct mtd_info *mtd;
mtd = kzalloc(sizeof(struct mtd_info),GFP_KERNEL);
mtd->owner = THIS_MODULE;
mtd->priv = s3c_nand;
max_chips 最多片数,我们只有1片,所以选1.
5、添加分区:
add_mtd_partitions(mtd,s3c_nand_parts,3);
函数原型为:
int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int);
在这个函数需要mtd_partition,来创建每个分区。分区如下:
static struct mtd_partition s3c_nand_parts[] = {
[0] = {
.name = "wangqi_Board_uboot",
.offset = 0x00000000,
.size = 0x00040000,
},
[1] = {
.name = "wangqi_Board_kernel",
.offset = 0x00200000,
.size = 0x00200000,
},
[2] = {
.name = "wangqi_Board_yaffs2",
.offset = 0x00400000,
.size = 0x0FB80000,
}
};
这样一个完整的nandflash驱动就做好了。
========================================================================================================
测试:
make menuconfig 在源码驱动中去掉nandflash的驱动程序。
-> Device Drivers
│ -> Memory Technology Device (MTD) support (MTD [=y])
│ -> NAND Device Support (MTD_NAND [=y])
文件系统需要挂载nfs网络文件系统。挂载方法:点击打开链接
insmod
ls -l /dev/mtd*
crw-rw---- 1 root root 90, 0 Jan 1 00:00 /dev/mtd0
crw-rw---- 1 root root 90, 1 Jan 1 00:00 /dev/mtd0ro
crw-rw---- 1 root root 90, 2 Jan 1 00:00 /dev/mtd1
crw-rw---- 1 root root 90, 3 Jan 1 00:00 /dev/mtd1ro
crw-rw---- 1 root root 90, 4 Jan 1 00:00 /dev/mtd2
crw-rw---- 1 root root 90, 5 Jan 1 00:00 /dev/mtd2ro
brw-rw---- 1 root root 31, 0 Jan 1 00:00 /dev/mtdblock0
brw-rw---- 1 root root 31, 1 Jan 1 00:00 /dev/mtdblock1
brw-rw---- 1 root root 31, 2 Jan 1 00:00 /dev/mtdblock2
完整源码:点击打开链接