diff options
-rw-r--r-- | drivers/mtd/nand/nandsim.c | 162 |
1 files changed, 138 insertions, 24 deletions
diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 5dd3c4eb4f01..f00e195f4711 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c | |||
@@ -227,6 +227,14 @@ MODULE_PARM_DESC(dbg, "Output debug information if not zero"); | |||
227 | #define NS_MAX_PREVSTATES 1 | 227 | #define NS_MAX_PREVSTATES 1 |
228 | 228 | ||
229 | /* | 229 | /* |
230 | * A union to represent flash memory contents and flash buffer. | ||
231 | */ | ||
232 | union ns_mem { | ||
233 | u_char *byte; /* for byte access */ | ||
234 | uint16_t *word; /* for 16-bit word access */ | ||
235 | }; | ||
236 | |||
237 | /* | ||
230 | * The structure which describes all the internal simulator data. | 238 | * The structure which describes all the internal simulator data. |
231 | */ | 239 | */ |
232 | struct nandsim { | 240 | struct nandsim { |
@@ -243,17 +251,11 @@ struct nandsim { | |||
243 | uint16_t npstates; /* number of previous states saved */ | 251 | uint16_t npstates; /* number of previous states saved */ |
244 | uint16_t stateidx; /* current state index */ | 252 | uint16_t stateidx; /* current state index */ |
245 | 253 | ||
246 | /* The simulated NAND flash image */ | 254 | /* The simulated NAND flash pages array */ |
247 | union flash_media { | 255 | union ns_mem *pages; |
248 | u_char *byte; | ||
249 | uint16_t *word; | ||
250 | } mem; | ||
251 | 256 | ||
252 | /* Internal buffer of page + OOB size bytes */ | 257 | /* Internal buffer of page + OOB size bytes */ |
253 | union internal_buffer { | 258 | union ns_mem buf; |
254 | u_char *byte; /* for byte access */ | ||
255 | uint16_t *word; /* for 16-bit word access */ | ||
256 | } buf; | ||
257 | 259 | ||
258 | /* NAND flash "geometry" */ | 260 | /* NAND flash "geometry" */ |
259 | struct nandsin_geometry { | 261 | struct nandsin_geometry { |
@@ -342,6 +344,46 @@ static struct mtd_info *nsmtd; | |||
342 | static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE]; | 344 | static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE]; |
343 | 345 | ||
344 | /* | 346 | /* |
347 | * Allocate array of page pointers and initialize the array to NULL | ||
348 | * pointers. | ||
349 | * | ||
350 | * RETURNS: 0 if success, -ENOMEM if memory alloc fails. | ||
351 | */ | ||
352 | static int | ||
353 | alloc_device(struct nandsim *ns) | ||
354 | { | ||
355 | int i; | ||
356 | |||
357 | ns->pages = vmalloc(ns->geom.pgnum * sizeof(union ns_mem)); | ||
358 | if (!ns->pages) { | ||
359 | NS_ERR("alloc_map: unable to allocate page array\n"); | ||
360 | return -ENOMEM; | ||
361 | } | ||
362 | for (i = 0; i < ns->geom.pgnum; i++) { | ||
363 | ns->pages[i].byte = NULL; | ||
364 | } | ||
365 | |||
366 | return 0; | ||
367 | } | ||
368 | |||
369 | /* | ||
370 | * Free any allocated pages, and free the array of page pointers. | ||
371 | */ | ||
372 | static void | ||
373 | free_device(struct nandsim *ns) | ||
374 | { | ||
375 | int i; | ||
376 | |||
377 | if (ns->pages) { | ||
378 | for (i = 0; i < ns->geom.pgnum; i++) { | ||
379 | if (ns->pages[i].byte) | ||
380 | kfree(ns->pages[i].byte); | ||
381 | } | ||
382 | vfree(ns->pages); | ||
383 | } | ||
384 | } | ||
385 | |||
386 | /* | ||
345 | * Initialize the nandsim structure. | 387 | * Initialize the nandsim structure. |
346 | * | 388 | * |
347 | * RETURNS: 0 if success, -ERRNO if failure. | 389 | * RETURNS: 0 if success, -ERRNO if failure. |
@@ -435,14 +477,8 @@ init_nandsim(struct mtd_info *mtd) | |||
435 | printk("sector address bytes: %u\n", ns->geom.secaddrbytes); | 477 | printk("sector address bytes: %u\n", ns->geom.secaddrbytes); |
436 | printk("options: %#x\n", ns->options); | 478 | printk("options: %#x\n", ns->options); |
437 | 479 | ||
438 | /* Map / allocate and initialize the flash image */ | 480 | if (alloc_device(ns) != 0) |
439 | ns->mem.byte = vmalloc(ns->geom.totszoob); | 481 | goto error; |
440 | if (!ns->mem.byte) { | ||
441 | NS_ERR("init_nandsim: unable to allocate %u bytes for flash image\n", | ||
442 | ns->geom.totszoob); | ||
443 | return -ENOMEM; | ||
444 | } | ||
445 | memset(ns->mem.byte, 0xFF, ns->geom.totszoob); | ||
446 | 482 | ||
447 | /* Allocate / initialize the internal buffer */ | 483 | /* Allocate / initialize the internal buffer */ |
448 | ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL); | 484 | ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL); |
@@ -461,7 +497,7 @@ init_nandsim(struct mtd_info *mtd) | |||
461 | return 0; | 497 | return 0; |
462 | 498 | ||
463 | error: | 499 | error: |
464 | vfree(ns->mem.byte); | 500 | free_device(ns); |
465 | 501 | ||
466 | return -ENOMEM; | 502 | return -ENOMEM; |
467 | } | 503 | } |
@@ -473,7 +509,7 @@ static void | |||
473 | free_nandsim(struct nandsim *ns) | 509 | free_nandsim(struct nandsim *ns) |
474 | { | 510 | { |
475 | kfree(ns->buf.byte); | 511 | kfree(ns->buf.byte); |
476 | vfree(ns->mem.byte); | 512 | free_device(ns); |
477 | 513 | ||
478 | return; | 514 | return; |
479 | } | 515 | } |
@@ -769,6 +805,84 @@ find_operation(struct nandsim *ns, uint32_t flag) | |||
769 | } | 805 | } |
770 | 806 | ||
771 | /* | 807 | /* |
808 | * Returns a pointer to the current page. | ||
809 | */ | ||
810 | static inline union ns_mem *NS_GET_PAGE(struct nandsim *ns) | ||
811 | { | ||
812 | return &(ns->pages[ns->regs.row]); | ||
813 | } | ||
814 | |||
815 | /* | ||
816 | * Retuns a pointer to the current byte, within the current page. | ||
817 | */ | ||
818 | static inline u_char *NS_PAGE_BYTE_OFF(struct nandsim *ns) | ||
819 | { | ||
820 | return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off; | ||
821 | } | ||
822 | |||
823 | /* | ||
824 | * Fill the NAND buffer with data read from the specified page. | ||
825 | */ | ||
826 | static void read_page(struct nandsim *ns, int num) | ||
827 | { | ||
828 | union ns_mem *mypage; | ||
829 | |||
830 | mypage = NS_GET_PAGE(ns); | ||
831 | if (mypage->byte == NULL) { | ||
832 | NS_DBG("read_page: page %d not allocated\n", ns->regs.row); | ||
833 | memset(ns->buf.byte, 0xFF, num); | ||
834 | } else { | ||
835 | NS_DBG("read_page: page %d allocated, reading from %d\n", | ||
836 | ns->regs.row, ns->regs.column + ns->regs.off); | ||
837 | memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num); | ||
838 | } | ||
839 | } | ||
840 | |||
841 | /* | ||
842 | * Erase all pages in the specified sector. | ||
843 | */ | ||
844 | static void erase_sector(struct nandsim *ns) | ||
845 | { | ||
846 | union ns_mem *mypage; | ||
847 | int i; | ||
848 | |||
849 | mypage = NS_GET_PAGE(ns); | ||
850 | for (i = 0; i < ns->geom.pgsec; i++) { | ||
851 | if (mypage->byte != NULL) { | ||
852 | NS_DBG("erase_sector: freeing page %d\n", ns->regs.row+i); | ||
853 | kfree(mypage->byte); | ||
854 | mypage->byte = NULL; | ||
855 | } | ||
856 | mypage++; | ||
857 | } | ||
858 | } | ||
859 | |||
860 | /* | ||
861 | * Program the specified page with the contents from the NAND buffer. | ||
862 | */ | ||
863 | static int prog_page(struct nandsim *ns, int num) | ||
864 | { | ||
865 | union ns_mem *mypage; | ||
866 | u_char *pg_off; | ||
867 | |||
868 | mypage = NS_GET_PAGE(ns); | ||
869 | if (mypage->byte == NULL) { | ||
870 | NS_DBG("prog_page: allocating page %d\n", ns->regs.row); | ||
871 | mypage->byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL); | ||
872 | if (mypage->byte == NULL) { | ||
873 | NS_ERR("prog_page: error allocating memory for page %d\n", ns->regs.row); | ||
874 | return -1; | ||
875 | } | ||
876 | memset(mypage->byte, 0xFF, ns->geom.pgszoob); | ||
877 | } | ||
878 | |||
879 | pg_off = NS_PAGE_BYTE_OFF(ns); | ||
880 | memcpy(pg_off, ns->buf.byte, num); | ||
881 | |||
882 | return 0; | ||
883 | } | ||
884 | |||
885 | /* | ||
772 | * If state has any action bit, perform this action. | 886 | * If state has any action bit, perform this action. |
773 | * | 887 | * |
774 | * RETURNS: 0 if success, -1 if error. | 888 | * RETURNS: 0 if success, -1 if error. |
@@ -776,7 +890,7 @@ find_operation(struct nandsim *ns, uint32_t flag) | |||
776 | static int | 890 | static int |
777 | do_state_action(struct nandsim *ns, uint32_t action) | 891 | do_state_action(struct nandsim *ns, uint32_t action) |
778 | { | 892 | { |
779 | int i, num; | 893 | int num; |
780 | int busdiv = ns->busw == 8 ? 1 : 2; | 894 | int busdiv = ns->busw == 8 ? 1 : 2; |
781 | 895 | ||
782 | action &= ACTION_MASK; | 896 | action &= ACTION_MASK; |
@@ -800,7 +914,7 @@ do_state_action(struct nandsim *ns, uint32_t action) | |||
800 | break; | 914 | break; |
801 | } | 915 | } |
802 | num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; | 916 | num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; |
803 | memcpy(ns->buf.byte, ns->mem.byte + NS_RAW_OFFSET(ns) + ns->regs.off, num); | 917 | read_page(ns, num); |
804 | 918 | ||
805 | NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n", | 919 | NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n", |
806 | num, NS_RAW_OFFSET(ns) + ns->regs.off); | 920 | num, NS_RAW_OFFSET(ns) + ns->regs.off); |
@@ -841,7 +955,7 @@ do_state_action(struct nandsim *ns, uint32_t action) | |||
841 | ns->regs.row, NS_RAW_OFFSET(ns)); | 955 | ns->regs.row, NS_RAW_OFFSET(ns)); |
842 | NS_LOG("erase sector %d\n", ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift)); | 956 | NS_LOG("erase sector %d\n", ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift)); |
843 | 957 | ||
844 | memset(ns->mem.byte + NS_RAW_OFFSET(ns), 0xFF, ns->geom.secszoob); | 958 | erase_sector(ns); |
845 | 959 | ||
846 | NS_MDELAY(erase_delay); | 960 | NS_MDELAY(erase_delay); |
847 | 961 | ||
@@ -864,8 +978,8 @@ do_state_action(struct nandsim *ns, uint32_t action) | |||
864 | return -1; | 978 | return -1; |
865 | } | 979 | } |
866 | 980 | ||
867 | for (i = 0; i < num; i++) | 981 | if (prog_page(ns, num) == -1) |
868 | ns->mem.byte[NS_RAW_OFFSET(ns) + ns->regs.off + i] &= ns->buf.byte[i]; | 982 | return -1; |
869 | 983 | ||
870 | NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n", | 984 | NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n", |
871 | num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off); | 985 | num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off); |