Chinaunix首页 | 论坛 | 博客
  • 博客访问: 646701
  • 博文数量: 329
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 693
  • 用 户 组: 普通用户
  • 注册时间: 2015-01-05 23:37
个人简介

Do not panic!

文章存档

2021年(1)

2018年(3)

2017年(7)

2016年(98)

2015年(220)

我的朋友

分类: 嵌入式

2015-01-24 18:24:17

//zz//#########################################################

linux内核代码nand的ecc校验设置分析-zz150124.txt
zz-Write:
    @2015-1-24 16:36:08
    @2015-1-24 18:18:43
    @

REF:
    ti-sdk-am335x-evm-06.00.00.00-Linux-x86-Install.bin
        linux-3.2-sdk6.0-am335x

KeyWords:
    platform_device "omap-gpmc"
        arch/arm/mach-omap2/devices.c => omap_init_gpmc() => omap_device_build()
    platform_driver "omap-gpmc"
        arch/arm/mach-omap2/gpmc.c => gpmc_driver
        => gpmc_probe() => gpmc_nand_init() => platform_device_register(&gpmc_nand_device);

    platform_device "omap2-nand"
        arch/arm/mach-omap2/gpmc-nand.c => gpmc_nand_device
    platform_driver "omap2-nand"
        drivers/mtd/nand/omap2.c => omap_nand_probe() => pdata->ecc_opt
        => info->nand.ecc.mode = NAND_ECC_SOFT;
    nand_correct_data() <= nand_scan_tail()

    omap_correct_data() => switch (info->ecc_opt) => OMAP_ECC_BCH8_CODE_HW

//zz//#########################################################
0.
倒序追踪,函数调用,参数设置流程追踪

1)
搜索 NAND_ECC_SW NAND_ECC_HW BCH8 看是在哪被调用
大致猜到一个文件 /drivers/mtd/nand/omap2.c

2)
看到如下配置 ecc 模式的地方,是个 probe 探针函数
omap_nand_prob()
{
    //z// 来自与平台设备的 platform_data 是 void * 万能类型指针
    pdata = pdev->dev.platform_data;
    
    //z// 根据用户在某处设置的 ecc_opt 选项,设置 nand.ecc.mode 等
    if (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_DEFAULT)
    
    info->nand.ecc.calculate        = omap_calculate_ecc;
    
    info->nand.ecc.mode             = NAND_ECC_HW;

}

3)
但这个 ecc_opt 是哪里设置的呢?
probe 探针是在一个平台驱动,名字是 "omap2-nand"
搜索名字,找到平台设备,看平台设备的 dev.platform_data 中的 ecc_opt 变量怎么设置的

#define DRIVER_NAME "omap2-nand"
static struct platform_driver omap_nand_driver = {
    .probe      = omap_nand_probe,
    .driver
        .name   = DRIVER_NAME, //z// "omap2-nand"
};

平台设备 "omap2-nand"
static struct platform_device gpmc_nand_device = {
    .name       = "omap2-nand",
    ...
};

4)
平台设备 "omap2-nand" 的 dev.platform_data 值在 gpmc_nand_init() 函数中设定
gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data)
{
    gpmc_nand_device.dev.platform_data = gpmc_nand_data;
}

5)
函数 gpmc_nand_init() 在 gpmc_probe() 函数中被调用
arch/arm/mach-omap2/gpmc.c

gpmc_probe()
{
    //z// 又在这个平台设备中的 dev.platform_data 中被设置
    struct gpmc_devices_info *gpmc_device = pdev->dev.platform_data
    
    for (p = gpmc_device->pdata; p; gpmc_device++, p = gpmc_device->pdata)
        if (gpmc_device->flag & GPMC_DEVICE_NAND)
            gpmc_nand_init((struct omap_nand_platform_data *) p);
}

6)
platfrom_driver "omap-gpmc"

探针函数 gpmc_probe() 是在平台设备 "omap-gpmc" 中
#define DRIVER_NAME "omap-gpmc"
static struct platform_driver gpmc_driver = {
    .probe      = gpmc_probe,
    .driver
        .name   = DRIVER_NAME,  //z// "omap-gpmc"
};

搜索名字 "omap-gpmc" 得到平台设备
omap_init_gpmc(gpmc_devices_info *pdata,..)
{
    char *name = "omap-gpmc";
    
    //z// 使用宏创建了一个 platform_device 平台设备
    // 其中的 pdata 参数即为 platform_data
    pdev = omap_device_build(name, -1, oh, pdata,
                pdata_len, NULL, 0, 0);
}

7)
omap_init_gpmc()
查找是在哪里被调用..
找到是在熟悉的
board-am335xevm.c
evm_nand_init()
{
    //z// gpio口复用mux设置
    setup_pin_mux(nand_pin_mux);
    
    //z// mtd 分区表设置
    pdata = omap_nand_init(am335x_nand_partitions,
        ARRAY_SIZE(am335x_nand_partitions), 0, 0,
        &am335x_nand_timings);
    
    //z// 这里不知道什么原因, ecc_opt 没有设置
    // 是默认的 OMAP_ECC_HAMMING_CODE_DEFAULT 即0 吗?
//  pdata->ecc_opt =OMAP_ECC_BCH8_CODE_HW;
    
    pdata->elm_used = true;
    gpmc_device[0].pdata = pdata;
    gpmc_device[0].flag = GPMC_DEVICE_NAND;


    //z// 设置为平台设备的 platform_data 万能类型指针变量
    omap_init_gpmc(gpmc_device, sizeof(gpmc_device));
    omap_init_elm();
}

8)
此函数 evm_nand_init() 被放到了熟悉的这个数组中,在 linux 初始化时候会被调用
至此,linux内核中 nand ecc 配置的整个流程就连起来了.
static struct evm_dev_cfg am335x_dev_cfg[] = {
    ...
    {evm_nand_init, DEV_ON_BASEBOARD, PROFILE_ALL},
    ...
};

//zz//#########################################################
1.
用户配置 ECC 校验方式
arch/arm/mach-omap2/board-am335xevm.c
    static struct evm_dev_cfg am335x_dev_cfg[] = {
    evm_nand_init,...
    ...
    };

evm_nand_init()
{
    //z// 配置 ECC 校验方式为 BCH8 HW ?
    // pdata->ecc_opt =OMAP_ECC_BCH8_CODE_HW;
    pdata->elm_used = true;
    gpmc_device[0].pdata = pdata;
    gpmc_device[0].flag = GPMC_DEVICE_NAND;
    
    omap_init_gpmc(gpmc_device, sizeof(gpmc_device));
}

omap_init_gpmc()
{
    char *name = "omap-gpmc";
    
    pdev = omap_device_build(name, -1, oh, pdata,
                pdata_len, NULL, 0, 0);
}

//zz//#########################################################
2.
最终的实际操作函数,结构体中函数指针赋值
drivers/mtd/nand/omap2.c

omap_nand_prob()
{
    //z// 关键的 ecc_opt 参数,决定 ecc.mode 使用 NAND_ECC_HW 或 NAND_ECC_SW 等等
    if (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_DEFAULT)
    else if
    ...
    
    info->nand.ecc.calculate        = omap_calculate_ecc;
    info->nand.ecc.hwctl            = omap_enable_hwecc;
    info->nand.ecc.correct          = omap_correct_data;
    info->nand.ecc.mode             = NAND_ECC_HW;

}

//zz//#########################################################
3.
关于 nand_ecclayout 使用的 oob | spare 区中的哪些字节做的?

drivers/mtd/nand/omap2.c

1)
static struct nand_ecclayout omap_oobinfo;
此结构体内部各变量的值,是在 omap_nand_probe() 函数中根据 info->nand.ecc 来配置的
配置根据 info->nand 来的

omap_oobinfo 
    <= info->nand
    <= mtd->priv
    <= chip->ecc.layout
    <= nand_oob_64 或者 nand_oob_16,nand_oob_8 等等

2)
omap_nand_probe()
{
    info->mtd.priv      = &info->nand;
    ...
    nand_scan_tail();
    ...
}

nand_scan_tail(struct mtd_info *mtd)
{
    struct nand_chip *chip = mtd->priv;
    
    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);


    }
}

这些 ecclayout 配置要与 u-boot write 时候对应起来才能用呀
这个 nand_oob_64 不是 ECC BCH8 的 14*4 = 56 字节?
那使用的什么 ECC 呢??
static struct nand_ecclayout nand_oob_64 = {
    .eccbytes = 24,
    .eccpos = {
           40, 41, 42, 43, 44, 45, 46, 47,
           48, 49, 50, 51, 52, 53, 54, 55,
           56, 57, 58, 59, 60, 61, 62, 63},
    .oobfree = {
        {.offset = 2,
         .length = 38} }
};




阅读(3402) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~