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