]> git.karo-electronics.de Git - karo-tx-redboot.git/blobdiff - packages/devs/flash/arm/mxc/v2_0/src/mxc_nfc.c
TX53 Release 2011-06-16
[karo-tx-redboot.git] / packages / devs / flash / arm / mxc / v2_0 / src / mxc_nfc.c
index c1f704b099ec791a7378c93522398f867430441b..7b19ff0ef19488de8046f03c2bdb61a9a955644e 100644 (file)
@@ -84,6 +84,7 @@
 #include <pkgconf/hal.h>
 #include <cyg/hal/hal_arch.h>
 #include <cyg/hal/hal_cache.h>
+#include <cyg/hal/hal_misc.h>
 #include <cyg/io/nand_bbt.h>
 #include <redboot.h>
 #include <stdlib.h>
@@ -94,7 +95,9 @@
 #define         _FLASH_PRIVATE_
 #include <cyg/io/flash.h>
 
+#ifdef CYGHWR_FLASH_NAND_BBT_HEADER
 #include CYGHWR_FLASH_NAND_BBT_HEADER
+#endif
 
 #include <cyg/io/imx_nfc.h>
 
@@ -118,8 +121,8 @@ CYG_MACRO_END
 #define MXC_UNLOCK_BLK_END             0xFFFF
 
 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_program_region(flash_addr_t addr, u8 *buf, u32 len);
+static 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,
@@ -134,10 +137,9 @@ 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_NONE;
+static int g_nfc_debug_level = NFC_DEBUG_DEF;
 static bool g_nfc_debug_measure = false;
 static bool g_is_2k_page = false;
 static unsigned int g_block_offset;
@@ -165,7 +167,7 @@ CYG_MACRO_END
 #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)
+#define write_nfc_ip_reg(a, b)         CYG_EMPTY_STATEMENT
 #endif
 
 #ifndef MXCFLASH_SELECT_MULTI
@@ -175,10 +177,19 @@ void nandflash_query(void *data)
 #endif
 {
        u32 id[2];
+
+       nfc_printf(NFC_DEBUG_MIN, "%s@%d data=%p\n", __FUNCTION__, __LINE__, data);
+
        read_nflash_id(&id[0], 0);
-       nfc_printf(NFC_DEBUG_MAX, "%s(ID=0x%02x: 0x%02x, 0x%02x, 0x%02x)\n", __FUNCTION__,
+       nfc_printf(NFC_DEBUG_MIN, "%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));
+       if (data != NULL) {
+               nfc_printf(NFC_DEBUG_MAX, "%s@%d copy flash ID from %p to %p\n",
+                               __FUNCTION__, __LINE__, &id[0], data);
+               memcpy(data, id, sizeof(id));
+       }
+       nfc_printf(NFC_DEBUG_MAX, "%s@%d called from %p\n", __FUNCTION__, __LINE__,
+                       __builtin_return_address(0));
 }
 
 #ifndef MXCFLASH_SELECT_MULTI
@@ -279,21 +290,21 @@ 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 NUM_ELEMS(supported_devices)
-
-#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) * 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 NUM_DEVICES                                            NUM_ELEMS(supported_devices)
+
+#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 * 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)
@@ -304,7 +315,7 @@ static const flash_dev_info_t supported_devices[] = {
 
 static u8 *g_bbt, *g_page_buf;
 static u32 g_bbt_sz;
-static bool mxcnfc_init_ok = false;
+static bool mxcnfc_init_ok;
 static bool mxc_nfc_scan_done;
 
 // this callback allows the platform specific function to be called right
@@ -322,7 +333,7 @@ static int flash_enable;
 static flash_addr_t nfc_l_to_p(flash_addr_t addr)
 {
        if (g_block_offset == 0) {
-               return addr;
+               return addr & MXC_NAND_ADDR_MASK;
        } else {
                flash_addr_t ra;
                u32 block = (addr & MXC_NAND_ADDR_MASK) / NF_BLK_SZ;
@@ -340,19 +351,26 @@ static flash_addr_t nfc_l_to_p(flash_addr_t addr)
 
 static int flash_addr_valid(flash_addr_t addr)
 {
+       if (!flash_enable) {
+               nfc_printf(NFC_DEBUG_MIN, "No flash area enabled\n");
+               return 1;
+       }
        if (addr < flash_region_start || addr >= flash_region_end) {
                diag_printf("Flash address 0x%08llx is outside valid region 0x%08llx..0x%08llx\n",
                                        (u64)addr, (u64)flash_region_start, (u64)flash_region_end);
+               return 0;
        }
-       return addr >= flash_region_start && addr < flash_region_end;
+       return 1;
 }
 
 /* FIXME: we should pass flash_addr_t as arguments */
 void mxc_flash_enable(void *start, void *end)
 {
        flash_addr_t s = (unsigned long)start & MXC_NAND_ADDR_MASK;
-       flash_addr_t e = (unsigned long)end & MXC_NAND_ADDR_MASK;
+       flash_addr_t e = ((unsigned long)end - 1) & MXC_NAND_ADDR_MASK;
 
+       if (start == end)
+               return;
        if (flash_enable++ == 0) {
                flash_region_start = s;
                flash_region_end = e;
@@ -371,8 +389,10 @@ void mxc_flash_enable(void *start, void *end)
 void mxc_flash_disable(void *start, void *end)
 {
        flash_addr_t s = (unsigned long)start & MXC_NAND_ADDR_MASK;
-       flash_addr_t e = (unsigned long)end & MXC_NAND_ADDR_MASK;
+       flash_addr_t e = ((unsigned long)end - 1) & MXC_NAND_ADDR_MASK;
 
+       if (start == end)
+               return;
        if (flash_enable) {
                if (--flash_enable == 0) {
                        diag_printf1("Disabling flash region 0x%08llx..0x%08llx\n",
@@ -408,6 +428,7 @@ nandflash_hwr_init(void)
 
        // Look through table for device data
        flash_dev_query(&id[0]);
+
        flash_dev_info = supported_devices;
        for (i = 0; i < NUM_DEVICES; i++) {
                if ((flash_dev_info->device_id == (id[0] & 0xffff)) &&
@@ -424,7 +445,6 @@ nandflash_hwr_init(void)
                return FLASH_ERR_DRV_WRONG_PART;
        }
 
-       nand_flash_index = i;
        mxcnfc_init_ok = true;
 
        if (NF_PG_SZ == 2048) {
@@ -439,12 +459,12 @@ nandflash_hwr_init(void)
        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) {
+       if (nfc_setup)
                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);
-       }
+                                                               flash_dev_info->type, num_of_nand_chips);
+
        diag_printf1("NFC version: %02x\n", g_nfc_version);
-       if (g_nfc_version == MXC_NFC_V3) {
+       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);
@@ -605,6 +625,8 @@ static void nfc_buf_write(unsigned long dst, void *src, u32 len)
        if (!nfc_verify_addr(dst, len)) {
                return;
        }
+       diag_printf1("Copying %u byte from %p..%p to flash buffer %08lx..%08lx\n",
+                               len, bp, bp + len - 1, dst, dst + len - 1);
        if (dst & 1) {
                store_byte(d, 1, *bp);
                d++;
@@ -676,7 +698,7 @@ 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",
+       nfc_printf(NFC_DEBUG_MIN, "%s: read flash id from chip %d @ %p\n",
                           __FUNCTION__, cs_line, ptr);
 
        NFC_PRESET(MXC_UNLOCK_BLK_END);
@@ -686,15 +708,15 @@ static void read_nflash_id(u32 *id, u32 cs_line)
        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++;
+       *id++ = *ptr++;
+       *id++ = *ptr++;
 }
 
 static void mark_blk_bad(unsigned int block, unsigned char *buf,
                                                 enum blk_bad_type bad_type)
 {
-       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 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) {
@@ -739,7 +761,7 @@ static int nfc_is_badblock(u32 block, u8 *buf)
                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",
+                       diag_printf1("Block %u at 0x%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);
@@ -798,6 +820,19 @@ static int nfc_is_badblock(u32 block, u8 *buf)
        return 0;
 }
 
+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;
+       }
+}
+
+#ifdef CYGHWR_FLASH_NAND_BBT_HEADER
 /*
  * check_short_pattern - [GENERIC] check if a pattern is in the buffer
  * @buf:       the buffer to search
@@ -851,18 +886,6 @@ static int mxc_nfc_write_bbt_page(struct nand_bbt_descr *td)
        return 0;
 }
 
-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;
-       }
-}
-
 static int mxc_nfc_write_bbt(struct nand_bbt_descr *td, struct nand_bbt_descr *md)
 {
        int ret = -1;
@@ -961,6 +984,12 @@ static int program_bbt_to_flash(void)
 {
        return mxc_nfc_update_bbt(g_mxc_nfc_bbt_main_descr, g_mxc_nfc_bbt_mirror_descr);
 }
+#else
+static int program_bbt_to_flash(void)
+{
+       return 0;
+}
+#endif
 
 /*!
  * Unconditionally erase a block without checking the BI field.
@@ -975,15 +1004,15 @@ static int nfc_erase_blk(u32 ra)
        u16 flash_status, i;
        u32 pg_no, pg_off;
 
-       if (g_nfc_version == MXC_NFC_V3) {
+       if (g_nfc_version >= MXC_NFC_V3) {
                // combine the two commands for erase
-               writel((FLASH_Start_Erase << 8) | FLASH_Block_Erase, NAND_CMD_REG);
+               nfc_reg_write((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);
+                       nfc_reg_write(NAND_LAUNCH_AUTO_ERASE, NAND_LAUNCH_REG);
                        wait_op_done();
                        pg_off = 0;
                }
@@ -1037,7 +1066,11 @@ static int nfc_program_blk(u32 ra, u8 *buf, u32 len)
                buf += NF_PG_SZ;
        }
        if (len != 0) {
+               diag_printf1("Clearing flash buffer from %p..%p\n", g_page_buf + len - 1,
+                                       g_page_buf + NF_PG_SZ - 1);
                memset(g_page_buf + len, 0xFF, NF_PG_SZ - len);
+               diag_printf1("Copying partial page from %p..%p to %p..%p\n",
+                                       buf, buf + len - 1, g_page_buf, g_page_buf + 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;
@@ -1058,7 +1091,7 @@ static int nfc_program_blk(u32 ra, u8 *buf, u32 len)
  *
  * @return                             FLASH_ERR_OK (0) if successful; non-zero otherwise
  */
-int nfc_erase_region(flash_addr_t addr, u32 len, bool skip_bad, bool verbose)
+static int nfc_erase_region(flash_addr_t addr, u32 len, bool skip_bad, bool verbose)
 {
        u32 sz, blk, update = 0, j = 0;
 
@@ -1073,9 +1106,12 @@ int nfc_erase_region(flash_addr_t addr, u32 len, bool skip_bad, bool verbose)
                diag_printf("Error: invalid length %u (must be > 0 and block aligned)\n", len);
                return FLASH_ERR_INVALID;
        }
-       addr = nfc_l_to_p(addr);
+       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) {
+               if (!flash_addr_valid(addr)) {
+                       return 0;
+               }
                blk = OFFSET_TO_BLOCK(addr);
                if (skip_bad && nfc_is_badblock(blk, g_bbt)) {
                        diag_printf("\nSkipping bad block %u at addr 0x%08llx\n",
@@ -1084,7 +1120,7 @@ int nfc_erase_region(flash_addr_t addr, u32 len, bool skip_bad, bool verbose)
                }
                if (nfc_erase_blk(addr) != 0) {
                        diag_printf("\n** Error: Failed to erase block %u at addr 0x%08llx\n",
-                                           blk, (u64)addr);
+                                               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.
@@ -1114,7 +1150,7 @@ int nfc_erase_region(flash_addr_t addr, u32 len, bool skip_bad, bool verbose)
  * @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)
+static int nfc_program_region(flash_addr_t addr, u8 *buf, u32 len)
 {
        u32 sz, blk, update = 0, partial_block_size;
 
@@ -1130,8 +1166,9 @@ int nfc_program_region(flash_addr_t addr, u8 *buf, u32 len)
                return FLASH_ERR_INVALID;
        }
 
-       partial_block_size = addr % NF_BLK_SZ;
+       partial_block_size = NF_BLK_SZ - (addr % NF_BLK_SZ);
 
+       mxc_nfc_buf_clear(NAND_SPAR_BUF0, 0xff, NF_SPARE_SZ);
        addr = nfc_l_to_p(addr);
        while (1) {
                if (!flash_addr_valid(addr)) {
@@ -1142,6 +1179,7 @@ int nfc_program_region(flash_addr_t addr, u8 *buf, u32 len)
                blk = OFFSET_TO_BLOCK(addr);
                if (nfc_is_badblock(blk, g_bbt)) {
                        diag_printf("\nSkipping bad block %u at addr 0x%08llx\n", blk, addr);
+                       g_block_offset++;
                        goto incr_address;
                }
 
@@ -1154,6 +1192,7 @@ int nfc_program_region(flash_addr_t addr, u8 *buf, u32 len)
                        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 program error again.
+                       g_block_offset++;
                        goto incr_address;
                }
                diag_printf(".");
@@ -1166,7 +1205,6 @@ int nfc_program_region(flash_addr_t addr, u8 *buf, u32 len)
 incr_address:
                addr += partial_block_size;
                partial_block_size = NF_BLK_SZ;
-               g_block_offset++;
        }
        if (update) {
                if (program_bbt_to_flash() != 0) {
@@ -1196,13 +1234,13 @@ int nfc_read_region(flash_addr_t addr, u8 *buf, u32 len)
        nfc_printf(NFC_DEBUG_MED, "%s: addr=0x%08llx, offset=%03x buf=0x%p, len=0x%08x\n",
                           __FUNCTION__, addr, offset, buf, len);
 
-       addr = nfc_l_to_p(addr);
        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;
        }
 
+       addr = nfc_l_to_p(addr);
        while (len > 0) {
                int i;
 
@@ -1283,11 +1321,21 @@ static int nfc_write_pg_random(u32 pg_no, u32 pg_off, u8 *buf, u32 ecc_force)
        }
 
        diag_printf1("%s(0x%x, 0x%x, %d)\n", __FUNCTION__, pg_no, pg_off, ecc_force);
+       if (g_nfc_version != MXC_NFC_V1) {
+               int i;
 
-       switch (g_nfc_version & 0xf0) {
-       case MXC_NFC_V3:
+               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);
+               }
+       }
+       if (g_nfc_version >= MXC_NFC_V3) {
                /* Check if Page size is greater than NFC buffer */
                do {
+                       rba = nfc_reg_read(NAND_CONFIGURATION1_REG);
+                       if ((rba >> 4) & 0x7) {
+                               nfc_reg_write(rba & ~0x70, NAND_CONFIGURATION1_REG);
+                       }
                        if (write_count <= NFC_BUFSIZE) {
                                // No need to worry about the spare area
                                nfc_buf_write(NAND_MAIN_BUF0, buf, write_count);
@@ -1299,7 +1347,7 @@ static int nfc_write_pg_random(u32 pg_no, u32 pg_off, u8 *buf, u32 ecc_force)
                                buf += NFC_BUFSIZE;
                        }
                        // combine the two commands for program
-                       writel((FLASH_Program << 8) | FLASH_Send_Data, NAND_CMD_REG);
+                       nfc_reg_write((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);
@@ -1310,38 +1358,30 @@ static int nfc_write_pg_random(u32 pg_no, u32 pg_off, u8 *buf, u32 ecc_force)
                                }
 
                                // For ECC
-                               v = readl(NFC_FLASH_CONFIG2_REG) & ~NFC_FLASH_CONFIG2_ECC_EN;
+                               v = nfc_reg_read(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);
+                               nfc_reg_write(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++;
+                               rba = nfc_reg_read(NAND_CONFIGURATION1_REG);
+                       }
+                       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;
                        }
                        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);
-                       }
-               }
+       } else {
                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
@@ -1349,9 +1389,9 @@ static int nfc_write_pg_random(u32 pg_no, u32 pg_off, u8 *buf, u32 ecc_force)
                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);
+                       nfc_reg_write(nfc_reg_read(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);
+                       nfc_reg_write(BARKER_CODE_VAL, NFC_BASE + BARKER_CODE_SWAP_LOC);
                }
 #endif
                NFC_CMD_INPUT(FLASH_Send_Data);
@@ -1380,7 +1420,73 @@ static int nfc_write_pg_random(u32 pg_no, u32 pg_off, u8 *buf, u32 ecc_force)
        return 0;
 }
 
-#ifndef NFC_V3_0
+#ifdef NFC_V3_0
+/*
+ * Do a page read at random address
+ *
+ * @param pg_no             page number offset from 0
+ * @param pg_off             byte offset within the page
+ * @param ecc_force        can force ecc to be off. Otherwise, by default it is on
+ *                                    unless the page offset is non-zero
+ * @param cs_line            indicates which NAND of interleaved NAND devices is used
+ *
+ * @return  0 if successful; non-zero otherwise
+ */
+static int nfc_read_pg_random(u32 pg_no, u32 pg_off, u32 ecc_force, u32 cs_line, u32 num_of_chips)
+{
+       u32 ecc = NFC_FLASH_CONFIG2_ECC_EN;
+       u32 v, res = 0;
+       int i;
+
+       // clear the NAND_STATUS_SUM_REG register
+       nfc_reg_write(0, NAND_STATUS_SUM_REG);
+
+       // 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;
+       }
+
+       // Take care of config1 for RBA and SP_EN
+       v = nfc_reg_read(NAND_CONFIGURATION1_REG) & ~0x71;
+       nfc_reg_write(v, NAND_CONFIGURATION1_REG);
+
+       // For ECC
+       v = nfc_reg_read(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_Read_Mode1, pg_no, pg_off, 0, cs_line, num_of_chips);
+
+       if (g_is_2k_page || g_is_4k_page) {
+               // combine the two commands for 2k/4k page read
+               nfc_reg_write((FLASH_Read_Mode1_LG << 8) | FLASH_Read_Mode1, NAND_CMD_REG);
+       } else {
+               // just one command is enough for 512 page
+               nfc_reg_write(FLASH_Read_Mode1, NAND_CMD_REG);
+       }
+
+       // start auto-read
+       nfc_reg_write(NAND_LAUNCH_AUTO_READ, NAND_LAUNCH_REG);
+       wait_op_done();
+
+       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);
+       }
+       v = nfc_reg_read(NAND_STATUS_SUM_REG);
+       // test for CS0 ECC error from the STATUS_SUM register
+       if ((v & (0x0100 << cs_line)) != 0) {
+               // clear the status
+               nfc_reg_write(v & ~(0x0100 << cs_line), NAND_STATUS_SUM_REG);
+               diag_printf("ECC error from NAND_STATUS_SUM_REG(0x%08lx) = 0x%08x\n",
+                                       NAND_STATUS_SUM_REG, v);
+               diag_printf("NAND_ECC_STATUS_RESULT_REG(0x%08lx) = 0x%08x\n", NAND_ECC_STATUS_RESULT_REG,
+                                       nfc_reg_read(NAND_ECC_STATUS_RESULT_REG));
+               res = -1;
+       }
+       return res;
+}
+#else
 // 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)
@@ -1430,7 +1536,7 @@ static int nfc_read_pg_random(u32 pg_no, u32 pg_off, u32 ecc_force, u32 cs_line,
                        NFC_DATA_OUTPUT(RAM_BUF_3, FDO_PAGE_SPARE, ecc);
                }
                if (ecc) {
-                       t1 = readl(ECC_STATUS_RESULT_REG);
+                       t1 = nfc_reg_read(ECC_STATUS_RESULT_REG);
                        if (g_is_2k_page || g_is_4k_page) {
                                t2 = (t1 >> 4) & 0xF;
                                t3 = (t1 >> 8) & 0xF;
@@ -1470,7 +1576,7 @@ static int nfc_read_pg_random(u32 pg_no, u32 pg_off, u32 ecc_force, u32 cs_line,
        // 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);
+               nfc_reg_write(nfc_reg_read(NAND_SPAR_BUF0), NFC_BASE + BARKER_CODE_SWAP_LOC);
        }
 #endif
 
@@ -1505,13 +1611,25 @@ static int nfc_write_page(u32 pg_no, u32 pg_off, u32 ecc_force)
                ecc = 0;
        }
 
+       if (g_nfc_version != MXC_NFC_V1) {
+               int i;
+
+               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);
+               }
+       }
        if (g_nfc_version == MXC_NFC_V3) {
                int i;
                u32 v;
                u32 start_point = 0, rba, rba_count = 0;
 
+               rba = nfc_reg_read(NAND_CONFIGURATION1_REG);
+               if ((rba >> 4) & 0x7) {
+                       nfc_reg_write(rba & ~0x70, NAND_CONFIGURATION1_REG);
+               }
                // combine the two commands for program
-               writel((FLASH_Program << 8) | FLASH_Send_Data, NAND_CMD_REG);
+               nfc_reg_write((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);
@@ -1522,14 +1640,14 @@ static int nfc_write_page(u32 pg_no, u32 pg_off, u32 ecc_force)
                        }
 
                        // For ECC
-                       v = readl(NFC_FLASH_CONFIG2_REG) & ~NFC_FLASH_CONFIG2_ECC_EN;
+                       v = nfc_reg_read(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);
+                       nfc_reg_write(NAND_LAUNCH_AUTO_PROG, NAND_LAUNCH_REG);
                        if (i < (num_of_nand_chips - i))
                                wait_for_auto_prog_done();
                        else
@@ -1537,17 +1655,14 @@ static int nfc_write_page(u32 pg_no, u32 pg_off, u32 ecc_force)
                        pg_off = 0;
                        rba_count++;
                }
-               start_point = i;
                flash_status = NFC_STATUS_READ();
-       } else {
-               if (g_nfc_version != MXC_NFC_V1) {
-                       int i;
-
-                       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);
-                       }
+               // 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;
                }
+               rba = nfc_reg_read(NAND_CONFIGURATION1_REG);
+               start_point = i;
+       } else {
                NFC_CMD_INPUT(FLASH_Send_Data);
                start_nfc_addr_ops(FLASH_Program, pg_no, pg_off, 0, 0, num_of_nand_chips);
 
@@ -1564,11 +1679,11 @@ static int nfc_write_page(u32 pg_no, u32 pg_off, u32 ecc_force)
                NFC_CMD_INPUT(FLASH_Program);
 
                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;
+               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;
+               }
        }
        return 0;
 }
@@ -1730,7 +1845,7 @@ local_cmd_entry("info",
 
 local_cmd_entry("show",
                                "Show a page main/spare areas or spare area only (-s)",
-                               "-f <raw page address> [-s]",
+                               "-f <raw page address> | -b <block> [-s]",
                                nand_show,
                                NAND_cmds
                   );
@@ -1815,24 +1930,33 @@ static void nand_usage(char *why)
 static u32 curr_addr;
 static void nand_show(int argc, char *argv[])
 {
-       u32 ra;
+       u32 ra, block;
        bool flash_addr_set = false;
+       bool block_set = false;
        bool spar_only = false;
-       struct option_info opts[2];
+       struct option_info opts[3];
 
        init_opts(&opts[0], 'f', true, OPTION_ARG_TYPE_NUM,
                          &ra, &flash_addr_set, "NAND FLASH memory byte address");
-       init_opts(&opts[1], 's', false, OPTION_ARG_TYPE_FLG,
+       init_opts(&opts[1], 'b', true, OPTION_ARG_TYPE_NUM,
+                         &block, &block_set, "NAND FLASH memory block number");
+       init_opts(&opts[2], 's', false, OPTION_ARG_TYPE_FLG,
                          &spar_only, NULL, "Spare only");
 
-       if (!scan_opts(argc, argv, 2, opts, 2, 0, 0, 0)) {
+       if (!scan_opts(argc, argv, 2, opts, NUM_ELEMS(opts), NULL, 0, NULL)) {
                return;
        }
-       if (!flash_addr_set) {
+       if (flash_addr_set && block_set) {
+               nand_usage("options -f and -b are mutually exclusive");
+               return;
+       } else if (flash_addr_set) {
+               curr_addr = ra;
+       } else if (block_set) {
+               ra = BLOCK_TO_OFFSET(block) + (unsigned long)flash_info.start;
+               curr_addr = ra;
+       } else {
                ra = curr_addr;
                curr_addr += NF_PG_SZ;
-       } else {
-               curr_addr = ra;
        }
 
        if (ra % NF_PG_SZ) {
@@ -1872,7 +1996,7 @@ static void nand_read(int argc, char *argv[])
        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)) {
+       if (!scan_opts(argc, argv, 2, opts, NUM_ELEMS(opts), NULL, 0, NULL)) {
                nand_usage("invalid arguments");
                return;
        }
@@ -1915,6 +2039,12 @@ static void nand_read(int argc, char *argv[])
                        diag_printf("\n** Error: flash address: 0x%08x out of range\n", ra);
                        return;
                }
+               if (nfc_is_badblock(OFFSET_TO_BLOCK(ra), g_bbt)) {
+                       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;
+               }
                pg_no = ra / NF_PG_SZ;
                pg_off = ra % NF_PG_SZ;
                for (i = 0; i < num_of_nand_chips; i++) {
@@ -1957,7 +2087,7 @@ static void nand_write(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)) {
+       if (!scan_opts(argc, argv, 2, opts, NUM_ELEMS(opts), NULL, 0, NULL)) {
                nand_usage("invalid arguments");
                return;
        }
@@ -1968,8 +2098,9 @@ 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);
+               ((mem_addr + len) >= (CYG_ADDRESS)ram_end)) {
+               diag_printf("** WARNING: RAM address range: %p..%p may be invalid\n",
+                                       (void *)mem_addr, (void *)(mem_addr + len));
                diag_printf("   valid range is %p-%p\n", (void *)ram_start, (void *)ram_end);
        }
 
@@ -1994,9 +2125,11 @@ static void nand_write(int argc, char *argv[])
        mem_addr_st = mem_addr;
        len_st = len;
        ra &= MXC_NAND_ADDR_MASK;
+
+       mxc_nfc_buf_clear(NAND_SPAR_BUF0, 0xff, NF_SPARE_SZ);
        do {
                if (OFFSET_TO_BLOCK(ra) > (NF_BLK_CNT - 1)) {
-                       diag_printf("\nOut of range: addr=0x%08x\n", ra);
+                       diag_printf("\nFlash address 0x%08x out of range\n", ra);
                        return;
                }
                if (nfc_is_badblock(OFFSET_TO_BLOCK(ra), g_bbt)) {
@@ -2016,7 +2149,7 @@ static void nand_write(int argc, char *argv[])
                        }
                        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
+                       mem_addr = mem_addr_st; // rewind to block boundary
                        len = len_st;
                        continue;
                }
@@ -2104,7 +2237,7 @@ static void nand_erase(int argc, char *argv[])
        init_opts(&opts[2], 'o', false, OPTION_ARG_TYPE_FLG,
                  &force_erase_set, &force_erase_set, "force erases block");
 
-       if (!scan_opts(argc, argv, 2, opts, 4, 0, 0, 0)) {
+       if (!scan_opts(argc, argv, 2, opts, NUM_ELEMS(opts), NULL, 0, NULL)) {
                nand_usage("invalid arguments");
                return;
        }
@@ -2113,19 +2246,28 @@ static void nand_erase(int argc, char *argv[])
                nand_usage("missing argument");
                return;
        }
-       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");
+       if ((ra % NF_BLK_SZ) != 0) {
+               diag_printf("Address must be block aligned!\n");
                diag_printf("Block size is 0x%x\n", NF_BLK_SZ);
                return;
        }
+       if ((len % NF_BLK_SZ) != 0) {
+               diag_printf("length must be block aligned!\n");
+               diag_printf("Block size is 0x%x\n", NF_BLK_SZ);
+               return;
+       }
+       if (len == 0) {
+               diag_printf("length must be > 0!\n");
+               return;
+       }
 
-       if (!verify_action("About to erase 0x%x bytes from nand offset 0x%x\n", len, ra)) {
+       if (!verify_action("About to erase 0x%08x bytes from nand offset 0x%08x", len, ra)) {
                diag_printf("** Aborted\n");
                return;
        }
 
-       // now ra is block aligned
+       diag_printf1("Enabling flash from %p..%p\n", (u8 *)ra, (u8 *)ra + len - 1);
+       FLASH_Enable((u8 *)ra, (u8 *)ra + len);
        if (force_erase_set == true) {
                diag_printf("Force erase ...");
                nfc_erase_region(ra, len, 0, 1);
@@ -2133,6 +2275,7 @@ static void nand_erase(int argc, char *argv[])
        } else {
                nfc_erase_region(ra, len, 1, 1);
        }
+       FLASH_Disable((u8 *)ra, (u8 *)ra + len);
        diag_printf("\n");
 }
 
@@ -2149,7 +2292,7 @@ static void nand_scan(int argc, char *argv[])
        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)) {
+       if (!scan_opts(argc, argv, 2, opts, NUM_ELEMS(opts), NULL, 0, NULL)) {
                nand_usage("invalid arguments");
                return;
        }
@@ -2192,11 +2335,6 @@ static void nand_info(int argc, char *argv[])
 {
        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);
@@ -2289,12 +2427,15 @@ static void do_nand_cmds(int argc, char *argv[])
        struct cmd *cmd;
 
        if (!mxcnfc_init_ok) {
+               flash_hwr_init();
+               if (!mxcnfc_init_ok) {
 #ifdef CYGHWR_DEVS_FLASH_MXC_MULTI
-               diag_printf("Warning: NAND flash hasn't been initialized. Try \"factive nand\" first\n\n");
+                       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");
+                       diag_printf("Error: NAND flash hasn't been initialized\n");
 #endif
-               return;
+                       return;
+               }
        }
 
        if (argc < 2) {
@@ -2352,29 +2493,24 @@ static void print_page(u32 addr, bool spare_only)
                diag_printf("Non page-aligned read not supported here: 0x%x\n", addr);
                return;
        }
-       if (spare_only) {
-               diag_printf("Error %d: Not supported\n", __LINE__);
-               return;
-       } else {
-               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("<<<<<<<<< spare area >>>>>>>>>\n");
-                       print_pkt_16((u16*)NAND_SPAR_BUF0, NF_SPARE_SZ);
+       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);
 
-                       if (!spare_only) {
-                               diag_printf("<<<<<<<<< main area >>>>>>>>>\n");
-                               print_pkt_16((u16*)NAND_MAIN_BUF0, NF_PG_SZ / num_of_nand_chips);
-                       }
+               diag_printf("<<<<<<<<< spare area >>>>>>>>>\n");
+               print_pkt_16((u16*)NAND_SPAR_BUF0, NF_SPARE_SZ);
 
-                       diag_printf("\n");
+               if (!spare_only) {
+                       diag_printf("<<<<<<<<< main area >>>>>>>>>\n");
+                       print_pkt_16((u16*)NAND_MAIN_BUF0, NF_PG_SZ / num_of_nand_chips);
                }
+
+               diag_printf("\n");
        }
 }