1.硬件芯片驱动
在底层驱动函数中,需要针对具体的芯片特性,编写6个基础函数提供给上层UFFS系统调用。
ReadID() 读芯片ID
EraseBlock() 块擦除
ReadPage() 读一页数据区
ReadPageSpare() 读一页备份区
WritePage() 写一页数据区
WritePageSpare() 写一页备份区
并且针对不同的芯片归纳出特定某一款nand芯片的参数,
typedef struct SNandInitInfo
{
rt_uint16_t uNandID ; /* Nand Chip ID */
rt_uint16_t uNandNbBlocks ;块数量
rt_uint32_t uNandBlockSize ;块大小
rt_uint16_t uNandSectorSize; ;簇大小(页大小)
rt_uint8_t uNandSpareSize ;备用区大小
rt_uint8_t uNandBusWidth ;总线宽度
rt_uint8_t uNandBadOffset ;坏块偏移
char name[16] ; /* Nand Name */
} SNandInitInfo, *PSNandInitInfo;
在nand配置文件中,可以定义一个结构,该结构内容包含多中NandFlash的信息,当我们移植了其他型号的NandFlash时,可以在NandFlash_InitInfo中找到对应的信息,这样代码基本不用修改,移植性很强: nand.c中针对常用nand芯片的信息有定义:
const struct SNandInitInfo NandFlash_InitInfo[] = {
/*
{0xecda, 0x800, 0x20000, 0x800, 0x40, 0x0,0x0, "K9F2G08U0M\0"},
{0xecaa, 0x800, 0x20000, 0x800, 0x40, 0x0,0x0, "K9F2G08R0A\0"},
*/
{0xec75, 0x800, 0x4000, 0x200, 0x10, 0x0,0x04, "k9f5608\0"}, /*add by misswhile*/
{0xecf1, 0x400, 0x20000, 0x800, 0x40, 0x0,0x0, "K9F1G08U0M\0"}, /*add by misswhile*/
{0xec76, 0x1000, 0x4000, 0x200, 0x10, 0x0,0x04, "K9F12808u0b\0"}, /*add by misswhile*/
{0xeca5, 0x800, 0x4000, 0x200, 0x10, 0x0,0x0, "K9F1G08U0A\0"}, /*add by misswhile*/
/*
{0x20aa, 0x800, 0x20000, 0x800, 0x40, 0x0,0x0, "STMNAND02GR3B\0"},
{0x2caa, 0x800, 0x20000, 0x800, 0x40, 0x0,0x0, "MT29F2G08ABD\0"}, */
{0,}
};
2.更换芯片后,需要修改的配置文件。
在现有集中器项目中, “nandflash-interface.c”这一文件做了底层nand驱动与上层UFFS直接的兼容配置。目的是做到,在驱动层列出的芯片中。无论哪种芯片都可以不修改代码执行使用。
这一层的操作思路:
先通过标准函数ReadID() 读出该块nand的ID号。在通过ID号与NandFlash_InitInfo中进行比对。并查找出,该块nand的基础信息,记录在SNandInitInfo中。
在uffs初始化时将这些基础信息配置给uffs
static void setup_flash_storage(struct uffs_StorageAttrSt *attr ,PSNandInitInfo info)
{
attr->page_data_size = info->uNandSectorSize;//NAND_PAGE_DATA_SIZE; /* page data size */
attr->pages_per_block = info->uNandBlockSize/info->uNandSectorSize;//NAND_PAGES_PER_BLOCK; /* pages per block */
attr->spare_size = info->uNandSpareSize;//NAND_PAGE_SPARE_SIZE; /* page spare size */
attr->block_status_offs = 4;//info->uNandBadOffset;//4; /* block status offset is 5th u8 in spare */
attr->ecc_opt = UFFS_ECC_SOFT; /* ecc option */
//attr->layout_opt = UFFS_LAYOUT_UFFS; /* let UFFS do the spare layout */
//attr->ecc_opt = UFFS_ECC_HW_AUTO;//UFFS_ECC_NONE;//UFFS_ECC_SOFT; /* ecc option */
attr->layout_opt = UFFS_LAYOUT_UFFS;//UFFS_LAYOUT_UFFS; /* let UFFS do the spare layout */
}
并且在这一层,对读写函数也进行一层封装。目的是兼容不同叶大小的读写。
如在具体读写nand时,会根据该nand的叶大小来具体调用相比配的函数。(有写nand的页是2k 有的是512,所以需要兼容处理)
static int nand_read_page(uffs_Device *dev, u32 block, u32 page, u8 *data, int data_len, u8 *ecc,
u8 *spare, int spare_len)
{
u8 val = 0;
int ret = UFFS_FLASH_NO_ERR;
if (data && data_len > 0) {
nand_read_page_data(dev, block, page, data, data_len, ecc);
}
if (spare && spare_len > 0) {
nand_read_page_spare(dev, block, page, spare, 0, spare_len);
}
if (data == NULL && spare == NULL) {
nand_read_page_spare(dev, block, page, &val, dev->attr->block_status_offs, 1);
ret = (val == 0xFF ? UFFS_FLASH_NO_ERR : UFFS_FLASH_BAD_BLK);
}
return ret;
}
该层最终提供给上层UFFS3个函数,其余函数未被使用。
const struct uffs_FlashOpsSt my_nand_driver_ops = {
NULL,
NULL,
nand_read_page, //ReadPageData
NULL, //ReadPageSpareWithLayout
nand_write_page, //WritePageData
NULL,
NULL, //spi_isbad_block, //IsBadBlock
NULL, //MarkBadBlock
nand_erase_block, //EraseBlock
};
其他函数应该为有些芯片的高级功能提供接口,比如,有些ecc的校验是硬件完成的,不需要软件计算。