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("\r\n");
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提供的代码进行操作即可,没有自己可操作的需要。
版权声明:本文为bang417原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。