之前提到nand驱动的初始化分析,有一个结构体 struct mtd_info始终贯穿这些代码
再来分析一下这个结构体的基本功能,如何初始化,如何使用
一、分析过程
看看结构体的出现和使用方式
第一次出现在文件\u-boot-sunxi-sunxi\drivers\mtd\nand\nand.c内:
#ifndef CONFIG_SYS_NAND_SELF_INIT
static void nand_init_chip(int i)
{
struct mtd_info *mtd= &nand_info[i];
struct nand_chip *nand = &nand_chip[i];
ulong base_addr = base_address[i];
int maxchips = CONFIG_SYS_NAND_MAX_CHIPS;
if (maxchips < 1)
maxchips = 1;
mtd->priv = nand;
nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr;
if (board_nand_init(nand))
return;
if (nand_scan(mtd, maxchips))
return;
nand_register(i);
}
#endif
看这句代码:
struct mtd_info *mtd = &nand_info[i];
结构体指向全局变量nand_info,这个变量就是nand设备的信息
再看初始化:
mtd->priv = nand;
mtd的私有数据就是一个struct nand_chip类型的结构体
从编程的角度来说,一个硬件驱动应该有两个面,一个面向上层,提供接口;一个面向底层,提供硬件操作
广义上来看:
struct mtd_info就是面向上层,提供数据接口
struct nand_chip面向nand设备,提供硬件接口
假如:mtd->priv = nand; 初始化为另外一种设备的结构体,例如nor flash,那么mtd就是一种nor flash的驱动,
用户实现nor flash相关的操作即可。
struct mtd_info结构体来自linux内核的MTD子系统,u-boot使用时进行了一些简化使用,毕竟不是操作系统,很多问题可以不用考虑
MTD的全称是memory technology device,主要针对是用于访问memory设备(ROM、flash),其目的就是简化驱动的更新,
例如cubieboard接了一个nand flash,型号是K9GBG08U0A,如果没有这个驱动如何简单添加呢?
从人的正常思考角度,加了一个nand,无外乎读,写,刷新几种主要操作,而MTD就提供了这几种操作,
用户在使用时需要实现这个接口就可以了,至于数据的格式,支持什么文件格式yaffs,ext3,ext4用户无须关心
MTD上层已经实现了,用户提供读写,刷新等等基本操作就可以了。大大简化了一个驱动的开发工作量。
基于这种思路,来看看struct mtd_info结构体
struct mtd_info {
u_char type; // 设备类型,指示这是一种什么设备,MTD支持多种设备
u_int32_t flags;
uint64_t size;/* Total size of the MTD */ // 总容量,假设板上有5颗nand flash,容量之和就是这个数
/* "Major" erase size for the device. Na茂ve users may take this
* to be the only erase size available, or may use the more detailed
* information below if they desire
*/
u_int32_t erasesize; // 刷新大小,对于nand,一块为单位刷新,对于cubieboard上的K9GBG08U0A,一个块就是1M大小
再来看看mtd里面这些参数是怎么初始化的,从代码流程分析一下
调用关系
nand_scan --> nand_scan_ident --> nand_get_flash_type
nand_scan 如何调用的需要读我前面一篇文章:http://blog.csdn.net/andy_wsj/article/details/9335755
nand_get_flash_type函数里面设置了几个变量,看看代码:
static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
struct nand_chip *chip,
int busw,
int *maf_id, int *dev_id,
const struct nand_flash_dev *type)
{
int i, maf_idx;
u8 id_data[8];
int ret;
/* Select the device */
chip->select_chip(mtd, 0); // 片选函数,用户驱动自己实现
/*
* Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
* after power-up
*/
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); // 命令操作函数----复位,用户驱动自己实现
/* Send the command for reading device ID */
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); // 命令操作函数----读ID
/* Read manufacturer and device IDs */
*maf_id = chip->read_byte(mtd); // 厂商ID
*dev_id = chip->read_byte(mtd); // 设备id
/* Try again to make sure, as some systems the bus-hold or other
* interface concerns can cause random data which looks like a
* possibly credible NAND flash to appear. If the two results do
* not match, ignore the device completely.
*/
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
for (i = 0; i < 2; i++) // 读两个字节,参考nand芯片资料,第1、2个字节就是厂商id和设备id
id_data[i] = chip->read_byte(mtd);
if (id_data[0] != *maf_id || id_data[1] != *dev_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) // 这个指针调用时传的是NULL,那么将指向nand_flash_ids
type = nand_flash_ids; // nand_flash_ids就是MTD支持的nand设备列表,在文件\u-boot-sunxi-sunxi\drivers\mtd\nand\nand_ids.c内
// cubieboard使用的K9GBG08U0A也在这个列表内,如果不再,自己定义,传过来也可以
for (; type->name != NULL; type++) // 通过设备id找到对应的设备
if (*dev_id == type->id)
break;
chip->onfi_version = 0;
if (!type->name || !type->pagesize) { // 如果找到设备,看看是不是ONFI标准芯片,这是一个intel推出的nand接口标准
/* Check is chip is ONFI compliant */ // 需要定义宏CONFIG_SYS_NAND_ONFI_DETECTION
ret = nand_flash_detect_onfi(mtd, chip, &busw); // 不用看资料,三星的芯片不会去凑intel的标准,不是让人宰吗?没有定义上述宏,调用返回值是0
if (ret) // 最后我忍不住看看K9GBG08U0A的资料,没有任何地方提到ONFI
goto ident_done;
}
// 若不是ONFI flash ,通过以下代码初始化
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
/* Read entire ID string */
for (i = 0; i < 8; i++)
id_data[i] = chip->read_byte(mtd); // 读取8个字节id信息,这个就要了解nand的id信息组成和作用了,不清楚的可以去网上找一找
if (!type->name)
return ERR_PTR(-ENODEV);
if (!mtd->name) // 初始化设备名, 操作mtd
mtd->name = type->name;
chip->chipsize = (uint64_t)type->chipsize << 20; // 芯片容量,按MB计算
if (!type->pagesize && chip->init_size) { // 若用户自己定义了初始化函数chip->init_size,则使用用户的初始化
/* set the pagesize, oobsize, erasesize by the driver*/
busw = chip->init_size(mtd, chip, id_data);
} else if (!type->pagesize) {
int extid;
/* The 3rd id byte holds MLC / multichip data */
chip->cellinfo = id_data[2];
/* The 4th id byte is the important one */
extid = id_data[3];
/*
* Field definitions are in the following datasheets:
* Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32)
* New style (6 byte ID): Samsung K9GBG08U0M (p.40)
*
* Check for wraparound + Samsung ID + nonzero 6th byte
* to decide what to do.
*/
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) {
/* Calc pagesize */
mtd->writesize = 2048 << (extid & 0x03);
extid >>= 2;
/* Calc oobsize */
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;
/* Calc blocksize */
mtd->erasesize = (128 * 1024) <<
(((extid >> 1) & 0x04) | (extid & 0x03));
busw = 0;
} else {
/* Calc pagesize */
mtd->writesize = 1024 << (extid & 0x03);
extid >>= 2;
/* Calc oobsize */
mtd->oobsize = (8 << (extid & 0x01)) *
(mtd->writesize >> 9);
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 chip data hardcoded in the device id table
*/
mtd->erasesize = type->erasesize;
mtd->writesize = type->pagesize;
mtd->oobsize = mtd->writesize / 32;
busw = type->options & NAND_BUSWIDTH_16;
/*
* Check for Spansion/AMD ID + repeating 5th, 6th byte since
* some Spansion chips have erasesize that conflicts with size
* listed in nand_ids table
* Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39)
*/
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);
}
}
/* Get chip options, preserve non chip based options */
chip->options |= type->options;
/* Check if chip is a not a samsung device. Do not clear the
* options for chips which are not having an extended id.
*/
if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
ident_done:
............篇幅关系,部分代码略............
return type;
}
这里初始化了:
mtd->name
mtd->erasesize
mtd->writesize
mtd->oobsize
这些都是描述nand的数据
那么操作函数在哪初始化呢?
nand_scan --> nand_scan_tail
如果上面的操作成功,那么就会调用函数nand_scan_tail初始化结构体定义的各种操作,
这个函数在文件\u-boot-sunxi-sunxi\drivers\mtd\nand\nand_base.c内,
函数比较长,截取其中一部分:
int nand_scan_tail(struct mtd_info *mtd)
{
.........略去N行..........
/* Fill in remaining MTD driver data */
mtd->type = MTD_NANDFLASH;
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->read_oob = nand_read_oob;
mtd->write_oob = nand_write_oob;
mtd->sync = nand_sync;
mtd->lock = NULL;
mtd->unlock = NULL;
mtd->block_isbad = nand_block_isbad;
mtd->block_markbad = nand_block_markbad;
/* propagate ecc.layout to mtd_info */
mtd->ecclayout = chip->ecc.layout;
/* Check, if we should skip the bad block table scan */
if (chip->options & NAND_SKIP_BBTSCAN)
chip->options |= NAND_BBT_SCANNED;
return 0;
}
可以看出,调用这个函数之后,一个MTD类型的nand驱动就建立了
上层,例如文件系统在写一个文件会调用mtd->write,从而进入函数nand_write();
至于mtd子系统如何与上层打交道,驱动工程师可以不关心。
就像我们知道一个CPU管脚的用法就可以了,不关心其内部电路结构也可以使用它。
当然,你要提升系统性能的时候,才需要每个点都去深究,那是是高级内容了。
这些初始化完成之后,通过以下调用路径加入处理链表中
nand_init --> nand_init_chip --> nand_register --> add_mtd_device
代码就是
#ifdef CONFIG_MTD_DEVICE
/*
* Add MTD device so that we can reference it later
* via the mtdcore infrastructure (e.g. ubi).
*/
add_mtd_device(mtd);
#endif
二、总结
如果需要初始化mtd模块
1、需要定义宏CONFIG_MTD_DEVICE
2、需要查找nand flash芯片资料,获取设备id,查找mtd是否默认支持,
若不支持,考虑自行添加确保初始化正确
3、初始化基本的操作,读、写、刷新等等