08_NandFlash驱动
文章目录
1、框架
1 app : open,read,write “1.txt”
-------------------------------------------- 文件的读写
文件系统: vfat, ext2, ext3, yaffs2, jffs2(这些文件系统就会把文件的读写转换为扇区的读写)
2 内核:-- ll_rw_block(通用的入口)---------- 扇区的读写
\1. 把"读写"放入队列
\2. 调用队列的处理函数(优化:调顺序/合并)
块设备驱动程序
-------------------------------------------
3 硬件: 硬盘、flash
2、怎么写块设备驱动程序呢
-
分配gendisk结构体 : 使用函数alloc_disk
-
设置gendisk结构体
2.1 分配/设置队列: request_queue_t // 它提供读写能力
使用函数blk_init_queue
2.2 设置gendisk结构体其他信息 // 它提供属性: 比如容量
- 注册: add_disk
3、硬件原理
NAND FLASH是一个存储芯片
那么: 这样的操作很合理"读地址A的数据,把数据B写到地址A"
问1. 原理图上NAND FLASH和S3C2440之间只有数据线,
怎么传输地址?
答1.在DATA0~DATA7上既传输数据,又传输地址
当ALE为高电平时传输的是地址,
问2. 从NAND FLASH芯片手册可知,要操作NAND FLASH需要先发出命令
怎么传入命令?
答2.在DATA0~DATA7上既传输数据,又传输地址,也传输命令
当ALE为高电平时传输的是地址,
当CLE为高电平时传输的是命令
当ALE和CLE都为低电平时传输的是数据
问3. 数据线既接到NAND FLASH,也接到NOR FLASH,还接到SDRAM、DM9000等等
那么怎么避免干扰?
答3. 这些设备,要访问之必须"选中",引脚nFCE
没有选中的芯片不会工作,相当于没接一样
问4. 假设烧写NAND FLASH,把命令、地址、数据发给它之后,
NAND FLASH肯定不可能瞬间完成烧写的,
怎么判断烧写完成?
答4. 通过状态引脚RnB来判断:它为高电平表示就绪,它为低电平表示正忙
问5. 怎么操作NAND FLASH呢?
答5. 根据NAND FLASH的芯片手册,一般的过程是:
发出命令
发出地址
发出数据/读数据
操作: NAND FLASH : S3C2440:
发命令 选中芯片
CLE设为高电平 NFCMMD=命令值
在DATA0~DATA7上输出命令值
发出一个写脉冲
发地址 选中芯片 NFADDR=地址值
ALE设为高电平
在DATA0~DATA7上输出地址值
发出一个写脉冲
发数据 选中芯片 NFDATA=数据值
ALE、CLE设为低电平
在DATA0~DATA7上输出数据值
发出一个写脉冲
读数据 选中芯片 val=NFDATA
发出读脉冲
读DATA0~DATA7的数据
例如读ID操作:
4、uboot体验nand flash操作
4.1、读ID
操作 S3C2440 u-boot
选中 NFCONT的bit1设为0 md.l 0x4E000004 1; mw.l 0x4E000004 1
(寄存器为4字节长度的,用md.l)
发出命令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
4.2、读内容: 读0地址的数据
Nand flash是256MB的,就是,需要28位的数据,一个周期传8位数据,至少需要4个周期,为了兼容更大容量的nand发出5个周期。
使用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
其中发出5个周期的地址原因为:
开发板外接的nand flash芯片容量为256Mb = 2^6 * 2^20
1位地址线可以表示:0、1两个地址;
2位地址线可以表示:00、01、10、11四个地址;
那么需要28为地址线才能表示256Mb的地址;
一个周期为8个地址,所以至少需要4个周期的地址,为了兼容更大容量的flash,所以发出了5个周期的地址。
5、Nand驱动框架
看内核启动信息:
S3C24XX NAND Driver, © 2004 Simtec Electronics
s3c2440-nand s3c2440-nand: Tacls=3, 30ns Twrph0=7 70ns, Twrph1=3 30ns
NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)
Scanning device for bad blocks
Bad eraseblock 790 at 0x062c0000
Bad eraseblock 1668 at 0x0d080000
Creating 4 MTD partitions on “NAND 256MiB 3,3V 8-bit”:
0x00000000-0x00040000 : “bootloader”
0x00040000-0x00060000 : “params”
0x00060000-0x00260000 : “kernel”
0x00260000-0x10000000 : “root”
图中残缺部分:在drivers/mtd/mtdchar.c }字符设备
在drivers/mtd/mtd_blkdevr.c}块设备
APP:使用的文件可能为块设备文件或者字符设备文件
图中VFS中的sys_open、sys_read等函数根据文件信息判断是字符设备还是块设备。
若为字符设备,找到字符设备驱动程序;若为块设备,根据文件系统FS找到块设备。
分析内核:
搜索:“S3C24XX NAND Driver”
得到:S3c2410.c (drivers\mtd\nand)
s3c2410_nand_inithw
s3c2410_nand_init_chip
nand_scan // drivers/mtd/nand/nand_base.c 根据nand_chip的底层操作函数识别NAND FLASH,构造mtd_info
nand_scan_ident
nand_set_defaults
if (!chip->select_chip)
chip->select_chip = nand_select_chip; // 默认值不适用
if (chip->cmdfunc == NULL)
chip->cmdfunc = nand_command;
chip->cmd_ctrl(mtd, command, ctrl);
if (!chip->read_byte)
chip->read_byte = nand_read_byte;
readb(chip->IO_ADDR_R);
if (chip->waitfunc == NULL)
chip->waitfunc = nand_wait;
chip->dev_ready
nand_get_flash_type
chip->select_chip(mtd, 0);
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
*maf_id = chip->read_byte(mtd);
dev_id = chip->read_byte(mtd);
nand_scan_tail
mtd->erase = nand_erase;
mtd->read = nand_read;
mtd->write = nand_write;
s3c2410_nand_add_partition
add_mtd_partitions
add_mtd_device
list_for_each(this, &mtd_notifiers) { // 问. 链表mtd_notifiers在哪设置
// 答. drivers/mtd/mtdchar.c,mtd_blkdev.c调用register_mtd_user
struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
not->add(mtd);
// mtd_notify_add 和 blktrans_notify_add
先看字符设备的mtd_notify_add
class_device_create
class_device_create
再看块设备的blktrans_notify_add
list_for_each(this, &blktrans_majors) { // 问. blktrans_majors在哪设置
//答. drivers\mtd\mdblock.c或mtdblock_ro.c 调用register_mtd_blktrans
struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
tr->add_mtd(tr, mtd);
mtdblock_add_mtd (drivers\mtd\mdblock.c)
add_mtd_blktrans_dev
alloc_disk
gd->queue = tr->blkcore_priv->rq; // tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
add_disk
6、编写驱动代码
6.6、包含头文件
/* 参考
* drivers\mtd\nand\s3c2410.c
* drivers\mtd\nand\at91_nand.c
*/
。。。。
6.5、定义变量
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 ;
};
static struct nand_chip *s3c_nand;
static struct mtd_info s3c_mtd; / 这些结构体中有那些读、写、擦除函数 */
static struct s3c_nand_regs *s3c_nand_regs;
6.4、分区表
static struct mtd_partition s3c_nand_parts[] = {
[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,
}
};
6.3、nand的操作函数
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);
}
}
static void s3c2440_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
if (ctrl & NAND_CLE)
{
/* 发命令: NFCMMD=dat */
s3c_nand_regs->nfcmd = dat;
}
else
{
/* 发地址: NFADDR=dat */
s3c_nand_regs->nfaddr = dat;
}
}
static int s3c2440_dev_ready(struct mtd_info *mtd)
{
return (s3c_nand_regs->nfstat & (1<<0));
}
6.1、入口函数s3c_nand_init中
static int s3c_nand_init(void)
{
struct clk *clk;
/* 1. 分配一个nand_chip结构体 */
s3c_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
/* 映射nand寄存器 */
s3c_nand_regs = ioremap(0x4E000000, sizeof(struct s3c_nand_regs));
/* 2. 设置nand_chip */
/* 设置nand_chip是给nand_scan使用的
* 如果不知道怎么设置,先看nand_scan是怎样使用的
* 它应该提供:选中、发命令、发地址、发数据、读数据、判断状态的功能
* 以上功能若默认函数能够使用则不设置,不能则用下列代码设置
*/
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;
/* 3. 硬件相关的设置:根据 */
/* 3.1 使能NAND FLASH控制器的时钟 */
clk = clk_get(NULL, “nand”);
clk_enable(clk); /* CLKCON’bit[4] */
/* 3.2 HCLK=100MHz
* TACLS: 发出CLE/ALE之后多长时间才发出nWE信号, 从NAND手册可知CLE/ALE与nWE可以同时发出,所以TACLS=0
* TWRPH0: nWE的脉冲宽度, HCLK x ( TWRPH0 + 1 ), 从NAND手册可知它要>=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);
/* 3.3 NFCONT:
* BIT1-设为1, 取消片选
* BIT0-设为1, 使能NAND FLASH控制器
*/
s3c_nand_regs->nfcont = (1<<1) | (1<<0);
/* 4. 使用:mtd_scan */
s3c_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
s3c_mtd->owner = THIS_MODULE;
s3c_mtd->priv = s3c_nand;
nand_scan(s3c_mtd, 1); /* 识别nand flash,构造mtd_info */
/* 5. add_mtd_partitions 添加分区 */
//add_mtd_device(struct mtd_info * mtd) 若不分区则使用这个函数即可
add_mtd_partitions(s3c_mtd, s3c_nand_parts, 4);
return 0;
}
6.2、在出口函数s3c_nand_exit中
static void s3c_nand_exit(void)
{
del_mtd_partitions(s3c_mtd);
kfree(s3c_mtd);
iounmap(s3c_nand_regs);
kfree(s3c_nand);
}
7、测试实验
2th:
insmod s3c_nand.ko
其中有一个警告:NAND_ECC_NONE
Nand flash的结构:
解决办法:
其中校验码可以用硬件或软件生成,下面使用软件生成。
3th:
在/* 2. 设置nand_chip */添加ecc.mode
s3c_nand->ecc.mode = NAND_ECC_SOFT;
4th:
- make menuconfig去掉内核自带的NAND FLASH驱动
ubuntu中:
-> Device Drivers
-> Memory Technology Device (MTD) support
-> NAND Device Support
< > NAND Flash support for S3C2410/S3C2440 SoC
- 编译内核
ubuntu中:
make uImage
cp /arch/arm/boot/uImage /work/nfs_root/uImage_nonand
- 设置启动参数
u-boot中:
之前的参数:bootargs=noinitrd root=/dev/mtdblock3 rootfstype=yaffs2 init=/linuxrc console=ttySAC0,115200
修改为:
OpenJTAG> set bootargs noinitrd console=ttySAC0 root=/dev/nfs nfsroot=192.168.2.16:/work/nfs_root/first_fs ip=192.168.2.55:192.168.2.16:192.168.2.1:255.255.255.0::eth0:off
OpenJTAG> save
注:
nfsroot=192.168.2.16: ubuntu ip地址
/work/nfs_root/first_fs要挂载的目录
ip=192.168.2.55: 单板ip(恢复出厂设置后记得先配置ip,手动挂载下能不能成功,可以成功的话再修改 bootargs自动挂载!)
192.168.2.16: 依然是ubuntu ip !!!!!注意!!!
192.168.2.1: 网关,只要处于同一网段就好。
255.255.255.0:: 子网掩码
eth0: 网卡,一般都是0
off 是否自动配置 off就可以
- 装载新内核并启动内核:使用新内核启动, 并且使用NFS作为根文件系统
OpenJTAG> nfs 30000000 192.168.2.16:/work/nfs_root/uImage_nonand; bootm 30000000
- 启动内核之后:
ls /dev/mtd*
ls /dev/mtd*
ls /dev/mtd* -l
mount /dev/mtdblock3 /mnt
- 若本来无文件系统,先格式化(参考编译工具)
flash_eraseall /dev/mtd3 /* yaffs */
编译工具:在ubuntu中
-
tar xjf mtd-utils-05.07.23.tar.bz2
-
cd mtd-utils-05.07.23/util
修改Makefile:
#CROSS=arm-linux-
改为
CROSS=arm-linux-
-
make
-
cp flash_erase flash_eraseall /work/nfs_root/first_fs/bin/
-
挂接
mount -t yaffs /dev/mtdblock3 /mnt
- 在里面创建文件
- 重启开发板,并挂接
# reboot
OpenJTAG> nfs 30000000 192.168.2.16:/work/nfs_root/uImage_nonand; bootm 30000000
# insmod s3c_nand.ko
# mount /dev/mtdblock3 /mnt /* mount -t yaffs /dev/mtdblock3 /mnt */
# cd /mnt
# ls
lost+found test
# cd test
# ls
1.txt
# cat 1.txt
268253260)]
- 若本来无文件系统,先格式化(参考编译工具)
flash_eraseall /dev/mtd3 /* yaffs */
[外链图片转存中…(img-wKyZeIAF-1612268253260)]
编译工具:在ubuntu中
-
tar xjf mtd-utils-05.07.23.tar.bz2
-
cd mtd-utils-05.07.23/util
修改Makefile:
#CROSS=arm-linux-
改为
CROSS=arm-linux-
-
make
-
cp flash_erase flash_eraseall /work/nfs_root/first_fs/bin/
-
挂接
mount -t yaffs /dev/mtdblock3 /mnt
[外链图片转存中…(img-pcecd8gK-1612268253261)]
- 在里面创建文件
[外链图片转存中…(img-HOekP9N1-1612268253261)]
- 重启开发板,并挂接
# reboot
OpenJTAG> nfs 30000000 192.168.2.16:/work/nfs_root/uImage_nonand; bootm 30000000
# insmod s3c_nand.ko
# mount /dev/mtdblock3 /mnt /* mount -t yaffs /dev/mtdblock3 /mnt */
# cd /mnt
# ls
lost+found test
# cd test
# ls
1.txt
# cat 1.txt
www.100ask.net