From patchwork Mon Jun 5 23:21:42 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Masahiro Yamada X-Patchwork-Id: 103107 Delivered-To: patch@linaro.org Received: by 10.182.29.35 with SMTP id g3csp1008952obh; Mon, 5 Jun 2017 16:27:17 -0700 (PDT) X-Received: by 10.84.129.132 with SMTP id b4mr7707246plb.0.1496705236948; Mon, 05 Jun 2017 16:27:16 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1496705236; cv=none; d=google.com; s=arc-20160816; b=IXHIZba/efIv5p+0hqRE7Iv50UIKjlXhPjhaR8MAMLmVUL8IdjzRG6G4Y/40aFhIbz RcuGFmCcX00VK/QOnyE+bKEhnEgL7bhDhfvay0tJpe/N4x5p4tSc4wn+rqPCOmkfELKM 7zzH0HX39QH8OGz0x/vJT5Q9kW33QryHxfA7sZEdaKo6UYA+TF+jJao4eDqARxcILeyX DXyCzSzV74ACG+FHxshuqZoOJpI4L/rZbXRnL2DXPTRFZi+9WPMvKQfiiKv048C9SQPZ CuDBmtOhtpDpQ5o42L6RSCSzO6Iac8kQrzCmHMfg+s2dnAlGuUOsDGtzynMpJrK3zm3K V8mw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:dkim-filter :arc-authentication-results; bh=A/NeolnhRM2kX9EmFk1LqpCRFndB6xF6oHRcjPyEjWs=; b=XD15eVRQ+0J39n1O4r9mJiGB3rZh9tfGE9Q1filOc5R+YWSHoZR7ssoOVhmXkvGH2P Ed3h5O2s0aSINUPhHyB1EFeIRd5MYu6fUW91xbg9JR+yx37uKi6bEdlX6C9OSlZVL6dS 71/aTb23RsuUXEVtdvO3ytmLVjkotj4BwVCiHqe6IoheGrndUDwuctyT1tsjysBdQ8+K 2E3zE3wGI5a4q9XhsOimdYEDINVP+GOtMJwbtNIsmrH428gQQLYZB+d5xVKrX0XWLpBP JrfLFVZHSu0ZYfLzHvByQ4Qxxv4pjaSzt//QZuAhyl/qrdvUZpEnEcTJ0kKEmRghe8FI LaGw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@nifty.com; 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 i7si32017216pfi.233.2017.06.05.16.27.16; Mon, 05 Jun 2017 16:27: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=@nifty.com; 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 S1751477AbdFEX0j (ORCPT + 25 others); Mon, 5 Jun 2017 19:26:39 -0400 Received: from conuserg-10.nifty.com ([210.131.2.77]:32371 "EHLO conuserg-10.nifty.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751274AbdFEXZY (ORCPT ); Mon, 5 Jun 2017 19:25:24 -0400 Received: from grover.sesame (FL1-118-110-19-204.osk.mesh.ad.jp [118.110.19.204]) (authenticated) by conuserg-10.nifty.com with ESMTP id v55NMD5d004412; Tue, 6 Jun 2017 08:22:30 +0900 DKIM-Filter: OpenDKIM Filter v2.10.3 conuserg-10.nifty.com v55NMD5d004412 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nifty.com; s=dec2015msa; t=1496704951; bh=A/NeolnhRM2kX9EmFk1LqpCRFndB6xF6oHRcjPyEjWs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=oYnUW7nFeZx2yJ81nwGAZHsKG8HYD7/y7WUtzwvumrhJ9Tsgoe9Lf49vHBgXxKpxf IQTy+vzFAQeyO7z35ykQTknL/6zuXvnbWQeRdMN3DecX7G6XtewccjGVT/XFgBf9Y+ u8ZMdkd61DHEvoOY75Ylc7J7yeYu86I35rg3k7YXB0gTnRI3vTrV/6e36I00+tiZ+o FOmV7mjOBAT3lwKSyaZdQCRCJx2biHarI16aEskAjr0XXAdmCh+5d61yCZRTlxKsTC 2cEKFFaZZt45BCKQAexPUUMKPewfAeNSZgo3oacdvrPLmycPtmbSLrm3Q/+yzhRL7F MgAFy0ujI1hmQ== X-Nifty-SrcIP: [118.110.19.204] From: Masahiro Yamada To: linux-mtd@lists.infradead.org Cc: Enrico Jorns , Artem Bityutskiy , Dinh Nguyen , Boris Brezillon , Marek Vasut , Graham Moore , David Woodhouse , Masami Hiramatsu , Chuanxiao Dong , Jassi Brar , Masahiro Yamada , Cyrille Pitchen , linux-kernel@vger.kernel.org, Brian Norris , Richard Weinberger Subject: [PATCH v4 03/23] mtd: nand: add generic helpers to check, match, maximize ECC settings Date: Tue, 6 Jun 2017 08:21:42 +0900 Message-Id: <1496704922-12261-4-git-send-email-yamada.masahiro@socionext.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1496704922-12261-1-git-send-email-yamada.masahiro@socionext.com> References: <1496704922-12261-1-git-send-email-yamada.masahiro@socionext.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Driver are responsible for setting up ECC parameters correctly. Those include: - Check if ECC parameters specified (usually by DT) are valid - Meet the chip's ECC requirement - Maximize ECC strength if NAND_ECC_MAXIMIZE flag is set The logic can be generalized by factoring out common code. This commit adds 3 helpers to the NAND framework: nand_check_ecc_caps - Check if preset step_size and strength are valid nand_match_ecc_req - Match the chip's requirement nand_maximize_ecc - Maximize the ECC strength To use the helpers above, a driver needs to provide: - Data array of supported ECC step size and strength - A hook that calculates ECC bytes from the combination of step_size and strength. By using those helpers, code duplication among drivers will be reduced. Signed-off-by: Masahiro Yamada --- Changes since the previous version: - Step size info holds an array of associated strengths - nand_match_ecc_req() does not take care of the case where ecc_size/strength is already set - Reflect more comments from Boris Previous version: http://patchwork.ozlabs.org/patch/752107/ Changes in v4: None Changes in v3: None Changes in v2: None drivers/mtd/nand/nand_base.c | 219 +++++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/nand.h | 35 +++++++ 2 files changed, 254 insertions(+) -- 2.7.4 diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index bdfa903..f2da4f2 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -4509,6 +4509,225 @@ static int nand_set_ecc_soft_ops(struct mtd_info *mtd) } } +/** + * nand_check_ecc_caps - check the sanity of preset ECC settings + * @mtd: mtd info structure + * @chip: nand chip info structure + * @caps: ECC caps info structure + * + * When ECC step size and strength are already set, check if they are supported + * by the controller and the calculated ECC bytes fit within the chip's OOB. + * On success, the calculated ECC bytes is set. + */ +int nand_check_ecc_caps(struct mtd_info *mtd, struct nand_chip *chip, + const struct nand_ecc_caps *caps) +{ + const struct nand_ecc_step_info *stepinfo; + int avail_oobsize = mtd->oobsize - caps->oob_reserve_bytes; + int preset_step = chip->ecc.size; + int preset_strength = chip->ecc.strength; + int ecc_bytes; + int i, j; + + if (WARN_ON(avail_oobsize < 0)) + return -EINVAL; + + if (!preset_step || !preset_strength) + return -ENODATA; + + for (i = 0; i < caps->nstepinfos; i++) { + stepinfo = &caps->stepinfos[i]; + + if (stepinfo->stepsize != preset_step) + continue; + + for (j = 0; j < stepinfo->nstrengths; j++) { + if (stepinfo->strengths[j] == preset_strength) + goto found; + } + } + + pr_err("ECC (step, strength) = (%d, %d) not supported on this controller", + preset_step, preset_strength); + + return -ENOTSUPP; + +found: + ecc_bytes = caps->calc_ecc_bytes(preset_step, preset_strength); + if (WARN_ON_ONCE(ecc_bytes < 0)) + return ecc_bytes; + + if (ecc_bytes * mtd->writesize / preset_step > avail_oobsize) { + pr_err("ECC (step, strength) = (%d, %d) does not fit in OOB", + preset_step, preset_strength); + return -ENOSPC; + } + + chip->ecc.bytes = ecc_bytes; + + return 0; +} +EXPORT_SYMBOL_GPL(nand_check_ecc_caps); + +/** + * nand_match_ecc_req - meet the chip's requirement with least ECC bytes + * @mtd: mtd info structure + * @chip: nand chip info structure + * @caps: ECC engine caps info structure + * + * If a chip's ECC requirement is provided, try to meet it with the least + * number of ECC bytes (i.e. with the largest number of OOB-free bytes). + * On success, the chosen ECC settings are set. + */ +int nand_match_ecc_req(struct mtd_info *mtd, struct nand_chip *chip, + const struct nand_ecc_caps *caps) +{ + const struct nand_ecc_step_info *stepinfo; + int avail_oobsize = mtd->oobsize - caps->oob_reserve_bytes; + int req_step = chip->ecc_step_ds; + int req_strength = chip->ecc_strength_ds; + int req_corr, step_size, strength, steps, ecc_bytes, ecc_bytes_total; + int best_step, best_strength, best_ecc_bytes; + int best_ecc_bytes_total = INT_MAX; + int i, j; + + if (WARN_ON(avail_oobsize < 0)) + return -EINVAL; + + /* No information provided by the NAND chip */ + if (!req_step || !req_strength) + return -ENOTSUPP; + + /* number of correctable bits the chip requires in a page */ + req_corr = mtd->writesize / req_step * req_strength; + + for (i = 0; i < caps->nstepinfos; i++) { + stepinfo = &caps->stepinfos[i]; + step_size = stepinfo->stepsize; + + for (j = 0; j < stepinfo->nstrengths; j++) { + strength = stepinfo->strengths[j]; + + /* + * If both step size and strength are smaller than the + * chip's requirement, it is not easy to compare the + * resulted reliability. + */ + if (step_size < req_step && strength < req_strength) + continue; + + if (mtd->writesize % step_size) + continue; + + steps = mtd->writesize / step_size; + + ecc_bytes = caps->calc_ecc_bytes(step_size, strength); + if (WARN_ON_ONCE(ecc_bytes < 0)) + continue; + ecc_bytes_total = ecc_bytes * steps; + + if (ecc_bytes_total > avail_oobsize || + strength * steps < req_corr) + continue; + + /* + * We assume the best is to meet the chip's requrement + * with the least number of ECC bytes. + */ + if (ecc_bytes_total < best_ecc_bytes_total) { + best_ecc_bytes_total = ecc_bytes_total; + best_step = step_size; + best_strength = strength; + best_ecc_bytes = ecc_bytes; + } + } + } + + if (best_ecc_bytes_total == INT_MAX) + return -ENOTSUPP; + + chip->ecc.size = best_step; + chip->ecc.strength = best_strength; + chip->ecc.bytes = best_ecc_bytes; + + return 0; +} +EXPORT_SYMBOL_GPL(nand_match_ecc_req); + +/** + * nand_maximize_ecc - choose the max ECC strength available + * @mtd: mtd info structure + * @chip: nand chip info structure + * @caps: ECC engine caps info structure + * + * Choose the max ECC strength that is supported on the controller, and can fit + * within the chip's OOB. On success, the chosen ECC settings are set. + */ +int nand_maximize_ecc(struct mtd_info *mtd, struct nand_chip *chip, + const struct nand_ecc_caps *caps) +{ + const struct nand_ecc_step_info *stepinfo; + int avail_oobsize = mtd->oobsize - caps->oob_reserve_bytes; + int step_size, strength, steps, ecc_bytes, corr; + int best_corr = 0; + int best_step = 0; + int best_strength, best_ecc_bytes; + int i, j; + + if (WARN_ON(avail_oobsize < 0)) + return -EINVAL; + + for (i = 0; i < caps->nstepinfos; i++) { + stepinfo = &caps->stepinfos[i]; + step_size = stepinfo->stepsize; + + + /* If chip->ecc.size is already set, respect it */ + if (chip->ecc.size && step_size != chip->ecc.size) + continue; + + for (j = 0; j < stepinfo->nstrengths; j++) { + strength = stepinfo->strengths[j]; + + if (mtd->writesize % step_size) + continue; + + steps = mtd->writesize / step_size; + + ecc_bytes = caps->calc_ecc_bytes(step_size, strength); + if (WARN_ON_ONCE(ecc_bytes < 0)) + continue; + + if (ecc_bytes * steps > avail_oobsize) + continue; + + corr = strength * steps; + + /* + * If the number of correctable bits is the same, + * bigger step_size has more reliability. + */ + if (corr > best_corr || + (corr == best_corr && step_size > best_step)) { + best_corr = corr; + best_step = step_size; + best_strength = strength; + best_ecc_bytes = ecc_bytes; + } + } + } + + if (!best_corr) + return -ENOTSUPP; + + chip->ecc.size = best_step; + chip->ecc.strength = best_strength; + chip->ecc.bytes = best_ecc_bytes; + + return 0; +} +EXPORT_SYMBOL_GPL(nand_maximize_ecc); + /* * Check if the chip configuration meet the datasheet requirements. diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 8f67b15..97ccb76 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -477,6 +477,32 @@ static inline void nand_hw_control_init(struct nand_hw_control *nfc) } /** + * struct nand_ecc_step_info - ECC step information of ECC engine + * @stepsize: data bytes per ECC step + * @strengths: array of supported strengths + * @nstrengths: number of supported strengths + */ +struct nand_ecc_step_info { + int stepsize; + const int *strengths; + int nstrengths; +}; + +/** + * struct nand_ecc_caps - capability of ECC engine + * @stepinfos: array of ECC step information + * @nstepinfos: number of ECC step information + * @calc_ecc_bytes: driver's hook to calculate ECC bytes per step + * @oob_reserve_bytes: number of bytes in OOB that must be reserved + */ +struct nand_ecc_caps { + const struct nand_ecc_step_info *stepinfos; + int nstepinfos; + int (*calc_ecc_bytes)(int step_size, int strength); + int oob_reserve_bytes; +}; + +/** * struct nand_ecc_ctrl - Control structure for ECC * @mode: ECC mode * @algo: ECC algorithm @@ -1244,6 +1270,15 @@ int nand_check_erased_ecc_chunk(void *data, int datalen, void *extraoob, int extraooblen, int threshold); +int nand_check_ecc_caps(struct mtd_info *mtd, struct nand_chip *chip, + const struct nand_ecc_caps *caps); + +int nand_match_ecc_req(struct mtd_info *mtd, struct nand_chip *chip, + const struct nand_ecc_caps *caps); + +int nand_maximize_ecc(struct mtd_info *mtd, struct nand_chip *chip, + const struct nand_ecc_caps *caps); + /* Default write_oob implementation */ int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page);