diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mtd/nand/mxs_nand.c | 156 |
1 files changed, 118 insertions, 38 deletions
diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c index d3c420afde6..b10539e674f 100644 --- a/drivers/mtd/nand/mxs_nand.c +++ b/drivers/mtd/nand/mxs_nand.c @@ -7,7 +7,7 @@ * Based on code from LTIB: * Freescale GPMI NFC NAND Flash Driver * - * Copyright (C) 2010 Freescale Semiconductor, Inc. + * Copyright (C) 2010-2016 Freescale Semiconductor, Inc. * Copyright (C) 2008 Embedded Alley Solutions, Inc. * * SPDX-License-Identifier: GPL-2.0+ @@ -46,6 +46,11 @@ #define MXS_NAND_BCH_TIMEOUT 10000 +int bbm_chunk; +int ecc_strength; +bool large_oob_flag; +bool ecc_for_meta; + struct mxs_nand_info { int cur_chip; @@ -137,7 +142,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_size; + int tmp = page_data_size / chunk_data_size; + return ecc_for_meta ? tmp + 1 : tmp; } static uint32_t mxs_nand_ecc_size_in_bits(uint32_t ecc_strength) @@ -150,10 +156,50 @@ 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_size * 8); + + j = (mtd->writesize * 8 - meta * 8) % + (gf_len * ecc_strength + + chunk_data_size * 8); + + if (j < chunk_data_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; int max_ecc_strength_supported; /* Refer to Chapter 17 for i.MX6DQ, Chapter 18 for i.MX6SX */ @@ -162,20 +208,55 @@ static inline uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size, else max_ecc_strength_supported = 40; - /* - * Determine the ECC layout with the formula: - * ECC bits per chunk = (total page spare data bits) / - * (bits per ECC level) / (chunks per page) - * where: - * total page spare data bits = - * (page oob size - meta data size) * (bits per byte) - */ - ecc_strength = ((page_oob_size - MXS_NAND_METADATA_SIZE) * 8) - / (galois_field * - mxs_nand_ecc_chunk_cnt(page_data_size)); + if (chip->ecc_strength_ds > max_ecc_strength_supported) { + printf("cannot support the NAND, ecc too weak\n"); + return -EINVAL; + } - return min(round_down(ecc_strength, 2), max_ecc_strength_supported); -} + 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_size = chip->ecc_step_ds; + if (1024 == chunk_data_size) + galois_field = 14; + if (chunk_data_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 > max_ecc_strength_supported)) { + 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 > max_ecc_strength_supported) { + 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_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) @@ -196,8 +277,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 @@ -228,15 +314,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; } @@ -992,15 +1074,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) { - galois_field = 14; - chunk_data_size = MXS_NAND_CHUNK_DATA_CHUNK_SIZE * 2; - } - - if (mtd->oobsize > chunk_data_size) { - printf("Not support the NAND chips whose oob size is larger then %d bytes!\n", chunk_data_size); - return -EINVAL; - } + /* 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); @@ -1009,16 +1084,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_size >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT; - tmp |= (14 == galois_field ? 1 : 0) << - BCH_FLASHLAYOUT0_GF13_0_GF14_1_OFFSET; + if (!ecc_for_meta) + tmp |= chunk_data_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_size >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT; tmp |= (14 == galois_field ? 1 : 0) << |