summaryrefslogtreecommitdiffstats
path: root/bsp/meta-freescale-3rdparty/recipes-kernel/linux/linux-fslc-lts-4.19/ccimx6ul/0001-MLK-11719-4-mtd-gpmi-change-the-BCH-layout-setting-f.patch
diff options
context:
space:
mode:
Diffstat (limited to 'bsp/meta-freescale-3rdparty/recipes-kernel/linux/linux-fslc-lts-4.19/ccimx6ul/0001-MLK-11719-4-mtd-gpmi-change-the-BCH-layout-setting-f.patch')
-rw-r--r--bsp/meta-freescale-3rdparty/recipes-kernel/linux/linux-fslc-lts-4.19/ccimx6ul/0001-MLK-11719-4-mtd-gpmi-change-the-BCH-layout-setting-f.patch552
1 files changed, 552 insertions, 0 deletions
diff --git a/bsp/meta-freescale-3rdparty/recipes-kernel/linux/linux-fslc-lts-4.19/ccimx6ul/0001-MLK-11719-4-mtd-gpmi-change-the-BCH-layout-setting-f.patch b/bsp/meta-freescale-3rdparty/recipes-kernel/linux/linux-fslc-lts-4.19/ccimx6ul/0001-MLK-11719-4-mtd-gpmi-change-the-BCH-layout-setting-f.patch
new file mode 100644
index 00000000..c2b81030
--- /dev/null
+++ b/bsp/meta-freescale-3rdparty/recipes-kernel/linux/linux-fslc-lts-4.19/ccimx6ul/0001-MLK-11719-4-mtd-gpmi-change-the-BCH-layout-setting-f.patch
@@ -0,0 +1,552 @@
+From: Alex Gonzalez <alex.gonzalez@digi.com>
+Date: Fri, 24 Aug 2018 18:53:40 +0200
+Subject: [PATCH] MLK-11719-4: mtd: gpmi: change the BCH layout setting for
+ large oob NAND
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The cod change updated the NAND driver BCH ECC layout algorithm to
+support large oob size NAND chips(oob > 1024 bytes) and proposed a new
+way to set ECC layout.
+
+Current implementation requires each chunk size larger than oob size so
+the bad block marker (BBM) can be guaranteed located in data chunk. The
+ECC layout always using the unbalanced layout(Ecc for both meta and
+Data0 chunk), but for the NAND chips with oob larger than 1k, the driver
+cannot support because BCH doesn’t support GF 15 for 2K chunk.
+
+The change keeps the data chunk no larger than 1k and adjust the ECC
+strength or ECC layout to locate the BBM in data chunk. General idea for
+large oob NAND chips is
+
+1.Try all ECC strength from the minimum value required by NAND spec to
+the maximum one that works, any ECC makes the BBM locate in data chunk
+can be chosen.
+
+2.If none of them works, using separate ECC for meta, which will add one
+extra ecc with the same ECC strength as other data chunks. This extra
+ECC can guarantee BBM located in data chunk, of course, we need to check
+if oob can afford it.
+
+Previous code has two methods for ECC layout setting, the
+legacy_set_geometry and set_geometry_by_ecc_info, the difference
+between these two methods is, legacy_set_geometry set the chunk size
+larger chan oob size and then set the maximum ECC strength that oob can
+afford. While the set_geometry_by_ecc_info set chunk size and ECC
+strength according to NAND spec. It has been proved that the first
+method cannot provide safe ECC strength for some modern NAND chips, so
+in current code,
+
+1. Driver read NAND parameters first and then chose the proper ECC
+layout setting method.
+
+2. If the oob is large or NAND required data chunk larger than oob size,
+chose set_geometry_for_large_oob, otherwise use set_geometry_by_ecc_info
+
+3. legacy_set_geometry only used for some NAND chips does not contains
+necessary information. So this is only a backup plan, it is NOT
+recommended to use these NAND chips.
+
+Signed-off-by: Han Xu <b45815@freescale.com>
+(cherry picked from commit 78e8beff734adb72185405ae2cb55e0097eb96cb)
+Signed-off-by: Alex Gonzalez <alex.gonzalez@digi.com>
+---
+ drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c | 16 +-
+ drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 269 ++++++++++++++++++++++++-----
+ drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h | 12 +-
+ 3 files changed, 248 insertions(+), 49 deletions(-)
+
+diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c
+index 88ea2203e263..a4cd9523e220 100644
+--- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c
++++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c
+@@ -212,7 +212,8 @@ void gpmi_dump_info(struct gpmi_nand_data *this)
+ "ECC Strength : %u\n"
+ "Page Size in Bytes : %u\n"
+ "Metadata Size in Bytes : %u\n"
+- "ECC Chunk Size in Bytes: %u\n"
++ "ECC Chunk0 Size in Bytes: %u\n"
++ "ECC Chunkn Size in Bytes: %u\n"
+ "ECC Chunk Count : %u\n"
+ "Payload Size in Bytes : %u\n"
+ "Auxiliary Size in Bytes: %u\n"
+@@ -223,7 +224,8 @@ void gpmi_dump_info(struct gpmi_nand_data *this)
+ geo->ecc_strength,
+ geo->page_size,
+ geo->metadata_size,
+- geo->ecc_chunk_size,
++ geo->ecc_chunk0_size,
++ geo->ecc_chunkn_size,
+ geo->ecc_chunk_count,
+ geo->payload_size,
+ geo->auxiliary_size,
+@@ -238,7 +240,8 @@ int bch_set_geometry(struct gpmi_nand_data *this)
+ struct resources *r = &this->resources;
+ struct bch_geometry *bch_geo = &this->bch_geometry;
+ unsigned int block_count;
+- unsigned int block_size;
++ unsigned int block0_size;
++ unsigned int blockn_size;
+ unsigned int metadata_size;
+ unsigned int ecc_strength;
+ unsigned int page_size;
+@@ -250,7 +253,8 @@ int bch_set_geometry(struct gpmi_nand_data *this)
+ return ret;
+
+ block_count = bch_geo->ecc_chunk_count - 1;
+- block_size = bch_geo->ecc_chunk_size;
++ block0_size = bch_geo->ecc_chunk0_size;
++ blockn_size = bch_geo->ecc_chunkn_size;
+ metadata_size = bch_geo->metadata_size;
+ ecc_strength = bch_geo->ecc_strength >> 1;
+ page_size = bch_geo->page_size;
+@@ -277,13 +281,13 @@ int bch_set_geometry(struct gpmi_nand_data *this)
+ | BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size)
+ | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this)
+ | BF_BCH_FLASH0LAYOUT0_GF(gf_len, this)
+- | BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size, this),
++ | BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block0_size, this),
+ r->bch_regs + HW_BCH_FLASH0LAYOUT0);
+
+ writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size)
+ | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this)
+ | BF_BCH_FLASH0LAYOUT1_GF(gf_len, this)
+- | BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size, this),
++ | BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(blockn_size, this),
+ r->bch_regs + HW_BCH_FLASH0LAYOUT1);
+
+ /* Set *all* chip selects to use layout 0. */
+diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
+index 1c1ebbc82824..bc4a364e5696 100644
+--- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
++++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
+@@ -179,6 +179,36 @@ static inline bool gpmi_check_ecc(struct gpmi_nand_data *this)
+ return geo->ecc_strength <= this->devdata->bch_max_ecc_strength;
+ }
+
++static inline bool bbm_in_data_chunk(struct gpmi_nand_data *this,
++ unsigned int *chunk_num)
++{
++ struct bch_geometry *geo = &this->bch_geometry;
++ struct mtd_info *mtd = &this->nand.mtd;
++ unsigned int i, j;
++
++ if (geo->ecc_chunk0_size != geo->ecc_chunkn_size) {
++ dev_err(this->dev, "The size of chunk0 must equal to chunkn\n");
++ return false;
++ }
++
++ i = (mtd->writesize * 8 - geo->metadata_size * 8) /
++ (geo->gf_len * geo->ecc_strength +
++ geo->ecc_chunkn_size * 8);
++
++ j = (mtd->writesize * 8 - geo->metadata_size * 8) -
++ (geo->gf_len * geo->ecc_strength +
++ geo->ecc_chunkn_size * 8) * i;
++
++ if (j < geo->ecc_chunkn_size * 8) {
++ *chunk_num = i+1;
++ dev_dbg(this->dev, "Set ecc to %d and bbm in chunk %d\n",
++ geo->ecc_strength, *chunk_num);
++ return true;
++ }
++
++ return false;
++}
++
+ /*
+ * If we can get the ECC information from the nand chip, we do not
+ * need to calculate them ourselves.
+@@ -207,13 +237,14 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this,
+ chip->ecc_strength_ds, chip->ecc_step_ds);
+ return -EINVAL;
+ }
+- geo->ecc_chunk_size = ecc_step;
+- geo->ecc_strength = round_up(ecc_strength, 2);
++ geo->ecc_chunk0_size = chip->ecc_step_ds;
++ geo->ecc_chunkn_size = chip->ecc_step_ds;
++ geo->ecc_strength = round_up(chip->ecc_strength_ds, 2);
+ if (!gpmi_check_ecc(this))
+ return -EINVAL;
+
+ /* Keep the C >= O */
+- if (geo->ecc_chunk_size < mtd->oobsize) {
++ if (geo->ecc_chunkn_size < mtd->oobsize) {
+ dev_err(this->dev,
+ "unsupported nand chip. ecc size: %d, oob size : %d\n",
+ ecc_step, mtd->oobsize);
+@@ -223,7 +254,7 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this,
+ /* The default value, see comment in the legacy_set_geometry(). */
+ geo->metadata_size = 10;
+
+- geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size;
++ geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunkn_size;
+
+ /*
+ * Now, the NAND chip with 2K page(data chunk is 512byte) shows below:
+@@ -295,6 +326,129 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this,
+ return 0;
+ }
+
++static int set_geometry_for_large_oob(struct gpmi_nand_data *this)
++{
++ struct bch_geometry *geo = &this->bch_geometry;
++ struct mtd_info *mtd = &this->nand.mtd;
++ struct nand_chip *chip = mtd->priv;
++ unsigned int block_mark_bit_offset;
++ unsigned int max_ecc;
++ unsigned int bbm_chunk;
++ unsigned int i;
++
++
++ /* sanity check for the minimum ecc nand required */
++ if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0))
++ return -EINVAL;
++ geo->ecc_strength = chip->ecc_strength_ds;
++
++ /* check if platform can support this nand */
++ if (!gpmi_check_ecc(this)) {
++ dev_err(this->dev, "unsupported NAND chip, minimum ecc required %d\n"
++ , geo->ecc_strength);
++ return -EINVAL;
++ }
++
++ /* calculate the maximum ecc platform can support*/
++ geo->metadata_size = 10;
++ geo->gf_len = 14;
++ geo->ecc_chunk0_size = 1024;
++ geo->ecc_chunkn_size = 1024;
++ geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunkn_size;
++ max_ecc = min(get_ecc_strength(this),
++ this->devdata->bch_max_ecc_strength);
++
++ /* search a supported ecc strength that makes bbm */
++ /* located in data chunk */
++ geo->ecc_strength = chip->ecc_strength_ds;
++ while (!(geo->ecc_strength > max_ecc)) {
++ if (bbm_in_data_chunk(this, &bbm_chunk))
++ goto geo_setting;
++ geo->ecc_strength += 2;
++ }
++
++ /* if none of them works, keep using the minimum ecc */
++ /* nand required but changing ecc page layout */
++ geo->ecc_strength = chip->ecc_strength_ds;
++ /* add extra ecc for meta data */
++ geo->ecc_chunk0_size = 0;
++ geo->ecc_chunk_count = (mtd->writesize / geo->ecc_chunkn_size) + 1;
++ geo->ecc_for_meta = 1;
++ /* check if oob can afford this extra ecc chunk */
++ if (mtd->oobsize * 8 < geo->metadata_size * 8 +
++ geo->gf_len * geo->ecc_strength
++ * geo->ecc_chunk_count) {
++ dev_err(this->dev, "unsupported NAND chip with new layout\n");
++ return -EINVAL;
++ }
++
++ /* calculate in which chunk bbm located */
++ bbm_chunk = (mtd->writesize * 8 - geo->metadata_size * 8 -
++ geo->gf_len * geo->ecc_strength) /
++ (geo->gf_len * geo->ecc_strength +
++ geo->ecc_chunkn_size * 8) + 1;
++
++geo_setting:
++
++ geo->page_size = mtd->writesize + mtd->oobsize;
++ geo->payload_size = mtd->writesize;
++
++ /*
++ * The auxiliary buffer contains the metadata and the ECC status. The
++ * metadata is padded to the nearest 32-bit boundary. The ECC status
++ * contains one byte for every ECC chunk, and is also padded to the
++ * nearest 32-bit boundary.
++ */
++ geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4);
++ geo->auxiliary_size = ALIGN(geo->metadata_size, 4)
++ + ALIGN(geo->ecc_chunk_count, 4);
++
++ if (!this->swap_block_mark)
++ return 0;
++
++ /* calculate the number of ecc chunk behind the bbm */
++ i = (mtd->writesize / geo->ecc_chunkn_size) - bbm_chunk + 1;
++
++ block_mark_bit_offset = mtd->writesize * 8 -
++ (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - i)
++ + geo->metadata_size * 8);
++
++ geo->block_mark_byte_offset = block_mark_bit_offset / 8;
++ geo->block_mark_bit_offset = block_mark_bit_offset % 8;
++
++ dev_dbg(this->dev, "BCH Geometry :\n"
++ "GF length : %u\n"
++ "ECC Strength : %u\n"
++ "Page Size in Bytes : %u\n"
++ "Metadata Size in Bytes : %u\n"
++ "ECC Chunk0 Size in Bytes: %u\n"
++ "ECC Chunkn Size in Bytes: %u\n"
++ "ECC Chunk Count : %u\n"
++ "Payload Size in Bytes : %u\n"
++ "Auxiliary Size in Bytes: %u\n"
++ "Auxiliary Status Offset: %u\n"
++ "Block Mark Byte Offset : %u\n"
++ "Block Mark Bit Offset : %u\n"
++ "Block Mark in chunk : %u\n"
++ "Ecc for Meta data : %u\n",
++ geo->gf_len,
++ geo->ecc_strength,
++ geo->page_size,
++ geo->metadata_size,
++ geo->ecc_chunk0_size,
++ geo->ecc_chunkn_size,
++ geo->ecc_chunk_count,
++ geo->payload_size,
++ geo->auxiliary_size,
++ geo->auxiliary_status_offset,
++ geo->block_mark_byte_offset,
++ geo->block_mark_bit_offset,
++ bbm_chunk,
++ geo->ecc_for_meta);
++
++ return 0;
++}
++
+ static int legacy_set_geometry(struct gpmi_nand_data *this)
+ {
+ struct bch_geometry *geo = &this->bch_geometry;
+@@ -314,13 +468,15 @@ static int legacy_set_geometry(struct gpmi_nand_data *this)
+ geo->gf_len = 13;
+
+ /* The default for chunk size. */
+- geo->ecc_chunk_size = 512;
+- while (geo->ecc_chunk_size < mtd->oobsize) {
+- geo->ecc_chunk_size *= 2; /* keep C >= O */
++ geo->ecc_chunk0_size = 512;
++ geo->ecc_chunkn_size = 512;
++ while (geo->ecc_chunkn_size < mtd->oobsize) {
++ geo->ecc_chunk0_size *= 2; /* keep C >= O */
++ geo->ecc_chunkn_size *= 2; /* keep C >= O */
+ geo->gf_len = 14;
+ }
+
+- geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size;
++ geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunkn_size;
+
+ /* We use the same ECC strength for all chunks. */
+ geo->ecc_strength = get_ecc_strength(this);
+@@ -409,22 +565,25 @@ static int legacy_set_geometry(struct gpmi_nand_data *this)
+
+ int common_nfc_set_geometry(struct gpmi_nand_data *this)
+ {
+- struct nand_chip *chip = &this->nand;
++ struct mtd_info *mtd = &this->nand.mtd;
++ struct nand_chip *chip = mtd_to_nand(mtd);
+
+- if (chip->ecc.strength > 0 && chip->ecc.size > 0)
+- return set_geometry_by_ecc_info(this, chip->ecc.strength,
+- chip->ecc.size);
++ if (chip->ecc_strength_ds > this->devdata->bch_max_ecc_strength) {
++ dev_err(this->dev,
++ "unsupported NAND chip, minimum ecc required %d\n"
++ , chip->ecc_strength_ds);
++ return -EINVAL;
++ }
+
+- if ((of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc"))
+- || legacy_set_geometry(this)) {
+- if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0))
+- return -EINVAL;
++ if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0) &&
++ !(mtd->oobsize > 1024))
++ return legacy_set_geometry(this);
+
+- return set_geometry_by_ecc_info(this, chip->ecc_strength_ds,
+- chip->ecc_step_ds);
+- }
++ if (mtd->oobsize > 1024 || chip->ecc_step_ds < mtd->oobsize)
++ return set_geometry_for_large_oob(this);
+
+- return 0;
++ return set_geometry_by_ecc_info(this, chip->ecc_strength_ds,
++ chip->ecc_step_ds);
+ }
+
+ struct dma_chan *get_dma_chan(struct gpmi_nand_data *this)
+@@ -997,7 +1156,8 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip,
+
+ /* Read ECC bytes into our internal raw_buffer */
+ offset = nfc_geo->metadata_size * 8;
+- offset += ((8 * nfc_geo->ecc_chunk_size) + eccbits) * (i + 1);
++ offset += ((8 * nfc_geo->ecc_chunkn_size) + eccbits) *
++ (i + 1);
+ offset -= eccbits;
+ bitoffset = offset % 8;
+ eccbytes = DIV_ROUND_UP(offset + eccbits, 8);
+@@ -1034,19 +1194,19 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip,
+ if (i == 0) {
+ /* The first block includes metadata */
+ flips = nand_check_erased_ecc_chunk(
+- buf + i * nfc_geo->ecc_chunk_size,
+- nfc_geo->ecc_chunk_size,
+- eccbuf, eccbytes,
+- this->auxiliary_virt,
+- nfc_geo->metadata_size,
+- nfc_geo->ecc_strength);
++ buf + i * nfc_geo->ecc_chunkn_size,
++ nfc_geo->ecc_chunkn_size,
++ eccbuf, eccbytes,
++ this->payload_virt,
++ nfc_geo->metadata_size,
++ nfc_geo->ecc_strength);
+ } else {
+ flips = nand_check_erased_ecc_chunk(
+- buf + i * nfc_geo->ecc_chunk_size,
+- nfc_geo->ecc_chunk_size,
+- eccbuf, eccbytes,
+- NULL, 0,
+- nfc_geo->ecc_strength);
++ buf + i * nfc_geo->ecc_chunkn_size,
++ nfc_geo->ecc_chunkn_size,
++ eccbuf, eccbytes,
++ NULL, 0,
++ nfc_geo->ecc_strength);
+ }
+
+ if (flips > 0) {
+@@ -1134,9 +1294,24 @@ static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
+ }
+ }
+
++ /*
++ * if there is an ECC dedicate for meta:
++ * - need to add an extra ECC size when calculating col and page_size,
++ * if the meta size is NOT zero.
++ *
++ * - chunk0 size need to set to the same size as other chunks,
++ * if the meta size is zero.
++ */
++
+ meta = geo->metadata_size;
+ if (first) {
+- col = meta + (size + ecc_parity_size) * first;
++ if (geo->ecc_for_meta)
++ col = meta + ecc_parity_size
++ + (size + ecc_parity_size) * first;
++ else
++ col = meta + (size + ecc_parity_size) * first;
++
++ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, col, -1);
+ meta = 0;
+ buf = buf + first * size;
+ }
+@@ -1149,21 +1324,37 @@ static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
+
+ /* change the BCH registers and bch_geometry{} */
+ n = last - first + 1;
+- page_size = meta + (size + ecc_parity_size) * n;
++
++ if (geo->ecc_for_meta && meta)
++ page_size = meta + ecc_parity_size
++ + (size + ecc_parity_size) * n;
++ else
++ page_size = meta + (size + ecc_parity_size) * n;
+
+ r1_new &= ~(BM_BCH_FLASH0LAYOUT0_NBLOCKS |
+ BM_BCH_FLASH0LAYOUT0_META_SIZE);
+- r1_new |= BF_BCH_FLASH0LAYOUT0_NBLOCKS(n - 1)
++ r1_new |= BF_BCH_FLASH0LAYOUT0_NBLOCKS(
++ (geo->ecc_for_meta && meta) ? n : n - 1)
+ | BF_BCH_FLASH0LAYOUT0_META_SIZE(meta);
++
++ /* set chunk0 size if meta size is 0 */
++ if (!meta) {
++ if (GPMI_IS_MX6(this))
++ r1_new &= ~MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE;
++ else
++ r1_new &= ~BM_BCH_FLASH0LAYOUT0_DATA0_SIZE;
++ r1_new |= BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(size, this);
++ }
+ writel(r1_new, bch_regs + HW_BCH_FLASH0LAYOUT0);
+
+ r2_new &= ~BM_BCH_FLASH0LAYOUT1_PAGE_SIZE;
+ r2_new |= BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size);
+ writel(r2_new, bch_regs + HW_BCH_FLASH0LAYOUT1);
+
+- geo->ecc_chunk_count = n;
++ geo->ecc_chunk_count = (geo->ecc_for_meta && meta) ? n + 1 : n;
+ geo->payload_size = n * size;
+ geo->page_size = page_size;
++ geo->metadata_size = meta;
+ geo->auxiliary_status_offset = ALIGN(meta, 4);
+
+ dev_dbg(this->dev, "page:%d(%d:%d)%d, chunk:(%d:%d), BCH PG size:%d\n",
+@@ -1386,7 +1577,7 @@ static int gpmi_ecc_read_page_raw(struct mtd_info *mtd,
+ {
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
+ struct bch_geometry *nfc_geo = &this->bch_geometry;
+- int eccsize = nfc_geo->ecc_chunk_size;
++ int eccsize = nfc_geo->ecc_chunkn_size;
+ int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
+ u8 *tmp_buf = this->raw_buffer;
+ size_t src_bit_off;
+@@ -1471,7 +1662,7 @@ static int gpmi_ecc_write_page_raw(struct mtd_info *mtd,
+ {
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
+ struct bch_geometry *nfc_geo = &this->bch_geometry;
+- int eccsize = nfc_geo->ecc_chunk_size;
++ int eccsize = nfc_geo->ecc_chunkn_size;
+ int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
+ u8 *tmp_buf = this->raw_buffer;
+ uint8_t *oob = chip->oob_poi;
+@@ -1847,7 +2038,7 @@ static int gpmi_init_last(struct gpmi_nand_data *this)
+ ecc->read_oob_raw = gpmi_ecc_read_oob_raw;
+ ecc->write_oob_raw = gpmi_ecc_write_oob_raw;
+ ecc->mode = NAND_ECC_HW;
+- ecc->size = bch_geo->ecc_chunk_size;
++ ecc->size = bch_geo->ecc_chunkn_size;
+ ecc->strength = bch_geo->ecc_strength;
+ mtd_set_ooblayout(mtd, &gpmi_ooblayout_ops);
+
+diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h
+index 69cd0cbde4f2..ef4e57256d30 100644
+--- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h
++++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h
+@@ -30,9 +30,9 @@ struct resources {
+ * @page_size: The size, in bytes, of a physical page, including
+ * both data and OOB.
+ * @metadata_size: The size, in bytes, of the metadata.
+- * @ecc_chunk_size: The size, in bytes, of a single ECC chunk. Note
+- * the first chunk in the page includes both data and
+- * metadata, so it's a bit larger than this value.
++ * @ecc_chunk0_size: The size, in bytes, of a first ECC chunk.
++ * @ecc_chunkn_size: The size, in bytes, of a single ECC chunk after
++ * the first chunk in the page.
+ * @ecc_chunk_count: The number of ECC chunks in the page,
+ * @payload_size: The size, in bytes, of the payload buffer.
+ * @auxiliary_size: The size, in bytes, of the auxiliary buffer.
+@@ -42,19 +42,23 @@ struct resources {
+ * which the underlying physical block mark appears.
+ * @block_mark_bit_offset: The bit offset into the ECC-based page view at
+ * which the underlying physical block mark appears.
++ * @ecc_for_meta: The flag to indicate if there is a dedicate ecc
++ * for meta.
+ */
+ struct bch_geometry {
+ unsigned int gf_len;
+ unsigned int ecc_strength;
+ unsigned int page_size;
+ unsigned int metadata_size;
+- unsigned int ecc_chunk_size;
++ unsigned int ecc_chunk0_size;
++ unsigned int ecc_chunkn_size;
+ unsigned int ecc_chunk_count;
+ unsigned int payload_size;
+ unsigned int auxiliary_size;
+ unsigned int auxiliary_status_offset;
+ unsigned int block_mark_byte_offset;
+ unsigned int block_mark_bit_offset;
++ unsigned int ecc_for_meta; /* ECC for meta data */
+ };
+
+ /**