jz2440 中使用的nandflash的型号是 K9F2G08U0M
图中可以看到这个nandflash的结构是:
1页 = (2k + 64) 字节
1块 = 64个页 = ( 2k + 64) x 64 = 128k + 4k 字节
1个设备 = 2048个块 = ...
对于某个字节的访问,地址的组成如上图,是分5个周期发送的,其中 Column是列,Row 是 [行/页]
操作nandflash的命令如下
2440 中带有 nandflash 控制器,如果想要读 ID,只需要以下几步
1)将寄存器NFCONT(0x4E000004)的bit1=0,来使能片选
2)写入寄存器NFCMMD(0x4E000008)=0X90,发送命令
3)写入寄存器NFADDR(0x4E00000C)=0X00,发送地址
4)while判断nRE(读使能)是否为低电平
5)读取寄存器NFDATA(0x4E000010),来读取数据
md 读 (md.l 读4字节 md.b 读1字节 )
mw 写(md.l 读4字节 md.b 读1字节 )
3.1)读 ID 的例子:
选中 NFCONT的bit1设为0 md.l 0x4E000004 1; mw.l 0x4E000004 1
发出命令0x90 NFCMMD=0x90 mw.b 0x4E000008 0x90
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00
读数据得到0xEC val=NFDATA md.b 0x4E000010 1
读数据得到device code val=NFDATA md.b 0x4E000010 1
0xda
退出读ID的状态 NFCMMD=0xff mw.b 0x4E000008 0xff
3.2)读内容: 读0地址的数据
使用UBOOT命令:
nand dump 0
Page 00000000 dump:
17 00 00 ea 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5
S3C2440 u-boot
选中 NFCONT的bit1设为0 md.l 0x4E000004 1; mw.l 0x4E000004 1
发出命令0x00 NFCMMD=0x00 mw.b 0x4E000008 0x00
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00
发出命令0x30 NFCMMD=0x30 mw.b 0x4E000008 0x30
读数据得到0x17 val=NFDATA md.b 0x4E000010 1
读数据得到0x00 val=NFDATA md.b 0x4E000010 1
读数据得到0x00 val=NFDATA md.b 0x4E000010 1
读数据得到0xea val=NFDATA md.b 0x4E000010 1
退出读状态 NFCMMD=0xff mw.b 0x4E000008 0xff
在内核中的一个有关2410的nandflash的例子是 drivers/mtd/nand/s3c2410.c,可以作为我们编写驱动的参考。
https://www.cnblogs.com/lifexy/p/7701181.html
内核mtd层提供了字符设备的操作接口和块设备的操作接口,在 /dev 下会有两个字符设备的节点,一个块设备的节点。内核把整个框架搭建好了,我们关心 nandflash 驱动我们需要做什么?
1)设置mtd_info结构体成员
2)设置nand_chip结构体成员
3)设置硬件相关(设置nand控制器时序等)
4)通过nand_scan()来扫描nandflash
5)通过add_mtd_partitions()来添加分区,创建MTD字符/块设备
一些函数:
int nand_scan(struct mtd_info *mtd, int maxchips); //扫描nandflash,扫描成功返回0
/* 将nandflash分成nbparts个分区,会创建多个MTD字符/块设备,成功返回0
* master:就是要创建的mtd设备
* parts:分区信息的数组,它的结构体是mtd_partition
* nbparts:要创建分区的个数,比如上图,那么就等于4
*/
int add_mtd_partitions(struct mtd_info *master,const struct mtd_partition *parts,int nbparts); // linux 2.xxx
int mtd_device_parse_register(struct mtd_info *mtd, const char **types,
struct mtd_part_parser_data *parser_data,
const struct mtd_partition *parts,
int nr_parts) // linux3.4.xxx
/* 卸载分区,并卸载MTD字符/块设备 */
int del_mtd_partitions(struct mtd_info *master); // linux 2.xxx
int mtd_device_unregister(struct mtd_info *master) // linux 3.4.xxx
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct mynand_regs {
unsigned long nfconf ; //0x4E000000
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 mynand_regs *my_regs; //nand寄存器
static struct mtd_info *my_mtd;
static struct nand_chip *mynand_chip;
static struct mtd_partition mynand_part[] = {
[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,
}
};
/*nand flash :CE */
static void mynand_select_chip(struct mtd_info *mtd, int chipnr)
{
if(chipnr==-1) //CE Disable
{
my_regs->nfcont|=(0x01<<1); //bit1置1
}
else //CE Enable
{
my_regs->nfcont&=~(0x01<<1); //bit1置0
}
}
/*命令/地址控制函数 */
static void mynand__cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
if (ctrl & NAND_CLE) //当前为command状态 ,
my_regs->nfcmd=dat;
else //当前为地址状态 , if (ctrl & NAND_ALE)
my_regs->nfaddr=dat;
}
/* nand flash 设备就绪函数(获取RnB引脚状态 */
static int mynand__device_ready(struct mtd_info *mtd)
{
return (my_regs->nfstat&0x01); //获取RnB状态,0:busy 1:ready
}
/*init入口函数*/
static int mynand_init(void)
{
struct clk *nand_clk;
int res;
/*1.分配结构体: mtd_info和nand_chip */
my_mtd=kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
mynand_chip=kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
/*2.获取nand flash 寄存器虚拟地址*/
my_regs=ioremap(0x4E000000, sizeof(struct mynand_regs));
/*3.设置mtd_info*/
my_mtd->owner=THIS_MODULE;
my_mtd->priv=mynand_chip; //私有数据
/*4.设置nand_chip*/
mynand_chip->IO_ADDR_R=&my_regs->nfdata; //设置读data
mynand_chip->IO_ADDR_W=&my_regs->nfdata; //设置写data
mynand_chip->select_chip=mynand_select_chip; //设置CE
mynand_chip->cmd_ctrl = mynand__cmd_ctrl; //设置写command/address
mynand_chip->dev_ready = mynand__device_ready; //设置RnB
mynand_chip->ecc.mode = NAND_ECC_SOFT; //设置软件ECC
/*5.设置硬件相关*/
/*5.1使能nand flash 时钟*/
nand_clk=clk_get(NULL,"nand");
clk_enable(nand_clk);
/*5.2设置时序*/
#define TACLS 0 //0nS
#define TWRPH0 1 //15nS
#define TWRPH1 0 //5nS
my_regs->nfconf = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
/*5.3 bit1:关闭片选, bit0:开启nand flash 控制器*/
my_regs->nfcont=(1<<1)|(1<<0);
/*6.扫描NAND*/
if (nand_scan(my_mtd, 1)) { // 1:表示只扫描一个nand flash 设备
res = -ENXIO;
goto out;
}
/*7.添加分区,创建字符/块设备*/
//res = add_mtd_partitions(my_mtd, mynand_part, 4);
//if(res)
//return 0;
res = mtd_device_parse_register(my_mtd, NULL, NULL, mynand_part, 4);
if(!res)
return 0;
out:
//del_mtd_partitions(my_mtd); //卸载分区,卸载字符/块设备
mtd_device_unregister(my_mtd);
kfree(my_mtd); //释放mtd
iounmap(my_regs); //释放nand flash寄存器
kfree(mynand_chip); //释放nand_chip
return 0;
}
/*exit出口函数*/
static void mynand_exit(void)
{
//del_mtd_partitions(my_mtd); //卸载分区,卸载字符/块设备
mtd_device_unregister(my_mtd);
kfree(my_mtd); //释放mtd
iounmap(my_regs); //释放nand flash寄存器
kfree(mynand_chip); //释放nand_chip
}
module_init(mynand_init);
module_exit(mynand_exit);
MODULE_LICENSE("GPL");
代码解释
其中 5 中的时间:
2440 上的控制器
nandflash芯片中描述的时间
则 tcls = 10ns
控制器 nandflash芯片
TACL = TCLS - TWP = 15ns - 15ns = 0ns
TWRPH0 = TWP = 15ns
TWRPH1 = TCLH = 5ns
我板子设置的时钟:
CPUID: 32440001
FCLK: 400 MHz
HCLK: 100 MHz
PCLK: 50 MHz
100MHZ ---> 10ns
寄存器中
TACLS
Duration = HCLK x TACLS
---> 0 = HCLK x [TACLS]
---> [TACLS] = 0
---> 实际的值是 10ns
TWRPH0
Duration = HCLK x ( TWRPH0 + 1 )
---> 15ns = HCLK x ( [TWRPH0] + 1 )
---> 15ns = 10(x+1) // 等号后边要大于等于前边的时间
---> x = 1
---> 实际的值是 20ns
TWRPH1
Duration = HCLK x ( TWRPH1 + 1 )
---> 5ns = HCLK x ( [TWRPH1] + 1 )
---> 5ns = 10(x+1)
---> x = 0
---> 实际的值是 10ns
······
1)先将内核中默认 nandflash 相关驱动去掉
make menuconfig-> Device Drivers-> Memory Technology Device (MTD) support-> NAND Device Support-> NAND Flash support for Samsung S3C SoCs
2)重新编译内核,并用新内核启动
3)将新编译的驱动拷贝到开发板中,加载
root@lip:~$ insmod nandflash_mtd.ko
NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)
Scanning device for bad blocks
Bad eraseblock 94 at 0x000000bc0000
...
Creating 4 MTD partitions on "NAND 256MiB 3,3V 8-bit":
0x000000000000-0x000000040000 : "bootloader"
0x000000040000-0x000000060000 : "params"
0x000000060000-0x000000260000 : "kernel"
0x000000260000-0x000010000000 : "root"
root@lip:~$ cat /proc/mtd
dev: size erasesize name
mtd0: 00040000 00020000 "bootloader"
mtd1: 00020000 00020000 "params"
mtd2: 00200000 00020000 "kernel"
mtd3: 0fda0000 00020000 "root"
上边的 erasesize = 0x00020000 = 128k
size = 00040000 = 256k
因此下边的 #blocks 单位是 1k
root@lip:~$ cat /proc/partitions
major minor #blocks name
31 0 256 mtdblock0
31 1 128 mtdblock1
31 2 2048 mtdblock2
31 3 259712 mtdblock3
4)格式化
> flash_eraseall /dev/mtd3
5) 挂载
> mount -t yaffs /dev/mtdblock3 /mnt
6)新建立文件,并卸载,重启后重新挂载,仍然后此文件