NXP

安霸flash dma操作

2019-07-12 12:21发布

flash的dma操作 nand_init() { //Flash_IO_dma_control 数据FIFO DMA使能和控制信息 //将memory target设置为NAND Flash,就是主内存和nand flash之间进行DMA数据传输 writel(FIO_DMACTR_REG,(readl(FIO_DMACTR_REG) & 0xcfffffff); //Force ReadID with 4-cycles writel(NAND_CTR_REG,readl(NAND_CTR_REG)|NAND_CTR_I4); //Reset chip 这个函数实现及过程在下面给出 nand_wait_cmd_done(NAND_CMD_RESET); //Read ID nand_wait_cmd_done(NAND_CMD_READID); id = readl(NAND_ID_REG); } void nand_wait_cmd_done(u32 cmd) { /*NAND_CMD_REG命令寄存器的后4位为命令标示位,具体命令详见芯片手册*/ writel(NAND_CMD_REG,cmd); rct_timer2_reset_count(); while(1) { /*读命令完成中断状态寄存器,命令完成后该中断标志会自动置为1*/ if(readl(NAND_INT_REG) & NAND_INT_DI)// 0x1 { break; } if(rct_timer2_get_count() >= NAND_CMD_TIMEOUT) { putstr("nand cmd timeout:"); puthex(cmd); putstr(" "); while(1); } } /*将中断完成标志为重新置为0*/ writel(NAND_INT_REG,0x0); } nand_reset() { /*Reset FIO FIFO,and Exit random read mode*/ setbitsl(FIO_CTR_REG,FIO_CTR_RR); rct_timer2_dly_ms(1);/*delay is must have*/ clrbitsl(FIO_CTR_REG,FIO_CTR_RR); /*Clear the FIO DMA Status Register*/ writel(FIO_DMASTA_REG,0x0); /*Setup FIO DMA Control Register*/ /*FIO_DMACTR_TS4B Transfer size 在AHB总线上每次传输的数据大小4字节*/ writel(FIO_DMACTR_REG,FIO_DMACTR_FL | FIO_DMACTR_TS4B); /*Setup NAND Flash Control Register*/ /*参考下面nand_control寄存器指定的相应位*/ writel(NAND_CTR_REG,flnand.control); /*清中断状态位*/ writel(NAND_INT_REG,0x0); /*Setup flash timing register*/ /*关于时序这块可参考下面详细的介绍*/ writel(NAND_TIM0_REG,flnand.timing0); ........ return 0; } //这些时序在nandflash的datasheet中可以查得 #define NAND_TCLS 12 #define NAND_TALS 12 #define NAND_TCS 20 #define NAND_TDS 12 #define NAND_TCLH 5 #define NAND_TALH 5 #define NAND_TCH 5 #define NAND_TDH 5 #define NAND_TWP 12 #define NAND_TWH 10 #define NAND_TWB 100 #define NAND_TRR 20 #define NAND_TRP 12 #define NAND_TREH 10 #define NAND_TRB 100 /*not defined in datasheet*/ #define NAND_TCEH 40 /*trhz - tchz = 60 - 20 = 40*/ #define NAND_TRDELAY 20 /*trea*/ #define NAND_TCLR 10 #define NAND_TWHR 60 #define NAND_TIR 0 #define NAND_TWW 100 #define NAND_TRHZ 60 #define NAND_TAR 10 #define NAND_TRHW 30 #define NAND_TADL 100 /*not defined in datasheet*/ #define NAND_TCRL 5 /*tcea - trea = 2*/ nand_control寄存器需要指定的功能位:
C2:列地址周期为2 2048-bytes pages
I4:ID读取需4个cycle
RC:read-confirm
CC:copy-confirm
IE:interrupt-enable
SZ:flash chip size 1G bit
EB:External banks 表面外部一共挂载的nandflash个数
WD:data bus width 8bit /** *Read data from NAND flash to memory *dst - address in dram *src - address in nand device *len - length to be read from nand *return - length of read data */ int nand_read_data(u8 *dst, u8 *src, int len) { val = src; block = val / flnand.block_size;//src地址对应的起始block val -= block * flnand.block_size; page = val / flnand.main_size; pos = val % flnand.main_size; pages = len / flnand.main_size;//读取数据长度占据的整页数 if(pos == 0) { first_ppage_size = 0;//读取地址是页的整数倍 } else { first_ppage_size = flnand.main_size - pos;//非整数倍,算出该页应读大小 } if(len >= first_ppage_size) { pages = (len - first_ppage_size) / flnand.main_size;//读取的整页数 last_ppage_size = (len - first_ppage_size) % flnand.main_size;//最后一页的大小 } else//读取长度不足一页 { first_ppage_size = len; pages = 0; last_ppage_seze = 0; } //读取长度被划分为以下三个部分,进行校验 if(len != first_ppage_size + pages * flnand.main_size + last_ppage_size) { return -1; } //接下来就是对应这三个部分分三步来进行数据读取 //第一部分 len = 0;//记录读取数据的实际长度 if(first_ppage_size) { rval = nand_read(block,page,1,buffer); if(rval < 0) { return len; } //因最小的读取单位为页,buffer里为整页的内容,要去除首页偏移 memcpy(dst, (void *)(buffer + pos), first_ppage_size); dst += first_ppage_size; len += first_ppage_size; nand_get_affset_adr(&block, &page, 1, rval); } //第二部分 if(pages > 0) { rval = nand_read(block, page, pages, dst); if(rval < 0) { return len; } dst += pages * flnand.main_size; len += pages * flnand.main_size; nand_get_offset_adr(&block, &page, pages, rval); } //第三部分 if(last_ppage_size > 0) { rval = nand_read(block, page, 1, buffer); if(rval < 0) { return len; } memcpy(dst, (void *)buffer, last_ppage_size); len += last_ppage_size; } return len; } //block和page后移 void nand_get_offset_adr(u32 *block, u32 *page, u32 pages, u32 bad_blks) { u32 blocks; blocks = pages / flnand.pages_per_block; pages = pages % flnand.pages_per_block; *block = *block + blocks; *page += pages; if(*page >= flnand.pages_per_block) { *page -= flnand.pages_per_block; *block += 1; } *block += bad_blks; } /* *block - 读取的起始block *page - 起始block中的起始页 *pages - 需读取的页数 *buf - 存放读回的数据 */ static int nand_read(u32 block, u32 page, u32 pages, u8 *buf) { /*起始block中需要读取的页数*/ first_blk_pages = flnand.pages_per_block - page; if(pages > first_blk_pages) { pages -= first_blk_pages; blocks = pages / flnand.pages_per_block; /*最后剩余的零页*/ last_blk_pages = pages % flnand.pages_per_block; } else { first_blk_pages = pages; blocks = 0; last_blk_pages = 0; } /*该函数也是将读取分为三个部分,这个不同在于是基于block来划分,每次读取之前都会进行坏块检测*/ /*第一部分*/ if(first_blk_pages) { while(nand_is_bad_block(block)) { block++; bad_blks++; } rval = nand_read_pages(block, page, first_blk_pages, buf, NULL, 1); if(rval < 0) { return -1; } block++; buf += first_blk_pages * flnand.main_size; } /*第二部分*/ while(blocks > 0) { while(nand_is_bad_block(block)) { block++; bad_blks++; } rval = nand_read_pages(block,0,flnand.pages_per_block,buf,NULL,1); if(rval < 0) { return -1; } block++; blocks--; buf += flnand.block_size; } /*第三部分*/ if(last_blk_pages) { while(nand_is_bad_block(block)) { block++; bad_blks++; } rval = nand_read_pages(block,0,last_blk_pages,buf,NULL,1); if(rval < 0) { return -1; } } return bad_blks; } 坏块检测这里不打算展开讲,由于其实现相对简单,这里只简述下它的管理思路。 1、创建BBT(bad block table) 2、将BBT存储在flash的最后一个block的第一页(防止最后一个block为坏块,一共预留了4个block) 3、坏块分类: 1)出厂坏块,在该block的第一页的OOB区域第6个字节标记为非0xff。 2)使用中产生的坏块,可以效仿出厂坏块进行标记,但这里为了区分,在第三页和第四页的OOB区域的第6个字节进行了标记。 4、BBT和坏块之间的映射。 1)出厂坏块 bbt[block >> 2] &= ~(0x03 << ((block << 1) % 8)); 2)使用中产生坏块 bbt[block >> 2] &= ~(0x01 << ((block << 1) % 8)); 3)从表中找出该block是否为坏块(逆操作) bb = (bbt[block >> 2] >> ((block << 1) % 8)) & 0x03; if(bb == 0x03) { 好块 } else if(bb == 0x02 || bb == 0x01) { 使用中产生坏块 } else if(bb == 0x0) { 出厂坏块 } DMA操作相关:
地址:0xE000_0000 - 0xE000_0FFF Flash 4-KB DMA Data FIFO
读操作时0xE000_0000作为DMA传输的源地址,buffer作为目的地址
写操作时0xE000_0000作为DMA传输的目的地址,buffer作为源地址 看完关于DMA的操作,觉得乏善可陈,按着安霸SDK提供的代码进行操作即可,没有自己可操作的需要。