Received: by 10.213.65.68 with SMTP id h4csp690015imn; Wed, 4 Apr 2018 05:45:16 -0700 (PDT) X-Google-Smtp-Source: AIpwx48xEgx+RNQdlqNZWe/Qug4jkf2aMGY8r9gNtWCIABWXNtxrW0NfETNGGMhkO1ywtNQPyW5y X-Received: by 2002:a17:902:322:: with SMTP id 31-v6mr18586285pld.122.1522845916278; Wed, 04 Apr 2018 05:45:16 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1522845916; cv=none; d=google.com; s=arc-20160816; b=Br6asvLPfmMTl+SN7gZ/J+jV/dOUQUZ6tqJW7Uxq7JBT0k9QnV9lcyAAIrtCCMVZyX YrpzbU8XNlXQ6lH3pA9mUqy4cQQjYquyVRPFRpNpSJV8wBWb83yWvSZJKfES/SRvlu2H bcWVYtU4zLlU97f1TFvXQ21JSLX+P3igHtcbyVbG6IJc740Jakfx+3DxS2zv2MInFebN l0XMtWoBKNVHEo8iIpYuecwh3HIdXhR3beEEFM14WB8VAe+D4TUJaP777m79qQuFYt9y UN5Ddt1Dlzbs039AiTLFLabueZ0Y0iKcIueSaPThxZ2VIlDTfN1SMSBkDta2we92PBZF 9bcw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dmarc-filter:dkim-signature:dkim-signature :arc-authentication-results; bh=4nkZhI6hPxsBk64cR8VLoXkjzfBhgRtKVehgNGqug5Q=; b=xW+bvvJ9VZGcOPQHqcZEUk/v2a3VQ/MRV2ZkOOsI+pvKfTO5GMUBs89xU0se4KaMaM lNqeEbyAupA0Pb8cwtzb2V21FiTrD9x0Nwc6aRvlB4704HhdyRwqF0b9GUI0wbtU2Pd9 DoW+LzYkFh4jo6WFRVKoZrJCks2K4BwJZAlgzc82Sd41yrok5gehGb2vx5JmvB6LQLtp 5J6644/S2wogNVd0lmi3gw5Nq0x3LV5XAKyCU8lF+oT0BpkmCgxAU21ujquncORJyl9i rEzn3N/ZzPhc+SuvdxVWZagwtVytbc0Ou7Uvda2b8rrInoUN4SsgCNjd6pjz2EPv4HV2 pQmw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@codeaurora.org header.s=default header.b=e62pZpa2; dkim=pass header.i=@codeaurora.org header.s=default header.b=nAtnzzeY; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id v14-v6si2543431ply.122.2018.04.04.05.45.01; Wed, 04 Apr 2018 05:45:16 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@codeaurora.org header.s=default header.b=e62pZpa2; dkim=pass header.i=@codeaurora.org header.s=default header.b=nAtnzzeY; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751896AbeDDMnr (ORCPT + 99 others); Wed, 4 Apr 2018 08:43:47 -0400 Received: from smtp.codeaurora.org ([198.145.29.96]:41868 "EHLO smtp.codeaurora.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751353AbeDDMnp (ORCPT ); Wed, 4 Apr 2018 08:43:45 -0400 Received: by smtp.codeaurora.org (Postfix, from userid 1000) id 85F2F60F72; Wed, 4 Apr 2018 12:43:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1522845824; bh=PVWB9nfCp19+SHl/xCt+f3N5XPdnTfUTAqmzssBbJ40=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=e62pZpa2m2RBI8bZHK4dJtVhZ2k0y47uZenp9Gd6eiCFlpI/JDGW+QiRrKcViRXJN 8kqQRky2QtU7GUoAq0ocP56omVy1CAl4Ue4MLlVA5R0OZ5YqnsoxoXoew7WRLaqmJo V/cw/m2gO1HeazIcjaBIVDms8TqCRbbV4gNKJuYc= X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on pdx-caf-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.8 required=2.0 tests=ALL_TRUSTED,BAYES_00, DKIM_SIGNED,T_DKIM_INVALID autolearn=no autolearn_force=no version=3.4.0 Received: from absahu-linux.qualcomm.com (blr-c-bdr-fw-01_globalnat_allzones-outside.qualcomm.com [103.229.19.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: absahu@smtp.codeaurora.org) by smtp.codeaurora.org (Postfix) with ESMTPSA id E4348601D3; Wed, 4 Apr 2018 12:43:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1522845822; bh=PVWB9nfCp19+SHl/xCt+f3N5XPdnTfUTAqmzssBbJ40=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=nAtnzzeY12IChufgP+CowiO0PE9wtmmWtAUf4WK0GVdwl3YReTqajWixDiHzzSnYX wtzuDuHk9MCmnXvspJPXGs+vD/45eJVSDJPJw/75i0ox260mJ5QyVf39uAIDd/AktR L+QA4qQijBqIeTK4MtL/8edY+hWxMbM2v6m4CD7M= DMARC-Filter: OpenDMARC Filter v1.3.2 smtp.codeaurora.org E4348601D3 Authentication-Results: pdx-caf-mail.web.codeaurora.org; dmarc=none (p=none dis=none) header.from=codeaurora.org Authentication-Results: pdx-caf-mail.web.codeaurora.org; spf=none smtp.mailfrom=absahu@codeaurora.org From: Abhishek Sahu To: Boris Brezillon Cc: David Woodhouse , Brian Norris , Marek Vasut , Richard Weinberger , Cyrille Pitchen , linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mtd@lists.infradead.org, Andy Gross , Archit Taneja , Abhishek Sahu Subject: [PATCH 9/9] mtd: nand: qcom: erased page bitflips detection Date: Wed, 4 Apr 2018 18:12:25 +0530 Message-Id: <1522845745-6624-10-git-send-email-absahu@codeaurora.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1522845745-6624-1-git-send-email-absahu@codeaurora.org> References: <1522845745-6624-1-git-send-email-absahu@codeaurora.org> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Some of the newer nand parts can have bit flips in an erased page due to the process technology used. In this case, qpic nand controller is not able to identify that page as an erased page. Currently the driver calls nand_check_erased_ecc_chunk for identifying the erased pages but this won’t work always since the checking is being with ECC engine returned data. In case of bitflips, the ECC engine tries to correct the data and then it generates the uncorrectable error. Now, this data is not equal to original raw data. For erased CW identification, the raw data should be read again from NAND device and this nand_check_erased_ecc_chunk function should be called for raw data only. Now following logic is being added to identify the erased codeword bitflips. 1. In most of the case, not all the codewords will have bitflips and only single CW will have bitflips. So, there is no need to read the complete raw page data. The NAND raw read can be scheduled for any CW in page. The NAND controller works on CW basis and it will update the status register after each CW read. Maintain the bitmask for the CW which generated the uncorrectable error. 2. Schedule the raw flash read from NAND flash device to NAND controller buffer for all these CWs between first and last uncorrectable errors CWs. Copy the content from NAND controller buffer to actual data buffer only for the uncorrectable errors CWs so that other CW data content won’t be affected, and unnecessary data copy can be avoided. 3. Both DATA and OOB need to be checked for number of 0. The top-level API can be called with only data buf or oob buf so use chip->databuf if data buf is null and chip->oob_poi if oob buf is null for copying the raw bytes temporarily. 4. For each CW, check the number of 0 in cw_data and usable oob bytes, The bbm and spare bytes bit flip won’t affect the ECC so don’t check the number of bitflips in this area. Signed-off-by: Abhishek Sahu --- drivers/mtd/nand/qcom_nandc.c | 144 ++++++++++++++++++++++++++++++------------ 1 file changed, 104 insertions(+), 40 deletions(-) diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c index f5d1fa4..ec0b7db 100644 --- a/drivers/mtd/nand/qcom_nandc.c +++ b/drivers/mtd/nand/qcom_nandc.c @@ -1698,25 +1698,112 @@ static int check_flash_errors(struct qcom_nand_host *host, int cw_cnt) } /* + * Bitflips can happen in erased codewords also so this function counts the + * number of 0 in each CW for which ECC engine returns the uncorrectable + * error. The page will be assumed as erased if this count is less than or + * equal to the ecc->strength for each CW. + * + * 1. Both DATA and OOB need to be checked for number of 0. The + * top-level API can be called with only data buf or oob buf so use + * chip->data_buf if data buf is null and chip->oob_poi if oob buf + * is null for copying the raw bytes. + * 2. Perform raw read for all the CW which has uncorrectable errors. + * 3. For each CW, check the number of 0 in cw_data and usable oob bytes. + * The bbm and spare bytes bit flip won’t affect the ECC so don’t check + * the number of bitflips in this area. + */ +static int +check_for_erased_page(struct qcom_nand_host *host, u8 *data_buf, + u8 *oob_buf, unsigned long uncorrectable_err_cws, + int page, unsigned int max_bitflips, bool last_cw) +{ + struct nand_chip *chip = &host->chip; + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct mtd_info *mtd = nand_to_mtd(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + int i, start_step, last_step, ret = 0; + + start_step = ffs(uncorrectable_err_cws) - 1; + last_step = fls(uncorrectable_err_cws); + + if (!last_cw) { + if (!data_buf) + data_buf = chip->data_buf; + if (!oob_buf) + oob_buf = chip->oob_poi; + data_buf += start_step * host->cw_data; + oob_buf += start_step * ecc->bytes; + } + + clear_read_regs(nandc); + nandc_read_page_raw(mtd, chip, data_buf, oob_buf, page, + uncorrectable_err_cws); + + for (i = start_step; i < last_step; i++) { + int data_size, oob_size; + + if (i == (ecc->steps - 1)) { + data_size = ecc->size - ((ecc->steps - 1) << 2); + oob_size = (ecc->steps << 2) + host->ecc_bytes_hw; + } else { + data_size = host->cw_data; + oob_size = host->ecc_bytes_hw; + } + + if (uncorrectable_err_cws & BIT(i)) { + /* + * make sure it isn't an erased page reported + * as not-erased by HW because of a few bitflips + */ + ret = nand_check_erased_ecc_chunk(data_buf, + data_size, oob_buf + host->bbm_size, + oob_size, NULL, + 0, ecc->strength); + if (ret < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += ret; + max_bitflips = + max_t(unsigned int, max_bitflips, ret); + } + } + + data_buf += data_size; + oob_buf += ecc->bytes; + } + + return max_bitflips; +} + +/* * reads back status registers set by the controller to notify page read * errors. this is equivalent to what 'ecc->correct()' would do. */ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, - u8 *oob_buf, bool last_cw) + u8 *oob_buf, bool last_cw, int page) { struct nand_chip *chip = &host->chip; struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); struct mtd_info *mtd = nand_to_mtd(chip); struct nand_ecc_ctrl *ecc = &chip->ecc; - unsigned int max_bitflips = 0; + unsigned int max_bitflips = 0, uncorrectable_err_cws = 0; struct read_stats *buf; bool flash_op_err = false; - int i, cw_cnt = last_cw ? 1 : ecc->steps; + int i, start_cw, cw_cnt = last_cw ? 1 : ecc->steps; + u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf; buf = (struct read_stats *)nandc->reg_read_buf; nandc_read_buffer_sync(nandc, true); - for (i = 0; i < cw_cnt; i++, buf++) { + if (last_cw) { + start_cw = ecc->steps - 1; + cw_cnt = 1; + } else { + start_cw = 0; + cw_cnt = ecc->steps; + } + + for (i = start_cw; i < start_cw + cw_cnt; i++, buf++) { u32 flash, buffer, erased_cw; int data_len, oob_len; @@ -1746,36 +1833,8 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, erased = false; } - if (erased) { - data_buf += data_len; - if (oob_buf) - oob_buf += oob_len + ecc->bytes; - continue; - } - - if (buffer & BS_UNCORRECTABLE_BIT) { - int ret, ecclen, extraooblen; - void *eccbuf; - - eccbuf = oob_buf ? oob_buf + oob_len : NULL; - ecclen = oob_buf ? host->ecc_bytes_hw : 0; - extraooblen = oob_buf ? oob_len : 0; - - /* - * make sure it isn't an erased page reported - * as not-erased by HW because of a few bitflips - */ - ret = nand_check_erased_ecc_chunk(data_buf, - data_len, eccbuf, ecclen, oob_buf, - extraooblen, ecc->strength); - if (ret < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += ret; - max_bitflips = - max_t(unsigned int, max_bitflips, ret); - } - } + if (!erased) + uncorrectable_err_cws |= BIT(i); } else if (flash & (FS_OP_ERR | FS_MPU_ERR)) { flash_op_err = true; } else { @@ -1795,7 +1854,12 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, if (flash_op_err) return -EIO; - return max_bitflips; + if (!uncorrectable_err_cws) + return max_bitflips; + + return check_for_erased_page(host, data_buf_start, oob_buf_start, + uncorrectable_err_cws, page, + max_bitflips, last_cw); } /* @@ -1803,7 +1867,7 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, * ecc->read_oob() */ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, - u8 *oob_buf) + u8 *oob_buf, int page) { struct nand_chip *chip = &host->chip; struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); @@ -1876,7 +1940,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, if (!ret) ret = parse_read_errors(host, data_buf_start, oob_buf_start, - false); + false, page); return ret; } @@ -1917,7 +1981,7 @@ static int copy_last_cw(struct qcom_nand_host *host, int page) if (host->use_ecc) ret = parse_read_errors(host, nandc->data_buffer, nandc->data_buffer + size, - true); + true, page); else ret = check_flash_errors(host, 1); } @@ -1939,7 +2003,7 @@ static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip, clear_bam_transaction(nandc); - return read_page_ecc(host, data_buf, oob_buf); + return read_page_ecc(host, data_buf, oob_buf, page); } /* implements ecc->read_page_raw() */ @@ -1966,7 +2030,7 @@ static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, set_address(host, 0, page); update_rw_regs(host, ecc->steps, true); - return read_page_ecc(host, NULL, chip->oob_poi); + return read_page_ecc(host, NULL, chip->oob_poi, page); } /* implements ecc->write_page() */ -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation