]> git.kernelconcepts.de Git - karo-tx-redboot.git/blobdiff - packages/devs/flash/arm/mxc/v2_0/src/mxc_nfc.c
unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / packages / devs / flash / arm / mxc / v2_0 / src / mxc_nfc.c
index 1a26e0245cc30a35b1db0b7e1762e65f81d82e40..6f8991a23aa157f530363d4ec59195080abf4ed1 100644 (file)
@@ -1,4 +1,4 @@
-//==-*- c-basic-offset: 4; tab-width: 4; -*-================================
+//==========================================================================
 //
 //             mxc_nfc.c
 //
 //
 // Author(s):   Kevin Zhang <k.zhang@freescale.com>
 // Contributors: Kevin Zhang <k.zhang@freescale.com>
-// Date:                2006-01-23
+// Date:                2006-01-23 Initial version
+// Date:                2007-12-20 Update to support 4K page and bbt management.
 // Purpose:
 // Description:
+//      -- Add bad block management according to Linux NAND MTD implementation.
+//             Reference linux/drivers/mtd/nand/nand_bbt.c by Thomas Gleixner
+//             Summary:
+//                1. Last 4 blocks are reserved for one main BBT and one
+//                       mirror BBT (2 spare ones just in case a block turns bad.)
+//                2. The main BBT block's spare area starts with "Bbt0" followed
+//                       by a version number starting from 1.
+//                3. The mirror BBT block's spare area starts with "1tbB" followed
+//                       by a version number also starting from 1.
+//                4. The actual main area, starting from first page in the BBT block,
+//                       is used to indicate if a block is bad or not through 2bit/block:
+//                             * The table uses 2 bits per block
+//                             * 11b:  block is good
+//                             * 00b:  block is factory marked bad
+//                             * 01b:  block is marked bad due to wear
+//                             * 10b:  block is marked reserved (for BBT)
+//             Redboot operations: During boot, it searches for the marker for
+//                                                     either main BBT or mirror BBT based on the marker:
+//                case 1: Neither table is found:
+//                                Do the bad block scan of the whole flash with ECC off. Use
+//                                manufactor marked BI field to decide if a block is bad and
+//                                then build the BBT in RAM. Then write this table to both
+//                                main BBT block and mirror BBT block.
+//                case 2: Only one table is found:
+//                                Load the BBT from the flash and stored in the RAM.
+//                                Then build the 2nd BBT in the flash.
+//                case 3: If both tables found, load the one with higher version in the
+//                                RAM and then update the block with older BBT info with the
+//                                newer one. If same version, just then read out the table in
+//                                RAM.
 //
 //####DESCRIPTIONEND####
 //
 #include <pkgconf/hal.h>
 #include <cyg/hal/hal_arch.h>
 #include <cyg/hal/hal_cache.h>
+#include <cyg/io/nand_bbt.h>
 #include <redboot.h>
 #include <stdlib.h>
+#if 0
+static int nfc_debug = 1;
+#endif
 
 #include CYGHWR_MEMORY_LAYOUT_H
+
 #include <cyg/hal/hal_io.h>
 #define         _FLASH_PRIVATE_
 #include <cyg/io/flash.h>
 
-#ifdef CYGPKG_HAL_ARM_MXC30031ADS
-#include <cyg/io/mxc_nfc_v2.h>
-#else
-#include <cyg/io/mxc_nfc.h>
-#endif
-
-#ifdef MXCFLASH_FLASH_BASED_BBT
-#include <cyg/io/nand_bbt.h>
 #include CYGHWR_FLASH_NAND_BBT_HEADER
-#endif
 
-#define MXC_UNLOCK_BLK_END             0xFFFF
+#include <cyg/io/imx_nfc.h>
+
+#define ECC_FORCE_ON   1
+#define ECC_FORCE_OFF  2
+
+typedef u64 flash_addr_t;
+
+enum blk_bad_type
+{
+       BLK_GOOD = 0,
+       BLK_BAD_RUNTIME = 1,
+       BLK_RESERVED = 2,
+       BLK_BAD_FACTORY = 3,
+};
 
-#define DBG(n, fmt...) nfc_printf((n) + 1, fmt)
+#define diag_printf1(fmt...) CYG_MACRO_START                                           \
+               if (g_nfc_debug_level >= NFC_DEBUG_MIN) diag_printf(fmt);       \
+CYG_MACRO_END
+
+#define MXC_UNLOCK_BLK_END             0xFFFF
 
-static void print_pkt_16(u16* pkt, u32 len);
-static void print_page (u32 addr, bool spare_only);
-static int nfc_read_page(u32 addr);
-static int nfc_read_page_sp(u32 addr);
-static int nfc_program_page(u32 flash_addr, u32 mem_addr, enum nfc_page_area area);
-static void nfc_flash_reset(void);
-static int mxc_nfc_scan(bool verbose);
-static void read_nflash_id(void* id);
+extern unsigned int hal_timer_count(void);
+int nfc_program_region(flash_addr_t addr, u8 *buf, u32 len);
+int nfc_erase_region(flash_addr_t addr, u32 len, bool skip_bad, bool verbose);
+
+static int nfc_write_pg_random(u32 pg_no, u32 pg_off, u8 *buf, u32 ecc_force);
+static int nfc_read_pg_random(u32 pg_no, u32 pg_off, u32 ecc_force, u32 cs_line,
+                                                         u32 num_of_nand_chips);
+static int nfc_erase_blk(u32 ra);
+static void print_page(u32 addr, bool spare_only);
+static int nfc_read_page(u32 cs_line, u32 pg_no, u32 pg_off);
+static int mxc_nfc_scan(bool lowlevel);
+static void read_nflash_id(u32 *id, u32 cs_line);
+static int nfc_program_blk(u32 ra, u8 *buf, u32 len);
+
+static void print_pkt_16(u16 *pkt, u32 len);
+
+// globals
 static int nand_flash_index = -1;
 static int g_ecc_enable = true;
 static int g_spare_only_read_ok = true;
-static int g_nfc_debug_level = NFC_DEBUG_DEF;
+static int g_nfc_debug_level = NFC_DEBUG_NONE;
 static bool g_nfc_debug_measure = false;
-static bool g_nfc_scan_done = false;
 static bool g_is_2k_page = false;
-static unsigned int g_nfc_version = MXC_NFC_V1;
-static unsigned int is_bad_blk = false;
-
-/*
-//#define NFC_2K_BI_SWAP
- *
- * The i.MX NAND flash controller overlays the 2KiB+64B page FLASH
- * with its internal 512B+16B buffer structure. Thus the indicator bytes
- * for factory bad blocks that are located at column address 2048
- * in the flash end up in the fourth main area buffer at offset 464.
- * This switch enables a routine that swaps the BI byte from the main
- * buffer to the spare buffer so it won't get cleared when the block is
- * programmed.
- * Since the factory bad block indicators are only meaningful for virgin
- * flash chips, the checking for the factory bad block indicators actually
- * needs to be done only once during initial flash programming and bad block
- * table creation.
- * Lateron the factory bad blocks will be mapped out via the bbt.
- *
- * Furthermore, the only thing that the manufacturer guarantees for
- * bad blocks is that the indicator byte in the first or second page
- * of a bad block will contain at least one zero. There is no guarantee
- * that any byte of the bad block will be changeable. Thus, moving the
- * BI to any other byte within the bad block may be impossible.
- *
- * Therefore this switch is NOT defined here!
- */
-
-extern unsigned int hal_timer_count(void);
+static unsigned long g_block_offset;
+static bool g_is_4k_page = false;
+static unsigned int g_nfc_version = MXC_NFC_V1; // default to version 1.0
+static int     num_of_nand_chips = 1;
+static int num_of_nand_chips_for_nandsize = 1;
+static int scale_block_cnt = 1;
+
+#define nfc_printf(level, args...) CYG_MACRO_START     \
+               if (g_nfc_debug_level >= level)                         \
+                       diag_printf(args);                                              \
+CYG_MACRO_END
+
+#if defined(NFC_V2_0) || defined(NFC_V2_1)
+#include <cyg/io/mxc_nfc_v2.h>
+#elif defined(NFC_V3_0)
+#include <cyg/io/mxc_nfc_v3.h>
+#else
+#include <cyg/io/mxc_nfc.h>
+#endif
 
-#define nfc_printf(level, args...)                             \
-       do {                                                                            \
-               if (g_nfc_debug_level >= level)                 \
-                       diag_printf(args);                                      \
-       } while (0)
+#ifndef NAND_LAUNCH_REG
+#define NAND_LAUNCH_REG                                0xDEADEEEE
+#define NAND_CONFIGURATION1_REG                0xDEADEEEE
+#define NFC_FLASH_CONFIG2_REG          0xDEADEEEE
+#define NFC_FLASH_CONFIG2_ECC_EN       0xDEADEEEE
+#define write_nfc_ip_reg(a, b)
+#endif
 
 #ifndef MXCFLASH_SELECT_MULTI
 void flash_query(void *data)
@@ -133,32 +177,33 @@ void flash_query(void *data)
 void nandflash_query(void *data)
 #endif
 {
-       read_nflash_id(data);
-       nfc_printf(NFC_DEBUG_MAX, "%s(ID=0x%x: 0x%x, 0x%x, 0x%x)\n",
-                          __FUNCTION__, *(u8*)(data), *(u8*)((u32)data + 1),
-                          *(u8*)((u32)data + 2), *(u8*)((u32)data + 3));
+       u32 id[2];
+       read_nflash_id(&id[0], 0);
+       nfc_printf(NFC_DEBUG_MAX, "%s(ID=0x%02x: 0x%02x, 0x%02x, 0x%02x)\n", __FUNCTION__,
+                          id[0] & 0xff, (id[0] >> 8) & 0xff, (id[0] >> 16) & 0xff, id[0] >> 24);
+       memcpy(data, id, sizeof(id));
 }
 
 #ifndef MXCFLASH_SELECT_MULTI
-int flash_program_buf(void* addr, void* data, int len)
+int flash_program_buf(void *addr, void *data, int len)
 #else
-int nandflash_program_buf(void* addr, void* data, int len)
+int nandflash_program_buf(void *addr, void *data, int len)
 #endif
 {
-       nfc_printf(NFC_DEBUG_MAX, "%s(addr=%p, data=%p, len=0x%x)\n",
+       nfc_printf(NFC_DEBUG_MAX, "%s(addr=%p, data=%p, len=0x%08x)\n",
                           __FUNCTION__, addr, data, len);
-       return nfc_program_region((u32)addr, (u32)data, (u32)len);
+       return nfc_program_region((u32)addr, data, len);
 }
 
 #ifndef MXCFLASH_SELECT_MULTI
-int flash_erase_block(voidblock, unsigned int size)
+int flash_erase_block(void *block, unsigned int size)
 #else
-int nandflash_erase_block(voidblock, unsigned int size)
+int nandflash_erase_block(void *block, unsigned int size)
 #endif
 {
-       nfc_printf(NFC_DEBUG_MAX, "%s(block=%p, size=0x%x)\n",
+       nfc_printf(NFC_DEBUG_MAX, "%s(block=%p, size=0x%08x)\n",
                           __FUNCTION__, block, size);
-       return nfc_erase_region((u32)block, size);
+       return nfc_erase_region((u32)block, size, 1, 0);
 }
 
 #ifndef MXCFLASH_SELECT_MULTI
@@ -185,9 +230,9 @@ int nandflash_hwr_map_error(int e)
 }
 
 #ifndef MXCFLASH_SELECT_MULTI
-int flash_lock_block(voidblock)
+int flash_lock_block(void *block)
 #else
-int nandflash_lock_block(voidblock)
+int nandflash_lock_block(void *block)
 #endif
 {
        // Not supported yet
@@ -195,9 +240,9 @@ int nandflash_lock_block(void* block)
 }
 
 #ifndef MXCFLASH_SELECT_MULTI
-int flash_unlock_block(voidblock, int block_size, int blocks)
+int flash_unlock_block(void *block, int block_size, int blocks)
 #else
-int nandflash_unlock_block(voidblock, int block_size, int blocks)
+int nandflash_unlock_block(void *block, int block_size, int blocks)
 #endif
 {
        // Not supported yet
@@ -216,55 +261,62 @@ typedef struct flash_dev_info {
        cyg_uint16       device_id3;
        cyg_uint16       device_id4;
        cyg_uint16       page_size;
-       cyg_uint32       spare_size;
+       cyg_uint16       spare_size;
        cyg_uint32       pages_per_block;
        cyg_uint32       block_size;
        cyg_int32        block_count;
-       cyg_uint32       base_mask;
-       cyg_uint32       chipsize;
        cyg_uint32       device_size;
        cyg_uint32       port_size;             // x8 or x16 IO
-       cyg_uint32       type;                  //SLC vs MLC
-       const char       *vendor_info;
+       cyg_uint32       type;                  // SLC vs MLC
+       cyg_uint32       options;
+       cyg_uint32       fis_start_addr;
+       cyg_uint32       bi_off;
+       cyg_uint32       bbt_blk_max_nr;
+       cyg_uint8        vendor_info[96];
+       cyg_uint32       col_cycle;                // number of column address cycles
+       cyg_uint32       row_cycle;                // number of row address cycles
+       cyg_uint32       max_bad_blk;
 } flash_dev_info_t;
 
-static const flash_dev_info_tflash_dev_info;
+static const flash_dev_info_t *flash_dev_info;
 static const flash_dev_info_t supported_devices[] = {
 #include <cyg/io/mxc_nand_parts.inl>
 };
-#define NUM_DEVICES (sizeof(supported_devices)/sizeof(flash_dev_info_t))
+#define NUM_DEVICES NUM_ELEMS(supported_devices)
 
-#define NF_PG_SZ                                       flash_dev_info->page_size
+#define COL_CYCLE                                      flash_dev_info->col_cycle
+#define ROW_CYCLE                                      flash_dev_info->row_cycle
+#define NF_PG_SZ                                       ((flash_dev_info->page_size) * num_of_nand_chips)
+#define NF_SPARE_SZ                                    ((flash_dev_info->spare_size) * num_of_nand_chips)
 #define NF_PG_PER_BLK                          flash_dev_info->pages_per_block
-#define NF_DEV_SZ                                      flash_dev_info->device_size
-#define NF_BLK_SZ                                      flash_dev_info->block_size
-#define NF_BLK_CNT                                     flash_dev_info->block_count
-#define NF_SPARE_SZ                                    flash_dev_info->spare_size
-
-#define NAND_PG_SHIFT                          (g_is_2k_page ? 12 : 9)
-
-// Mask off the higher bits representing linear address of the nand flash
-#define MXC_NAND_LA_MASK                       (NF_DEV_SZ - 1)
-
-#define NFC_DEVICE_ALIGN(a)                    ((a) & MXC_NAND_LA_MASK & (~(NF_DEV_SZ - 1)))
-#define NFC_BLOCK_ALIGN(a)                     ((a) & MXC_NAND_LA_MASK & (~(NF_BLK_SZ - 1)))
-#define NFC_PAGE_ALIGN(a)                      ((a) & MXC_NAND_LA_MASK & (~(NF_PG_SZ - 1)))
-
-#define BLOCK_TO_OFFSET(blk)                   (blk * NF_PG_PER_BLK * NF_PG_SZ)
-#define BLOCK_TO_PAGE(blk)                             (blk * NF_PG_PER_BLK)
-#define BLOCK_PAGE_TO_OFFSET(blk, pge) ((blk * NF_PG_PER_BLK + pge) * NF_PG_SZ)
-#define OFFSET_TO_BLOCK(offset)                        ((offset / NF_PG_SZ) / NF_PG_PER_BLK)
-#define OFFSET_TO_PAGE(offset)                 ((offset / NF_PG_SZ) % NF_PG_PER_BLK)
-
-static u8 *bad_block_table;
-static u32 *l_to_p_table;
+#define NF_DEV_SZ                                      ((flash_dev_info->device_size) * num_of_nand_chips_for_nandsize)
+#define NF_BLK_SZ                                      ((flash_dev_info->block_size) * num_of_nand_chips)
+#define NF_BLK_CNT                                     ((flash_dev_info->block_count) / scale_block_cnt)
+#define NF_VEND_INFO                           flash_dev_info->vendor_info
+#define NF_OPTIONS                                     flash_dev_info->options
+#define NF_BBT_MAX_NR                          flash_dev_info->bbt_blk_max_nr
+#define NF_OPTIONS                                     flash_dev_info->options
+#define NF_BI_OFF                                      flash_dev_info->bi_off
+
+#define MXC_NAND_ADDR_MASK                             (NF_DEV_SZ - 1)
+#define BLOCK_TO_OFFSET(blk)                   ((blk) * NF_PG_PER_BLK * NF_PG_SZ)
+#define BLOCK_TO_PAGE(blk)                             ((blk) * NF_PG_PER_BLK)
+#define BLOCK_PAGE_TO_OFFSET(blk, pge) (((blk) * NF_PG_PER_BLK + (pge)) * NF_PG_SZ)
+#define OFFSET_TO_BLOCK(offset)                        (((offset) / NF_PG_SZ) / NF_PG_PER_BLK)
+#define OFFSET_TO_PAGE(offset)                 (((offset) / NF_PG_SZ) % NF_PG_PER_BLK)
+
+static u8 *g_bbt, *g_page_buf;
+static u32 g_bbt_sz;
 static bool mxcnfc_init_ok = false;
+static bool mxc_nfc_scan_done;
 
-//mxc_nand_fixup_t nand_page_sz_2k_fixup;
-//mxc_nand_fixup_t nand_port_sz_x16_fixup;
-
+// this callback allows the platform specific function to be called right
+// after flash_dev_query()
 nfc_setup_func_t *nfc_setup = NULL;
 
+// this callback allows the platform specific iomux setup
+nfc_iomuxsetup_func_t *nfc_iomux_setup = NULL;
+
 int
 #ifndef MXCFLASH_SELECT_MULTI
 flash_hwr_init(void)
@@ -272,138 +324,123 @@ flash_hwr_init(void)
 nandflash_hwr_init(void)
 #endif
 {
-       cyg_uint16 id[4];
-       int i, bad_block_num;
+       u32 id[2];
+       int i;
+
        nfc_printf(NFC_DEBUG_MAX, "%s()\n", __FUNCTION__);
-#ifdef CYGPKG_HAL_ARM_MXC91131
-extern u32 system_rev;
-       // take care of the NFC spare-only read bug on MXC91131 TO 2.0
-       if (system_rev == CHIP_REV_2_0) {
-               g_spare_only_read_ok = false;
-       }
-#endif
+
+       if (nfc_iomux_setup)
+               nfc_iomux_setup();
+
+       NFC_SET_NFC_ACTIVE_CS(0);
+       NFC_CMD_INPUT(FLASH_Reset);
 
        // Look through table for device data
+       flash_dev_query(&id[0]);
        flash_dev_info = supported_devices;
-       flash_dev_query(id);
        for (i = 0; i < NUM_DEVICES; i++) {
-               if ((flash_dev_info->device_id == id[0]) &&
-                       (flash_dev_info->device_id2 == 0xFFFF || flash_dev_info->device_id2 == id[1]))
+               if ((flash_dev_info->device_id == (id[0] & 0xffff)) &&
+                       (flash_dev_info->device_id2 == 0xFFFF ||
+                        flash_dev_info->device_id2 == (id[0] >> 16)))
                        break;
                flash_dev_info++;
        }
 
-       nfc_printf(NFC_DEBUG_MED, "%s(): %d out of NUM_DEVICES=%d, id=0x%x\n",
-                          __FUNCTION__, i, (u32)NUM_DEVICES, flash_dev_info->device_id);
-
        // Did we find the device? If not, return error.
        if (NUM_DEVICES == i) {
-               diag_printf("Unrecognized NAND part: 0x%04x, 0x%04x, 0x%04x, 0x%04x\n",
-                                       id[0], id[1], id[2], id[3]);
+               diag_printf("Unrecognized NAND part: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
+                                       id[0] & 0xff, (id[0] >> 8) & 0xff, (id[0] >> 16) & 0xff, id[0] >> 24);
                return FLASH_ERR_DRV_WRONG_PART;
        }
 
+       nand_flash_index = i;
+       mxcnfc_init_ok = true;
+
        if (NF_PG_SZ == 2048) {
                g_is_2k_page = true;
                g_spare_only_read_ok = false;
        }
+       if (NF_PG_SZ == 4096) {
+               g_is_4k_page = true;
+               g_spare_only_read_ok = false;
+       }
+
+       nfc_printf(NFC_DEBUG_MED, "%s(): %d out of NUM_DEVICES=%d, id=0x%02x\n",
+                          __FUNCTION__, i, NUM_DEVICES, flash_dev_info->device_id);
+
        if (nfc_setup) {
-               g_nfc_version = nfc_setup(NF_PG_SZ, flash_dev_info->port_size,
-                                                                 flash_dev_info->type);
+               g_nfc_version = nfc_setup(NF_PG_SZ / num_of_nand_chips, flash_dev_info->port_size,
+                                                                 flash_dev_info->type, num_of_nand_chips);
+       }
+       diag_printf1("NFC version: %02x\n", g_nfc_version);
+       if (g_nfc_version == MXC_NFC_V3) {
+               for (i = 2; i <= NUM_OF_CS_LINES; i++) {
+                       u32 id_tmp[2];
+                       read_nflash_id(&id_tmp[0], i - 1);
+                       if (id[0] != id_tmp[0]) {
+                               break;
+                       }
+                       /* Support interleave with 1, 2, 4, 8 chips */
+                       if (i == (num_of_nand_chips * 2)) {
+                               num_of_nand_chips = i;
+                       }
+                       NFC_CMD_INPUT(FLASH_Reset);
+               }
+
+               if (nfc_setup && (num_of_nand_chips > 1)) {
+                       nfc_setup(NF_PG_SZ / num_of_nand_chips, flash_dev_info->port_size,
+                                                  flash_dev_info->type, num_of_nand_chips);
+               }
        }
 
-       nand_flash_index = i;
-       nfc_flash_reset();
-       mxcnfc_init_ok = true;
+       NFC_ARCH_INIT();
 
-       bad_block_table = malloc(NF_BLK_CNT / 4);
-       if (bad_block_table == NULL) {
-               diag_printf("** Error: could not allocate %d byte for bad block table\n",
-                                       NF_BLK_CNT / 4);
+       g_bbt_sz = NF_BLK_CNT / 4;
+       g_bbt = malloc(g_bbt_sz); // two bit for each block
+       if (g_bbt == NULL) {
+               diag_printf("%s(): failed to allocate %d byte for bbt\n", __FUNCTION__, g_bbt_sz);
                return FLASH_ERR_PROTOCOL;
        }
-       l_to_p_table = malloc(NF_BLK_CNT * 4);
-       if (l_to_p_table == NULL) {
-               diag_printf("** Error: could not allocate %d byte for bad block relocation table\n",
-                                       NF_BLK_CNT * 4);
+
+       g_page_buf = malloc(NF_PG_SZ); // for programming less than one page size buffer
+       if (g_page_buf == NULL) {
+               diag_printf("%s(): failed to allocate %d byte page buffer\n", __FUNCTION__,
+                                       NF_PG_SZ);
                return FLASH_ERR_PROTOCOL;
        }
-       memset(bad_block_table, 0, NF_BLK_CNT / 4);
-       memset(l_to_p_table, 0, NF_BLK_CNT * 4);
+       memset(g_bbt, 0, g_bbt_sz);
 
-       nfc_printf(NFC_DEBUG_MAX, "%s(bad_block_table=%p)\n",
-                          __FUNCTION__, bad_block_table);
-       bad_block_num = mxc_nfc_scan(false);
-
-       nfc_printf(NFC_DEBUG_MIN, "\nFound %d bad/reserved blocks\n\n", bad_block_num);
+       /* For now cap off the Device size to 2GB */
+       i = 1;
+       while ((i <= num_of_nand_chips) && ((NF_DEV_SZ * i) < 0x80000000)) {
+               num_of_nand_chips_for_nandsize = i;
+               i *= 2;
+       }
 
+       scale_block_cnt = num_of_nand_chips / num_of_nand_chips_for_nandsize;
        // Hard wired for now
        flash_info.block_size = NF_BLK_SZ;
-       flash_info.blocks = NF_BLK_CNT - bad_block_num;
+       flash_info.blocks = NF_BLK_CNT - CYGNUM_FLASH_NAND_BBT_BLOCKS;
        flash_info.start = (void *)MXC_NAND_BASE_DUMMY;
-       flash_info.end = (void *)(MXC_NAND_BASE_DUMMY + (NF_DEV_SZ) -
-                                                         (bad_block_num * NF_BLK_SZ));
-
-       nfc_printf(NFC_DEBUG_MED, "%s(): block_size=0x%x, blocks=0x%x, start=%p, end=%p\n",
-                          __FUNCTION__, flash_info.block_size, flash_info.blocks,
-                          flash_info.start, flash_info.end);
-
-       return FLASH_ERR_OK;
-}
+       flash_info.end = (void *)(MXC_NAND_BASE_DUMMY + NF_DEV_SZ -
+                                                         CYGNUM_FLASH_NAND_BBT_BLOCKS * NF_BLK_SZ);
 
-/*!
- * Starts the address input cycles for different operations as defined in ops.
- *
- * @param ops                  operations as defined in enum nfc_addr_ops
- * @param addr                 starting address
- * @param mask                 mask for the full address range of the nand flash
- *                                             For 64MB flash, the mask should be 0x03FFFFFF (64MB-1)
- */
-static void start_nfc_addr_ops(enum nfc_addr_ops ops, u32 addr, u32 mask)
-{
-       u32 m = mask, a = addr;
+       mxc_nfc_scan(false); // look for table
 
-       switch (ops) {
-       case ADDRESS_INPUT_READ_ID:
-               NFC_ADDR_INPUT(0);
-               return;
-       case ADDRESS_INPUT_READ_PAGE:
-       case ADDRESS_INPUT_PROGRAM_PAGE:
-               if (g_is_2k_page) {
-                       NFC_ADDR_INPUT(a & 0xFF);
-                       NFC_ADDR_INPUT((a >> 8) & 0xF);
-               } else {
-                       NFC_ADDR_INPUT(a & 0xFF);
-               }
-               // don't break on purpose
-       case ADDRESS_INPUT_ERASE_BLOCK:
-               a >>= NAND_PG_SHIFT;
-               m >>= NAND_PG_SHIFT;
-               break;
-       default:
-               diag_printf("!!!!!! %s(): wrong ops: %d !!!!!\n", __FUNCTION__, ops);
-               return;
-       }
+       diag_printf1("%s(): block_size=0x%08x, blocks=0x%08x, start=%p, end=%p\n",
+                                __FUNCTION__, flash_info.block_size, flash_info.blocks,
+                                flash_info.start, flash_info.end);
 
-       do {
-               NFC_ADDR_INPUT(a);
-               m >>= ADDR_INPUT_SIZE;
-               a >>= ADDR_INPUT_SIZE;
-       } while (m != 0);
+       return FLASH_ERR_OK;
 }
 
-// Doesn't seem to need it as when reaching here means past the query
-// function. So reset isn't necessary.
-static void nfc_flash_reset(void)
+// used by redboot/current/src/flash.c
+int mxc_nand_fis_start(void)
 {
-#ifdef MXC_NFC_RESET
-       nfc_printf(NFC_DEBUG_MAX, "%s()\n", __FUNCTION__);
-       NFC_PRESET(MXC_UNLOCK_BLK_END);
-       NFC_CMD_INPUT(FLASH_Reset);
-#endif
+       return flash_dev_info->fis_start_addr * num_of_nand_chips;
 }
 
-static u8 get_byte(cyg_uint16 *buf, int offs)
+static inline u8 get_byte(cyg_uint16 *buf, int offs)
 {
        cyg_uint16 word = buf[offs >> 1];
        if (offs & 1) {
@@ -412,7 +449,7 @@ static u8 get_byte(cyg_uint16 *buf, int offs)
        return word & 0xff;
 }
 
-static void store_byte(cyg_uint16 *buf, int offs, u8 val)
+static inline void store_byte(cyg_uint16 *buf, int offs, u8 val)
 {
        cyg_uint16 word = buf[offs >> 1];
 
@@ -424,1137 +461,1318 @@ static void store_byte(cyg_uint16 *buf, int offs, u8 val)
        buf[offs >> 1] = word;
 }
 
-static void nfc_buf_mem_cpy(void *dst, void *src, u32 len)
+static inline bool nfc_verify_addr(unsigned long dst, unsigned long len)
+{
+       if (dst < NAND_MAIN_BUF0 || dst + len >= NAND_SPAR_BUF3 + NFC_SPARE_BUF_SZ) {
+               diag_printf("%s: Bad NFC Buffer address 0x%08lx\n", __FUNCTION__, dst);
+               return false;
+       }
+       return true;
+}
+
+static void nfc_buf_read(void *dst, unsigned long src, u32 len)
 {
-       u16 *d = dst, *s = src;
+       u16 *s = (u16 *)(src & ~1);
+       u8 *bp = dst;
 
-       if (((unsigned long)dst & 1) || ((unsigned long)src & 1)) {
-               diag_printf("%s: Source (%p) or destination address (%p) not halfword aligned\n",
-                                       __FUNCTION__, src, dst);
+       if (len == 0) {
                return;
        }
-       if (len == 0) {
+       if (src + len < src) {
+               diag_printf("%s: Bad address range 0x%08lx .. 0x%08lx\n", __FUNCTION__,
+                                       src, src + len);
+       }
+       if ((unsigned long)dst + len < (unsigned long)dst) {
+               diag_printf("%s: Bad address range 0x%08lx .. 0x%08lx\n", __FUNCTION__,
+                                       (unsigned long)dst, (unsigned long)dst + len);
+       }
+       if (src < NAND_MAIN_BUF0 || src + len >= NAND_SPAR_BUF3 + NF_PG_SZ) {
+               diag_printf("%s: Bad NFC Buffer address 0x%08lx\n", __FUNCTION__, src);
                return;
        }
-       do {
-               *d++ = *s++;
-               len -= 2;
-       } while (len > 1);
+       if ((unsigned long)dst >= NAND_MAIN_BUF0 &&
+               (unsigned long)dst < NAND_SPAR_BUF3 + NF_PG_SZ) {
+               diag_printf("%s: Bad memory address 0x%08lx\n", __FUNCTION__,
+                                       (unsigned long)dst);
+               return;
+       }
+       if (src & 1) {
+               *bp++ = get_byte(s, 1);
+               s++;
+               len--;
+       }
+       if ((unsigned long)bp & 1) {
+               while (len > 1) {
+                       u16 word = *s++;
+                       *bp++ = word & 0xff;
+                       *bp++ = word >> 8;
+                       len -= 2;
+               }
+       } else {
+               u16 *wp = (u16 *)bp;
 
+               while (len > 1) {
+                       *wp++ = *s++;
+                       len -= 2;
+               }
+               bp = (u8*)wp;
+       }
        if (len != 0) {
-               u16 tmp = *d;
-               tmp = (tmp & ~0xff) | (*s & 0xff);
+               u16 word = *s;
+               *bp = word & 0xff;
        }
 }
 
-static void read_nflash_id(void *id)
-{
-       volatile u32 *ptr = (u32*)NAND_MAIN_BUF0;
-       volatile u32 *id_32 = (u32*)id;
-
-       nfc_printf(NFC_DEBUG_MAX, "%s()\n", __FUNCTION__);
-//       NFC_PRESET(NF_BLK_CNT -1); -- doesn't work for 2k flash, why?
-       NFC_PRESET(MXC_UNLOCK_BLK_END);
-       NFC_CMD_INPUT(FLASH_Read_ID);
-       start_nfc_addr_ops(ADDRESS_INPUT_READ_ID, 0, 0);
-       NFC_DATA_OUTPUT(RAM_BUF_0, FDO_FLASH_ID, g_ecc_enable);
-
-       *id_32++ = *ptr++;
-       *id_32++ = *ptr++;
-}
-
-static u8 bad_block_code(int block)
+static void nfc_buf_write(unsigned long dst, void *src, u32 len)
 {
-       int offs = block >> 2;
-       int shift = (block & 0x03) << 1;
-       u8 code = (bad_block_table[offs] >> shift) & 0x03;
+       u8 *bp = src;
+       u16 *d = (u16 *)(dst & ~1);
 
-       if (code != 0) {
-               DBG(0, "Block %d is marked %s (%02x) in mem bbt @ %04x\n", block,
-                       (code != 2) ? "bad" : "reserved", code, offs);
+       if (len == 0) {
+               return;
+       }
+       if (!nfc_verify_addr(dst, len)) {
+               return;
+       }
+       if (dst & 1) {
+               store_byte(d, 1, *bp);
+               d++;
+               bp++;
+               len--;
+       }
+       if ((unsigned long)bp & 1) {
+               while (len > 1) {
+                       u16 word;
+                       word = *bp++;
+                       word |= (u16)(*bp++) << 8;
+                       *d++ = word;
+                       len -= 2;
+               }
+       } else {
+               u16 *wp = (u16 *)bp;
+               while (len > 1) {
+                       *d++ = *wp++;
+                       len -= 2;
+               }
+               bp = (u8 *)wp;
+       }
+       if (len != 0) {
+               store_byte(d, 1, *bp);
        }
-       return code;
 }
 
+#ifndef NFC_V3_0
 /*!
- * Checks to see if a block is bad by looking at the 6th byte of the spare area
- * inside a page.
- * @param ra           starting address in the raw address space (offset)
- *                                     (No error checking). It doesn't have to be block-aligned.
- * @return                     true if bad block; false otherwise
+ * Starts the address input cycles for different operations as defined in ops.
+ *
+ * @param ops                  operations as defined in enum nfc_addr_ops
+ * @param pg_no                        page number offset from 0
+ * @param pg_off               byte offset within the page
+ * @param is_erase             don't care for earlier NFC
+ * @param cs_line               don't care for earlier NFC
  */
-static u8 nfc_is_badblock(u32 ra)
+static void start_nfc_addr_ops(u32 ops, u32 pg_no, u32 pg_off, u32 is_erase,
+                                  u32 cs_line, u32 num_of_chips)
 {
-       u32 block = OFFSET_TO_BLOCK(ra), ecc_val = g_ecc_enable;
-       bool res = false;
-       u16 temp, i;
-
-       if (g_nfc_scan_done) {
-               if (block >= NF_BLK_CNT) {
-                       diag_printf("Error %d: Block count out of range: %d\n", __LINE__, block);
-                       return true;
-               }
-               return bad_block_code(block);
-       }
+       int i;
 
-       // turn off ecc when scanning for bad blocks
-//       g_ecc_enable = false; // TODO: we should turn OFF ecc
-       g_ecc_enable = true;
-       // check for the 1st and 2nd pages
-       for (i = 0, ra = NFC_BLOCK_ALIGN(ra); i < 2; i++, ra += NF_PG_SZ) {
-               if (nfc_read_page(ra) != 0) {
-                       diag_printf("Warning: uncorrectable ECC at addr 0x%08x\n", ra);
+       switch (ops) {
+       case FLASH_Read_ID:
+               /* Only supports one NAND chip (CS0) */
+               if (cs_line != 0)
+                       return;
+               NFC_ADDR_INPUT(0);
+               return;
+       case FLASH_Read_Mode1:
+       case FLASH_Program:
+               for (i = 0; i < COL_CYCLE; i++, pg_off >>= 8) {
+                       NFC_ADDR_INPUT(pg_off & 0xFF);
                }
-               if (g_is_2k_page && is_bad_blk) {
-                       DBG(3, "Bad block %d\n", block);
-                       res = true;
-                       break;
-               } else {
-                       temp = readw(NAND_SPAR_BUF0 + 4);
-                       if ((temp >> 8) != 0xFF) {
-                               res = true;
-                               DBG(2, "Block %d is marked bad in OOB area\n", block);
-                               print_pkt_16((u16*)(NAND_SPAR_BUF0), g_is_2k_page ? 64 : 16);
-                               break;
-                       }
+               // don't break on purpose
+       case FLASH_Block_Erase:
+               for (i = 0; i < ROW_CYCLE; i++, pg_no >>= 8) {
+                       NFC_ADDR_INPUT(pg_no & 0xFF);
                }
+               break;
+       default:
+               diag_printf("!!!!!! %s(): wrong ops: %d !!!!!\n", __FUNCTION__, ops);
+               return;
        }
-       g_ecc_enable = ecc_val;
-       return res;
+}
+#endif                                 // #ifndef NFC_V3_0
+
+static void read_nflash_id(u32 *id, u32 cs_line)
+{
+       volatile u32 *ptr = (volatile u32*)NAND_MAIN_BUF0;
+
+    nfc_printf(NFC_DEBUG_MIN, "%s: read flash id from chip %d @ %p\n",
+                          __FUNCTION__, cs_line, ptr);
+
+       NFC_PRESET(MXC_UNLOCK_BLK_END);
+       NFC_SET_NFC_ACTIVE_CS(cs_line);
+       NFC_CMD_INPUT(FLASH_Read_ID);
+
+       start_nfc_addr_ops(FLASH_Read_ID, 0, 0, 0, cs_line, num_of_nand_chips);
+       NFC_DATA_OUTPUT(RAM_BUF_0, FDO_FLASH_ID, g_ecc_enable);
+
+    *id++ = *ptr++;
+    *id++ = *ptr++;
 }
 
-static void nfc_update_blk_table(u32 faddr, u8 is_bad)
+static void mark_blk_bad(unsigned int block, unsigned char *buf,
+                                                enum blk_bad_type bad_type)
 {
-       u32 block = OFFSET_TO_BLOCK(faddr);
-       int offs = block >> 2;
-       int shift = (block & 0x03) << 1;
-       u8 mask = 0x03 << shift;
+       unsigned int off = block >> 2;           // byte offset - each byte can hold status for 4 blocks
+       unsigned int sft = (block & 3) << 1;  // bit shift 0, 2, 4, 6
+       unsigned char val = buf[off];
 
-       if (block >= NF_BLK_CNT) {
-               diag_printf("Block count out of range: %d\n", block);
+       if (block > NF_BLK_CNT) {
+               diag_printf("%s: Block number %u out of range: 0..%u\n", __FUNCTION__,
+                                       block, NF_BLK_CNT - 1);
                return;
        }
-       if (is_bad) {
-               nfc_printf(NFC_DEBUG_MED, "marking block %d %s\n", block,
-                                  is_bad == 2 ? "reserved" : "bad");
-               bad_block_table[offs] = (bad_block_table[offs] & ~mask) | (is_bad << shift);
-       } else {
-               nfc_printf(NFC_DEBUG_MAX, "Block %d is good\n", block);
-               bad_block_table[offs] &= ~mask;
-       }
+       val = (val & ~(3 << sft)) | (bad_type << sft);
+       buf[off] = val;
 }
 
 /*!
- * Erase a block without checking the BI field. If the block is bad, mark it
- * in the global table. Note that there is NO error checking for passed-in ra.
- * @param ra           starting address in the raw address space (offset)
- *                                     Must be block-aligned
- * @return                     0 if successful; -1 otherwise
+ * Checks to see if a block is bad. If buf is not NULL, it indicates a valid
+ * BBT in the RAM. In this case, it assumes to have 2-bit to represent each
+ * block for good or bad
+ *                             * 11b:  block is good
+ *                             * 00b:  block is factory marked bad
+ *                             * 01b:  block is marked bad due to wear
+ *                             * 10b:  block is marked reserved (for BBT)
+ * If buf is NULL, then it indicates a low level scan based on the certain
+ * offset value in certain pages and certain offset to be non-0xFF. In this
+ * case, the HW ECC will be turned off.
+ *
+ * @param block                0-based block number
+ * @param buf          BBT buffer. Could be NULL (see above explanation)
+ *
+ * @return                     1 if bad block; 0 otherwise
  */
-static int nfc_erase_blk(u32 ra)
+static int nfc_is_badblock(u32 block, u8 *buf)
 {
-       u16 flash_status;
-       u32 flash_addr;
-
-       if (ra % NF_BLK_SZ) {
-               diag_printf("** Error: block erase address must be block aligned: 0x%08x\n", ra);
-               return -1;
+       u32 off;           // byte offset
+       u32 sft;           // bit shift 0, 2, 4, 6
+       flash_addr_t addr;
+       u16 temp, i;
+       int res;
+       u32 pg_no;
+
+       if (buf) {
+               // use BBT
+               off = block >> 2;               // byte offset
+               sft = (block & 3) << 1;  // bit shift 0, 2, 4, 6
+               res = (buf[off] >> sft) & 0x3;
+               if (res) {
+                       addr = BLOCK_TO_OFFSET(block);
+                       diag_printf1("Block %u at %08llx is marked %s (%d) in BBT@%p[%02x] mask %02x\n",
+                                                block, (u64)addr, res == BLK_RESERVED ? "reserved" :
+                                                res == BLK_BAD_FACTORY ? "factory bad" : "runtime bad",
+                                                res, buf, off, 3 << sft);
+               }
+               return res;
+       }
+
+       // need to do low level scan with ECC off
+       if (NF_OPTIONS & NAND_BBT_SCANLSTPAGE) {
+               if (g_is_4k_page || g_is_2k_page) {
+                       addr = (block + 1) * NF_BLK_SZ - NF_PG_SZ;
+                       pg_no = addr / NF_PG_SZ;
+                       for (i = 0; i < num_of_nand_chips; i++) {
+                               // we don't do partial page read here. No ecc either
+                               nfc_read_pg_random(pg_no, 0, ECC_FORCE_OFF, i, num_of_nand_chips);
+                               temp = readw((u32)NAND_MAIN_BUF0 + NF_BI_OFF);
+                               if ((temp & 0xFF) != 0xFF) {
+                                       return BLK_BAD_FACTORY;
+                               }
+                       }
+               } else {
+                       diag_printf("only 2K/4K page is supported\n");
+                       // die here -- need to fix the SW
+                       while (1);
+               }
+               return 0;
        }
-       flash_addr = (ra / NF_PG_SZ) << NAND_PG_SHIFT;
-       nfc_printf(NFC_DEBUG_MED, "%s: Erasing block %d @ %08x\n", __FUNCTION__, ra / NF_BLK_SZ, ra);
-
-       NFC_CMD_INPUT(FLASH_Block_Erase);
-
-       start_nfc_addr_ops(ADDRESS_INPUT_ERASE_BLOCK, flash_addr, MXC_NAND_LA_MASK);
-       NFC_CMD_INPUT(FLASH_Start_Erase);
-
-       flash_status = NFC_STATUS_READ();
-
-       // check I/O bit 0 to see if it is 0 for success
-       if ((flash_status & 0x1) != 0) {
-               diag_printf("** Error: failed to erase block %d at %08x; status=0x%x\n",
-                                       OFFSET_TO_BLOCK(ra), ra, flash_status);
-               nfc_update_blk_table(ra, true);
-               return -1;
+       addr = block * NF_BLK_SZ;
+       pg_no = addr / NF_PG_SZ;
+       for (i = 0; i < num_of_nand_chips; i++) {
+               nfc_read_pg_random(pg_no, 0, ECC_FORCE_OFF, i, num_of_nand_chips); // no ecc
+               if (g_is_2k_page || g_is_4k_page) {
+                       temp = readw(NAND_MAIN_BUF0 + NF_BI_OFF);
+               } else {
+                       temp = readw(NAND_SPAR_BUF0 + 4) >> 8; // BI is at 5th byte in spare area
+               }
+               if ((temp & 0xFF) != 0xFF) {
+                       return BLK_BAD_FACTORY;
+               }
+       }
+       if (NF_OPTIONS & NAND_BBT_SCAN2NDPAGE) {
+               addr += NF_PG_SZ;
+               pg_no++;
+               for (i = 0; i < num_of_nand_chips; i++) {
+                       nfc_read_pg_random(pg_no, 0, ECC_FORCE_OFF, i, num_of_nand_chips); // no ecc
+                       if (g_is_2k_page || g_is_4k_page) {
+                               temp = readw(NAND_MAIN_BUF0 + NF_BI_OFF);
+                       } else {
+                               temp = readw(NAND_SPAR_BUF0 + 4) >> 8; // BI is at 5th byte in spare area
+                       }
+                       if ((temp & 0xFF) != 0xFF) {
+                               return BLK_BAD_FACTORY;
+                       }
+               }
        }
        return 0;
 }
 
-/*!
- * Program a block of data in the flash. This function doesn't do
- * bad block checking. But if program fails, it returns an error code.
- * @param ra           destination raw flash address
- * @param maddr                source address in the RAM
- @ @return                     0 if successful; -1 otherwise
- */
-static int nfc_program_blk(u32 ra, u32 maddr)
+/*
+ * check_short_pattern - [GENERIC] check if a pattern is in the buffer
+ * @buf:       the buffer to search
+ * @td:                search pattern descriptor
+ *
+ * Check for a pattern at the given place. Used to search bad block
+ * tables and good / bad block identifiers.
+*/
+static int check_short_pattern(void *buf, struct nand_bbt_descr *td)
 {
-       u32 i;
+       int i;
 
-       for (i = 0; i < NF_PG_PER_BLK; i++) {
-               if (nfc_program_page(ra, maddr, NFC_MAIN_ONLY) != 0) {
-                       nfc_update_blk_table(ra, true);
+       for (i = 0; i < td->len; i++) {
+               if (get_byte(buf, td->offs + i) != td->pattern[i]) {
                        return -1;
                }
-               ra += NF_PG_SZ;
-               maddr += NF_PG_SZ;
        }
        return 0;
 }
 
+static int nfc_write_page(u32 pg_no, u32 pg_off, u32 ecc_force);
 /*
- * Convert a linear address to raw address. No address checking in this function.
- * @param la           linear address used by the upper flash driver
- * @return                     raw address for NAND flash
+ * Program g_bbt into the NAND block with offset at g_main_bbt_addr.
+ * This assumes that the g_bbt has been built already.
+ *
+ * If g_main_bbt_addr is 0, search for a free block from the bottom 4 blocks (but make
+ * sure not re-using the mirror block). If g_mirror_bbt_page is 0, do the same thing.
+ * Otherwise, just use g_main_bbt_addr, g_mirror_bbt_page numbers to prgram the
+ * g_bbt into those two blocks.
+ * todo: need to do the version to see which one is newer.
+ *
+ * @return     0 if successful; -1 otherwise.
  */
-static u32 nfc_l_to_p(u32 la)
+static int mxc_nfc_write_bbt_page(struct nand_bbt_descr *td)
 {
-       u32 block, offset, ra;
+       int ret;
+       u32 block = td->pages / NF_PG_PER_BLK;
+       flash_addr_t addr = td->pages * NF_PG_SZ;
 
-       block = la / NF_BLK_SZ;
-       offset = la % NF_BLK_SZ;
-       ra = (l_to_p_table[block] * NF_BLK_SZ) + offset;
+       ret = nfc_erase_blk(addr);
+       if (ret != 0) {
+               diag_printf("Failed to erase bbt block %u\n", block);
+               return ret;
+       }
+       ret = nfc_write_page(td->pages, 0, 0);
+       if (ret != 0) {
+               diag_printf("Failed to write bbt block %u\n", block);
+               return ret;
+       }
+       mark_blk_bad(block, g_bbt, BLK_RESERVED);
+       return 0;
+}
 
-       nfc_printf(NFC_DEBUG_MAX, "\n%s(): l_to_p_table[0x%08x]=0x%08x, offset=0x%08x\n",
-                          __FUNCTION__, block, l_to_p_table[block], offset);
-       nfc_printf(NFC_DEBUG_MAX, "%s(la=0x%08x, ra=0x%08x)\n",
-                          __FUNCTION__, la, ra);
-       return ra;
+static inline void mxc_nfc_buf_clear(unsigned long buf, u8 pattern, int size)
+{
+       int i;
+       u16 *p = (u16 *)buf;
+       u16 fill = pattern;
 
+       fill = (fill << 8) | pattern;
+       for (i = 0; i < size >> 1; i++) {
+               p[i] = fill;
+       }
 }
-/*!
- * Erase a range of NAND flash
- * @param la                   linear NAND flash address. it has to be block size aligned
- * @param len                  number of bytes
- * @return                             FLASH_ERR_OK (0) if successful; non-zero otherwise
- */
-int nfc_erase_region(u32 la, int len)
+
+static int mxc_nfc_write_bbt(struct nand_bbt_descr *td, struct nand_bbt_descr *md)
 {
-       u32 ra;
+       int ret = -1;
+       int block;
+       int pg_offs = 0;
+       int page = 0;
+       u16 *buf = (u16 *)NAND_MAIN_BUF0;
 
-       nfc_printf(NFC_DEBUG_MED, "%s(la=0x%08x, len=0x%08x)\n", __FUNCTION__, la, len);
+       for (block = NF_BLK_CNT - 1; block >= NF_BLK_CNT - td->maxblocks - 1; block--) {
+               int pg = block * NF_PG_PER_BLK;
 
-       la &= MXC_NAND_LA_MASK;
-       if ((la % NF_BLK_SZ) != 0) {
-               diag_printf("** Error: address %08x not aligned to block boundary\n", la);
-               return FLASH_ERR_INVALID;
+               if ((nfc_is_badblock(block, g_bbt) & 1) == 0) {
+                       if (md != NULL && md->pages == pg) {
+                               continue;
+                       }
+                       td->pages = pg;
+                       break;
+               }
        }
-       if (len <= 0 || la + len >= NF_DEV_SZ) {
-               diag_printf("** Error: invalid length %d\n", len);
-               return FLASH_ERR_INVALID;
+       if (td->pages < 0) {
+               return -1;
        }
+       mxc_nfc_buf_clear(NAND_SPAR_BUF0, 0xff, NF_SPARE_SZ);
+       mxc_nfc_buf_clear(NAND_MAIN_BUF0, 0xff, NF_PG_SZ);
+       diag_printf1("%s: Updating bbt %c%c%c%c version %d\n", __FUNCTION__,
+                                td->pattern[0], td->pattern[1], td->pattern[2], td->pattern[3], td->version);
+       nfc_buf_write(NAND_SPAR_BUF0 + td->offs, td->pattern, td->len);
+       store_byte((u16 *)NAND_SPAR_BUF0, td->veroffs, td->version);
 
-       // now la has to be block aligned
-       do {
-               ra = nfc_l_to_p(la);
-               la += NF_BLK_SZ;
+       for (block = 0, pg_offs = 0; block < NF_BLK_CNT; pg_offs++) {
+               u16 tmp = 0xffff;
+               int i;
 
-               if (ra > (NF_DEV_SZ - NF_BLK_SZ)) {
-                       diag_printf("** Error: la=0x%08x (ra=0x%08x) is out of valid range\n", la, ra);
-                       return FLASH_ERR_ERASE;
+               if (pg_offs << 1 >= NF_PG_SZ) {
+                       ret = mxc_nfc_write_bbt_page(td);
+                       if (ret != 0) {
+                               return ret;
+                       }
+                       page++;
+                       mxc_nfc_buf_clear(NAND_SPAR_BUF0, 0xff, NF_SPARE_SZ);
+                       mxc_nfc_buf_clear(NAND_MAIN_BUF0, 0xff, NF_PG_SZ);
+                       pg_offs = 0;
                }
-               if (nfc_is_badblock(ra)) {
-                       diag_printf("** Error: bad block: %d at address %08x\n",
-                                           OFFSET_TO_BLOCK(ra), ra);
-                       return FLASH_ERR_ERASE;
-               } else {
-                       if (nfc_erase_blk(ra) == 0) {
-                               // erase ok
-                               len -= NF_BLK_SZ;
+               for (i = 0; i < 16 && block < NF_BLK_CNT; i += 2, block++) {
+                       u8 code = nfc_is_badblock(block, g_bbt);
+                       if ((code & 1) != 0) {
+                               tmp &= ~(code << i);
+                               diag_printf1("%s: bad block %u pattern[%p] 0x%04x mask 0x%04x\n", __FUNCTION__,
+                                                        block, &buf[pg_offs], tmp, 0x03 << i);
+                       }
+               }
+               buf[pg_offs] = tmp;
+       }
+       if (pg_offs > 0) {
+               diag_printf1("%s: Writing final bbt block %d page %d\n", __FUNCTION__,
+                                        td->pages / NF_PG_PER_BLK, page);
+               ret = mxc_nfc_write_bbt_page(td);
+       }
+       return ret;
+}
+
+static int mxc_nfc_update_bbt(struct nand_bbt_descr *td, struct nand_bbt_descr *md)
+{
+       int ret;
+
+       if (td == NULL) {
+               return -1;
+       }
+       if (td->pages < 0 && (md == NULL || md->pages == -1)) {
+               td->version = 1;
+       } else {
+               if (md != NULL && md->pages >= 0) {
+                       if (md->version >= td->version) {
+                               td->version = ++md->version;
                        } else {
-                               return FLASH_ERR_ERASE;
+                               md->version = ++td->version;
                        }
+               } else {
+                       td->version++;
                }
-       } while (len > 0);
+       }
+       ret = mxc_nfc_write_bbt(td, md);
+       if (ret) {
+               diag_printf("** Error: Failed to update main BBT\n");
+       }
+       if (md) {
+               ret = mxc_nfc_write_bbt(md, td);
+               if (ret) {
+                       diag_printf("** Error: Failed to update mirror BBT\n");
+               }
+       }
+       return ret;
+}
 
-       return FLASH_ERR_OK;
+static int program_bbt_to_flash(void)
+{
+       return mxc_nfc_update_bbt(g_mxc_nfc_bbt_main_descr, g_mxc_nfc_bbt_mirror_descr);
 }
 
 /*!
- * Program data from memory to flash
- * @param la                   linear NAND flash address. it has to be block size aligned
- * @param maddr                        memory buf address where data will be copied from
- * @param len                  number of bytes
- * @return                             FLASH_ERR_OK (0) if successful; non-zero otherwise
+ * Unconditionally erase a block without checking the BI field.
+ * Note that there is NO error checking for passed-in ra.
+ *
+ * @param ra           starting address in the raw address space (offset)
+ *                                     Must be block-aligned
+ * @return                     0 if successful; -1 otherwise
  */
-int nfc_program_region(u32 la, u32 maddr, int len)
+static int nfc_erase_blk(u32 ra)
 {
-       u32 ra;
-
-       nfc_printf(NFC_DEBUG_MED, "%s(la=0x%08x, maddr=0x%08x, len=0x%x)\n",
-                          __FUNCTION__, la, maddr, len);
+       u16 flash_status, i;
+       u32 pg_no, pg_off;
+
+       if (g_nfc_version == MXC_NFC_V3) {
+               // combine the two commands for erase
+               writel((FLASH_Start_Erase << 8) | FLASH_Block_Erase, NAND_CMD_REG);
+               pg_no = ra / NF_PG_SZ;
+               pg_off = ra % NF_PG_SZ;
+               for (i = 0; i < num_of_nand_chips; i++) {
+                       start_nfc_addr_ops(FLASH_Block_Erase, pg_no, pg_off, 1, i, num_of_nand_chips);
+                       // start auto-erase
+                       writel(NAND_LAUNCH_AUTO_ERASE, NAND_LAUNCH_REG);
+                       wait_op_done();
+                       pg_off = 0;
+               }
+               flash_status = NFC_STATUS_READ();
+               // check I/O bit 0 to see if it is 0 for success
+               if ((flash_status & ((0x1 << num_of_nand_chips) - 1)) != 0) {
+                       return -1;
+               }
+       } else {
+               NFC_CMD_INPUT(FLASH_Block_Erase);
+               start_nfc_addr_ops(FLASH_Block_Erase, ra / NF_PG_SZ, ra % NF_PG_SZ,
+                                                  1, 0, num_of_nand_chips);
+               NFC_CMD_INPUT(FLASH_Start_Erase);
 
-       la &=  MXC_NAND_LA_MASK;
+               flash_status = NFC_STATUS_READ();
 
-       if ((la % NF_BLK_SZ) != 0 || len <= 0) {
-               diag_printf("%s(): invalid or not block aligned\n", __FUNCTION__);
-               diag_printf("la=0x%08x, len=%d\n", la, len);
-               return FLASH_ERR_INVALID;
+               // check I/O bit 0 to see if it is 0 for success
+               if ((flash_status & 0x1) != 0) {
+                       return -1;
+               }
        }
+       return 0;
+}
 
-       do {
-               ra = nfc_l_to_p(la);
-               la += NF_BLK_SZ;
+/*!
+ * Program a block of data in the flash. This function doesn't do
+ * bad block checking. But if program fails, it return error.
+ * Note: If "len" is less than a block it will program up to a page's
+ *              boundary. If not within a page boundary, then it fills the
+ *              rest of the page with 0xFF.
+ *
+ * @param ra           destination raw flash address
+ * @param buf          source address in the RAM
+ * @param len          len to be programmed
+ *
+ * @return                     0 if successful; -1 otherwise
+ */
+static int nfc_program_blk(u32 ra, u8 *buf, u32 len)
+{
+       u32 temp = num_of_nand_chips;
 
-               if (ra > (NF_DEV_SZ - NF_BLK_SZ)) {
-                       diag_printf("%s()1: la=0x%08x (ra=0x%08x) is out of valid range\n",
-                                               __FUNCTION__, la, ra);
-                       return FLASH_ERR_PROGRAM;
-               }
+       /* Needed when romupdate is called */
+       if (ra == 0)
+               num_of_nand_chips = 1;
 
-               if (nfc_is_badblock(ra)) {
-                       diag_printf("\n%s(ra=0x%08x): bad block: %d\n",
-                                               __FUNCTION__, ra, OFFSET_TO_BLOCK(ra));
-                       return FLASH_ERR_PROGRAM;
-               } else {
-                       if (nfc_program_blk(ra, maddr) == 0) {
-                               len -= NF_BLK_SZ;
-                               maddr += NF_BLK_SZ;
-                       } else {
-                               diag_printf("\n%s2(ra=0x%08x): bad block: %d\n",
-                                                       __FUNCTION__, ra, OFFSET_TO_BLOCK(ra));
-                               return FLASH_ERR_PROGRAM;
-                       }
+       for (; len >= NF_PG_SZ; len -= NF_PG_SZ) {
+               if (nfc_write_pg_random(ra / NF_PG_SZ, ra % NF_PG_SZ, buf, 0) != 0) {
+                       return -1;
                }
-       } while (len > 0);
-
-       return FLASH_ERR_OK;
+               ra += NF_PG_SZ;
+               buf += NF_PG_SZ;
+       }
+       if (len != 0) {
+               memset(g_page_buf + len, 0xFF, NF_PG_SZ - len);
+               memcpy(g_page_buf, buf, len);
+               if (nfc_write_pg_random(ra / NF_PG_SZ, ra % NF_PG_SZ, g_page_buf, 0) != 0) {
+                       num_of_nand_chips = temp;
+                       return -1;
+               }
+       }
+       num_of_nand_chips = temp;
+       return 0;
 }
 
 /*!
- * Read data from linear NAND flash address to memory. The MSB of the passed-
- * in flash address will be masked off inside the function.
- *
- * @param la                   linear NAND flash address. it has to be page aligned
- * @param mem_addr             memory buf address where data will be copied to
+ * Erase a range of NAND flash good blocks only.
+ * It skips bad blocks and update the BBT once it sees new bad block due to erase.
+ * @param addr                 raw NAND flash address. it has to be block size aligned
  * @param len                  number of bytes
+ * @param skip_bad             if 1, don't erase bad block; otherwise, always erase
+ * @param verbose              use true to print more messages
+ *
  * @return                             FLASH_ERR_OK (0) if successful; non-zero otherwise
  */
-int nfc_read_region(u32 la, u32 mem_addr, int len)
+int nfc_erase_region(flash_addr_t addr, u32 len, bool skip_bad, bool verbose)
 {
-       u32 ra;
-       u32 dst = mem_addr;
-
-       // make sure 32-bit aligned
-       len = (len + 3) & (~0x3);
-
-       nfc_printf(NFC_DEBUG_MED, "\n%s(la=0x%08x, mem_addr=0x%08x, len=0x%x)\n",
-                          __FUNCTION__, la, mem_addr, len);
+       u32 sz, blk, update = 0, skip = 0, j = 0;
 
-       if (la < (u32)(flash_info.start) || (la + len) > (u32)(flash_info.end)) {
-               diag_printf("\n%s(): Error: invalid address=0x%08x, len=%d\n",
-                                       __FUNCTION__, la, len);
-               return FLASH_ERR_INVALID;
-       }
-       la &=  MXC_NAND_LA_MASK;
+       nfc_printf(NFC_DEBUG_MED, "%s: addr=0x%08llx len=0x%08x\n",
+                          __FUNCTION__, (u64)addr, len);
 
-       if (len <= 0) {
-               diag_printf("** Error: invalid length %d\n", len);
+       if ((addr % NF_BLK_SZ) != 0) {
+               diag_printf("Error: flash address 0x%08llx not block aligned\n", addr);
                return FLASH_ERR_INVALID;
        }
-       if ((la % NF_PG_SZ) != 0) {
-               diag_printf("** Error: flash address 0x%08x not page aligned\n", la);
+       if ((len % NF_BLK_SZ) != 0 || len == 0) {
+               diag_printf("Error: invalid length %u (must be > 0 and block aligned)\n", len);
                return FLASH_ERR_INVALID;
        }
-
-       do {
-               ra = nfc_l_to_p(la);
-               la += NF_PG_SZ;
-
-               if (nfc_is_badblock(ra)) {
-                       diag_printf("\n%s(1): ra=0x%08x bad block: %d\n",
-                                          __FUNCTION__, ra, OFFSET_TO_BLOCK(ra));
-                       return FLASH_ERR_INVALID;
-               } else {
-                       int i;
-                       if (nfc_read_page(ra) != 0) {
-                               return FLASH_ERR_INVALID;
+       addr &= MXC_NAND_ADDR_MASK;
+       // now addr has to be block aligned
+       for (sz = 0; sz < len; addr += NF_BLK_SZ, j++, sz += NF_BLK_SZ) {
+               blk = OFFSET_TO_BLOCK(addr);
+               if (skip_bad && nfc_is_badblock(blk, g_bbt)) {
+                       if (skip++ >= flash_dev_info->max_bad_blk) {
+                               diag_printf("\nToo many bad blocks encountered\n");
+                               return FLASH_ERR_PROTOCOL;
                        }
-                       i = (len < NF_PG_SZ) ? len: NF_PG_SZ;
-//                       diag_printf("\nlen=%d, i=%d\n", len, i);
-                       // now do the copying
-                       nfc_buf_mem_cpy((void*)dst, (void*)(NAND_MAIN_BUF0), i);
-                       len -= i;
-                       dst += i;
+                       diag_printf("\nSkipping bad block %u at addr 0x%08llx\n",
+                                               blk, (u64)addr);
+                       continue;
                }
-       } while (len > 0);
-
-       return FLASH_ERR_OK;
-}
-
-#ifdef NFC_2K_BI_SWAP
-static void mxc_swap_2k_BI_main_sp(int check_bad_blk)
-{
-       u16 tmp1, tmp2, new_tmp1;
-       tmp1 = readw(BAD_BLK_MARKER_464);
-       tmp2 = readw(BAD_BLK_MARKER_SP_5);
-
-       new_tmp1 = (tmp1 & 0xFF00) | (tmp2 >> 8);
-       tmp2 = (tmp1 << 8) | (tmp2 & 0xFF);
-       writew(new_tmp1, BAD_BLK_MARKER_464);
-       writew(tmp2, BAD_BLK_MARKER_SP_5);
-       if (check_bad_blk) {
-               is_bad_blk = 0;
-               if ((tmp1 & 0xFF) != 0xFF) {
-                       is_bad_blk = 1;
+               if (nfc_erase_blk(addr) != 0) {
+                       diag_printf("\nError: Failed to erase block %u at addr 0x%08llx\n",
+                                           blk, (u64)addr);
+                       mark_blk_bad(blk, g_bbt, BLK_BAD_RUNTIME);
+                       // we don't need to update the table immediately here since even
+                       // with power loss now, we should see the same erase error again.
+                       update++;
+                       continue;
+               }
+               if (verbose) {
+                       if ((j % 0x20) == 0)
+                               diag_printf("\n%s 0x%08llx: ", skip_bad ? "Erase" : "FORCE erase", (u64)addr);
+                       diag_printf(".");
+               }
+       }
+       if (update) {
+               if (program_bbt_to_flash() != 0) {
+                       diag_printf("\nError: Failed to update bad block table\n");
+                       return -1;
                }
+               diag_printf("\nnew bad blocks=%d\n", update);
        }
+       return FLASH_ERR_OK;
 }
-#endif
 
-static int nfc_program_page_raw(u32 block, u32 page)
+/*!
+ * Program a range of NAND flash in blocks only.
+ * It skips bad blocks and update the BBT once it sees new bad block due to program.
+ * @param addr                 raw NAND flash address. it has to be block size aligned
+ * @param len                  number of bytes
+ * @return                             FLASH_ERR_OK (0) if successful; non-zero otherwise
+ */
+int nfc_program_region(flash_addr_t addr, u8 *buf, u32 len)
 {
-       u16 flash_status;
-       u32 flash_addr = (block * NF_PG_PER_BLK + page) << NAND_PG_SHIFT;
+       u32 sz, blk, update = 0, skip = 0, partial_block_size;
 
-       diag_printf("%s: addr=%08x block=%6d page=%6d\n", __FUNCTION__,
-                               flash_addr, block, page);
-#if 0
-return 0;
-#endif
-       NFC_CMD_INPUT(FLASH_Send_Data);
-       start_nfc_addr_ops(ADDRESS_INPUT_PROGRAM_PAGE, flash_addr,
-                                          MXC_NAND_LA_MASK);
+       diag_printf1("%s: addr=0x%08llx, len=0x%08x\n", __FUNCTION__, (u64)addr, len);
 
-       NFC_DATA_INPUT(RAM_BUF_0, NFC_MAIN_ONLY, g_ecc_enable);
-       if (g_is_2k_page && PG_2K_DATA_OP_MULTI_CYCLES()) {
-               NFC_DATA_INPUT_2k(RAM_BUF_1);
-               NFC_DATA_INPUT_2k(RAM_BUF_2);
-               NFC_DATA_INPUT_2k(RAM_BUF_3);
+       if ((addr % (NF_PG_SZ / num_of_nand_chips)) != 0) {
+               diag_printf("Error: flash address 0x%08llx not page aligned\n", (u64)addr);
+               return FLASH_ERR_INVALID;
        }
-       NFC_CMD_INPUT(FLASH_Program);
-
-       flash_status = NFC_STATUS_READ();
-       // check I/O bit 0 to see if it is 0 for success
-       if ((flash_status & 0x1) != 0) {
-               diag_printf("** Error: failed to program page %d at 0x%08x status=0x%x\n",
-                                       flash_addr >> NAND_PG_SHIFT, (block * NF_PG_PER_BLK + page) * NF_PG_SZ,
-                                       flash_status);
-               return -1;
+       if (len == 0) {
+               diag_printf("Error: invalid length\n");
+               return FLASH_ERR_INVALID;
        }
 
-       return 0;
-}
+       partial_block_size = addr % NF_BLK_SZ;
 
-static int nfc_write_pg_random(u32 flash_addr, u32 mem_addr,
-                                                          enum nfc_page_area area, int swap)
-{
-       u16 flash_status, i;
-
-       nfc_printf(NFC_DEBUG_MAX, "%s: addr=%08x block=%6d page=%6d, col=%4d\n", __FUNCTION__,
-                          flash_addr, (flash_addr >> NAND_PG_SHIFT) / NF_PG_PER_BLK,
-                          flash_addr >> NAND_PG_SHIFT, flash_addr % NF_PG_SZ);
-       switch (area) {
-       case NFC_MAIN_ONLY:
-               // Read back the spare area first
-               for (i = 0; i < 16; i++) {
-                       // Make all spare area as 0xFF
-                       writel(0xFFFFFFFF, NAND_SPAR_BUF0 + i * 4);
-               }
-
-               nfc_buf_mem_cpy((void *)NAND_MAIN_BUF0, (void *)mem_addr, 512);
-               if (g_is_2k_page) {
-                       nfc_buf_mem_cpy((void *)NAND_MAIN_BUF1, (void *)(mem_addr + 512),
-                                                       512 * 3);
-#ifdef MXC_NAND_BOOT_LOAD_AT_0x400
-                       // To replace the data at offset 0x400 with the address of the NFC base
-                       // This is needed for certain platforms 
-                       if ((flash_addr <= 0x400) && ((flash_addr + NF_PG_SZ - 1) > 0x400)) {
-//                               diag_printf("\nflash_addr = 0x%08x\n", flash_addr);
-                               diag_printf("\n[INFO] 2K page: copy data at 0x400 to spare area and set it to 0x%08x\n", NFC_BASE);
-                               writel(readl(NFC_BASE + 0x400), NAND_SPAR_BUF2);
-                               writel(NFC_BASE, NFC_BASE + 0x400);
-                       }
-#endif
-#ifdef NFC_2K_BI_SWAP
-                       if (swap)
-                               mxc_swap_2k_BI_main_sp(0);
-#endif
-               } else {
-#ifdef MXC_NAND_BOOT_LOAD_AT_0x400
-                       // To replace the data at offset 0x400 with the address of the NFC base
-                       // This is needed for certain platforms 
-                       if ((flash_addr <= 0x400) && ((flash_addr + NF_PG_SZ - 1) > 0x400)) {
-                               diag_printf("\nflash_addr = 0x%08x\n", flash_addr);
-                               diag_printf("\n[INFO] 512 page: copy data at 0x400 to spare area and set it to 0x%08x\n", NFC_BASE);
-                               writel(readl(NFC_BASE), NAND_SPAR_BUF0);
-                               writel(NFC_BASE, NFC_BASE);
+       addr &= MXC_NAND_ADDR_MASK;
+       // now addr has to be block aligned
+       while (1) {
+               blk = OFFSET_TO_BLOCK(addr);
+               if (nfc_is_badblock(blk, g_bbt)) {
+                       if (skip++ >= flash_dev_info->max_bad_blk) {
+                               diag_printf("\nToo many bad blocks encountered\n");
+                               return FLASH_ERR_PROTOCOL;
                        }
-#endif
+                       diag_printf("\nSkipping bad block %u at addr 0x%08llx\n", blk, addr);
+                       goto incr_address;
                }
-               break;
-       case NFC_SPARE_ONLY:
-               // This is used ONLY for testing when manually create "bad" blocks
-               nfc_buf_mem_cpy((void *)(NAND_SPAR_BUF0), (void *)mem_addr, 16);
-               if (!g_is_2k_page) {
-                       NFC_CMD_INPUT(FLASH_Read_Mode3);
+
+               sz = (len >= partial_block_size) ? partial_block_size : len;
+
+               if (nfc_program_blk(addr, buf, sz) != 0) {
+                       update++;
+                       diag_printf("\nError: Failed to program flash block %u at addr 0x%08llx\n",
+                                               blk, (u64)addr);
+                       mark_blk_bad(blk, g_bbt, BLK_BAD_RUNTIME);
+                       if (skip + update > flash_dev_info->max_bad_blk) {
+                               diag_printf("\nToo many bad blocks encountered\n");
+                               return FLASH_ERR_PROTOCOL;
+                       }
+                       // we don't need to update the table immediately here since even
+                       // with power loss now, we should see the same program error again.
+                       goto incr_address;
                }
-               break;
-       default:
-               diag_printf("NOT supported yet!\n");
-               return -1;
-       }
+               diag_printf(".");
 
-       NFC_CMD_INPUT(FLASH_Send_Data);
-       start_nfc_addr_ops(ADDRESS_INPUT_PROGRAM_PAGE, flash_addr,
-                                          MXC_NAND_LA_MASK);
+               len -= sz;
+               buf += sz;
+               if (len == 0)
+                       break;
 
-       NFC_DATA_INPUT(RAM_BUF_0, area, g_ecc_enable);
-       if (g_is_2k_page && PG_2K_DATA_OP_MULTI_CYCLES()) {
-               NFC_DATA_INPUT_2k(RAM_BUF_1);
-               NFC_DATA_INPUT_2k(RAM_BUF_2);
-               NFC_DATA_INPUT_2k(RAM_BUF_3);
+incr_address:
+               addr += partial_block_size;
+               partial_block_size = NF_BLK_SZ;
        }
-       NFC_CMD_INPUT(FLASH_Program);
-
-       flash_status = NFC_STATUS_READ();
-       // check I/O bit 0 to see if it is 0 for success
-       if ((flash_status & 0x1) != 0) {
-               diag_printf("Error: failed to write page %d col=%d (address 0x%08x) status=0x%x\n",
-                                       flash_addr >> NAND_PG_SHIFT, flash_addr % NF_PG_SZ,
-                                       (flash_addr >> NAND_PG_SHIFT) * NF_PG_SZ + (flash_addr % NF_PG_SZ),
-                                       flash_status);
-               return -1;
+       if (update) {
+               if (program_bbt_to_flash() != 0) {
+                       diag_printf("\nError: Failed to update bad block table\n");
+                       return -1;
+               }
+               diag_printf("\nnew bad blocks: %d\n", update);
        }
+       if (skip)
+               diag_printf("\nbad blocks skipped: %d\n", skip);
 
-       return 0;
+       return FLASH_ERR_OK;
 }
 
 /*!
- * This function programs a page's main, spare, or both. For main area program,
- * It copies out the spare area of that page first and then write it along
- * with the main area back to the NAND flash (FIXME: can't just program main alone?
- * For spare area program, it will scratch out the main area data (FIXME).
+ * Read data from raw NAND flash address to memory. The MSB of the passed-
+ * in flash address will be masked off inside the function.
+ * It skips bad blocks and read good blocks of data for "len" bytes.
  *
- * @param ra                   starting address to be programmed inside the NAND flash.
- *                                             Must be page-aligned
- * @param mem_addr             source address in the RAM.
- *                                             For main area:  mem_addr -> starting of data for main area
- *                                             For spare area: mem_addr -> starting of data for spare area
- *                                             For both area: mem_addr -> starting of data for main area along with spare area
- * @return                             0 if no error or 1-bit error; -1 otherwise
+ * @param addr                 NAND flash address. it has to be page aligned
+ * @param buf                  memory buf where data will be copied to
+ * @param len                  number of bytes
+ * @return                             FLASH_ERR_OK (0) if successful; non-zero otherwise
  */
-// FIXME: Add programming of the spare area only
-static int nfc_program_page(u32 ra, u32 mem_addr, enum nfc_page_area area)
+int nfc_read_region(flash_addr_t addr, u8 *buf, u32 len)
 {
-       u32 flash_addr;
+       u32 blk, bad = 0, start_point = 0, pg_no;
+       unsigned long offset = addr % NF_PG_SZ;
 
-//       diag_printf("%s(0x%08x, 0x%08x, %d\n", __FUNCTION__, ra, mem_addr, area);
+       diag_printf1("%s: addr=0x%08llx, buf=0x%p, len=0x%08x\n",
+                                __FUNCTION__, addr, buf, len);
 
-       if (ra % NF_PG_SZ) {
-               diag_printf("** Error: Non page-aligned write not supported: 0x%08x\n", ra);
-               return -1;
+       if (addr < (u32)flash_info.start || (addr + len) > (u32)flash_info.end || len == 0) {
+               diag_printf("Error: flash address 0x%08llx..0x%08llx outside valid range %p..%p\n",
+                                       (u64)addr, (u64)addr + len - 1, flash_info.start, flash_info.end);
+               return FLASH_ERR_INVALID;
        }
-       flash_addr = (ra / NF_PG_SZ) << NAND_PG_SHIFT;
 
-       return nfc_write_pg_random(flash_addr, mem_addr, area, 1);
-}
+       addr = (addr & MXC_NAND_ADDR_MASK) - offset;
+       blk = OFFSET_TO_BLOCK(addr);
+       while (len > 0) {
+               int i;
 
-/*!
- * Low level spare-only read. Only applies to 512 byte page NAND.
- *
- * @param addr                 starting address to be read from the NAND flash
- * @param buf                  one of the internal buffers
- * @return                             0 if no error or 1-bit error; -1 otherwise
- */
-static int nfc_sp_only_read_ll(u32 addr, enum nfc_internal_buf buf)
-{
-       volatile u16 temp;
+               if ((addr % NF_BLK_SZ) == 0) {
+                       // only need to test block aligned page address
+                       blk = OFFSET_TO_BLOCK(addr);
+                       if (nfc_is_badblock(blk, g_bbt)) {
+                               if (bad++ >= flash_dev_info->max_bad_blk) {
+                                       diag_printf("\nToo many bad blocks encountered\n");
+                                       return FLASH_ERR_PROTOCOL;
+                               }
+                               diag_printf("\nSkipping bad block %u at addr 0x%08llx\n", blk, (u64)addr);
+                               addr += NF_BLK_SZ;
+                               continue;
+                       }
+               }
 
-       NFC_CMD_INPUT(FLASH_Read_Mode3);
-       start_nfc_addr_ops(ADDRESS_INPUT_READ_PAGE, addr, MXC_NAND_LA_MASK);
-       NFC_DATA_OUTPUT(buf, FDO_SPARE_ONLY, g_ecc_enable);
-       temp = readw(ECC_STATUS_RESULT_REG);
-       NFC_CMD_INPUT(FLASH_Read_Mode1);
+               pg_no = addr / NF_PG_SZ;
+               if ((addr % NF_PG_SZ) != 0) {
+                       /* Find which interleaved NAND device */
+                       start_point = (addr - (pg_no * NF_PG_SZ)) / (NF_PG_SZ / num_of_nand_chips);
+               } else {
+                       start_point = 0;
+               }
+               for (i = start_point; i < num_of_nand_chips; i++) {
+                       int chunk_size = len > NF_PG_SZ / num_of_nand_chips ?
+                               NF_PG_SZ / num_of_nand_chips : len;
 
-       if (g_ecc_enable) {
-               if ((temp & 0x2) != 0x0) {
-                       nfc_printf(NFC_DEBUG_MED, "\nError %d: %s(addr=0x%08x): ECC status result reg=0x%x\n",
-                                               __LINE__, __FUNCTION__, addr, temp);
-                       return -1;
+                       if (nfc_read_page(i, pg_no, 0) != 0) {
+                               diag_printf("\nError: Failed to read flash block %u at addr 0x%08llx\n",
+                                                       blk, (u64)addr);
+                               return FLASH_ERR_INVALID;
+                       }
+                       // now do the copying
+                       nfc_buf_read(buf, NAND_MAIN_BUF0, chunk_size);
+
+                       buf += chunk_size;
+                       len -= chunk_size;
+                       addr += NF_PG_SZ / num_of_nand_chips - offset;
+                       offset = 0;
                }
        }
 
-       return 0;
+       return FLASH_ERR_OK;
 }
 
-/*!
- * Read spare area from NAND flash to the 1st internal RAM buffer. 
- * Not supported for 2kB page NAND.
- * 
- * @param addr                 starting address to be read from the NAND flash
+/*
+ * Support only either program for main area only. Or spare-area only for 512B.
+ * If one wants to write to the spare-area, then before calling this function,
+ * the spare area NFC RAM buffer has to be setup already. This function doesn't touch
+ * the spare area NFC RAM buffer.
  *
- * @return                             0 if no error or 1-bit error; -1 otherwise
+ * @param pg_no                        page number offset from 0
+ * @param pg_off               byte offset within the page
+ * @param buf                  data buffer in the RAM to be written to NAND flash
+ * @param ecc_force            can force ecc to be off. Otherwise, by default it is on
+ *                                             unless the page offset is non-zero
+ *
+ * @return     0 if successful; non-zero otherwise
  */
-static int nfc_read_page_sp(u32 addr)
+// SP-only opearation is not supported anymore !!!
+static int nfc_write_pg_random(u32 pg_no, u32 pg_off, u8 *buf, u32 ecc_force)
 {
-       if (g_spare_only_read_ok) {
-               if (g_is_2k_page) {
-                       diag_printf("** Error: spare-only read for 2k page is not supported\n");
+       u16 flash_status;
+       u32 ecc = NFC_FLASH_CONFIG2_ECC_EN, v, i;
+       u32 write_count = NF_PG_SZ, start_point = 0, rba, rba_count = 0;
+
+       // the 2nd condition is to test for unaligned page address -- ecc has to be off.
+       if (ecc_force == ECC_FORCE_OFF || pg_off != 0) {
+               ecc = 0;
+       }
+
+       diag_printf1("%s(0x%x, 0x%x, %d)\n", __FUNCTION__, pg_no, pg_off, ecc_force);
+
+       switch (g_nfc_version & 0xf0) {
+       case MXC_NFC_V3:
+               /* Check if Page size is greater than NFC buffer */
+               do {
+                       if (write_count <= NFC_BUFSIZE) {
+                               // No need to worry about the spare area
+                               nfc_buf_write(NAND_MAIN_BUF0, buf, write_count);
+                               write_count = 0;
+                       } else {
+                               // No need to worry about the spare area
+                               nfc_buf_write(NAND_MAIN_BUF0, buf, NFC_BUFSIZE);
+                               write_count -= NFC_BUFSIZE;
+                               buf += NFC_BUFSIZE;
+                       }
+                       // combine the two commands for program
+                       writel((FLASH_Program << 8) | FLASH_Send_Data, NAND_CMD_REG);
+
+                       for (i = start_point; i < num_of_nand_chips; i++) {
+                               rba = rba_count * ((NF_PG_SZ / num_of_nand_chips) / 512);
+                               /* Completely wrote out the NFC buffer, break and copy more to the NFC buffer */
+                               if (rba > 7) {
+                                       rba_count = 0;
+                                       break;
+                               }
+
+                               // For ECC
+                               v = readl(NFC_FLASH_CONFIG2_REG) & ~NFC_FLASH_CONFIG2_ECC_EN;
+                               // setup config2 register for ECC enable or not
+                               write_nfc_ip_reg(v | ecc, NFC_FLASH_CONFIG2_REG);
+
+                               start_nfc_addr_ops(FLASH_Program, pg_no, pg_off, 0, i, num_of_nand_chips);
+
+                               // start auto-program
+                               writel(NAND_LAUNCH_AUTO_PROG, NAND_LAUNCH_REG);
+                               if (i < (num_of_nand_chips - i))
+                                       wait_for_auto_prog_done();
+                               else
+                                       wait_op_done();
+                               pg_off = 0;
+                               rba_count++;
+                       }
+                       start_point = i;
+               } while (write_count > 0);
+               flash_status = NFC_STATUS_READ();
+               // check I/O bit 0 to see if it is 0 for success
+               if ((flash_status & ((0x1 << num_of_nand_chips) - 1)) != 0) {
+                       return -1;
+               }
+               break;
+       default:
+               if (g_nfc_version != MXC_NFC_V1) {
+                       int i;
+
+                       for (i = 1; i < NFC_SPARE_BUF_SZ / 16; i++) {
+                               memcpy((void *)(NAND_SPAR_BUF0 + i * NFC_SPARE_BUF_SZ),
+                                          (void *)(NAND_SPAR_BUF0 + i * 16), 16);
+                       }
+               }
+               nfc_buf_write(NAND_MAIN_BUF0, buf, NF_PG_SZ);
+#ifdef BARKER_CODE_SWAP_LOC
+               // To replace the data at offset MXC_NAND_BOOT_LOAD_BARKER with
+               // the address of the NFC base. This is needed for certain platforms.
+               if (pg_no == 0) {
+                       diag_printf("\n[INFO]: copy data at 0x%x to spare area and set it to 0x%x\n",
+                                               BARKER_CODE_SWAP_LOC, BARKER_CODE_VAL);
+                       writel(readl(NFC_BASE + BARKER_CODE_SWAP_LOC), NAND_SPAR_BUF0);
+                       // todo: set BARKER_CODE_VAL and BARKER_CODE_SWAP_LOC for skye, etc.
+                       writel(BARKER_CODE_VAL, NFC_BASE + BARKER_CODE_SWAP_LOC);
+               }
+#endif
+               NFC_CMD_INPUT(FLASH_Send_Data);
+               start_nfc_addr_ops(FLASH_Program, pg_no, pg_off, 0, 0, num_of_nand_chips);
+
+               NFC_DATA_INPUT(RAM_BUF_0, NFC_MAIN_ONLY, ecc);
+               if (g_is_4k_page && PG_2K_DATA_OP_MULTI_CYCLES()) {
+                       diag_printf("4K page with multi cycle write is not supported\n");
+                       return -1;
+               }
+               if (g_is_2k_page && PG_2K_DATA_OP_MULTI_CYCLES()) {
+                       NFC_DATA_INPUT_2k(RAM_BUF_1);
+                       NFC_DATA_INPUT_2k(RAM_BUF_2);
+                       NFC_DATA_INPUT_2k(RAM_BUF_3);
+               }
+               NFC_CMD_INPUT(FLASH_Program);
+
+               flash_status = NFC_STATUS_READ();
+               // check I/O bit 0 to see if it is 0 for success
+               if ((flash_status & 0x1) != 0) {
+                       diag_printf("** Error: failed to program page %u at 0x%08x status=0x%02x\n",
+                                               pg_no, pg_no * NF_PG_SZ + pg_off, flash_status);
                        return -1;
                }
-               return nfc_sp_only_read_ll(addr, RAM_BUF_0);
        }
-       return -1;
+       return 0;
 }
 
-static int nfc_read_pg_random(u32 flash_addr, int swap)
+#ifndef NFC_V3_0
+// for version V1 and V2 of NFC
+static int nfc_read_pg_random(u32 pg_no, u32 pg_off, u32 ecc_force, u32 cs_line,
+                                                         u32 num_of_nand_chips)
 {
-       volatile u16 t1, t2 = 0, t3 = 0, t4 = 0;
+       u32 t1, ecc = 1;
+       u8 t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0, t8 = 0;
        int res = 0;
-               
-#if 0 //TODO: kevin revisit
-       if (!g_is_2k_page && (t1 = (flash_addr & ((1 << (1 + NAND_PG_SHIFT)) - 1))) >= 512) {
-                       NFC_CMD_INPUT(FLASH_Read_Mode3);
-                       flash_addr -= 512;
-                       diag_printf("kevin: 0x%08x\n", flash_addr);
-       } else {
-                       NFC_CMD_INPUT(FLASH_Read_Mode1);
-       }
-#endif
-       nfc_printf(NFC_DEBUG_MAX, "%s: addr=%08x block=%6d page=%6d, col=%4d\n", __FUNCTION__,
-                          flash_addr, (flash_addr >> NAND_PG_SHIFT) / NF_PG_PER_BLK,
-                          flash_addr >> NAND_PG_SHIFT, flash_addr % NF_PG_SZ);
+
+       if (ecc_force == ECC_FORCE_OFF || pg_off != 0 )
+               ecc = 0;
 
        NFC_CMD_INPUT(FLASH_Read_Mode1);
+       start_nfc_addr_ops(FLASH_Read_Mode1, pg_no, pg_off, 0, 0, num_of_nand_chips);
 
-       start_nfc_addr_ops(ADDRESS_INPUT_READ_PAGE, flash_addr, MXC_NAND_LA_MASK);
-       if (g_is_2k_page) {
-               NFC_CMD_INPUT(FLASH_Read_Mode1_2K);
+       if (g_is_2k_page || g_is_4k_page) {
+               NFC_CMD_INPUT(FLASH_Read_Mode1_LG);
        }
-       
-       if (g_nfc_version == MXC_NFC_V1) {
-               NFC_DATA_OUTPUT(RAM_BUF_0, FDO_PAGE_SPARE, g_ecc_enable);
-               t1 = readw(ECC_STATUS_RESULT_REG);
 
-               if (g_is_2k_page) {
-                       NFC_DATA_OUTPUT(RAM_BUF_1, FDO_PAGE_SPARE, g_ecc_enable);
+       NFC_DATA_OUTPUT(RAM_BUF_0, FDO_PAGE_SPARE, ecc);
+       switch (g_nfc_version & 0xf0) {
+       case MXC_NFC_V1:
+               t1 = readw(ECC_STATUS_RESULT_REG);
+               if (g_is_2k_page && PG_2K_DATA_OP_MULTI_CYCLES()) {
+                       NFC_DATA_OUTPUT(RAM_BUF_1, FDO_PAGE_SPARE, ecc);
                        t2 = readw(ECC_STATUS_RESULT_REG);
-                       NFC_DATA_OUTPUT(RAM_BUF_2, FDO_PAGE_SPARE, g_ecc_enable);
+                       NFC_DATA_OUTPUT(RAM_BUF_2, FDO_PAGE_SPARE, ecc);
                        t3 = readw(ECC_STATUS_RESULT_REG);
-                       NFC_DATA_OUTPUT(RAM_BUF_3, FDO_PAGE_SPARE, g_ecc_enable);
+                       NFC_DATA_OUTPUT(RAM_BUF_3, FDO_PAGE_SPARE, ecc);
                        t4 = readw(ECC_STATUS_RESULT_REG);
                }
-               
-               if (g_ecc_enable && ((t1 & 0xA) != 0x0 || (t2 & 0xA) != 0x0 || (t3 & 0xA) != 0x0
-                       || (t4 & 0xA) != 0x0)) {
-                       diag_printf("** Error: uncorrectable ECC error in flash at addr 0x%08x page %d, col %d: ECC status=0x%x:0x%x:0x%x:0x%x\n",
-                                               (flash_addr >> NAND_PG_SHIFT) * NF_PG_SZ + (flash_addr % NF_PG_SZ),
-                                               flash_addr >> NAND_PG_SHIFT,
-                                               flash_addr % NF_PG_SZ, t1, t2, t3, t4);
+
+               if (ecc && ((t1 & 0xA) != 0x0 || (t2 & 0xA) != 0x0 ||
+                                       (t3 & 0xA) != 0x0 || (t4 & 0xA) != 0x0)) {
+                       diag_printf("\n** Error: %s(page=%d, col=%d): ECC status=0x%x:0x%x:0x%x:0x%x\n",
+                                               __FUNCTION__, pg_no, pg_off, t1, t2, t3, t4);
                        res = -1;
                        goto out;
                }
-       } else if (g_nfc_version == MXC_NFC_V2) {
-               NFC_DATA_OUTPUT(RAM_BUF_0, FDO_PAGE_SPARE, g_ecc_enable);
-               if (g_is_2k_page) {
-                       if (PG_2K_DATA_OP_MULTI_CYCLES()) {
-                               NFC_DATA_OUTPUT(RAM_BUF_1, FDO_PAGE_SPARE, g_ecc_enable);
-                               NFC_DATA_OUTPUT(RAM_BUF_2, FDO_PAGE_SPARE, g_ecc_enable);
-                               NFC_DATA_OUTPUT(RAM_BUF_3, FDO_PAGE_SPARE, g_ecc_enable);
-                       }
-                       // To replace the data at offset 0x400 with the address of the NFC base
-                       // This is needed for certain platforms 
-                       if ((flash_addr <= 0x400) && ((flash_addr + NF_PG_SZ - 1) > 0x400)) {
-//                               diag_printf("\nRead: flash_addr = 0x%08x\n", flash_addr);
-                               diag_printf("\n[INFO] 2K page: copy back data from spare to 0x400\n");
-                               writel(readl(NAND_SPAR_BUF2), NFC_BASE + 0x400);
-                       }
-               } else {
-                       // To replace the data at offset 0x400 with the address of the NFC base
-                       // This is needed for certain platforms 
-                       if ((flash_addr <= 0x400) && ((flash_addr + NF_PG_SZ - 1) > 0x400)) {
-                               diag_printf("\nflash_addr = 0x%08x\n", flash_addr);
-                               diag_printf("\n[INFO] 512 page: copy back data from spare to 0x400\n");
-                               writel(readl(NAND_SPAR_BUF0), NFC_BASE);
-                       }
+               break;
+       case MXC_NFC_V2:
+               if (g_is_2k_page && PG_2K_DATA_OP_MULTI_CYCLES()) {
+                       NFC_DATA_OUTPUT(RAM_BUF_1, FDO_PAGE_SPARE, ecc);
+                       NFC_DATA_OUTPUT(RAM_BUF_2, FDO_PAGE_SPARE, ecc);
+                       NFC_DATA_OUTPUT(RAM_BUF_3, FDO_PAGE_SPARE, ecc);
                }
-               if (g_ecc_enable) {
-                       t1 = readw(ECC_STATUS_RESULT_REG);
-                       if (!g_is_2k_page) {
-                               if ((t1 & 0xF) > 4) {
-                                       diag_printf("** Error: uncorrectable ECC error at address 0x%08x page %d, col %d ECC status=0x%x\n",
-                                                               (flash_addr >> NAND_PG_SHIFT) * NF_PG_SZ + (flash_addr % NF_PG_SZ),
-                                                               flash_addr >> NAND_PG_SHIFT,
-                                                               flash_addr % NF_PG_SZ, t1 & 0xF);
-                                       res = -1;
-                                       goto out;
-                               }
-                       } else {
+               if (ecc) {
+                       t1 = readl(ECC_STATUS_RESULT_REG);
+                       if (g_is_2k_page || g_is_4k_page) {
                                t2 = (t1 >> 4) & 0xF;
                                t3 = (t1 >> 8) & 0xF;
                                t4 = (t1 >> 12) & 0xF;
-                               if (t2 > 4 || t3 > 4 || t4 > 4) {
-                                       diag_printf("** Error: uncorrectable ECC error at address 0x%08x page %d, col=%d ECC status=0x%x:0x%x:0x%x\n",
-                                                               (flash_addr >> NAND_PG_SHIFT) * NF_PG_SZ + (flash_addr % NF_PG_SZ),
-                                                               flash_addr >> NAND_PG_SHIFT,
-                                                               flash_addr % NF_PG_SZ, t2, t3, t4);
-                                       res = -1;
-                                       goto out;
+                               if (g_is_4k_page) {
+                                       t5 = (t1 >> 16) & 0xF;
+                                       t6 = (t1 >> 20) & 0xF;
+                                       t7 = (t1 >> 24) & 0xF;
+                                       t8 = (t1 >> 28) & 0xF;
                                }
                        }
+                       if ((t1 = (t1 & 0xF)) > 4 || t2 > 4 || t3 > 4 || t4 > 4 ||
+                               t5 > 4 || t6 > 4 || t7 > 4 || t8 > 4) {
+                               diag_printf("\n** Error: ECC error reading block %u page %u\n",
+                                                       pg_no / NF_PG_PER_BLK, pg_no % NF_PG_PER_BLK);
+                               diag_printf("   ECC status=%x:%x:%x:%x:%x:%x:%x:%x\n",
+                                                       t1, t2, t3, t4, t5, t6, t7, t8);
+                               res = -1;
+                               goto out;
+                       }
                }
+               break;
+       default:
+               diag_printf("Unknown NFC version: %d\n", g_nfc_version);
+               return -1;
        }
-out:
-       if (g_is_2k_page) {
-#ifdef NFC_2K_BI_SWAP
-               if (swap)
-                       mxc_swap_2k_BI_main_sp(1);
-#endif
+       if (g_nfc_version != MXC_NFC_V1) {
+               int i;
+
+               for (i = 1; i < NFC_SPARE_BUF_SZ / 16; i++) {
+                       memcpy((void *)(NAND_SPAR_BUF0 + i * 16),
+                                  (void *)(NAND_SPAR_BUF0 + i * NFC_SPARE_BUF_SZ), 16);
+               }
+       }
+#ifdef BARKER_CODE_SWAP_LOC
+       // To replace the data at offset BARKER_CODE_SWAP_LOC with the address of the NFC base
+       // This is needed for certain platforms
+       if (pg_no == 0) {
+               diag_printf("\n[INFO]: copy back data from spare to 0x%x\n", BARKER_CODE_SWAP_LOC);
+               writel(readl(NAND_SPAR_BUF0), NFC_BASE + BARKER_CODE_SWAP_LOC);
        }
+#endif
+
+out:
        return res;
 }
+#endif                 // ifndef NFC_V3_0
 
 /*!
  * Read a page's both main and spare area from NAND flash to the internal RAM buffer.
  * It always reads data to the internal buffer 0.
  *
- * @param ra             starting address to be read from the NAND flash; must be page-aligned
+ * @param cs_line      which NAND device is used
+ * @param pg_no           page number of the device
+ * @param pg_off       offset within a page
  *
  * @return                             0 if no error or 1-bit error; -1 otherwise
  */
-static int nfc_read_page(u32 ra)
+static int nfc_read_page(u32 cs_line, u32 pg_no, u32 pg_off)
 {
-       u32 flash_addr;
-
-       if (ra % NF_PG_SZ) {
-               diag_printf("Non page-aligned read not supported here: 0x%08x\n", ra);
-               return -1;
-       }
-
-       flash_addr = (ra / NF_PG_SZ) << NAND_PG_SHIFT;
-
-       return nfc_read_pg_random(flash_addr, 1);
+       return nfc_read_pg_random(pg_no, pg_off, ECC_FORCE_ON, cs_line, num_of_nand_chips);
 }
 
-// Read data into buffer
-int flash_read_buf(void* addr, void* data, int len)
+static int nfc_write_page(u32 pg_no, u32 pg_off, u32 ecc_force)
 {
-       if (IS_BOOTING_FROM_NOR() || IS_FIS_FROM_NOR()) {
-               memcpy(data, addr, len);
-               return 0;
-       } else {
-               return nfc_read_region((u32)addr, (u32)data, len);
+       u16 flash_status;
+       u32 ecc = NFC_FLASH_CONFIG2_ECC_EN;
+
+       diag_printf1("Writing page %u addr 0x%08llx\n",
+                                pg_no, (u64)pg_no * NF_PG_SZ + pg_off);
+       if (ecc_force == ECC_FORCE_OFF || pg_off != 0) {
+               ecc = 0;
        }
-}
 
-void mxc_nfc_print_info(void)
-{
-       diag_printf("[0x%08x bytes]: %d blocks of %d pages of %d bytes each.\n",
-                               NF_DEV_SZ, NF_BLK_CNT,
-                               NF_PG_PER_BLK, NF_PG_SZ);
-}
+       if (g_nfc_version == MXC_NFC_V3) {
+               int i;
+               u32 v;
+               u32 start_point = 0, rba, rba_count = 0;
 
-#ifdef MXCFLASH_FLASH_BASED_BBT
-/*
- * The NFC buffers cannot be accessed in byte mode.
- * This routine extracts one byte at a given location in the NFC buffer.
- */
+               // combine the two commands for program
+               writel((FLASH_Program << 8) | FLASH_Send_Data, NAND_CMD_REG);
 
-/**
- * check_short_pattern - [GENERIC] check if a pattern is in the buffer
- * @buf:       the buffer to search
- * @td:                search pattern descriptor
- *
- * Check for a pattern at the given place. Used to search bad block
- * tables and good / bad block identifiers.
- *
-*/
-static int check_short_pattern(void *buf, struct nand_bbt_descr *td)
-{
-       int i;
+               for (i = start_point; i < num_of_nand_chips; i++) {
+                       rba = rba_count * ((NF_PG_SZ / num_of_nand_chips) / 512);
+                       /* Completely wrote out the NFC buffer, break and copy more to the NFC buffer */
+                       if (rba > 7) {
+                               rba_count = 0;
+                               break;
+                       }
 
-       for (i = 0; i < td->len; i++) {
-               if (get_byte(buf, td->offs + i) != td->pattern[i]) {
-                       return -1;
+                       // For ECC
+                       v = readl(NFC_FLASH_CONFIG2_REG) & ~NFC_FLASH_CONFIG2_ECC_EN;
+                       // setup config2 register for ECC enable or not
+                       write_nfc_ip_reg(v | ecc, NFC_FLASH_CONFIG2_REG);
+
+                       start_nfc_addr_ops(FLASH_Program, pg_no, pg_off, 0, i, num_of_nand_chips);
+
+                       // start auto-program
+                       writel(NAND_LAUNCH_AUTO_PROG, NAND_LAUNCH_REG);
+                       if (i < (num_of_nand_chips - i))
+                               wait_for_auto_prog_done();
+                       else
+                               wait_op_done();
+                       pg_off = 0;
+                       rba_count++;
                }
-       }
-       return 0;
-}
-
-/**
- * search_bbt - [GENERIC] scan the device for a specific bad block table
- * @mtd:       MTD device structure
- * @buf:       temporary buffer
- * @td:                descriptor for the bad block table
- *
- * Read the bad block table by searching for a given ident pattern.
- * Search is preformed either from the beginning up or from the end of
- * the device downwards. The search starts always at the start of a
- * block.
- * If the option NAND_BBT_PERCHIP is given, each chip is searched
- * for a bbt, which contains the bad block information of this chip.
- * This is necessary to provide support for certain DOC devices.
- *
- * The bbt ident pattern resides in the oob area of the first page
- * in a block.
- */
-static int search_bbt(struct nand_bbt_descr *td)
-{
-       int bits, startblock, block, dir;
-       int bbtblocks;
-       void *oob = (void *)NAND_SPAR_BUF0;
-
-       /* Search direction top -> down ? */
-       if (td->options & NAND_BBT_LASTBLOCK) {
-               startblock = (NF_DEV_SZ / NF_BLK_SZ) - 1;
-               dir = -1;
+               start_point = i;
+               flash_status = NFC_STATUS_READ();
        } else {
-               startblock = 0;
-               dir = 1;
-       }
-
-       bbtblocks = NF_DEV_SZ / NF_BLK_SZ;
+               if (g_nfc_version != MXC_NFC_V1) {
+                       int i;
 
-       /* Number of bits for each erase block in the bbt */
-       bits = td->options & NAND_BBT_NRBITS_MSK;
+                       for (i = NFC_SPARE_BUF_SZ / 16 - 1; i >= 0; i--) {
+                               memcpy((void *)(NAND_SPAR_BUF0 + i * NFC_SPARE_BUF_SZ),
+                                          (void *)(NAND_SPAR_BUF0 + i * 16), 16);
+                       }
+               }
+               NFC_CMD_INPUT(FLASH_Send_Data);
+               start_nfc_addr_ops(FLASH_Program, pg_no, pg_off, 0, 0, num_of_nand_chips);
 
-       /* Reset version information */
-       td->version = 0;
-       td->pages = -1;
-       /* Scan the maximum number of blocks */
-       for (block = 0; block < td->maxblocks; block++) {
-               int actblock = startblock + dir * block;
-               int ret;
-
-               nfc_printf(NFC_DEBUG_MAX, "%s: Reading block %d (page %d) addr %08x\n", __FUNCTION__,
-                                  actblock, actblock * NF_PG_PER_BLK, actblock * NF_BLK_SZ);
-
-               ret = nfc_read_page(actblock * NF_BLK_SZ);
-               if (ret != 0) {
-                       nfc_printf(NFC_DEBUG_MED, "Failed to read bbt page %d\n",
-                                          actblock * NF_PG_PER_BLK);
-                       continue;
+               NFC_DATA_INPUT(RAM_BUF_0, NFC_MAIN_ONLY, ecc);
+               if (g_is_4k_page && PG_2K_DATA_OP_MULTI_CYCLES()) {
+                       diag_printf("4K page with multi cycle write is not supported\n");
+                       return -1;
                }
-               if (check_short_pattern(oob, td) == 0) {
-                       nfc_printf(NFC_DEBUG_MED, "Found bbt pattern in block %d\n", actblock);
-                       td->pages = actblock * NF_PG_PER_BLK;
-                       if (td->options & NAND_BBT_VERSION) {
-                               td->version = get_byte(oob, td->veroffs);
-                       }
-                       break;
+               if (g_is_2k_page && PG_2K_DATA_OP_MULTI_CYCLES()) {
+                       NFC_DATA_INPUT_2k(RAM_BUF_1);
+                       NFC_DATA_INPUT_2k(RAM_BUF_2);
+                       NFC_DATA_INPUT_2k(RAM_BUF_3);
                }
-               nfc_printf(NFC_DEBUG_MED, "No bbt pattern in block %d\n", actblock);
-       }
-       startblock += flash_dev_info->chipsize / NF_BLK_SZ;
+               NFC_CMD_INPUT(FLASH_Program);
 
-       /* Check, if we found a bbt */
-       if (td->pages == -1) {
-               nfc_printf(NFC_DEBUG_MED, "Bad block table not found\n");
+               flash_status = NFC_STATUS_READ();
+       }
+       if ((flash_status & 0x1) != 0) {
+               diag_printf("** Error: failed to program page %u at addr 0x%08llx\n",
+                                       pg_no, (u64)pg_no * NF_PG_SZ + pg_off);
                return -1;
-       } else {
-               nfc_printf(NFC_DEBUG_MED, "Bad block table found at page %d, version 0x%02X\n",
-                                  td->pages, td->version);
        }
        return 0;
 }
 
-/**
- * nand_isbad_bbt - [NAND Interface] Check if a block is bad
- * @mtd:       MTD device structure
- * @offs:      offset in the device
- * @allowbbt:  allow access to bad block table region
- *
-*/
-int nand_isbad_bbt(u16 *bbt, int block, int allowbbt)
-{
-       cyg_uint8 res;
-
-       block <<= 1;
-       res = (get_byte(bbt, block >> 3) >> (block & 0x06)) & 0x03;
-
-       switch (res ^ 0x03) {
-       case 0x00:
-               return 0;
-       case 0x01:
-               return 1;
-       case 0x02:
-               return allowbbt ? 0 : 1;
-       }
-       return 1;
-}
-
-static int mxc_nfc_find_bbt(struct nand_bbt_descr *td, struct nand_bbt_descr *md)
+// Read data into buffer
+#ifndef MXCFLASH_SELECT_MULTI
+int flash_read_buf(void *addr, void *data, int len)
+#else
+int nandflash_read_buf(void *addr, void *data, int len)
+#endif
 {
-       int ret;
-       int bad = 0;
-       int block;
-       int good;
-       struct nand_bbt_descr *bd = NULL;
-
-       search_bbt(td);
-       bad += td->pages >= 0; /* account for reserved bbt block */
-       if (md != NULL) {
-               search_bbt(md);
-               bad += md->pages >= 0;
-       }
-       if (td->pages < 0 && (md != NULL && md->pages < 0)) {
-               diag_printf("No FLASH based bad block table found\n");
-               return -1;
-       }
-       if (md == NULL || md->version <= td->version) {
-               ret = nfc_read_page(td->pages * NF_PG_SZ);
-               if (ret == 0) {
-                       bd = td;
-                       nfc_printf(NFC_DEBUG_MIN, "Using normal bbt at page %d\n", bd->pages);
-               }
-       }
-       if (bd == NULL && md != NULL) {
-               ret = nfc_read_page(md->pages * NF_PG_SZ);
-               if (ret == 0) {
-                       bd = md;
-                       nfc_printf(NFC_DEBUG_MIN, "Using mirror bbt at page %d\n", bd->pages);
-               }
-       }
-       if (bd == NULL) {
-               ret = nfc_read_page(td->pages * NF_PG_SZ);
-               if (ret == 0) {
-                       bd = td;
-                       nfc_printf(NFC_DEBUG_MIN, "Using normal bbt at page %d\n", bd->pages);
-                       nfc_update_blk_table(td->pages * NF_PG_SZ, 2);
-                       bad++;
-               } else {
-                       diag_printf("** Error: Failed to read bbt from flash\n");
-                       return -1;
-               }
-       }
-       for (block = 0, good = 0; block < NF_BLK_CNT; block++) {
-               if (nand_isbad_bbt((u16 *)NAND_MAIN_BUF0, block, true)) {
-                       nfc_update_blk_table(block * NF_BLK_SZ, true);
-                       nfc_printf(NFC_DEBUG_MIN, "Block %d is marked bad in flash bbt\n", block);
-                       bad++;
-               } else {
-                       l_to_p_table[good] = block;
-                       good++;
-               }
-       }
-       g_nfc_scan_done = true;
-       nfc_printf(NFC_DEBUG_MIN, "%s: Found %d bad/reserved blocks\n", __FUNCTION__, bad);
-       return bad;
+       flash_addr_t flash_addr = (unsigned long)addr;
+       return nfc_read_region(flash_addr, data, len);
 }
 
-static inline void mxc_nfc_buf_clear(unsigned long buf, u8 pattern, int size)
+void mxc_nfc_print_info(void)
 {
-       int i;
-       u16 *p = (u16 *)buf;
-       u16 fill = pattern;
-
-       fill = (fill << 8) | pattern;
-       for (i = 0; i < size >> 1; i++) {
-               p[i] = fill;
-       }
+       diag_printf("[0x%08x bytes]: %u blocks of %u pages of %u bytes each.\n",
+                               NF_DEV_SZ, NF_BLK_CNT,
+                               NF_PG_PER_BLK, NF_PG_SZ);
 }
 
-static int mxc_nfc_write_bbt(int block, int page)
+static inline void mxc_clr_block_offset(void *start, void *end)
 {
-       int ret;
-
-       DBG(1, "%s: Writing bbt block %d page %d\n", __FUNCTION__,
-               block, page);
-       ret = nfc_program_page_raw(block, page);
-       if (ret != 0) {
-               DBG(0, "%s: Failed to write bbt block %d page %d\n", __FUNCTION__, block, page);
-               return ret;
-       }
-       nfc_update_blk_table(block * NF_BLK_SZ, 2);
-       return 0;
+       nfc_printf(NFC_DEBUG_MIN, "Clearing block offset %lu for %p..%p\n",
+                          g_block_offset, start, end);
+       g_block_offset = 0;
 }
 
-static int mxc_nfc_create_bbt(struct nand_bbt_descr *td, struct nand_bbt_descr *md)
-{
-       int ret = 0;
-       int block;
-       int pg_offs = 0;
-       int page = 0;
-       u16 *buf = (u16 *)NAND_MAIN_BUF0;
-       u16 *oob = (u16 *)NAND_SPAR_BUF0;
+static void *flash_region_start;
+static void *flash_region_end;
+static int flash_enable;
 
-       if (td->pages >= 0) {
-               return 1;
-       }
-       if (md->pages < 0) {
-               td->version = 1;
+void mxc_flash_enable(void *start, void *end)
+{
+       if (flash_enable++ == 0) {
+               flash_region_start = start;
+               flash_region_end = end;
+               mxc_clr_block_offset(start, end);
        } else {
-               td->version = md->version;
+               if (start < flash_region_start || end > flash_region_end) {
+                       diag_printf("** WARNING: Enable %p..%p outside enabled flash region %p..%p\n",
+                                               start, end, flash_region_start, flash_region_end);
+               }
        }
-       for (block = NF_BLK_CNT - 1; block >= NF_BLK_CNT - td->maxblocks - 1; block--) {
-               int pg = block * NF_PG_PER_BLK;
+}
 
-               if ((bad_block_code(block) & ~2) == 0) {
-                       if (md != NULL && md->pages == pg) {
-                               continue;
+void mxc_flash_disable(void *start, void *end)
+{
+       if (flash_enable) {
+               if (--flash_enable == 0) {
+                       if (start != flash_region_start || end != flash_region_end) {
+                               diag_printf("** Error: Disable %p..%p not equal to enabled flash region %p..%p\n",
+                                                       start, end, flash_region_start, flash_region_end);
                        }
-                       td->pages = pg;
-                       break;
                }
+       } else {
+               diag_printf("** Error: unbalanced call to flash_disable()\n");
        }
-       if (td->pages < 0) {
-               return -1;
-       }
-       mxc_nfc_buf_clear(NAND_SPAR_BUF0, 0xff, NF_SPARE_SZ);
-       mxc_nfc_buf_clear(NAND_MAIN_BUF0, 0xff, NF_PG_SZ);
+}
+
+static int mxc_nfc_isbad_bbt(u16 *bbt, int block)
+{
+       cyg_uint8 res;
 
-       DBG(0, "%s: Creating bbt %c%c%c%c version %d\n", __FUNCTION__,
-               td->pattern[0], td->pattern[1], td->pattern[2], td->pattern[3], td->version);
-       nfc_buf_mem_cpy(oob + (td->offs >> 1), td->pattern, td->len);
-       store_byte(oob, td->veroffs, td->version);
+       block <<= 1;
+       res = (get_byte(bbt, block >> 3) >> (block & 0x06)) & 0x03;
+       res ^= 0x03;
+       return res;
+}
 
-       for (block = 0, pg_offs = 0; block < NF_BLK_CNT;) {
-               u16 tmp = 0xffff;
-               int i;
+static int mxc_nfc_search_bbt(struct nand_bbt_descr *td)
+{
+       int i;
 
-               if (pg_offs << 1 >= NF_PG_SZ) {
-                       ret = mxc_nfc_write_bbt(td->pages / NF_PG_PER_BLK, page);
-                       if (ret != 0) {
-                               return ret;
-                       }
-                       page++;
-                       mxc_nfc_buf_clear(NAND_SPAR_BUF0, 0xff, NF_SPARE_SZ);
-                       mxc_nfc_buf_clear(NAND_MAIN_BUF0, 0xff, NF_PG_SZ);
-                       pg_offs = 0;
+       td->pages = -1;
+       for (i = 0; i < NF_BBT_MAX_NR; i++) {
+               u32 blk = NF_BLK_CNT - i - 1;
+               flash_addr_t addr = blk * NF_BLK_SZ;
+
+               if (nfc_read_pg_random(addr / NF_PG_SZ, addr % NF_PG_SZ,
+                                                          ECC_FORCE_ON, 0, num_of_nand_chips) != 0) {
+                       diag_printf("Failed to read bbt page %u at 0x%08llx\n",
+                                               (u32)(addr / NF_PG_SZ), addr);
+                       continue;
                }
-               for (i = 0; i < 16 && block < NF_BLK_CNT; i += 2, block++) {
-                       u8 code = bad_block_code(block);
-                       if ((code & ~2) != 0) {
-                               tmp &= ~(code << i);
-                               DBG(2, "%s: bad block %d pattern[%p] %04x mask %04x\n", __FUNCTION__,
-                                       block, &buf[pg_offs], tmp, 0x03 << i);
-                       }
+               if (check_short_pattern((void *)NAND_SPAR_BUF0, td) == 0) {
+                       diag_printf1("found BBT at block %u addr %08llx\n", blk, (u64)addr);
+                       td->pages = blk * NF_PG_PER_BLK;
+                       td->version = get_byte((void *)NAND_SPAR_BUF0, td->veroffs);
+                       mark_blk_bad(blk, g_bbt, BLK_RESERVED);
+                       diag_printf1("Found version %d BBT at block %d (0x%08llx)\n",
+                                                td->version, td->pages / NF_PG_PER_BLK,
+                                                (u64)td->pages * NF_PG_SZ);
+                       return 0;
                }
-               buf[pg_offs] = tmp;
-               pg_offs++;
-       }
-       if (pg_offs > 0) {
-               DBG(0, "%s: Writing final bbt block %d page %d\n", __FUNCTION__,
-                       td->pages / NF_PG_PER_BLK, page);
-               ret = mxc_nfc_write_bbt(td->pages / NF_PG_PER_BLK, page);
        }
-       return ret;
+       return 1;
 }
-#endif
 
-static int mxc_nfc_scan(bool verbose)
+/*
+ * Look for the BBT depending on the passed-in lowlevel value.
+ * @param      lowlevel        If true, then it does a low level scan based on factory
+ *                                             marked BI(block info) field with ECC off to decide if a
+ *                                             block is bad.
+ *                                             If false, then it checks to see if an existing BBT in the
+ *                                             flash or not. If not, then it returns -1. If yes, it will
+ *                                             prints out the number of bad blocks.
+ *
+ * @return     number of bad blocks for the whole nand flash
+ *
+ * Note: For a brand new flash, this function has to be called with
+ *              lowlevel=true.
+ *
+ *
+ */
+static int mxc_nfc_scan(bool lowlevel)
 {
-       int addr, bad = -1;
-       int i, j;
-       u32 count1 = hal_timer_count(), count2;
+       u32 bad = 0, i;
+       u32 count1 = 0, count2 = 0;
+       u8 *buf = NULL;
+       struct nand_bbt_descr *td = g_mxc_nfc_bbt_main_descr;
+       struct nand_bbt_descr *md = g_mxc_nfc_bbt_mirror_descr;
 
-       g_nfc_scan_done = false;
+       nfc_printf(NFC_DEBUG_MAX, "%s()\n", __FUNCTION__);
+       mxc_nfc_scan_done = 0;
 
-#ifdef MXCFLASH_FLASH_BASED_BBT
-       bad = mxc_nfc_find_bbt(g_mxc_nfc_bbt_main_descr, g_mxc_nfc_bbt_mirror_descr);
-#endif
-       if (bad < 0) {
-               for (i = 0, j = 0, addr = 0; addr < NF_DEV_SZ; addr += NF_BLK_SZ, i++) {
-                       if (nfc_is_badblock(addr)) {
+       if (g_nfc_debug_measure) {
+               count1 = hal_timer_count();
+       }
+       // read out the last 4 blocks for marker
+       // need to keep where is the td and md block number
+       if (!lowlevel) {
+               struct nand_bbt_descr *bd;
+
+               diag_printf1("Searching for BBT in the flash ...\n");
+               if (mxc_nfc_search_bbt(td) != 0) {
+                       diag_printf("No main BBT found in flash\n");
+               }
+               if (md && mxc_nfc_search_bbt(md) != 0) {
+                       diag_printf("No mirror BBT found in flash\n");
+               }
+               if (td->pages == -1 && (!md || md->pages == -1)) {
+                       diag_printf("No BBT found. Need to do \"nand scan\" first\n");
+                       return -1;
+               }
+               if (td->pages >= 0 && (md == NULL || md->version <= td->version)) {
+                       bd = td;
+                       nfc_printf(NFC_DEBUG_MIN, "Using normal bbt at page %d\n", bd->pages);
+               } else if (md != NULL && md->pages >= 0) {
+                       bd = md;
+                       nfc_printf(NFC_DEBUG_MIN, "Using mirror bbt at page %d\n", bd->pages);
+               } else {
+                       diag_printf("** Error: Failed to read bbt from flash\n");
+                       return -1;
+               }
+               nfc_read_page(0, bd->pages, 0);
+               for (i = 0; i < NF_BLK_CNT; i++) {
+                       int res = mxc_nfc_isbad_bbt((u16 *)NAND_MAIN_BUF0, i);
+                       if (res) {
+                               // construct the bad block table
+                               mark_blk_bad(i, g_bbt, res);
                                bad++;
-                               nfc_update_blk_table(addr, true);
-                               if (verbose)
-                                       nfc_printf(NFC_DEBUG_DEF, "      block %d at 0x%08x\n",
-                                                          OFFSET_TO_BLOCK(addr), addr);
-                       } else {
-                               nfc_update_blk_table(addr, false);
-                               l_to_p_table[j] = i;
-                               j++;
                        }
                }
-       }
-#ifdef MXCFLASH_FLASH_BASED_BBT
-       if (mxc_nfc_create_bbt(g_mxc_nfc_bbt_main_descr, g_mxc_nfc_bbt_mirror_descr) == 0) {
-               bad++; /* account for reserved block for main bbt */
-       }
-       if (g_mxc_nfc_bbt_mirror_descr != NULL) {
-               if (mxc_nfc_create_bbt(g_mxc_nfc_bbt_mirror_descr, g_mxc_nfc_bbt_main_descr) == 0) {
-                       bad++; /* account for reserved block for mirror bbt */
+               buf = g_bbt;
+       } else {
+               diag_printf("Doing low level scan to construct BBT\n");
+               for (i = 0; i < NF_BLK_CNT; i++) {
+                       int res = nfc_is_badblock(i, buf);
+                       if (res) {
+                               // construct the bad block table
+                               if (!buf)
+                                       mark_blk_bad(i, g_bbt, res);
+                               bad++;
+                       }
                }
        }
-#endif
+       diag_printf1("Total bad blocks: %d\n", bad);
        if (g_nfc_debug_measure) {
                count2 = hal_timer_count();
-               diag_printf("counter1=%d, counter2=%d, diff=%d\n",
-                                       count1, count2, count2 - count1);
-               diag_printf("Using [diff * 1000000 / 32768] to get usec\n");
+               diag_printf("counter1=0x%x, counter2=0x%x, diff=0x%x (%u usec)\n",
+                                       count1, count2, count2 - count1,
+                                       (count2 - count1) * 1000000 / 32768);
        }
-       g_nfc_scan_done = true;
+       mxc_nfc_scan_done = 1;
        return bad;
 }
 
 ////////////////////////// "nand" commands support /////////////////////////
 // Image management functions
 local_cmd_entry("info",
-               "Show nand flash info (number of good/bad blocks)",
-               "[-f <raw address>] [-l <length>]",
-               nand_info,
-               NAND_cmds
+                               "Show nand flash info (number of good/bad blocks)",
+                               "",
+                               nand_info,
+                               NAND_cmds
                   );
 
 local_cmd_entry("show",
-               "Show a page main/spare areas or spare area only (-s)",
-               "-f <raw page address> [-s]",
-               nand_show,
-               NAND_cmds
+                               "Show a page main/spare areas or spare area only (-s)",
+                               "-f <raw page address> [-s]",
+                               nand_show,
+                               NAND_cmds
                   );
 
 local_cmd_entry("read",
-               "Read data from nand flash into RAM",
-               "-f <raw address> -b <memory_load_address> -l <image_length> [-c <col_addr>]",
-               nand_read,
-               NAND_cmds
+                               "Read data from nand flash into RAM",
+                               "-f <raw addr> -b <mem_load_addr> -l <byte len> [-c <col>]\n"
+                               "      Note -c is only for 2K-page for value <0, 2048+64-1>",
+                               nand_read,
+                               NAND_cmds
                   );
 
 local_cmd_entry("write",
-               "Write data from RAM into nand flash",
-               "-f <raw address> -b <memory_address> -l <image_length> [-c <col_addr>]",
-               nand_write,
-               NAND_cmds
+                               "Write data from RAM into nand flash",
+                               "-f <raw address> -b <memory_address> -l <image_length> [-c <col_addr>]",
+                               nand_write,
+                               NAND_cmds
                   );
 
 local_cmd_entry("erase",
-               "Erase nand flash contents",
-               "-f <raw address> -l <length> [-o] [-z] \n\
-                               -o: force erase (even for bad blocks) \n\
-                               -z: mark bad (testing only!)",
-               nand_erase,
-               NAND_cmds
+                               "Erase nand flash contents",
+                               "-f <raw address> -l <length> [-o]\n"
+                               "             -o: force erase (even for bad blocks)",
+                               nand_erase,
+                               NAND_cmds
                   );
 
-#if 0
-local_cmd_entry("format",
-               "Check ALL blocks with ECC disabled and Erase the entire NAND flash with ECC ",
-               "-f <raw address> -l <length> [-o] [-z] \n\
-                               -o: force erase (even for bad blocks) \n\
-                               -z: mark bad (testing only!)",
-               nand_format,
-               NAND_cmds
+local_cmd_entry("scan",
+                               "Scan bad blocks and may also save bad block table into the NAND flash.",
+                               "[-o] [-r]\n"
+                               "No argument: save existing bad block table (BBT)\n"
+                               "            -r: re-scan with ECC off and save BBT -- for brand NEW flash\n"
+                               "            -o: force erase all, reconstruct BBT (no ECC) and save BBT -- for development.",
+                               nand_scan,
+                               NAND_cmds
                   );
-#endif
+
 local_cmd_entry("debug",
-               "Various NAND debug features ",
-               "<0> min debug messages <default> \n\
-                        <1> med debug messages \n\
-                        <2> max debug messages \n\
-                        <3> enable(default)/disable h/w ECC for both r/w \n\
-                        <4> disable(default)/enable spare-only read \n\
-                        <9> enable/disable measurement \n\
-                        no parameter - display current debug setup",
-               nand_debug_fun,
-               NAND_cmds
-                  );
+                               "Various NAND debug features ",
+                               "<0> no debug messages <default>\n"
+                               "             <1> min debug messages\n"
+                               "             <2> med debug messages\n"
+                               "             <3> max debug messages\n"
+                               "             <4> enable(default)/disable h/w ECC for both r/w\n"
+                               "             <5> disable(default)/enalbe spare-only read\n"
+                               "             <9> enable/disable measurement\n"
+                               "             no parameter - display current debug setup",
+                               nand_debug_fun,
+                               NAND_cmds
+                               );
+
+local_cmd_entry("bad",
+                               "Mark bad block in BBT",
+                               "[-f <raw address>] [-b <block number>] [-c]\n"
+                               "           -c: clear bad block mark\n"
+                               "           -f and -b are mutually exclusive",
+                               nand_bad,
+                               NAND_cmds
+                               );
 
 // Define table boundaries
-CYG_HAL_TABLE_BEGIN(__NAND_cmds_TAB__, NAND_cmds);
-CYG_HAL_TABLE_END(__NAND_cmds_TAB_END__, NAND_cmds);
+CYG_HAL_TABLE_BEGIN( __NAND_cmds_TAB__, NAND_cmds);
+CYG_HAL_TABLE_END( __NAND_cmds_TAB_END__, NAND_cmds);
 
 extern struct cmd __NAND_cmds_TAB__[], __NAND_cmds_TAB_END__;
 
 // CLI function
 static cmd_fun do_nand_cmds;
 RedBoot_nested_cmd("nand",
-                                  "Utility function to NAND flash using raw address",
-                                  "{cmds}",
-                                  do_nand_cmds,
-                                  __NAND_cmds_TAB__, &__NAND_cmds_TAB_END__
-                                  );
+                  "Utility function to NAND flash using raw address",
+                  "{cmds}",
+                  do_nand_cmds,
+                  __NAND_cmds_TAB__, &__NAND_cmds_TAB_END__
+                 );
 
 static void nand_usage(char *why)
 {
@@ -1571,11 +1789,11 @@ static void nand_show(int argc, char *argv[])
        struct option_info opts[2];
 
        init_opts(&opts[0], 'f', true, OPTION_ARG_TYPE_NUM,
-                 (void *)&ra, (bool *)&flash_addr_set, "NAND FLASH memory byte address");
+                         &ra, &flash_addr_set, "NAND FLASH memory byte address");
        init_opts(&opts[1], 's', false, OPTION_ARG_TYPE_FLG,
-                 (void *)&spar_only, (bool *)0, "Spare only");
+                         &spar_only, NULL, "Spare only");
 
-       if (!scan_opts(argc, argv, 2, opts, 2, 0, 0, "")) {
+       if (!scan_opts(argc, argv, 2, opts, 2, 0, 0, 0)) {
                return;
        }
        if (!flash_addr_set) {
@@ -1585,14 +1803,13 @@ static void nand_show(int argc, char *argv[])
                curr_addr = ra;
        }
 
-       ra &= MXC_NAND_LA_MASK;
-
        if (ra % NF_PG_SZ) {
-               diag_printf("** Error: address not page aligned\n");
+               diag_printf("** Error: flash address must be page aligned\n");
                return;
        }
 
-       if (nfc_is_badblock(NFC_BLOCK_ALIGN(ra))) {
+       ra &= MXC_NAND_ADDR_MASK;
+       if (nfc_is_badblock(OFFSET_TO_BLOCK(ra), g_bbt)) {
                diag_printf("This is a bad block\n");
        }
 
@@ -1605,14 +1822,14 @@ static void nand_show(int argc, char *argv[])
 static void nand_read(int argc, char *argv[])
 {
        int len;
-       unsigned long mem_addr, ra, col;
+       u32 mem_addr, ra, col, i, pg_no, pg_off;
        bool mem_addr_set = false;
        bool flash_addr_set = false;
        bool length_set = false;
        bool col_set = false;
        struct option_info opts[4];
        int j = 0;
-       bool ecc_status = g_ecc_enable;;
+       bool ecc_status = g_ecc_enable;
 
        init_opts(&opts[0], 'b', true, OPTION_ARG_TYPE_NUM,
                          &mem_addr, &mem_addr_set, "memory base address");
@@ -1622,94 +1839,94 @@ static void nand_read(int argc, char *argv[])
                          &len, &length_set, "image length [in FLASH]");
        init_opts(&opts[3], 'c', true, OPTION_ARG_TYPE_NUM,
                          &col, &col_set, "column addr");
-       
+
        if (!scan_opts(argc, argv, 2, opts, 4, 0, 0, 0)) {
                nand_usage("invalid arguments");
                return;
        }
 
+       if (ra % NF_PG_SZ) {
+               diag_printf("** Error: flash address must be page aligned\n");
+               return;
+       }
+
        if (!mem_addr_set || !flash_addr_set || !length_set) {
-               nand_usage("required parameter missing");
+               nand_usage("** Error: required parameter missing");
                return;
        }
        if ((mem_addr < (CYG_ADDRESS)ram_start) ||
-               ((mem_addr + len) >= (CYG_ADDRESS)ram_end)) {
-               diag_printf("** WARNING: RAM address: %08lx may be invalid\n", mem_addr);
-               diag_printf("   valid range is %p-%p\n", ram_start, ram_end);
+               ((mem_addr+len) >= (CYG_ADDRESS)ram_end)) {
+               diag_printf("** WARNING: RAM address: 0x%08x may be invalid\n", mem_addr);
+               diag_printf("   valid range is 0x%p-0x%p\n", ram_start, ram_end);
        }
 
-       // Safety check - make sure the address range is not within the code we're running
-       if (flash_code_overlaps((void *)ra, (void *)(ra+len-1))) {
-               diag_printf("**Error: Can't program this region - contains code in use!\n");
-               return;
-       }
-       
        if (col_set) {
-               u32 flash_addr = ((ra / NF_PG_SZ) << NAND_PG_SHIFT) + col;
+               diag_printf("Random read at page %u, column 0x%04x\n",
+                                       ra / NF_PG_SZ, col);
 
-               diag_printf("Random read at page %ld, column %ld (addr %08x)\n",
-                                       ra / NF_PG_SZ, col, flash_addr);
-
-               if (g_is_2k_page) {
+               if (g_is_2k_page || g_is_4k_page) {
                        g_ecc_enable = false;
                }
-               nfc_read_pg_random(flash_addr, 0); // don't swap BI for 2k page
-               if (g_is_2k_page) {
+               nfc_read_pg_random(ra / NF_PG_SZ, col, ECC_FORCE_OFF, 0, num_of_nand_chips);
+               if (g_is_2k_page || g_is_4k_page) {
                        g_ecc_enable = ecc_status;
                }
-               
-               nfc_buf_mem_cpy((void *)mem_addr, (void *)NAND_MAIN_BUF0, NF_PG_SZ);
+               nfc_buf_read((void *)mem_addr, NAND_MAIN_BUF0, NF_PG_SZ);
                return;
        }
-       
+
        // ensure integer multiple of page size
        len = (len + NF_PG_SZ - 1) & ~(NF_PG_SZ - 1);
-       ra &= MXC_NAND_LA_MASK;
-
+       ra &= MXC_NAND_ADDR_MASK;
        do {
                if (OFFSET_TO_BLOCK(ra) > (NF_BLK_CNT - 1)) {
-                       diag_printf("Out of range: addr=0x%08lx\n", ra);
+                       diag_printf("\n** Error: flash address: 0x%08x out of range\n", ra);
                        return;
                }
-               if (nfc_read_page(ra) != 0) {
-                       diag_printf("** Error: uncorrectable ECC at addr 0x%08lx\n", ra);
-                       diag_printf("should invoke bad block management to replace this block\n");
-                       diag_printf("and then mark this block \"bad\". But Redboot doesn't do it yet.\n");
-               }
-               if ((j++ % 0x20) == 0)
-                       diag_printf("\n%s 0x%08lx: ", __FUNCTION__, ra);
-               diag_printf(".");
+               pg_no = ra / NF_PG_SZ;
+               pg_off = ra % NF_PG_SZ;
+               for (i = 0; i < num_of_nand_chips; i++) {
+                       if (nfc_read_page(i, pg_no, pg_off) != 0) {
+                               diag_printf("\n** Error: uncorrectable ECC at addr 0x%08x\n", ra);
+                               diag_printf("use 'nand bad -b %u' to mark this block in BBT\n",
+                                                       pg_no / NF_PG_PER_BLK);
+                       }
+                       if ((j++ % 0x20) == 0)
+                               diag_printf("\n%s 0x%08x: ", __FUNCTION__, ra);
+                       diag_printf(".");
 
-               nfc_buf_mem_cpy((void *)mem_addr, (void *)NAND_MAIN_BUF0, NF_PG_SZ);
+                       nfc_buf_read((void *)mem_addr, NAND_MAIN_BUF0, NF_PG_SZ / num_of_nand_chips);
 
-               ra += NF_PG_SZ;
-               mem_addr += NF_PG_SZ;
-               len -= NF_PG_SZ;
+                       ra += NF_PG_SZ / num_of_nand_chips;
+                       mem_addr += NF_PG_SZ / num_of_nand_chips;
+                       len -= NF_PG_SZ / num_of_nand_chips;
+                       pg_off = 0;
+               }
        } while (len > 0);
        diag_printf("\n");
 }
 
 static void nand_write(int argc, char *argv[])
 {
-       int len, j = 0;
-       u32 mem_addr, ra, col;
+       int len, len_st, j = 0;
+       u32 mem_addr, mem_addr_st, ra, col;
        bool mem_addr_set = false;
        bool flash_addr_set = false;
        bool length_set = false;
        bool col_set = false;
        struct option_info opts[4];
-       bool ecc_status = g_ecc_enable;;
+       bool ecc_status = g_ecc_enable;
+       int skip = 0;
 
        init_opts(&opts[0], 'b', true, OPTION_ARG_TYPE_NUM,
-                         (void *)&mem_addr, (bool *)&mem_addr_set, "memory base address");
+                         &mem_addr, &mem_addr_set, "memory base address");
        init_opts(&opts[1], 'f', true, OPTION_ARG_TYPE_NUM,
-                         (void *)&ra, (bool *)&flash_addr_set, "FLASH memory base address");
+                         &ra, &flash_addr_set, "FLASH memory base address");
        init_opts(&opts[2], 'l', true, OPTION_ARG_TYPE_NUM,
-                         (void *)&len, (bool *)&length_set, "image length [in FLASH]");
+                         &len, &length_set, "image length [in FLASH]");
        init_opts(&opts[3], 'c', true, OPTION_ARG_TYPE_NUM,
-                         (void *)&col, (bool *)&col_set, "column addr");
-       if (!scan_opts(argc, argv, 2, opts, 4, 0, 0, 0))
-       {
+                         &col, &col_set, "column addr");
+       if (!scan_opts(argc, argv, 2, opts, 4, 0, 0, 0)) {
                nand_usage("invalid arguments");
                return;
        }
@@ -1722,82 +1939,98 @@ static void nand_write(int argc, char *argv[])
        if ((mem_addr < (CYG_ADDRESS)ram_start) ||
                ((mem_addr+len) >= (CYG_ADDRESS)ram_end)) {
                diag_printf("** WARNING: RAM address: %p may be invalid\n", (void *)mem_addr);
-               diag_printf("   valid range is %p-%p\n", (void *)ram_start, (void *)ram_end);
+               diag_printf("   valid range is %p-%p\n", (void *)ram_start, (void *)ram_end);
        }
 
        if (col_set) {
-               u32 flash_addr = ((ra / NF_PG_SZ) << NAND_PG_SHIFT) + col;
+               diag_printf("Random write at page %u, column %u\n", ra / NF_PG_SZ, col);
 
-               diag_printf("Random write at page %d, column %d (addr %08x)\n",
-                                       ra / NF_PG_SZ, col, flash_addr);
-               
-               if (g_is_2k_page) {
+               if (g_is_2k_page || g_is_4k_page) {
                        g_ecc_enable = false;
                }
-               nfc_write_pg_random(flash_addr, mem_addr, NFC_MAIN_ONLY, 0);
-               if (g_is_2k_page) {
+               nfc_write_pg_random(ra / NF_PG_SZ, col, (u8 *)mem_addr, 0);
+               if (g_is_2k_page || g_is_4k_page) {
                        g_ecc_enable = ecc_status;
                }
                return;
        }
 
-       ra &= MXC_NAND_LA_MASK;
-
-       if ((len % NF_PG_SZ) != 0) {
-               diag_printf("Not a full page write?\n\n");
+       if ((ra % NF_PG_SZ) != 0) {
+               diag_printf("** Error: flash address must be page aligned\n");
+               return;
        }
 
+       mem_addr_st = mem_addr;
+       len_st = len;
+       ra &= MXC_NAND_ADDR_MASK;
        do {
                if (OFFSET_TO_BLOCK(ra) > (NF_BLK_CNT - 1)) {
-                       diag_printf("Out of range: addr=0x%08x\n", ra);
+                       diag_printf("Out of range: addr=0x%x\n", ra);
                        return;
                }
-               if (nfc_is_badblock(ra)) {
-                       diag_printf("\nERROR: bad block at raw addr=0x%08x(block=%d)\n",
-                                          ra, OFFSET_TO_BLOCK(ra));
-                       diag_printf("%s() failed\n", __FUNCTION__);
-                       return;
+               if (nfc_is_badblock(OFFSET_TO_BLOCK(ra), g_bbt)) {
+                       if (skip++ >= flash_dev_info->max_bad_blk) {
+                               diag_printf("\nToo many bad blocks encountered\n");
+                               return;
+                       }
+                       diag_printf("\nSkipping bad block %u at addr=0x%08llx\n",
+                                               OFFSET_TO_BLOCK(ra), (u64)ra);
+                       ra = (OFFSET_TO_BLOCK(ra) + 1) *  NF_BLK_SZ;
+                       continue;
                }
 
-               if (nfc_program_page(ra, mem_addr, NFC_MAIN_ONLY) != 0) {
+               if ((ra % NF_BLK_SZ) == 0) {
+                        mem_addr_st = mem_addr;
+                        len_st = len;
+               }
+               if (nfc_write_pg_random(ra / NF_PG_SZ, ra % NF_PG_SZ, (u8 *)mem_addr, 0) != 0) {
                        if (g_nfc_debug_level >= NFC_DEBUG_DEF) {
-                               diag_printf("Error %d: program error at addr 0x%08x\n", __LINE__, ra);
-                               diag_printf("should invoke bad block management to replace this block \n");
-                               diag_printf("and then mark this block \"bad\". But Redboot doesn't do it yet.\n");
+                               diag_printf("Warning %d: program error at addr 0x%x\n", __LINE__, ra);
                        }
-                       return;
+                       mark_blk_bad(OFFSET_TO_BLOCK(ra), g_bbt, BLK_BAD_RUNTIME);
+                       ra = (OFFSET_TO_BLOCK(ra) + 1) *  NF_BLK_SZ; //make sure block size aligned
+                       mem_addr = mem_addr_st; // rewind to blocl boundary
+                       len = len_st;
+                       continue;
                }
                if ((j++ % 0x20) == 0)
                        diag_printf("\nProgramming 0x%08x: ", ra);
                diag_printf(".");
-               
+
                len -= NF_PG_SZ;
                ra += NF_PG_SZ;
                mem_addr += NF_PG_SZ;
        } while (len > 0);
+       if (skip) {
+               diag_printf("\n%s(skip bad blocks=%d\n\n", __FUNCTION__, skip);
+       }
        diag_printf("\n");
 }
 
 void nand_debug_fun(int argc, char *argv[])
 {
        int opt;
+       const char *dbg_lvl_str;
 
        if (argc == 3) {
                opt = argv[2][0] - '0';
                switch (opt) {
                case 0:
-                       g_nfc_debug_level = NFC_DEBUG_MIN;
+                       g_nfc_debug_level = NFC_DEBUG_NONE;
                        break;
                case 1:
-                       g_nfc_debug_level = NFC_DEBUG_MED;
+                       g_nfc_debug_level = NFC_DEBUG_MIN;
                        break;
                case 2:
-                       g_nfc_debug_level = NFC_DEBUG_MAX;
+                       g_nfc_debug_level = NFC_DEBUG_MED;
                        break;
                case 3:
-                       g_ecc_enable = g_ecc_enable? false: true;
+                       g_nfc_debug_level = NFC_DEBUG_MAX;
                        break;
                case 4:
+                       g_ecc_enable = g_ecc_enable? false: true;
+                       break;
+               case 5:
                        // toggle g_spare_only_read_ok
                        g_spare_only_read_ok = g_spare_only_read_ok? false: true;
                        break;
@@ -1807,35 +2040,45 @@ void nand_debug_fun(int argc, char *argv[])
 
                default:
                        diag_printf("%s(%s) not supported\n", __FUNCTION__, argv[2]);
-                       break;
-
                }
        }
-       diag_printf("Current debug options are: \n");
-       diag_printf("    h/w ECC: %s\n", g_ecc_enable ? "on":"off");
-       diag_printf("    sp-only read: %s\n", g_spare_only_read_ok ? "on":"off");
-       diag_printf("    measurement: %s\n", g_nfc_debug_measure ? "on":"off");
-       diag_printf("    message level: %s\n", (g_nfc_debug_level == NFC_DEBUG_MIN) ? "min" : \
-               ((g_nfc_debug_level == NFC_DEBUG_MED) ? "med" : "max"));
+       switch (g_nfc_debug_level) {
+       case NFC_DEBUG_NONE:
+               dbg_lvl_str = "none";
+               break;
+       case NFC_DEBUG_MIN:
+               dbg_lvl_str = "min";
+               break;
+       case NFC_DEBUG_MED:
+               dbg_lvl_str = "med";
+               break;
+       case NFC_DEBUG_MAX:
+               dbg_lvl_str = "max";
+               break;
+       default:
+               dbg_lvl_str = "invalid";
+       }
+       diag_printf("Current debug options are:\n");
+       diag_printf("    h/w ECC: %s\n", g_ecc_enable ? "on" : "off");
+       diag_printf("    sp-only read: %s\n", g_spare_only_read_ok ? "on" : "off");
+       diag_printf("    measurement: %s\n", g_nfc_debug_measure ? "on" : "off");
+       diag_printf("    message level: %s\n", dbg_lvl_str);
 }
 
 static void nand_erase(int argc, char *argv[])
 {
-       u32 i, j = 0, len, ra;
+       u32 len, ra;
        bool faddr_set = false;
        bool force_erase_set = false;
-       bool force_bad_block_set = false;
        bool length_set = false;
        struct option_info opts[4];
 
        init_opts(&opts[0], 'f', true, OPTION_ARG_TYPE_NUM,
-                         &ra, (bool *)&faddr_set, "FLASH memory base address");
+                 &ra, &faddr_set, "FLASH memory base address");
        init_opts(&opts[1], 'l', true, OPTION_ARG_TYPE_NUM,
-                         &len, (bool *)&length_set, "length in bytes");
+                 &len, &length_set, "length in bytes");
        init_opts(&opts[2], 'o', false, OPTION_ARG_TYPE_FLG,
-                         &force_erase_set, (bool *)&force_erase_set, "force erases block");
-       init_opts(&opts[3], 'z', false, OPTION_ARG_TYPE_FLG,
-                         &force_bad_block_set, (bool *)&force_bad_block_set, "erases blocks and mark bad");
+                 &force_erase_set, &force_erase_set, "force erases block");
 
        if (!scan_opts(argc, argv, 2, opts, 4, 0, 0, 0)) {
                nand_usage("invalid arguments");
@@ -1849,154 +2092,172 @@ static void nand_erase(int argc, char *argv[])
        if ((ra % NF_BLK_SZ) != 0 ||
                (len % NF_BLK_SZ) != 0 || len == 0) {
                diag_printf("Address or length is not block aligned or length is zero!\n");
-               diag_printf("Block size is 0x%08x\n", NF_BLK_SZ);
+               diag_printf("Block size is 0x%x\n", NF_BLK_SZ);
                return;
        }
 
-       if (!verify_action("About to erase 0x%08x bytes from nand offset 0x%08x\n", len, ra)) {
+       if (!verify_action("About to erase 0x%x bytes from nand offset 0x%x\n", len, ra)) {
                diag_printf("** Aborted\n");
                return;
        }
 
-       ra &= MXC_NAND_LA_MASK;
-
        // now ra is block aligned
        if (force_erase_set == true) {
                diag_printf("Force erase ...");
-               for (i = ra; i < (ra + len); i += NF_BLK_SZ) {
-                       if (nfc_erase_blk(i) != 0) { //error
-                               diag_printf("\n**Error: could not erase block %d at address 0x%08x\n",
-                                                       i / NF_BLK_SZ, i);
-                               goto nand_erase_out;               //don't erase bad block
-                       } else {
-                               if ((j++ % 0x20) == 0)
-                                       diag_printf("\nErasing 0x%08x: ", i);
-                               diag_printf(".");
-                       }
-               }
+               nfc_erase_region(ra, len, 0, 1);
                diag_printf("\n");
-       } else if (force_bad_block_set == true) {
-               u16 temp_spare_buf[8] = {0, 0, 0, 0, 0, 0, 0, 0};
-               
-               for (i = ra; i < (ra + len); i += NF_BLK_SZ) {
-                       if (i == 0) {
-                               continue;
-                       }
-                       if (nfc_is_badblock(i)) {
-                               diag_printf("block at 0x%08x is already bad\n", i);
-                               continue;                       //don't erase bad block
-                       }
-                       diag_printf("Erasing ... \n");
+       } else {
+               nfc_erase_region(ra, len, 1, 1);
+       }
+       diag_printf("\n");
+}
 
-                       if (nfc_erase_blk(i) != 0) { //error
-                               diag_printf("\n**Error: could not erase block %d at address 0x%08x\n",
-                                                       i / NF_BLK_SZ, i);
-                               goto nand_erase_out;               //don't erase bad block
-                       }
+extern void romupdate(int argc, char *argv[]);
+static void nand_scan(int argc, char *argv[])
+{
+       bool force_erase = false;
+       bool force_rescan = false;
+       struct option_info opts[2];
 
-                       diag_printf("\nMarking bad block at: 0x%08x\n", i);
+       init_opts(&opts[0], 'o', false, OPTION_ARG_TYPE_FLG,
+                 &force_erase, NULL, "force erases block first");
 
-                       if (nfc_program_page(i, (u32)temp_spare_buf, NFC_SPARE_ONLY) != 0) {
-                               diag_printf("**Error: Can't program block %d at address 0x%08x\n",
-                                                       i / NF_BLK_SZ, i);
-                               continue;
-                       }
-               }
-       } else {
-               for (i = ra; i < (ra + len); i += NF_BLK_SZ) {
-                       if (nfc_is_badblock(i)) {
-                               diag_printf("\nWarning: Skipping erase of bad/reserved block %d at address 0x%08x\n",
-                                                       i / NF_BLK_SZ, i);
-                               continue;                       //don't erase bad block
-                       }
-                       if (nfc_erase_blk(i) != 0) { //error
-                               diag_printf("\n**Error: could not erase block %d at address 0x%08x\n",
-                                                       i / NF_BLK_SZ, i);
-                               continue;                       //don't erase bad block
-                       }
-                       if ((j++ % 0x20) == 0)
-                               diag_printf("\nErasing 0x%08x: ", i);
-                       diag_printf(".");
+       init_opts(&opts[1], 'r', false, OPTION_ARG_TYPE_FLG,
+                 &force_rescan, NULL, "force low level re-scan");
+
+       if (!scan_opts(argc, argv, 2, opts, 2, 0, 0, 0)) {
+               nand_usage("invalid arguments");
+               return;
+       }
+
+       if (!force_erase && !force_rescan && !mxc_nfc_scan_done) {
+               diag_printf("Need to build BBT first with \"nand scan [-o|-r]\"\n");
+               return;
+       }
+       if (force_erase) {
+               void *bbt = g_bbt;
+
+               diag_printf("Force erase first ...\n");
+               g_bbt = NULL;
+               // do force erase, skipping bad blocks. After this call, g_bbt should be re-built
+               // for the whole NAND flash.
+               if (nfc_erase_region(0, NF_DEV_SZ, true, false) != 0) {
+                       g_bbt = bbt;
+                       return;
                }
+               g_bbt = bbt;
+               mxc_nfc_scan_done = 0;
+               diag_printf("\n");
+       }
+       if (force_rescan) {
+               diag_printf("Force re-scan ...\n");
+               memset(g_bbt, 0, g_bbt_sz);
+               mxc_nfc_scan(true);
+       }
+       // program g_bbt into the flash
+       diag_printf("Writing BBT to flash\n");
+       if (program_bbt_to_flash() != 0) {
+               diag_printf("Error: Failed to write BBT to flash\n");
+       }
+       if (force_erase) {
+               romupdate(0, NULL);
        }
-nand_erase_out:
-       diag_printf("\n");
-       mxc_nfc_scan(false);
 }
 
-static int nfc_dump_bad_blocks(unsigned long ra, u32 len)
+static void nand_info(int argc, char *argv[])
 {
-       int i, j = 0;
-
-       for (i = 0; i < ((len + NF_BLK_SZ - 1) / NF_BLK_SZ); i++) {
-               u8 code = nfc_is_badblock(NFC_BLOCK_ALIGN(ra));
-               if (code != 0) {
-                       diag_printf("block %ld at offset 0x%08lx is %s\n",
-                                               OFFSET_TO_BLOCK(ra), ra,
-                                               code == 2 ? "reserved" : "bad");
+       u32 i, j = 0;
+
+       if (nand_flash_index == -1) {
+               diag_printf("Can't find valid NAND flash: %d\n", __LINE__);
+               return;
+       }
+
+       diag_printf("\nType:\t\t %s\n", NF_VEND_INFO);
+       diag_printf("Total size:\t 0x%08x bytes (%d MiB)\n", NF_DEV_SZ, NF_DEV_SZ / SZ_1M);
+       diag_printf("Total blocks:\t 0x%x (%d)\n", NF_BLK_CNT, NF_BLK_CNT);
+       diag_printf("Block size:\t 0x%x (%d)\n", NF_BLK_SZ, NF_BLK_SZ);
+       diag_printf("Page size:\t 0x%x (%d)\n", NF_PG_SZ, NF_PG_SZ);
+       diag_printf("Spare size:\t 0x%x (%d)\n", NF_SPARE_SZ, NF_SPARE_SZ);
+       diag_printf("Pages per block: 0x%x (%d)\n", NF_PG_PER_BLK, NF_PG_PER_BLK);
+
+       if (mxc_nfc_scan(false) == -1) {
+               return;
+       }
+       diag_printf("\n");
+       for (i = 0; i < NF_BLK_CNT; i++) {
+               int res = nfc_is_badblock(i, g_bbt);
+               if (res & ~BLK_RESERVED) {
+                       diag_printf("block %d at offset 0x%x is a %s bad block\n",
+                                               i, i * NF_BLK_SZ, res == BLK_BAD_FACTORY ? "factory" : "runtime");
                        j++;
                }
-               ra += NF_BLK_SZ;
        }
-       return j;
+       diag_printf("==================================\n");
+       diag_printf("Found %d bad block(s) out of %d\n", j, i);
 }
 
-static void nand_info(int argc, char *argv[])
+static void nand_bad(int argc, char *argv[])
 {
-       u32 i, j = 0, len, ra;
-       bool flash_addr_set = false;
-       bool flash_len_set = false;
-       struct option_info opts[2];
+       u32 ra;
+       u32 block;
+       bool ra_set = false;
+       bool block_set = false;
+       bool clear = false;
+       struct option_info opts[3];
+       int bad;
 
        init_opts(&opts[0], 'f', true, OPTION_ARG_TYPE_NUM,
-                         &ra, &flash_addr_set, "NAND FLASH memory byte address");
-       init_opts(&opts[1], 'l', true, OPTION_ARG_TYPE_NUM,
-                         &len, &flash_len_set, "length");
+                         &ra, &ra_set, "FLASH memory base address");
+       init_opts(&opts[1], 'b', true, OPTION_ARG_TYPE_NUM,
+                         &block, &block_set, "block number");
+       init_opts(&opts[2], 'c', false, OPTION_ARG_TYPE_FLG,
+                         &clear, NULL, "clear bad block marker");
 
-       if (!scan_opts(argc, argv, 2, opts, 2, 0, 0, 0)) {
+       if (!scan_opts(argc, argv, 2, opts, NUM_ELEMS(opts), NULL, 0, NULL)) {
                nand_usage("invalid arguments");
                return;
        }
 
-       if (nand_flash_index == -1) {
-               diag_printf("Can't find valid NAND flash: %d\n", __LINE__);
+       if (!ra_set && !block_set) {
+               nand_usage("missing argument");
                return;
        }
-
-       i = mxc_nfc_scan(true);
-       if (!flash_addr_set) {
-               diag_printf("\nType: %s\n", flash_dev_info->vendor_info);
-               diag_printf("Total size:\t 0x%08x bytes (%d MB)\n", NF_DEV_SZ, NF_DEV_SZ / 0x100000);
-               diag_printf("Total blocks:\t 0x%x (%d)\n", NF_BLK_CNT, NF_BLK_CNT);
-               diag_printf("Block size:\t 0x%x (%d)\n", NF_BLK_SZ, NF_BLK_SZ);
-               diag_printf("Page size:\t 0x%x (%d)\n", NF_PG_SZ, NF_PG_SZ);
-               diag_printf("Pages per block: 0x%x (%d)\n", NF_PG_PER_BLK, NF_PG_PER_BLK);
-
-               diag_printf("Bad blocks: \n");
-
-               if (i == 0) {
-                       diag_printf("  none\n");
-               } else {
-                       nfc_dump_bad_blocks(0, NF_DEV_SZ);
-                       diag_printf("\nTotal number of bad/reserved blocks: %d\n", i);
-               }
+       if (ra_set && block_set) {
+               nand_usage("options -f and -b are mutually exclusive");
                return;
+       } else if (ra_set) {
+               block = OFFSET_TO_BLOCK(ra & MXC_NAND_ADDR_MASK);
+       } else {
+               ra = BLOCK_TO_OFFSET(block) + (unsigned long)flash_info.start;
        }
-
-       if (!flash_len_set) {
-               len = NF_DEV_SZ;
+       if ((ra % NF_BLK_SZ) != 0) {
+               diag_printf("Address is not block aligned!\n");
+               diag_printf("Block size is 0x%08x\n", NF_BLK_SZ);
+               return;
        }
 
-       ra &= MXC_NAND_LA_MASK;
-
-       if (ra % NF_BLK_SZ) {
-               diag_printf("** Error: address 0x%08x not aligned to block boundary\n", ra);
+       bad = nfc_is_badblock(block, g_bbt);
+       if ((bad && !clear) || (!bad && clear)) {
+               diag_printf("block %5u at address 0x%08x is already %s\n",
+                                       block, ra, bad ? "bad" : "good");
                return;
        }
-       diag_printf("\n");
-       j = nfc_dump_bad_blocks(0, NF_DEV_SZ);
-       diag_printf("==================================\n");
-       diag_printf("Found %d bad block(s) out of %d\n", j, (len + NF_BLK_SZ - 1) / NF_BLK_SZ);
+       if (clear && bad != BLK_BAD_RUNTIME) {
+               diag_printf("Refusing to mark a factory bad block as good!\n");
+               return;
+       }
+       if (!verify_action("Mark block %u at address 0x%08x %s in BBT",
+                                          block, ra, clear ? "good" : "bad")) {
+               diag_printf("** Aborted\n");
+               return;
+       }
+
+       nfc_printf(NFC_DEBUG_MIN, "Marking block %5u at 0x%08x %s\n",
+                          block, ra, clear ? "good" : "bad");
+       mark_blk_bad(block, g_bbt, clear ? 0 : BLK_BAD_RUNTIME);
+       mxc_nfc_update_bbt(g_mxc_nfc_bbt_main_descr,
+                                          g_mxc_nfc_bbt_mirror_descr);
 }
 
 static void do_nand_cmds(int argc, char *argv[])
@@ -2004,7 +2265,11 @@ static void do_nand_cmds(int argc, char *argv[])
        struct cmd *cmd;
 
        if (!mxcnfc_init_ok) {
-               diag_printf("\nWarning:NAND flash hasn't been initialized. Try \"factive nand\" first\n\n");
+#ifdef CYGHWR_DEVS_FLASH_MXC_MULTI
+               diag_printf("Warning: NAND flash hasn't been initialized. Try \"factive nand\" first\n\n");
+#else
+               diag_printf("Error: NAND flash hasn't been initialized\n");
+#endif
                return;
        }
 
@@ -2012,9 +2277,10 @@ static void do_nand_cmds(int argc, char *argv[])
                nand_usage("too few arguments");
                return;
        }
+
        if ((cmd = cmd_search(__NAND_cmds_TAB__, &__NAND_cmds_TAB_END__,
-                                                 argv[1])) != (struct cmd *)0) {
-               (cmd->fun)(argc, argv);
+                                                 argv[1])) != NULL) {
+               cmd->fun(argc, argv);
                return;
        }
        nand_usage("unrecognized command");
@@ -2025,79 +2291,66 @@ static void do_nand_cmds(int argc, char *argv[])
  * @param pkt  pointer to the starting address of the memory
  * @param len  byte length of the buffer to be displayed
  */
-static void print_pkt_16(u16pkt, u32 len)
+static void print_pkt_16(u16 *pkt, u32 len)
 {
        diag_printf("******************** %d bytes********************\n", len);
        u32 i = 0, tempLen = (len + 1) / 2;
 
-       while (tempLen >= 0) {
+       while (tempLen != 0) {
                if (tempLen >= 8) {
-                       diag_printf("[%03x-%03x] ", i*2, ((i*2)+14));
+                       diag_printf("[%03x-%03x] ", i * 2, (i * 2) + 14);
                        diag_printf("%04x %04x %04x %04x %04x %04x %04x %04x\n",
-                                               pkt[i], pkt[i+1], pkt[i+2], pkt[i+3],
-                                               pkt[i+4], pkt[i+5], pkt[i+6], pkt[i+7]);
+                                               pkt[i], pkt[i + 1], pkt[i + 2], pkt[i + 3],
+                                               pkt[i + 4], pkt[i + 5], pkt[i + 6], pkt[i + 7]);
+                       tempLen -= 8;
+                       i += 8;
                } else {
-                       if (tempLen == 0) {
-                               diag_printf("*************************************************\n");
-                               return;
-                       }
-                       diag_printf("[%03x-%03x] ", i*2, ((i*2)+14));
-                       switch(tempLen) {
-                               case 1:
-                                       diag_printf("%04x\n", pkt[i]);
-                                       break;
-                               case 2:
-                                       diag_printf("%04x %04x\n", pkt[i], pkt[i+1]);
-                                       break;
-                               case 3:
-                                       diag_printf("%04x %04x %04x\n", pkt[i], pkt[i+1], pkt[i+2]);
-                                       break;
-                               case 4:
-                                       diag_printf("%04x %04x %04x %04x\n", pkt[i],pkt[i+1], pkt[i+2],pkt[i+3]);
-                                       break;
-                               case 5:
-                                       diag_printf("%04x %04x %04x %04x %04x\n", pkt[i], pkt[i+1], pkt[i+2], pkt[i+3],pkt[i+4]);
-                                       break;
-                               case 6:
-                                       diag_printf("%04x %04x %04x %04x %04x %04x\n", pkt[i], pkt[i+1], pkt[i+2], pkt[i+3],pkt[i+4],
-                                                        pkt[i+5]);
-                                       break;
-                               case 7:
-                                       diag_printf("%04x %04x %04x %04x %04x %04x %04x\n", pkt[i], pkt[i+1], pkt[i+2], pkt[i+3],pkt[i+4],
-                                                        pkt[i+5], pkt[i+6]);
-                                       break;
+                       if (tempLen != 0) {
+                               diag_printf("[%03x-%03x]", i * 2, (i + tempLen) * 2);
+                               while (tempLen-- != 0) {
+                                       diag_printf(" %04x", pkt[i++]);
+                               }
+                               diag_printf("\n");
                        }
+                       diag_printf("*************************************************\n");
+                       return;
                }
-               tempLen -= 8;
-               i += 8;
        }
 }
 
 // addr = starting byte address within NAND flash
 static void print_page(u32 addr, bool spare_only)
 {
+       u32 i, pg_no, pg_off;
        u32 blk_num = OFFSET_TO_BLOCK(addr), pg_num = OFFSET_TO_PAGE(addr);
 
+       if (addr % NF_PG_SZ) {
+               diag_printf("Non page-aligned read not supported here: 0x%x\n", addr);
+               return;
+       }
        if (spare_only) {
-               if (nfc_read_page_sp(addr) != 0) {
-                       diag_printf("Error %d: uncorrectable. But still printing ...\n", __LINE__);
-               }
+               diag_printf("Error %d: Not supported\n", __LINE__);
+               return;
        } else {
-               if (nfc_read_page(addr) != 0) {
-                       diag_printf("Error %d: uncorrectable. But still printing ...\n", __LINE__);
-               }
-       }
+               pg_no = addr / NF_PG_SZ;
+               pg_off = addr % NF_PG_SZ;
+               for (i = 0; i < num_of_nand_chips; i++) {
+                       if (nfc_read_page(i, pg_no, pg_off) != 0) {
+                               diag_printf("Error %d: uncorrectable. But still printing ...\n", __LINE__);
+                       }
+                       pg_off = 0;
+                       diag_printf("\n============ Printing block(%d) page(%d)  ==============\n",
+                                                                 blk_num, pg_num);
 
-       diag_printf("\n============ Printing block(%d) page(%d)  ==============\n",
-                               blk_num, pg_num);
+                       diag_printf("<<<<<<<<< spare area >>>>>>>>>\n");
+                       print_pkt_16((u16*)NAND_SPAR_BUF0, NF_SPARE_SZ);
 
-       diag_printf("<<<<<<<<< spare area >>>>>>>>>\n");
-       print_pkt_16((u16*)(NAND_SPAR_BUF0), g_is_2k_page ? 64 : 16);
+                       if (!spare_only) {
+                               diag_printf("<<<<<<<<< main area >>>>>>>>>\n");
+                               print_pkt_16((u16*)NAND_MAIN_BUF0, NF_PG_SZ / num_of_nand_chips);
+                       }
 
-       if (!spare_only) {
-               diag_printf("<<<<<<<<< main area >>>>>>>>>\n");
-               print_pkt_16((u16*)(NAND_MAIN_BUF0), NF_PG_SZ);
+                       diag_printf("\n");
+               }
        }
-
-       diag_printf("\n");
 }