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