From: lothar Date: Wed, 8 Jul 2009 10:46:28 +0000 (+0000) Subject: use nfc_l_to_p() to remap block numbers X-Git-Tag: v1.5.3~54 X-Git-Url: https://git.kernelconcepts.de/?p=karo-tx-redboot.git;a=commitdiff_plain;h=e9c95ab24ba03bb83084777ddbb405d8bb54f041 use nfc_l_to_p() to remap block numbers enable reading of partial pages at arbitrary offset --- diff --git a/packages/devs/flash/arm/mxc/v2_0/src/mxc_nfc.c b/packages/devs/flash/arm/mxc/v2_0/src/mxc_nfc.c index 6f8991a2..c1f704b0 100644 --- a/packages/devs/flash/arm/mxc/v2_0/src/mxc_nfc.c +++ b/packages/devs/flash/arm/mxc/v2_0/src/mxc_nfc.c @@ -87,9 +87,6 @@ #include #include #include -#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++; }