一、MTD系统架构
1.MTD设备体验
FLASH在嵌入式系统中是必不可少的,它是bootloader、linux内核和文件系统的最佳载体。
在Linux内核中引入了MTD子系统为NORFLASH和NAND FLASH设备提供统一的接口,从而使得FLASH驱动的设计大为简化。
cat /proc/mtd
每个分区对应一个块设备
ls -l /dev/mtd*
crw-rw—- 1 0 0 90, 0 Jan 1 00:00 /dev/mtd0
crw-rw—- 1 0 0 90, 1 Jan 1 00:00 /dev/mtd0ro
crw-rw—- 1 0 0 90, 2 Jan 1 00:00 /dev/mtd1
crw-rw—- 1 0 0 90, 3 Jan 1 00:00 /dev/mtd1ro
crw-rw—- 1 0 0 90, 4 Jan 1 00:00 /dev/mtd2
crw-rw—- 1 0 0 90, 5 Jan 1 00:00 /dev/mtd2ro
brw-rw—- 1 0 0 31, 0 Jan 1 00:00 /dev/mtdblock0
brw-rw—- 1 0 0 31, 1 Jan 1 00:00 /dev/mtdblock1
brw-rw—- 1 0 0 31, 2 Jan 1 00:00 /dev/mtdblock2
2.块设备驱动系统架构
二、YAFFS2文件系统应用
1.MTD分区设置
配置linux内核支持mtd,找到mtd接口文件,设置空间大小。
2.Yaffs2文件系统制作
将rootfs格式化生成yaffs文件系统。
/home/win/mkyaffs2image ./rootfs/ rootfs.img
3.Uboot参数设置
在uboot_tq2440includeconfigsTQ2440.h中有uboot的启动配置选项
#define CONFIG_BZIP2
#define CONFIG_LZO
#define CONFIG_LZMA
#define CONFIG_CMD_NAND_YAFFS
#define CONFIG_BOOTARGS “console=ttySAC0 root=/dev/mtdblock3”
#define CONFIG_BOOTCOMMAND “nand read 0x30000000 kernel;bootm 0x30000000”
4.下载烧写与启动
在uboot中用dnw下载
三、Nandflash驱动设计
s3c2410.c/s3c24xx_nand_probe:
static int s3c24xx_nand_probestruct platform_device *pdev,
enum s3c_cpu_type cpu_type)
{
struct s3c2410_platform_nand *plat = to_nand_platpdev);
struct s3c2410_nand_info *info;
struct s3c2410_nand_mtd *nmtd;
struct s3c2410_nand_set *sets;
struct resource *res;
int err = 0;
int size;
int nr_sets;
int setno;
pr_debug“s3c2410_nand_probe%p)
“, pdev);
info = kmallocsizeof*info), GFP_KERNEL);
if info == NULL) {
dev_err&pdev–>dev, “no memory for flash info
“);
err = –ENOMEM;
goto exit_error;
}
memsetinfo, 0, sizeof*info));
platform_set_drvdatapdev, info);
spin_lock_init&info–>controller.lock);
init_waitqueue_head&info–>controller.wq);
/* get the clock source and enable it */
info–>clk = clk_get&pdev–>dev, “nand”); //获取时钟,并使能
if IS_ERRinfo–>clk)) {
dev_err&pdev–>dev, “failed to get clock
“);
err = –ENOENT;
goto exit_error;
}
clk_enableinfo–>clk);
/* allocate and map the resource */
/* currently we assume we have the one resource */
res = pdev–>resource;
size = res–>end – res–>start + 1;
info–>area = request_mem_regionres–>start, size, pdev–>name); //地址转换
if info–>area == NULL) {
dev_err&pdev–>dev, “cannot reserve register region
“);
err = –ENOENT;
goto exit_error;
}
info–>device = &pdev–>dev;
info–>platform = plat;
info–>regs = ioremapres–>start, size);
info–>cpu_type = cpu_type;
if info–>regs == NULL) {
dev_err&pdev–>dev, “cannot reserve register region
“);
err = –EIO;
goto exit_error;
}
dev_dbg&pdev–>dev, “mapped registers at %p
“, info–>regs);
/* initialise the hardware */
err = s3c2410_nand_inithwinfo); //初始化硬件
if err != 0)
goto exit_error;
sets = plat != NULL) ? plat–>sets : NULL;
nr_sets = plat != NULL) ? plat–>nr_sets : 1;
info–>mtd_count = nr_sets;
/* allocate our information */
size = nr_sets * sizeof*info–>mtds);
info–>mtds = kmallocsize, GFP_KERNEL);
if info–>mtds == NULL) {
dev_err&pdev–>dev, “failed to allocate mtd storage
“);
err = –ENOMEM;
goto exit_error;
}
memsetinfo–>mtds, 0, size);
/* initialise all possible chips */
nmtd = info–>mtds;
for setno = 0; setno < nr_sets; setno++, nmtd++) {
pr_debug“initialising set %d %p, info %p)
“, setno, nmtd, info);
s3c2410_nand_init_chipinfo, nmtd, sets); //里面有校验nandflash
nmtd–>scan_res = nand_scan_ident&nmtd–>mtd, //搜索nandflash
sets) ? sets–>nr_chips : 1);
if nmtd–>scan_res == 0) {
s3c2410_nand_update_chipinfo, nmtd);
nand_scan_tail&nmtd–>mtd);
s3c2410_nand_add_partitioninfo, nmtd, sets); //注册分区信息
}
if sets != NULL)
sets++;
}
err = s3c2410_nand_cpufreq_registerinfo);
if err < 0) {
dev_err&pdev–>dev, “failed to init cpufreq support
“);
goto exit_error;
}
if allow_clk_stopinfo)) {
dev_info&pdev–>dev, “clock idle support enabled
“);
clk_disableinfo–>clk);
}
pr_debug“initialised ok
“);
return 0;
exit_error:
s3c2410_nand_removepdev);
if err == 0)
err = –EINVAL;
return err;
}
MTD通用驱动部分nand_base.c(nand_read:
static int nand_readstruct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, uint8_t *buf)
{
struct nand_chip *chip = mtd–>priv;
int ret;
/* Do not allow reads past end of device */
if from + len) > mtd–>size)
return –EINVAL;
if !len)
return 0;
nand_get_devicechip, mtd, FL_READING);
chip–>ops.len = len;
chip–>ops.datbuf = buf;
chip–>ops.oobbuf = NULL;
ret = nand_do_read_opsmtd, from, &chip–>ops); //进行读操作的代码
*retlen = chip–>ops.retlen;
nand_release_devicemtd);
return ret;
}
nand_do_read_ops:
/**
* nand_do_read_ops – [Internal] Read data with ECC
*
* @mtd: MTD device structure
* @from: offset to read from
* @ops: oob ops structure
*
* Internal function. Called with chip held.
*/
static int nand_do_read_opsstruct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
int chipnr, page, realpage, col, bytes, aligned;
struct nand_chip *chip = mtd–>priv;
struct mtd_ecc_stats stats;
int blkcheck = 1 << chip–>phys_erase_shift – chip–>page_shift)) – 1;
int sndcmd = 1;
int ret = 0;
uint32_t readlen = ops–>len;
uint32_t oobreadlen = ops–>ooblen;
uint8_t *bufpoi, *oob, *buf;
stats = mtd–>ecc_stats;
chipnr = int)from >> chip–>chip_shift);
chip–>select_chipmtd, chipnr);
realpage = int)from >> chip–>page_shift);
page = realpage & chip–>pagemask;
col = int)from & mtd–>writesize – 1));
buf = ops–>datbuf;
oob = ops–>oobbuf;
while1) {
bytes = minmtd–>writesize – col, readlen);
aligned = bytes == mtd–>writesize);
/* Is the current page in the buffer ? */
if realpage != chip–>pagebuf || oob) {
bufpoi = aligned ? buf : chip–>buffers–>databuf;
if likelysndcmd)) {
chip–>cmdfuncmtd, NAND_CMD_READ0, 0x00, page); //实际对应了nand_command_lp,cmd命令是0
sndcmd = 0;
}
………
}
nand_command_lp:
static void nand_command_lpstruct mtd_info *mtd, unsigned int command,
int column, int page_addr)
{
register struct nand_chip *chip = mtd–>priv;
/* Emulate NAND_CMD_READOOB */
if command == NAND_CMD_READOOB) {
column += mtd–>writesize;
command = NAND_CMD_READ0;
}
/* Command latch cycle */
chip–>cmd_ctrlmtd, command & 0xff,
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); //cmd_ctrl来源于底层驱动,在s3c2410_nand_init_chip中赋值了。
…….
}
s3c2410_nand_hwcontrol:
/* s3c2410_nand_hwcontrol
*
* Issue command and address cycles to the chip
*/
static void s3c2410_nand_hwcontrolstruct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfomtd);
if cmd == NAND_CMD_NONE)
return;
if ctrl & NAND_CLE)
writebcmd, info–>regs + S3C2410_NFCMD); //往NFCONT寄存器中写入cmd,cmd来自于nand_command,往上回溯为nand_read.其实就是发送了命令0x00
else
writebcmd, info–>regs + S3C2410_NFADDR);
}
继续回到nand_command_lp:
……………
if column != –1 || page_addr != –1) {
int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
/* Serially input address */
if column != –1) {
/* Adjust columns for 16 bit buswidth */
if chip–>options & NAND_BUSWIDTH_16)
column >>= 1;
chip–>cmd_ctrlmtd, column, ctrl); //紧接着发送列地址
ctrl &= ~NAND_CTRL_CHANGE;
chip–>cmd_ctrlmtd, column >> 8, ctrl);
}
if page_addr != –1) {
chip–>cmd_ctrlmtd, page_addr, ctrl); //发送行地址
chip–>cmd_ctrlmtd, page_addr >> 8,
NAND_NCE | NAND_ALE);
/* One more address cycle for devices > 128MiB */
if chip–>chipsize > 128 << 20))
chip–>cmd_ctrlmtd, page_addr >> 16,
NAND_NCE | NAND_ALE);
}
}
chip–>cmd_ctrlmtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
/*
* program and erase have their own busy handlers
* status, sequential in, and deplete1 need no delay
*/
switch command) {
case NAND_CMD_CACHEDPROG:
case NAND_CMD_PAGEPROG:
case NAND_CMD_ERASE1:
case NAND_CMD_ERASE2:
case NAND_CMD_SEQIN:
case NAND_CMD_RNDIN:
case NAND_CMD_STATUS:
case NAND_CMD_DEPLETE1:
return;
/*
* read error status commands require only a short delay
*/
case NAND_CMD_STATUS_ERROR:
case NAND_CMD_STATUS_ERROR0:
case NAND_CMD_STATUS_ERROR1:
case NAND_CMD_STATUS_ERROR2:
case NAND_CMD_STATUS_ERROR3:
udelaychip–>chip_delay);
return;
case NAND_CMD_RESET:
if chip–>dev_ready)
break;
udelaychip–>chip_delay);
chip–>cmd_ctrlmtd, NAND_CMD_STATUS,
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
chip–>cmd_ctrlmtd, NAND_CMD_NONE,
NAND_NCE | NAND_CTRL_CHANGE);
while !chip–>read_bytemtd) & NAND_STATUS_READY)) ;
return;
case NAND_CMD_RNDOUT:
/* No ready / busy check necessary */
chip–>cmd_ctrlmtd, NAND_CMD_RNDOUTSTART,
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
chip–>cmd_ctrlmtd, NAND_CMD_NONE,
NAND_NCE | NAND_CTRL_CHANGE);
return;
case NAND_CMD_READ0:
chip–>cmd_ctrlmtd, NAND_CMD_READSTART, //这里发送了0x30命令
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
chip–>cmd_ctrlmtd, NAND_CMD_NONE,
NAND_NCE | NAND_CTRL_CHANGE);
/* This applies to read commands */
default:
/*
* If we don‘t have access to the busy pin, we apply the given
* command delay
*/
if !chip–>dev_ready) {
udelaychip–>chip_delay);
return;
}
}
/* Apply this short delay always to ensure that we do wait tWB in
* any case on any machine. */
ndelay100);
nand_wait_readymtd); //wait等待
}
无欲速,无见小利。欲速,则不达;见小利,则大事不成。