安霸flash dma操作

  • Post author:
  • Post category:其他


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 版权协议,转载请附上原文出处链接和本声明。