天天看點

linux核心代碼nand的ecc校驗設定分析-zz150124

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

linux核心代碼nand的ecc校驗設定分析-zz150124

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} }

};