MTD原始设备与FLASH硬件驱动的对话
luofuchong
看了<
>后对以MTD的分层结构以及各层的分工情况有了大致的了
解,然而各层之间是如何进行对话的呢,对于这个问题,<>上没有
详细的去说明。
小弟抽空研究了一下,打算从下到上,在从上到下,分两条主线来研究一下 MTD 原始设备与
FLASH硬件驱动的对话(MTD原始设备与更上层的对话留待以后再研究)。
以下是第一部分,从下到上的介绍 FLASH硬件驱动与MTD原始设备是如何建立联系的。
1、首先从入口函数开始:
static int s3c24xx_nand_probe(struct device *dev, int is_s3c2440)
{
struct platform_device *pdev = to_platform_device(dev);
struct s3c2410_platform_nand *plat = to_nand_plat(dev);
//获取 nand flash配置用结构体数据(dev.c中定义,详细见附录部分)
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", dev);
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (info == NULL) {
printk(KERN_ERR PFX "no memory for flash info\n");
err = -ENOMEM;
goto exit_error;
}
memzero(info, sizeof(*info));
dev_set_drvdata(dev, info); //以后有用
spin_lock_init(&info->controller.lock); //初始化自旋锁
init_waitqueue_head(&info->controller.wq); //初始化等待队列
/* get the clock source and enable it */
info->clk = clk_get(dev, "nand");
if (IS_ERR(info->clk)) {
printk(KERN_ERR PFX "failed to get clock");
err = -ENOENT;
goto exit_error;
}
clk_use(info->clk);
clk_enable(info->clk);
/* allocate and map the resource */
/* currently we assume we have the one resource */
res = pdev->resource; //提取 dev.c中定义的与设备相关的资源
size = res->end - res->start + 1;
info->area = request_mem_region(res->start, size, pdev->name);
if (info->area == NULL) {
printk(KERN_ERR PFX "cannot reserve register region\n");
err = -ENOENT;
goto exit_error;
}
info->device = dev;
info->platform = plat; //保存好 struct s3c2410_platform_nand
结构数据
info->regs = ioremap(res->start, size);//映射 nand flash用到的寄存器
info->is_s3c2440 = is_s3c2440;
if (info->regs == NULL) {
printk(KERN_ERR PFX "cannot reserve register region\n");
err = -EIO;
goto exit_error;
}
printk(KERN_INFO PFX "mapped registers at %p\n", info->regs);
/* initialise the hardware */
err = s3c2410_nand_inithw(info, dev);
//初始化 s3c2410 nand flash控制,主要是配置 S3C2410_NFCONF寄存器
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;
//我的板上只有一块 nand flash,配置信息见 plat-sets,数目为 1。
/* allocate our information */
size = nr_sets * sizeof(*info->mtds);
info->mtds = kmalloc(size, GFP_KERNEL);
if (info->mtds == NULL) {
printk(KERN_ERR PFX "failed to allocate mtd storage\n");
err = -ENOMEM;
goto exit_error;
}
memzero(info->mtds, 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);
nmtd->scan_res = nand_scan(&nmtd->mtd,
(sets) ? sets->nr_chips : 1);//为什么使用 set->nr_chips(还没配置
的东西)?
if (nmtd->scan_res == 0) {
s3c2410_nand_add_partition(info, nmtd, sets);
}
if (sets != NULL)
sets++;
}
pr_debug("initialised ok\n");
return 0;
exit_error:
s3c2410_nand_remove(dev);
if (err == 0)
err = -EINVAL;
return err;
}
//初始化代
表
关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf
一片 flash的 struct nand_chip结构
static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
struct s3c2410_nand_mtd *nmtd,
struct s3c2410_nand_set *set)
{
struct nand_chip *chip = &nmtd->chip;
chip->IO_ADDR_R = info->regs + S3C2410_NFDATA; //读地址
chip->IO_ADDR_W = info->regs + S3C2410_NFDATA; //写地址
chip->hwcontrol = s3c2410_nand_hwcontrol;
chip->dev_ready = s3c2410_nand_devready; //ready状态查询
chip->write_buf = s3c2410_nand_write_buf; //写函数
chip->read_buf = s3c2410_nand_read_buf; //读函数
chip->select_chip = s3c2410_nand_select_chip; //片选函数
chip->chip_delay = 50;
chip->priv = nmtd;
chip->options = 0;
chip->controller = &info->controller;
if (info->is_s3c2440) {
chip->IO_ADDR_R = info->regs + S3C2440_NFDATA;
chip->IO_ADDR_W = info->regs + S3C2440_NFDATA;
chip->hwcontrol = s3c2440_nand_hwcontrol;
}
nmtd->info = info;
nmtd->mtd.priv = chip;
//nand_scan 函数中会调用 struct nand_chip *this = mtd->priv 取出该 struct
nand_chip结构
nmtd->set = set;
if (hardware_ecc) {
chip->correct_data = s3c2410_nand_correct_data;
chip->enable_hwecc = s3c2410_nand_enable_hwecc;
chip->calculate_ecc = s3c2410_nand_calculate_ecc;
chip->eccmode = NAND_ECC_HW3_512;
chip->autooob = &nand_hw_eccoob;
if (info->is_s3c2440) {
chip->enable_hwecc = s3c2440_nand_enable_hwecc;
chip->calculate_ecc = s3c2440_nand_calculate_ecc;
}
} else {
chip->eccmode = NAND_ECC_SOFT; //ECC的类型
}
}
/* command and control functions
*
* Note, these all use tglx's method of changing the IO_ADDR_W field
* to make the code simpler, and use the nand layer's code to issue the
* command and address sequences via the proper IO ports.
*
*/
static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd)
{
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
struct nand_chip *chip = mtd->priv;
switch (cmd) {
case NAND_CTL_SETNCE:
case NAND_CTL_CLRNCE:
printk(KERN_ERR "%s: called for NCE\n", __FUNCTION__);
break;
case NAND_CTL_SETCLE:
chip->IO_ADDR_W = info->regs + S3C2410_NFCMD;//写命令
break;
case NAND_CTL_SETALE:
chip->IO_ADDR_W = info->regs + S3C2410_NFADDR;//写地址
break;
/* NAND_CTL_CLRCLE: */
/* NAND_CTL_CLRALE: */
default:
chip->IO_ADDR_W = info->regs + S3C2410_NFDATA;//写数据
break;
}
}
/* s3c2410_nand_devready()
*
* returns 0 if the nand is busy, 1 if it is ready
*/
static int s3c2410_nand_devready(struct mtd_info *mtd)
{
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
if (info->is_s3c2440)
return readb(info->regs + S3C2440_NFSTAT) & S3C2440_NFSTAT_READY;
return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY;//返回
nand flash都忙标志
}
static void s3c2410_nand_write_buf(struct mtd_info *mtd,
const u_char *buf, int len)
{
struct nand_chip *this = mtd->priv;
writesb(this->IO_ADDR_W, buf, len);//写操作
}
static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
struct nand_chip *this = mtd->priv;
readsb(this->IO_ADDR_R, buf, len);//读操作
}
/* select chip */
/*
* 根据 chip都值设置 nand flash都片选信号:
* chip = -1 -- 禁用 nand flash
* chip !=-1 -- 选择对应的 nand flash
*/
static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
{
struct s3c2410_nand_info *info;
struct s3c2410_nand_mtd *nmtd;
struct nand_chip *this = mtd->priv;
void __iomem *reg;
unsigned long cur;
unsigned long bit;
nmtd = this->priv;
info = nmtd->info;
bit = (info->is_s3c2440) ? S3C2440_NFCONT_nFCE : S3C2410_NFCONF_nFCE;
reg = info->regs+((info->is_s3c2440) ? S3C2440_NFCONT:S3C2410_NFCONF);
cur = readl(reg);
if (chip == -1) {
cur |= bit;
} else {
if (nmtd->set != NULL && chip > nmtd->set->nr_chips) {
printk(KERN_ERR PFX "chip %d out of range\n", chip);
return;
}
if (info->platform != NULL) {
if (info->platform->select_chip != NULL)
(info->platform->select_chip)(nmtd->set, chip);
}
cur &= ~bit;
}
writel(cur, reg);
}
注:
s3c2410_nand_init_chip 填充 struct nand_chip 的一部分成员,nand_scan 以通用
nand flash的标准进行检测,并填充 struct nand_chip的其它成员,必要时根据检测结果进
行取舍。
int nand_scan (struct mtd_info *mtd, int maxchips)
{
int i, nand_maf_id, nand_dev_id, busw, maf_id;
struct nand_chip *this = mtd->priv; //取出 struct nand_chip结构
/* Get buswidth to select the correct functions*/
busw = this->options & NAND_BUSWIDTH_16; //nand flash的位宽
/* check for proper chip_delay setup, set 20us if not */
if (!this->chip_delay)
this->chip_delay = 20;
/* check, if a user supplied command function given */
if (this->cmdfunc == NULL) //填充命令函数
this->cmdfunc = nand_command;
/* check, if a user supplied wait function given */
if (this->waitfunc == NULL) //填充等待函数
this->waitfunc = nand_wait;
if (!this->select_chip) //s3c2410_nand_init_chip中已定义
this->select_chip = nand_select_chip;
if (!this->write_byte) //使用默认的
this->write_byte = busw ? nand_write_byte16 : nand_write_byte;
if (!this->read_byte) //使用默认的
this->read_byte = busw ? nand_read_byte16 : nand_read_byte;
if (!this->write_word) //使用默认的
this->write_word = nand_write_word;
if (!this->read_word) //使用默认的
this->read_word = nand_read_word;
if (!this->block_bad) //使用默认的
this->block_bad = nand_block_bad;
if (!this->block_markbad) //使用默认的
this->block_markbad = nand_default_block_markbad;
if (!this->write_buf) //s3c2410_nand_init_chip中已定义
this->write_buf = busw ? nand_write_buf16 : nand_write_buf;
if (!this->read_buf) //s3c2410_nand_init_chip中已定义
this->read_buf = busw ? nand_read_buf16 : nand_read_buf;
if (!this->verify_buf) //使用默认的
this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
if (!this->scan_bbt) //使用默认的
this->scan_bbt = nand_default_bbt;
/* Select the device */
this->select_chip(mtd, 0); //片选,可惜在 s3c2410 nand flash控制器中此操作
为空
/* Send the command for reading device ID */
this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);//发送读 ID命令
/* Read manufacturer and device IDs */
nand_maf_id = this->read_byte(mtd); //读取生产商 ID
nand_dev_id = this->read_byte(mtd); //读取设备 ID
/* Print and store flash device information */
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
//保存着 nand flash资料的 nand_flash_ids表在 include/linux/mtd/nand_ids.c文件中,
详细见附录
if (nand_dev_id != nand_flash_ids[i].id) //比较设备 ID
continue;
if (!mtd->name) mtd->name = nand_flash_ids[i].name; //填充设备名
this->chipsize = nand_flash_ids[i].chipsize << 20; //填充设备大小
/* New devices have all the information in additional id bytes */
if (!nand_flash_ids[i].pagesize) {
int extid;
/* The 3rd id byte contains non relevant data ATM */
extid = this->read_byte(mtd);
/* The 4th id byte is the important one */
extid = this->read_byte(mtd);
/* Calc pagesize */
mtd->oobblock = 1024 << (extid & 0x3);
extid >>= 2;
/* Calc oobsize */
mtd->oobsize = (8 << (extid & 0x03)) * (mtd->oobblock / 512);
extid >>= 2;
/* Calc blocksize. Blocksize is multiples of 64KiB */
mtd->erasesize = (64 * 1024) << (extid & 0x03);
extid >>= 2;
/* Get buswidth information */
busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
} else {
/* Old devices have this data hardcoded in the
* device id table */
mtd->erasesize = nand_flash_ids[i].erasesize; //填充檫除单元大小(16k)
mtd->oobblock = nand_flash_ids[i].pagesize; //填充页大小(512)
mtd->oobsize = mtd->oobblock / 32; //oob大小(512/32=16)
busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16;//获取 nand
flash表中定义的位宽
}
/* Try to identify manufacturer */ //比较生产商 ID
for (maf_id = 0; nand_manuf_ids[maf_id].id != 0x0; maf_id++) {
if (nand_manuf_ids[maf_id].id == nand_maf_id)
break;
}
/* Check, if buswidth is correct. Hardware drivers should set
* this correct ! */
/用户定义的位宽与芯片实际的位宽不一致,取消 nand flash的片选
if (busw != (this->options & NAND_BUSWIDTH_16)) {
printk (KERN_INFO "NAND device: Manufacturer ID:"
" 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id,
nand_manuf_ids[maf_id].name , mtd->name);
printk (KERN_WARNING
"NAND bus width %d instead %d bit\n",
(this->options & NAND_BUSWIDTH_16) ? 16 : 8,
busw ? 16 : 8);
this->select_chip(mtd, -1);//在 s3c2410 nand flash控制器驱动中,此操作为
空操作
return 1;
}
/* Calculate the address shift from the page size */
//计算页、可檫除单元、nand flash大小的偏移值
this->page_shift = ffs(mtd->oobblock) - 1;
this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1;
this->chip_shift = ffs(this->chipsize) - 1;
/* Set the bad block position */
//标注此 nand flash为大页还是小页?
this->badblockpos = mtd->oobblock > 512 ?
NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
/* Get chip options, preserve non chip based options */
//用户没指定的选项从 nand flash表中获取补上
this->options &= ~NAND_CHIPOPTIONS_MSK;
this->options |= nand_flash_ids[i].options & NAND_CHIPOPTIONS_MSK;
/* Set this as a default. Board drivers can override it, if neccecary */
this->options |= NAND_NO_AUTOINCR;
/* Check if this is a not a samsung device. Do not clear the options
* for chips which are not having an extended id.
*/
if (nand_maf_id != NAND_MFR_SAMSUNG && !nand_flash_ids[i].pagesize)
this->options &= ~NAND_SAMSUNG_LP_OPTIONS;
/* Check for AND chips with 4 page planes */
if (this->options & NAND_4PAGE_ARRAY)
this->erase_cmd = multi_erase_cmd;
else
this->erase_cmd = single_erase_cmd;
/* Do not replace user supplied command function ! */
if (mtd->oobblock > 512 && this->cmdfunc == nand_command)
this->cmdfunc = nand_command_lp;
printk (KERN_INFO "NAND device: Manufacturer ID:"
" 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id,
nand_manuf_ids[maf_id].name , nand_flash_ids[i].name);
break;
}//好的,检测结束^_^
if (!nand_flash_ids[i].name) {
printk (KERN_WARNING "No NAND device found!!!\n");
this->select_chip(mtd, -1);
return 1;
}
//统计一下同种类型的 nand flash有多少块(我板上只有一块)
for (i=1; i < maxchips; i++) {
this->select_chip(mtd, i);
/* Send the command for reading device ID */
this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);
/* Read manufacturer and device IDs */
if (nand_maf_id != this->read_byte(mtd) ||
nand_dev_id != this->read_byte(mtd))
break;
}
if (i > 1)
printk(KERN_INFO "%d NAND chips detected\n", i);
/* Allocate buffers, if neccecary */
if (!this->oob_buf) {
size_t len;
//求出一个檫除单元 64K中 oob所占用的总空间
len = mtd->oobsize << (this->phys_erase_shift - this->page_shift);
this->oob_buf = kmalloc (len, GFP_KERNEL);
if (!this->oob_buf) {
printk (KERN_ERR "nand_scan(): Cannot allocate oob_buf\n");
return -ENOMEM;
}
this->options |= NAND_OOBBUF_ALLOC;//oob空间已分配,置相应的标志位
}
if (!this->data_buf) {
size_t len;
len = mtd->oobblock + mtd->oobsize;//512+16=128
this->data_buf = kmalloc (len, GFP_KERNEL);
if (!this->data_buf) {
if (this->options & NAND_OOBBUF_ALLOC)
kfree (this->oob_buf);
printk (KERN_ERR "nand_scan(): Cannot allocate data_buf\n");
return -ENOMEM;
}
this->options |= NAND_DATABUF_ALLOC;//数据空间已分配,置相应的标志位
}
/* Store the number of chips and calc total size for mtd */
this->numchips = i;//记录 nand flash片数
mtd->size = i * this->chipsize;//计算出 nand flash总大小
/* Convert chipsize to number of pages per chip -1. */
this->pagemask = (this->chipsize >> this->page_shift) -
1;//(64M>>9)-1=128k-1=0x1ffff
/* Preset the internal oob buffer */
//oob_buf全部置为 0xff
memset(this->oob_buf, 0xff, mtd->oobsize << (this->phys_erase_shift -
this->page_shift));
/* If no default placement scheme is given, select an
* appropriate one */
if (!this->autooob) { //我们选用的是 NAND_ECC_SOFT,autooob未设置
/* Select the appropriate default oob placement scheme for
* placement agnostic filesystems */
switch (mtd->oobsize) {
case 8:
this->autooob = &nand_oob_8;
break;
case 16:
this->autooob = &nand_oob_16;//我们的 nand flash属于这一类
break;
case 64:
this->autooob = &nand_oob_64;
break;
default:
printk (KERN_WARNING "No oob scheme defined for oobsize %d\n",
mtd->oobsize);
BUG();
}
}
注:
ECC的东西不是很懂,先跳过^_^
/* The number of bytes available for the filesystem to place fs dependend
* oob data */
mtd->oobavail = 0;
for (i = 0; this->autooob->oobfree[i][1]; i++)
mtd->oobavail += this->autooob->oobfree[i][1];
/*
* check ECC mode, default to software
* if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize
* fallback to software ECC
*/
this->eccsize = 256; /* set default eccsize */
this->eccbytes = 3;
switch (this->eccmode) {
case NAND_ECC_HW12_2048:
if (mtd->oobblock < 2048) {
printk(KERN_WARNING "2048 byte HW ECC not possible on %d byte page
size, fallback to SW ECC\n",
mtd->oobblock);
this->eccmode = NAND_ECC_SOFT;
this->calculate_ecc = nand_calculate_ecc;
this->correct_data = nand_correct_data;
} else
this->eccsize = 2048;
break;
case NAND_ECC_HW3_512:
case NAND_ECC_HW6_512:
case NA