nandflash驱动详解

对nandflash存储原理不了解的请先阅读《 flash memory技术
架构图

+ ----------- +系统总线+ ------------------- + CLE,io [8:0] + ----- -------------- +
 | cpu |←------------→| nand控制器| ←--------------→| nand flash |
+ ----------- + + ------------------- + + --------------- ---- +

cpu通过系统总线访问nand控制器寄存器,设置读写flash的命令和相应的地址,当完成操作时nand controler发出中断,也可以通过查询nand controler的状态寄存器来获取操作状态,nand controler将相应的命令状态为nand flash能够理解的时序

nand flash引脚
nandflash驱动详解_第1张图片
如果支持直接访问模式,可以将nandflash直接映射到物理地址空间,通过XOR可以映射到不同的物理空间,这样就可以直接从nandflash执行程序了(注意不能写,因为写之前必须要先擦除)

NAND直接 存取模式  

/ *禁止所有NAND CS的直接寻址+ XOR * /
    BDEV_UNSET(BCHP_NAND_CS_NAND_SELECT,0xff);
    BDEV_UNSET(BCHP_NAND_CS_NAND_XOR,0xff);

nandflash控制器全局寄存器
nandflash驱动详解_第2张图片
nandflash驱动详解_第3张图片
nandflash驱动详解_第4张图片
nandflash驱动详解_第5张图片
nandflash驱动详解_第6张图片
oob:spare_area 0x40 = 64byte
数据:flash_cache 128字节= 128 * 4 = 512字节

1.BCHP_NAND_CS_NAND_SELECT:
[0:0]  EBI_CS_0_SEL【0-7】每位对一个银行
指示EBI_CS0b是否使用直接寻址机制,并将Nand Flash映射到MIPS物理地址空间,并使用EBI_CS_BASE_0寄存器指定的基址和大小。(EBI_CS_CONFIG_0将被忽略。)

(注意:建议软件使用寄存器阵列寻址,除了初始引导读取之外的所有Nand Flash访问,无论此位是否设置,都可以使用寄存器阵列寻址。

1 =允许直接寻址EBI_CS0b。
0 =仅使用EBI_CS0b的寄存器阵列寻址。当芯片从NAND闪存启动时,该位被初始化。

[8:8] EBI_CS_0_USES_NAND 【8-15】每位对   一个银行
指示EBI_CS0b是否连接到Nand Flash设备。该位不被硬件使用,但可由软件用于指示哪些EBI_CS连接到Nand Flash器件。当芯片从NAND闪存启动时,该位被初始化。

保留 [27:16]

WR_PROTECT_BLK0 [28:28] RW 
该位将保护连接到EBI_CS0b的Nand Flash的块0的内容。该位置1时,控制器将忽略目标地址位于EBI_CS0b块0中的闪存修改操作(PAGE_PROGRAM,ERASE等)。(块大小由NAND_CONFIG_CS [n]寄存器的BLOCK_SIZE字段决定。)

1 =块写入EBI_CS0b块0。
0 =允许写入EBI_CS0b块0。

复位值:0x0

NAND_WP [29:29] RW 
该位控制NAND_WPb引脚(通过反相器)以保护闪存器件。只要SW希望擦除或编程闪存,该位应由SW清零。

(请注意,内部电压监视器检测VDD,并且当VDD超出容许范围时,NAND_WPb被驱动为低电平,无论此位的状态如何。)

1 = NAND_WPb引脚为低电平。Flash受到写保护。
0 = NAND_WPb引脚为高电平。Flash可以被擦除或编程。

重置值:0x1

AUTO_DEVICE_ID_CONFIG [30:30] RW 
如果设置了该位,则Nand Flash控制器将在任何其他Nand Flash访问之前自动执行FLASH_RESET,然后执行Read Device ID命令。从Nand Flash器件返回的值将被硬件用于基于内部维护的已知Nand Flash器件的数据库来初始化 NAND_CONFIG_CS [n] 寄存器。
当strap_nand_flash = 1时,该位​​将被置1,并且读取器件ID命令和NAND_CONFIG_CS [n]寄存器的初始化将在上电时发生。
如果软件没有设置该位,并且strap_nand_flash = 0,软件应该在使用Nand Flash控制器之前(在写入CMD_START之前)执行FLASH_RESET命令并初始化NAND_CONFIG_CS [n]寄存器。

1 =执行FLASH_RESET和Device ID读取,NAND_CONFIG_CS [n]寄存器自动初始化。
0 =不要执行设备ID读取或NAND_CONFIG_CS [n]初始化。

当芯片从NAND闪存启动时,该位被初始化。

CS_LOCK [31:31] RW 
当该位设置为1时,该寄存器变为只读寄存器,防止进一步写入该寄存器。一旦设置,该位只能通过复位清零。

复位值:0x0

2.BCHP_NAND_CS_NAND_XOR
EBI_CS_0_ADDR_XOR [0:0] RW   【0-7】每位对 一个银行
指示为EBI_CS0b指定的地址是否与0xE000_0000异或。
1 =与E000_0000到EBI_CS0b的XOR地址。
0 =直接将地址传递给EBI_CS0b(无XOR)。当芯片从NAND闪存启动时,该位被初始化。

保留 [30:8] RWX   

ONLY_BLOCK_0_XOR [31:31] RW 
仅对块0打开地址XOR。CS0中的所有其他块地址不会异或。
该位仅影响CS0,因为块0被定义为CS0器件的第一个块。要使用该位,EBI_CS_0_ADDR_XOR也必须设置为1。
当控制器空闲时,软件应该改变这个位。
1 =仅对块0使用E000_0000的XOR地址。
0 =所有块的EOR_0000异或地址。

复位值:0x0

INTFC_STATUS - Nand Flash接口状态
CTLR_READY [31:31] R表示EBI内部Nand Flash接口控制器状态机逻辑已完成此接口的所有命令。

FLASH_READY [30:30] R反映外部Nand Flash器件的R / B(就绪/忙碌)引脚的即时状态。(0 =忙碌)

CACHE_VALID [29:29] R指示512字节的FlashCache缓冲区是否包含位于FLASH_READ_ADDR寄存器指示的地址处的有效数据。

SPARE_AREA_VALID [28:28] R指示16字节备用区域读取寄存器是否包含位于FLASH_READ_ADDR寄存器指示的地址处的有效数据。

ERASED [27:27] R指示最近的PAGE_READ命令是否导致数据和备用区域的所有字节均为FFh。

PLANE_READY [26:26] R仅适用于多平面设备。
指示EBI内部Nand Flash接口控制器状态机逻辑已在当前平面上完成PROGRAM_PAGE_MULTI命令。

保留[25:8] RX   

FLASH_STATUS [7:0] R在每次程序页面,块擦除或复制命令操作后,使用来自闪存设备的状态读取命令更新此字节值。
复位值:0x0


每个银行的配置寄存器:
nandflash驱动详解_第7张图片
Brcm nandflash控制器最多支持7个银行

  BCHP_NAND_ACC_CONTROL_CS0             
 Nand Flash访问控制 

RD_ECC_EN [31:31] RW为PAGE_READ操作启用ECC校正。
重置值:0x1

WR_ECC_EN [30:30] RW为任何块启用对PROGRAM_PAGE或PROGRAM_SPARE_AREA操作的ECC校验位生成。当该位置位时,ECC字节将被忽略。
选择汉明码时的ECC字节位置是SPARE_AREA_WRITE_OFS_4和SPARE_AREA_WRITE_OFS_8寄存器的BYTE_OFS_6,BYTE_OFS_7和BYTE_OFS_8的3个字节。选择BCH码时的ECC字节位置是备用区域的最后字节位置。例如,如果SPARE_AREA_SIZE = 16且ECC_LEVEL = 4(7个ECC字节),则ECC字节位置是BYTE_OFS_9到BYTE_OFS_15中的16个字节中的最后7个。
当此位设置为PROGRAM_PAGE操作时,ECC字节将由H / W生成的ECC校验字节替代; 对于PROGRAM_SPARE_AREA操作,ECC字节将被FFh替换。
重置值:0x1

CE_CARE [29:29] RW此字段仅适用于CE护理设备。
当设置为1时,EBI_CS [n]将在NAND_RBb低电平期间保持置位状态(器件忙)。在此期间,其他芯片选择中的器件无法访问。
默认设置为0以获得更好的性能。
复位值:0x0

保留 [28:28] RWX   

RD_ERASED_ECC_EN [27:27] RW确定擦除页面中的PAGE_READ操作是否会生成不可纠正的ECC错误。(一个Erased Page被定义为全部512个数据字节,所有3个ECC字节都包含0xFF,剩余的Spare Area字节未被考虑)。无论该位的状态如何,如果ECC被RD_ECC_EN = 0或RD_ECC_BLK0_EN = 0禁用,将不会生成错误。
1 =从擦除页读取生成ECC错误。
0 =从擦除页读取不会产生ECC错误。
复位值:0x0

PARTIAL_PAGE_EN [26:26] RW启用部分页面编程。建议清除此位以提高性能并防止部分页面编程,这是许多nand闪存设备所禁止的。   当该位置位且软件启动PROGRAM_PAGE操作时,硬件将数据直接写入闪存单元。当该位清零并且软件启动PROGRAM_PAGE操作时,硬件将数据移入NAND Flash设备缓存并返回空闲状态。只有当NAND_CMD_ADDRESS指向页面的最后512字节扇区时,硬件才会将数据编程到闪存单元中。
小页面设备会忽略该位。
复位值:0x0

WR_PREEMPT_EN [25:25] RW使能其他EBI或PCI周期的写突发操作的抢占。当另一个请求者试图使用总线时,这允许将来自Flash外部总线的长512字节突发分成16次传输的多个突发。
重置值:0x1

PAGE_HIT_EN [24:24] RW启用Flash页面检测检测,作为页面大小大于512B的页面内512字节读取的优化。该位仅为大页面设备提供性能优化。如果设置,并且硬件发现读取未命中,但是从已经读入FlashCache的相同页面,它将简单地使用闪存的随机读取操作来节省时间。
重置值:0x1

PREFETCH_EN [23:23] RW硬件自动读取当前页面读取命令地址的下一个512B或1KB数据,扇区大小取决于SECTOR_SIZE_1K位。启用此位可提高读取连续地址的性能。
预取对软件是透明的。CTLR_READY位始终指示当前地址读取完成。注:建议将此位设置为长连续读取,例如DMA传输。如果读取地址不连续,则由于后台预取,软件可能会看到更长的繁忙时间和性能下降。
注:仅当ECC_type为BCH时才支持预取。
复位值:0x0

CACHE_MODE_EN [22:22] RW启用程序页面或页面读取缓存模式。
如果置位,并且发出页面读取命令,硬件将启动具有缓存模式操作码31h的页面读操作。
如果置位,并且发出程序页命令,则硬件以高速缓存模式操作码15h结束程序页操作。
缓存模式通过连续页面访问跳过闪存设备繁忙时间来提高性能。例如:第n,n + 1,n + 2,n + 3,n + 4 ... n + m的DMA传输
注:硬件不检测页面地址的连贯性。软件需要管理设置这一点。要恢复正常的页面读取和程序页面命令,请取消设置此位。
复位值:0x0

保留 [21:21] RWX   

ECC_LEVEL [20:16] RW设置每512个数据字节可以纠正的编码类型和位数。
定义T:=每扇区可修正的位数。
当SECTOR_SIZE_1K = 0时,则T = ECC_LEVEL
当SECTOR_SIZE_1K = 1时,则T = ECC_LEVEL * 2
异常是SECTOR_SIZE_1K = 0,SPARE_AREA_SIZE = 16,ECC_LEVEL = 15,则T = 1(海明模式)

保留 [15:8] RWX   

SECTOR_SIZE_1K [7:7] RW定义ECC码字大小,也称为扇区大小。注意:当使用1k字节扇区大小时,ECC_LEVEL字段指定T值的一半,汉明码不可用。
请参阅ECC_LEVEL说明中显示的表。
注意:当使用1k字节扇区大小时,必须清除PARTIAL_PAGE_EN位,并且闪存页面大小必须大于512。
0 512字节
1个1k字节

当strap_nand_flash = 1时,该字段将根据选定的ECC级别绑定在strap_nand_ecc_level [n:0]上进行初始化。
复位值:0x0

SPARE_AREA_SIZE [6:0] RW表示每512个数据字节包含在闪存设备中的备用区字节数。有效设置是16到64(含)。对于16位宽的NAND闪存器件,此字段必须包含偶数。
对于大页面设备(页面大小为2kB或更大),每页的备用区域除以每页的扇区数量。   例如,(2kB + 64B)页面大小和512B扇区大小每页有4个扇区,每页全部64字节备用区域将被分成4个相等的部分,每个16字节; 并且这些部分中的每一个都将被不同的部门使用。
选择汉明码时的ECC字节位置 是SPARE_AREA_WRITE_OFS_4和SPARE_AREA_WRITE_OFS_8寄存器的BYTE_OFS_6,BYTE_OFS_7和BYTE_OFS_8的3个字节。
选择BCH码时的ECC字节位置是该特定扇区的备用区的最后一个字节位置。   例如,如果SPARE_AREA_SIZE = 16且ECC_LEVEL = 4(7个ECC字节),则ECC字节位置是BYTE_OFS_9到BYTE_OFS_15中的16个字节中的最后7个。
当SECTOR_SIZE_1K = 1时,该字段仍指定每512个备用字节。

当strap_nand_flash = 1时,该字段将根据选定的ECC级别绑定在strap_nand_ecc_level [n:0]上进行初始化。
重置值:0x10

因为ECC校验是由控制器自动完成的,那么就必须让控制器知道该bank上的nandflash使用的ECC LEVEL和SPARE_AREA_SIZE,这样控制器就能知道ECC字节在SPARE_AREA_READ_OFS_0中的位置.ECC字节在nandflash中的位置理论上来说跟nandflash没有多大关系,因为nandflash对ECC字节是无知的,它由控制器来使用,nandflash会根据本身工艺推荐ECC LEVEL,理论上大于这个值都是可行的。


BCHP_NAND_CONFIG_EXT_CS0          
Nand Flash配置扩展 
闪存设备接口参数。根据NAND_CMD_EXT_ADDRESS寄存器中的CS_SEL值,硬件会根据刚刚复位后从外部Nand Flash器件读取的DeviceID或ONFI参数值将缺省值装入此寄存器。

保留[31:12] RWX   

BLOCK_SIZE [11:4] RW块大小(以字节为单位)。(注意:该字段仅用于确定目标地址是否位于闪存的块0中,以便在WR_PROTECT_BLK0位置位时知道是否应用写保护,或者当RD_ECC_BLK0_EN位为是时是否执行ECC纠正组。)
值名称
0 BK_SIZE_8KB
1 BK_SIZE_16KB
2 BK_SIZE_32KB
3 BK_SIZE_64KB
4 BK_SIZE_128KB
5 BK_SIZE_256KB
6 BK_SIZE_512KB
7 BK_SIZE_1024KB
8 BK_SIZE_2048KB
9 BK_SIZE_4096KB
10 BK_SIZE_8192KB

PAGE_SIZE [3:0] RW页面大小(以字节为单位)。
值名称说明
0 PG_SIZE_512 512字节。
1 PG_SIZE_1KB 1k字节。
2 PG_SIZE_2KB 2k字节。
3 PG_SIZE_4KB 4k字节。
4 PG_SIZE_8KB 8k字节。
5 PG_SIZE_16KB 16k字节。


BCHP_NAND_CONFIG_CS0                 
Nand Flash配置
闪存设备接口参数。根据NAND_CMD_EXT_ADDRESS寄存器中的CS_SEL值,硬件会根据刚刚复位后从外部Nand Flash器件读取的DeviceID或ONFI参数值将缺省值装入此寄存器。

CONFIG_LOCK [31:31] RW当该位设置为1时,该寄存器和CONFIG_EXT寄存器变为只读寄存器,防止进一步写入该寄存器和CONFIG_EXT寄存器。一旦设置,该位只能通过复位清零。
复位值:0x0

保留[30:28] RWX   

DEVICE_SIZE [27:24] RW设备大小(以字节为单位)。
值名称
0 DVC_SIZE_4MB
1 DVC_SIZE_8MB
2 DVC_SIZE_16MB
3 DVC_SIZE_32MB
4 DVC_SIZE_64MB
5 DVC_SIZE_128MB
6 DVC_SIZE_256MB
7 DVC_SIZE_512MB
8 DVC_SIZE_1GB
9 DVC_SIZE_2GB
10 DVC_SIZE_4GB
11 DVC_SIZE_8GB
12 DVC_SIZE_16GB
13 DVC_SIZE_32GB
14 DVC_SIZE_64GB
15 DVC_SIZE_128GB

DEVICE_WIDTH [23:23] RW设备I / O数据总线宽度(以位为单位)。
值名称
0 DVC_WIDTH_8
1 DVC_WIDTH_16

保留[22:19] RWX   

FUL_ADR_BYTES [18:16] RW在设备内发送到Flash的完整地址的地址字节数。
当PAGE_SIZE> 512B时,FUL_ADR_BYTES = COL_ADR_BYTES + BLK_ADR_BYTES。
当PAGE_SIZE = 512B时,FUL_ADR_BYTES = 1 + BLK_ADR_BYTES。
该字段通过绑定或AUTO_DEVICE_ID_CONFIG初始化。

保留[15:15] RWX   

COL_ADR_BYTES [14:12] RW发送到Flash以指定页面内随机数据访问的列地址的地址字节数。该字段仅用于页面大小大于512B的页面,用于页面内的随机寻址,以寻址PROGRAM_PAGE和PAGE_READ命令的备用区域。
2为512B <=页面大小<= 32KB
该字段通过绑定或AUTO_DEVICE_ID_CONFIG初始化。

保留[11:11] RWX   

BLK_ADR_BYTES [10:8] RW发送到Flash以指定行和块的地址字节数。该字段相当于NAND闪存数据表中的页面地址+块地址。
该字段仅用于BLOCK_ERASE和BLOCKS_UNLOCK命令。
该字段通过绑定或AUTO_DEVICE_ID_CONFIG初始化。

保留[7:0] RWX   


  BCHP_NAND_TIMING_1_CS0                
 Nand Flash时序参数1 
闪存设备接口时序参数。时序以内部108 MHz时钟(9.26 ns)或内部216 MHz时钟(4.63 ns)为单位指定,具体取决于TIMING_2.CLK_SELECT的值。
任何时间字段中的最小值是2(2个时钟)。

tWP [31:28] RW时钟数WE低脉冲宽度。

重置值:0x6
tWH [27:24] RW片段数WE高脉冲宽度。(除了ALE较高时,在这种情况下,tALH控制WE高脉冲宽度而不是tWH。)

重置值:0x5
tRP [23:20] RW时钟数RE低脉冲宽度。

重置值:0x7
tREH [19:16] RW时钟数RE高脉冲宽度。

重置值:0x4
tCS [15:12] RW从CE低到第一个WE低的时钟数。注意:当CLK_SELECT = CLK_108时,该寄存器字段中的值将被硬件加倍,当CLK_SELECT = CLK_216时,该值将被硬件加倍。

重置值:0x8
tCLH [11:8] RW从WE高到CLE低的块数。

重置值:0x4
tALH [7:4] RW从WE高到ALE低的时针数量。而且,当ALE高时,屏幕的数量WE高脉冲宽度。该位应该被设置为来自闪存制造商数据表的tWH和tALH之间的较大值。

重置值:0x5
tADL [3:0] RW从最后一个addr到第一次数据写入的时钟片数。注意:当CLK_SELECT = CLK_108时,该寄存器字段中的值将被硬件加倍,当CLK_SELECT = CLK_216时,该值将被硬件加倍。

复位值:0xB


BCHP_NAND_TIMING_2_CS0                  
 Nand Flash时序参数2 
闪存设备接口时序参数。时序以内部108 MHz时钟(9.26 ns)或内部216 MHz时钟(4.63 ns)为单位指定,具体取决于TIMING_2.CLK_SELECT的值。
任何时间字段中的最小值是2(2个时钟)。

CLK_SELECT [31:31] RW确定NAND_TIMING_1和NAND_TIMING_2中的参数用于nand闪存定时状态机的时钟。
值名称说明
0 CLK_108内部108 MHz时钟。
1 CLK_216内部216 MHz时钟。

重置值:CLK_108
保留[30:20] RWX   
tCCS [19:16] RW更改列命令设置时间。
注:当CLK_SELECT = CLK_108时,硬件将该寄存器字段中的值翻两倍,并且当CLK_SELECT = CLK_216时,该值将由硬件八倍。

重置值:0x9
保留[15:13] RWX   
tWB [12:9] RW在硬件开始采样R / B引脚之前,CE的高电平时钟数。注意:当CLK_SELECT = CLK_108时,该寄存器字段中的值将被硬件加倍,当CLK_SELECT = CLK_216时,该值将被硬件加倍。

复位值:0xF
tWHR [8:4] RW从WE高到RE低的时钟片数。此参数仅用于状态读取和设备ID读取操作。注意:当CLK_SELECT = CLK_108时,该寄存器字段中的值将被硬件加倍,当CLK_SELECT = CLK_216时,该值将被硬件加倍。

重置值:0x9
tREAD [3:0] RW RE低后采样读取数据之前等待的时钟数。

为获得最佳性能,应通过将nns闪存器件的RE存取时间(tREA)加上17ns,然后将总和除以CLK_SELECT所选的时钟周期,然后将其四舍五入至下一较大整数来计算此参数。例如,如果器件tREA = 20ns并且CLK_SELECT = 1(216MHz => 4.63ns):
tREAD =(20ns + 17ns)/4.63ns=7.99(四舍五入)=> 8。

tREAD的最大值是(tRP + tREH)。

重置值:0x6

操作时序
在编程操作中,要被编程的数据 在WE#的上升沿 被同步到数据寄存器中
数据以类似的方式通过读使能(RE#)信号从数据寄存器输出,该信号负责输出当前数据并递增到下一个位置。WE#和RE#时钟每次传输可以运行25ns。 当RE#或芯片使能(CE#)未置为低电平时,输出缓冲器为三态 CE#和RE#的这种组合激活了输出缓冲器,使NAND Flash能够与其他类型的存储器(如NOR闪存,SRAM或DRAM)共享数据总线。这个功能有时被称为“芯片使能不关心”。
在RE#为低时,读使能,输出缓冲器直接放置在io上,可跟着变化。
所有的NAND Flash操作都是通过发出一个命令周期来启动的。这是通过将命令放在I / O [7:0]上,驱动CE#为低电平并且为高电平,然后发出WE#时钟来完成的。命令,地址和数据在WE#的上升沿时钟输入NAND闪存器件,
大多数命令需要多个地址周期,然后是第二个命令周期。除RESET和READ STATUS命令外,当设备忙时不应发出新命令。
支持的命令:
nandflash驱动详解_第8张图片
2Gb NAND Flash器件的寻址方案如表6所示。第一个和第二个地址周期(或字节)指定了列地址,它指定了页面内的起始字节。最后一列的位置是2112,因此最后一个位置的地址是第二个字节中的08h,第一个字节中是3Fh。PA [5:0]指定块内的页面地址,BA [16:6]指定块地址。尽管大多数PROGRAM和READ操作需要完整的5字节地址,但对于在页面内随机访问数据的操作,只需要第一个和第二个字节(或循环)。BLOCK ERASE操作只需要三个最重要的字节(第三,第四和第五)来选择该块。
nandflash驱动详解_第9张图片
当CA11 = 1时表示访问oob数据,一般列地址都为0,表示从页面内偏移0开始访问整个页面。

复位操作
RESET是NAND Flash器件繁忙时可以发出的两个命令之一。如果设备正忙于处理先前的命令,则发出RESET命令会中止前一个操作。如果之前的操作是ERASE或PROGRAM命令,则发出RESET命令会过早地中止该命令,并且所需的操作不会完成。ERASE和PROGRAM可以是耗时的操作; 发出RESET命令可以中止或稍后重新发出命令。
nandflash驱动详解_第10张图片

CE先拉低选中芯片,在io [8:0]上放置FFh,然后CLE拉高,将io [8:0]与命令锁存器相连,然后将WE拉低,接着拉高,在上升沿完成命令的写操作,延时TWB后查询R / B脚的状态,当为1时表示RESET完成。

READ ID操作
READ ID(90h)命令需要一个虚拟地址周期(00h),但不需要第二个命令周期。在发出命令和虚拟地址后,通过保持CLE和ALE为低电平并读取ID的每个字节的RE#信号,可以读出ID数据。
nandflash驱动详解_第11张图片
CE先拉低选中chip,在io [8:0]上放置90h,然后CLE拉高,将io [8:0]与命令锁存器相连,然后将WE拉低,接着拉高,在上升沿完成命令的写操作,然后拉低CLE,拉高ALE,将io [8:0]与地址锁存器相连,在io [8:0]上放置00h,然后将WE拉低,然后拉高WE ,在上升沿完成addrees的写操作,接着拉低ALE,适当的延时后拉低RE,(CLE和ALE都为低时,IO [8:0]与数据锁存器相连,CLE和ALE都为高时为非法),chip将数据放置在io [8:0]上,nand controler从io [8:0]上读取数据,读完后RE拉高,然后拉低读取下一个字节。

READ STATUS操作
读取状态(70h)是NAND Flash器件忙时可以发出的第二个命令。该命令不需要地址或第二个命令周期。通过在READ STATUS命令之后发出RE#时钟信号,可以监控NAND闪存器件的状态。 如果使用READ STATUS命令监视器件的就绪状态,则该命令只能发出一次,并且可以通过重新发出RE#时钟来重新读取状态。或者,RE#信号可以保持低电平,在继续之前等待接收适当的状态位。 READ STATUS还会报告写保护信号的状态以及之前PROGRAM或ERASE操作的通过/失败状态。在PROGRAM或ERASE操作中必须达到合格状态以确保数据完整性。
nandflash驱动详解_第12张图片

擦除操作
BLOCK ERASE(60h)操作会擦除整个64页的页面,总共128KB。要发出BLOCK ERASE操作,使用WE#信号在CLE置位时在ERASE(60h)命令中计时。接下来,在三个地址周期中的时钟,保持每个字节地址的ALE有效。(这三个地址周期是最重要的地址周期,包括块地址和页地址,如第9页的表6所示。)页地址部分(第三个地址周期的六个低位)被忽略,并且只使用三个最高有效字节的块地址部分。地址完全输入后,发出D0h的第二个命令(命令周期2),当CLE置位时,它由WE#提供时钟。这确认了ERASE操作,并且设备忙了大约500μs。当设备完成此操作时,它已准备好执行另一个命令。READ STATUS命令可以随时发出,即使在ERASE操作过程中设备处于繁忙状态。微处理器或控制器可以通过READ STATUS命令监视设备

nandflash驱动详解_第13张图片
提交70H命令查询状态后,RE一直为低,监控IO [5]引脚上的值,为0时表示擦除完成

程序操作
PROGRAM操作只能将位编程为0,并假定用户以先前擦除的块开始。如果用户不想编程一个位(或一组位),则通过将该特定位/组设置为1,可以将位保持在擦除状态。当接收到编程页(80h)命令时,输入寄存器被重置(内部)到全1。这支持只输入要用0位编程的数据字节。PROGRAM操作从80h命令开始(CLE置位 - 见图8)。接下来,取消断言CLE并断言ALE输入完整的五个地址周期。输入命令和地址后,数据输入到寄存器。当所有的数据都被输入时,发出10h命令来确认前一个命令并开始编程操作。PROGRAM操作通常需要220μs,虽然它可能需要多达600μs。用户必须阅读状态并检查操作是否成功。如果操作不成功,则该块应该记录为坏块并且将来不会使用。所有的数据应该移到一个好的块。
nandflash驱动详解_第14张图片
可以编程1-2112个字节,根据col add,如果页内偏移为0,即编程整个页,则2112个字节

READ操作
READ操作从00h命令开始,随后是5个地址周期,然后是30h命令以确认命令序列。经过大约25μs的读取传输时间(t R)后,数据被加载到寄存器并准备输出。置位RE#使NAND Flash器件能够输出与地址中指定的列地址相对应的数据的第一个字节。随后的RE#从连续的列位置转换输出数据。当RE#信号为高(未置位)时,I / O线为三态。 读取设备末尾(字节2112或字1056)会导致无效数据。
nandflash驱动详解_第15张图片
READ PAGE CACHE SEQUENTIAL操作
至此只讨论了NAND​​闪存器件中的一个寄存器。NAND Flash器件实际上有两个寄存器,一个数据寄存器和一个高速缓存寄存器,如图12所示。这两个寄存器的属性在各种NAND Flash缓存模式中起着重要的作用。PAGE READ CACHE MODE命令使用户能够在输出先前访问的数据的同时从数组中流水线化下一个顺序访问。这种双缓冲技术可以隐藏读取传输时间(t R)。数据最初从NAND闪存阵列传输到数据寄存器。如果高速缓存寄存器可用(不忙),数据将很快从数据寄存器移至高速缓存寄存器。数据传输到高速缓存寄存器后,数据寄存器可用,并且可以开始加载NAND闪存阵列中的下一个连续页面。与8位I / O设备上的传统PAGE READ命令相比,使用PAGE READ CACHE MODE命令可提高33%的性能,吞吐量可达31 MB / s。在16位I / O设备上,吞吐量可以提高到37 MB / s,与普通的PAGE READ操作相比,性能提高40%。
www.micron.com/products/nand/technotes上的 技术说明提供了有关缓存模式的更多详细信息以及如何使用它们来提高性能。PAGE READ CACHE MODE在系统启动时特别有用,当大量数据通常从NAND闪存设备中读取并且启动时间非常关键时。
nandflash驱动详解_第16张图片
PROGRAM PAGE CACHE操作
PROGRAM PAGE CACHE MODE提供比正常PROGRAM PAGE操作更高的性能(见图14和15)。PROGRAM PAGE CACHE MODE是一种双缓冲技术,它使控制器能够将数据直接输入到高速缓存寄存器,并使用数据寄存器作为存储区来提供用于编程阵列的数据。这将释放高速缓存寄存器,以便可以并行加载下一个顺序页面操作。在许多应用中,编程时间(t PROG)可以完全隐藏。与PAGE READ CACHE MODE命令一样,数据寄存器用于在整个编程周期内保持数据吞吐量。这将释放缓存寄存器以接收来自控制器的下一页数据。
nandflash驱动详解_第17张图片
nandflash驱动详解_第18张图片

存储方法
图10显示了将数据和备用信息存储在同一页面的两种常用方法。第一种方法显示512字节的数据区加上与其直接相邻的16字节备用区; 组合区域为528字节。一个2112字节的页面可以包含这些528字节元素中的四个。第二个实施涉及单独存储数据和备用信息。四个512字节的数据区首先被存储,并且它们对应的16字节备用区按顺序依次位于页面的末尾。
nandflash驱动详解_第19张图片

至于具体采用哪种存储方式,由控制器决定,在NANDFLASH看来,数据和OOB并没有什么区别.BRCM控制器采用的第二种方式。

部分页面编程
与NAND接口一致,NAND中的基本编程单元是一个页面; 然而,通过利用部分页面编程,可以将页面编程成更小的部分。为了便于部分页面编程,每个页面被进一步分成八个段(数据区域四个,备用区域四个)。
页面中的每个段都充当独立的可编程单元; 它们可以单独编程或以任何组合段编程。每个片段允许的最大连续部分页面程序数量为1。也就是说,页面中的每个段可以被编程一次,并且在需要块擦除之前页面可以被编程多达八次。
部分页面编程通过发出一个输入命令(80h),然后是多段编程的随机数据输入命令(85h)序列完成。加载最后一个数据后,程序确认命令(10h)启动编程操作并将数据写入到所需位置的缓冲区中。写状态位(I / O0)可以通过命令(70h)进行验证,以验证编程是否成功。
nandflash驱动详解_第20张图片
部分页面读取
部分页面读取是通过发出一个读取命令(00h),然后是一个随机数据读取命令(05h)序列来完成多段读取。 

读取内部数据移动操作
READ FOR INTERNAL DATA MOVE(00h-35h)命令也被称为“复制”。它提供了将数据从一个页面内部移动到另一个页面的能力 - 数据永远不会离开NAND Flash设备。READ FOR INTERNAL DATA MOVE操作将从NAND闪存阵列读取的数据传输到高速缓存寄存器。数据可以被编程到设备的另一个页面中。这在控制器在擦除块之前需要将数据从块中移出的情况下非常有用。在PROGRAM操作开始之前,也可以修改读取的数据。如果用户想在编程之前更改数据,这很有用。此功能可在NAND Flash设备内移动数据,而无需占用处理器或I / O总线。

代码分析
static uint32_t do_nand_cmd(struct nand_probe_info * info,uint32_t cmd,
        uint64_t addr)
{
    int t = 100 * 1000; / * 100ms * /

    BDEV_WR(BCHP_NAND_CMD_EXT_ADDRESS,
            (info-> cs << 16)| ((addr >> 32)&0xffff));
    BDEV_WR(BCHP_NAND_CMD_ADDRESS,addr&0xffffffff);
    BDEV_WR_F(NAND_CMD_START,操作码,cmd);

    while(!BDEV_RD_F(NAND_INTFC_STATUS,CTLR_READY)){
        如果(t <= 0)
            打破;
        bolt_usleep(1);

        t - = 1;
    }

   如果(!BDEV_RD_F(NAND_INTFC_STATUS,CTLR_READY))
           err_msg(“NAND:超时等待命令(%#04x @%llx)[%d,%d]”,
                cmd,addr,
                BDEV_RD_F(NAND_INTFC_STATUS,CTLR_READY),
                BDEV_RD_F(NAND_INTFC_STATUS,FLASH_READY));

    返回BDEV_RD(BCHP_NAND_INTFC_STATUS)&NAND_STATUS_FAIL;
}
BRCM的nand controler比较智能,我们只需要设置要进行的操作和地址,控制器会自动解释发出芯片能够理解的命令和地址。

Linux的内核中关于NANDFLASH的代码:
include / mtd / mtd-abi.h中 定义 nand_ecclayout 结构 指定OOB备用区域的布局:  
struct nand_ecclayout {
uint 32_t eccbytes;
uint32_t eccpos [64];
uint32_t oobavail;
struct nand_oobfree oobfree [MTD_MAX_OOBFREE_ENTRIES];
};

在此结构中, eccbytes 保存存储ECC数据的OOB字节的数量, eccpos 是包含ECC数据的OOB区域的 偏移 数组。oobfree 将可用于闪存文件系统的OOB区域中未使用的字节记录为用于存储标志的标志,例如 清除标志 ,表示擦除操作成功完成。
各个NAND程序驱动 根据芯片的属性 初始化它们的 nand_ecclayout
static struct nand_ecclayout * brcmstb_nand_create_layout(int ecc_level,
        struct brcmstb_nand_cfg * cfg)
{
    int i,j;
    struct nand_ecclayout * layout;
    int req;
    整个行业;
    int sas;
    int idx1,idx2;

    layout = kzalloc(sizeof(* layout),GFP_KERNEL);
    if(!layout){
        pr_err(“%s:无法分配内存\ n”,__func__);
        返回NULL;
    }

    sector = cfg-> page_size /(512 << cfg-> sector_size_1k);
    sas = cfg-> spare_area_size << cfg-> sector_size_1k;

    / *汉明* /
    if(is_hamming_ecc(cfg)){
        for(i = 0,idx1 = 0,idx2 = 0; i
            / *每个页面的第一个扇区可能有BBI * /
            if(i == 0){
                layout-> oobfree [idx2] .offset = i * sas + 1;
                / *小页面NAND使用字节6用于BBI * /
                if(cfg-> page_size == 512)
                    布局 - > oobfree [IDX2] .offset--;
                layout-> oobfree [idx2] .length = 5;
            } else {
                layout-> oobfree [idx2] .offset = i * sas;
                layout-> oobfree [idx2] .length = 6;
            }
            IDX2 ++;
            layout->eccpos[idx1++] = i * sas + 6;
            layout->eccpos[idx1++] = i * sas + 7;
            layout->eccpos[idx1++] = i * sas + 8;      //对于Hamming, 6,7,8 byte为Hamming ECC
            layout->oobfree[idx2].offset = i * sas + 9;
            layout->oobfree[idx2].length = 7;
            idx2++;
            /* Leave zero-terminated entry for OOBFREE */
            if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE ||
                    idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1)
                break;
        }
        goto out;
    }

    /*
     * CONTROLLER_VERSION:
     *   < v5.0: ECC_REQ = ceil(BCH_T * 13/8)
     *  >= v5.0: ECC_REQ = ceil(BCH_T * 14/8)  [see SWLINUX-2038]
     * But we will just be conservative.
     */
    req = (ecc_level * 14 + 7) / 8;      // 根据ECC LEVEL得到需要的ECC byte数
    if (req >= sas) {
        pr_info("%s: ECC too large for OOB, using dummy layout\n",
            __func__);
        memcpy(layout, &brcmstb_nand_dummy_layout, sizeof(*layout));
        return layout;
    }

    DBG("OOBLAYOUT: sas=%d  req=%d  sectors=%d\n", sas, req, sectors);

    layout->eccbytes = req * sectors;
    for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) {
        for (j = sas - req; j < sas && idx1 <
                MTD_MAX_ECCPOS_ENTRIES_LARGE; j++, idx1++)
            layout->eccpos[idx1] = i * sas + j;        //对于BCH,ECC byte放在每个oob的最后面

        /* First sector of each page may have BBI */
        if (i == 0) {
            if (cfg->page_size == 512 && (sas - req >= 6)) {   //小页使用byte 6指示坏块,一般page size都是2k
                /* Small-page NAND use byte 6 for BBI */
                layout->oobfree[idx2].offset = 0;
                layout->oobfree[idx2].length = 5;
                idx2++;
                if (sas - req > 6) {
                    layout->oobfree[idx2].offset = 6;
                    layout->oobfree[idx2].length =
                        sas - req - 6;
                    idx2++;
                }
            } else if (sas > req + 1) {
                layout->oobfree[idx2].offset = i * sas + 1;     //使用byte 0 指示坏块
                layout->oobfree[idx2].length = sas - req - 1;
                idx2++;
            }
        } else if (sas > req) {
            layout->oobfree[idx2].offset = i * sas;
            layout->oobfree[idx2].length = sas - req;
            idx2++;
        }
        /* Leave zero-terminated entry for OOBFREE */
        if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE ||
                idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1)
            break;
    }
out:
    /* Sum available OOB */
    for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE; i++)
        layout->oobavail += layout->oobfree[i].length;
    return layout;
}

对于大页nand flash,一个page含义多个sector,每个sector 512byte或者1024byte,一个block含有多个页。

通常,NAND控制器通过对OOB区域中的ECC字段进行操作来在硬件中执行纠错和检测。 但是,如果您的NAND控制器不支持错误管理,则需要通过软件让MTD为您完成。 MTD   nand_ecc 驱动程序( drivers / mtd / nand / nand_ecc.c )实现软件ECC。
    case NAND_ECC_SOFT:
        chip->ecc.calculate = nand_calculate_ecc;
        chip->ecc.correct = nand_correct_data;
        chip->ecc.read_page = nand_read_page_swecc;
        chip->ecc.read_subpage = nand_read_subpage;
        chip->ecc.write_page = nand_write_page_swecc;
        chip->ecc.read_page_raw = nand_read_page_raw;
        chip->ecc.write_page_raw = nand_write_page_raw;
        chip->ecc.read_oob = nand_read_oob_std;
        chip->ecc.write_oob = nand_write_oob_std;
        if (!chip->ecc.size)
            chip->ecc.size = 256;
        chip->ecc.bytes = 3;

包含坏块标记的OOB存储器字节。 这些标记用于标记有故障的闪存块,并且通常出现在属于每个块的第一页的OOB区域中。 标记在OOB区域内的位置取决于芯片的属性。 坏块标记可以在生产过程中在工厂设置,也可以在软件中检测到块中的磨损。 MTD在 drivers / mtd / nand / nand_bbt.c中 实现坏块管理
#
# The NAND_BAD_BLOCK_INDICATOR_MAP has the following format:
#
#        bit[15:8]   - Flags indicating which pages, offset from start of block,
#                      are used as bad block indicators.  Bit8 = first page.
#                      Bit15 = 8th page.
#
#        bit[7:0]    - Flags indicating which OFS bytes are used as bad block
#                      indicators for pages specified by bits[15:8].  Bit0 = OFS0.
#                      Bit7 = OFS7.
#
#        bit[31:24]  - Flags indicating which pages, offset from end of block,
#                      are used as bad block indicators.  Bit24 = last page.
#                      bit31 = 8th to the last page.
#
#        bit[23:16]  - Flags indicating which OFS bytes are used as bad block
#                      indicators for pages specified by bits[31:24].  Bit16 = OFS0.
#                      Bit23 = OFS7.
#
#
ifndef NAND_BAD_BLOCK_INDICATOR_MAP

ifeq ($(strip ${NAND_MEM_TYPE}),0)
    NAND_BAD_BLOCK_INDICATOR_MAP=0x00000301  #first 2 pages, OFS0
endif

ifeq ($(strip ${NAND_MEM_TYPE}),1)
    NAND_BAD_BLOCK_INDICATOR_MAP=0x00000301  #first 2 pages, OFS0
endif

ifeq ($(strip ${NAND_MEM_TYPE}),2)
    NAND_BAD_BLOCK_INDICATOR_MAP=0x03010000  #last 2 pages, OFS0
endif

ifeq ($(strip ${NAND_MEM_TYPE}),3)
    NAND_BAD_BLOCK_INDICATOR_MAP=0x03010000  #last 2 pages, OFS0
endif

endif
所以绝大多数都是block的前两个页的OFS0用来标记坏块,非0xFF则表示坏块。

解析的过程可以参考boot代码中的nand_get_page_status()函数

工具:
你必须使用 nanddump nandwrite 等NAND感知工具 而不是更常见的 dd 工具来创建或恢复NAND

nanddump -f /tmp/dump2.bin -l 131072 -s 131072  -o  /dev/mtd3

nandwrite -o  /dev/mtd3  /mnt/usb/dump1.bin


参考文档:
https://www.ece.umd.edu/~blj/CS-590.26/micron-tn2919.pdf
https://www.ece.umd.edu/~blj/CS-590.26/nand-presentation-2010.pdf


你可能感兴趣的:(mips,硬件驱动)