一、MTD系统架构
1.MTD设备体验
FLASH在嵌入式系统中是必不可少的,它是bootloader、linux内核和文件系统的最佳载体。
在Linux内核中引入了MTD子系统为NORFLASH和NAND FLASH设备提供统一的接口,从而使得FLASH驱动的设计大为简化。
每个分区对应一个块设备
-
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_tq2440\include\configs\TQ2440.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_probe(struct platform_device *pdev,
-
enum s3c_cpu_type cpu_type)
-
{
-
struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
-
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)\n", pdev);
-
-
info = kmalloc(sizeof(*info), GFP_KERNEL);
-
if (info == NULL) {
-
dev_err(&pdev->dev, "no memory for flash info\n");
-
err = -ENOMEM;
-
goto exit_error;
-
}
-
-
memset(info, 0, sizeof(*info));
-
platform_set_drvdata(pdev, 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_ERR(info->clk)) {
-
dev_err(&pdev->dev, "failed to get clock\n");
-
err = -ENOENT;
-
goto exit_error;
-
}
-
-
clk_enable(info->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_region(res->start, size, pdev->name); //地址转换
-
-
if (info->area == NULL) {
-
dev_err(&pdev->dev, "cannot reserve register region\n");
-
err = -ENOENT;
-
goto exit_error;
-
}
-
-
info->device = &pdev->dev;
-
info->platform = plat;
-
info->regs = ioremap(res->start, size);
-
info->cpu_type = cpu_type;
-
-
if (info->regs == NULL) {
-
dev_err(&pdev->dev, "cannot reserve register region\n");
-
err = -EIO;
-
goto exit_error;
-
}
-
-
dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs);
-
-
/* initialise the hardware */
-
-
err = s3c2410_nand_inithw(info); //初始化硬件
-
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 = kmalloc(size, GFP_KERNEL);
-
if (info->mtds == NULL) {
-
dev_err(&pdev->dev, "failed to allocate mtd storage\n");
-
err = -ENOMEM;
-
goto exit_error;
-
}
-
-
memset(info->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)\n", setno, nmtd, info);
-
-
s3c2410_nand_init_chip(info, 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_chip(info, nmtd);
-
nand_scan_tail(&nmtd->mtd);
-
s3c2410_nand_add_partition(info, nmtd, sets); //注册分区信息
-
}
-
-
if (sets != NULL)
-
sets++;
-
}
-
-
err = s3c2410_nand_cpufreq_register(info);
-
if (err < 0) {
-
dev_err(&pdev->dev, "failed to init cpufreq support\n");
-
goto exit_error;
-
}
-
-
if (allow_clk_stop(info)) {
-
dev_info(&pdev->dev, "clock idle support enabled\n");
-
clk_disable(info->clk);
-
}
-
-
pr_debug("initialised ok\n");
-
return 0;
-
-
exit_error:
-
s3c2410_nand_remove(pdev);
-
-
if (err == 0)
-
err = -EINVAL;
-
return err;
-
}
MTD通用驱动部分nand_base.c(nand_read:
-
static int nand_read(struct 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_device(chip, mtd, FL_READING);
-
-
chip->ops.len = len;
-
chip->ops.datbuf = buf;
-
chip->ops.oobbuf = NULL;
-
-
ret = nand_do_read_ops(mtd, from, &chip->ops); //进行读操作的代码
-
-
*retlen = chip->ops.retlen;
-
-
nand_release_device(mtd);
-
-
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_ops(struct 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_chip(mtd, chipnr);
-
-
realpage = (int)(from >> chip->page_shift);
-
page = realpage & chip->pagemask;
-
-
col = (int)(from & (mtd->writesize - 1));
-
-
buf = ops->datbuf;
-
oob = ops->oobbuf;
-
-
while(1) {
-
bytes = min(mtd->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 (likely(sndcmd)) {
-
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); //实际对应了nand_command_lp,cmd命令是0
-
sndcmd = 0;
-
}
-
-
.........
-
}
nand_command_lp:
-
static void nand_command_lp(struct 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_ctrl(mtd, 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_hwcontrol(struct mtd_info *mtd, int cmd,
-
unsigned int ctrl)
-
{
-
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
-
-
if (cmd == NAND_CMD_NONE)
-
return;
-
-
if (ctrl & NAND_CLE)
-
writeb(cmd, info->regs + S3C2410_NFCMD); //往NFCONT寄存器中写入cmd,cmd来自于nand_command,往上回溯为nand_read.其实就是发送了命令0x00
-
else
-
writeb(cmd, 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_ctrl(mtd, column, ctrl); //紧接着发送列地址
-
ctrl &= ~NAND_CTRL_CHANGE;
-
chip->cmd_ctrl(mtd, column >> 8, ctrl);
-
}
-
if (page_addr != -1) {
-
chip->cmd_ctrl(mtd, page_addr, ctrl); //发送行地址
-
chip->cmd_ctrl(mtd, page_addr >> 8,
-
NAND_NCE | NAND_ALE);
-
/* One more address cycle for devices > 128MiB */
-
if (chip->chipsize > (128 << 20))
-
chip->cmd_ctrl(mtd, page_addr >> 16,
-
NAND_NCE | NAND_ALE);
-
}
-
}
-
chip->cmd_ctrl(mtd, 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:
-
udelay(chip->chip_delay);
-
return;
-
-
case NAND_CMD_RESET:
-
if (chip->dev_ready)
-
break;
-
udelay(chip->chip_delay);
-
chip->cmd_ctrl(mtd, NAND_CMD_STATUS,
-
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
-
chip->cmd_ctrl(mtd, NAND_CMD_NONE,
-
NAND_NCE | NAND_CTRL_CHANGE);
-
while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ;
-
return;
-
-
case NAND_CMD_RNDOUT:
-
/* No ready / busy check necessary */
-
chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART,
-
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
-
chip->cmd_ctrl(mtd, NAND_CMD_NONE,
-
NAND_NCE | NAND_CTRL_CHANGE);
-
return;
-
-
case NAND_CMD_READ0:
-
chip->cmd_ctrl(mtd, NAND_CMD_READSTART, //这里发送了0x30命令
-
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
-
chip->cmd_ctrl(mtd, 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) {
-
udelay(chip->chip_delay);
-
return;
-
}
-
}
-
-
/* Apply this short delay always to ensure that we do wait tWB in
-
* any case on any machine. */
-
ndelay(100);
-
-
nand_wait_ready(mtd); //wait等待
-
}
阅读(682) | 评论(0) | 转发(0) |