1:版本
使用的版本上v3.1.4中移除了mtd的相关代码,如果需要添加就从v3.1.3中获取。
由于我要移植yaffs所以需要这个框架
2:注册函数
//mtd.c
/*
* Register MTD driver
*
* @parts partion description
* @np number of partitions
* @return number of unregistered partitions
*
*/
int rt_mtd_register(rt_mtd_t *master, const struct mtd_part *parts, int np)
{
int ret;
rt_mtd_t *slave;
master->master = master;
master->parent.type = RT_Device_Class_MTD;
if (np > 0)
{
master->offset = parts->offset;
master->size = parts->size;
ret = rt_device_register((rt_device_t)master, parts->name, 0);
if (ret != 0)
goto _out;
np --;
parts ++;
}
while (np > 0)
{
slave = mtd_part_alloc(master, parts);
if (!slave)
break;
ret = rt_device_register((rt_device_t)slave, parts->name, 0);
if (ret)
break;
parts ++;
np --;
}
_out:
return np;
}
这个函数会注册一个mtd设备
所以要对
rt_mtd_t *master
const struct mtd_part *parts
int np
进行赋值
typedef struct mtd_info
{
struct rt_device parent;
const struct mtd_ops *ops; //集体的操作函数,看下个图
uint16_t oob_size;
uint16_t sector_size; /* Minimal writable flash unit size */
uint32_t block_size:28; /* Erase size for the device */
uint32_t type:4;
size_t size; /* Total size of the MTD */
loff_t offset; /* At which this MTD starts, from the beginning of the MEMORY */
struct mtd_info *master;
void *priv;
}rt_mtd_t;
这个具体的函数指针,要对这样指针进行赋值具体的函数,如果设备注册成功的话。调用设备里的读写函数就是调用这里的指向的函数。
struct mtd_ops
{
int(*erase)(rt_mtd_t *mtd, loff_t addr, size_t len); /* return 0 if success */
int(*read) (rt_mtd_t *mtd, loff_t from, struct mtd_io_desc *ops); /* return 0 if success */
int(*write) (rt_mtd_t *mtd, loff_t to, struct mtd_io_desc *ops); /* return 0 if success */
int(*isbad) (rt_mtd_t *mtd, uint32_t block); /* return 1 if bad, 0 not bad */
int(*markbad) (rt_mtd_t *mtd, uint32_t block); /* return 0 if success */
};
这个结构体:由设备的名字,起始位置,大小。
struct mtd_part
{
const char *name; /* name of the MTD partion */
loff_t offset; /* start addr of partion */
size_t size; /* size of partion */
};
3:赋值
在Rt-thread下自带的文件mtdnand.c中已经对操作函数进行赋值:
//mtdnand.c
static const struct mtd_ops _ops =
{
nand_erase,
nand_read,
nand_write,
nand_block_isbad,
nand_block_markbad,
};
int rt_mtd_nand_init(rt_nand_t *nand, int blk_size, int page_size, int oob_size)
{
uint8_t *buf;
buf = rt_malloc(oob_size * 3);
if (buf == RT_NULL)
return -ENOMEM;
nand->oob_poi = buf;
buf += oob_size;
nand->buffers.ecccalc = buf;
buf += oob_size;
nand->buffers.ecccode = buf;
nand->pagebuf = 0; /* alloc when unaligen access */
nand->pages_pb = blk_size / page_size;
nand->ecc._step = page_size / nand->ecc.stepsize;
nand->page_size = page_size;
nand->oobsize = oob_size;
nand->parent.type = MTD_TYPE_NAND;
nand->parent.ops = &_ops;
nand->parent.sector_size = page_size;
nand->parent.block_size = blk_size;
nand->parent.oob_size = oob_size;
switch (nand->ecc.mode)
{
case NAND_ECCM_NONE:
{
nand->read_page = nand_read_page_raw;
nand->write_page = nand_write_page_raw;
}break;
case NAND_ECCM_HW:
{
nand->read_page = nand_read_page_hwecc;
nand->write_page = nand_write_page_hwecc;
}break;
default:
{
rt_free(buf);
return -1;
}
}
return 0;
}
用其中的擦除为例nand_erase
static int nand_erase(rt_mtd_t *mtd, loff_t addr, size_t size)
{
rt_nand_t *chip;
int status;
int page;
uint32_t blksize;
chip = MTDTONAND(mtd);
blksize = mtd->block_size;
page = addr / chip->page_size;
while (size >= blksize)
{
status = chip->ops->cmdfunc(chip, NAND_CMD_BLK_ERASE, page, 0);
if (status & NAND_STATUS_FAIL)
{
break;
}
size -= blksize;
page += chip->pages_pb;
}
return size;
}
其中由status = chip->ops->cmdfunc(chip, NAND_CMD_BLK_ERASE, page, 0); 表示通过这个语句可以经行具体的命令的操作。这里就操作到的底层的驱动。这个需要实现。这个函数在下面这个结构体中的const struct nand_ops *ops;
typedef struct nand_chip
{
rt_mtd_t parent;
/* driver must init these */
const struct nand_ops *ops;
struct nand_ecc ecc;
const struct mtd_oob_region *freelayout;
/* driver do not touch */
struct nand_buffers buffers;
uint8_t *oob_poi;
uint8_t *pagebuf;
uint32_t size;
uint16_t oobsize;
uint8_t pages_pb;
uint16_t page_size;
int(*read_page)(struct nand_chip *chip, uint8_t *buf, int oob_required, int page);
int(*write_page)(struct nand_chip *chip, const uint8_t *buf, int oob_required, int page);
}rt_nand_t;
struct nand_ops
{
int(*cmdfunc)(rt_nand_t *nand, int cmd, int page, int offset); /* send nand operation cmd, return Status bits(0 success),
if nand is busy please wait in low driver */
int(*read_buf)(rt_nand_t *nand, uint8_t *buf, int len); /* read data from nand chip's page buffer */
int(*write_buf)(rt_nand_t *nand, const uint8_t *buf, int len);/* write data to nand chip's page buffer */
int(*isbad)(rt_nand_t *nand, uint32_t blk); /* if NULL OOB[0] used as bad mark(not 0xff is bad) */
int(*markbad)(rt_nand_t *nand, uint32_t blk); /* if NULL OOB[0] used as bad mark(set to 0x00) */
};
4:底层驱动
所以我们在这里建立新的文件对应自己的nand-flash驱动,对上面的函数和结构体进行赋值
并进行初始化flash控制器的操作
static const struct nand_ops nuc970_nand_ops =
{
nuc977_nand_cmdfunc,
nuc977_nand_read_buf,
nuc977_nand_write_buf,
nuc977_nand_isbad,
nuc977_nand_markbad
};
/* nandflash confg */
int nuc970_fmi_nand_init(void)
{
char *name = "nand1";
rt_uint32_t eBCHAlgo;
rt_nand_t *nand = &nand_chip[0];
nand->ops = &nuc970_nand_ops;
nand->oobsize = PAGE_OOB_SIZE;
/* initial NAND controller */
outpw(REG_CLK_HCLKEN, (inpw(REG_CLK_HCLKEN) | 0x300000));
/* select NAND function pins */
if (inpw(REG_SYS_PWRON) & 0x08000000)
{
/* Set GPI1~15 for NAND */
outpw(REG_SYS_GPI_MFPL, 0x55555550);
outpw(REG_SYS_GPI_MFPH, 0x55555555);
}
else
{
/* Set GPC0~14 for NAND */
outpw(REG_SYS_GPC_MFPL, 0x55555555);
outpw(REG_SYS_GPC_MFPH, 0x05555555);
}
// Enable SM_EN
outpw(REG_FMI_CTL, NAND_EN);
outpw(REG_NANDTMCTL, 0x20305);
// Enable SM_CS0
outpw(REG_NANDCTL, (inpw(REG_NANDCTL)&(~0x06000000))|0x04000000);
outpw(REG_NANDECTL, 0x1); /* un-lock write protect */
// NAND Reset
outpw(REG_NANDCTL, inpw(REG_NANDCTL) | 0x1); // software reset
while (inpw(REG_NANDCTL) & 0x1);
/* Detect NAND chips */
/* first scan to find the device and get the page size */
// if (nand_scan_ident(&(nuc970_nand->mtd), 1, NULL)) {
// rt_kprintf("NAND Flash not found !\n");
// return -1;
// }
//Set PSize bits of SMCSR register to select NAND card page size
switch (PAGE_PAGE_SIZE) {
case 2048:
outpw(REG_NANDCTL, (inpw(REG_NANDCTL)&(~0x30000)) + 0x10000);
eBCHAlgo = 0; /* T4 */
// nuc970_layout_oob_table ( &nuc970_nand_oob, mtd->oobsize, g_i32ParityNum[1][nuc970_nand->eBCHAlgo] );
break;
case 4096:
outpw(REG_NANDCTL, (inpw(REG_NANDCTL)&(~0x30000)) + 0x20000);
eBCHAlgo = 1; /* T8 */
//nuc970_layout_oob_table ( &nuc970_nand_oob, mtd->oobsize, g_i32ParityNum[2][nuc970_nand->eBCHAlgo] );
break;
case 8192:
outpw(REG_NANDCTL, (inpw(REG_NANDCTL)&(~0x30000)) + 0x30000);
eBCHAlgo = 2; /* T12 */
//nuc970_layout_oob_table ( &nuc970_nand_oob, mtd->oobsize, g_i32ParityNum[3][nuc970_nand->eBCHAlgo] );
break;
/* Not support now. */
case 512:
default:
rt_kprintf("NUC970 NAND CONTROLLER IS NOT SUPPORT THE PAGE SIZE. (%d, %d)\n", 2048, 64 );
}
// Redundant area size
outpw(REG_NANDRACTL, PAGE_OOB_SIZE);
// Protect redundant 3 bytes
// because we need to implement write_oob function to partial data to oob available area.
// Please note we skip 4 bytes
outpw(REG_NANDCTL, inpw(REG_NANDCTL) | 0x100);
// To read/write the ECC parity codes automatically from/to NAND Flash after data area field written.
outpw(REG_NANDCTL, inpw(REG_NANDCTL) | 0x10);
// Set BCH algorithm
outpw(REG_NANDCTL, (inpw(REG_NANDCTL) & (~0x007C0000)) | g_i32BCHAlgoIdx[eBCHAlgo]);
// Enable H/W ECC, ECC parity check enable bit during read page
outpw(REG_NANDCTL, inpw(REG_NANDCTL) | 0x00800080);
rt_mtd_nand_init(nand, PAGE_BLOCK_SIZE, PAGE_PAGE_SIZE, PAGE_OOB_SIZE);
rt_mtd_register(&(nand->parent), &parts, 1);
return 0;
}
void nuc970_nand_mtd_init(void)
{
nuc970_fmi_nand_init();
nuc970_nand_read_id();
}
INIT_DEVICE_EXPORT(nuc970_nand_mtd_init);
5:总结
mtd.c提供了注册函数。mtdnand.c 实现内部的操作函数,但是到底层的具体操作没有实现。再加上自己的驱动文件,来实现底层操作来供mtdnand.c 来使用
这样就能注册MTD的设备