ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

NAND驱动初始化分析

2021-09-27 10:31:12  阅读:189  来源: 互联网

标签:info 初始化 ecc chip nand NAND 驱动 id mtd


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层相关代码此处不作详细分析。

至此,驱动的初始化过程基本完成。

标签:info,初始化,ecc,chip,nand,NAND,驱动,id,mtd
来源: https://blog.csdn.net/DSA567/article/details/120503385

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有