#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
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;
#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;
// 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)
*/
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);
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
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);
}
*/
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);
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;
}
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;
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;
}
* 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;
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;
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;
}
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;
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");
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;
}
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
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");
}
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++;
}