diff options
-rw-r--r-- | drivers/mtd/nand/mxs_nand.c | 136 |
1 files changed, 113 insertions, 23 deletions
diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c index f17a691..7f6351f 100644 --- a/drivers/mtd/nand/mxs_nand.c +++ b/drivers/mtd/nand/mxs_nand.c @@ -49,6 +49,10 @@ int chunk_data_chunk_size = MXS_NAND_CHUNK_DATA_CHUNK_SIZE; int galois_field = 13; +int bbm_chunk; +int ecc_strength; +bool large_oob_flag; +bool ecc_for_meta; struct mxs_nand_info { int cur_chip; @@ -139,7 +143,8 @@ static void mxs_nand_return_dma_descs(struct mxs_nand_info *info) static uint32_t mxs_nand_ecc_chunk_cnt(uint32_t page_data_size) { - return page_data_size / chunk_data_chunk_size; + int tmp = page_data_size / chunk_data_chunk_size; + return ecc_for_meta ? tmp + 1 : tmp; } static uint32_t mxs_nand_ecc_size_in_bits(uint32_t ecc_strength) @@ -152,19 +157,101 @@ static uint32_t mxs_nand_aux_status_offset(void) return (MXS_NAND_METADATA_SIZE + 0x3) & ~0x3; } -static inline uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size, - uint32_t page_oob_size) +/* + * For some large oob NAND chip( the oob larger than data chunk), combined meta + * with chunk0 style bch layout might override the bbm with ecc data. The + * function checked if bbm can be in the data chunk. If it is true, chunk_num + * indicate the chunk number that bbm located. + * + */ +static bool mxs_nand_bbm_in_data_chunk(struct mtd_info *mtd, int gf_len, + int *chunk_num) { - int ecc_strength; + int i, j; + int meta = MXS_NAND_METADATA_SIZE; + + i = (mtd->writesize * 8 - meta * 8) / + (gf_len * ecc_strength + + chunk_data_chunk_size * 8); - ecc_strength = ((page_oob_size - MXS_NAND_METADATA_SIZE) * 8) - /(galois_field * mxs_nand_ecc_chunk_cnt(page_data_size)); + j = (mtd->writesize * 8 - meta * 8) % + (gf_len * ecc_strength + + chunk_data_chunk_size * 8); - /* We need the minor even number. */ - ecc_strength -= ecc_strength & 1; - return min(ecc_strength, MXS_NAND_MAX_ECC_STRENGTH); + if (j < chunk_data_chunk_size * 8) { + *chunk_num = i+1; + return true; + } + + return false; } +/* + * the work flow about how to set the ecc layout + * + * 1. if ecc_strength_ds>max_soc_ecc, quit + * 2. if ecc_strength_ds>0 and ecc_stride_ds>0, + * if ecc_stride_ds > oob, go to large_oob branch + * else go to normal branch + * 3. if either ecc_stride_ds<=0 or ecc_stride_ds<=0, quit + * + */ +static int mxs_nand_get_ecc_strength(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + uint32_t page_oob_size = mtd->oobsize; + int meta = MXS_NAND_METADATA_SIZE; + + if (chip->ecc_strength_ds > MXS_NAND_MAX_ECC_STRENGTH) { + printf("cannot support the NAND, ecc too weak\n"); + return -EINVAL; + } + + if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0) && + !(page_oob_size > 1024)) { + printf("cannot support the NAND, missing necessary info\n"); + return -EINVAL; + } + + /* set some parameters according to NAND chip parameters */ + chunk_data_chunk_size = chip->ecc_step_ds; + if (1024 == chunk_data_chunk_size) + galois_field = 14; + if (chunk_data_chunk_size < page_oob_size) + large_oob_flag = true; + + if (large_oob_flag) { + /* start from the minimum ecc NAND chips required */ + ecc_strength = chip->ecc_strength_ds; + while (!(ecc_strength > MXS_NAND_MAX_ECC_STRENGTH)) { + if (mxs_nand_bbm_in_data_chunk(mtd, + galois_field, + &bbm_chunk)) + break; + ecc_strength += 2; + } + /* + * if all supported ecc cannot satisfy the bbm + * requirement, change * the ecc layout to meta + * with ecc type. + * + */ + if (ecc_strength > MXS_NAND_MAX_ECC_STRENGTH) { + ecc_strength = chip->ecc_strength_ds; + ecc_for_meta = true; + /* calculate in which chunk bbm located */ + bbm_chunk = (mtd->writesize * 8 - meta * 8 - + galois_field * ecc_strength) / + (galois_field * ecc_strength + + chunk_data_chunk_size * 8) + 1; + } + } else { + ecc_strength = chip->ecc_strength_ds; + ecc_strength += ecc_strength & 1; + } + return 0; +}; + static inline uint32_t mxs_nand_get_mark_offset(uint32_t page_data_size, uint32_t ecc_strength) { @@ -184,8 +271,13 @@ static inline uint32_t mxs_nand_get_mark_offset(uint32_t page_data_size, /* Compute the bit offset of the block mark within the physical page. */ block_mark_bit_offset = page_data_size * 8; - /* Subtract the metadata bits. */ - block_mark_bit_offset -= MXS_NAND_METADATA_SIZE * 8; + if (ecc_for_meta) + /* Subtract the metadata bits and ecc bits. */ + block_mark_bit_offset -= MXS_NAND_METADATA_SIZE * 8 + + chunk_ecc_size_in_bits; + else + /* Subtract the metadata bits. */ + block_mark_bit_offset -= MXS_NAND_METADATA_SIZE * 8; /* * Compute the chunk number (starting at zero) in which the block mark @@ -216,15 +308,11 @@ static inline uint32_t mxs_nand_get_mark_offset(uint32_t page_data_size, static uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd) { - uint32_t ecc_strength; - ecc_strength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize); return mxs_nand_get_mark_offset(mtd->writesize, ecc_strength) >> 3; } static uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd) { - uint32_t ecc_strength; - ecc_strength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize); return mxs_nand_get_mark_offset(mtd->writesize, ecc_strength) & 0x7; } @@ -980,10 +1068,8 @@ static int mxs_nand_scan_bbt(struct mtd_info *mtd) struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE; uint32_t tmp; - if (mtd->oobsize > MXS_NAND_CHUNK_DATA_CHUNK_SIZE) { - chunk_data_chunk_size = MXS_NAND_CHUNK_DATA_CHUNK_SIZE * 2; - galois_field = 14; - } + /* calculate ecc_strength, bbm_chunk, eec_for meta, if necessary */ + mxs_nand_get_ecc_strength(mtd); /* Configure BCH and set NFC geometry */ mxs_reset_block(&bch_regs->hw_bch_ctrl_reg); @@ -992,17 +1078,21 @@ static int mxs_nand_scan_bbt(struct mtd_info *mtd) tmp = (mxs_nand_ecc_chunk_cnt(mtd->writesize) - 1) << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET; tmp |= MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET; - tmp |= (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1) + tmp |= (ecc_strength >> 1) << BCH_FLASHLAYOUT0_ECC0_OFFSET; - tmp |= chunk_data_chunk_size - >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT; + if (!ecc_for_meta) + tmp |= chunk_data_chunk_size + >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT; + else + /* set data0 size as 0 */ + tmp &= ~BCH_FLASHLAYOUT0_DATA0_SIZE_MASK; tmp |= (14 == galois_field ? 1 : 0) << BCH_FLASHLAYOUT0_GF13_0_GF14_1_OFFSET; writel(tmp, &bch_regs->hw_bch_flash0layout0); tmp = (mtd->writesize + mtd->oobsize) << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET; - tmp |= (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1) + tmp |= (ecc_strength >> 1) << BCH_FLASHLAYOUT1_ECCN_OFFSET; tmp |= chunk_data_chunk_size >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT; |