From patchwork Mon Mar 4 22:28:31 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 10838729 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B33C1922 for ; Mon, 4 Mar 2019 22:31:58 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9B8CE2BB2D for ; Mon, 4 Mar 2019 22:31:58 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 98E942BB88; Mon, 4 Mar 2019 22:31:58 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id BBE842BB2D for ; Mon, 4 Mar 2019 22:31:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=TlmhbWM566jK8NyJMOGjzdFaDiXiXDQtaMmGajo6LV8=; b=hNFGjJ6Oj+bWMX ilQj3NSKCsTXsi6fAMPDJt0BypnGb0mMZ9yhIRRlXqzRMC9JyMp2gWlyGBBPsEC6fz/GWMuMjfr+w LlFDeL02GZkdciYaBEHsNzjlEoPGZleGEBhwgHyIXVWgaEmOEQWg40zmZ/pB5top+aY6Z9O49DITX xEo67OkSiy994aHW2uaNOZGIwrNuhkpOgbxDPbXwkUO1eUgoDmzsWSyu22rwVFRYrUVTb/GCYQ/HF eXleSVXWRZbC3+O6olK+NqGHroyMCjVK+LCmJfdtCcldkfAj8sSAcwwEckxeyxSTB9XaF/hByPrM2 xcViA2aubTHMlwGq6k5Q==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1h0w7m-0000d8-78; Mon, 04 Mar 2019 22:31:54 +0000 Received: from relay1-d.mail.gandi.net ([217.70.183.193]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1h0w72-00084m-9L; Mon, 04 Mar 2019 22:31:51 +0000 X-Originating-IP: 90.5.42.199 Received: from localhost.localdomain (atoulouse-657-1-1088-199.w90-5.abo.wanadoo.fr [90.5.42.199]) (Authenticated sender: miquel.raynal@bootlin.com) by relay1-d.mail.gandi.net (Postfix) with ESMTPSA id DC425240007; Mon, 4 Mar 2019 22:31:02 +0000 (UTC) From: Miquel Raynal To: Boris Brezillon , Richard Weinberger , David Woodhouse , Brian Norris , Marek Vasut , Tudor Ambarus Subject: [PATCH v2 26/36] mtd: nand: ecc: Create the software Hamming engine instance Date: Mon, 4 Mar 2019 23:28:31 +0100 Message-Id: <20190304222841.13899-27-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190304222841.13899-1-miquel.raynal@bootlin.com> References: <20190304222841.13899-1-miquel.raynal@bootlin.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190304_143109_178910_1D4FF15E X-CRM114-Status: GOOD ( 22.71 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Vignesh R , Tudor Ambarus , Julien Su , Schrempf Frieder , Paul Cercueil , linux-mtd@lists.infradead.org, Thomas Petazzoni , Miquel Raynal , Mason Yang , linux-arm-kernel@lists.infradead.org Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP Let's continue introducing the generic ECC engine abstraction in the NAND subsystem by instantiating a second ECC engine: the software Hamming one. Signed-off-by: Miquel Raynal --- drivers/mtd/nand/ecc/sw-hamming.c | 208 ++++++++++++++++++++++++ drivers/mtd/nand/raw/nand_base.c | 25 +-- include/linux/mtd/nand-ecc-sw-hamming.h | 15 ++ 3 files changed, 231 insertions(+), 17 deletions(-) diff --git a/drivers/mtd/nand/ecc/sw-hamming.c b/drivers/mtd/nand/ecc/sw-hamming.c index 816e5ea816c2..c58dfddc0ff3 100644 --- a/drivers/mtd/nand/ecc/sw-hamming.c +++ b/drivers/mtd/nand/ecc/sw-hamming.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -464,6 +465,213 @@ int nand_ecc_sw_hamming_correct(struct nand_device *nand, unsigned char *buf, } EXPORT_SYMBOL(nand_ecc_sw_hamming_correct); +int nand_ecc_sw_hamming_init_ctx(struct nand_device *nand) +{ + struct nand_ecc_conf *conf = &nand->ecc.ctx.conf; + struct nand_ecc_sw_hamming_conf *engine_conf; + struct mtd_info *mtd = nanddev_to_mtd(nand); + + if (!mtd->ooblayout) { + switch (mtd->oobsize) { + case 8: + case 16: + mtd_set_ooblayout(mtd, &nand_ooblayout_sp_ops); + break; + case 64: + case 128: + mtd_set_ooblayout(mtd, &nand_ooblayout_lp_hamming_ops); + break; + default: + return -ENOTSUPP; + } + } + + conf->provider = NAND_ECC_SOFT; + conf->algo = NAND_ECC_HAMMING; + conf->step_size = nand->ecc.user_conf.step_size; + conf->strength = 1; + + /* Use the strongest configuration by default */ + if (conf->step_size != 256 && conf->step_size != 512) + conf->step_size = 256; + + engine_conf = kzalloc(sizeof(*engine_conf), GFP_KERNEL); + if (!engine_conf) + return -ENOMEM; + + engine_conf->code_size = 3; + engine_conf->nsteps = mtd->writesize / conf->step_size; + engine_conf->calc_buf = kzalloc(sizeof(mtd->oobsize), GFP_KERNEL); + engine_conf->code_buf = kzalloc(sizeof(mtd->oobsize), GFP_KERNEL); + if (!engine_conf->calc_buf || !engine_conf->code_buf) { + kfree(engine_conf); + return -ENOMEM; + } + + nand->ecc.ctx.priv = engine_conf; + nand->ecc.ctx.total = engine_conf->nsteps * engine_conf->code_size; + + return 0; +} +EXPORT_SYMBOL(nand_ecc_sw_hamming_init_ctx); + +void nand_ecc_sw_hamming_cleanup_ctx(struct nand_device *nand) +{ + struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv; + + if (engine_conf) { + kfree(engine_conf->calc_buf); + kfree(engine_conf->code_buf); + } + + kfree(engine_conf); +} +EXPORT_SYMBOL(nand_ecc_sw_hamming_cleanup_ctx); + +static int nand_ecc_sw_hamming_prepare_io_req(struct nand_device *nand, + struct nand_page_io_req *req, + void *oobbuf) +{ + struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv; + struct mtd_info *mtd = nanddev_to_mtd(nand); + int eccsize = nand->ecc.ctx.conf.step_size; + int eccbytes = engine_conf->code_size; + int eccsteps = engine_conf->nsteps; + int total = nand->ecc.ctx.total; + u8 *ecccalc = engine_conf->calc_buf; + const u8 *data = req->databuf.out; + int i, ret; + + /* Ensure the OOB buffer is empty before using it */ + if (req->oobbuf.in) + memset(req->oobbuf.in, 0xff, nanddev_per_page_oobsize(nand)); + + if (req->mode == MTD_OPS_RAW) + return 0; + + /* This engine does not provide BBM/free OOB bytes protection */ + if (!req->datalen) + return 0; + + /* + * Ensure OOB area is fully read/written otherwise the software + * correction cannot not apply. + */ + engine_conf->reqooblen = req->ooblen; + req->ooblen = nanddev_per_page_oobsize(nand); + + /* No preparation for page read */ + if (req->type == NAND_PAGE_READ) + return 0; + + /* Preparation for page write: derive the ECC bytes and place them */ + for (i = 0; eccsteps; eccsteps--, i += eccbytes, data += eccsize) + nand_ecc_sw_hamming_calculate(nand, data, &ecccalc[i]); + + ret = mtd_ooblayout_set_eccbytes(mtd, ecccalc, oobbuf, 0, total); + + /* Also place user data OOB bytes in the free area, if any */ + if (engine_conf->reqooblen) { + if (req->mode == MTD_OPS_AUTO_OOB) + mtd_ooblayout_set_databytes(mtd, req->oobbuf.out, + oobbuf, + req->ooboffs, + req->ooblen); + else + memcpy(oobbuf + req->ooboffs, req->oobbuf.out, + req->ooblen); + } + + return ret; +} + +static int nand_ecc_sw_hamming_finish_io_req(struct nand_device *nand, + struct nand_page_io_req *req, + void *oobbuf) +{ + struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv; + struct mtd_info *mtd = nanddev_to_mtd(nand); + int eccsize = nand->ecc.ctx.conf.step_size; + int total = nand->ecc.ctx.total; + int eccbytes = engine_conf->code_size; + int eccsteps = engine_conf->nsteps; + u8 *ecccalc = engine_conf->calc_buf; + u8 *ecccode = engine_conf->code_buf; + unsigned int max_bitflips = 0; + u8 *data = req->databuf.in; + int i, ret; + + if (req->mode == MTD_OPS_RAW) + return 0; + + /* This engine does not provide BBM/free OOB bytes protection */ + if (!req->datalen) + return 0; + + /* Don't mess up with the upper layer: restore the request OOB length */ + req->ooblen = engine_conf->reqooblen; + + /* Nothing more to do for page write */ + if (req->type == NAND_PAGE_WRITE) + return 0; + + /* Finish a page read: retrieve the (raw) ECC bytes*/ + ret = mtd_ooblayout_get_eccbytes(mtd, ecccode, oobbuf, 0, total); + if (ret) + return ret; + + /* Calculate the ECC bytes */ + for (i = 0; eccsteps; eccsteps--, i += eccbytes, data += eccsize) + nand_ecc_sw_hamming_calculate(nand, data, &ecccalc[i]); + + eccsteps = engine_conf->nsteps; + + /* Finish a page read: compare and correct */ + for (eccsteps = engine_conf->nsteps, i = 0, data = req->databuf.in; + eccsteps; + eccsteps--, i += eccbytes, data += eccsize) { + int stat = nand_ecc_sw_hamming_correct(nand, data, + &ecccode[i], + &ecccalc[i]); + if (stat < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } + } + + /* Format the OOB buffer that will be returned to the user */ + if (req->ooblen) { + if (req->mode == MTD_OPS_AUTO_OOB) + mtd_ooblayout_get_databytes(mtd, oobbuf, + req->oobbuf.in, + req->ooboffs, req->ooblen); + else + memcpy(req->oobbuf.in, oobbuf + req->ooboffs, + req->ooblen); + } + + return max_bitflips; +} + +static struct nand_ecc_engine_ops nand_ecc_sw_hamming_engine_ops = { + .init_ctx = nand_ecc_sw_hamming_init_ctx, + .cleanup_ctx = nand_ecc_sw_hamming_cleanup_ctx, + .prepare_io_req = nand_ecc_sw_hamming_prepare_io_req, + .finish_io_req = nand_ecc_sw_hamming_finish_io_req, +}; + +static struct nand_ecc_engine nand_ecc_sw_hamming_engine = { + .ops = &nand_ecc_sw_hamming_engine_ops, +}; + +struct nand_ecc_engine *nand_ecc_sw_hamming_get_engine(void) +{ + return &nand_ecc_sw_hamming_engine; +} +EXPORT_SYMBOL(nand_ecc_sw_hamming_get_engine); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Frans Meulenbroeks "); MODULE_DESCRIPTION("NAND software Hamming ECC support"); diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 8b910c818755..3149af72f519 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -4849,34 +4849,24 @@ static void nand_scan_ident_cleanup(struct nand_chip *chip) int rawnand_sw_hamming_init(struct nand_chip *chip) { - struct mtd_info *mtd = nand_to_mtd(chip); struct nand_ecc_sw_hamming_conf *engine_conf; struct nand_device *base = &chip->base; + int ret; base->ecc.user_conf.provider = NAND_ECC_SOFT; base->ecc.user_conf.algo = NAND_ECC_HAMMING; base->ecc.user_conf.strength = chip->ecc.strength; base->ecc.user_conf.step_size = chip->ecc.size; - if (base->ecc.user_conf.strength != 1 || - (base->ecc.user_conf.step_size != 256 && - base->ecc.user_conf.step_size != 512)) { - pr_err("%s: unsupported strength or step size\n", __func__); - return -EINVAL; - } + ret = nand_ecc_sw_hamming_init_ctx(base); + if (ret) + return ret; - engine_conf = kzalloc(sizeof(*engine_conf), GFP_KERNEL); - if (!engine_conf) - return -ENOMEM; - - engine_conf->code_size = 3; - engine_conf->nsteps = mtd->writesize / base->ecc.user_conf.step_size; + engine_conf = base->ecc.ctx.priv; if (chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER) engine_conf->sm_order = true; - base->ecc.ctx.priv = engine_conf; - chip->ecc.size = base->ecc.ctx.conf.step_size; chip->ecc.strength = base->ecc.ctx.conf.strength; chip->ecc.total = base->ecc.ctx.total; @@ -4912,7 +4902,7 @@ void rawnand_sw_hamming_cleanup(struct nand_chip *chip) { struct nand_device *base = &chip->base; - kfree(base->ecc.ctx.priv); + nand_ecc_sw_hamming_cleanup_ctx(base); } EXPORT_SYMBOL(rawnand_sw_hamming_cleanup); @@ -5376,7 +5366,8 @@ static int nand_scan_tail(struct nand_chip *chip) * If no default placement scheme is given, select an appropriate one. */ if (!mtd->ooblayout && - !(ecc->mode == NAND_ECC_SOFT && ecc->algo == NAND_ECC_BCH)) { + !(ecc->mode == NAND_ECC_SOFT && ecc->algo == NAND_ECC_BCH) && + !(ecc->mode == NAND_ECC_SOFT && ecc->algo == NAND_ECC_HAMMING)) { switch (mtd->oobsize) { case 8: case 16: diff --git a/include/linux/mtd/nand-ecc-sw-hamming.h b/include/linux/mtd/nand-ecc-sw-hamming.h index 4d41d6ec9fe7..9ea096aaf230 100644 --- a/include/linux/mtd/nand-ecc-sw-hamming.h +++ b/include/linux/mtd/nand-ecc-sw-hamming.h @@ -32,6 +32,8 @@ struct nand_ecc_sw_hamming_conf { #if IS_REACHABLE(CONFIG_MTD_NAND_ECC_SW_HAMMING) +int nand_ecc_sw_hamming_init_ctx(struct nand_device *nand); +void nand_ecc_sw_hamming_cleanup_ctx(struct nand_device *nand); int ecc_sw_hamming_calculate(const unsigned char *buf, unsigned int step_size, unsigned char *code, bool sm_order); int nand_ecc_sw_hamming_calculate(struct nand_device *nand, @@ -43,9 +45,17 @@ int ecc_sw_hamming_correct(unsigned char *buf, unsigned char *read_ecc, int nand_ecc_sw_hamming_correct(struct nand_device *nand, unsigned char *buf, unsigned char *read_ecc, unsigned char *calc_ecc); +struct nand_ecc_engine *nand_ecc_sw_hamming_get_engine(void); #else /* !CONFIG_MTD_NAND_ECC_SW_HAMMING */ +static inline int nand_ecc_sw_hamming_init_ctx(struct nand_device *nand) +{ + return -ENOTSUPP; +} + +static inline void nand_ecc_sw_hamming_cleanup_ctx(struct nand_device *nand) {} + static inline int ecc_sw_hamming_calculate(const unsigned char *buf, unsigned int step_size, unsigned char *code, bool sm_order) @@ -76,6 +86,11 @@ static inline int nand_ecc_sw_hamming_correct(struct nand_device *nand, return -ENOTSUPP; } +static inline struct nand_ecc_engine *nand_ecc_sw_hamming_get_engine(void) +{ + return NULL; +} + #endif /* CONFIG_MTD_NAND_ECC_SW_HAMMING */ #endif /* __MTD_NAND_ECC_SW_HAMMING_H__ */