前一篇文章分析了MTD的框架,添加一个MTD设备的方法:
1 填充mtd_info结构体
2 add_mtd_device添加MTD设备
那么这个mtd_info到底怎么填写,不同的设备有不同的操作方法,无论对于NANDFLASH的读写还是对于NORFLASH的读写都有一套自己的标准,这一套标准就叫做操作协议层,这一层内核已经帮我们实现了,这个协议层会帮我们填充好mtd_info结构体,而我们需要做的只是设置好协议层提供的接口。
NAND的对应的协议接口是nand_chip结构体。
我们以Atmel_nand.c为例分析,看他究竟干了什么事情:
static int __init atmel_nand_probe(struct platform_device *pdev)
{
struct atmel_nand_host *host;
struct mtd_info *mtd;
struct nand_chip *nand_chip;
struct resource *regs;
struct resource *mem;
int res;
#ifdef CONFIG_MTD_PARTITIONS
struct mtd_partition *partitions = NULL;
int num_partitions = 0;
#endif
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem) {
printk(KERN_ERR "atmel_nand: can't get I/O resource mem\n");
return -ENXIO;
}
/* Allocate memory for the device structure (and zero it) */
host = kzalloc(sizeof(struct atmel_nand_host), GFP_KERNEL);
if (!host) {
printk(KERN_ERR "atmel_nand: failed to allocate device structure.\n");
return -ENOMEM;
}
host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
if (host->io_base == NULL) {
printk(KERN_ERR "atmel_nand: ioremap failed\n");
res = -EIO;
goto err_nand_ioremap;
}
mtd = &host->mtd;
nand_chip = &host->nand_chip;
host->board = pdev->dev.platform_data;
host->dev = &pdev->dev;
nand_chip->priv = host;
mtd->priv = nand_chip; //nand_chip赋值给mtd私有数据指针,这里就设置好了mtd获取nand_chip的入口
mtd->owner = THIS_MODULE;
/* Set address of NAND IO lines */
nand_chip->IO_ADDR_R = host->io_base; //设置读IO寄存器地址
nand_chip->IO_ADDR_W = host->io_base; //设置写IO寄存器地址
nand_chip->cmd_ctrl = atmel_nand_cmd_ctrl; //设置cmd控制函数
if (host->board->rdy_pin)
nand_chip->dev_ready = atmel_nand_device_ready; //设置nandflash忙等待函数
regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!regs && hard_ecc) {
printk(KERN_ERR "atmel_nand: can't get I/O resource "
"regs\nFalling back on software ECC\n");
}
nand_chip->ecc.mode = NAND_ECC_SOFT; //使能ECC检查
if (no_ecc)
nand_chip->ecc.mode = NAND_ECC_NONE;
if (hard_ecc && regs) {
host->ecc = ioremap(regs->start, regs->end - regs->start + 1);
if (host->ecc == NULL) {
printk(KERN_ERR "atmel_nand: ioremap failed\n");
res = -EIO;
goto err_ecc_ioremap;
}
nand_chip->ecc.mode = NAND_ECC_HW;
nand_chip->ecc.calculate = atmel_nand_calculate;
nand_chip->ecc.correct = atmel_nand_correct;
nand_chip->ecc.hwctl = atmel_nand_hwctl;
nand_chip->ecc.read_page = atmel_nand_read_page;
nand_chip->ecc.bytes = 4;
}
nand_chip->chip_delay = 20; /* 20us command delay time */
if (host->board->bus_width_16) { /* 16-bit bus width */
nand_chip->options |= NAND_BUSWIDTH_16;
nand_chip->read_buf = atmel_read_buf16;
nand_chip->write_buf = atmel_write_buf16;
} else {
nand_chip->read_buf = atmel_read_buf; //设置读buf操作函数
nand_chip->write_buf = atmel_write_buf; //设置写buf操作函数
}
platform_set_drvdata(pdev, host);
atmel_nand_enable(host);
if (host->board->det_pin) {
if (gpio_get_value(host->board->det_pin)) {
printk(KERN_INFO "No SmartMedia card inserted.\n");
res = ENXIO;
goto err_no_card;
}
}
if (on_flash_bbt) {
printk(KERN_INFO "atmel_nand: Use On Flash BBT\n");
nand_chip->options |= NAND_USE_FLASH_BBT;
}
/* first scan to find the device and get the page size */
if (nand_scan_ident(mtd, 1)) { //扫描nandflash
res = -ENXIO;
goto err_scan_ident;
}
if (nand_chip->ecc.mode == NAND_ECC_HW) {
/* ECC is calculated for the whole page (1 step) */
nand_chip->ecc.size = mtd->writesize;
/* set ECC page size and oob layout */
switch (mtd->writesize) {
case 512:
nand_chip->ecc.layout = &atmel_oobinfo_small;
ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_528);
break;
case 1024:
nand_chip->ecc.layout = &atmel_oobinfo_large;
ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_1056);
break;
case 2048:
nand_chip->ecc.layout = &atmel_oobinfo_large;
ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_2112);
break;
case 4096:
nand_chip->ecc.layout = &atmel_oobinfo_large;
ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_4224);
break;
default:
/* page size not handled by HW ECC */
/* switching back to soft ECC */
nand_chip->ecc.mode = NAND_ECC_SOFT;
nand_chip->ecc.calculate = NULL;
nand_chip->ecc.correct = NULL;
nand_chip->ecc.hwctl = NULL;
nand_chip->ecc.read_page = NULL;
nand_chip->ecc.postpad = 0;
nand_chip->ecc.prepad = 0;
nand_chip->ecc.bytes = 0;
break;
}
}
/* second phase scan */
if (nand_scan_tail(mtd)) { //完善mtd结构体,设置函数指针
res = -ENXIO;
goto err_scan_tail;
}
#ifdef CONFIG_MTD_PARTITIONS
#ifdef CONFIG_MTD_CMDLINE_PARTS
mtd->name = "atmel_nand";
num_partitions = parse_mtd_partitions(mtd, part_probes,
&partitions, 0);
#endif
if (num_partitions <= 0 && host->board->partition_info)
partitions = host->board->partition_info(mtd->size,
&num_partitions);
if ((!partitions) || (num_partitions == 0)) {
printk(KERN_ERR "atmel_nand: No partitions defined, or unsupported device.\n");
res = ENXIO;
goto err_no_partitions;
}
res = add_mtd_partitions(mtd, partitions, num_partitions); //如果有分区,添加多个mtd设备
#else
res = add_mtd_device(mtd); //如果没分区直接添加mtd设备
#endif
if (!res)
return res;
#ifdef CONFIG_MTD_PARTITIONS
err_no_partitions:
#endif
nand_release(mtd);
err_scan_tail:
err_scan_ident:
err_no_card:
atmel_nand_disable(host);
platform_set_drvdata(pdev, NULL);
if (host->ecc)
iounmap(host->ecc);
err_ecc_ioremap:
iounmap(host->io_base);
err_nand_ioremap:
kfree(host);
return res;
}
从这个例子可以看出NANFLASH驱动由下面两个部分组成:
1 两个重要结构体:
nand_chip
mtd_info
2 三个重点函数:
nand_scan_ident
nand_scan_tail
(上面两个合起来一步nand_scan)
add_mtd_device
一.分配并设置协议层接口 nand_chip
二.匹配协议 nand_scan(扫描设备并填充mtd_info)
三.添加mtd设备
所以一个MTD设备的主要区别在于匹配的协议,不同的设备对于传输的数据的协议不一样。
这里NAND的协议是通过nand_scan:
nand_scan包含nand_scan_ident与nand_scan_tail:
int nand_scan_ident(struct mtd_info *mtd, int maxchips,struct nand_flash_dev *table)
{
int i, busw, nand_maf_id, nand_dev_id;
struct nand_chip *chip = mtd->priv; //私有数据中获取nand_chip
struct nand_flash_dev *type;
busw = chip->options & NAND_BUSWIDTH_16; //获取总线宽度
nand_set_defaults(chip, busw); //设置nand_chip默认方法函数
type = nand_get_flash_type(mtd, chip, busw,&nand_maf_id, &nand_dev_id, table); //获取nand flash设备类型
if (IS_ERR(type)) {
if (!(chip->options & NAND_SCAN_SILENT_NODEV))
printk(KERN_WARNING "No NAND device found.\n");
chip->select_chip(mtd, -1);
return PTR_ERR(type);
}
for (i = 1; i < maxchips; i++) { //芯片个数
chip->select_chip(mtd, i); //调用select_chip方法选中芯片
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); //调用NAND_CMD_RESET命令重启设备
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); //调用NAND_CMD_READID命令读取设备ID
if (nand_maf_id != chip->read_byte(mtd) || nand_dev_id != chip->read_byte(mtd)) //读厂商ID和设备ID
break;
}
if (i > 1)
printk(KERN_INFO "%d NAND chips detected\n", i);
chip->numchips = i; //芯片数
mtd->size = i * chip->chipsize; //MTD设备总容量
return 0;
}
EXPORT_SYMBOL(nand_scan_ident);
1.1nand_set_defaults设置一些默认方法
static void nand_set_defaults(struct nand_chip *chip, int busw)
{
if (!chip->chip_delay)
chip->chip_delay = 20;
if (chip->cmdfunc == NULL)
chip->cmdfunc = nand_command; //nand命令
if (chip->waitfunc == NULL)
chip->waitfunc = nand_wait; //等待命令执行完
if (!chip->select_chip)
chip->select_chip = nand_select_chip; //片选
if (!chip->read_byte)
chip->read_byte = busw ? nand_read_byte16 : nand_read_byte; //读字节
if (!chip->read_word)
chip->read_word = nand_read_word; //读字
if (!chip->block_bad)
chip->block_bad = nand_block_bad; //坏块查找
if (!chip->block_markbad)
chip->block_markbad = nand_default_block_markbad; //标记坏块
if (!chip->write_buf)
chip->write_buf = busw ? nand_write_buf16 : nand_write_buf; //写缓冲区
if (!chip->read_buf)
chip->read_buf = busw ? nand_read_buf16 : nand_read_buf; //读缓冲区
if (!chip->verify_buf)
chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf; //校验缓冲区
if (!chip->scan_bbt)
chip->scan_bbt = nand_default_bbt; //扫描bbt
if (!chip->controller) {
chip->controller = &chip->hwcontrol;
spin_lock_init(&chip->controller->lock);
init_waitqueue_head(&chip->controller->wq);
}
}
1.2nand_get_flash_type获取nand flash类型
static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,struct nand_chip *chip,int busw,int *maf_id, int *dev_id,struct nand_flash_dev *type)
{
int i, maf_idx;
u8 id_data[8];
int ret;
chip->select_chip(mtd, 0); //片选芯片
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); //重启芯片
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); //读芯片ID
*maf_id = chip->read_byte(mtd); //读工厂id
*dev_id = chip->read_byte(mtd); //读设备id
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); //读芯片ID
for (i = 0; i < 2; i++)
id_data[i] = chip->read_byte(mtd); //读取芯片ID
if (id_data[0] != *maf_id || id_data[1] != *dev_id) { //比较ID值
printk(KERN_INFO "%s: second ID read did not match %02x,%02x against %02x,%02x\n", __func__,*maf_id, *dev_id, id_data[0], id_data[1]);
return ERR_PTR(-ENODEV);
}
if (!type)
type = nand_flash_ids;
for (; type->name != NULL; type++) //获取匹配的设备
if (*dev_id == type->id)
break;
chip->onfi_version = 0;
if (!type->name || !type->pagesize) { //检测是否onfi接口标准
ret = nand_flash_detect_onfi(mtd, chip, busw);
if (ret)
goto ident_done;
}
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); //读芯片ID
for (i = 0; i < 8; i++) //读取全部id信息
id_data[i] = chip->read_byte(mtd);
if (!type->name)
return ERR_PTR(-ENODEV);
if (!mtd->name)
mtd->name = type->name; //设置名字
chip->chipsize = (uint64_t)type->chipsize << 20; //获取芯片容量
if (!type->pagesize && chip->init_size) {
busw = chip->init_size(mtd, chip, id_data); //初始化芯片容量
}
else if (!type->pagesize) {
int extid;
chip->cellinfo = id_data[2];
extid = id_data[3];
if (id_data[0] == id_data[6] && id_data[1] == id_data[7] && id_data[0] == NAND_MFR_SAMSUNG
&& (chip->cellinfo & NAND_CI_CELLTYPE_MSK) && id_data[5] != 0x00) {
mtd->writesize = 2048 << (extid & 0x03);
extid >>= 2;
switch (extid & 0x03) {
case 1:
mtd->oobsize = 128;
break;
case 2:
mtd->oobsize = 218;
break;
case 3:
mtd->oobsize = 400;
break;
default:
mtd->oobsize = 436;
break;
}
extid >>= 2;
mtd->erasesize = (128 * 1024) << (((extid >> 1) & 0x04) | (extid & 0x03));
busw = 0;
}
else {
mtd->writesize = 1024 << (extid & 0x03);
extid >>= 2;
mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
extid >>= 2;
mtd->erasesize = (64 * 1024) << (extid & 0x03);
extid >>= 2;
busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
}
}
else {
mtd->erasesize = type->erasesize; //擦除尺寸
mtd->writesize = type->pagesize; //页尺寸
mtd->oobsize = mtd->writesize / 32; //oob区尺寸
busw = type->options & NAND_BUSWIDTH_16; //数据宽度
if (*maf_id == NAND_MFR_AMD && id_data[4] != 0x00 && id_data[5] == 0x00 && id_data[6] == 0x00 && id_data[7] == 0x00 && mtd->writesize == 512) {
mtd->erasesize = 128 * 1024;
mtd->erasesize <<= ((id_data[3] & 0x03) << 1);
}
}
chip->options &= ~NAND_CHIPOPTIONS_MSK;
chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
ident_done:
chip->options |= NAND_NO_AUTOINCR;
for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
if (nand_manuf_ids[maf_idx].id == *maf_id)
break;
}
if (busw != (chip->options & NAND_BUSWIDTH_16)) {
printk(KERN_INFO "NAND device: Manufacturer ID: 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,*dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
printk(KERN_WARNING "NAND bus width %d instead %d bit\n",(chip->options & NAND_BUSWIDTH_16) ? 16 : 8,busw ? 16 : 8);
return ERR_PTR(-EINVAL);
}
chip->page_shift = ffs(mtd->writesize) - 1;
chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
chip->bbt_erase_shift = chip->phys_erase_shift =
ffs(mtd->erasesize) - 1;
if (chip->chipsize & 0xffffffff)
chip->chip_shift = ffs((unsigned)chip->chipsize) - 1;
else {
chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32));
chip->chip_shift += 32 - 1;
}
chip->badblockbits = 8;
if (mtd->writesize > 512 || (busw & NAND_BUSWIDTH_16)) //设置坏块位置
chip->badblockpos = NAND_LARGE_BADBLOCK_POS;
else
chip->badblockpos = NAND_SMALL_BADBLOCK_POS;
if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) && (*maf_id == NAND_MFR_SAMSUNG || *maf_id == NAND_MFR_HYNIX))
chip->options |= NAND_BBT_SCANLASTPAGE;
else if ((!(chip->cellinfo & NAND_CI_CELLTYPE_MSK) && (*maf_id == NAND_MFR_SAMSUNG || *maf_id == NAND_MFR_HYNIX
|| *maf_id == NAND_MFR_TOSHIBA || *maf_id == NAND_MFR_AMD)) || (mtd->writesize == 2048 && *maf_id == NAND_MFR_MICRON))
chip->options |= NAND_BBT_SCAN2NDPAGE;
if (!(busw & NAND_BUSWIDTH_16) && *maf_id == NAND_MFR_STMICRO && mtd->writesize == 2048) {
chip->options |= NAND_BBT_SCANBYTE1AND6;
chip->badblockpos = 0;
}
if (chip->options & NAND_4PAGE_ARRAY)
chip->erase_cmd = multi_erase_cmd;
else
chip->erase_cmd = single_erase_cmd;
if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
chip->cmdfunc = nand_command_lp;
printk(KERN_INFO "NAND device: Maf ID: 0x%02x,Chip ID: 0x%02x (%s, %s)\n erasesize: 0x%x, writesize: %d, oobsize: %d\n", *maf_id, *dev_id,
nand_manuf_ids[maf_idx].name, chip->onfi_version ? type->name : chip->onfi_params.model,mtd->erasesize, mtd->writesize, mtd->oobsize);
return type;
}
2.nand_scan_tail填充未初始化的功能函数默认函数并扫描坏块表
int nand_scan_tail(struct mtd_info *mtd)
{
int i;
struct nand_chip *chip = mtd->priv;
if (!(chip->options & NAND_OWN_BUFFERS))
chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);
if (!chip->buffers)
return -ENOMEM;
chip->oob_poi = chip->buffers->databuf + mtd->writesize; //计算oob位置
if (!chip->ecc.layout) { //设置ecc区布局
switch (mtd->oobsize) {
case 8:
chip->ecc.layout = &nand_oob_8;
break;
case 16:
chip->ecc.layout = &nand_oob_16;
break;
case 64:
chip->ecc.layout = &nand_oob_64;
break;
case 128:
chip->ecc.layout = &nand_oob_128;
break;
default:
printk(KERN_WARNING "No oob scheme defined for oobsize %d\n", mtd->oobsize);
BUG();
}
}
if (!chip->write_page) //没有页写方法
chip->write_page = nand_write_page; //指定默认的页写方法
switch (chip->ecc.mode) {
case NAND_ECC_HW_OOB_FIRST:
if (!chip->ecc.calculate || !chip->ecc.correct || !chip->ecc.hwctl) {
printk(KERN_WARNING "No ECC functions supplied; Hardware ECC not possible\n");
BUG();
}
if (!chip->ecc.read_page)
chip->ecc.read_page = nand_read_page_hwecc_oob_first;
case NAND_ECC_HW:
if (!chip->ecc.read_page)
chip->ecc.read_page = nand_read_page_hwecc;
if (!chip->ecc.write_page)
chip->ecc.write_page = nand_write_page_hwecc;
if (!chip->ecc.read_page_raw)
chip->ecc.read_page_raw = nand_read_page_raw;
if (!chip->ecc.write_page_raw)
chip->ecc.write_page_raw = nand_write_page_raw;
if (!chip->ecc.read_oob)
chip->ecc.read_oob = nand_read_oob_std;
if (!chip->ecc.write_oob)
chip->ecc.write_oob = nand_write_oob_std;
case NAND_ECC_HW_SYNDROME:
if ((!chip->ecc.calculate || !chip->ecc.correct || !chip->ecc.hwctl) &&
(!chip->ecc.read_page || chip->ecc.read_page == nand_read_page_hwecc ||
!chip->ecc.write_page || chip->ecc.write_page == nand_write_page_hwecc)) {
printk(KERN_WARNING "No ECC functions supplied; Hardware ECC not possible\n");
BUG();
}
if (!chip->ecc.read_page)
chip->ecc.read_page = nand_read_page_syndrome;
if (!chip->ecc.write_page)
chip->ecc.write_page = nand_write_page_syndrome;
if (!chip->ecc.read_page_raw)
chip->ecc.read_page_raw = nand_read_page_raw_syndrome;
if (!chip->ecc.write_page_raw)
chip->ecc.write_page_raw = nand_write_page_raw_syndrome;
if (!chip->ecc.read_oob)
chip->ecc.read_oob = nand_read_oob_syndrome;
if (!chip->ecc.write_oob)
chip->ecc.write_oob = nand_write_oob_syndrome;
if (mtd->writesize >= chip->ecc.size)
break;
printk(KERN_WARNING "%d byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",chip->ecc.size, mtd->writesize);
chip->ecc.mode = NAND_ECC_SOFT;
case NAND_ECC_SOFT:
chip->ecc.calculate = nand_calculate_ecc;
chip->ecc.correct = nand_correct_data;
chip->ecc.read_page = nand_read_page_swecc;
chip->ecc.read_subpage = nand_read_subpage;
chip->ecc.write_page = nand_write_page_swecc;
chip->ecc.read_page_raw = nand_read_page_raw;
chip->ecc.write_page_raw = nand_write_page_raw;
chip->ecc.read_oob = nand_read_oob_std;
chip->ecc.write_oob = nand_write_oob_std;
if (!chip->ecc.size)
chip->ecc.size = 256;
chip->ecc.bytes = 3;
break;
case NAND_ECC_NONE:
printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n");
chip->ecc.read_page = nand_read_page_raw;
chip->ecc.write_page = nand_write_page_raw;
chip->ecc.read_oob = nand_read_oob_std;
chip->ecc.read_page_raw = nand_read_page_raw;
chip->ecc.write_page_raw = nand_write_page_raw;
chip->ecc.write_oob = nand_write_oob_std;
chip->ecc.size = mtd->writesize;
chip->ecc.bytes = 0;
break;
default:
printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n",chip->ecc.mode);
BUG();
}
chip->ecc.layout->oobavail = 0;
for (i = 0; chip->ecc.layout->oobfree[i].length && i < ARRAY_SIZE(chip->ecc.layout->oobfree); i++)
chip->ecc.layout->oobavail += chip->ecc.layout->oobfree[i].length;
mtd->oobavail = chip->ecc.layout->oobavail;
chip->ecc.steps = mtd->writesize / chip->ecc.size;
if (chip->ecc.steps * chip->ecc.size != mtd->writesize) {
printk(KERN_WARNING "Invalid ecc parameters\n");
BUG();
}
chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {
switch (chip->ecc.steps) {
case 2:
mtd->subpage_sft = 1;
break;
case 4:
case 8:
case 16:
mtd->subpage_sft = 2;
break;
}
}
chip->subpagesize = mtd->writesize >> mtd->subpage_sft;
chip->state = FL_READY; //初始化状态
chip->select_chip(mtd, -1); //选中芯片
chip->pagebuf = -1;
mtd->type = MTD_NANDFLASH; //MTD设备类型
mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM :MTD_CAP_NANDFLASH;
mtd->erase = nand_erase; //擦除方法
mtd->point = NULL;
mtd->unpoint = NULL;
mtd->read = nand_read; //读方法
mtd->write = nand_write; //写方法
mtd->panic_write = panic_nand_write;
mtd->read_oob = nand_read_oob; //读oob区
mtd->write_oob = nand_write_oob; //写oob区
mtd->sync = nand_sync; //同步
mtd->lock = NULL;
mtd->unlock = NULL;
mtd->suspend = nand_suspend; //挂起
mtd->resume = nand_resume; //唤醒
mtd->block_isbad = nand_block_isbad; //坏块判断
mtd->block_markbad = nand_block_markbad; //标记坏块
mtd->writebufsize = mtd->writesize;
mtd->ecclayout = chip->ecc.layout; //ecc布局
if (chip->options & NAND_SKIP_BBTSCAN)
return 0;
return chip->scan_bbt(mtd); //扫描,标记坏块
}
EXPORT_SYMBOL(nand_scan_tail);
3.调用add_mtd_device或调用add_mtd_partitions添加mtd device
综合上面写好框架:
/* 参考
* drivers\mtd\nand\s3c2410.c
* drivers\mtd\nand\Atmel_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>
static struct nand_chip *s3c_nand;
static struct mtd_info *s3c_mtd;
static void s3c2440_select_chip(struct mtd_info *mtd, int chipnr)
{
if (chipnr == -1)
{
/* 取消选中: NFCONT[1]设为0 */
}
else
{
/* 选中: NFCONT[1]设为1 */
}
}
static void s3c2440_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
if (ctrl & NAND_CLE)
{
/* 发命令: NFCMMD=dat */
}
else
{
/* 发地址: NFADDR=dat */
}
}
static int s3c2440_dev_ready(struct mtd_info *mtd)
{
return "NFSTAT的bit[0]";
}
static int s3c_nand_init(void)
{
/* 分配一个nand_chip结构体 */
s3c_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
/* 设置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 = "NFDATA的虚拟地址";
s3c_nand->IO_ADDR_W = "NFDATA的虚拟地址";
s3c_nand->dev_ready = s3c2440_dev_ready;
/* 硬件相关的设置 */
/* 使用: 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, 1); /* 识别NAND FLASH, 填充mtd_info */
/* 5. add_mtd_partitions */
return 0;
}
static void s3c_nand_exit(void)
{
}
module_init(s3c_nand_init);
module_exit(s3c_nand_exit);
MODULE_LICENSE("GPL");
最后根据芯片手册完成具体操作:
#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[] = { //4个分区,分区地址和uboot对应
[0] = {
.name = "bootloader",
.size = 0x00040000,
.offset = 0,
},
[1] = {
.name = "params",
.offset = MTDPART_OFS_APPEND, //接着上面结束地址
.size = 0x00020000,
},
[2] = {
.name = "kernel",
.offset = MTDPART_OFS_APPEND,
.size = 0x00200000,
},
[3] = {
.name = "root",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
}
};
static void s3c2440_select_chip(struct mtd_info *mtd, int chipnr)
{
if (chipnr == -1)
{
/* 取消选中: NFCONT[1]设为1 */
s3c_nand_regs->nfcont |= (1<<1);
}
else
{
/* 选中: NFCONT[1]设为0 */
s3c_nand_regs->nfcont &= ~(1<<1);
}
}
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 & (1<<0));
}
static int s3c_nand_init(void)
{
struct clk *clk;
/* 分配一个nand_chip结构体 */
s3c_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
s3c_nand_regs = ioremap(0x4E000000, sizeof(struct s3c_nand_regs));
/* 设置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;
/* 硬件相关的设置: 根据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<<12) | (TWRPH0<<8) | (TWRPH1<<4);
/* NFCONT:
* BIT1-设为1, 取消片选
* BIT0-设为1, 使能NAND FLASH控制器
*/
s3c_nand_regs->nfcont = (1<<1) | (1<<0);
/* 分配mtd,并使用: nand_scan初始化mtd */
s3c_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
s3c_mtd->owner = THIS_MODULE;
s3c_mtd->priv = s3c_nand;
nand_scan(s3c_mtd, 1); /* 识别NAND FLASH, 构造mtd_info */
/* add_mtd_partitions */
add_mtd_partitions(s3c_mtd, s3c_nand_parts, 4);
//add_mtd_device(s3c_mtd); //如果不需要分区,使用这个
return 0;
}
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");
这样就完成了一个完整的NANFLASH驱动(MTD设备端)。
————————————————————————————————————————————————————————测试方法:
1. make menuconfig去掉内核自带的NAND FLASH驱动
-> Device Drivers
-> Memory Technology Device (MTD) support
-> NAND Device Support
< > NAND Flash support for S3C2410/S3C2440 SoC
2. make uImage
使用新内核启动, 并且使用NFS作为根文件系统
3. insmod s3c_nand.ko
4. 格式化 (参考下面编译工具)
flash_eraseall /dev/mtd3 // yaffs
5. 挂接
mount -t yaffs /dev/mtdblock3 /mnt
6. 在/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/
————————————————————————————————————————————————————————————————
附NANFLASH操作原理:
问1. 原理图上NAND FLASH和S3C2440之间只有数据线,
怎么传输地址?
答1.在DATA0~DATA7上既传输数据,又传输地址
当ALE为高电平时传输的是地址
问2. 从NAND FLASH芯片手册可知,要操作NAND FLASH需要先发出命令
怎么传入命令?
答2.在DATA0~DATA7上既传输数据,又传输地址,也传输命令
当ALE为高电平时传输的是地址,
当CLE为高电平时传输的是命令
当ALE和CLE都为低电平时传输的是数据
问3. 数据线既接到NAND FLASH,也接到NOR FLASH,还接到SDRAM、DM9000等等
那么怎么避免干扰?
答3. 这些设备,要访问之必须"选中",
没有选中的芯片不会工作,相当于没接一样
问4. 假设烧写NAND FLASH,把命令、地址、数据发给它之后,
NAND FLASH肯定不可能瞬间完成烧写的,
怎么判断烧写完成?
答4. 通过状态引脚RnB来判断:它为高电平表示就绪,它为低电平表示正忙
问5. 怎么操作NAND FLASH呢?
答5. 根据NAND FLASH的芯片手册,一般的过程是:
发出命令
发出地址
发出数据/读数据
NANFLASH与S3C2440 NANDFLASH控制器操作状态表:
NAND FLASH S3C2440
发命令:
选中芯片
CLE设为高电平 NFCMMD=命令值
在DATA0~DATA7上输出命令值
发出一个写脉冲
发地址:
选中芯片 NFADDR=地址值
ALE设为高电平
在DATA0~DATA7上输出地址值
发出一个写脉冲
发数据 :
选中芯片 NFDATA=数据值
ALE,CLE设为低电平
在DATA0~DATA7上输出数据值
发出一个写脉冲
读数据:
选中芯片 val=NFDATA
发出读脉冲
读DATA0~DATA7的数据
————————————————————————————————————————————————————————————————————