NAND驱动初始化分析
一. 版本说明
代码分析基于龙芯2K1000平台,内核版本为3.10。
二. NAND驱动初始化分析
龙芯平台的NAND驱动初始化代码入口在 drivers/mtd/nand/ls-nand.c
文件中,通过 module_init()
向内核注册初始化函数 ls_nand_init()
。
ls_nand_init()
负责注册平台驱动:
static int __init ls_nand_init(void)
{
pr_info("%s driver initializing\n", DRIVER_NAME);
return platform_driver_register(&ls_nand_driver);
}
内核启动时,将调用注册的probe回调进行驱动初始化,probe函数的关键代码分析如下:
static int ls_nand_probe(struct platform_device *pdev)
{
struct ls2k_nand_plat_data *pdata;
struct mtd_part_parser_data ppdata;
struct ls_nand_info *info;
struct nand_chip *this;
struct mtd_info *mtd;
struct resource *r;
...
//从设备树中获取DMA信息等
...
pdata = devm_kzalloc(&pdev->dev,
sizeof(struct ls2k_nand_plat_data),
GFP_KERNEL); //为ls2k_nand_plat_data结构体分配存储空间
//设置相关参数
pdata->chip_ver = LS2K_VER3;
pdata->cs = 0;
pdata->csrdy = 0x88442200;
pdata->nr_parts = data;
pdata->enable_arbiter = 1;
mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct ls_nand_info),
GFP_KERNEL); //为mtd_info、ls_nand_info结构体分配空间
//设置相关参数
info = (struct ls_nand_info *)(&mtd[1]);
info->pdev = pdev;
info->chip_version = pdata->chip_ver;
info->cs = pdata->cs;
info->csrdy = pdata->csrdy;
...
...
//获取并保存控制器及DMA的地址
...
ret = ls_nand_init_buff(info); //初始化DMA Buffer
irq = platform_get_irq(pdev, 0); //获取中断号
info->irq = irq;
ls_nand_init_mtd(mtd, info); //**初始化NAND操作回调及ECC,详细分析1
ls_nand_init_info(info); //初始化部分参数及寄存器
dma_desc_init(info); //DMA相关信息初始化
platform_set_drvdata(pdev, mtd); //将mtd_info结构保存到platform_device结构中
//**扫描NAND Flash,详细分析2
if (nand_scan(mtd, 1)) {
goto fail_free_io;
}
//注册MTD设备
ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
return ret;
}
详细分析1:
nand_scan()
在 nand_base.c
中定义,主要负责扫描NAND设备并设置mtd_info
结构体。
int nand_scan(struct mtd_info *mtd, int maxchips)
{
int ret;
//检查错误调用
if (!mtd->owner && caller_is_module()) {...}
ret = nand_scan_ident(mtd, maxchips, NULL);
if (!ret)
ret = nand_scan_tail(mtd);
return ret;
}
nand_scan_ident()
作为 nand_scan()
的第一阶段,负责读取flash ID并根据它设置MTD参数。
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;
struct nand_flash_dev *type;
busw = chip->options & NAND_BUSWIDTH_16; //获取总线宽度
nand_set_defaults(chip, busw); //设置默认回调函数
//**读取flash ID并检查型号是否支持,详细分析1.1
type = nand_get_flash_type(mtd, chip, busw,
&nand_maf_id, &nand_dev_id, table);
...
//检查多片flash
...
//保存芯片数及空间大小
chip->numchips = i;
mtd->size = i * chip->chipsize;
return 0;
}
nand_scan_tail()
作为 nand_scan()
的第二阶段,负责使用默认值填充为初始化的函数指针及建立坏块表。
int nand_scan_tail(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd->priv;
//设置内部oob buffer位置
chip->oob_poi = chip->buffers->databuf + mtd->writesize;
//未指定ecc layout时,为其设置合适的值
if (!chip->ecc.layout && (chip->ecc.mode != NAND_ECC_SOFT_BCH)) {
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:
pr_warn("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:
...
case NAND_ECC_HW:
pr_warn("********* ecc.mode = NAND_ECC_HW. ***************** \n");
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;
...
default:
...
}
//计算剩余的oob空间大小
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;
//设置ecc相关参数
chip->ecc.steps = mtd->writesize / chip->ecc.size;
if (chip->ecc.steps * chip->ecc.size != mtd->writesize) {
pr_warn("Invalid ECC parameters\n");
BUG();
}
chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
...
//填充其余的mtd结构数据
mtd->type = MTD_NANDFLASH;
mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM :
MTD_CAP_NANDFLASH;
mtd->_erase = nand_erase;
...
if (chip->options & NAND_SKIP_BBTSCAN)
return 0;
//建立坏块表
return chip->scan_bbt(mtd);
}
详细分析1.1:
nand_get_flash_type()
负责读取flash ID并检查型号是否支持。
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];
chip->select_chip(mtd, 0);
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
//读取制造商和设备ID
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
*maf_id = chip->read_byte(mtd);
*dev_id = chip->read_byte(mtd);
//再次读取ID,如果两次不匹配则忽略此设备
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
for (i = 0; i < 8; i++)
id_data[i] = chip->read_byte(mtd);
if (id_data[0] != *maf_id || id_data[1] != *dev_id) {
pr_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);
}
//默认在nand_flash_ids结构体中匹配ID
if (!type)
type = nand_flash_ids;
for (; type->name != NULL; type++) {
if (is_full_id_nand(type)) {
if (find_full_id_nand(mtd, chip, type, id_data, &busw))
goto ident_done;
} else if (*dev_id == type->dev_id) {
break;
}
}
...
//根据匹配的结果设置chipsize等部分芯片参数
...
if (!type->pagesize && chip->init_size) {
//由驱动设置pagesize, oobsize, erasesize
busw = chip->init_size(mtd, chip, id_data);
} else if (!type->pagesize) {
//根据ext id解析参数
nand_decode_ext_id(mtd, chip, id_data, &busw);
} else {
nand_decode_id(mtd, chip, type, id_data, &busw);
}
...
ident_done:
...
return type;
}
注:根据上述分析可知,如需增加支持的flash型号,可以将flash的ID信息加入 nand_flash_ids
结构体,它位于 nand_ids.c
文件中。另外flash的部分参数可能较特殊,可在 nand_decode_ext_id()
或 nand_decode_id()
中加入特殊判断进行配置。
三. 向MTD层注册
ls_nand_probe()
最后,调用 mtd_device_parse_register()
向MTD层注册分区,MTD层相关代码此处不作详细分析。
至此,驱动的初始化过程基本完成。