/* Copyright 2000-2010 Broadcom Corporation <:label-BRCM:2012:DUAL/GPL:standard This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation (the "GPL"). This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. :> */ /*************************************************************************** * File Name : nandflash.c * * Description: This file implements the Broadcom DSL defined flash api for * for NAND flash parts. ***************************************************************************/ /** Includes. **/ #include "lib_types.h" #include "lib_printf.h" #include "lib_string.h" #include "lib_malloc.h" // #include "bcm_map_part.h" #include "bcm_map.h" #include "bcmtypes.h" #include "bcm_hwdefs.h" #include "flash_api.h" #include "jffs2.h" #if defined(CFG_RAMAPP) #include "cfe_timer.h" #endif /* NAND Prefetch deaktivieren, da sonst SDK04 basierte Firmware mit einem * SDK05 CFE nicht mehr bootfähig ist. * Die neue Firmware schaltet das Feature im Linux NAND Treiber wieder an */ #undef NAC_PREFETCH_EN #if defined(_BCM96838_) /* Not used on non-BCM963268 chips but prevents compile errors. */ #define NC_BLK_SIZE_2048K 0x60000000 #define NC_BLK_SIZE_1024K 0x50000000 #define NC_BLK_SIZE_256K 0x40000000 #define NC_PG_SIZE_8K 0x00300000 #define NC_PG_SIZE_4K 0x00200000 #define NAC_FAST_PGM_RDIN 0x10000000 #define NAC_PARTIAL_PAGE_EN 0x04000000 #define NAC_ECC_LVL_0_SHIFT 20 #define NAC_ECC_LVL_0_MASK 0x00f00000 #define NAC_ECC_LVL_SHIFT 16 #define NAC_ECC_LVL_MASK 0x000f0000 #define NAC_SPARE_SZ_SHIFT 0 #define NAC_SPARE_SZ_MASK 0x0000003f #define NAC_ECC_LVL_DISABLE 0 #define NAC_ECC_LVL_BCH_1 1 #define NAC_ECC_LVL_BCH_2 2 #define NAC_ECC_LVL_BCH_3 3 #define NAC_ECC_LVL_BCH_4 4 #define NAC_ECC_LVL_BCH_5 5 #define NAC_ECC_LVL_BCH_6 6 #define NAC_ECC_LVL_BCH_7 7 #define NAC_ECC_LVL_BCH_8 8 #define NAC_ECC_LVL_BCH_9 9 #define NAC_ECC_LVL_BCH_10 10 #define NAC_ECC_LVL_BCH_11 11 #define NAC_ECC_LVL_BCH_12 12 #define NAC_ECC_LVL_RESVD_1 13 #define NAC_ECC_LVL_RESVD_2 14 #define NAC_ECC_LVL_HAMMING 15 #define NandSpareAreaReadOfs10 NandSpareAreaReadOfs0 #endif /* for debugging in jtag */ #if !defined(CFG_RAMAPP) #define static #endif #ifndef INC_BTRM_BUILD #define INC_BTRM_BUILD 0 #endif /** Defines. **/ #define SPARE_MAX_SIZE (27 * 16) #define CTRLR_CACHE_SIZE 512 #define NAND_CI_CELLTYPE_MSK 0x00000c00 #define NAND_IS_MLC(chip) \ (NAND->NandRevision > 0x00000202 && \ (chip)->chip_page_size > 512 && \ ((chip)->chip_device_id & NAND_CI_CELLTYPE_MSK)) #define NAND_CHIPID(chip) ((chip)->chip_device_id >> 16) /* Flash manufacturers. */ #define FLASHTYPE_SAMSUNG 0xec #define FLASHTYPE_ST 0x20 #define FLASHTYPE_MICRON 0x2c #define FLASHTYPE_HYNIX 0xad #define FLASHTYPE_TOSHIBA 0x98 #define FLASHTYPE_MXIC 0xc2 #define FLASHTYPE_SPANSION 0x01 /* Samsung flash parts. */ #define SAMSUNG_K9F5608U0A 0x55 #define SAMSUNG_K9F1208U0 0x76 #define SAMSUNG_K9F1G08U0 0xf1 /* ST flash parts. */ #define ST_NAND512W3A2CN6 0x76 #define ST_NAND01GW3B2CN6 0xf1 /* Micron flash parts. */ #define MICRON_MT29F1G08AAC 0xf1 #define MICRON_MT29F2G08ABA 0xda #define MICRON_MT29F4G08ABA 0xdc #define MICRON_MT29F8G08ABA 0x38 #define MICRON_MT29F8G16ABA 0xd3 /* Hynix flash parts. */ #define HYNIX_H27U1G8F2B 0xf1 #define HYNIX_H27U518S2C 0x76 /* MXIC flash parts */ #define MXIC_MX30LF1208AA 0xf0 #define MXIC_MX30LF1G08AA 0xf1 /* SPANSION flash parts */ #define SPANSION_S34ML01G1 0xf1 #define SPANSION_S34ML02G1 0xda #define SPANSION_S34ML04G1 0xdc /* Flash id to name mapping. */ #define NAND_MAKE_ID(A,B) \ (((unsigned short) (A) << 8) | ((unsigned short) B & 0xff)) #define NAND_FLASH_DEVICES \ {{NAND_MAKE_ID(FLASHTYPE_SAMSUNG,SAMSUNG_K9F5608U0A),"Samsung K9F5608U0"}, \ {NAND_MAKE_ID(FLASHTYPE_SAMSUNG,SAMSUNG_K9F1208U0),"Samsung K9F1208U0"}, \ {NAND_MAKE_ID(FLASHTYPE_SAMSUNG,SAMSUNG_K9F1G08U0),"Samsung K9F1G08U0"}, \ {NAND_MAKE_ID(FLASHTYPE_ST,ST_NAND512W3A2CN6),"ST NAND512W3A2CN6"}, \ {NAND_MAKE_ID(FLASHTYPE_ST,ST_NAND01GW3B2CN6),"ST NAND01GW3B2CN6"}, \ {NAND_MAKE_ID(FLASHTYPE_MICRON,MICRON_MT29F1G08AAC),"Micron MT29F1G08AAC"},\ {NAND_MAKE_ID(FLASHTYPE_MICRON,MICRON_MT29F2G08ABA),"Micron MT29F2G08ABA"},\ {NAND_MAKE_ID(FLASHTYPE_MICRON,MICRON_MT29F4G08ABA),"Micron MT29F4G08ABA"},\ {NAND_MAKE_ID(FLASHTYPE_MICRON,MICRON_MT29F8G08ABA),"Micron MT29F8G08ABA"},\ {NAND_MAKE_ID(FLASHTYPE_MICRON,MICRON_MT29F8G16ABA),"Micron MT29F8G16ABA"},\ {NAND_MAKE_ID(FLASHTYPE_HYNIX,HYNIX_H27U1G8F2B),"Hynix H27U1G8F2B"}, \ {NAND_MAKE_ID(FLASHTYPE_HYNIX,HYNIX_H27U518S2C),"Hynix H27U518S2C"}, \ {NAND_MAKE_ID(FLASHTYPE_MXIC,MXIC_MX30LF1208AA),"MXIC MX30LF1208AA"}, \ {NAND_MAKE_ID(FLASHTYPE_MXIC,MXIC_MX30LF1G08AA),"MXIC MX30LF1G08AA"}, \ {NAND_MAKE_ID(FLASHTYPE_SPANSION,SPANSION_S34ML01G1),"Spansion S34ML01G1"},\ {NAND_MAKE_ID(FLASHTYPE_SPANSION,SPANSION_S34ML02G1),"Spansion S34ML02G1"},\ {NAND_MAKE_ID(FLASHTYPE_SPANSION,SPANSION_S34ML04G1),"Spansion S34ML04G1"},\ {0,""} \ } #define NAND_FLASH_MANUFACTURERS \ {{FLASHTYPE_SAMSUNG, "Samsung"}, \ {FLASHTYPE_ST, "ST"}, \ {FLASHTYPE_MICRON, "Micron"}, \ {FLASHTYPE_HYNIX, "Hynix"}, \ {FLASHTYPE_TOSHIBA, "Toshiba"}, \ {FLASHTYPE_MXIC, "MXIC"}, \ {FLASHTYPE_SPANSION, "Spansion"}, \ {0,""} \ } /* Condition to determine the spare layout. */ #define LAYOUT_PARMS(L,S,P) \ (((unsigned int)(L)<<28) | ((unsigned int)(S)<<16) | (P)) /* Each bit in the ECCMSK array represents a spare area byte. Bits that are * set correspond to spare area bytes that are reserved for the ECC or bad * block indicator. Bits that are not set can be used for data such as the * JFFS2 clean marker. This macro returns 0 if the spare area byte at offset, * OFS, is available and non-0 if it is being used for the ECC or BI. */ #define ECC_MASK_BIT(ECCMSK, OFS) (ECCMSK[OFS / 8] & (1 << (OFS % 8))) #define SPARE_BI_MARKER 0 #define SPARE_GOOD_MARKER 0xFF #if defined(__MIPSEL) || defined(__ARMEL__) || defined(__AARCH64EL__) #define JFFS2_CLEANMARKER {JFFS2_MAGIC_BITMASK, \ JFFS2_NODETYPE_CLEANMARKER, 0x0008, 0x0000} #else #define JFFS2_CLEANMARKER {JFFS2_MAGIC_BITMASK, \ JFFS2_NODETYPE_CLEANMARKER, 0x0000, 0x0008} #endif /* ONFI data offsets */ #define ONFI_SIGNATURE 0 #define ONFI_REVISION 4 #define ONFI_FEATURES 6 #define ONFI_OPTIONAL_COMMANDS 8 #define ONFI_RESERVED 10 #define ONFI_MANUFACTURER 32 #define ONFI_MODEL 44 #define ONFI_MANUFACTURER_ID 64 #define ONFI_DATE 65 #define ONFI_RESERVED2 67 #define ONFI_DATA_BYTES_PER_PAGE 80 #define ONFI_SPARE_BYTES_PER_PAGE 84 #define ONFI_DATA_BYTES_PER_PARTIAL_PAGE 86 #define ONFI_SPARE_BYTES_PER_PARTIAL_PAGE 90 #define ONFI_PAGES_PER_BLOCK 92 #define ONFI_BLOCKS_PER_UNIT 96 #define ONFI_LOGICAL_UNITS 100 #define ONFI_ADDRESS_CYCLES 101 #define ONFI_BITS_PER_CELL 102 #define ONFI_MAX_BAD_BLOCKS_PER_UNIT 103 #define ONFI_BLOCK_ENDURANCE 105 #define ONFI_VALID_BLOCKS 107 #define ONFI_VALID_BLOCKS_ENDURANCE 108 #define ONFI_PROGRAMS_PER_PAGE 110 #define ONFI_PARTIAL_PROGRAM_ATTRIBUTES 111 #define ONFI_ECC_BITS 112 #define ONFI_INTERLEAVED_ADDRESS_BITS 113 #define ONFI_INTERLEAVED_OPERATION_ATTRIBUTES 114 #define ONFI_RESERVED3 115 #define ONFI_IO_PIN_CAPACITANCE 128 #define ONFI_TIMING_MODE_SUPPORT 129 #define ONFI_PROGRAM_CACHE_TIMING 131 #define ONFI_MAX_PAGE_PROGRAM_TIME 133 #define ONFI_MAX_BLOCK_ERASE_TIME 135 #define ONFI_MAX_RANDOM_READ_TIME 137 #define ONFI_tCCS_MINIMUM 139 #define ONFI_RESERVED4 141 #define ONFI_VENDOR_SPECIFIC_REVISION_NUM 164 #define ONFI_VENDOR_SPECIFIC 166 #define ONFI_CRC 254 #undef DEBUG_NAND #if defined(DEBUG_NAND) && defined(CFG_RAMAPP) #define DBG_PRINTF printf #else #define DBG_PRINTF(...) #endif /** Externs. **/ extern void board_setleds(unsigned int); /** Structs. **/ typedef struct SpareLayout { unsigned char sl_bi_ofs[2]; unsigned char sl_spare_mask[]; } SPARE_LAYOUT, *PSPARE_LAYOUT; typedef struct CfeNandChip { char *chip_name; unsigned long chip_device_id; unsigned long chip_base; unsigned long chip_total_size; unsigned long chip_block_size; unsigned long chip_page_size; unsigned long chip_spare_size; unsigned long chip_spare_step_size; unsigned char *chip_spare_mask; unsigned long chip_bi_index_1; unsigned long chip_bi_index_2; } CFE_NAND_CHIP, *PCFE_NAND_CHIP; static void nand_reset_device(void); extern inline void nandflash_copy_from_cache(unsigned char *buffer, int offset, int numbytes); extern inline int nandflash_wait_cmd(void); extern inline int nandflash_wait_cache(void); #if defined(CFG_RAMAPP) /** Prototypes for CFE RAM. **/ int nand_flash_init(flash_device_info_t **flash_info); int mpinand_flash_init(flash_device_info_t **flash_info); static void nand_init_cleanmarker(PCFE_NAND_CHIP pchip); static int nand_adjust_cfg(PCFE_NAND_CHIP pchip); static int nand_is_blk_cleanmarker(PCFE_NAND_CHIP pchip, unsigned long start_addr); static int nand_is_blk_bad(PCFE_NAND_CHIP pchip, unsigned short blockNum); static int nand_initialize_spare_area(PCFE_NAND_CHIP pchip, int eraseBadBlocks); static void nand_mark_bad_blk(PCFE_NAND_CHIP pchip, unsigned long page_addr); static int nand_flash_sector_erase_int(unsigned short blk); static int nand_flash_read_buf(unsigned short blk, int offset, unsigned char *buffer, int len); static int nand_flash_write_buf(unsigned short blk, int offset, unsigned char *buffer, int numbytes); static int nand_flash_get_numsectors(void); static int nand_flash_get_sector_size(unsigned short sector); static unsigned char *nand_flash_get_memptr(unsigned short sector); static int nand_flash_get_blk(int addr); static int nand_flash_get_total_size(void); static int nand_flash_dev_specific_cmd(unsigned int command, void * inBuf, void * outBuf); static int nandflash_wait_status(unsigned int status_mask); extern inline int nandflash_wait_device(void); extern inline int nandflash_wait_cmd(void); static int nandflash_read_spare_area(PCFE_NAND_CHIP pchip, unsigned long page_addr, unsigned char *buffer, int len); static int nandflash_write_spare_area(PCFE_NAND_CHIP pchip, unsigned long page_addr, unsigned char *buffer, int len); static int nandflash_read_page(PCFE_NAND_CHIP pchip, unsigned long start_addr, unsigned char *buffer, int len); static int nandflash_write_page(PCFE_NAND_CHIP pchip, unsigned long page_addr, unsigned int page_offset, unsigned char *buffer, int len); static int nandflash_block_erase(PCFE_NAND_CHIP pchip, unsigned long blk_addr, int force); #else /** Prototypes for CFE ROM. **/ void rom_nand_flash_init(void); static int nand_is_blk_cleanmarker(PCFE_NAND_CHIP pchip, unsigned long start_addr); static int nand_adjust_cfg(PCFE_NAND_CHIP pchip); int nand_flash_get_sector_size(unsigned short sector); int nand_flash_get_numsectors(void); static int nandflash_wait_status(unsigned int status_mask); #if (INC_BTRM_BUILD==1) int nand_flash_btrm_read_buf(unsigned char *buffer, unsigned int numDups); static void nandflash_btrm_ecc_setleds(UINT32 cfeRomMirror); static int nandflash_btrm_read(PCFE_NAND_CHIP pchip, UINT32 page_addr, UINT32 cache_offset); static int nandflash_btrm_nand_mirroring(PCFE_NAND_CHIP pchip, unsigned char *buffer, int len, unsigned long start_addr, UINT32 start_blk); static int nandflash_btrm_skip_bad_block(PCFE_NAND_CHIP pchip, unsigned char *buffer, int len); static int nandflash_btrm_itis_what_itis(PCFE_NAND_CHIP pchip, unsigned char *buffer, int len); static void nandflash_btrm_find_bad_blocks(PCFE_NAND_CHIP pchip, unsigned long *start_addr, UINT32 *start_blk); static int nandflash_btrm_get_content_from_block(PCFE_NAND_CHIP pchip, UINT32 blkNum, unsigned char *buffer, int len); static int nandflash_btrm_check_ecc(void); static int check_btrm_ecc_for_ffs(PCFE_NAND_CHIP pchip, UINT32 step); #endif static int nandflash_read_page(PCFE_NAND_CHIP pchip, unsigned long start_addr, unsigned char *buffer, int len); int nand_flash_read_buf(unsigned short blk, int offset, unsigned char *buffer, int len); extern inline void nandflash_copy_from_spare(unsigned char *buffer, int numbytes); static int nandflash_wait_status(unsigned int status_mask); extern inline int nandflash_wait_device(void); extern inline int nandflash_wait_spare(void); static int nandflash_check_ecc(void); static int check_ecc_for_ffs(PCFE_NAND_CHIP pchip, UINT32 step); static int nandflash_read_spare_area(PCFE_NAND_CHIP pchip, unsigned long page_addr, unsigned char *buffer, int len); static int nand_is_blk_bad(PCFE_NAND_CHIP pchip, unsigned short blockNum); #endif #if defined(CFG_RAMAPP) /** Variables for CFE RAM. **/ CFE_NAND_CHIP g_chip = {NULL,0,0,0,0,0,0,0,NULL,0,0}; static unsigned char g_spare_cleanmarker[SPARE_MAX_SIZE]; static unsigned char g_spare_tffs[SPARE_MAX_SIZE]; static flash_device_info_t flash_nand_dev = { 0xffff, FLASH_IFC_NAND, "", nand_flash_sector_erase_int, // fn_flash_sector_erase_int in flash_api.c nand_flash_read_buf, // fn_flash_read_buf in flash_api.c nand_flash_write_buf, // fn_flash_write_buf in flash_api.c nand_flash_get_numsectors, // fn_flash_get_numsectors in flash_api.c nand_flash_get_sector_size, // fn_flash_get_sector_size in flash_api.c nand_flash_get_memptr, // fn_flash_get_memptr in flash_api.c nand_flash_get_blk, // fn_flash_get_blk in flash_api.c nand_flash_get_total_size, // fn_flash_get_total_size in flash_api.c nand_flash_dev_specific_cmd // fn_flash_dev_specific_cmd in flash_api.c }; // persistant flags for nand_flash_dev_specific_cmd function static int g_no_ecc = 0; static int g_write_spare_tffs = 0; /* 0,0,0,0,0,B,E,E-E,0,0,0,0,0,0,0 */ SPARE_LAYOUT brcmnand_oob_16 = {{5, 5}, {0xe0, 0x01}}; /* B,B,0,0,0,0,E,E-E,0,0,0,0,0,0,0-0,0,0,0,0,0,E,E-E,0,0,0,0,0,0,0 * 0,0,0,0,0,0,E,E-E,0,0,0,0,0,0,0-0,0,0,0,0,0,E,E-E,0,0,0,0,0,0,0 */ SPARE_LAYOUT brcmnand_oob_64 = {{0, 1}, {0xc3, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01}}; /* B,B,0,0,0,0,E,E-E,0,0,0,0,0,0,0-0,0,0,0,0,0,E,E-E,0,0,0,0,0,0,0 * 0,0,0,0,0,0,E,E-E,0,0,0,0,0,0,0-0,0,0,0,0,0,E,E-E,0,0,0,0,0,0,0 * 0,0,0,0,0,0,E,E-E,0,0,0,0,0,0,0-0,0,0,0,0,0,E,E-E,0,0,0,0,0,0,0 * 0,0,0,0,0,0,E,E-E,0,0,0,0,0,0,0-0,0,0,0,0,0,E,E-E,0,0,0,0,0,0,0 */ SPARE_LAYOUT brcmnand_oob_128 = {{0, 1}, {0xc3, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01}}; /* 0,0,0,0,0,B,0,0-0,E,E,E,E,E,E,E */ SPARE_LAYOUT brcmnand_oob_bch4_512 = {{5, 5}, {0x20, 0xfe}}; /* B,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E-0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E * 0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E-0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E */ SPARE_LAYOUT brcmnand_oob_bch4_2k = {{0, 0}, {0x01, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe}}; /* B,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E-0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E * 0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E-0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E * 0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E-0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E * 0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E-0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E */ SPARE_LAYOUT brcmnand_oob_bch4_4k = {{0, 0}, {0x01, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe}}; /* B,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E-0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E * 0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E-0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E * 0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E-0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E * 0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E-0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E * 0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E-0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E * 0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E-0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E * 0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E-0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E * 0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E-0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E */ SPARE_LAYOUT brcmnand_oob_bch4_8k = {{0, 0}, {0x01, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe}}; /* For 63138 the NAND controller use new ECC algorithm that requires more ECC bytes. ECC_Bytes_Reqd (per 512 data Bytes) = roundup(ECC_LEVEL * M/8) where M is the BCH finite field order. For early chip, M is 13. For 63138, M is 14. So it does not affect the Hamming and BCH4. But for BCH8 and BCH12, 63138 use one more byte. On 63138, for BCH8 2K page size, there is not enough spare area for cleanmarker if spare area is 16 bytes. So only the NAND part with 27 bytes spare area is supported */ #if defined(_BCM963268_) || defined(_BCM96838_) /* B,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E * 0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E */ SPARE_LAYOUT brcmnand_oob_bch8_16_2k = {{0, 0}, {0xf9, 0xff, 0xf8, 0xff, 0xf8, 0xff, 0xf8, 0xff}}; /* B,0,0,0,0,0,0,0-0,0,0,0,0,0,E,E-E,E,E,E,E,E,E,E-E,E,E * 0,0,0,0,0-0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E-E,E,E,E,E,E * 0,0-0,0,0,0,0,0,0,0-0,0,0,0,E,E,E,E-E,E,E,E,E,E,E,E-E * 0,0,0,0,0,0,0-0,0,0,0,0,0,0,E-E,E,E,E,E,E,E,E-E,E,E,E */ SPARE_LAYOUT brcmnand_oob_bch8_27_2k = {{0, 0}, {0x01, 0xc0, 0xff, 0x07, 0x00, 0xfe, 0x3f, 0x00, 0xf0, 0xff, 0x01, 0x80, 0xff, 0x0f}}; /* B,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,0,E,E,E,E,E,-E,E,E,E,E,E,E,E * 0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,0,E,E,E,E,E,-E,E,E,E,E,E,E,E * 0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,0,E,E,E,E,E,-E,E,E,E,E,E,E,E * 0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,0,E,E,E,E,E,-E,E,E,E,E,E,E,E */ SPARE_LAYOUT brcmnand_oob_bch8_16_4k = {{0, 0}, {0xf9, 0xff, 0xf8, 0xff, 0xf8, 0xff, 0xf8, 0xff, 0xf8, 0xff, 0xf8, 0xff, 0xf8, 0xff, 0xf8, 0xff}}; /* B,0,0,0,0,0,0,0-0,0,0,0,0,0,E,E-E,E,E,E,E,E,E,E-E,E,E * 0,0,0,0,0-0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E-E,E,E,E,E,E * 0,0-0,0,0,0,0,0,0,0-0,0,0,0,E,E,E,E-E,E,E,E,E,E,E,E-E * 0,0,0,0,0,0,0-0,0,0,0,0,0,0,E-E,E,E,E,E,E,E,E-E,E,E,E * 0,0,0,0-0,0,0,0,0,0,0,0-0,0,E,E,E,E,E,E-E,E,E,E,E,E,E * 0-0,0,0,0,0,0,0,0-0,0,0,0,0,E,E,E-E,E,E,E,E,E,E,E-E,E * 0,0,0,0,0,0-0,0,0,0,0,0,0,0-E,E,E,E,E,E,E,E-E,E,E,E,E * 0,0,0-0,0,0,0,0,0,0,0-0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E */ SPARE_LAYOUT brcmnand_oob_bch8_27_4k = {{0, 0}, {0x01, 0xc0, 0xff, 0x07, 0x00, 0xfe, 0x3f, 0x00, 0xf0, 0xff, 0x01, 0x80, 0xff, 0x0f, 0x00, 0xfc, 0x7f, 0x00, 0xe0, 0xff, 0x03, 0x00, 0xff, 0x1f, 0x00, 0xf8, 0xff}}; /* B,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E * 0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E * 0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E * 0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E * 0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E * 0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E * 0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E * 0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E */ SPARE_LAYOUT brcmnand_oob_bch8_16_8k = {{0, 0}, {0xf9, 0xff, 0xf8, 0xff, 0xf8, 0xff, 0xf8, 0xff, 0xf8, 0xff, 0xf8, 0xff, 0xf8, 0xff, 0xf8, 0xff, 0xf8, 0xff, 0xf8, 0xff, 0xf8, 0xff, 0xf8, 0xff, 0xf8, 0xff, 0xf8, 0xff, 0xf8, 0xff, 0xf8, 0xff}}; /* B,0,0,0,0,0,0,0-0,0,0,0,0,0,E,E-E,E,E,E,E,E,E,E-E,E,E * 0,0,0,0,0-0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E-E,E,E,E,E,E * 0,0-0,0,0,0,0,0,0,0-0,0,0,0,E,E,E,E-E,E,E,E,E,E,E,E-E * 0,0,0,0,0,0,0-0,0,0,0,0,0,0,E-E,E,E,E,E,E,E,E-E,E,E,E * 0,0,0,0-0,0,0,0,0,0,0,0-0,0,E,E,E,E,E,E-E,E,E,E,E,E,E * 0-0,0,0,0,0,0,0,0-0,0,0,0,0,E,E,E-E,E,E,E,E,E,E,E-E,E * 0,0,0,0,0,0-0,0,0,0,0,0,0,0-E,E,E,E,E,E,E,E-E,E,E,E,E * 0,0,0-0,0,0,0,0,0,0,0-0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E * 0,0,0,0,0,0,0,0-0,0,0,0,0,0,E,E-E,E,E,E,E,E,E,E-E,E,E * 0,0,0,0,0-0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E-E,E,E,E,E,E * 0,0-0,0,0,0,0,0,0,0-0,0,0,0,E,E,E,E-E,E,E,E,E,E,E,E-E * 0,0,0,0,0,0,0-0,0,0,0,0,0,0,E-E,E,E,E,E,E,E,E-E,E,E,E * 0,0,0,0-0,0,0,0,0,0,0,0-0,0,E,E,E,E,E,E-E,E,E,E,E,E,E * 0-0,0,0,0,0,0,0,0-0,0,0,0,0,E,E,E-E,E,E,E,E,E,E,E-E,E * 0,0,0,0,0,0-0,0,0,0,0,0,0,0-E,E,E,E,E,E,E,E-E,E,E,E,E * 0,0,0-0,0,0,0,0,0,0,0-0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E */ SPARE_LAYOUT brcmnand_oob_bch8_27_8k = {{0, 0}, {0x01, 0xc0, 0xff, 0x07, 0x00, 0xfe, 0x3f, 0x00, 0xf0, 0xff, 0x01, 0x80, 0xff, 0x0f, 0x00, 0xfc, 0x7f, 0x00, 0xe0, 0xff, 0x03, 0x00, 0xff, 0x1f, 0x00, 0xf8, 0xff, 0x00, 0xc0, 0xff, 0x07, 0x00, 0xfe, 0x3f, 0x00, 0xf0, 0xff, 0x01, 0x80, 0xff, 0x0f, 0x00, 0xfc, 0x7f, 0x00, 0xe0, 0xff, 0x03, 0x00, 0xff, 0x1f, 0x00, 0xf8, 0xff}}; /* B,0,0,0,0,0,0,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E * 0,0,0,0,0-0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E * 0,0-0,0,0,0,0,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E * 0,0,0,0,0,0,0-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E */ SPARE_LAYOUT brcmnand_oob_bch12_27_2k = {{0, 0}, {0x81, 0xff, 0xff, 0x07, 0xfc, 0xff, 0x3f, 0xe0, 0xff, 0xff, 0x01, 0xff, 0xff, 0x0f}}; /* B,0,0,0,0,0,0,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E * 0,0,0,0,0-0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E * 0,0-0,0,0,0,0,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E * 0,0,0,0,0,0,0-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E * 0,0,0,0-0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E * 0-0,0,0,0,0,0,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E * 0,0,0,0,0,0-0,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E * 0,0,0-0,0,0,0,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E */ SPARE_LAYOUT brcmnand_oob_bch12_27_4k = {{0, 0}, {0x81, 0xff, 0xff, 0x07, 0xfc, 0xff, 0x3f, 0xe0, 0xff, 0xff, 0x01, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0x7f, 0xc0, 0xff, 0xff, 0x03, 0xfe, 0xff, 0x1f, 0xf0, 0xff, 0xff}}; /* B,0,0,0,0,0,0,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E * 0,0,0,0,0-0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E * 0,0-0,0,0,0,0,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E * 0,0,0,0,0,0,0-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E * 0,0,0,0-0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E * 0-0,0,0,0,0,0,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E * 0,0,0,0,0,0-0,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E * 0,0,0-0,0,0,0,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E * 0,0,0,0,0,0,0,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E * 0,0,0,0,0-0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E * 0,0-0,0,0,0,0,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E * 0,0,0,0,0,0,0-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E * 0,0,0,0-0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E * 0-0,0,0,0,0,0,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E * 0,0,0,0,0,0-0,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E * 0,0,0-0,0,0,0,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E */ SPARE_LAYOUT brcmnand_oob_bch12_27_8k = {{0, 0}, {0x81, 0xff, 0xff, 0x07, 0xfc, 0xff, 0x3f, 0xe0, 0xff, 0xff, 0x01, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0x7f, 0xc0, 0xff, 0xff, 0x03, 0xfe, 0xff, 0x1f, 0xf0, 0xff, 0xff, 0x81, 0xff, 0xff, 0x07, 0xfc, 0xff, 0x3f, 0xe0, 0xff, 0xff, 0x01, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0x7f, 0xc0, 0xff, 0xff, 0x03, 0xfe, 0xff, 0x1f, 0xf0, 0xff, 0xff}}; #else /* B,0,0,0,0,0,0,0-0,0,0,0,0,E,E,E-E,E,E,E,E,E,E,E-E,E,E * 0,0,0,0,0-0,0,0,0,0,0,0,0-E,E,E,E,E,E,E,E-E,E,E,E,E,E * 0,0-0,0,0,0,0,0,0,0-0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E-E * 0,0,0,0,0,0,0-0,0,0,0,0,0,E,E-E,E,E,E,E,E,E,E-E,E,E,E */ SPARE_LAYOUT brcmnand_oob_bch8_27_2k = {{0, 0}, {0x01, 0xe0, 0xff, 0x07, 0x00, 0xff, 0x3f, 0x00, 0xf8, 0xff, 0x01, 0xc0, 0xff, 0x0f}}; /* B,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,E,E,E,E,E,E,-E,E,E,E,E,E,E,E * 0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,E,E,E,E,E,E,-E,E,E,E,E,E,E,E * 0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,E,E,E,E,E,E,-E,E,E,E,E,E,E,E * 0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,E,E,E,E,E,E,-E,E,E,E,E,E,E,E */ SPARE_LAYOUT brcmnand_oob_bch8_16_4k = {{0, 0}, {0xfd, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff}}; /* B,0,0,0,0,0,0,0-0,0,0,0,0,E,E,E-E,E,E,E,E,E,E,E-E,E,E * 0,0,0,0,0-0,0,0,0,0,0,0,0-E,E,E,E,E,E,E,E-E,E,E,E,E,E * 0,0-0,0,0,0,0,0,0,0-0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E-E * 0,0,0,0,0,0,0-0,0,0,0,0,0,E,E-E,E,E,E,E,E,E,E-E,E,E,E * 0,0,0,0-0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E-E,E,E,E,E,E,E * 0-0,0,0,0,0,0,0,0-0,0,0,0,E,E,E,E-E,E,E,E,E,E,E,E-E,E * 0,0,0,0,0,0-0,0,0,0,0,0,0,E-E,E,E,E,E,E,E,E-E,E,E,E,E * 0,0,0-0,0,0,0,0,0,0,0-0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E */ SPARE_LAYOUT brcmnand_oob_bch8_27_4k = {{0, 0}, {0x01, 0xe0, 0xff, 0x07, 0x00, 0xff, 0x3f, 0x00, 0xf8, 0xff, 0x01, 0xc0, 0xff, 0x0f, 0x00, 0xfe, 0x7f, 0x00, 0xf0, 0xff, 0x03, 0x80, 0xff, 0x1f, 0x00, 0xfc, 0xff}}; /* B,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E * 0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E * 0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E * 0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E * 0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E * 0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E * 0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E * 0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E-0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E */ SPARE_LAYOUT brcmnand_oob_bch8_16_8k = {{0, 0}, {0xfd, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff}}; /* B,0,0,0,0,0,0,0-0,0,0,0,0,E,E,E-E,E,E,E,E,E,E,E-E,E,E * 0,0,0,0,0-0,0,0,0,0,0,0,0-E,E,E,E,E,E,E,E-E,E,E,E,E,E * 0,0-0,0,0,0,0,0,0,0-0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E-E * 0,0,0,0,0,0,0-0,0,0,0,0,0,E,E-E,E,E,E,E,E,E,E-E,E,E,E * 0,0,0,0-0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E-E,E,E,E,E,E,E * 0-0,0,0,0,0,0,0,0-0,0,0,0,E,E,E,E-E,E,E,E,E,E,E,E-E,E * 0,0,0,0,0,0-0,0,0,0,0,0,0,E-E,E,E,E,E,E,E,E-E,E,E,E,E * 0,0,0-0,0,0,0,0,0,0,0-0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E * 0,0,0,0,0,0,0,0-0,0,0,0,0,E,E,E-E,E,E,E,E,E,E,E-E,E,E * 0,0,0,0,0-0,0,0,0,0,0,0,0-E,E,E,E,E,E,E,E-E,E,E,E,E,E * 0,0-0,0,0,0,0,0,0,0-0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E-E * 0,0,0,0,0,0,0-0,0,0,0,0,0,E,E-E,E,E,E,E,E,E,E-E,E,E,E * 0,0,0,0-0,0,0,0,0,0,0,0-0,E,E,E,E,E,E,E-E,E,E,E,E,E,E * 0-0,0,0,0,0,0,0,0-0,0,0,0,E,E,E,E-E,E,E,E,E,E,E,E-E,E * 0,0,0,0,0,0-0,0,0,0,0,0,0,E-E,E,E,E,E,E,E,E-E,E,E,E,E * 0,0,0-0,0,0,0,0,0,0,0-0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E */ SPARE_LAYOUT brcmnand_oob_bch8_27_8k = {{0, 0}, {0x01, 0xe0, 0xff, 0x07, 0x00, 0xff, 0x3f, 0x00, 0xf8, 0xff, 0x01, 0xc0, 0xff, 0x0f, 0x00, 0xfe, 0x7f, 0x00, 0xf0, 0xff, 0x03, 0x80, 0xff, 0x1f, 0x00, 0xfc, 0xff, 0x00, 0xe0, 0xff, 0x07, 0x00, 0xff, 0x3f, 0x00, 0xf8, 0xff, 0x01, 0xc0, 0xff, 0x0f, 0x00, 0xfe, 0x7f, 0x00, 0xf0, 0xff, 0x03, 0x80, 0xff, 0x1f, 0x00, 0xfc, 0xff}}; /* B,0,0,0,0,0,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E * 0,0,0,0,0-0,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E * 0,0-0,0,0,0,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E * 0,0,0,0,0,0,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E */ SPARE_LAYOUT brcmnand_oob_bch12_27_2k = {{0, 0}, {0xc1, 0xff, 0xff, 0x07, 0xfe, 0xff, 0x3f, 0xf0, 0xff, 0xff, 0x81, 0xff, 0xff, 0x0f}}; /* B,0,0,0,0,0,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E * 0,0,0,0,0-0,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E * 0,0-0,0,0,0,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E * 0,0,0,0,0,0,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E * 0,0,0,0-0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E * 0-0,0,0,0,0,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E * 0,0,0,0,0,0-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E * 0,0,0-0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E */ SPARE_LAYOUT brcmnand_oob_bch12_27_4k = {{0, 0}, {0xc1, 0xff, 0xff, 0x07, 0xfe, 0xff, 0x3f, 0xf0, 0xff, 0xff, 0x81, 0xff, 0xff, 0x0f, 0xfc, 0xff, 0x7f, 0xe0, 0xff, 0xff, 0x03, 0xff, 0xff, 0x1f, 0xf8, 0xff, 0xff}}; /* B,0,0,0,0,0,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E * 0,0,0,0,0-0,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E * 0,0-0,0,0,0,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E * 0,0,0,0,0,0,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E * 0,0,0,0-0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E * 0-0,0,0,0,0,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E * 0,0,0,0,0,0-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E * 0,0,0-0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E * 0,0,0,0,0,0,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E * 0,0,0,0,0-0,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E * 0,0-0,0,0,0,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E * 0,0,0,0,0,0,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E * 0,0,0,0-0,0,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E * 0-0,0,0,0,0,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E * 0,0,0,0,0,0-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E * 0,0,0-0,0,0,E,E,E,E,E-E,E,E,E,E,E,E,E-E,E,E,E,E,E,E,E */ SPARE_LAYOUT brcmnand_oob_bch12_27_8k = {{0, 0}, {0xc1, 0xff, 0xff, 0x07, 0xfe, 0xff, 0x3f, 0xf0, 0xff, 0xff, 0x81, 0xff, 0xff, 0x0f, 0xfc, 0xff, 0x7f, 0xe0, 0xff, 0xff, 0x03, 0xff, 0xff, 0x1f, 0xf8, 0xff, 0xff, 0xc0, 0xff, 0xff, 0x07, 0xfe, 0xff, 0x3f, 0xf0, 0xff, 0xff, 0x81, 0xff, 0xff, 0x0f, 0xfc, 0xff, 0x7f, 0xe0, 0xff, 0xff, 0x03, 0xff, 0xff, 0x1f, 0xf8, 0xff, 0xff}}; #endif #else /** Variables for CFE ROM. **/ /* DO NOT define any global variables, static variable and * any literal strings for CFE ROM code. It is shared by * the CFE util which does not load all the data segments * because the total cfe util size exceeds 16KB NAND limitation. * Global variable are defined in the init_utils_mips.S to make * sure they are within 16KB. */ #ifdef CFG_BOOTUTILS extern #endif CFE_NAND_CHIP g_chip; #endif static void nand_reset_device(void) { NAND->NandNandBootConfig = NBC_AUTO_DEV_ID_CFG | 1; #if (INC_SPI_FLASH_DRIVER==1) cfe_usleep(1000); #endif nandflash_wait_device(); NAND->NandCsNandXor = 0; return; } #if defined(CFG_RAMAPP) /*************************************************************************** * Function Name: nand_flash_init * Description : Initialize flash part. * Returns : FLASH_API_OK or FLASH_API_ERROR ***************************************************************************/ int nand_flash_init(flash_device_info_t **flash_info) { static int initialized = 0; int ret = FLASH_API_OK; if( initialized == 0 ) { PCFE_NAND_CHIP pchip = &g_chip; static struct flash_name_from_id fnfi[] = NAND_FLASH_DEVICES; static struct flash_name_from_id fmfi[] = NAND_FLASH_MANUFACTURERS; struct flash_name_from_id *fnfi_ptr; DBG_PRINTF(">> nand_flash_init - entry\n"); /* Enable NAND data on MII ports. */ #if defined(_BCM963268_) PERF->blkEnables |= NAND_CLK_EN; #endif #if defined(_BCM963268_) && (INC_SPI_FLASH_DRIVER==1) GPIO->GPIOBaseMode |= NAND_GPIO_OVERRIDE; #endif #if (INC_SPI_FLASH_DRIVER==1) { unsigned int acc = 0; /* when boot from SPI, the nand controller always set ECC to hamming. update it based on strap */ acc = NAND->NandAccControl; #if defined(_BCM963268_) acc &= ~(NAC_ECC_LVL_MASK|NAC_ECC_LVL_0_MASK); switch(MISC->miscStrapBus&MISC_STRAP_BUS_NAND_ECC_SELECT_MASK) { case MISC_STRAP_BUS_NAND_ECC_8_BIT: acc |= (NAC_ECC_LVL_BCH_8 << NAC_ECC_LVL_SHIFT)|(NAC_ECC_LVL_BCH_8 << NAC_ECC_LVL_0_SHIFT); acc &= ~(NAC_SPARE_SZ_MASK|NAC_SPARE_SZ_0_MASK); acc |= (27<miscStrapBus&MISC_STRAP_BUS_BOOT_SEL_ECC_MASK) { case MISC_STRAP_BUS_BOOT_NAND_ECC_12_BIT: acc |= (NAC_ECC_LVL_BCH_12 << NAC_ECC_LVL_SHIFT); acc &= ~NAC_SPARE_SZ_MASK; acc |= (27<NandAccControl = acc; } #endif nand_reset_device(); NAND->NandCmdAddr = 0; NAND->NandCmdExtAddr = 0; NAND->NandCmdStart = NCMD_DEV_ID_READ; nandflash_wait_cmd(); /* Read the NAND flash chip id. */ pchip->chip_device_id = NAND->NandFlashDeviceId; flash_nand_dev.flash_device_id = NAND_CHIPID(pchip); for( fnfi_ptr = fnfi; fnfi_ptr->fnfi_id != 0; fnfi_ptr++ ) { if( fnfi_ptr->fnfi_id == flash_nand_dev.flash_device_id ) { strcpy(flash_nand_dev.flash_device_name, fnfi_ptr->fnfi_name); break; } } /* If NAND chip is not in the list of NAND chips, try to identify the * manufacturer. */ if( flash_nand_dev.flash_device_name[0] == '\0' ) { for( fnfi_ptr = fmfi; fnfi_ptr->fnfi_id != 0; fnfi_ptr++ ) { if( fnfi_ptr->fnfi_id == flash_nand_dev.flash_device_id ) { strcpy(flash_nand_dev.flash_device_name, fnfi_ptr->fnfi_name); break; } } } *flash_info = &flash_nand_dev; pchip->chip_base = 0; ret = nand_adjust_cfg(pchip); if( ret != FLASH_API_OK ) { *flash_info = NULL; return ret; } nand_init_cleanmarker(pchip); DBG_PRINTF(">> nand_flash_init - return %d\n", ret); initialized = 1; } else *flash_info = &flash_nand_dev; return( ret ); } /* nand_flash_init */ /*************************************************************************** * Function Name: nand_init_cleanmarker * Description : Initializes the JFFS2 clean marker buffer. * Returns : None. ***************************************************************************/ static void nand_init_cleanmarker(PCFE_NAND_CHIP pchip) { unsigned short cleanmarker[] = JFFS2_CLEANMARKER; unsigned char *pcm = (unsigned char *) cleanmarker; unsigned char *spare_mask = pchip->chip_spare_mask; int i, j; /* Skip spare area offsets reserved for ECC bytes. */ for( i = 0, j = 0; i < pchip->chip_spare_size; i++ ) { if( ECC_MASK_BIT(spare_mask, i) == 0 && j < sizeof(cleanmarker)) g_spare_cleanmarker[i] = pcm[j++]; else g_spare_cleanmarker[i] = 0xff; } } /* nand_init_cleanmarker */ #else /*************************************************************************** * Function Name: rom_nand_flash_init * Description : Initialize flash part just enough to read blocks. * Returns : FLASH_API_OK or FLASH_API_ERROR ***************************************************************************/ void rom_nand_flash_init(void) { PCFE_NAND_CHIP pchip = &g_chip; /* Enable NAND data on MII ports. */ #if defined(_BCM963268_) PERF->blkEnables |= NAND_CLK_EN; #endif nand_reset_device(); pchip->chip_base = 0; /* Read the chip id. Only use the most signficant 16 bits. */ pchip->chip_device_id = NAND->NandFlashDeviceId; nand_adjust_cfg(pchip); } /* nand_flash_init */ #endif /*************************************************************************** * Function Name: nand_adjust_cfg * Description : Reads and stores the chip configuration. * Returns : None. ***************************************************************************/ static int nand_adjust_cfg(PCFE_NAND_CHIP pchip) { /* Read chip configuration. */ unsigned int cfg = NAND->NandConfig; int ret = FLASH_API_OK; /* Special case changes from what the NAND controller configured. */ switch(NAND_CHIPID(pchip)) { case NAND_MAKE_ID(FLASHTYPE_HYNIX,HYNIX_H27U1G8F2B): cfg &= ~(NC_DEV_SIZE_MASK | NC_FUL_ADDR_MASK | (0xff << NC_BLK_ADDR_SHIFT)); /* 128 MB device size, 4 full address bytes, 2 column address bytes, 2 block address bytes */ cfg |= (5 << NC_DEV_SIZE_SHIFT) | (0x04 << NC_FUL_ADDR_SHIFT) | (0x22 << NC_BLK_ADDR_SHIFT); NAND->NandConfig = cfg; break; case NAND_MAKE_ID(FLASHTYPE_SAMSUNG,SAMSUNG_K9F5608U0A): case NAND_MAKE_ID(FLASHTYPE_SAMSUNG,SAMSUNG_K9F1208U0): case NAND_MAKE_ID(FLASHTYPE_SAMSUNG,SAMSUNG_K9F1G08U0): case NAND_MAKE_ID(FLASHTYPE_HYNIX,HYNIX_H27U518S2C): /* Set device id "cell type" to 0 (SLC). */ pchip->chip_device_id &= ~NAND_CI_CELLTYPE_MSK; NAND->NandFlashDeviceId = pchip->chip_device_id; break; case NAND_MAKE_ID(FLASHTYPE_MXIC,MXIC_MX30LF1208AA): /* This 64MB device was detected as 256MB device on 63268. Manually update * device size in the cfg register. */ cfg &= ~NC_DEV_SIZE_MASK; cfg |= (0x04<NandConfig = cfg; break; case NAND_MAKE_ID(FLASHTYPE_SPANSION,SPANSION_S34ML01G1): /* Set device size to 128MB, it is misconfigured to 512MB. */ cfg &= ~NC_DEV_SIZE_MASK; cfg |= (0x05<NandConfig = cfg; break; case NAND_MAKE_ID(FLASHTYPE_SPANSION,SPANSION_S34ML02G1): /* Set device size to 256MB, it is misconfigured to 512MB. */ cfg &= ~NC_DEV_SIZE_MASK; cfg |= (0x06<NandConfig = cfg; break; case NAND_MAKE_ID(FLASHTYPE_SPANSION,SPANSION_S34ML04G1): /* Set block size to 128KB, it is misconfigured to 512MB. */ #if defined (_BCM963268_) || defined(_BCM96838_) || defined(_BCM963381_) || defined(_BCM963138_) || defined(_BCM947189_) cfg &= ~NC_BLK_SIZE_MASK; cfg |= NC_BLK_SIZE_128K; NAND->NandConfig = cfg; #endif break; #if defined(CONFIG_BRCM_IKOS) case 0x2cda: case 0x2cdc: /* Set device id "cell type" to 0 (SLC). */ pchip->chip_device_id &= ~NAND_CI_CELLTYPE_MSK; /* simulate only 4MB */ cfg &= ~NC_DEV_SIZE_MASK; NAND->NandConfig = cfg; printf("Simulated Micron NAND device id: 0x%x\n", NAND_CHIPID(pchip)); break; case 0x2c75: /* simulate only 4MB */ cfg &= ~NC_DEV_SIZE_MASK; NAND->NandConfig = cfg; cfg = NAND->NandConfigExt; cfg &= ~NC_BLK_SIZE_MASK; cfg |= NC_BLK_SIZE_16K; NAND->NandConfigExt = cfg; printf("Simulated Micron NAND device id: 0x%x\n", NAND_CHIPID(pchip)); break; #endif } pchip->chip_total_size = (4 * (1 << ((cfg & NC_DEV_SIZE_MASK) >> NC_DEV_SIZE_SHIFT))) << 20; /* block size and page size move to ext config register in NAND controller rev7.1 and later */ #if defined(_BCM963148_) || defined(_BCM96848_) || defined(_BCM94908_) || defined(_BCM96858_) || defined(_BCM96855_) || \ defined(_BCM963158_) || defined(_BCM96846_) || defined(_BCM96856_) || defined(_BCM963178_) || defined(_BCM947622_) || defined(_BCM96878_) || defined(_BCM96855_) cfg = NAND->NandConfigExt; #endif switch( (cfg & NC_BLK_SIZE_MASK) ) { case NC_BLK_SIZE_2048K: pchip->chip_block_size = 2048 * 1024; break; case NC_BLK_SIZE_1024K: pchip->chip_block_size = 1024 * 1024; break; case NC_BLK_SIZE_512K: pchip->chip_block_size = 512 * 1024; break; case NC_BLK_SIZE_256K: pchip->chip_block_size = 256 * 1024; break; case NC_BLK_SIZE_128K: pchip->chip_block_size = 128 * 1024; break; case NC_BLK_SIZE_16K: pchip->chip_block_size = 16 * 1024; break; case NC_BLK_SIZE_8K: pchip->chip_block_size = 8 * 1024; break; } switch( (cfg & NC_PG_SIZE_MASK) ) { case NC_PG_SIZE_8K: pchip->chip_page_size = 8 * 1024; break; case NC_PG_SIZE_4K: pchip->chip_page_size = 4 * 1024; break; case NC_PG_SIZE_2K: pchip->chip_page_size = 2 * 1024; break; case NC_PG_SIZE_512B: pchip->chip_page_size = 512; break; } #if defined(CFG_RAMAPP) { unsigned int acc; unsigned int acc_save; acc = NAND->NandAccControl; acc_save = acc; /* always treat parallel NAND flash parts as requiring the whole page to be written at once for new chips */ #if !defined(_BCM963268_) && !defined(_BCM96838_) if( (acc & NAC_PARTIAL_PAGE_EN) != 0 ) #else if( (acc & (NAC_FAST_PGM_RDIN | NAC_PARTIAL_PAGE_EN)) != 0 ) #endif { DBG_PRINTF("Disable NAND fast program read and partial page enable\n"); #if !defined(_BCM963268_) && !defined(_BCM96838_) acc &= ~NAC_PARTIAL_PAGE_EN; #else acc &= ~(NAC_FAST_PGM_RDIN | NAC_PARTIAL_PAGE_EN); #endif } if (NAND_IS_MLC(pchip) && (((acc & NAC_ECC_LVL_MASK)>>NAC_ECC_LVL_SHIFT)==NAC_ECC_LVL_HAMMING)) { DBG_PRINTF("Changing ECC to BCH-4 from Hamming for MLC part\n"); #if !defined(_BCM963268_) && !defined(_BCM96838_) acc &= ~NAC_ECC_LVL_MASK; acc |= (NAC_ECC_LVL_BCH_4 << NAC_ECC_LVL_SHIFT); #else acc &= ~(NAC_ECC_LVL_0_MASK | NAC_ECC_LVL_MASK); acc |= (NAC_ECC_LVL_BCH_4 << NAC_ECC_LVL_0_SHIFT) | (NAC_ECC_LVL_BCH_4 << NAC_ECC_LVL_SHIFT); #endif } #if defined(NAC_PREFETCH_EN) && !defined(_BCM947189_) /* turn on prefetch bit for performance*/ if( NAND->NandRevision >= 0x00000700 && pchip->chip_page_size > 512 ) acc |= NAC_PREFETCH_EN; #endif if( acc != acc_save ) { NAND->NandAccControl = acc; DBG_PRINTF("NAND Access Control register = 0x%8.8lx, was 0x%8.8lx\n", acc, acc_save); } } #if !(INC_BTRM_BOOT==1) && !defined(CONFIG_BRCM_IKOS) if( (NAND->NandRevision & 0xffff) >= 0x00000400 ) { /* adjust reading timing */ /* Default of TRP=4 and TREAD=5 for Hynix parts on 63268 */ /* Almost all parts could use TRP=3 and TREAD=4 */ NAND->NandTiming1 &= ~(NT_TREH_MASK|NT_TRP_MASK); NAND->NandTiming1 |= (2<NandTiming2 &= ~NT_TREAD_MASK; NAND->NandTiming2 |= (5<NandRevision <= 0x00000202 ) { PSPARE_LAYOUT spare_layout = NULL; pchip->chip_spare_step_size = 16; pchip->chip_spare_size = pchip->chip_page_size >> 5; switch(pchip->chip_page_size) { case 2048: spare_layout = &brcmnand_oob_64; break; case 4096: spare_layout = &brcmnand_oob_128; break; /* case 512: */ default: spare_layout = &brcmnand_oob_16; break; } pchip->chip_spare_mask = spare_layout->sl_spare_mask; pchip->chip_bi_index_1 = spare_layout->sl_bi_ofs[0]; pchip->chip_bi_index_2 = spare_layout->sl_bi_ofs[1]; } else { PSPARE_LAYOUT spare_layout = NULL; unsigned int acc = NAND->NandAccControl; unsigned int ecc_lvl = (acc & NAC_ECC_LVL_MASK) >> NAC_ECC_LVL_SHIFT; unsigned int layout_parms; /* The access control register spare size is the number of spare area * bytes per 512 bytes of data. The chip_spare_size is the number * of spare area bytes per page. */ pchip->chip_spare_step_size = ((acc & NAC_SPARE_SZ_MASK) >> NAC_SPARE_SZ_SHIFT); pchip->chip_spare_size = pchip->chip_spare_step_size * (pchip->chip_page_size >> 9); layout_parms = LAYOUT_PARMS(ecc_lvl, pchip->chip_spare_size, pchip->chip_page_size); DBG_PRINTF(">> nand_adjust_cfg - layout_parms=0x%8.8lx\n", layout_parms); if( ecc_lvl == NAC_ECC_LVL_DISABLE ) { #if defined(CONFIG_BRCM_IKOS) ecc_lvl = NAC_ECC_LVL_HAMMING; printf("ECC force to hamming for IKOS\n "); layout_parms = LAYOUT_PARMS(ecc_lvl, pchip->chip_spare_size, pchip->chip_page_size); #else printf("ECC Disabled is not supported\n "); return FLASH_API_ERROR; #endif } else if( ecc_lvl == NAC_ECC_LVL_HAMMING ) printf("NAND ECC Hamming, "); else printf("NAND ECC BCH-%d, ", ecc_lvl); printf("page size 0x%x bytes, spare size used %d bytes\n", pchip->chip_page_size, pchip->chip_spare_size); switch(layout_parms) { case (LAYOUT_PARMS(NAC_ECC_LVL_HAMMING,16,512)): spare_layout = &brcmnand_oob_16; break; case (LAYOUT_PARMS(NAC_ECC_LVL_HAMMING,64,2048)): spare_layout = &brcmnand_oob_64; break; case (LAYOUT_PARMS(NAC_ECC_LVL_HAMMING,128,4096)): spare_layout = &brcmnand_oob_128; break; case (LAYOUT_PARMS(NAC_ECC_LVL_BCH_4,16,512)): spare_layout = &brcmnand_oob_bch4_512; break; case (LAYOUT_PARMS(NAC_ECC_LVL_BCH_4,64,2048)): spare_layout = &brcmnand_oob_bch4_2k; break; case (LAYOUT_PARMS(NAC_ECC_LVL_BCH_4,128,4096)): spare_layout = &brcmnand_oob_bch4_4k; break; case (LAYOUT_PARMS(NAC_ECC_LVL_BCH_4,256,8192)): spare_layout = &brcmnand_oob_bch4_8k; break; #if defined(_BCM963268_) || defined(_BCM96838_) case (LAYOUT_PARMS(NAC_ECC_LVL_BCH_8,64,2048)): spare_layout = &brcmnand_oob_bch8_16_2k; break; #endif case (LAYOUT_PARMS(NAC_ECC_LVL_BCH_8,108,2048)): spare_layout = &brcmnand_oob_bch8_27_2k; break; case (LAYOUT_PARMS(NAC_ECC_LVL_BCH_8,128,4096)): spare_layout = &brcmnand_oob_bch8_16_4k; break; case (LAYOUT_PARMS(NAC_ECC_LVL_BCH_8,216,4096)): spare_layout = &brcmnand_oob_bch8_27_4k; break; case (LAYOUT_PARMS(NAC_ECC_LVL_BCH_8,256,8192)): spare_layout = &brcmnand_oob_bch8_16_8k; break; case (LAYOUT_PARMS(NAC_ECC_LVL_BCH_8,432,8192)): spare_layout = &brcmnand_oob_bch8_27_8k; break; case (LAYOUT_PARMS(NAC_ECC_LVL_BCH_12,108,2048)): spare_layout = &brcmnand_oob_bch12_27_2k; break; case (LAYOUT_PARMS(NAC_ECC_LVL_BCH_12,216,4096)): spare_layout = &brcmnand_oob_bch12_27_4k; break; case (LAYOUT_PARMS(NAC_ECC_LVL_BCH_12,432,8192)): spare_layout = &brcmnand_oob_bch12_27_8k; break; default: printf("No valid spare layout for level=%lu, spare size=%lu," " page size=%lu\n", ecc_lvl, pchip->chip_spare_size, pchip->chip_page_size); return FLASH_API_ERROR; } pchip->chip_spare_mask = spare_layout->sl_spare_mask; pchip->chip_bi_index_1 = spare_layout->sl_bi_ofs[0]; pchip->chip_bi_index_2 = spare_layout->sl_bi_ofs[1]; } DBG_PRINTF(">> nand_adjust_cfg - size=%luMB, block=%luKB, page=%luB, " "spare=%lu\n", pchip->chip_total_size / (1024 * 1024), pchip->chip_block_size / 1024, pchip->chip_page_size, pchip->chip_spare_size); #else /* for cfe rom, only need to know the bbi location */ { uint32_t acc = NAND->NandAccControl; uint32_t ecc_lvl = (acc & NAC_ECC_LVL_MASK) >> NAC_ECC_LVL_SHIFT; /* The access control register spare size is the number of spare area * bytes per 512 bytes of data. The chip_spare_size is the number * of spare area bytes per page. */ pchip->chip_spare_step_size = ((acc & NAC_SPARE_SZ_MASK) >> NAC_SPARE_SZ_SHIFT); pchip->chip_spare_size = pchip->chip_spare_step_size * (pchip->chip_page_size >> 9); if( ecc_lvl == NAC_ECC_LVL_HAMMING ) { if( pchip->chip_page_size == 512 ) { pchip->chip_bi_index_1 = pchip->chip_bi_index_2 = 5; } else { pchip->chip_bi_index_1 = 0; pchip->chip_bi_index_2 = 1; } } else if( ecc_lvl == NAC_ECC_LVL_BCH_4 && pchip->chip_page_size == 512 ) { pchip->chip_bi_index_1 = pchip->chip_bi_index_2 = 5; } else pchip->chip_bi_index_1 = pchip->chip_bi_index_2 = 0; } #endif NAND->NandCmdStart = NCMD_PARAM_READ; if( ( nandflash_wait_cmd() == FLASH_API_OK ) && ( nandflash_wait_cache() == FLASH_API_OK ) ) { // Hardware NAND controller does not take into account LUNs, so if this value is not 1 we calculate the die stack NAND size unsigned char onfi[256]; nandflash_copy_from_cache(onfi, 0, 256); if ( (onfi[ONFI_SIGNATURE] == 'O') && (onfi[ONFI_SIGNATURE+1] == 'N') && (onfi[ONFI_SIGNATURE+2] == 'F') && (onfi[ONFI_SIGNATURE+3] == 'I') && (onfi[ONFI_LOGICAL_UNITS] != 1) ) { // adjust size based on # of luns unsigned int cfg; unsigned int size = ((onfi[ONFI_DATA_BYTES_PER_PAGE+3] << 24) | (onfi[ONFI_DATA_BYTES_PER_PAGE+2] << 16) | (onfi[ONFI_DATA_BYTES_PER_PAGE+1] << 8) | (onfi[ONFI_DATA_BYTES_PER_PAGE])) * // bytes per page ((onfi[ONFI_PAGES_PER_BLOCK+3] << 24) | (onfi[ONFI_PAGES_PER_BLOCK+2] << 16) | (onfi[ONFI_PAGES_PER_BLOCK+1] << 8) | (onfi[ONFI_PAGES_PER_BLOCK])) * // pages per block ((onfi[ONFI_BLOCKS_PER_UNIT+3] << 24) | (onfi[ONFI_BLOCKS_PER_UNIT+2] << 16) | (onfi[ONFI_BLOCKS_PER_UNIT+1] << 8) | (onfi[ONFI_BLOCKS_PER_UNIT])) * // blocks per LUN (onfi[ONFI_LOGICAL_UNITS]); // LUNs DBG_PRINTF("ONFI detected, old size 0x%x, new size 0x%x\n", pchip->chip_total_size, size); pchip->chip_total_size = size; cfg = 0; size >>= 23; while(size) { cfg++; size >>= 1; } NAND->NandConfig = (NAND->NandConfig & ~NC_DEV_SIZE_MASK) | (cfg << NC_DEV_SIZE_SHIFT); } } return ret; } /* nand_adjust_cfg */ /* Check if the block is good or bad. If bad returns 1, if good returns 0 */ static int nand_is_blk_bad(PCFE_NAND_CHIP pchip, unsigned short blockNum) { #ifdef CONFIG_BRCM_IKOS /* spare area not supported in sim/emu environment */ return 0; // good block #else #if defined(CFG_RAMAPP) unsigned char *spare; #else /* CFE ROM does not have real heap. KMALLOC simply use mem_heapstart memory. * as caller of this function already use mem_heapstart, we can't use heap again * must switch to stack. KFREE is a nop for CFE ROM */ unsigned char spare[SPARE_MAX_SIZE]; #endif UINT32 page_addr = (blockNum * pchip->chip_block_size) & ~(pchip->chip_page_size - 1); int i; if (blockNum == 0) return 0; // always return good for block 0, because if it's a bad chip quite possibly the board is useless #if defined(CFG_RAMAPP) spare = KMALLOC(SPARE_MAX_SIZE, 0); #endif /* Read the spare area of first page and check for bad block indicator */ for (i=0; i<2; i+=1, page_addr += pchip->chip_page_size) { if (nandflash_read_spare_area(pchip, page_addr, spare, pchip->chip_spare_size) == FLASH_API_OK) { if ( (spare[pchip->chip_bi_index_1] != SPARE_GOOD_MARKER) || (spare[pchip->chip_bi_index_2] != SPARE_GOOD_MARKER)) { KFREE(spare); return 1; // bad block } } else { KFREE(spare); return 1;//bad block } } KFREE(spare); return 0; // good block #endif } #if !defined(CFG_RAMAPP) /* This function always returns that a NAND flash block spare area contains a * JFFS2 clean marker for the CFE ROM build. It does not do the validation in * order to save code and data space. However, the CFE ROM boot code verifies * that the first two bytes of each NAND flash data block contain the JFFS2 * magic number so there is NAND flash block validation. */ static int nand_is_blk_cleanmarker(PCFE_NAND_CHIP pchip, unsigned long start_addr) { /* Always return TRUE in CFE ROM. */ return(1); } #else /*************************************************************************** * Function Name: nand_is_blk_cleanmarker * Description : Compares a buffer to see if it a JFFS2 cleanmarker. * Returns : 1 - is cleanmarker, 0 - is not cleanmarker ***************************************************************************/ static int nand_is_blk_cleanmarker(PCFE_NAND_CHIP pchip, unsigned long start_addr) { unsigned short cleanmarker[] = JFFS2_CLEANMARKER; unsigned char *pcm = (unsigned char *) cleanmarker; unsigned char *spare = KMALLOC(SPARE_MAX_SIZE, 0); unsigned char *comparebuf = KMALLOC(SPARE_MAX_SIZE, 0); unsigned char *spare_mask = pchip->chip_spare_mask; unsigned int i, j; int ret = 1; if( nandflash_read_spare_area( pchip, start_addr, spare, pchip->chip_spare_size) == FLASH_API_OK ) { /* Skip spare offsets that are reserved for the ECC. Make spare data * bytes contiguous in the spare buffer. */ for( i = 0, j = 0; i < pchip->chip_spare_size; i++ ) if( ECC_MASK_BIT(spare_mask, i) == 0 ) comparebuf[j++] = spare[i]; /* Compare spare area data to the JFFS2 cleanmarker. */ for( i = 0, ret = 1; i < sizeof(cleanmarker) && ret == 1; i++ ) if( comparebuf[i] != pcm[i]) ret = 0; } if( (spare[pchip->chip_bi_index_1] != SPARE_GOOD_MARKER) || (spare[pchip->chip_bi_index_2] != SPARE_GOOD_MARKER) ) { /* This is a bad block */ ret = 0; } KFREE(spare); KFREE(comparebuf); return( ret ); } /* nand_is_blk_cleanmarker */ /*************************************************************************** * Function Name: nand_initialize_spare_area * Description : Initializes the spare area of the first page of each block * to a cleanmarker. * Returns : FLASH_API_OK or FLASH_API_ERROR ***************************************************************************/ static int nand_initialize_spare_area(PCFE_NAND_CHIP pchip, int eraseBadBlocks) { unsigned char *spare = KMALLOC(SPARE_MAX_SIZE, 0); unsigned int i; int ret; DBG_PRINTF(">> nand_initialize_spare_area - entry\n"); for( i = 0; i < pchip->chip_total_size; i += pchip->chip_block_size ) { /* Read the current spare area. */ ret = nandflash_read_spare_area(pchip,i,spare,pchip->chip_spare_size); if(ret == FLASH_API_OK) { /* if eraseBadBlocks is set, erase bad blocks, else check spare area to see if * the block is good and then erase. */ int writeSpare = eraseBadBlocks || ( (spare[pchip->chip_bi_index_1] == SPARE_GOOD_MARKER) && (spare[pchip->chip_bi_index_2] == SPARE_GOOD_MARKER) ); if( writeSpare && (nandflash_block_erase(pchip, i, eraseBadBlocks) != FLASH_API_OK) ) { printf("\nInitialization spare area error blk=%d writeSpare=%d\n", i/pchip->chip_block_size, writeSpare); } } } KFREE(spare); return( FLASH_API_OK ); } /* nand_initialize_spare_area */ /*************************************************************************** * Function Name: nand_mark_bad_blk * Description : Marks the specified block as bad by writing 0xFFs to the * spare area and updating the in memory bad block table. * Returns : None. ***************************************************************************/ static void nand_mark_bad_blk(PCFE_NAND_CHIP pchip, unsigned long page_addr) { static int marking_bad_blk = 0; unsigned char *spare; unsigned int blk = page_addr/pchip->chip_block_size; if (nand_is_blk_bad(pchip, blk)) { printf("nand_mark_bad_blk() : Attempting to mark a bad block %d that is already bad.\n", blk); return ; } spare = KMALLOC(SPARE_MAX_SIZE, 0); if( marking_bad_blk == 0 ) { marking_bad_blk = 1; DBG_PRINTF(">> nand_mark_bad_blk - addr=0x%8.8lx, block=0x%8.8lx\n", page_addr, page_addr / pchip->chip_block_size); nandflash_block_erase(pchip, page_addr, 0); memset(spare, 0xff, pchip->chip_spare_size); spare[pchip->chip_bi_index_1] = SPARE_BI_MARKER; spare[pchip->chip_bi_index_2] = SPARE_BI_MARKER; nandflash_write_spare_area(pchip,page_addr,spare,pchip->chip_spare_size); #if (INC_BTRM_BOOT==1) && defined(_BCM963268_) if( page_addr / pchip->chip_block_size > 2 ) #endif { /* Also mark the first page in the block as bad so the Linux NAND * flash driver finds it when updating the BBT. */ page_addr &= ~(pchip->chip_block_size - 1); nandflash_write_spare_area(pchip, page_addr, spare, pchip->chip_spare_size); /* Mark second page of the block as well */ page_addr += pchip->chip_page_size; nandflash_write_spare_area(pchip, page_addr, spare, pchip->chip_spare_size); } marking_bad_blk = 0; } KFREE(spare); } /* nand_mark_bad_blk */ /*************************************************************************** * Function Name: nand_flash_sector_erase_int * Description : Erase the specfied flash sector. * Returns : FLASH_API_OK or FLASH_API_ERROR ***************************************************************************/ static int nand_flash_sector_erase_int(unsigned short blk) { int ret = FLASH_API_OK; PCFE_NAND_CHIP pchip = &g_chip; { unsigned long page_addr = blk * pchip->chip_block_size; if( (ret = nandflash_block_erase(pchip, page_addr, 0)) != FLASH_API_OK ) printf("\nError erasing flash block, blk=%d\n", blk); #if defined(_BCM963138_) || defined(_BCM963148_) else { /* write clean marker in block 0 page 0 for backward compatiblity for 416L01 in 63148/63138 as these chip's cfe rom/nvram starts from 64KB offset. When everybody move to 416L02, we can remove this code */ if( page_addr == 0 ) ret = nandflash_write_spare_area(pchip, page_addr, g_spare_cleanmarker, pchip->chip_spare_size); } #endif } return( ret ); } /* nand_flash_sector_erase_int */ #endif /*************************************************************************** * Function Name: nand_flash_read_buf * Description : Reads from flash memory. * Returns : number of bytes read or FLASH_API_ERROR ***************************************************************************/ #if defined(CFG_RAMAPP) static #endif int nand_flash_read_buf(unsigned short blk, int offset, unsigned char *buffer, int len) { int ret; PCFE_NAND_CHIP pchip = &g_chip; UINT32 start_addr; UINT32 blk_addr; UINT32 blk_offset; UINT32 size; UINT32 numBlocksInChip = pchip->chip_total_size/pchip->chip_block_size; #if defined(CFG_RAMAPP) bool flash_features; flash_features = (len & FLASH_FEATURES); len &= ~FLASH_FEATURES; #endif ret = len; DBG_PRINTF(">> nand_flash_read_buf - 1 blk=0x%8.8lx, offset=%d, len=%lu\n", blk, offset, len); start_addr = (blk * pchip->chip_block_size) + offset; blk_addr = start_addr & ~(pchip->chip_block_size - 1); blk_offset = start_addr - blk_addr; size = pchip->chip_block_size - blk_offset; if(size > len) size = len; if (blk >= numBlocksInChip) { printf("Attempt to read block number(%d) beyond the nand max blk(%d) \n", blk, numBlocksInChip-1); return (FLASH_API_ERROR); } #if defined(CFG_RAMAPP) if (flash_features) ret = nand_is_blk_cleanmarker(pchip, blk_addr); #endif if (len) do { if (nandflash_read_page(pchip,start_addr,buffer,size) != FLASH_API_OK) { ret = FLASH_API_ERROR; break; } len -= size; if( len ) { blk++; DBG_PRINTF(">> nand_flash_read_buf - 2 blk=0x%8.8lx, len=%lu\n", blk, len); start_addr = blk * pchip->chip_block_size; buffer += size; if(len > pchip->chip_block_size) size = pchip->chip_block_size; else size = len; } } while(len); if (nand_is_blk_bad(pchip, blk)) { /* don't check for bad block during page read/write since may be reading/writing to bad block marker, check for bad block after read to allow for data recovery */ printf("nand_flash_read_buf(): Attempt to read bad nand block %d\n", blk); return(FLASH_API_ERROR); } DBG_PRINTF(">> nand_flash_read_buf - ret=%d\n", ret); return( ret ) ; } /* nand_flash_read_buf */ #if defined(CFG_RAMAPP) /*************************************************************************** * Function Name: nand_flash_write_buf * Description : Writes to flash memory. * Returns : number of bytes written or FLASH_API_ERROR ***************************************************************************/ static int nand_flash_write_buf(unsigned short blk, int offset, unsigned char *buffer, int len) { int ret = len; PCFE_NAND_CHIP pchip = &g_chip; unsigned int addr; unsigned int page_addr; unsigned int page_offset; unsigned int page_boundary; unsigned int size; unsigned int numBlocksInChip = pchip->chip_total_size/pchip->chip_block_size; DBG_PRINTF(">> nand_flash_write_buf - 1 blk=0x%8.8lx, offset=%d, len=%d\n", blk, offset, len); addr = (blk * pchip->chip_block_size) + offset; page_addr = addr & ~(pchip->chip_page_size - 1); page_offset = addr - page_addr; page_boundary = page_addr + pchip->chip_page_size; size = page_boundary - addr; if(size > len) size = len; if (blk >= numBlocksInChip) { printf("Attempt to write block number(%d) beyond the nand max blk(%d) \n", blk, numBlocksInChip-1); return(ret - len); } if (nand_is_blk_bad(pchip, blk)) { printf("nand_flash_write_buf(): Attempt to write bad nand block %d\n", blk); return(ret - len); } if ( ((addr & (pchip->chip_block_size-1)) + len) > pchip->chip_block_size) { // cannot write past block boundary, otherwise if one block is good and another is bad then whole write is invalid printf("nand_flash_write_buf(): Attempt to write past block boundary, blk 0x%x, address 0x%x, length 0x%x\n", blk, addr, len); return (ret - len); } do { if(nandflash_write_page(pchip, page_addr, page_offset, buffer, size) != FLASH_API_OK) { ret = ret - len; break; } len -= size; if( len ) { DBG_PRINTF(">> nand_flash_write_buf- 2 blk=0x%8.8lx, len=%d\n", blk, len); page_addr += pchip->chip_page_size; page_offset = 0; buffer += size; if(len > pchip->chip_page_size) size = pchip->chip_page_size; else size = len; } } while(len); g_write_spare_tffs = 0; DBG_PRINTF(">> nand_flash_write_buf - ret=%d\n", ret); return( ret ) ; } /* nand_flash_write_buf */ /*************************************************************************** * Function Name: nand_flash_get_memptr * Description : Returns the base MIPS memory address for the specfied flash * sector. * Returns : Base MIPS memory address for the specfied flash sector. ***************************************************************************/ static unsigned char *nand_flash_get_memptr(unsigned short sector) { /* Bad things will happen if this pointer is referenced. But it can * be used for pointer arithmetic to deterine sizes. */ return((unsigned char *) (FLASH_BASE + (sector * g_chip.chip_block_size))); } /* nand_flash_get_memptr */ /*************************************************************************** * Function Name: nand_flash_get_blk * Description : Returns the flash sector for the specfied MIPS address. * Returns : Flash sector for the specfied MIPS address. ***************************************************************************/ static int nand_flash_get_blk(int addr) { return((int) ((unsigned long) addr - FLASH_BASE) / g_chip.chip_block_size); } /* nand_flash_get_blk */ /*************************************************************************** * Function Name: nand_flash_get_total_size * Description : Returns the number of bytes in the "CFE Linux code" * partition. * Returns : Number of bytes ***************************************************************************/ static int nand_flash_get_total_size(void) { return(g_chip.chip_total_size); } /* nand_flash_get_total_size */ /*************************************************************************** * Function Name: nand_flash_dev_specific_cmd * Description : Triggers a device specific feature, used to access * non-standard features. * Returns : Feature specific return code. ***************************************************************************/ static int nand_flash_dev_specific_cmd(unsigned int command, void * inBuf, void * outBuf) { PCFE_NAND_CHIP pchip = &g_chip; switch(command) { case WRITE_PAGE: { // write to page and spare area flash_write_data_t *fwd = inBuf; int status; g_no_ecc = *(int*)outBuf; status = nandflash_write_page(pchip, (fwd->block * pchip->chip_block_size) + (fwd->page * pchip->chip_page_size), fwd->offset, fwd->data, fwd->amount); g_no_ecc = 0; return(status); } case READ_PAGE: { flash_write_data_t *fwd = inBuf; int status; g_no_ecc = *(int*)outBuf; if ((fwd->offset + fwd->amount) > pchip->chip_page_size) { // reading from spare area, offset into spare area seems always from beginning? int size = (fwd->offset < pchip->chip_page_size) ? fwd->amount - (pchip->chip_page_size - fwd->offset) : fwd->amount; status = nandflash_read_spare_area(pchip, (fwd->block * pchip->chip_block_size) + (fwd->page * pchip->chip_page_size), fwd->data + (fwd->amount - size), size); } if (fwd->offset < pchip->chip_page_size) { // reading from page area int size = (fwd->offset + fwd->amount) > pchip->chip_page_size ? pchip->chip_page_size - fwd->offset : fwd->amount; status = nandflash_read_page(pchip, (fwd->block * pchip->chip_block_size) + (fwd->page * pchip->chip_page_size), fwd->data, size); // != FLASH_API_OK) } g_no_ecc = 0; return(status); } case NAND_REINIT_FLASH: return(nand_initialize_spare_area(pchip, 0)); case NAND_REINIT_FLASH_BAD: return(nand_initialize_spare_area(pchip, 1)); case CHECK_BAD_BLOCK: return(nand_is_blk_bad(pchip, *(unsigned int *)inBuf)); case MARK_BLOCK_BAD: { unsigned long page_addr = *(unsigned int *)inBuf * pchip->chip_block_size; nand_mark_bad_blk(pchip, page_addr); break; } case FORCE_ERASE: return(nandflash_block_erase(pchip, *(unsigned int *)inBuf * pchip->chip_block_size, 1)); case GET_PAGE_SIZE: return(pchip->chip_page_size); case GET_SPARE_SIZE: return(pchip->chip_spare_size); case GET_AVAILABLE_SPARE_SIZE: { unsigned char *spare_mask = pchip->chip_spare_mask; int spare_mask_bytes = pchip->chip_spare_size / sizeof(char); int i, size = 0; for (i = 0; i < spare_mask_bytes; i++) size += __builtin_popcount(spare_mask[i]); return size; } } return 0; } #endif /*************************************************************************** * Function Name: nand_flash_get_sector_size * Description : Returns the number of bytes in the specfied flash sector. * Returns : Number of bytes in the specfied flash sector. ***************************************************************************/ #if defined(CFG_RAMAPP) static #endif int nand_flash_get_sector_size(unsigned short sector) { return(g_chip.chip_block_size); } /* nand_flash_get_sector_size */ /*************************************************************************** * Function Name: nand_flash_get_numsectors * Description : Returns the number of blocks in the "CFE Linux code" * partition. * Returns : Number of blocks ***************************************************************************/ #if defined(CFG_RAMAPP) static #endif int nand_flash_get_numsectors(void) { return(g_chip.chip_total_size / g_chip.chip_block_size); } /* nand_flash_get_numsectors */ /*************************************************************************** * NAND Flash Implementation Functions ***************************************************************************/ /*************************************************************************** * Function Name: nandflash_copy_from_cache * Description : Copies data from the chip NAND cache to a local memory * buffer. * Returns : None. ***************************************************************************/ extern inline void nandflash_copy_from_cache(unsigned char *buffer, int offset, int numbytes) { volatile unsigned char *cache = NAND_CACHE; unsigned int i, tmp; unsigned int* buf = (unsigned int*)buffer; #if defined(_BCM947189_) { uint32 ioctrl=NAND_FLASH_CTRL_WRAP->ioctrl; ioctrl = ioctrl | NAND_APB_LITTLE_ENDIAN; NAND_FLASH_CTRL_WRAP->ioctrl=ioctrl; } #endif if( ((uintptr_t)buffer&0x3) ) { for( i = 0; i < numbytes; i += sizeof(int) ) { tmp = *(unsigned int*) &cache[i+offset]; #if defined(__ARMEL__) || defined(__MIPSEL) || defined(__AARCH64EL__) *buffer++ = (unsigned char) ((tmp >> 0) & 0xff); *buffer++ = (unsigned char) ((tmp >> 8) & 0xff); *buffer++ = (unsigned char) ((tmp >> 16) & 0xff); *buffer++ = (unsigned char) ((tmp >> 24) & 0xff); #else *buffer++ = (unsigned char) ((tmp >> 24) & 0xff); *buffer++ = (unsigned char) ((tmp >> 16) & 0xff); *buffer++ = (unsigned char) ((tmp >> 8) & 0xff); *buffer++ = (unsigned char) ((tmp >> 0) & 0xff); #endif } } else { #if defined(__ARMEL__) && defined(CFG_ARM_ARCH_7A) if( (numbytes&0x1f) == 0 ) /* use ldm for 32 bytes burt read */ { for( i = 0; i < numbytes; i += 32 ) { __asm__ __volatile__ ( "LDMIA %0, {r0-r7} \n" "STMIA %1, {r0-r7} \n" : : "r"(&cache[i+offset]), "r"(buf) : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7" ); buf += 8; } } else #endif #if ((defined(__ARMEL__) && defined(CFG_ARMV8_AARCH64)) || defined(__AARCH64EL__)) if( (numbytes&0xf) == 0 && (((uintptr_t)buffer)&0x7) == 0 ) /* use ldm for 16 bytes burt read if buffer is 8 bytes aligned*/ { for( i = 0; i < numbytes; i += 16 ) { __asm__ __volatile__ ( "LDP x0, x1, [%0] \n" "STP x0, x1, [%1] \n" : : "r"(&cache[i+offset]), "r"(buf) : "x0", "x1" ); buf += 4; } } else #endif { for( i = 0; i < numbytes; i += sizeof(int) ) { *buf = *(unsigned int *) &cache[i+offset]; buf++; } } } #if defined(_BCM947189_) { uint32 ioctrl=NAND_FLASH_CTRL_WRAP->ioctrl; ioctrl = ioctrl & ~NAND_APB_LITTLE_ENDIAN; NAND_FLASH_CTRL_WRAP->ioctrl=ioctrl; } #endif } /* nandflash_copy_from_cache */ /*************************************************************************** * Function Name: nandflash_copy_from_spare * Description : Copies data from the chip NAND spare registers to a local * memory buffer. * Returns : None. ***************************************************************************/ extern inline void nandflash_copy_from_spare(unsigned char *buffer, int numbytes) { volatile unsigned int *spare_area = (volatile unsigned int *) &NAND->NandSpareAreaReadOfs0; unsigned char *pbuff = buffer; unsigned int i, j, k, l; for(i=0; i < 4; ++i) { j = spare_area[i]; *pbuff++ = (unsigned char) ((j >> 24) & 0xff); *pbuff++ = (unsigned char) ((j >> 16) & 0xff); *pbuff++ = (unsigned char) ((j >> 8) & 0xff); *pbuff++ = (unsigned char) ((j >> 0) & 0xff); } /* Spare bytes greater than 16 are for the ECC. */ if( NAND->NandRevision > 0x00000202 && numbytes > 16 ) { spare_area = (unsigned int *) &NAND->NandSpareAreaReadOfs10; for(i=0, k=16; i < 4 && k < numbytes; ++i) { j = spare_area[i]; l = 0; while( k < numbytes && l <= 24 ) { *pbuff++ = (unsigned char) ((j >> (24 - l)) & 0xff); k++; l += 8; } } } } /* nandflash_copy_from_spare */ #if defined(CFG_RAMAPP) /*************************************************************************** * Function Name: nandflash_copy_to_cache * Description : Copies data from a local memory buffer to the the chip NAND * cache. * Returns : None. ***************************************************************************/ extern inline void nandflash_copy_to_cache(unsigned char *buffer, int offset, int numbytes) { unsigned char *cache = (unsigned char *) NAND_CACHE; unsigned int i; unsigned int* buf = (unsigned int*)buffer; #if defined(_BCM947189_) { uint32 ioctrl=NAND_FLASH_CTRL_WRAP->ioctrl; ioctrl = ioctrl | NAND_APB_LITTLE_ENDIAN; NAND_FLASH_CTRL_WRAP->ioctrl=ioctrl; } #endif if( ((uintptr_t)buffer&0x3) ) { for( i = 0; i < numbytes; i += sizeof(int) ) { #if defined(__ARMEL__) || defined(__MIPSEL) || defined(__AARCH64EL__) *(unsigned int *) &cache[i] = ((unsigned int) buffer[i + 0] << 0) | ((unsigned int) buffer[i + 1] << 8) | ((unsigned int) buffer[i + 2] << 16) | ((unsigned int) buffer[i + 3] << 24); #else *(unsigned int *) &cache[i] = ((unsigned int) buffer[i + 0] << 24) | ((unsigned int) buffer[i + 1] << 16) | ((unsigned int) buffer[i + 2] << 8) | ((unsigned int) buffer[i + 3] << 0); #endif } } else { for( i = 0; i < numbytes; i += sizeof(int) ) { *(unsigned int *) &cache[i] = *buf; buf++; } } #if defined(_BCM947189_) { uint32 ioctrl=NAND_FLASH_CTRL_WRAP->ioctrl; ioctrl = ioctrl & ~NAND_APB_LITTLE_ENDIAN; NAND_FLASH_CTRL_WRAP->ioctrl=ioctrl; } #endif } /* nandflash_copy_to_cache */ /*************************************************************************** * Function Name: nandflash_copy_to_spare * Description : Copies data from a local memory buffer to the the chip NAND * spare registers. * Returns : None. ***************************************************************************/ extern inline void nandflash_copy_to_spare(unsigned char *buffer,int numbytes) { unsigned int *spare_area = (unsigned int *) &NAND->NandSpareAreaWriteOfs0; unsigned char *pbuff = buffer; int i, j; /* Spare bytes greater than 16 are for the ECC. */ if( (numbytes > 16) && !g_no_ecc ) numbytes = 16; /* controller use big endian for spare area bytes. On LE cpu, the following code convert LE to BE. On BE MIPS, it actually does nothing because the pbuff are already in BE */ for(i=0; i< numbytes / sizeof(unsigned int); ++i, pbuff += sizeof(int)) { j = ((unsigned int) pbuff[0] << 24) | ((unsigned int) pbuff[1] << 16) | ((unsigned int) pbuff[2] << 8) | ((unsigned int) pbuff[3] << 0); spare_area[i] = j; } } /* nandflash_copy_to_spare */ #endif /*************************************************************************** * Function Name: nandflash_wait_status * Description : Polls the NAND status register waiting for a condition. * Returns : FLASH_API_OK or FLASH_API_ERROR ***************************************************************************/ static int nandflash_wait_status(unsigned int status_mask) { const unsigned int nand_poll_max = 2000000; unsigned int data; unsigned int poll_count = 0; int ret = FLASH_API_OK; do { data = NAND->NandIntfcStatus; } while(!(status_mask & data) && (++poll_count < nand_poll_max)); if(!(status_mask & NAND->NandIntfcStatus)) { printf("Status wait timeout: nandsts=0x%8.8lx mask=0x%8.8lx, count=" "%lu\n", NAND->NandIntfcStatus, status_mask, poll_count); ret = FLASH_API_ERROR; } return( ret ); } /* nandflash_wait_status */ extern inline int nandflash_wait_cmd(void) { return nandflash_wait_status(NIS_CTLR_READY); } /* nandflash_wait_cmd */ extern inline int nandflash_wait_device(void) { return nandflash_wait_status(NIS_FLASH_READY); } /* nandflash_wait_device */ extern inline int nandflash_wait_cache(void) { return nandflash_wait_status(NIS_CACHE_VALID); } /* nandflash_wait_cache */ extern inline int nandflash_wait_spare(void) { return nandflash_wait_status(NIS_SPARE_VALID); } /* nandflash_wait_spare */ #if !defined(CFG_RAMAPP) static int nandflash_check_ecc(void) { int ret = FLASH_API_OK; UINT32 intrCtrl; UINT32 accessCtrl; /* read interrupt status */ intrCtrl = NAND_INTR->NandInterrupt; accessCtrl = NAND->NandAccControl; if( (intrCtrl & NINT_ECC_ERROR_UNC) != 0 ) ret = FLASH_API_ERROR; if( (intrCtrl & NINT_ECC_ERROR_CORR) != 0 ) { printf("Correctable ECC Error detected: addr=0x%8.8lx, intrCtrl=0x" "%08X, accessCtrl=0x%08X\n", NAND->NandEccCorrAddr, (UINT)intrCtrl, (UINT)accessCtrl); } return( ret ); } static int check_ecc_for_ffs(PCFE_NAND_CHIP pchip, UINT32 step) { return( 1 ); } #else /*************************************************************************** * Function Name: turn_off_ecc * Description : Turns off ECC * Returns : access control register initial value ***************************************************************************/ static unsigned int turn_off_ecc(void) { unsigned int acc = NAND->NandAccControl; unsigned int acc_save = acc; #if !defined(_BCM963268_) && !defined(_BCM96838_) acc &= ~NAC_ECC_LVL_MASK; acc |= (NAC_ECC_LVL_DISABLE << NAC_ECC_LVL_SHIFT); #else acc &= ~(NAC_ECC_LVL_0_MASK | NAC_ECC_LVL_MASK); acc |= (NAC_ECC_LVL_DISABLE << NAC_ECC_LVL_0_SHIFT) | (NAC_ECC_LVL_DISABLE << NAC_ECC_LVL_SHIFT); #endif NAND->NandAccControl = acc; return(acc_save); } /*************************************************************************** * Function Name: nandflash_check_ecc * Description : Reads ECC status. * Returns : FLASH_API_OK or FLASH_API_ERROR ***************************************************************************/ static int nandflash_check_ecc(void) { int ret = FLASH_API_OK; UINT32 intrCtrl; UINT32 accessCtrl; /* read interrupt status */ intrCtrl = NAND_INTR->NandInterrupt; accessCtrl = NAND->NandAccControl; if( (intrCtrl & NINT_ECC_ERROR_UNC) != 0 ) ret = FLASH_API_ERROR; if( (intrCtrl & NINT_ECC_ERROR_CORR) != 0 ) { printf("Correctable ECC Error detected: addr=0x%8.8lx, intrCtrl=0x" "%08X, accessCtrl=0x%08X\n", NAND->NandEccCorrAddr, (UINT)intrCtrl, (UINT)accessCtrl); } return( ret ); } /*************************************************************************** * Function Name: check_ecc_for_ffs * Description : Checks if the ECC bytes are all 0xFFs * Returns : 1 if all ECC bytes are 0xff, 0 if not ***************************************************************************/ static int check_ecc_for_ffs(PCFE_NAND_CHIP pchip, UINT32 step) { int ret = 1; UINT8 *spare = KMALLOC(32, 0); UINT8 *spare_mask = pchip->chip_spare_mask; UINT32 i, j = step * pchip->chip_spare_step_size; memset(spare, 0xff, 32); nandflash_copy_from_spare(spare, pchip->chip_spare_step_size); for( i = 0; i < pchip->chip_spare_step_size; i++, j++ ) { if( ECC_MASK_BIT(spare_mask, j) != 0 && spare[i] != 0xff ) { ret = 0; break; } } KFREE(spare); return( ret ); } /*************************************************************************** * Function Name: nandflash_write_spare_area * Description : Writes the spare area for the specified page. * Returns : FLASH_API_OK or FLASH_API_ERROR ***************************************************************************/ static int nandflash_write_spare_area(PCFE_NAND_CHIP pchip, unsigned long page_addr, unsigned char *buffer, int len) { int ret = FLASH_API_OK; unsigned char * nand_cache = KMALLOC(CTRLR_CACHE_SIZE, 0); unsigned char * spare = KMALLOC(SPARE_MAX_SIZE, 0); unsigned int is_mlc = 1; if( len <= pchip->chip_spare_size ) { unsigned int acc_save; UINT32 steps = pchip->chip_spare_size / pchip->chip_spare_step_size; UINT32 i; if( is_mlc ) memset(nand_cache, 0xff, CTRLR_CACHE_SIZE); memset(spare, 0xff, pchip->chip_spare_size); memcpy(spare, buffer, len); for( i = 0; i < steps; i++ ) { NAND->NandCmdAddr = pchip->chip_base + page_addr + (i * CTRLR_CACHE_SIZE); NAND->NandCmdExtAddr = 0; if( is_mlc ) { acc_save = turn_off_ecc(); /* MLC parts use PROGRAM_PAGE to program spare area. */ nandflash_copy_to_cache(nand_cache, 0, CTRLR_CACHE_SIZE); nandflash_copy_to_spare(spare+(i*pchip->chip_spare_step_size), pchip->chip_spare_step_size); NAND->NandCmdStart = NCMD_PROGRAM_PAGE; } else { /* SLC parts use PROGRAM_SPARE to program spare area. */ nandflash_copy_to_spare(spare+(i*pchip->chip_spare_step_size), pchip->chip_spare_step_size); NAND->NandCmdStart = NCMD_PROGRAM_SPARE; } if( (ret = nandflash_wait_cmd()) == FLASH_API_OK ) { unsigned int sts = NAND->NandIntfcStatus; if( (sts & NIS_PGM_ERASE_ERROR) != 0 ) { printf("Error writing to spare area, sts=0x%8.8lx\n", sts); nand_mark_bad_blk(pchip, page_addr); ret = FLASH_API_ERROR; } } if( is_mlc ) NAND->NandAccControl = acc_save; } } else ret = FLASH_API_ERROR; KFREE(nand_cache); KFREE(spare); return( ret ); } /* nandflash_write_spare_area */ #endif /*************************************************************************** * Function Name: nandflash_read_spare_area * Description : Reads the spare area for the specified page. * Returns : FLASH_API_OK or FLASH_API_ERROR ***************************************************************************/ static int nandflash_read_spare_area(PCFE_NAND_CHIP pchip, unsigned long page_addr, unsigned char *buffer, int len) { int ret = FLASH_API_ERROR; if( len >= pchip->chip_spare_size ) { UINT32 steps = pchip->chip_spare_size / pchip->chip_spare_step_size; UINT32 i; #if defined(CFG_RAMAPP) unsigned int acc_save; if( g_no_ecc ) acc_save = turn_off_ecc(); #endif for( i = 0; i < steps; i++ ) { NAND->NandCmdAddr = pchip->chip_base + page_addr + (i * CTRLR_CACHE_SIZE); NAND->NandCmdExtAddr = 0; NAND->NandCmdStart = NCMD_SPARE_READ; if( (ret = nandflash_wait_cmd()) == FLASH_API_OK ) { /* wait until data is available in the spare area registers */ if( (ret = nandflash_wait_spare()) == FLASH_API_OK ) nandflash_copy_from_spare(buffer + (i * pchip->chip_spare_step_size), pchip->chip_spare_step_size); else break; } else break; } #if defined(CFG_RAMAPP) if( g_no_ecc ) NAND->NandAccControl=acc_save; #endif } return ret; } /* nandflash_read_spare_area */ /*************************************************************************** * Function Name: nandflash_read_page * Description : Reads up to a NAND block of pages into the specified buffer. * Returns : FLASH_API_OK or FLASH_API_ERROR ***************************************************************************/ static int nandflash_read_page(PCFE_NAND_CHIP pchip, unsigned long start_addr, unsigned char *buffer, int len) { int ret = FLASH_API_ERROR; if( len <= pchip->chip_block_size ) { UINT32 page_addr = start_addr & ~(pchip->chip_page_size - 1); UINT32 index = 0; UINT32 subpage; INT32 length = len; #if defined(CFG_RAMAPP) unsigned int acc_save_ecc; if( g_no_ecc ) acc_save_ecc = turn_off_ecc(); #endif do { for( subpage = 0, ret = FLASH_API_OK; (subpage < pchip->chip_page_size) && (ret == FLASH_API_OK); subpage += CTRLR_CACHE_SIZE) { /* clear interrupts, so we can check later for ECC errors */ #if !defined(_BCM963268_) && !defined(_BCM96838_) /* write zero to clear in 63138 */ NAND_INTR->NandInterrupt = 0; #else NAND_INTR->NandInterrupt = NINT_STS_MASK; #endif /* send command */ NAND->NandCmdAddr = pchip->chip_base + page_addr + subpage; NAND->NandCmdExtAddr = 0; NAND->NandCmdStart = NCMD_PAGE_READ; if( (ret = nandflash_wait_cmd()) == FLASH_API_OK ) { /* wait until data is available in the cache */ if( (ret = nandflash_wait_cache()) == FLASH_API_OK ) { if((ret = nandflash_check_ecc())==FLASH_API_ERROR) { #if defined(CFG_RAMAPP) if(check_ecc_for_ffs(pchip, subpage/CTRLR_CACHE_SIZE)) ret = FLASH_API_OK; else { printf("Uncorrectable ECC Error: intr 0x%x, addr=" "0x%8.8lx, blk=%d,\n", NAND_INTR->NandInterrupt, NAND->NandEccUncAddr, NAND->NandEccUncAddr/pchip->chip_block_size); #if 1 ret = FLASH_API_OK; #else nand_mark_bad_blk(pchip, start_addr & ~(pchip->chip_page_size-1)); #endif } #endif /* CFG_RAMAPP */ } } if( (ret == FLASH_API_OK) && (start_addr < (page_addr + subpage + CTRLR_CACHE_SIZE)) && ((start_addr + len) > page_addr + subpage) ) { // copy from cache only if buffer is within the subpage UINT32 copy_size, offset; if ( start_addr <= page_addr + subpage ) { offset = 0; if ( (start_addr + len) >= (page_addr + subpage + CTRLR_CACHE_SIZE) ) copy_size = CTRLR_CACHE_SIZE; else copy_size = (start_addr + len) - (page_addr + subpage); } else { // start_addr > page_addr + subpage offset = start_addr - (page_addr + subpage); if ( (start_addr + len) >= (page_addr + subpage + CTRLR_CACHE_SIZE) ) copy_size = page_addr + subpage + CTRLR_CACHE_SIZE - start_addr; else copy_size = start_addr + len - start_addr; } nandflash_copy_from_cache(&buffer[index], offset, copy_size); index += copy_size; length -= copy_size; } } } if(ret != FLASH_API_OK) break; page_addr += pchip->chip_page_size; } while(length); #if defined(CFG_RAMAPP) if( g_no_ecc ) NAND->NandAccControl=acc_save_ecc; #endif } return( ret ) ; } /* nandflash_read_page */ #if (INC_BTRM_BUILD==1) && !defined(_BCM96838_) && !defined(_BCM963381_) #if defined(_BCM963268_) /* CFE ROM is padded out to 48K (3x16K) because security is supported in bootrom */ #define NUM_16K_IN_CFE_ROM 3 #endif #define SMALPG_BLK_SIZE 16 * 1024 #define CFE_ROM_SIZE NUM_16K_IN_CFE_ROM * SMALPG_BLK_SIZE #define MAX_NUM_BTLDR_DUP 3 static unsigned int totalCfeRoms = 0; static int cfeRom1Ecc = 0; static int cfeRom2Ecc = 0; static int cfeRom3Ecc = 0; static int badBlk[MAX_NUM_BTLDR_DUP * NUM_16K_IN_CFE_ROM]; /*************************************************************************** * Function Name: check_btrm_ecc_for_ffs * Description : Checks if the ECC bytes are all 0xFFs * Returns : 1 if all ECC bytes are 0xff, 0 if not ***************************************************************************/ static int check_btrm_ecc_for_ffs(PCFE_NAND_CHIP pchip, UINT32 step) { int ret = 1; UINT8 *spare = KMALLOC(32, 0); UINT8 *spare_mask = pchip->chip_spare_mask; UINT32 i, j = step * pchip->chip_spare_step_size; nandflash_copy_from_spare(spare, pchip->chip_spare_step_size); for( i = 0; i < pchip->chip_spare_step_size; i++, j++ ) { if( ECC_MASK_BIT(spare_mask, j) != 0 && spare[i] != 0xff ) { ret = 0; break; } } KFREE(spare); return( ret ); } /*************************************************************************** * Function Name: nandflash_btrm_ecc_setleds * Description : prints out ECC1, ECC2 or ECC3 if ECC page errors are * detected in CFE ROM 1, 2 or 3 respectively. The print occurs * only once for each CFE ROM if more than one ECC error is * detected within the CFE ROM image * Returns : nothing ***************************************************************************/ static void nandflash_btrm_ecc_setleds(UINT32 cfeRomMirror) { if (cfeRomMirror == 0) { if (cfeRom1Ecc == 0) { board_setleds(0x42504730); cfeRom1Ecc = 1; } } else { if (cfeRomMirror == CFE_ROM_SIZE) { if (cfeRom2Ecc == 0) { board_setleds(0x42504731); cfeRom2Ecc = 1; } } else { if (cfeRomMirror == 2 * CFE_ROM_SIZE) { if (cfeRom3Ecc == 0) { board_setleds(0x42504732); cfeRom3Ecc = 1; } } } } } /*************************************************************************** * Function Name: nandflash_btrm_check_ecc * Description : Reads ECC status. * Returns : FLASH_API_OK or FLASH_API_ERROR ***************************************************************************/ static int nandflash_btrm_check_ecc(void) { int ret = FLASH_API_OK; UINT32 intrCtrl; /* read interrupt status */ intrCtrl = NAND_INTR->NandInterrupt; if( (intrCtrl & NINT_ECC_ERROR_UNC) != 0 ) ret = FLASH_API_ERROR; return( ret ); } /*************************************************************************** * Function Name: nandflash_btrm_read * Description : Sends commands to perform read from NAND flash * Returns : FLASH_API_OK or FLASH_API_ERROR ***************************************************************************/ static int nandflash_btrm_read(PCFE_NAND_CHIP pchip, UINT32 page_addr, UINT32 page_offset) { int ret = FLASH_API_ERROR; /* clear interrupts, so we can check later for ECC errors */ #if !defined(_BCM963268_) && !defined(_BCM96838_) /* write zero to clear in 63138 */ NAND_INTR->NandInterrupt = 0; #else NAND_INTR->NandInterrupt = NINT_STS_MASK; #endif /* send command */ NAND->NandCmdAddr = pchip->chip_base + page_addr + page_offset; NAND->NandCmdExtAddr = 0; NAND->NandCmdStart = NCMD_PAGE_READ; if( (ret = nandflash_wait_cmd()) == FLASH_API_OK ) { /* wait until data is available in the cache */ if( (ret = nandflash_wait_cache()) == FLASH_API_OK ) { /* Set the ret value to FLASH_API_ERROR if the ECC fails */ ret = nandflash_btrm_check_ecc(); } } return ret; } /*************************************************************************** * Function Name: nandflash_btrm_itis_what_itis * Description : Reads CFE ROM bootloader into the specified buffer. * An internal bootrom is retrieving the CFE ROM from flash. * The flash uses no strategy: * Arguements : * len - either 16KB (no security) or 48KB (with security) * if 16KB, then it doesn't matter whether it is small or * large page nand because 16KB fits entirely into block 0 * which is always considered good. Just return block 0. * if 48KB and small page nand, return first three blocks * if 48KB and large page nand, return block 0 * Returns : FLASH_API_OK or FLASH_API_ERROR ***************************************************************************/ static int nandflash_btrm_itis_what_itis(PCFE_NAND_CHIP pchip, unsigned char *buffer, int len) { int ret = FLASH_API_ERROR; #if defined(_BCM963268_) int blkSize = pchip->chip_block_size; if (blkSize == SMALPG_BLK_SIZE) { // no bad block mgmt is used; grab blocks 0, 1 and 2 // retrieve the first part of the bootloader from block 0 ret = nandflash_btrm_get_content_from_block(pchip, 0, buffer, SMALPG_BLK_SIZE); if (ret == FLASH_API_OK) { // retrieve the second part of the bootloader from block 1 ret = nandflash_btrm_get_content_from_block(pchip, 1, buffer + SMALPG_BLK_SIZE, SMALPG_BLK_SIZE); if (ret == FLASH_API_OK) { // retrieve the third part of the bootloader from block 2 ret = nandflash_btrm_get_content_from_block(pchip, 2, buffer + (2 * SMALPG_BLK_SIZE), SMALPG_BLK_SIZE); } } } else { // 48KB len will fit entirely within block 0 which is assumed to be good. ret = nandflash_btrm_get_content_from_block(pchip, 0, buffer, len); } #endif return( ret ) ; } /*************************************************************************** * Function Name: nandflash_btrm_skip_bad_block * Description : Reads CFE ROM bootloader into the specified buffer. * An internal bootrom is retrieving the CFE ROM from flash. * The flash uses the skip bad block strategy: * Arguements : * len - either 16KB (no security) or 48KB (with security) * if 16KB, then it doesn't matter whether it is small or * large page nand because 16KB fits entirely into block 0 * which is always considered good. Just return block 0. * if 48KB, then only small page nand requires skip bad block * because 48KB fits entirely into a large page block 0 * Returns : FLASH_API_OK or FLASH_API_ERROR ***************************************************************************/ static int nandflash_btrm_skip_bad_block(PCFE_NAND_CHIP pchip, unsigned char *buffer, int len) { int ret = FLASH_API_ERROR; #if defined(_BCM963268_) int blkSize = pchip->chip_block_size; if (blkSize == SMALPG_BLK_SIZE) { // skip bad block is used; block 0 is good, but block 1 and 2 need to be checked // retrieve the first part of the bootloader from block 0 ret = nandflash_btrm_get_content_from_block(pchip, 0, buffer, SMALPG_BLK_SIZE); if (ret == FLASH_API_OK) { // block 0 retrieved just fine (as expected) UINT32 i; UINT32 j = 0; UINT32 k = 0; // retrieve the next two good blocks for (i = 1; i < 3; i++) { ret = FLASH_API_ERROR; while ( ret == FLASH_API_ERROR) { j += SMALPG_BLK_SIZE; k++; /* Check the first page of this block to see if block is marked bad. */ /* If the func below returns FLASH_API_ERROR, it could be the block */ /* is marked bad, or the nand controller is stating that an */ /* uncorrectable ECC has occurred. Either way, this block is no good */ /* and just skip this block. If the func returns, FLASH_API_OK, then */ /* at a minimum, the block is not marked bad, and therefore attempt */ /* to read it */ ret = nandflash_btrm_read(pchip, j, 0); } /* Retrieve the part of the bootloader. It could return FLASH_API_ERROR */ /* due to read disturb wear and tear, but there is nothing we can do about that */ ret = nandflash_btrm_get_content_from_block(pchip, k, buffer + (i * SMALPG_BLK_SIZE), SMALPG_BLK_SIZE); } } } else { // 48KB len will fit entirely within block 0 which is assumed to be good. ret = nandflash_btrm_get_content_from_block(pchip, 0, buffer, len); } #endif return( ret ) ; } /*************************************************************************** * Function Name: nandflash_btrm_get_content_from_block * Description : Reads up to an entire block's worth of nand data into the * buffer. If any uncorrectable ECC occurs, the operation is * aborted, and FLASH_API_ERROR is returned; * Returns : FLASH_API_OK or FLASH_API_ERROR ***************************************************************************/ static int nandflash_btrm_get_content_from_block(PCFE_NAND_CHIP pchip, UINT32 blkNum, unsigned char *buffer, int len) { int ret = FLASH_API_ERROR; int blkSize = pchip->chip_block_size; unsigned long start_addr = blkSize * blkNum; UINT32 page_addr = start_addr & ~(pchip->chip_page_size - 1); UINT32 page_offset = start_addr - page_addr; UINT32 size = pchip->chip_page_size - page_offset; UINT32 index = 0; UINT32 i; if(size > len) size = len; do { /* Nand controller retrieves 512 bytes at a time. A nand page can*/ /* be 512 bytes or multiples of this. Loop as necessary in order */ /* to retrieve the entire page via one nand cache at a time */ for( i = 0, ret = FLASH_API_OK; i < pchip->chip_page_size && ret == FLASH_API_OK; i += CTRLR_CACHE_SIZE) { /* retrieve one nand cache worth of data (512 bytes) */ ret = nandflash_btrm_read(pchip, page_addr, i); if( ret == FLASH_API_OK ) { /* nand cache data is good to use in creating page */ if( i < size ) { UINT32 copy_size = (i + CTRLR_CACHE_SIZE <= size) ? CTRLR_CACHE_SIZE : size - i; nandflash_copy_from_cache(&buffer[index + i], page_offset, copy_size); } } else { /* An uncorrectable ECC occurred, we are done and the read has failed */ /* Break out of the for loop */ break; } } /* If an uncorrectable ECC occurred, we are done and the read has failed */ /* Break out of the do loop */ if(ret != FLASH_API_OK) break; /* Page read was successful. Get ready to retrieve the next page */ page_offset = 0; page_addr += pchip->chip_page_size; index += size; len -= size; if(len > pchip->chip_page_size) size = pchip->chip_page_size; else size = len; } while(len); return( ret ) ; } /*************************************************************************** * Function Name: nandflash_btrm_nand_mirroring * Description : Reads CFE ROM bootloader into the specified buffer. * An internal bootrom is retrieving the CFE ROM from flash. * If only nand mirroring: * and small page NAND, the CFE ROM is duplicated across the * first one, two, or three NAND blocks. If large page NAND, * the CFE ROM is duplicated one, two or three times but all * within the first block. * If nand mirroring and security: * and small page NAND, the CFE ROM takes up three blocks, * so three mirrors will use nine NAND blocks. If large page NAND, * the CFE ROM is duplicated one, two or three times but the * restriction is it must all be within the first block. * If any page read has ECC errors, the mirror pages are used * from the other duplicates. * Returns : FLASH_API_OK or FLASH_API_ERROR ***************************************************************************/ static int nandflash_btrm_nand_mirroring(PCFE_NAND_CHIP pchip, unsigned char *buffer, int len, unsigned long start_addr, UINT32 start_blk) { int ret = FLASH_API_ERROR; int blkSize = pchip->chip_block_size; UINT32 index = 0; UINT32 i, k, cur_blk; #if defined(_BCM963268_) UINT32 blk, blkBndry; #endif UINT32 page_addr = start_addr & ~(pchip->chip_page_size - 1); UINT32 page_offset = start_addr - page_addr; UINT32 size = pchip->chip_page_size - page_offset; if(size > len) size = len; /* Retrieve the entire CFE ROM */ do { /* Nand controller retrieves 512 bytes at a time. A nand page can*/ /* be 512 bytes or multiples of this. Loop as necessary in order */ /* to retrieve the entire page via one nand cache at a time */ for( i = 0, ret = FLASH_API_OK; i < pchip->chip_page_size && ret == FLASH_API_OK; i += CTRLR_CACHE_SIZE) { cur_blk = start_blk; /*There could be up to three mirrors of the current nand cache*/ /* content being retrieved. Loop until a good one is found,but*/ /* exclude any within a known bad block */ for( k = 0, ret = FLASH_API_ERROR; k < ((totalCfeRoms * CFE_ROM_SIZE) - start_addr) && ret == FLASH_API_ERROR; k += CFE_ROM_SIZE) { if (! badBlk[cur_blk]) { /* retrieve one nand cache worth of data (512 bytes) */ /* from a specific nand page */ ret = nandflash_btrm_read(pchip, page_addr + k, i); if( ret == FLASH_API_OK ) { /* nand cache data is good to use in creating page */ if( i < size ) { UINT32 copy_size = (i + CTRLR_CACHE_SIZE <= size) ? CTRLR_CACHE_SIZE : size - i; nandflash_copy_from_cache(&buffer[index + i], page_offset, copy_size); break; } } else nandflash_btrm_ecc_setleds(start_addr + k); } if (blkSize == SMALPG_BLK_SIZE) cur_blk += NUM_16K_IN_CFE_ROM; } if (ret != FLASH_API_OK) { /* The content within the same page offset in all NAND */ /* mirrors are bad.This unit is toast.What are the odds?*/ board_setleds(0x4641494c); break; } } /* Check to see if this page retrieval was good (FLASH_API_OK)*/ /* or, if this page is bad in all mirrors (FLASH_API_ERROR) */ /* If bad page in all dup mirrors, this flash is toast */ if(ret != FLASH_API_OK) break; /* Page read was successful. Get ready to retrieve the next page */ page_offset = 0; page_addr += pchip->chip_page_size; index += size; len -= size; #if defined(_BCM963268_) /* Check if we are operating on a new block */ if (blkSize == SMALPG_BLK_SIZE) { for (blkBndry = 1; blkBndry < NUM_16K_IN_CFE_ROM; blkBndry++) { if (index == (SMALPG_BLK_SIZE * blkBndry)) { /* The 1st or 2nd of three blocks has been read */ /* Get ready to read the next block */ ret = FLASH_API_ERROR; for (blk = blkBndry; blk < (totalCfeRoms * NUM_16K_IN_CFE_ROM); blk += NUM_16K_IN_CFE_ROM) { if (!badBlk[blk]) { start_addr = blk * SMALPG_BLK_SIZE; page_addr = start_addr; start_blk = blk; ret = FLASH_API_OK; break; } } /* Check to see if no good blks exist */ if (ret == FLASH_API_ERROR) return( ret ) ; } } } #endif if(len > pchip->chip_page_size) size = pchip->chip_page_size; else size = len; } while(len); return( ret ) ; } /* nandflash_btrm_nand_mirroring */ /*************************************************************************** * Function Name: nandflash_btrm_find_bad_blocks * Description : loads up badBlk struct * Returns : none ***************************************************************************/ static void nandflash_btrm_find_bad_blocks(PCFE_NAND_CHIP pchip, unsigned long *start_addr, UINT32 *start_blk) { int ret; /* If small page nand, verify if any of the CFE ROM blocks are marked */ /* bad due to failed block erasing or programming. If large page nand,*/ /* there is only one block which is assumed to be good */ if (pchip->chip_block_size == SMALPG_BLK_SIZE) { UINT32 i, k = 0, serOut = 0x42424b30; for (i = 0; i < (totalCfeRoms * CFE_ROM_SIZE); i += SMALPG_BLK_SIZE) { /* Assume the current block is good */ badBlk[k] = 0; ret = nandflash_btrm_read(pchip, i, 0); if( ret == FLASH_API_ERROR ) { /* Could be a bad erase took place on this block and the block is */ /* marked as bad, or it might be an uncorrectable ECC due to read */ /* disturb. Find out which it is (if read disturb, the rest of */ /* this block can still be used) */ if (check_btrm_ecc_for_ffs(pchip,0)) { /* It is a bad block */ badBlk[k] = 1; board_setleds(serOut + k); if (k == 0) { /* Block 0 is bad, but it has a mirror block. Start at block 0's 2nd mirror */ *start_addr = CFE_ROM_SIZE; *start_blk = NUM_16K_IN_CFE_ROM; } if ((*start_blk == k) && (totalCfeRoms > 2)) { /* Block 0 and it's 2nd mirror are bad, but there is a 3rd mirror. Start there */ *start_addr = CFE_ROM_SIZE * 2; *start_blk = NUM_16K_IN_CFE_ROM * 2; } } } k++; } } else badBlk[0] = 0; } /*************************************************************************** * Function Name: nand_flash_btrm_read_buf * Description : Retrieves a CFE ROM bootloader. * Returns : FALSH_API_OK or FLASH_API_ERROR ***************************************************************************/ int nand_flash_btrm_read_buf(unsigned char *buffer, unsigned int numDups) { int ret; PCFE_NAND_CHIP pchip = &g_chip; unsigned long start_addr = 0; UINT32 start_blk = 0; /* numDups can be 0, 1, 2, or 3. */ /* 0: skip bad block is to be used, and is only useful with security */ /* on small page nand. */ /* 1: nand mirror is to be used, in which one copy of CFE ROM exists */ /* 2: nand mirror is to be used, in which two copies of CFE ROM exists*/ /* 3: nand mirror is to be used, in which three copies exist */ totalCfeRoms = numDups; /* Now build up the 16k (or 48k) CFE ROM bootloader */ switch (totalCfeRoms) { case 0: ret = nandflash_btrm_skip_bad_block(pchip, buffer, CFE_ROM_SIZE); break; case 1: ret = nandflash_btrm_itis_what_itis(pchip, buffer, CFE_ROM_SIZE); break; case 2: case 3: nandflash_btrm_find_bad_blocks(pchip, &start_addr, &start_blk); ret = nandflash_btrm_nand_mirroring(pchip, buffer, CFE_ROM_SIZE, start_addr, start_blk); break; default: ret = FLASH_API_ERROR; break; } return( ret ) ; } /* nand_flash_btrm_read_buf */ #endif /* #if (INC_BTRM_*) */ #if defined(CFG_RAMAPP) /*************************************************************************** * Function Name: nandflash_write_page * Description : Writes a NAND page from the specified buffer. * Returns : FLASH_API_OK or FLASH_API_ERROR ***************************************************************************/ static int nandflash_write_page(PCFE_NAND_CHIP pchip, unsigned long page_addr, unsigned int page_offset, unsigned char *buffer, int len) { unsigned char * xfer_buf; int ret = FLASH_API_ERROR; unsigned int acc_save; { // check if write is blank unsigned int i; /* Don't write page if data is all 0xFFs. This allows the * NAND flash driver to successfully write the Linux page. */ ret = FLASH_API_OK_BLANK; for( i = 0; i < len; i++ ) { // do this on a byte basis because buffer may not be aligned/whole page if( *(buffer + i) != 0xff ) { ret = FLASH_API_OK; break; } } } if (ret == FLASH_API_OK_BLANK) { // don't write to page if data is all FF's return(FLASH_API_OK); } // all these calculations are made with the assumpation that there is page data to write (with a possible offset into the page) and if there's spare data to write it immediately follows the page data xfer_buf = KMALLOC(pchip->chip_page_size + pchip->chip_spare_size, 0); memset(xfer_buf, 0xff, pchip->chip_page_size + pchip->chip_spare_size); memcpy(xfer_buf + page_offset, buffer, len); page_offset = 0; if( g_no_ecc ) acc_save = turn_off_ecc(); for (len = 0; len < (pchip->chip_spare_size / pchip->chip_spare_step_size); len++) { NAND->NandCmdAddr = pchip->chip_base + page_addr + page_offset; NAND->NandCmdExtAddr = 0; nandflash_copy_to_cache(xfer_buf + page_offset, 0, CTRLR_CACHE_SIZE); if (g_write_spare_tffs) { nandflash_copy_to_spare(g_spare_tffs + (len * pchip->chip_spare_step_size), pchip->chip_spare_step_size); } else { /* write JFFS2 clean marker into spare area buffer if writing into data area of first page of block */ if ( !g_no_ecc && ((page_addr & (pchip->chip_block_size - 1)) == 0) ) nandflash_copy_to_spare(g_spare_cleanmarker + (len * pchip->chip_spare_step_size), pchip->chip_spare_step_size); else // write spare area data, which may be all 0xFF nandflash_copy_to_spare(&xfer_buf[pchip->chip_page_size + (len * pchip->chip_spare_step_size)], pchip->chip_spare_step_size + 3); // make sure it snaps to a value divisible by 4 which still includes the data, i.e. 27 bytes spare will get divided down /4 in nandflash_copy_to_spare } NAND->NandCmdStart = NCMD_PROGRAM_PAGE; if( (ret = nandflash_wait_cmd()) == FLASH_API_OK ) { unsigned int sts = NAND->NandIntfcStatus; if( (sts & NIS_PGM_ERASE_ERROR) != 0 ) { printf("Error writing to block, sts=0x%8.8lx\n", sts); nand_mark_bad_blk(pchip, page_addr & ~(pchip->chip_page_size - 1)); ret = FLASH_API_ERROR; } } page_offset += CTRLR_CACHE_SIZE; } KFREE(xfer_buf); if( g_no_ecc ) NAND->NandAccControl = acc_save; return( ret ); } /* nandflash_write_page */ /*************************************************************************** * Function Name: nandflash_block_erase * Description : Erases a block. * Returns : FLASH_API_OK or FLASH_API_ERROR ***************************************************************************/ static int nandflash_block_erase(PCFE_NAND_CHIP pchip, unsigned long blk_addr, int force ) { int ret = FLASH_API_OK; unsigned int blk = blk_addr/pchip->chip_block_size; if (nand_is_blk_bad(pchip, blk)) { printf("nandflash_block_erase(): Attempt to erase bad nand block %d force %d\n", blk, force); if( force == 0 ) return FLASH_API_ERROR; } /* send command */ NAND->NandCmdAddr = pchip->chip_base + blk_addr; NAND->NandCmdExtAddr = 0; NAND->NandCmdStart = NCMD_BLOCK_ERASE; if( (ret = nandflash_wait_cmd()) == FLASH_API_OK ) { unsigned int sts = NAND->NandIntfcStatus; if( (sts & NIS_PGM_ERASE_ERROR) != 0 ) { printf("\nError erasing block 0x%8.8lx, sts=0x%8.8lx\n", \ blk_addr, sts); nand_mark_bad_blk(pchip, blk_addr); ret = FLASH_API_ERROR; } } DBG_PRINTF(">> nandflash_block_erase - addr=0x%8.8lx, ret=%d\n", blk_addr, ret); return( ret ); } /* nandflash_block_erase */ void dump_spare(void); void dump_spare(void) { PCFE_NAND_CHIP pchip = &g_chip; unsigned char * spare = KMALLOC(SPARE_MAX_SIZE, 0); unsigned int i; for( i = 0; i < pchip->chip_total_size; i += pchip->chip_block_size ) { if( nandflash_read_spare_area(pchip, i, spare, pchip->chip_spare_size) == FLASH_API_OK ) { printf("%8.8x: %8.8x %8.8x %8.8x %8.8x\n", i, *(unsigned int *) &spare[0], *(unsigned int *) &spare[4], *(unsigned int *) &spare[8], *(unsigned int *) &spare[12]); if( pchip->chip_spare_size > 16 ) { printf("%8.8x: %8.8x %8.8x %8.8x %8.8x\n", i, *(unsigned int *)&spare[16],*(unsigned int *)&spare[20], *(unsigned int *)&spare[24],*(unsigned int *)&spare[28]); printf("%8.8x: %8.8x %8.8x %8.8x %8.8x\n", i, *(unsigned int *)&spare[32],*(unsigned int *)&spare[36], *(unsigned int *)&spare[40],*(unsigned int *)&spare[44]); printf("%8.8x: %8.8x %8.8x %8.8x %8.8x\n", i, *(unsigned int *)&spare[48],*(unsigned int *)&spare[52], *(unsigned int *)&spare[56],*(unsigned int *)&spare[60]); } } else printf("Error reading spare 0x%8.8x\n", i); } KFREE(spare); } int read_spare_data(int blk, int offset, unsigned char *buf, int bufsize, int read_all_oob); int read_spare_data(int blk, int offset, unsigned char *buf, int bufsize, int read_all_oob) { PCFE_NAND_CHIP pchip = &g_chip; unsigned char * spare = KMALLOC(SPARE_MAX_SIZE, 0); unsigned long page_addr = (blk * pchip->chip_block_size) + offset; unsigned int i, j; int ret; if( (ret = nandflash_read_spare_area( pchip, page_addr, spare, pchip->chip_spare_size)) == FLASH_API_OK ) { if( read_all_oob ) { memset(buf, 0x00, bufsize); memcpy(buf, spare, (pchip->chip_spare_size > bufsize) ? bufsize : pchip->chip_spare_size); } else { unsigned char *spare_mask = pchip->chip_spare_mask; /* Skip spare offsets that are reserved for the ECC. Make * spare data bytes contiguous in the spare buffer. */ for( i = 0, j = 0; i < pchip->chip_spare_size; i++ ) if( ECC_MASK_BIT(spare_mask, i) == 0 && j < bufsize ) buf[j++] = spare[i]; } } KFREE(spare); return(ret); } int write_spare_data(int blk, int offset, unsigned char *buf, int bufsize, int write_all_oob, int ooboffs); int write_spare_data(int blk, int offset, unsigned char *buf, int bufsize, int write_all_oob, int ooboffs) { PCFE_NAND_CHIP pchip = &g_chip; unsigned char spare[SPARE_MAX_SIZE]; unsigned long page_addr = (blk * pchip->chip_block_size) + offset; unsigned long i, j; unsigned char *spare_mask = pchip->chip_spare_mask; memset(spare, 0xff, SPARE_MAX_SIZE); if (write_all_oob) { memcpy(spare, buf, (pchip->chip_spare_size > bufsize) ? bufsize : pchip->chip_spare_size); } else { /* Skip spare offsets that are reserved for the ECC. Make * spare data bytes contiguous in the spare buffer. */ for (i = ooboffs, j = 0; i < pchip->chip_spare_size; i++) if (ECC_MASK_BIT(spare_mask, i) == 0 && j < bufsize) spare[i] = buf[j++]; } /* clear spare bytes */ for (i = 0; i < pchip->chip_spare_size; i++) { if (ECC_MASK_BIT(spare_mask, i) != 0) spare[i] = 0xff; } /* wenn write_all_oob gesetzt ist, wollen wir den bad block marker schreiben und keine Userdaten * deshalb wird hier direkt der spare bereich geschrieben. Ansonsten werden die OOB-Daten in einen * Buffer abgelegt und spaeter zusammen mit den Nutzdaten geschrieben. Dadurch werden die ECC-Werte * korrekt berechnet */ if (write_all_oob) return nandflash_write_spare_area(pchip, page_addr, spare, pchip->chip_spare_size); else { memcpy(g_spare_tffs, spare, SPARE_MAX_SIZE); g_write_spare_tffs = 1; return 0; } } int dump_spare_pages(int blk); int dump_spare_pages(int blk) { PCFE_NAND_CHIP pchip = &g_chip; unsigned char * spare = KMALLOC(SPARE_MAX_SIZE / 4, 0); unsigned long page_addr = blk * pchip->chip_block_size; unsigned int i; int ret = 0; for( i = 0; i < 6; i++ ) { if( (ret = nandflash_read_spare_area( pchip, page_addr + (i * pchip->chip_page_size), (unsigned char *) spare, pchip->chip_spare_size)) == FLASH_API_OK ) { printf("%8.8x %8.8x %8.8x %8.8x\n", spare[0], spare[1], spare[2], spare[3]); if( pchip->chip_spare_size > 16 ) { printf("%8.8x %8.8x %8.8x %8.8x\n", spare[4], spare[5], spare[6], spare[7]); printf("%8.8x %8.8x %8.8x %8.8x\n", spare[8], spare[9], spare[10], spare[11]); printf("%8.8x %8.8x %8.8x %8.8x\n\n", spare[12], spare[13], spare[14], spare[15]); } } } KFREE(spare); return(ret); } #endif