zoukankan      html  css  js  c++  java
  • 十八、flash 适配(三)

    ecc 是 nandflash 的一个重要功能。这个功能与数据的完整性密切相关。很多移植视频和文档都很弱化这方面的东西,不管是以前的老视频和文档还是现在的新视频和文档基本上都不讲这个,只能自己多阅读文档和进行研究。

    这一节专门研究 ECC。

    参看文档:

    https://blog.csdn.net/gzxb1995/article/details/103888607

    https://www.cnblogs.com/pengdonglin137/p/3438001.html

    18.7 ECC 介绍

    18.7.1 为什么需要 ECC

    Nand flash 相对其他的 flash 成本相对低,缺点是使用中数据读写容易出错,所以一般都需要有对应的软件或者硬件的数据校验算法,统称为ECC。

    18.7.2  SLC 和 MLC

    Nand Flash 按照内部存储数据单元的电压的不同层次,也就是单个内存单元中,是存储1位数据,还是多位数据,可以分为 SLC 和 MLC。那么软件如何识别系统上使用过的SLC还是MLC呢? 
    Nand Flash设计中,有个命令叫做 Read ID,读取 ID,读取好几个字节,一般最少是4个,新的芯片,支持5个甚至更多,从这些字节中,可以解析出很多相关的信息,比如此 Nand Flash 内部是几个芯片(chip)所组成的,每个 chip 包含了几片(Plane),每一片中的页大小,块大小,等等。在这些信息中,其中有一个,就是识别此 flash 是 SLC 还是 MLC。

    18.7.3 OOB

    每一个页,对应还有一块区域,叫做空闲区域(spare area)/冗余区域(redundant area),而 Linux 系统中,一般叫做 OOB(Out Of Band),这个区域,是最初基于 Nand Flash 的硬件特性:数据在读写时候相对容易错误,所以为了保证数据的正确性,必须要有对应的检测和纠错机制,此机制被叫做 EDC(Error Detection Code)/ECC(Error Code Correction, 或者 Error Checking and Correcting),所以设计了多余的区域,用于放置数据的校验值。

    OOB 的读写操作,一般是随着页的操作一起完成的,即读写页的时候,对应地就读写了OOB。

    关于 OOB 具体用途,总结起来有:

    1. 标记是否是坏快
    2. 存储 ECC 数据
    3. 存储一些和文件系统相关的数据。如 jffs2 就会用到这些空间存储一些特定信息,而 yaffs2 文件系统,会在 OOB 中存放很多和自己文件系统相关的信息

    18.7.4 坏块管理

    Nand Flash 由于其物理特性,只有有限的擦写次数,超过那个次数,基本上就是坏了。在使用过程中,有些 Nand Flash 的 block 会出现被用坏了,当发现了,要及时将此 block 标注为坏块,不再使用。与此相关的管理工作,属于 Nand Flash 的坏块管理的一部分工作。

    18.7.5 Wear-Leveling负载平衡

    Nand Flash 的 block 管理,还包括负载平衡。

    正是由于 Nand Flash 的 block 都是有一定寿命限制的,所以如果每次都往同一个 block 擦除然后写入数据,那么那个 block 就很容易被用坏了,所以我们要去管理一下,将这么多次的对同一个 block 的操作,平均分布到其他一些 block 上面,使得在 block 的使用上,相对较平均,这样相对来说,可以更能充分利用 Nand Flash。

    18.7.6 ECC 的错误校验码

    Nand Flash 物理特性上使得其数据读写过程中会发生一定几率的错误,所以要有个对应的错误检测和纠正的机制,于是才有了 ECC,用于数据错误的检测与纠正。Nand Flash 的 ECC,常见的算法有海明码和 BCH,这类算法的实现,可以是软件也可以是硬件。不同系统,根据自己的需求,采用对应的软件或者是硬件。

    相对来说,硬件实现这类 ECC 算法,肯定要比软件速度要快,但是多加了对应的硬件部分,所以成本相对要高些。如果系统对于性能要求不是很高,那么可以采用软件实现这类 ECC 算法,但是由于增加了数据读取和写入前后要做的数据错误检测和纠错,所以性能相对要降低一些,即 Nand Flash 的读取和写入速度相对会有所影响。

    其中,Linux 中的软件实现 ECC 算法,即 NAND_ECC_SOFT 模式,就是用的对应的海明码。

    而对于目前常见的 MLC 的 Nand Flash 来说,由于容量比较大,动辄 2GB,4GB,8GB 等,常用 BCH 算法。BCH 算法,相对来说,算法比较复杂。

    BCH 算法,通常是由对应的 Nand Flash 的 Controller 中,包含对应的硬件 BCH ECC 模块,实现了 BCH 算法,而作为软件方面,需要在读取数据后,写入数据之前,分别操作对应 BCH 相关的寄存器,设置成 BCH 模式,然后读取对应的 BCH 状态寄存器,得知是否有错误,和生成的 BCH 校验码,用于写入。

    其具体代码是如何操作这些寄存器的,由于是和具体的硬件,具体的 nand flash 的 controller 不同而不同,无法用同一的代码。如果你是nand flash驱动开发者,自然会得到对应的起 nand flash 的 controller 部分的 datasheet,按照手册说明,去操作即可。

    18.7.7 位反转

    Nand Flash 的位反转现象,主要是由以下一些原因/效应所导致:

    • 漂移效应(Drifting Effects):漂移效应指的是,Nand Flash 中 cell 的电压值,慢慢地变了,变的和原始值不一样了。
    • 编程干扰所产生的错误(Program-Disturb Errors):此现象有时候也叫做过度编程效应(over-program effect)。对于某个页面的编程操作,即写操作,引起非相关的其他的页面的某个位跳变了。
    • 读操作干扰产生的错误(Read-Disturb Errors):此效应是,对一个页进行数据读取操作,却使得对应的某个位的数据,产生了永久性的变化,即 Nand Flash 上的该位的值变了。

    对应位反转的类型,Nand Flash 位反转的类型和解决办法,有两种:

    • 一种是 nand flash 物理上的数据存储的单元上的数据,是正确的,只是在读取此数据出来的数据中的某位,发生变化,出现了位反转,即读取出来的数据中,某位错了,本来是 0 变成 1,或者本来是 1 变成 0 了。此处可以成为软件上位反转。此数据位的错误,当然可以通过一定的校验算法检测并纠正。
    • 另外一种,就是 nand flash 中的物理存储单元中,对应的某个位,物理上发生了变化,原来是 1 的,变成了 0,或原来是 0 的,变成了 1,发生了物理上的位的数据变化。此处可以成为硬件上的位反转。此错误,由于是物理上发生的,虽然读取出来的数据的错误,可以通过软件或硬件去检测并纠正过来,但是物理上真正发生的位的变化,则没办法改变了。可以通过擦 除Erase 整个数据块 Block 的方式去擦除此错误,不过在之后的 Nand Flash 的使用过程中,估计此位还是很可能继续发生同样的硬件的位反转的错误。

    以上两种类型的位反转,其实对于从 Nand Flash 读取出来的数据来说,解决其中的错误的位的方法,都是一样的,即通过一定的校验算法,常称为 ECC,去检测出来,或检测并纠正错误。
    如果只是单独检测错误,那么如果发现数据有误,那么再重新读取一次即可。
    实际中更多的做法是,ECC 校验发现有错误,会有对应的算法去找出哪位错误并且纠正过来。

    其中对错误的检测和纠正,具体的实现方式,有软件算法,也有硬件实现,即硬件 Nand Flash 的控制器 controller 本身包含对对应的硬件模块以实现数据的校验和纠错的。

    18.7.8  Nand Flash控制器与Nand Flash芯片

    我们写驱动,是写 Nand Flash 控制器的驱动,而不是 Nand Flash 芯片的驱动,因为独立的 Nand Flash 芯片,一般来说,是很少直接拿来用的,多数都是硬件上有对应的硬件的 Nand Flash 的控制器,去操作和控制 Nand Flash,包括提供时钟信号,提供硬件 ECC 校验等等功能,我们所写的驱动软件,是去操作 Nand Flash 的控制器,然后由控制器去操作 Nand Flash 芯片,实现我们所要的功能。

    由于 Nand Flash 读取和编程操作来说,一般最小单位是页,所以 Nand Flash 在硬件设计时候,就考虑到这一特性,对于每一片(Plane),都有一个对应的区域专门用于存放将要写入到物理存储单元中去的或者刚从存储单元中读取出来的,一页的数据,这个数据缓存区,本质上就是一个缓存 buffer,但是只是此处 datasheet 里面把其叫做页寄存器 page register 而已,实际将其理解为页缓存,更贴切原意。

    而正是因为有些人不了解此内部结构,才容易产生之前遇到的某人的误解,以为内存里面的数据,通过 Nand Flash 的 FIFO,写入到 Nand Flash 里面去,就以为立刻实现了实际数据写入到物理存储单元中了,而实际上只是写到了这个页缓存中,只有当你再发送了对应的编程第二阶段的确认命令,即 0x10 之后,实际的编程动作才开始,才开始把页缓存中的数据,一点点写到物理存储单元中去。

    18.7.9 坏块的标记

    具体标记的地方是,对于现在常见的页大小为 2K 的 Nand Flash,是块中第一个页的 OOB 起始位置的第1个字节(旧的小页面,pagesize 是 512B 甚至 256B 的Nand Flash,坏块标记是第 6 个字节),如果不是 0xFF,就说明是坏块。相对应的是,所有正常的块,好的块,里面所有数据都是 0xFF 的。

    对于坏块的标记,本质上,也只是对应的 flash 上的某些字节的数据是非 0xFF 而已,所以,只要是数据,就是可以读取和写入的。也就意味着,可以写入其他值,也就把这个坏块标记信息破坏了。对于出厂时的坏块,一般是不建议将标记好的信息擦除掉的。

    uboot中有个命令是 nand scrub 就可以将块中所有的内容都擦除了,包括坏块标记,不论是出厂时的,还是后来使用过程中出现而新标记的。nand erase 只擦除好的块,对于已经标记坏块的块,不要轻易擦除掉,否则就很难区分哪些是出厂时就坏的,哪些是后来使用过程中用坏的了。

    18.8 软件 ECC

    nand 初始化中的 ECC 初始化的源码:

    从上面可以知道,S3C24X0 可以使用硬件 ECC 和软件  ECC。

    18.8.1 内核的中软件 ECC 文档

    内核中的软件 ECC 实现与 uboot 中的软件 ECC 实现类似,而且有文档,在看代码之前,需要先看看这个文档,了解下软件 ECC 算法是如何实现的。

    文件路径:linux-5.6.15Documentationdriver-apimtd and_ecc.rst

    部分翻译如下:

    NAND flash(至少是 SLC)通常有256字节的扇区。然而,NAND flash 不是非常可靠,所以一些错误检测(有时是纠正)是需要的。

    ECC 校验是通过 Hamming code 来实现的。

    如前所述,ecc 计算是在 256 字节的扇区上执行的。这是通过在行和列上计算几个奇偶校验位来实现的。使用的奇偶校验是偶数,这意味着如果奇偶校验数据是 1,奇偶位 = 1;如果奇偶校验数据是 0,奇偶位 = 0。因此,计算奇偶校验的数据上的总比特数 + 奇偶校验位是偶数。奇偶校验通常通过异或操作计算。在 C 中,xor 的运算符是 ^。

    如下图:

     这个图代表一个 255 字节的扇区。cp 表示列奇偶性的缩写,rp 表示行奇偶性的缩写

    • 列奇偶性:
      • cp0 是所有的 bit0、bit2、bit4、bit6;所以所有的 bit0, bit2, bit4 和 bit6 的值加上 cp0 本身是偶数。
      • cp1 与 cp0 相同,cp1 是所有的 bit1、bit3、bit5、bit7.
      • cp2 是 bit0、bit1、bit4 和 bit5 上的奇偶校验
      • cp3 是 bit2、bit3、bit6 和 bit7 上的奇偶校验
      • cp4 是 bit0、bit1、bit2 和 bit3 上的奇偶校验
      • cp5 是 bit4、bit5、bit6 和 bit7 上的奇偶校验
      • 注意,每个cp0 ..cp5正好是1bit。
    • 行奇偶性:
      • rp0 是所有偶数字节的奇偶校验(0, 2, 4, 6, ... 252, 254)
      • rp1 是所有奇数字节的奇偶校验(1, 3, 5, 7, ..., 253, 255)
      • rp2 是字节 0,1,4,5,8,9,…的奇偶校验,即处理两个字节,然后跳过两个字节
      • rp3 处理了 rp2 没有处理的那一半(bytes 2, 3, 6, 7, 10, 11, ...)
      • 对于 rp4,规则是处理 4 个字节,跳过 4 个字节,处理 4 个字节,跳过 4,等等。所以 rp4 按字节 0, 1, 2, 3, 8, 9, 10, 11, 16, ... 计算奇偶校验
      • rp5 处理 rp4 没有处理的另一半,因此字节 4、5、6、7、12、13、14、15、20,..
      • rp6 处理 8 个字节,然后跳过 8 个字节
      • rp7 跳过 8 个字节,然后处理 8 个字节
      • rp8 处理 16 个字节,然后跳过 16 个字节
      • rp9 跳过 16 个字节,然后处理 16 个字节
      • rp10 处理 32 个字节,然后跳过 32 个字节
      • rp11 跳过 32 个字节,然后处理 32 个字节
      • rp12 处理 64 个字节,然后跳过 64 个字节
      • rp13 跳过 64 个字节,然后处理 64 个字节
      • rp14 处理 128 个字节,然后跳过 128 个字节
      • rp15 跳过 128 个字节,然后处理 128 个字节

    最后,奇偶校验位组在一起为三个字节如下:

     基本算法就是如此,后面的内容都是作者为了编码的合理性做的些实验,同样作者也实验了码表,通过码表与数据字节进行逻辑操作来获得 cp 和 rp 的值。

    18.8.2 总体代码实现位置

    nandflash 的软件 ECC 的初始化的具体代码在 nand_scan_tail()(driversmtd and and_base.c)函数中:

    上面注册的回调函数都在文件 driversmtd and and_base.c 中。

    这里面最重点的函数就是 nand_calculate_ecc,ecc 的值都是通过此计算的。

    为了加快计算速度,程序中使用了一个预先计算好的列极性表。这个表中每一个元素都是 unsigned char 类型,表示 8 位二进制数。

    表中 8 位二进制数每位都有含义:

     表的意思是对 0~255 这 256 个数,计算并存储每个数的列校验值和行校验值,以数作数组下标。举个例子,计算 ECC 的函数定义如下:

    1 int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)

    dat 是要读或写 nandflash 的数据,根据前面的内容可以知道,数据包中的每个数据(即 1 个 byte),会传给上面的编码表数组,作为编码表数组的下标。当当前的数据包的这个字节是 20 的时候,那么当前码表是 nand_ecc_precalc_table[20],20 的值是 0001 0100,那么计算 20 的码表值:(^ 符号为异或,两个数字相同为 0,不同为 1)

    • CP0 = Bit0^Bit2^Bit4^Bit6 = 0 ^ 1 ^ 1 ^ 0 = 0;
    • CP1 = Bit1^Bit3^Bit5^Bit7 = 0 ^ 0 ^ 0 ^ 0 = 0;
    • CP2 = Bit0^Bit1^Bit4^Bit5 = 0 ^ 0 ^ 1 ^ 0 = 1;
    • CP3 = Bit2^Bit3^Bit6^Bit7 = 1 ^ 0 ^ 0 ^ 0 = 1;
    • CP4 = Bit0^Bit1^Bit2^Bit3 = 0 ^ 0 ^ 1 ^ 0 = 1;
    • CP5 = Bit4^Bit5^Bit6^Bit7 = 1 ^ 0 ^ 0 ^ 0 = 1;

    行极性 RP = Bit0^Bit1^Bit2^Bit3^Bit4^Bit5^Bit6^Bit7 = 0 ^ 0 ^ 1 ^ 0 ^ 1 ^ 0 ^ 0 ^ 0 = 0

    最后得到的 ecc 值是 0011 1100 = 0x3c,再看看 nand_ecc_precalc_table[20] 的值,正好是 0x3c。

    现在看函数的代码:

    83 行获取了 *dat 的 ecc 码表值,dat 地址 + 1,按上面举例的 20 ,那么当前 idx = 0x3c,0x3c & 0x3f = 0x3c, ret1 = 0,reg1 ^ 0x3c = 0x3c,即 reg1 = 0x3c;

    判断当前行极性 0x3c & 0x40 = 0,行极性是偶数行,那么 if 语句中的不执行。if 语句的判断主要是用来判断 RP 值,即极性值,主要是用来给行校验用的。

    对于列校验总结一下:计算列极性的过程是先在一个字节数据的内部计算 CP0 ~ CP5, 每个字节都计算完后再与其它字节的计算结果求异或。而内核文档中是先对一列 Bit0 求异或,再去异或一列 Bit2这两种只是计算顺序不同,结果是一致的。因为异或运算的顺序是可交换的。

    行极性的计算要比列计算复杂。

    nand_ecc_precalc_table[] 表中的 Bit6 已经保存了每个单字节数的行极性值。对于待校验的 256字 节数据,分别查表,如果其行极性为 1,则记录该数据所在的行索引(也就是 for 循环的 i 值),这里的行索引是很重要的,因为 RP0 ~ RP15 的计算都是跟行索引紧密相关的,如  RP0 只计算偶数行,RP1 只计算奇数行,等等。

    上面第 89 90 行,Reg3和reg2都是unsigned char 型的变量,并都初始化为零。 传入的数据的行索引(也就是 for 循环里的 i)的取值范围为0~255,根据行奇偶性可以得出以下规律: 

    • RP0 只计算行索引的 Bit0 为 0 的行,RP1 只计算行索引的 Bit0 为 1 的行;
    • RP2 只计算行索引的 Bit1 为 0 的行,RP3 只计算行索引的 Bit1 为 1 的行;
    • RP4 只计算行索引的 Bit2 为 0 的行,RP5 只计算行索引的 Bit2 为 1 的行;
    • RP6 只计算行索引的 Bit3 为 0 的行,RP7 只计算行索引的 Bit3 为 1 的行;
    • RP8 只计算行索引的 Bit4 为 0 的行,RP9 只计算行索引的 Bit4 为 1 的行;
    • RP10 只计算行索引的 Bit5 为 0 的行,RP11 只计算行索引的 Bit5 为1 的行;
    • RP12 只计算行索引的 Bit6 为 0 的行,RP13 只计算行索引的 Bit6 为1 的行;
    • RP14 只计算行索引的 Bit7 为 0 的行,RP15 只计算行索引的 Bit7 为1 的行;

    如奇数行,最低位(bit0) 始终是 1,偶数行,最低位(bit0)始终是0;RP2 是 0,1,4,5,8,9... bit1 始终是0.

    已经知道,异或运算的作用是判断比特位为 1 的个数,跟比特位为0的个数没有关系。如果有偶数个 1 则异或的结果为 0,如果有奇数个 1 则异或的结果为 1。
    那么,程序第 89 行,对所有行校验为 1 的行索引按位异或运算,作用便是:
    判断在所有行校验为 1 的行中,

    • 属于 RP1 计算范围内的行有多少个------由reg3 的 Bit 0 指示,0 表示有偶数个,1 表示有奇数个;
    • 属于 RP3 计算范围内的行有多少个------由 reg3 的 Bit 1 指示,0 表示有偶数个,1 表示有奇数个;
    • 属于 RP5 计算范围内的行有多少个------由 reg3 的 Bit 2 指示,0 表示有偶数个,1 表示有奇数个;
    • 属于 RP7 计算范围内的行有多少个------由 reg3 的 Bit 3 指示,0 表示有偶数个,1 表示有奇数个;
    • 属于 RP9 计算范围内的行有多少个------由 reg3 的 Bit 4 指示,0 表示有偶数个,1 表示有奇数个;
    • 属于 RP11 计算范围内的行有多少个------由 reg3 的 Bit 5 指示,0 表示有偶数个,1 表示有奇数个;
    • 属于 RP13 计算范围内的行有多少个------由 reg3 的 Bit 6 指示,0 表示有偶数个,1 表示有奇数个;
    • 属于 RP15 计算范围内的行有多少个------由 reg3 的 Bit 7 指示,0 表示有偶数个,1 表示有奇数个;

    所以,reg3每个Bit位的作用如下表所示:

     

    第 90 行,对所有行校验为 1 的行索引按位取反之后,再按位异或,作用就是判断比特位为 0 的个数。比如 reg2 的 Bit0 为 0 表示:所有行校验为 1 的行中,行索引的 Bit0 为 0 的行有偶数个,也就是落在 RP0 计算范围内的行有偶数个。所以得到结论:
    在所有行校验为 1 的行中,

    • 属于 RP0 计算范围内的行有多少个------由 reg2 的 Bit 0 指示,0 表示有偶数个,1 表示有奇数个;
    • 属于 RP2 计算范围内的行有多少个------由 reg2 的 Bit 1 指示,0 表示有偶数个,1 表示有奇数个;
    • 属于 RP4 计算范围内的行有多少个------由 reg2 的 Bit 2 指示,0 表示有偶数个,1 表示有奇数个;
    • 属于 RP6 计算范围内的行有多少个------由 reg2 的 Bit 3 指示,0 表示有偶数个,1 表示有奇数个;
    • 属于 RP8 计算范围内的行有多少个------由 reg2 的 Bit 4 指示,0 表示有偶数个,1 表示有奇数个;
    • 属于 RP10 计算范围内的行有多少个------由 reg2 的 Bit 5 指示,0 表示有偶数个,1 表示有奇数个;
    • 属于 RP12 计算范围内的行有多少个------由 reg2 的 Bit 6 指示,0 表示有偶数个,1 表示有奇数个;
    • 属于 RP14 计算范围内的行有多少个------由 reg2 的 Bit 7 指示,0 表示有偶数个,1 表示有奇数个;

    所以,reg2 每个 Bit 位的作用如下表所示:

     

     后面的代码就是继续计算值了,有兴趣可以继续研究。

    18.8.3 配置软件ECC 烧写测试

    只要不开硬件 ECC 就默认为是软件 ECC,可以配置好后,可以编译烧写进 norflash 中,在此之前还需要修改下 Kconfig 文件,要区分软件和硬件 ECC,同时 NAND_BBT 的配置统一为 S3C24X0:

    drivers/mtd/nand/Kconfig:

     1 config NAND_S3C24X0
     2     bool "Support S3c24x0 NAND Controller"
     3     default n
     4     help
     5       Support s3c2410 s3c2440.
     6 
     7 config SYS_MAX_NAND_DEVICE
     8     int "Number of nand device"
     9     default 0
    10     help
    11       There may be more than one nandflash on a board.
    12       Enter the number of nandflash on your board.
    13       default 0
    14 
    15 config SYS_NAND_BASE
    16     hex "Basic address of NAND controller"
    17     default 0x4e000000 if S3C2440
    18     default 0x4e000000 if S3C2410
    19     help
    20       Base address of NAND controller of SOC.
    21 
    22 config S3C24XX_CUSTOM_NAND_TIMING
    23     bool "Support s3c24xx Custom NAND Timing"
    24     default n
    25     depends on NAND_S3C24X0
    26     help
    27       Support s3c24xx custom nand timing
    28 
    29 config S3C24XX_TACLS
    30     int "Tacls of NAND controller of S3C24XX"
    31     depends on S3C24XX_CUSTOM_NAND_TIMING
    32     help
    33       Tacls of NAND controller of S3C24XX.
    34       This is the parameter of the NAND controller's register nfconf.
    35       For S3C2440, tacls = S3C24XX_TACLS - 1
    36 
    37 config S3C24XX_TWRPH0
    38     int "Twrph0 of NAND controller of S3C24XX"
    39     depends on S3C24XX_CUSTOM_NAND_TIMING
    40     help
    41       Twrph0 of NAND controller of S3C24XX.
    42       This is the parameter of the NAND controller's register nfconf.
    43       For S3C2440, twrph0 = S3C24XX_TWRPH0 - 1
    44 
    45 config S3C24XX_TWRPH1
    46     int "Twrph1 of NAND controller of S3C24XX"
    47     depends on S3C24XX_CUSTOM_NAND_TIMING
    48     help
    49       Twrph1 of NAND controller of S3C24XX.
    50       This is the parameter of the NAND controller's register nfconf.
    51       For S3C2440, Twrph1 = S3C24XX_TWRPH1 - 1
    52 
    53 choice
    54     prompt "s3c24x0 nand ecc"
    55     default n
    56     depends on NAND_S3C24X0
    57     help
    58       Support s3c24x0 nand hwecc
    59 
    60 config NAND_SOFT_ECC
    61     bool "software nand ecc"
    62 
    63 config NAND_HW_ECC
    64     bool "hardware nand ecc"
    65 
    66 endchoice
    67 
    68 choice
    69     prompt "s3c24x0 nand hardware ecc"
    70     depends on NAND_HW_ECC
    71     help
    72       Support s3c24x0 nand Hardware ECC
    73 
    74 config S3C2410_NAND_HWECC
    75     bool "s3c2410 nand hwecc"
    76 
    77 config S3C2440_NAND_HWECC
    78     bool "s3c2440 nand hwecc"
    79 
    80 endchoice
    81 
    82 config S3C24X0_NAND_BBT
    83     bool "Support S3c24x0 NAND BBT"
    84     default n
    85     depends on NAND_S3C24X0
    86     help
    87       Support S3C24X0 NAND BBT

    代码当前还需要修改一处:CONFIG_S3C2410_NAND_BBT 的地方改为 CONFIG_S3C24X0_NAND_BBT

    Makefile(drivers/mtd/nand/)修改:前面把字母给小写了,导致文件没有编译进去

     编译烧写进 norflash 测试:

     

     可以看见,nandflash 已经正确识别出来了,为 256M,运行 nand info 命令,查看 nand 命令:

    擦写 0x100000 地址开始,大小为 0x300000 的 nand 空间:nand erase 0x100000 0x300000

    这各地方错误,这么多的坏块,很现然是不正常的,擦写的命令都在 s3c24x0_hwcontrol 函数中,那么我们换一种写法,将以前的代码屏蔽掉。添加下面的代码:

     1 static void s3c24x0_hwcontrol(struct mtd_info *mtd, int dat, unsigned int ctrl)
     2 {
     3     struct s3c24x0_nand *nand = s3c24x0_get_base_nand();
     4 
     5     if (ctrl & NAND_CLE)
     6     {
     7         writeb(dat, &nand->nfcmd);
     8     }
     9     else if (ctrl & NAND_ALE)
    10     {
    11         writeb(dat, &nand->nfaddr);
    12     }
    13 }

    直接通过 nfcmd 和 nfaddr 进行控制,前面本身就又操作反向的问题,索性不用它的代码了。

    执行写操作:nand write 0 0x100000 0x300000,将内存地址(SDRAM)中的地址为 0,大小为 0x300000 处的内容写入 nandflash 的 0x100000 处。

     执行读操作:nand read 0x30004000 0x100000 0x300000,将 nandflash 中起始地址为 0x100000,大小为  0x300000 处的内容读入到内存地址(SDRAM)0x30004000处。

    读一下 SDRAM 的 0 地址和 30004000 地址处的内容:

    比较一下两块的内容:cmp.b 0 0x30004000 0x300000

    然后将镜像烧写到 nandflash 中,看是否正常:

     现在 0 地址处映射的是片内的 4KB 的 SRAM,NorFlash 已经不可见了,因此可以从输出信息上看到 u-boot 没有识别到 Flash。

  • 相关阅读:
    让AutoMapper更好用
    设置ADB网络连接目标板
    windos或linux中 which命令 查看当前要执行的命令所在的路径
    secureCRT使用退格键(backspace)出现^H解决办法
    Ubuntu12.04安装insight-6.8
    Linux命令之type
    BUG:给Nexus7编译Android4.2的时候出现 fatal error: map: No such file or directory
    Kconfig和Makefile的修改
    diff命令的参数详解和实例
    Linux获取当前目录名,shell获取当前目录名
  • 原文地址:https://www.cnblogs.com/kele-dad/p/13160145.html
Copyright © 2011-2022 走看看