Chinaunix首页 | 论坛 | 博客
  • 博客访问: 49319
  • 博文数量: 8
  • 博客积分: 176
  • 博客等级: 入伍新兵
  • 技术积分: 85
  • 用 户 组: 普通用户
  • 注册时间: 2009-09-27 08:58
文章分类

全部博文(8)

文章存档

2016年(1)

2015年(1)

2011年(3)

2009年(3)

我的朋友

分类: 嵌入式

2015-12-05 16:56:01

http://blog.csdn.net/fulinus/article/details/8737129
http://blog.chinaunix.net/uid-20789836-id-370514.html

一、在2440的uboot和内核中添加nandflash硬件ECC
平台                        :             tq2440开发板,fl2440开发板
bootload                :             u-boot-2010.09
转载声明:
http://blog.csdn.net/sonbai/article/details/8737129
借鉴:
http://blog.csdn.net/zhaocj/article/details/6709948
http://blog.chinaunix.net/uid-24063584-id-2642193.html
————————————————————————————————————————
一下是以smdk2410为模板,对代码稍加改造的基础上,添加nandflash硬件ECC的功能如下:


第一步:在include/configs/fl2440.h文件中添加三个宏定义,因为我的nandflash的页大小是512B的所以下面是的
#define CONFIG_S3C2440_NAND_HWECC
#define CONFIG_SYS_NAND_ECCSIZE 512
#define CONFIG_SYS_NAND_ECCBYTES 4

如果你的是2048就改成2048就行
/*-----------------------------------------------------------------------
 * NAND flash settings
 */
#if defined(CONFIG_CMD_NAND)
#define CONFIG_NAND_S3C2410
#define CONFIG_SYS_MAX_NAND_DEVICE  1   /* Max number of NAND devices       */
#define CONFIG_SYS_NAND_BASE 0 
#define SECTORSIZE 512
#define SECTORSIZE_2K 2048
#define NAND_SECTOR_SIZE SECTORSIZE
#define NAND_SECTOR_SIZE_2K SECTORSIZE_2K
#define NAND_BLOCK_MASK 511
#define NAND_BLOCK_MASK_2K 2047
#define NAND_MAX_CHIPS 1
#define CONFIG_MTD_NAND_VERIFY_WRITE 
#define CONFIG_SYS_64BIT_VSPRINTF       /* needed for nand_util.c */
#define CONFIG_S3C2440_NAND_HWECC
#define CONFIG_SYS_NAND_ECCSIZE 512
#define CONFIG_SYS_NAND_ECCBYTES 4

#endif  /* CONFIG_CMD_NAND */


第二步:在drivers/mtd/nand/s3c2410_nand.c文件中对s3c2410_nand_enable_hwecc函数、s3c2410_nand_calculate_ecc函数和s3c2410_nand_correct_data函数进行修改。
1、该函数的任务就是初始化ECC(即复位ECC),并解锁main区ECC。
#ifde CONFIG_s3c2410_NAND_HWECC
       .........
       .........
       .........

#elif defined(CONFIG_S3C2440_NAND_HWECC)
void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
    printf("nand_enable_hwecc\n");
    struct s3c2410_nand *nand = s3c2410_get_base_nand();
    debugX(1, "s3c2410_nand_enable_hwecc(%p, %d)\n", mtd, mode);
    writel(readl(&nand->NFCONT) | S3C2410_NFCONT_INITECC & ~S3C2410_NFCONT_MAINECCLOCK, &nand->NFCONT);
}
2、该函数首先锁定main区ECC,然后读取寄存器NFMECC0,该寄存器存放着由硬件生成的main区ECC,最后把4个1字节的ECC存放到ecc_code数组内。
static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
                      u_char *ecc_code)
{
    struct s3c2410_nand *nand = s3c2410_get_base_nand();


    writel(read(&nand->NFCONT)|S3C2410_NFCONT_MAINECLLOCK,&nand->NFCONT)


    ecc_code[0] = readb(&nand->NFECC);
    ecc_code[1] = readb(&nand->NFECC + 1);
    ecc_code[2] = readb(&nand->NFECC + 2);
    ecc_code[3] = readb(&nand->NFECC + 3);
    debugX(1, "s3c2410_nand_calculate_hwecc(%p,): 0x%02x 0x%02x 0x%02x 0x%02x\n",
           mtd , ecc_code[0], ecc_code[1], ecc_code[2] ecc_code[3]);


    return 0;
}
3、该函数首先把read_ecc数组内的ECC存入寄存器NFMECCD0和寄存器NFMECCD1中,这样系统就会自动校验数据,并把状态放入寄存器NFESTAT0中,然后读取该寄存器的后4位,当为0时表示校验正确;当为1时表示发生了1位错误(该类错误可以校正),我们把它校正过来;当为2和3时表示发生其他类型的错误,这类错误是无法校正的。
static int s3c2440_nand_correct_data(struct mtd_info *mtd, u_char *dat,
        u_char *read_ecc, u_char *calc_ecc)
{
    struct s3c2410_nand *nand = s3c2410_get_base_nand();


    u32 estat0,err_byte_addr;
    int ret = -1;
    u8 repaired;


    writel((read_ecc[1] << 16) | read_ecc[0],&nand->NFMECCD0);
    writel((read_ecc[3] << 16) | read_ecc[2],&nand->NFMECCD1);


    /* Read ecc status */
    estat0= readl(&nand->NFSTAT);


    switch(estat0 & 0x3) {
        case  0: /*  No error */
            ret= 0;
            break;


        case  1:
            /*
             *  1 bit error (Correctable)
             *  (nfestat0 >> 7) & 0x7ff    :error byte number
             *  (nfestat0 >> 4) & 0x7      :error bit number
             *      
             */
            err_byte_addr= (estat0 >> 7) & 0x7ff;
            repaired= dat[err_byte_addr] ^ (1 << ((estat0 >> 4) & 0x7));


            printf("S3C NAND: 1 bit error detected at byte%ld. "
                    "Correcting from 0x%02x to0x%02x...OK\n",
                    err_byte_addr, dat[err_byte_addr],repaired);


            dat[err_byte_addr]= repaired;


            ret= 1;
            break;


        case  2: /*  Multiple error */
        case  3: /*  ECC area error */
            printf("S3C NAND: ECC uncorrectable errordetected. "
                    "Not correctable.\n");
            ret= -1;
            break;
    }


    return   ret;
}
#endif


4、下面是将s3c2440_nand_enable_hwecc函数、s3c2440_nand_calculate_ecc函数和s3c2440_nand_correct_data函数,这三个函数放到
 一个结构体中:
#ifdef CONFIG_S3C2410_NAND_HWECC
    nand->ecc.hwctl = s3c2410_nand_enable_hwecc;
    nand->ecc.calculate = s3c2410_nand_calculate_ecc;
    nand->ecc.correct = s3c2410_nand_correct_data;
    nand->ecc.mode = NAND_ECC_HW;
    nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
    nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
#elif defined(CONFIG_S3C2440_NAND_HWECC)
    nand->ecc.hwctl = s3c2440_nand_enable_hwecc;
    nand->ecc.calculate = s3c2440_nand_calculate_ecc;
    nand->ecc.correct = s3c2440_nand_correct_data;
    nand->ecc.mode = NAND_ECC_HW;
    nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
    nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
#else

    nand->ecc.mode = NAND_ECC_SOFT;
#endif


这样我们重新make一下就行了。

内核的ECC配置我上个图片吧告诉你路径,你选上就行:


后话:

在内核中我添加了支持硬件ECC后发现了有错误,如图:

上面是进入如linux登陆界面之前打印的信息,下面是我查看nand挂载在mnt/目录下的错误:

我的mnt目录下有个xxx文件上面是ECC error

我擦了一下uboot后面的nandflash后,问题就好了,你可以看下:

这就没有打印ECC错误

二、从nandflash上启动uboot的ecc校验问题(STI7105) 2011-06-13 15:22:23

项目进行到最后阶段,需要把程序拿到工厂去烧写测试。

结果发现工厂的软件没有针对ST-Uboot自带stm_nand_calculate_ecc校验,所以只能够将uboot.bin做成跟
根文件系统一样的自带ecc校验的来进行烧写,即2048 64字节的bin文件。
拿回来之后启动正常,但是出现了
 
nand_read_bbt: Bad block at 0x00000000
 
第一个块居然坏了!!!
但是芯片出厂的时候是保证第一个块是好的。
 
经过仔细分析,终于发现了问题
 
当uboot从nandflash上启动时,自身也有ecc校验并且和linux默认的ecc校验很不一样,如果改成其自带的linux默认ecc校验 会导致uboot根本无法启动:
  1. /*
  2.      * Do we want to read/write NAND Flash compatible with the ST40's
  3.      * NAND Controller H/W IP block for "boot-mode"? If we want
  4.      * to read/write NAND flash that is meant to support booting
  5.      * from NAND, then we need to use 3 bytes of ECC per 128 byte
  6.      * record. If so, then define the "CFG_NAND_ECC_HW3_128" macro.
  7.      */
  8. #    define CFG_NAND_ECC_HW3_128    /* define for "boot-from-NAND" compatibility */

  9.     /*
  10.      * If using CFG_NAND_ECC_HW3_128, then we must also define
  11.      * where the (high watermark) boundary is. That is, the
  12.      * NAND offset, below which we are in "boot-mode", and
  13.      * must use 3 bytes of ECC for each 128 byte record.
  14.      * For this offset (and above) we can use any supported
  15.      * ECC configuration (e.g 3/256 S/W, or 3/512 H/W).
  16.      */
  17. #    define CFG_NAND_STM_BOOT_MODE_BOUNDARY (1ul << 17)    /* 128 Kb */
 
从注释可以得知 要使用 boot from nand 就必须要用到3 bytes of ECC for each 128 byte record.
即每128字节做3字节的ecc校验
 
我们用到的nandflash是K9F1G08U0B这块:存储单元为(128M 4M)*8位,数据寄存器为(2K 64)字节,
所以在uboot上他的oob区域被定义为
  1. /* for LARGE-page devices */
  2. static struct nand_oobinfo stm_nand_oobinfo_64 = {
  3.     .useecc = MTD_NANDECC_AUTOPLACE,
  4.     .eccbytes = 48,
  5.     .eccpos = {
  6.          0, 1, 2,    /* ECC for 1st 128-byte record */
  7.          4, 5, 6,    /* ECC for 2nd 128-byte record */
  8.          8, 9, 10,    /* ECC for 3rd 128-byte record */
  9.         12, 13, 14,    /* ECC for 4th 128-byte record */
  10.         16, 17, 18,    /* ECC for 5th 128-byte record */
  11.         20, 21, 22,    /* ECC for 6th 128-byte record */
  12.         24, 25, 26,    /* ECC for 7th 128-byte record */
  13.         28, 29, 30,    /* ECC for 8th 128-byte record */
  14.         32, 33, 34,    /* ECC for 9th 128-byte record */
  15.         36, 37, 38,    /* ECC for 10th 128-byte record */
  16.         40, 41, 42,    /* ECC for 11th 128-byte record */
  17.         44, 45, 46,    /* ECC for 12th 128-byte record */
  18.         48, 49, 50,    /* ECC for 13th 128-byte record */
  19.         52, 53, 54,    /* ECC for 14th 128-byte record */
  20.         56, 57, 58,    /* ECC for 15th 128-byte record */
  21.         60, 61, 62},    /* ECC for 16th 128-byte record */
  22.     .oobfree = {
  23.         { 3, 1}, { 7, 1}, {11, 1}, {15, 1},
  24.         {19, 1}, {23, 1}, {27, 1}, {31, 1},
  25.         {35, 1}, {39, 1}, {43, 1}, {47, 1},
  26.         {51, 1}, {55, 1}, {59, 1}, {63, 1} },
  27. };
 
以第一块第一页(2048 byte)为例:
OOB:
        cf 00 fc ff 0f 00 3c ff
        00 00 00 ff 95 25 64 ff
        65 26 a8 ff 3c 00 cc ff
        95 2a 68 ff 30 33 0c ff
        cc 30 c0 ff 59 16 54 ff
        aa 16 64 ff 3f 0f 30 ff
        6a 2a 58 ff f0 0f f0 ff
        55 25 a4 ff 59 15 54 ff
 
和默认的linux ecc校验区域做对比 以第二快第二页为例:
OOB:
        ff ff ff ff ff ff ff ff
        ff ff ff ff ff ff ff ff
        ff ff ff ff ff ff ff ff
        ff ff ff ff ff ff ff ff
        ff ff ff ff ff ff ff ff
        ff 3f 3f 5a 6a 9b 99 a6
        9b a6 5a 9b 59 6a 6b 00
        0f 33 9a 65 57 0f f3 0f
很明显的可以看出差别。
 
然后uboot启动时会首先去寻找bbt,即坏块表,如果找不到的话会在某个指定的位置建立一个bbt,而我们的bbt放在了nandflash最后3个块上面。
 
将bbt dump出来后发现
Page 07fc0000 dump:
        fc ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff
        ff ff ff ff ff ff ff ff  ff ff ff ff
阅读(2101) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~