From 43caee68715247ac5a44c967381a12a94f64d1d3 Mon Sep 17 00:00:00 2001 From: Pradeep P V K Date: Thu, 18 Sep 2025 18:25:47 +0530 Subject: [PATCH 1/2] mtd: msm_qpic_nand: check for page_erased bit along with op_err Due to a hardware bug in ECC-Engine, ECC-Engine couldn't able to set OP_ERR bit in flash_status regesiter whenever an erased page encounters bitflips. Due to this, ECC-Engine is trying to correct the bitflips on an erased page and leading to data corruption. So, a check for PAGE_ERASED bit is added prior to OP_ERR bit for an erased page detection logic. Change-Id: I570625123fe828450dade06570f782ebe93d39f1 Signed-off-by: Pradeep P V K Signed-off-by: Pradeep P V K Signed-off-by: Ram Kumar Dwivedi --- drivers/mtd/devices/msm_qpic_nand.c | 64 ++++++++++++++++++++++++++--- drivers/mtd/devices/msm_qpic_nand.h | 6 ++- 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/devices/msm_qpic_nand.c b/drivers/mtd/devices/msm_qpic_nand.c index 329e7ebbd191..0b7a478bb612 100644 --- a/drivers/mtd/devices/msm_qpic_nand.c +++ b/drivers/mtd/devices/msm_qpic_nand.c @@ -2,7 +2,7 @@ /* * Copyright (C) 2007 Google, Inc. * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "msm_qpic_nand.h" @@ -1944,7 +1944,7 @@ free_dma: if (last_pos < ecc_bytes_percw_in_bits) num_zero_bits++; - if (num_zero_bits > 4) { + if (num_zero_bits > MAX_ECC_BIT_FLIPS) { *erased_page = false; goto free_mem; } @@ -1956,7 +1956,7 @@ free_dma: ecc_temp += chip->ecc_parity_bytes; } - if ((n == cwperpage) && (num_zero_bits <= 4)) + if ((n == cwperpage) && (num_zero_bits <= MAX_ECC_BIT_FLIPS)) *erased_page = true; free_mem: kfree(ecc); @@ -2163,6 +2163,33 @@ static int msm_nand_read_pagescope(struct mtd_info *mtd, loff_t from, goto free_dma; /* Check for flash status errors */ pageerr = rawerr = 0; + + /* + * PAGE_ERASED bit will set only if all + * CODEWORD_ERASED bit of all codewords + * of the page is set. + * + * PAGE_ERASED bit is a 'logical and' of all + * CODEWORD_ERASED bit of all codewords i.e. + * even if one codeword is detected as not + * an erased codeword, PAGE_ERASED bit will unset. + */ + for (n = rw_params.start_sector; n < cwperpage; n++) { + if ((dma_buffer->result[n].erased_cw_status & + (1 << PAGE_ERASED)) && + (dma_buffer->result[n].buffer_status & + NUM_ERRORS)) { + err = msm_nand_is_erased_page_ps(mtd, + from, ops, + &rw_params, + &erased_page); + if (err) + goto free_dma; + if (erased_page) + rawerr = -EIO; + break; + } + } for (n = rw_params.start_sector; n < cwperpage; n++) { if (dma_buffer->result[n].flash_status & (FS_OP_ERR | FS_MPU_ERR)) { @@ -2554,7 +2581,7 @@ free_dma: if (last_pos < ecc_bytes_percw_in_bits) num_zero_bits++; - if (num_zero_bits > 4) { + if (num_zero_bits > MAX_ECC_BIT_FLIPS) { *erased_page = false; goto free_mem; } @@ -2566,7 +2593,7 @@ free_dma: ecc_temp += chip->ecc_parity_bytes; } - if ((n == cwperpage) && (num_zero_bits <= 4)) + if ((n == cwperpage) && (num_zero_bits <= MAX_ECC_BIT_FLIPS)) *erased_page = true; free_mem: kfree(ecc); @@ -2760,6 +2787,33 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, goto free_dma; /* Check for flash status errors */ pageerr = rawerr = 0; + + /* + * PAGE_ERASED bit will set only if all + * CODEWORD_ERASED bit of all codewords + * of the page is set. + * + * PAGE_ERASED bit is a 'logical and' of all + * CODEWORD_ERASED bit of all codewords i.e. + * even if one codeword is detected as not + * an erased codeword, PAGE_ERASED bit will unset. + */ + for (n = rw_params.start_sector; n < cwperpage; n++) { + if ((dma_buffer->result[n].erased_cw_status & + (1 << PAGE_ERASED)) && + (dma_buffer->result[n].buffer_status & + NUM_ERRORS)) { + err = msm_nand_is_erased_page(mtd, + from, ops, + &rw_params, + &erased_page); + if (err) + goto free_dma; + if (erased_page) + rawerr = -EIO; + break; + } + } for (n = rw_params.start_sector; n < cwperpage; n++) { if (dma_buffer->result[n].flash_status & (FS_OP_ERR | FS_MPU_ERR)) { diff --git a/drivers/mtd/devices/msm_qpic_nand.h b/drivers/mtd/devices/msm_qpic_nand.h index cc1df16f5b3d..0297ad9697c4 100644 --- a/drivers/mtd/devices/msm_qpic_nand.h +++ b/drivers/mtd/devices/msm_qpic_nand.h @@ -154,7 +154,10 @@ #define RESET_ERASED_DET (1 << AUTO_DETECT_RES) #define ACTIVE_ERASED_DET (0 << AUTO_DETECT_RES) #define CLR_ERASED_PAGE_DET (RESET_ERASED_DET | MASK_ECC) -#define SET_ERASED_PAGE_DET (ACTIVE_ERASED_DET | MASK_ECC) +#define SET_ERASED_PAGE_DET (ACTIVE_ERASED_DET | MASK_ECC | SET_N_MAX_ZEROS) +#define N_MAX_ZEROS 2 +#define MAX_ECC_BIT_FLIPS 4 +#define SET_N_MAX_ZEROS (MAX_ECC_BIT_FLIPS << N_MAX_ZEROS) #define MSM_NAND_ERASED_CW_DETECT_STATUS(info) MSM_NAND_REG(info, 0x300EC) #define PAGE_ALL_ERASED 7 @@ -163,6 +166,7 @@ #define CODEWORD_ERASED 4 #define ERASED_PAGE ((1 << PAGE_ALL_ERASED) | (1 << PAGE_ERASED)) #define ERASED_CW ((1 << CODEWORD_ALL_ERASED) | (1 << CODEWORD_ERASED)) +#define NUM_ERRORS 0x1f #define MSM_NAND_CTRL(info) MSM_NAND_REG(info, 0x30F00) #define BAM_MODE_EN 0 From 96f3098bef83a0a81d5abca00df27cc043073fa6 Mon Sep 17 00:00:00 2001 From: Pradeep P V K Date: Fri, 5 Jun 2020 18:38:21 +0530 Subject: [PATCH 2/2] mtd: msm_qpic_nand: Use flash device ECC capability for erase page Page codeword's ECC data is used to determine if the page is actually erased or not. On an erased page, this data is all 0xFF. The acceptable bitflips on this ECC data is flash device dependent. So, always check against flash device ECC capability value for erase page determination. Change-Id: Ib3889f91c871b5f131fe8bc960ffa68ac11e0633 Signed-off-by: Pradeep P V K Signed-off-by: Pradeep P V K Signed-off-by: Ram Kumar Dwivedi --- drivers/mtd/devices/msm_qpic_nand.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/devices/msm_qpic_nand.c b/drivers/mtd/devices/msm_qpic_nand.c index 0b7a478bb612..eb45e4908968 100644 --- a/drivers/mtd/devices/msm_qpic_nand.c +++ b/drivers/mtd/devices/msm_qpic_nand.c @@ -1944,7 +1944,7 @@ free_dma: if (last_pos < ecc_bytes_percw_in_bits) num_zero_bits++; - if (num_zero_bits > MAX_ECC_BIT_FLIPS) { + if (num_zero_bits > info->flash_dev.ecc_capability) { *erased_page = false; goto free_mem; } @@ -1955,8 +1955,8 @@ free_dma: num_zero_bits = last_pos = next_pos = 0; ecc_temp += chip->ecc_parity_bytes; } - - if ((n == cwperpage) && (num_zero_bits <= MAX_ECC_BIT_FLIPS)) + if ((n == cwperpage) && + (num_zero_bits <= info->flash_dev.ecc_capability)) *erased_page = true; free_mem: kfree(ecc); @@ -2581,7 +2581,7 @@ free_dma: if (last_pos < ecc_bytes_percw_in_bits) num_zero_bits++; - if (num_zero_bits > MAX_ECC_BIT_FLIPS) { + if (num_zero_bits > info->flash_dev.ecc_capability) { *erased_page = false; goto free_mem; } @@ -2593,7 +2593,8 @@ free_dma: ecc_temp += chip->ecc_parity_bytes; } - if ((n == cwperpage) && (num_zero_bits <= MAX_ECC_BIT_FLIPS)) + if ((n == cwperpage) && + (num_zero_bits <= info->flash_dev.ecc_capability)) *erased_page = true; free_mem: kfree(ecc);