[MTD] NAND: nandsim page-wise allocation (2/2)
For page wise allocation, an array of flash page pointers is allocated during initialization. The flash pages are themselves allocated when a write occurs to the page. The flash pages are deallocated when they are erased. Signed-off-by: Vijay Kumar <vijaykumar@bravegnu.org> Signed-off-by: David Woodhouse <dwmw2@infradead.org>
This commit is contained in:
committed by
David Woodhouse
parent
47e3774338
commit
d086d43640
@@ -226,6 +226,14 @@ MODULE_PARM_DESC(dbg, "Output debug information if not zero");
|
|||||||
*/
|
*/
|
||||||
#define NS_MAX_PREVSTATES 1
|
#define NS_MAX_PREVSTATES 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A union to represent flash memory contents and flash buffer.
|
||||||
|
*/
|
||||||
|
union ns_mem {
|
||||||
|
u_char *byte; /* for byte access */
|
||||||
|
uint16_t *word; /* for 16-bit word access */
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The structure which describes all the internal simulator data.
|
* The structure which describes all the internal simulator data.
|
||||||
*/
|
*/
|
||||||
@@ -243,17 +251,11 @@ struct nandsim {
|
|||||||
uint16_t npstates; /* number of previous states saved */
|
uint16_t npstates; /* number of previous states saved */
|
||||||
uint16_t stateidx; /* current state index */
|
uint16_t stateidx; /* current state index */
|
||||||
|
|
||||||
/* The simulated NAND flash image */
|
/* The simulated NAND flash pages array */
|
||||||
union flash_media {
|
union ns_mem *pages;
|
||||||
u_char *byte;
|
|
||||||
uint16_t *word;
|
|
||||||
} mem;
|
|
||||||
|
|
||||||
/* Internal buffer of page + OOB size bytes */
|
/* Internal buffer of page + OOB size bytes */
|
||||||
union internal_buffer {
|
union ns_mem buf;
|
||||||
u_char *byte; /* for byte access */
|
|
||||||
uint16_t *word; /* for 16-bit word access */
|
|
||||||
} buf;
|
|
||||||
|
|
||||||
/* NAND flash "geometry" */
|
/* NAND flash "geometry" */
|
||||||
struct nandsin_geometry {
|
struct nandsin_geometry {
|
||||||
@@ -341,6 +343,46 @@ static struct mtd_info *nsmtd;
|
|||||||
|
|
||||||
static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE];
|
static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate array of page pointers and initialize the array to NULL
|
||||||
|
* pointers.
|
||||||
|
*
|
||||||
|
* RETURNS: 0 if success, -ENOMEM if memory alloc fails.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
alloc_device(struct nandsim *ns)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
ns->pages = vmalloc(ns->geom.pgnum * sizeof(union ns_mem));
|
||||||
|
if (!ns->pages) {
|
||||||
|
NS_ERR("alloc_map: unable to allocate page array\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
for (i = 0; i < ns->geom.pgnum; i++) {
|
||||||
|
ns->pages[i].byte = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free any allocated pages, and free the array of page pointers.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
free_device(struct nandsim *ns)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (ns->pages) {
|
||||||
|
for (i = 0; i < ns->geom.pgnum; i++) {
|
||||||
|
if (ns->pages[i].byte)
|
||||||
|
kfree(ns->pages[i].byte);
|
||||||
|
}
|
||||||
|
vfree(ns->pages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the nandsim structure.
|
* Initialize the nandsim structure.
|
||||||
*
|
*
|
||||||
@@ -435,14 +477,8 @@ init_nandsim(struct mtd_info *mtd)
|
|||||||
printk("sector address bytes: %u\n", ns->geom.secaddrbytes);
|
printk("sector address bytes: %u\n", ns->geom.secaddrbytes);
|
||||||
printk("options: %#x\n", ns->options);
|
printk("options: %#x\n", ns->options);
|
||||||
|
|
||||||
/* Map / allocate and initialize the flash image */
|
if (alloc_device(ns) != 0)
|
||||||
ns->mem.byte = vmalloc(ns->geom.totszoob);
|
goto error;
|
||||||
if (!ns->mem.byte) {
|
|
||||||
NS_ERR("init_nandsim: unable to allocate %u bytes for flash image\n",
|
|
||||||
ns->geom.totszoob);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
memset(ns->mem.byte, 0xFF, ns->geom.totszoob);
|
|
||||||
|
|
||||||
/* Allocate / initialize the internal buffer */
|
/* Allocate / initialize the internal buffer */
|
||||||
ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
|
ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
|
||||||
@@ -461,7 +497,7 @@ init_nandsim(struct mtd_info *mtd)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
vfree(ns->mem.byte);
|
free_device(ns);
|
||||||
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
@@ -473,7 +509,7 @@ static void
|
|||||||
free_nandsim(struct nandsim *ns)
|
free_nandsim(struct nandsim *ns)
|
||||||
{
|
{
|
||||||
kfree(ns->buf.byte);
|
kfree(ns->buf.byte);
|
||||||
vfree(ns->mem.byte);
|
free_device(ns);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -768,6 +804,84 @@ find_operation(struct nandsim *ns, uint32_t flag)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns a pointer to the current page.
|
||||||
|
*/
|
||||||
|
static inline union ns_mem *NS_GET_PAGE(struct nandsim *ns)
|
||||||
|
{
|
||||||
|
return &(ns->pages[ns->regs.row]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Retuns a pointer to the current byte, within the current page.
|
||||||
|
*/
|
||||||
|
static inline u_char *NS_PAGE_BYTE_OFF(struct nandsim *ns)
|
||||||
|
{
|
||||||
|
return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fill the NAND buffer with data read from the specified page.
|
||||||
|
*/
|
||||||
|
static void read_page(struct nandsim *ns, int num)
|
||||||
|
{
|
||||||
|
union ns_mem *mypage;
|
||||||
|
|
||||||
|
mypage = NS_GET_PAGE(ns);
|
||||||
|
if (mypage->byte == NULL) {
|
||||||
|
NS_DBG("read_page: page %d not allocated\n", ns->regs.row);
|
||||||
|
memset(ns->buf.byte, 0xFF, num);
|
||||||
|
} else {
|
||||||
|
NS_DBG("read_page: page %d allocated, reading from %d\n",
|
||||||
|
ns->regs.row, ns->regs.column + ns->regs.off);
|
||||||
|
memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Erase all pages in the specified sector.
|
||||||
|
*/
|
||||||
|
static void erase_sector(struct nandsim *ns)
|
||||||
|
{
|
||||||
|
union ns_mem *mypage;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
mypage = NS_GET_PAGE(ns);
|
||||||
|
for (i = 0; i < ns->geom.pgsec; i++) {
|
||||||
|
if (mypage->byte != NULL) {
|
||||||
|
NS_DBG("erase_sector: freeing page %d\n", ns->regs.row+i);
|
||||||
|
kfree(mypage->byte);
|
||||||
|
mypage->byte = NULL;
|
||||||
|
}
|
||||||
|
mypage++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Program the specified page with the contents from the NAND buffer.
|
||||||
|
*/
|
||||||
|
static int prog_page(struct nandsim *ns, int num)
|
||||||
|
{
|
||||||
|
union ns_mem *mypage;
|
||||||
|
u_char *pg_off;
|
||||||
|
|
||||||
|
mypage = NS_GET_PAGE(ns);
|
||||||
|
if (mypage->byte == NULL) {
|
||||||
|
NS_DBG("prog_page: allocating page %d\n", ns->regs.row);
|
||||||
|
mypage->byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
|
||||||
|
if (mypage->byte == NULL) {
|
||||||
|
NS_ERR("prog_page: error allocating memory for page %d\n", ns->regs.row);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memset(mypage->byte, 0xFF, ns->geom.pgszoob);
|
||||||
|
}
|
||||||
|
|
||||||
|
pg_off = NS_PAGE_BYTE_OFF(ns);
|
||||||
|
memcpy(pg_off, ns->buf.byte, num);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If state has any action bit, perform this action.
|
* If state has any action bit, perform this action.
|
||||||
*
|
*
|
||||||
@@ -776,7 +890,7 @@ find_operation(struct nandsim *ns, uint32_t flag)
|
|||||||
static int
|
static int
|
||||||
do_state_action(struct nandsim *ns, uint32_t action)
|
do_state_action(struct nandsim *ns, uint32_t action)
|
||||||
{
|
{
|
||||||
int i, num;
|
int num;
|
||||||
int busdiv = ns->busw == 8 ? 1 : 2;
|
int busdiv = ns->busw == 8 ? 1 : 2;
|
||||||
|
|
||||||
action &= ACTION_MASK;
|
action &= ACTION_MASK;
|
||||||
@@ -800,7 +914,7 @@ do_state_action(struct nandsim *ns, uint32_t action)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
|
num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
|
||||||
memcpy(ns->buf.byte, ns->mem.byte + NS_RAW_OFFSET(ns) + ns->regs.off, num);
|
read_page(ns, num);
|
||||||
|
|
||||||
NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n",
|
NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n",
|
||||||
num, NS_RAW_OFFSET(ns) + ns->regs.off);
|
num, NS_RAW_OFFSET(ns) + ns->regs.off);
|
||||||
@@ -841,7 +955,7 @@ do_state_action(struct nandsim *ns, uint32_t action)
|
|||||||
ns->regs.row, NS_RAW_OFFSET(ns));
|
ns->regs.row, NS_RAW_OFFSET(ns));
|
||||||
NS_LOG("erase sector %d\n", ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift));
|
NS_LOG("erase sector %d\n", ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift));
|
||||||
|
|
||||||
memset(ns->mem.byte + NS_RAW_OFFSET(ns), 0xFF, ns->geom.secszoob);
|
erase_sector(ns);
|
||||||
|
|
||||||
NS_MDELAY(erase_delay);
|
NS_MDELAY(erase_delay);
|
||||||
|
|
||||||
@@ -864,8 +978,8 @@ do_state_action(struct nandsim *ns, uint32_t action)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < num; i++)
|
if (prog_page(ns, num) == -1)
|
||||||
ns->mem.byte[NS_RAW_OFFSET(ns) + ns->regs.off + i] &= ns->buf.byte[i];
|
return -1;
|
||||||
|
|
||||||
NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n",
|
NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n",
|
||||||
num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off);
|
num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off);
|
||||||
|
Reference in New Issue
Block a user