use nfc_l_to_p() to remap block numbers
authorlothar <lothar>
Wed, 8 Jul 2009 10:46:28 +0000 (10:46 +0000)
committerlothar <lothar>
Wed, 8 Jul 2009 10:46:28 +0000 (10:46 +0000)
enable reading of partial pages at arbitrary offset

packages/devs/flash/arm/mxc/v2_0/src/mxc_nfc.c

index 6f8991a..c1f704b 100644 (file)
@@ -87,9 +87,6 @@
 #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
 
@@ -143,7 +140,7 @@ static int g_spare_only_read_ok = true;
 static int g_nfc_debug_level = NFC_DEBUG_NONE;
 static bool g_nfc_debug_measure = false;
 static bool g_is_2k_page = false;
-static unsigned long g_block_offset;
+static unsigned int 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;
@@ -302,8 +299,8 @@ static const flash_dev_info_t supported_devices[] = {
 #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)
+#define OFFSET_TO_BLOCK(offset)                        ((u32)((offset) / (NF_PG_SZ * NF_PG_PER_BLK)))
+#define OFFSET_TO_PAGE(offset)                 ((u32)((offset) / NF_PG_SZ) % NF_PG_PER_BLK)
 
 static u8 *g_bbt, *g_page_buf;
 static u32 g_bbt_sz;
@@ -317,6 +314,80 @@ nfc_setup_func_t *nfc_setup = NULL;
 // this callback allows the platform specific iomux setup
 nfc_iomuxsetup_func_t *nfc_iomux_setup = NULL;
 
+static flash_addr_t flash_region_start;
+static flash_addr_t flash_region_end;
+static int flash_enable;
+
+/* This assumes reading the flash with monotonically increasing flash addresses */
+static flash_addr_t nfc_l_to_p(flash_addr_t addr)
+{
+       if (g_block_offset == 0) {
+               return addr;
+       } else {
+               flash_addr_t ra;
+               u32 block = (addr & MXC_NAND_ADDR_MASK) / NF_BLK_SZ;
+               u32 offset = addr % NF_BLK_SZ;
+
+               ra = (block + g_block_offset) * NF_BLK_SZ + offset;
+               if (offset == 0) {
+                       nfc_printf(NFC_DEBUG_MIN,
+                                          "Remapping block %u at addr 0x%08llx to block %u at addr 0x%08llx\n",
+                                          block, (u64)addr, block + g_block_offset, (u64)ra);
+               }
+               return ra;
+       }
+}
+
+static int flash_addr_valid(flash_addr_t addr)
+{
+       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 addr >= flash_region_start && addr < flash_region_end;
+}
+
+/* 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;
+
+       if (flash_enable++ == 0) {
+               flash_region_start = s;
+               flash_region_end = e;
+               diag_printf1("Enabling flash region 0x%08llx..0x%08llx\n",
+                                        (u64)s, (u64)e);
+               g_block_offset = 0;
+       } else {
+               if (s < flash_region_start ||
+                       e > flash_region_end) {
+                       diag_printf("** WARNING: Enable 0x%08llx..0x%08llx outside enabled flash region 0x%08llx..0x%08llx\n",
+                                               (u64)s, (u64)e, (u64)flash_region_start, (u64)flash_region_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;
+
+       if (flash_enable) {
+               if (--flash_enable == 0) {
+                       diag_printf1("Disabling flash region 0x%08llx..0x%08llx\n",
+                                                (u64)s, (u64)e);
+                       if (s != flash_region_start ||
+                               e != flash_region_end) {
+                               diag_printf("** Error: Disable 0x%08llx..0x%08llx not equal to enabled flash region 0x%08llx..0x%08llx\n",
+                                               (u64)s, (u64)e, (u64)flash_region_start, (u64)flash_region_end);
+                       }
+               }
+       } else {
+               diag_printf("** Error: unbalanced call to flash_disable()\n");
+       }
+}
+
 int
 #ifndef MXCFLASH_SELECT_MULTI
 flash_hwr_init(void)
@@ -989,7 +1060,7 @@ static int nfc_program_blk(u32 ra, u8 *buf, u32 len)
  */
 int nfc_erase_region(flash_addr_t addr, u32 len, bool skip_bad, bool verbose)
 {
-       u32 sz, blk, update = 0, skip = 0, j = 0;
+       u32 sz, blk, update = 0, j = 0;
 
        nfc_printf(NFC_DEBUG_MED, "%s: addr=0x%08llx len=0x%08x\n",
                           __FUNCTION__, (u64)addr, len);
@@ -1002,21 +1073,17 @@ 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 &= MXC_NAND_ADDR_MASK;
+       addr = nfc_l_to_p(addr);
        // 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;
-                       }
                        diag_printf("\nSkipping bad block %u at addr 0x%08llx\n",
                                                blk, (u64)addr);
                        continue;
                }
                if (nfc_erase_blk(addr) != 0) {
-                       diag_printf("\nError: Failed to erase block %u at addr 0x%08llx\n",
+                       diag_printf("\n** Error: 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
@@ -1033,7 +1100,7 @@ int nfc_erase_region(flash_addr_t addr, u32 len, bool skip_bad, bool verbose)
        if (update) {
                if (program_bbt_to_flash() != 0) {
                        diag_printf("\nError: Failed to update bad block table\n");
-                       return -1;
+                       return FLASH_ERR_PROGRAM;
                }
                diag_printf("\nnew bad blocks=%d\n", update);
        }
@@ -1049,9 +1116,10 @@ int nfc_erase_region(flash_addr_t addr, u32 len, bool skip_bad, bool verbose)
  */
 int nfc_program_region(flash_addr_t addr, u8 *buf, u32 len)
 {
-       u32 sz, blk, update = 0, skip = 0, partial_block_size;
+       u32 sz, blk, update = 0, partial_block_size;
 
-       diag_printf1("%s: addr=0x%08llx, len=0x%08x\n", __FUNCTION__, (u64)addr, len);
+       nfc_printf(NFC_DEBUG_MED, "%s: addr=0x%08llx, len=0x%08x\n",
+                          __FUNCTION__, (u64)addr, len);
 
        if ((addr % (NF_PG_SZ / num_of_nand_chips)) != 0) {
                diag_printf("Error: flash address 0x%08llx not page aligned\n", (u64)addr);
@@ -1064,15 +1132,15 @@ int nfc_program_region(flash_addr_t addr, u8 *buf, u32 len)
 
        partial_block_size = addr % NF_BLK_SZ;
 
-       addr &= MXC_NAND_ADDR_MASK;
-       // now addr has to be block aligned
+       addr = nfc_l_to_p(addr);
        while (1) {
+               if (!flash_addr_valid(addr)) {
+                       diag_printf("\nToo many bad blocks in flash region 0x%08llx..0x%08llx\n",
+                                               (u64)flash_region_start, (u64)flash_region_end);
+                       return FLASH_ERR_INVALID;
+               }
                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;
-                       }
                        diag_printf("\nSkipping bad block %u at addr 0x%08llx\n", blk, addr);
                        goto incr_address;
                }
@@ -1084,10 +1152,6 @@ int nfc_program_region(flash_addr_t addr, u8 *buf, u32 len)
                        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;
@@ -1102,17 +1166,14 @@ 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) {
                        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 FLASH_ERR_OK;
 }
 
@@ -1121,68 +1182,75 @@ incr_address:
  * in flash address will be masked off inside the function.
  * It skips bad blocks and read good blocks of data for "len" bytes.
  *
- * @param addr                 NAND flash address. it has to be page aligned
+ * @param addr                 NAND flash address.
  * @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
  */
 int nfc_read_region(flash_addr_t addr, u8 *buf, u32 len)
 {
-       u32 blk, bad = 0, start_point = 0, pg_no;
-       unsigned long offset = addr % NF_PG_SZ;
+       u32 start_point = 0, pg_no;
+       unsigned int offset = addr % NF_PG_SZ;
+       int chk_bad = 1;
 
-       diag_printf1("%s: addr=0x%08llx, buf=0x%p, len=0x%08x\n",
-                                __FUNCTION__, addr, buf, 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",
+               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 = (addr & MXC_NAND_ADDR_MASK) - offset;
-       blk = OFFSET_TO_BLOCK(addr);
        while (len > 0) {
                int i;
 
-               if ((addr % NF_BLK_SZ) == 0) {
-                       // only need to test block aligned page address
-                       blk = OFFSET_TO_BLOCK(addr);
+               if (!flash_addr_valid(addr)) {
+                       diag_printf("Too many bad blocks in flash region 0x%08llx..0x%08llx\n",
+                                               (u64)flash_region_start, (u64)flash_region_end);
+                       return FLASH_ERR_INVALID;
+               }
+               if (chk_bad) {
+                       int 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);
+                               diag_printf("Skipping bad block %u at addr 0x%08llx\n", blk, (u64)addr);
                                addr += NF_BLK_SZ;
+                               g_block_offset++;
                                continue;
                        }
+                       chk_bad = 0;
                }
 
                pg_no = addr / NF_PG_SZ;
-               if ((addr % NF_PG_SZ) != 0) {
+               if (offset != 0) {
                        /* Find which interleaved NAND device */
-                       start_point = (addr - (pg_no * NF_PG_SZ)) / (NF_PG_SZ / num_of_nand_chips);
+                       start_point = offset / (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;
+                       int chunk_size = (NF_PG_SZ - offset) / num_of_nand_chips;
 
+                       if (chunk_size > len)
+                               chunk_size = len;
+                       nfc_printf(NFC_DEBUG_MED, "Reading page %d addr 0x%08llx chip %d len 0x%03x\n",
+                                          pg_no, (u64)addr, i, chunk_size);
                        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);
+                               diag_printf("** Error: Failed to read flash block %u at addr 0x%08llx\n",
+                                                       OFFSET_TO_BLOCK(addr), (u64)addr);
                                return FLASH_ERR_INVALID;
                        }
                        // now do the copying
-                       nfc_buf_read(buf, NAND_MAIN_BUF0, chunk_size);
+                       nfc_buf_read(buf, NAND_MAIN_BUF0 + offset, chunk_size);
 
                        buf += chunk_size;
                        len -= chunk_size;
                        addr += NF_PG_SZ / num_of_nand_chips - offset;
                        offset = 0;
                }
+               chk_bad = (addr % NF_BLK_SZ) == 0;
        }
 
        return FLASH_ERR_OK;
@@ -1321,6 +1389,9 @@ static int nfc_read_pg_random(u32 pg_no, u32 pg_off, u32 ecc_force, u32 cs_line,
        u8 t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0, t8 = 0;
        int res = 0;
 
+       nfc_printf(NFC_DEBUG_MAX, "%s: reading page %u offset 0x%03x (addr 0x%08llx)\n",
+                          __FUNCTION__, pg_no, pg_off, (flash_addr_t)pg_no * NF_PG_SZ + pg_off);
+
        if (ecc_force == ECC_FORCE_OFF || pg_off != 0 )
                ecc = 0;
 
@@ -1346,8 +1417,8 @@ static int nfc_read_pg_random(u32 pg_no, u32 pg_off, u32 ecc_force, u32 cs_line,
 
                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);
+                       diag_printf("\n** Error: ECC error page %u, col %u: ECC status=0x%x:0x%x:0x%x:0x%x\n",
+                                               pg_no, pg_off, t1, t2, t3, t4);
                        res = -1;
                        goto out;
                }
@@ -1520,45 +1591,6 @@ void mxc_nfc_print_info(void)
                                NF_PG_PER_BLK, NF_PG_SZ);
 }
 
-static inline void mxc_clr_block_offset(void *start, void *end)
-{
-       nfc_printf(NFC_DEBUG_MIN, "Clearing block offset %lu for %p..%p\n",
-                          g_block_offset, start, end);
-       g_block_offset = 0;
-}
-
-static void *flash_region_start;
-static void *flash_region_end;
-static int flash_enable;
-
-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 {
-               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);
-               }
-       }
-}
-
-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);
-                       }
-               }
-       } else {
-               diag_printf("** Error: unbalanced call to flash_disable()\n");
-       }
-}
-
 static int mxc_nfc_isbad_bbt(u16 *bbt, int block)
 {
        cyg_uint8 res;
@@ -1916,7 +1948,6 @@ static void nand_write(int argc, char *argv[])
        bool col_set = false;
        struct option_info opts[4];
        bool ecc_status = g_ecc_enable;
-       int skip = 0;
 
        init_opts(&opts[0], 'b', true, OPTION_ARG_TYPE_NUM,
                          &mem_addr, &mem_addr_set, "memory base address");
@@ -1965,14 +1996,10 @@ static void nand_write(int argc, char *argv[])
        ra &= MXC_NAND_ADDR_MASK;
        do {
                if (OFFSET_TO_BLOCK(ra) > (NF_BLK_CNT - 1)) {
-                       diag_printf("Out of range: addr=0x%x\n", ra);
+                       diag_printf("\nOut of range: addr=0x%08x\n", ra);
                        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;
@@ -1985,7 +2012,7 @@ static void nand_write(int argc, char *argv[])
                }
                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("Warning %d: program error at addr 0x%x\n", __LINE__, ra);
+                               diag_printf("\nWarning %d: program error at addr 0x%x\n", __LINE__, ra);
                        }
                        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
@@ -2001,9 +2028,6 @@ static void nand_write(int argc, char *argv[])
                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");
 }
 
@@ -2188,7 +2212,7 @@ static void nand_info(int argc, char *argv[])
        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",
+                       diag_printf("block %d at offset 0x%08x is a %s bad block\n",
                                                i, i * NF_BLK_SZ, res == BLK_BAD_FACTORY ? "factory" : "runtime");
                        j++;
                }