//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) |