一、关于NAND FLASH的基本问题:
问1. 原理图上NAND FLASH和S3C2440之间只有数据线,怎么传输地址?
答1.在DATA0~DATA7上既传输数据,又传输地址
当ALE为高电平时传输的是地址,
问2. 从NAND FLASH芯片手册可知,要操作NAND FLASH需要先发出命令怎么传入命令?
答2.在DATA0~DATA7上既传输数据,又传输地址,也传输命令
当ALE为高电平时传输的是地址,
当CLE为高电平时传输的是命令
当ALE和CLE都为低电平时传输的是数据
问3. 假设烧写NAND FLASH,把命令、地址、数据发给它之后,
NAND FLASH肯定不可能瞬间完成烧写的,怎么判断烧写完成?
答3. 通过状态引脚RnB来判断:它为高电平表示就绪,它为低电平表示正忙
问4. NAND Flash引脚描述?
答4. CLE:指令锁存使能 ALE:地址锁存使能 CE:芯片使能
RE: 读使能 WE:写使能 WP:写保护
R/B:就绪/忙
问5. 怎么操作NAND FLASH呢?
答5. 根据NAND FLASH的芯片手册,一般的过程是:
a. 发出命令
b. 发出地址
c. 发出数据/读数据
由于S3C2440内部存在NAND Flash的控制器,可以方便开发,省掉很多繁琐的操作比如说发命令,它需要经历“选中芯片”、“CLE设为高电平”、“在DATA0~DATA7上输出命令值”发出一个写脉冲”一系列操作才可以实现发命令,但是斗鱼S3c2440的内部NAND Flash的控制器的存在,只需要令寄存器“ NFCMMD=命令值”就可以实现。
6.用UBOOT来体验NAND FLASH的操作:
二、驱动程序框架
static int s3c_nand_init(void)
{
1.分配一个nand_chip结构体
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2. 设置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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3. 硬件相关的设置: 根据NAND FLASH的手册设置时间参数
3.1 设置 TACLS、TWRPH0、TWRPH1三个参数
3.2 取消片选 、使能NAND FLASH控制器
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4. 使用: nand_scan
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5. add_mtd_partitions
}
static void s3c_nand_exit(void)
{
}
以上需要用到的函数定义
static void s3c2440_select_chip(struct mtd_info *mtd, int chipnr)
{
if (chipnr == -1)
{
}
else
{
}
}
static void s3c2440_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
if (ctrl & NAND_CLE)
{
}
else
{
}
}
static int s3c2440_dev_ready(struct mtd_info *mtd)
{
}
以下是完整代码:
/* 参考
* drivers\mtd\nand\s3c2410.c
* drivers\mtd\nand\at91_nand.c
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <asm/arch/regs-nand.h>
#include <asm/arch/nand.h>
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;
static struct mtd_partition s3c_nand_parts[] = {
[] = {
.name = "bootloader",
.size = ,
.offset = ,
},
[] = {
.name = "params",
.offset = MTDPART_OFS_APPEND,
.size = ,
},
[] = {
.name = "kernel",
.offset = MTDPART_OFS_APPEND,
.size = ,
},
[] = {
.name = "root",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
}
};
static void s3c2440_select_chip(struct mtd_info *mtd, int chipnr)
{
if (chipnr == -)
{
/* 取消选中: NFCONT[1]设为1 */
s3c_nand_regs->nfcont |= (<<);
}
else
{
/* 选中: NFCONT[1]设为0 */
s3c_nand_regs->nfcont &= ~(<<);
}
}
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 & (<<));
}
static int s3c_nand_init(void)
{
struct clk *clk;
/* 1. 分配一个nand_chip结构体 */
s3c_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
s3c_nand_regs = ioremap(, 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. 硬件相关的设置: 根据NAND FLASH的手册设置时间参数 */
/* 使能NAND FLASH控制器的时钟 */
clk = clk_get(NULL, "nand");
clk_enable(clk); /* CLKCON'bit[4] */
/* 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<<) | (TWRPH0<<) | (TWRPH1<<);
/* NFCONT:
* BIT1-设为1, 取消片选
* BIT0-设为1, 使能NAND FLASH控制器
*/
s3c_nand_regs->nfcont = (<<) | (<<);
/* 4. 使用: nand_scan */
s3c_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
s3c_mtd->owner = THIS_MODULE;
s3c_mtd->priv = s3c_nand;
nand_scan(s3c_mtd, ); /* 识别NAND FLASH, 构造mtd_info */
/* 5. add_mtd_partitions */
add_mtd_partitions(s3c_mtd, s3c_nand_parts, );
//add_mtd_device(s3c_mtd);
return ;
}
static void s3c_nand_exit(void)
{
del_mtd_partitions(s3c_mtd);
kfree(s3c_mtd);
iounmap(s3c_nand_regs);
kfree(s3c_nand);
}
module_init(s3c_nand_init);
module_exit(s3c_nand_exit);
MODULE_LICENSE("GPL");
三、测试
-
make menuconfig去掉内核自带的NAND FLASH驱动
-> Device Drivers
-> Memory Technology Device (MTD) support
-> NAND Device Support
< > NAND Flash support for S3C2410/S3C2440 SoC
-
make uImage
使用新内核启动, 并且使用NFS作为根文件系统
cp arch/arm/boot/uImage /work/nfs_root/uImage_nonand 复制到文件系统
重启开发板,倒数到0之前按下Q:
set bootargs console=ttySAC0 root=/dev/nfs nfsroot=192.168.1.7:/work/nfs_root/first_fs ip=192.168.1.2:192.168.1.7:192.168.1.1:255.255.255.0::eth0:off 设置启动参数
save
nfs 30000000 192.168.1.7:/work/nfs_root/uImage_nonand
bootm 30000000
-
装载
ls /dev/mtd* 显示 ls: /dev/mtd*: No such file or directory
insmod s3c_nand.ko
-
格式化 (参考下面编译工具)
flash_eraseall /dev/mtd3 // yaffs
-
挂接
mount -t yaffs /dev/mtdblock3 /mnt
- 在/mnt目录下建文件
编译工具:
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/