Chinaunix首页 | 论坛 | 博客
  • 博客访问: 453621
  • 博文数量: 72
  • 博客积分: 3186
  • 博客等级: 中校
  • 技术积分: 1039
  • 用 户 组: 普通用户
  • 注册时间: 2009-03-07 16:53
文章分类

全部博文(72)

文章存档

2012年(1)

2011年(5)

2010年(10)

2009年(56)

我的朋友

分类: 嵌入式

2009-10-16 16:12:40

经过之前对nandflash的熟悉之后,大体已经了解了对其的操作。在vivi的main中使用了mtd来封装nandflash的一些具体操作。mtd对上层提供了统一接口来操作nandflash。这样,我们使用nandflash时就可以忽略细节而直接掉用mtd的一些处理函数。关于mtd的介绍可以参考网上的一些资料,这里就只对构建mtd的代码进行分析。

        /*
         * Step 5:
         */

        ret = mtd_dev_init();
        putstr("Succeed mtd initialization\r\n");



在mtdcore.c中代码如下

建立了一个mtd_info结构mymtd,这是一个全局的,在之后的有关nandflash的代码里会经常用到。等到具体用到它的时候再分析其各成员。

struct mtd_info *mymtd = NULL;
/*
 * Initialise
 */

extern int mtd_init(void);

int
mtd_dev_init(void)
{
        int ret = 0;

        printk("Initialize MTD device\n");

        ret = mtd_init(); //调用


//        add_command(&flash_cmd);


        return ret;
}

int
mtd_init(void)
{
        int ret;

        ret = smc_init(); //调用

        if (ret) {
                mymtd = NULL;
                return ret;
        }

        return 0;
}




struct mtd_info *mymtd = NULL;
/*
 * Initialise
 */

extern int mtd_init(void);

int
mtd_dev_init(void)
{
        int ret = 0;

        printk("Initialize MTD device\n");

        ret = mtd_init(); //调用


//        add_command(&flash_cmd);


        return ret;
}

int
mtd_init(void)
{
        int ret;

        ret = smc_init(); //调用

        if (ret) {
                mymtd = NULL;
                return ret;
        }

        return 0;
}


/*
 * === FUNCTION ======================================================================
 * Name: smc_init
 * Description:
 *         1. Create 'mtd_info' and 'nand_chip';
 *         2. mymtd->priv = this;
 *         3. Initialize 'struct nand_chip *this', the NAND Flash controller;
 *         4. Reset the NAND Flash;
 *         5. Call 'smc_insert(this)';
 * =====================================================================================
 */

static int
smc_init(void)
{
        struct nand_chip *this;
        u_int16_t nfconf;

        /* Allocate memory for MTD device structure and private date */
        mymtd = (struct mtd_info *)mmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip));

        if (!mymtd) {
                printk("Unable to allocate S3C2410 NAND MTD device structure.\n");
                return -ENOMEM;
        }

        this = (struct nand_chip *)(&mymtd[1]);
/* 以上代码,实际上分配了两个数据结构(mtd_info&nand_chip)所用的空间,前面一个为mymtd指向的mtd_info,紧接着的是this指向的nand_chip。 */
        memset((char *)mymtd, 0, sizeof(struct mtd_info));
        memset((char *)this, 0, sizeof(struct nand_chip));

        mymtd->priv = this; //将两个数据结构联系起来,所以this定义为局部变量,之后的其他函数里通过mymtd来找到这个nand_chip结构。


/* 配置nandflash,即写NFCONF寄存器。(在汇编里已经配置过一次,这里重新配置)*/
        nfconf = NFCONF;
        nfconf |= NFCONF_FCTRL_EN;
        nfconf &= ~NFCONF_TWRPH1;
        nfconf |= NFCONF_TWRPH0_3;
        nfconf &= ~NFCONF_TACLS;
        
        NFCONF = nfconf;

/* 填充nand_chip数据结构,nand_chip的成员是直接操作nandflash的。由于上面已经建立了mtd与nand_chip的联系,所以mtd对nand的操作都是通过调用nand_chip的成员函数来完成的。从这里就可以看出程序的层次关系,mtd在nand_chip的上层 */
        this->hwcontrol = smc_hwcontrol;
        this->write_cmd = write_cmd;
        this->write_addr = write_addr;
        this->read_data = read_data;
        this->write_data = write_data;
        this->wait_for_ready = wait_for_ready;

/* 0xff reset nand*/
        this->hwcontrol(NAND_CTL_SETNCE); //使能nandflash

        this->write_cmd(NAND_CMD_RESET); //写reset命令

        this->wait_for_ready(); //等待写操作完成

        this->hwcontrol(NAND_CTL_CLRNCE); //禁止nandflash


        smc_insert(this);

        return 0;
}




以进行smc_insert分析之前,我们先来看一下这个reset过程是如何完成的

        this->hwcontrol(NAND_CTL_SETNCE); //使能nandflash
        this->write_cmd(NAND_CMD_RESET); //写reset命令
        this->wait_for_ready();           //等待写操作完成
        this->hwcontrol(NAND_CTL_CLRNCE); //禁止nandflash

可以看到,使能和禁止就是配置NFCONF
static void
smc_hwcontrol(int cmd)
{
        switch (cmd) {
                case NAND_CTL_SETNCE:    NFCONF &= ~NFCONF_nFCE_HIGH; break;
                case NAND_CTL_CLRNCE:     NFCONF |= NFCONF_nFCE_HIGH; break;
                case NAND_CTL_SETCLE:      break;
                case NAND_CTL_CLRCLE:      break;
                case NAND_CTL_SETALE:      break;
                case NAND_CTL_CLRALE:      break;
                case NAND_CTL_DAT_IN:      break;
                case NAND_CTL_DAT_OUT:     break;
        }
}

写命令就是向NFCMD寄存器里写数据
static void
write_cmd(u_char val)
{
        NFCMD = (u_char)val;
}

等待就是判断NFSTAT里相应的标志位(操作完成后标志位就发生变化)
static void
wait_for_ready(void)
{
        while (!(NFSTAT & NFSTAT_RnB))
                udelay(10);
}


好了,继续来看下smc_insert做了什么

static inline int
smc_insert(struct nand_chip *this)
{
        if (smc_scan(mymtd)) //首先调用smc_scan这个函数,它做的工作有点复杂,下面具体分析。

                return -ENXIO;

/* 下面这一句看似是比较简单的,分配一个空间,并用this->data_buf指向它,但具体这个空间有什么用呢.其实它是用于写入的。我们已经知道nand的写是按页进行的,那么这里的buf正好是528=512+16.之后我们在烧写时,就是先把数据放到这个buf里,然后再把buf里的数据写到nand里 */
                                                              // 512 + 16

        this->data_buf = (u_char *)mmalloc(sizeof(u_char) * (mymtd->oobblock + mymtd->oobsize));
        if (!this->data_buf) {
/* 分配空间失败 */
                printk("Unable to allocate NAND data buffer for S3C2410.\n");
                this->data_buf = NULL;
                return -ENOMEM;
        }

        return 0;
}


smc_scan位于smc_core.c中




int
smc_scan(struct mtd_info *mtd)
{
        int i, nand_maf_id, nand_dev_id;
        struct nand_chip *this = mtd->priv;

        /* Select the device 使能并reset*/
        nand_select();

        /* Send the command for reading device ID 发送读ID的命令*/
        nand_command(mtd, NAND_CMD_READID, 0x00, -1);
        this->wait_for_ready();

        /* Read manufacturer and device IDs 读取nand的ID*/
        nand_maf_id = this->read_data(); // 0xec

        nand_dev_id = this->read_data(); // 0x76


        /* Print and store flash device information */
        for (i = 0; nand_flash_ids[i].name != NULL; i++) {
//这一句根据两个id,然后从id表里查找具体的芯片

                if (nand_maf_id == nand_flash_ids[i].manufacture_id && nand_dev_id == nand_flash_ids[i].model_id) {
                        if (!(mtd->size) && !(nand_flash_ids[i].page256)) {
                                /* 填充mtd结构 */
                                mtd->name = nand_flash_ids[i].name; //"Samsung K9D1208V0M"

                                mtd->erasesize = nand_flash_ids[i].erasesize; //0x4000

                                mtd->size = 1 << nand_flash_ids[i].chipshift; //26

                                mtd->eccsize = 256;
                                mtd->oobblock = 512;
                                mtd->oobsize = 16;
                                this->page_shift = 9;
                                this->dev = &nand_smc_info[GET_DI_NUM(nand_flash_ids[i].chipshift)];
                        }
/* NAND device: Manufacture ID: 0xec, Chip ID: 0x76 (Samsung K9D1208V0M) */
                        printk("NAND device: Manufacture ID: 0x%02x, Chip ID: 0x%02x (%s)\n", nand_maf_id, nand_dev_id, mtd->name);
                        break;
                }
        }
        nand_deselect();//禁止nand

        if (!mtd->size) {
                printk("No NAND device found!!!\n");
                return 1;
        }
/* 填充mtd结构 */
        mtd->type = MTD_NANDFLASH;
        mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;
        mtd->module = NULL;
        mtd->ecctype = MTD_ECC_SW;
        mtd->erase = nand_erase;//从这以下会填充一些操作nandflash的函数

        mtd->point = NULL;
        mtd->unpoint = NULL;
        mtd->read = nand_read;
        mtd->write = nand_write;
        mtd->read_ecc = nand_read_ecc;
        mtd->write_ecc = nand_write_ecc;
        mtd->read_oob = nand_read_oob;
        mtd->write_oob = nand_write_oob;
        mtd->lock = NULL;
        mtd->unlock = NULL;

        return 0;
}



nand_select() 只是使能nandflash,然后reset,最后延时一下。
#define nand_select() this->hwcontrol(NAND_CTL_SETNCE); \  //使能nandflash,
        nand_command(mtd, NAND_CMD_RESET, -1, -1); \  //这个只是reset nandflash
        udelay(10);


参数:
mtd mtd_info结构
command nandflash命令(仅当其不为NAND_CMD_SEQIN)时才执行这个命令)
column nandflash列地址(仅当column不为-1时才写列地址)
page_addr 页地址(仅当其不为-1时才写页地址)
函数作用:有条件地执行command命令且有条件地写nandflash地址


static void
nand_command(struct mtd_info *mtd, unsigned command, int column, int page_addr)
{
        register struct nand_chip *this = mtd->priv;

        this->hwcontrol(NAND_CTL_SETCLE); //无效

        this->hwcontrol(NAND_CTL_DAT_OUT); //无效


        if (command != NAND_CMD_SEQIN)
                this->write_cmd(command);
        else {
                if (mtd->oobblock == 256 && column >= 256) {
                        column -= 256;
                        this->write_cmd(NAND_CMD_RESET);
                        this->write_cmd(NAND_CMD_READOOB);
                        this->write_cmd(NAND_CMD_SEQIN);
                } else if (mtd->oobblock == 512 && column >= 256) {
                        if (column < 512) {
                                column -= 256;
                                this->write_cmd(NAND_CMD_READ1);
                                this->write_cmd(NAND_CMD_SEQIN);
                        } else {
                                column -= 512;
                        this->write_cmd(NAND_CMD_READOOB);
                        this->write_cmd(NAND_CMD_SEQIN);
                        }
                } else {
                                this->write_cmd(NAND_CMD_READ0);
                                this->write_cmd(NAND_CMD_SEQIN);
                }
        }
        this->hwcontrol(NAND_CTL_CLRCLE); //无效

        this->hwcontrol(NAND_CTL_SETALE); //无效


        if (column != -1)
                this->write_addr(column);
        if (page_addr != -1) {
                this->write_addr((u_char)(page_addr & 0xff));
                this->write_addr((u_char)((page_addr >> 8) & 0xff));
                if (mtd->size & 0x0c000000) //通过nand的大小来判断是否需要四步寻址

                        this->write_addr((u_char)((page_addr >> 16) & 0xff));

                this->hwcontrol(NAND_CTL_CLRALE);
                this->hwcontrol(NAND_CTL_DAT_IN);

                udelay(15);
        }
}



至此,mtd这一层就构建好了




2. NandFlash的写操作


从上面可以看到它即为mtd 结构的写成员指向的函数。
参考这块芯片的数据手册。其中,关于nand的写(program)的流程图如 下:



/*
 * NAND write 对应着上面的流程图,阅读这段程序就容易多了。
< /p>

 * mtd -> mtd_info 结构

 * to -> (要写入nand中的数据的)目标地址

 * len -> 写入数据的大小(长度)

 * retlen -> 记录写入数据的长度

 * buf -> 待写入的数据首地址

 * 返回值:成功返回0 ,失败返回相应错误代码。retlen里记录写入了的数据的长度,供后续程序使用
< /p>

 */
static int
nand_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
{
        unsigned int i, col, cnt;
        int page, status;
        struct nand_chip *this = mtd->priv;

        DEBUG(MTD_DEBUG_LEVEL3,
                        __FUNCTION__"(): to = 0x%08x, len = %i\n", (unsigned int)to, (int)len);

        if ((to + len) > mtd->size) {
                DEBUG(MTD_DEBUG_LEVEL0,
                                __FUNCTION__"(): Attempted write past end of device\n");
                return -EINVAL;
        }

        page = ((int)to) >> this->page_shift;

        col = to & (mtd->oobblock - 1);

        *retlen = 0;

        nand_select();
        nand_command(mtd, NAND_CMD_STATUS, -1, -1);
        this->wait_for_ready();
        if (!(this->read_data() & SMC_STAT_NOT_WP)) {
                DEBUG(MTD_DEBUG_LEVEL0,
                                __FUNCTION__"(): Device is write protected!!!\n");
                i = -EPERM;
                goto nand_write_exit;
        }

        while (*retlen < len) {
                // data,从col开始写到512,而0到col无有效数据,在下面会置0xff

                if ((col + len) > mtd->oobblock)
                        for (i = col, cnt = 0; i < mtd->oobblock; i++, cnt++)
                                this->data_buf[i] = buf[*retlen + cnt];
                else
                        for (i = col, cnt = 0; cnt < (len - *retlen); i++, cnt++)
                                this->data_buf[i] = buf[*retlen + cnt];
                //ecc 512->528

                for (i = mtd->oobblock; i < (mtd->oobblock + mtd->oobsize); i++)
                        this->data_buf[i] = 0xff;

                //一页内,col之前的都置为0xff(因为这部分无有效数据)

                for (i = 0; i < col; i++)
                        this->data_buf[i] = 0xff;
                if ((col + (len - *retlen)) < mtd->oobblock)
                        for (i = col + cnt; i < mtd->oobblock; i++)
                                this->data_buf[i] = 0xff;
               
                //write 0x80 -> write Address

                nand_command(mtd, NAND_CMD_SEQIN, 0x00, page);
                this->hwcontrol(NAND_CTL_DAT_OUT);
                //write data

                for (i = 0; i < mtd->oobblock + mtd->oobsize; i++)
                        this->write_data(this->data_buf[i]);
                this->hwcontrol(NAND_CTL_DAT_IN);
                
                //write 0x10

                nand_command(mtd, NAND_CMD_PAGEPROG, -1, -1);

                this->wait_for_ready(); // wait until R/B = 1


                status = 0;
                //read status register

                for (i = 0; i < 24; i++) {
                        udelay(125);
                        nand_command(mtd, NAND_CMD_STATUS, -1, -1); //write 0x70

                        status = (int)this->read_data();
                        if (status & SMC_STAT_READY) //i/o 6 = 1 ?

                                break;
                }
                
                //I/O 0 = 0 ?

                if (status & SMC_STAT_WRITE_ERR) {
                        //if I/O=0, Program Error

                        DEBUG(MTD_DEBUG_LEVEL0,
                                        __FUNCTION__"(): Failed write, page 0x%08x, %6i bytes were sucesful\n", page, *retlen);
                        i = -EIO;
                        goto nand_write_exit;
                }

                /*
                 * verify. read nandflash and compare the data
                 */

                if (col < mtd->eccsize)
                        nand_command(mtd, NAND_CMD_READ0, col, page);
                else
                        nand_command(mtd, NAND_CMD_READ1, col - 256, page);
                this->wait_for_ready();
                for (i = col; i < cnt; i++) {
                        if (this->data_buf[i] != this->read_data()) {
                                DEBUG(MTD_DEBUG_LEVEL0,
                                                __FUNCTION__"(): Failed write verify, page 0x%08x, %6i bytes were succesful\n", page, *retlen);
                                i = -EIO;
                                goto nand_write_exit;
                        }
                }
                if (col)
                        col = 0x00;
                *retlen += cnt;
                page++; //page program completed -> next page

        }
        *retlen = len;
        i = 0;
nand_write_exit:
        nand_deselect();

        return i;
}



3. NandFlash擦除操作
同样对应着下面的流程图,其擦除代码就也很简单。

/*
 * NAND erase

 * 参数: mtd 即mtd_info结构

 * instr 这是一个erase_info结构,其中两个关键的成员项:addr(待擦除的起始地址).len(擦除长度).

 * 由于擦除是按block进行的,所以保证这两个成员的值是按block对齐的

 * 返回值: 0成功 。   非0 相应的错误代码。

 */
static int
nand_erase(struct mtd_info *mtd, struct erase_info *instr)
{
        int i, page, len, status, pages_per_block;
        struct nand_chip *this = mtd->priv;

        DEBUG(MTD_DEBUG_LEVEL3,
                        __FUNCTION__"(): start = 0x%08x, len = %i\n",
                        (unsigned int)instr->addr, (unsigned int)instr->len);

        if (instr->addr & (mtd->erasesize - 1)) { //擦除起始地址。确保地址块对齐
                DEBUG(MTD_DEBUG_LEVEL0,
                                __FUNCTION__"(): Unaligned address\n");
                return -EINVAL;
        }

        if (instr->len & (mtd->erasesize - 1)) { //擦除长度。确保长度为块的整数倍
                DEBUG(MTD_DEBUG_LEVEL0,
                                __FUNCTION__"(): Length not block aligned\n");
                return -EINVAL;
        }

        if ((instr->len + instr->addr) > mtd->size) { //擦除不跃界
                DEBUG(MTD_DEBUG_LEVEL0,
                                __FUNCTION__"(): Erase past end of device\n");
                return -EINVAL;
        }

        page = (int)(instr->addr >> this->page_shift);
        pages_per_block = mtd->erasesize / mtd->oobblock; //每块中的页数
        nand_select();
        nand_command(mtd, NAND_CMD_STATUS, -1, -1);
        this->wait_for_ready();
        if (!(this->read_data() & SMC_STAT_NOT_WP)) {
                DEBUG(MTD_DEBUG_LEVEL0,
                                __FUNCTION__"(): Device is write protected!!!\n");
                nand_deselect();
                return -EIO;
        }

        len = instr->len;
        while (len) {
                nand_command(mtd, NAND_CMD_ERASE1, -1, page); //write 0x60 -> write block address

                nand_command(mtd, NAND_CMD_ERASE2, -1, -1); //write 0xd0

                this->wait_for_ready(); // wait R/B = 1


                status = 0;
                for (i = 0; i < 32; i++) {
                        udelay(125);
                        nand_command(mtd, NAND_CMD_STATUS, -1, -1);
                        this->wait_for_ready();
                        status = (int)this->read_data();
                        if (status & SMC_STAT_READY) //wait I/O6 = 1

                                break;
                }

                if (status & SMC_STAT_WRITE_ERR) { //if I/O0 = 1 ,erase error

                        DEBUG(MTD_DEBUG_LEVEL0,
                                        __FUNCTION__"(): Failed erase, page 0x%08x\n", page);
                        nand_deselect();
                        return -EIO;
                }

                len -= mtd->erasesize;
                page += pages_per_block; //block erase completed -> next block

        }
        nand_deselect();

        return 0;
}


阅读(1855) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~