360 lines
11 KiB
C
360 lines
11 KiB
C
|
/*
|
||
|
* RT-Thread Device Interface for uffs
|
||
|
*/
|
||
|
|
||
|
#include <rtthread.h>
|
||
|
#include <rtdevice.h>
|
||
|
#include "dfs_uffs.h"
|
||
|
|
||
|
static int nand_init_flash(uffs_Device *dev)
|
||
|
{
|
||
|
return UFFS_FLASH_NO_ERR;
|
||
|
}
|
||
|
|
||
|
static int nand_release_flash(uffs_Device *dev)
|
||
|
{
|
||
|
return UFFS_FLASH_NO_ERR;
|
||
|
}
|
||
|
static int nand_erase_block(uffs_Device *dev, unsigned block)
|
||
|
{
|
||
|
int res;
|
||
|
|
||
|
res = rt_mtd_nand_erase_block(RT_MTD_NAND_DEVICE(dev->_private), block);
|
||
|
|
||
|
return res == RT_EOK ? UFFS_FLASH_NO_ERR : UFFS_FLASH_IO_ERR;
|
||
|
}
|
||
|
|
||
|
#if defined(RT_UFFS_USE_CHECK_MARK_FUNCITON)
|
||
|
static int nand_check_block(uffs_Device *dev, unsigned block)
|
||
|
{
|
||
|
int res;
|
||
|
|
||
|
res = rt_mtd_nand_check_block(RT_MTD_NAND_DEVICE(dev->_private), block);
|
||
|
|
||
|
return res == RT_EOK ? UFFS_FLASH_NO_ERR : UFFS_FLASH_BAD_BLK;
|
||
|
}
|
||
|
|
||
|
static int nand_mark_badblock(uffs_Device *dev, unsigned block)
|
||
|
{
|
||
|
int res;
|
||
|
|
||
|
res = rt_mtd_nand_mark_badblock(RT_MTD_NAND_DEVICE(dev->_private), block);
|
||
|
|
||
|
return res == RT_EOK ? UFFS_FLASH_NO_ERR : UFFS_FLASH_IO_ERR;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#if (RT_CONFIG_UFFS_ECC_MODE == UFFS_ECC_NONE) || (RT_CONFIG_UFFS_ECC_MODE == UFFS_ECC_SOFT)
|
||
|
static int nand_read_page(uffs_Device *dev,
|
||
|
u32 block,
|
||
|
u32 page,
|
||
|
u8 *data,
|
||
|
int data_len,
|
||
|
u8 *ecc,
|
||
|
rt_uint8_t *spare,
|
||
|
int spare_len)
|
||
|
{
|
||
|
int res;
|
||
|
|
||
|
page = block * dev->attr->pages_per_block + page;
|
||
|
if (data == NULL && spare == NULL)
|
||
|
{
|
||
|
#if defined(RT_UFFS_USE_CHECK_MARK_FUNCITON)
|
||
|
RT_ASSERT(0); //should not be here
|
||
|
#else
|
||
|
/* check block status: bad or good */
|
||
|
rt_uint8_t spare[UFFS_MAX_SPARE_SIZE];
|
||
|
|
||
|
rt_memset(spare, 0, UFFS_MAX_SPARE_SIZE);
|
||
|
|
||
|
rt_mtd_nand_read(RT_MTD_NAND_DEVICE(dev->_private),
|
||
|
page, RT_NULL, 0,
|
||
|
spare, dev->attr->spare_size);//dev->mem.spare_data_size
|
||
|
|
||
|
res = spare[dev->attr->block_status_offs] == 0xFF ?
|
||
|
UFFS_FLASH_NO_ERR : UFFS_FLASH_BAD_BLK;
|
||
|
|
||
|
return res;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
rt_mtd_nand_read(RT_MTD_NAND_DEVICE(dev->_private),
|
||
|
page, data, data_len, spare, spare_len);
|
||
|
|
||
|
return UFFS_FLASH_NO_ERR;
|
||
|
}
|
||
|
|
||
|
static int nand_write_page(uffs_Device *dev,
|
||
|
u32 block,
|
||
|
u32 page,
|
||
|
const u8 *data,
|
||
|
int data_len,
|
||
|
const u8 *spare,
|
||
|
int spare_len)
|
||
|
{
|
||
|
int res;
|
||
|
|
||
|
RT_ASSERT(UFFS_MAX_SPARE_SIZE >= dev->attr->spare_size);
|
||
|
|
||
|
page = block * dev->attr->pages_per_block + page;
|
||
|
|
||
|
if (data == NULL && spare == NULL)
|
||
|
{
|
||
|
#if defined(RT_UFFS_USE_CHECK_MARK_FUNCITON)
|
||
|
RT_ASSERT(0); //should not be here
|
||
|
#else
|
||
|
/* mark bad block */
|
||
|
rt_uint8_t spare[UFFS_MAX_SPARE_SIZE];
|
||
|
|
||
|
rt_memset(spare, 0xFF, UFFS_MAX_SPARE_SIZE);
|
||
|
spare[dev->attr->block_status_offs] = 0x00;
|
||
|
|
||
|
res = rt_mtd_nand_write(RT_MTD_NAND_DEVICE(dev->_private),
|
||
|
page, RT_NULL, 0,
|
||
|
spare, dev->attr->spare_size);//dev->mem.spare_data_size
|
||
|
if (res != RT_EOK)
|
||
|
goto __error;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
res = rt_mtd_nand_write(RT_MTD_NAND_DEVICE(dev->_private),
|
||
|
page, data, data_len, spare, spare_len);
|
||
|
if (res != RT_EOK)
|
||
|
goto __error;
|
||
|
|
||
|
return UFFS_FLASH_NO_ERR;
|
||
|
|
||
|
__error:
|
||
|
return UFFS_FLASH_IO_ERR;
|
||
|
}
|
||
|
|
||
|
const uffs_FlashOps nand_ops =
|
||
|
{
|
||
|
nand_init_flash, /* InitFlash() */
|
||
|
nand_release_flash, /* ReleaseFlash() */
|
||
|
nand_read_page, /* ReadPage() */
|
||
|
NULL, /* ReadPageWithLayout */
|
||
|
nand_write_page, /* WritePage() */
|
||
|
NULL, /* WritePageWithLayout */
|
||
|
#if defined(RT_UFFS_USE_CHECK_MARK_FUNCITON)
|
||
|
nand_check_block,
|
||
|
nand_mark_badblock,
|
||
|
#else
|
||
|
NULL, /* IsBadBlock(), let UFFS take care of it. */
|
||
|
NULL, /* MarkBadBlock(), let UFFS take care of it. */
|
||
|
#endif
|
||
|
nand_erase_block, /* EraseBlock() */
|
||
|
};
|
||
|
|
||
|
void uffs_setup_storage(struct uffs_StorageAttrSt *attr,
|
||
|
struct rt_mtd_nand_device *nand)
|
||
|
{
|
||
|
rt_memset(attr, 0, sizeof(struct uffs_StorageAttrSt));
|
||
|
|
||
|
// attr->total_blocks = nand->end_block - nand->start_block + 1;/* no use */
|
||
|
attr->page_data_size = nand->page_size; /* page data size */
|
||
|
attr->pages_per_block = nand->pages_per_block; /* pages per block */
|
||
|
attr->spare_size = nand->oob_size; /* page spare size */
|
||
|
attr->ecc_opt = RT_CONFIG_UFFS_ECC_MODE; /* ecc option */
|
||
|
attr->ecc_size = 0; /* ecc size is 0 , the uffs will calculate the ecc size*/
|
||
|
attr->block_status_offs = attr->ecc_size; /* indicate block bad or good, offset in spare */
|
||
|
attr->layout_opt = RT_CONFIG_UFFS_LAYOUT; /* let UFFS do the spare layout */
|
||
|
}
|
||
|
|
||
|
#elif RT_CONFIG_UFFS_ECC_MODE == UFFS_ECC_HW_AUTO
|
||
|
static int WritePageWithLayout(uffs_Device *dev,
|
||
|
u32 block,
|
||
|
u32 page,
|
||
|
const u8 *data,
|
||
|
int data_len,
|
||
|
const u8 *ecc, //NULL
|
||
|
const uffs_TagStore *ts)
|
||
|
{
|
||
|
int res;
|
||
|
int spare_len;
|
||
|
rt_uint8_t spare[UFFS_MAX_SPARE_SIZE];
|
||
|
|
||
|
RT_ASSERT(UFFS_MAX_SPARE_SIZE >= dev->attr->spare_size);
|
||
|
|
||
|
page = block * dev->attr->pages_per_block + page;
|
||
|
spare_len = dev->mem.spare_data_size;
|
||
|
|
||
|
if (data == NULL && ts == NULL)
|
||
|
{
|
||
|
#if defined(RT_UFFS_USE_CHECK_MARK_FUNCITON)
|
||
|
RT_ASSERT(0); //should not be here
|
||
|
#else
|
||
|
/* mark bad block */
|
||
|
rt_memset(spare, 0xFF, UFFS_MAX_SPARE_SIZE);
|
||
|
spare[dev->attr->block_status_offs] = 0x00;
|
||
|
|
||
|
res = rt_mtd_nand_write(RT_MTD_NAND_DEVICE(dev->_private),
|
||
|
page, RT_NULL, 0,
|
||
|
spare, dev->attr->spare_size);//dev->mem.spare_data_size
|
||
|
if (res != RT_EOK)
|
||
|
goto __error;
|
||
|
|
||
|
dev->st.io_write++;
|
||
|
return UFFS_FLASH_NO_ERR;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
if (data != NULL && data_len != 0)
|
||
|
{
|
||
|
RT_ASSERT(data_len == dev->attr->page_data_size);
|
||
|
|
||
|
dev->st.page_write_count++;
|
||
|
dev->st.io_write += data_len;
|
||
|
}
|
||
|
|
||
|
if (ts != RT_NULL)
|
||
|
{
|
||
|
uffs_FlashMakeSpare(dev, ts, RT_NULL, (u8 *)spare);
|
||
|
dev->st.spare_write_count++;
|
||
|
dev->st.io_write += spare_len;
|
||
|
}
|
||
|
|
||
|
res = rt_mtd_nand_write(RT_MTD_NAND_DEVICE(dev->_private),
|
||
|
page, data, data_len, spare, spare_len);
|
||
|
if (res != RT_EOK)
|
||
|
goto __error;
|
||
|
|
||
|
return UFFS_FLASH_NO_ERR;
|
||
|
|
||
|
__error:
|
||
|
return UFFS_FLASH_IO_ERR;
|
||
|
}
|
||
|
|
||
|
static URET ReadPageWithLayout(uffs_Device *dev,
|
||
|
u32 block,
|
||
|
u32 page,
|
||
|
u8 *data,
|
||
|
int data_len,
|
||
|
u8 *ecc, //NULL
|
||
|
uffs_TagStore *ts,
|
||
|
u8 *ecc_store) //NULL
|
||
|
{
|
||
|
int res = UFFS_FLASH_NO_ERR;
|
||
|
int spare_len;
|
||
|
rt_uint8_t spare[UFFS_MAX_SPARE_SIZE];
|
||
|
|
||
|
RT_ASSERT(UFFS_MAX_SPARE_SIZE >= dev->attr->spare_size);
|
||
|
|
||
|
page = block * dev->attr->pages_per_block + page;
|
||
|
spare_len = dev->mem.spare_data_size;
|
||
|
|
||
|
if (data == RT_NULL && ts == RT_NULL)
|
||
|
{
|
||
|
#if defined(RT_UFFS_USE_CHECK_MARK_FUNCITON)
|
||
|
RT_ASSERT(0); //should not be here
|
||
|
#else
|
||
|
/* check block good or bad */
|
||
|
|
||
|
rt_mtd_nand_read(RT_MTD_NAND_DEVICE(dev->_private),
|
||
|
page, RT_NULL, 0,
|
||
|
spare, dev->attr->spare_size);//dev->mem.spare_data_size
|
||
|
|
||
|
dev->st.io_read++;
|
||
|
|
||
|
res = spare[dev->attr->block_status_offs] == 0xFF ?
|
||
|
UFFS_FLASH_NO_ERR : UFFS_FLASH_BAD_BLK;
|
||
|
return res;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
if (data != RT_NULL)
|
||
|
{
|
||
|
dev->st.io_read += data_len;
|
||
|
dev->st.page_read_count++;
|
||
|
}
|
||
|
|
||
|
res = rt_mtd_nand_read(RT_MTD_NAND_DEVICE(dev->_private),
|
||
|
page, data, data_len, spare, spare_len);
|
||
|
if (res == 0)
|
||
|
res = UFFS_FLASH_NO_ERR;
|
||
|
else if (res == -1)
|
||
|
{
|
||
|
//TODO ecc correct, add code to use hardware do ecc correct
|
||
|
res = UFFS_FLASH_ECC_OK;
|
||
|
}
|
||
|
else
|
||
|
res = UFFS_FLASH_ECC_FAIL;
|
||
|
|
||
|
if (ts != RT_NULL)
|
||
|
{
|
||
|
// unload ts and ecc from spare, you can modify it if you like
|
||
|
uffs_FlashUnloadSpare(dev, (const u8 *)spare, ts, RT_NULL);
|
||
|
|
||
|
if ((spare[spare_len - 1] == 0xFF) && (res == UFFS_FLASH_NO_ERR))
|
||
|
res = UFFS_FLASH_NOT_SEALED;
|
||
|
|
||
|
dev->st.io_read += spare_len;
|
||
|
dev->st.spare_read_count++;
|
||
|
}
|
||
|
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
const uffs_FlashOps nand_ops =
|
||
|
{
|
||
|
nand_init_flash, /* InitFlash() */
|
||
|
nand_release_flash, /* ReleaseFlash() */
|
||
|
NULL, /* ReadPage() */
|
||
|
ReadPageWithLayout, /* ReadPageWithLayout */
|
||
|
NULL, /* WritePage() */
|
||
|
WritePageWithLayout,/* WritePageWithLayout */
|
||
|
|
||
|
#if defined(RT_UFFS_USE_CHECK_MARK_FUNCITON)
|
||
|
nand_check_block,
|
||
|
nand_mark_badblock,
|
||
|
#else
|
||
|
NULL, /* IsBadBlock(), let UFFS take care of it. */
|
||
|
NULL, /* MarkBadBlock(), let UFFS take care of it. */
|
||
|
#endif
|
||
|
nand_erase_block, /* EraseBlock() */
|
||
|
};
|
||
|
|
||
|
static rt_uint8_t hw_flash_data_layout[UFFS_SPARE_LAYOUT_SIZE] =
|
||
|
{
|
||
|
0x05, 0x08, 0xFF, 0x00
|
||
|
};
|
||
|
|
||
|
static rt_uint8_t hw_flash_ecc_layout[UFFS_SPARE_LAYOUT_SIZE] =
|
||
|
{
|
||
|
0x00, 0x04, 0xFF, 0x00
|
||
|
};
|
||
|
|
||
|
void uffs_setup_storage(struct uffs_StorageAttrSt *attr,
|
||
|
struct rt_mtd_nand_device *nand)
|
||
|
{
|
||
|
rt_memset(attr, 0, sizeof(struct uffs_StorageAttrSt));
|
||
|
|
||
|
// attr->total_blocks = nand->end_block - nand->start_block + 1;/* no use */
|
||
|
attr->page_data_size = nand->page_size; /* page data size */
|
||
|
attr->pages_per_block = nand->pages_per_block; /* pages per block */
|
||
|
attr->spare_size = nand->oob_size; /* page spare size */
|
||
|
attr->ecc_opt = RT_CONFIG_UFFS_ECC_MODE; /* ecc option */
|
||
|
attr->ecc_size = nand->oob_size-nand->oob_free; /* ecc size */
|
||
|
attr->block_status_offs = attr->ecc_size; /* indicate block bad or good, offset in spare */
|
||
|
attr->layout_opt = RT_CONFIG_UFFS_LAYOUT; /* let UFFS do the spare layout */
|
||
|
|
||
|
/* calculate the ecc layout array */
|
||
|
hw_flash_data_layout[0] = attr->ecc_size + 1; /* ecc size + 1byte block status */
|
||
|
hw_flash_data_layout[1] = 0x08;
|
||
|
hw_flash_data_layout[2] = 0xFF;
|
||
|
hw_flash_data_layout[3] = 0x00;
|
||
|
|
||
|
hw_flash_ecc_layout[0] = 0;
|
||
|
hw_flash_ecc_layout[1] = attr->ecc_size;
|
||
|
hw_flash_ecc_layout[2] = 0xFF;
|
||
|
hw_flash_ecc_layout[3] = 0x00;
|
||
|
|
||
|
/* initialize _uffs_data_layout and _uffs_ecc_layout */
|
||
|
rt_memcpy(attr->_uffs_data_layout, hw_flash_data_layout, UFFS_SPARE_LAYOUT_SIZE);
|
||
|
rt_memcpy(attr->_uffs_ecc_layout, hw_flash_ecc_layout, UFFS_SPARE_LAYOUT_SIZE);
|
||
|
|
||
|
attr->data_layout = attr->_uffs_data_layout;
|
||
|
attr->ecc_layout = attr->_uffs_ecc_layout;
|
||
|
}
|
||
|
#endif
|