经过之前对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) |