MTD系统架构和yaffs2使用、Nandflash驱动设计
一、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_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)
", pdev);
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (info == NULL) {
dev_err(&pdev->dev, "no memory for flash info
");
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
");
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
");
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
");
err = -EIO;
goto exit_error;
}
dev_dbg(&pdev->dev, "mapped registers at %p
", 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
");
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)
", 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
");
goto exit_error;
}
if (allow_clk_stop(info)) {
dev_info(&pdev->dev, "clock idle support enabled
");
clk_disable(info->clk);
}
pr_debug("initialised ok
");
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等待
}
無欲速,無見小利。欲速,則不達;見小利,則大事不成。
總結
以上是生活随笔為你收集整理的MTD系统架构和yaffs2使用、Nandflash驱动设计的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何启用 SAP Spartacus G
- 下一篇: 斐讯n1进入u盘启动