08_NandFlash驱动

08_NandFlash驱动

1、框架

image-20210202194857392

1 app : open,read,write “1.txt”

-------------------------------------------- 文件的读写

文件系统: vfat, ext2, ext3, yaffs2, jffs2(这些文件系统就会把文件的读写转换为扇区的读写)

2 内核:-- ll_rw_block(通用的入口)---------- 扇区的读写

​ \1. 把"读写"放入队列

​ \2. 调用队列的处理函数(优化:调顺序/合并)

​ 块设备驱动程序

-------------------------------------------

3 硬件: 硬盘、flash

2、怎么写块设备驱动程序呢

  1. 分配gendisk结构体 : 使用函数alloc_disk

  2. 设置gendisk结构体

    2.1 分配/设置队列: request_queue_t // 它提供读写能力

    ​ 使用函数blk_init_queue

​ 2.2 设置gendisk结构体其他信息 // 它提供属性: 比如容量

  1. 注册: add_disk

3、硬件原理

NAND FLASH是一个存储芯片

image-20210202195022285

那么: 这样的操作很合理"读地址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操作:

image-20210202195053698

4、uboot体验nand flash操作

image-20210202195113632

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的,就是img,需要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}块设备

image-20210202195220869

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

​ */

image-20210202195412781

#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

image-20210202195455008

其中有一个警告:NAND_ECC_NONE

Nand flash的结构:

image-20210202195516986

解决办法:

image-20210202195542378

其中校验码可以用硬件或软件生成,下面使用软件生成。

3th:

在/* 2. 设置nand_chip */添加ecc.mode

s3c_nand->ecc.mode = NAND_ECC_SOFT;

image-20210202195607872

4th:

  1. make menuconfig去掉内核自带的NAND FLASH驱动

ubuntu中:

-> Device Drivers

-> Memory Technology Device (MTD) support

-> NAND Device Support

< > NAND Flash support for S3C2410/S3C2440 SoC

  1. 编译内核

ubuntu中:

make uImage

cp /arch/arm/boot/uImage /work/nfs_root/uImage_nonand

  1. 设置启动参数

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就可以

  1. 装载新内核并启动内核:使用新内核启动, 并且使用NFS作为根文件系统

OpenJTAG> nfs 30000000 192.168.2.16:/work/nfs_root/uImage_nonand; bootm 30000000

  1. 启动内核之后:

ls /dev/mtd*

image-20210202195632740

ls /dev/mtd*

image-20210202195651386

ls /dev/mtd* -l

image-20210202195710354

mount /dev/mtdblock3 /mnt

image-20210202195730599

  1. 若本来无文件系统,先格式化(参考编译工具)

flash_eraseall /dev/mtd3 /* yaffs */

image-20210202195751879

编译工具:在ubuntu中

  1. tar xjf mtd-utils-05.07.23.tar.bz2

  2. cd mtd-utils-05.07.23/util

    修改Makefile:

    #CROSS=arm-linux-

    改为

    CROSS=arm-linux-

  3. make

  4. cp flash_erase flash_eraseall /work/nfs_root/first_fs/bin/

  5. 挂接

    mount -t yaffs /dev/mtdblock3 /mnt

image-20210202195812797

  1. 在里面创建文件

image-20210202195835110

  1. 重启开发板,并挂接

# 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)]

  1. 若本来无文件系统,先格式化(参考编译工具)

flash_eraseall /dev/mtd3 /* yaffs */

[外链图片转存中…(img-wKyZeIAF-1612268253260)]

编译工具:在ubuntu中

  1. tar xjf mtd-utils-05.07.23.tar.bz2

  2. cd mtd-utils-05.07.23/util

    修改Makefile:

    #CROSS=arm-linux-

    改为

    CROSS=arm-linux-

  3. make

  4. cp flash_erase flash_eraseall /work/nfs_root/first_fs/bin/

  5. 挂接

    mount -t yaffs /dev/mtdblock3 /mnt

[外链图片转存中…(img-pcecd8gK-1612268253261)]

  1. 在里面创建文件

[外链图片转存中…(img-HOekP9N1-1612268253261)]

  1. 重启开发板,并挂接

# 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

「已注销」

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值