From patchwork Wed Mar 30 16:14:39 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Boris Brezillon X-Patchwork-Id: 603474 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2001:1868:205::9]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3qZtCb5wJBz9s5J for ; Thu, 31 Mar 2016 03:20:11 +1100 (AEDT) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1alIpY-0001xb-NK; Wed, 30 Mar 2016 16:18:52 +0000 Received: from down.free-electrons.com ([37.187.137.238] helo=mail.free-electrons.com) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1alImm-000834-QL; Wed, 30 Mar 2016 16:16:08 +0000 Received: by mail.free-electrons.com (Postfix, from userid 110) id 465561BBC; Wed, 30 Mar 2016 18:15:43 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on mail.free-electrons.com X-Spam-Level: X-Spam-Status: No, score=-1.0 required=5.0 tests=ALL_TRUSTED,SHORTCIRCUIT, URIBL_BLOCKED shortcircuit=ham autolearn=disabled version=3.4.0 Received: from localhost.localdomain (LMontsouris-657-1-184-87.w90-63.abo.wanadoo.fr [90.63.216.87]) by mail.free-electrons.com (Postfix) with ESMTPSA id 171BD183D; Wed, 30 Mar 2016 18:15:26 +0200 (CEST) From: Boris Brezillon To: David Woodhouse , Brian Norris , linux-mtd@lists.infradead.org, Boris Brezillon , Richard Weinberger Subject: [PATCH v5 24/50] mtd: nand: brcm: switch to mtd_ooblayout_ops Date: Wed, 30 Mar 2016 18:14:39 +0200 Message-Id: <1459354505-32551-25-git-send-email-boris.brezillon@free-electrons.com> X-Mailer: git-send-email 2.5.0 In-Reply-To: <1459354505-32551-1-git-send-email-boris.brezillon@free-electrons.com> References: <1459354505-32551-1-git-send-email-boris.brezillon@free-electrons.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20160330_091601_397903_7DFAF79B X-CRM114-Status: GOOD ( 23.71 ) X-Spam-Score: -2.9 (--) X-Spam-Report: SpamAssassin version 3.4.0 on bombadil.infradead.org summary: Content analysis details: (-2.9 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [37.187.137.238 listed in list.dnswl.org] -1.0 RP_MATCHES_RCVD Envelope sender domain matches handover relay domain -0.0 SPF_PASS SPF: sender matches SPF record -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-mips@linux-mips.org, Krzysztof Kozlowski , Harvey Hunt , Nicolas Ferre , Stefan Agner , linux-sunxi@googlegroups.com, Alexandre Belloni , punnaiah choudary kalluri , Robert Jarzmik , devel@driverdev.osuosl.org, Archit Taneja , linux-samsung-soc@vger.kernel.org, Kamal Dasu , Josh Wu , Chen-Yu Tsai , Kukjin Kim , bcm-kernel-feedback-list@broadcom.com, Ezequiel Garcia , Huang Shijie , Jean-Christophe Plagniol-Villard , Haojian Zhuang , Han Xu , linux-arm-kernel@lists.infradead.org, Priit Laes , Greg Kroah-Hartman , linux-kernel@vger.kernel.org, Ralf Baechle , Wenyou Yang , Kyungmin Park , linux-api@vger.kernel.org, Maxime Ripard , Daniel Mack MIME-Version: 1.0 Sender: "linux-mtd" Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/brcmnand/brcmnand.c | 258 +++++++++++++++++++++-------------- 1 file changed, 157 insertions(+), 101 deletions(-) diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c index e052839..4c3c0a9 100644 --- a/drivers/mtd/nand/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/brcmnand/brcmnand.c @@ -781,127 +781,183 @@ static inline bool is_hamming_ecc(struct brcmnand_cfg *cfg) } /* - * Returns a nand_ecclayout strucutre for the given layout/configuration. - * Returns NULL on failure. + * Set mtd->ooblayout to the appropriate mtd_ooblayout_ops given + * the layout/configuration. + * Returns -ERRCODE on failure. */ -static struct nand_ecclayout *brcmnand_create_layout(int ecc_level, - struct brcmnand_host *host) +static int brcmnand_hamming_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) { + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); struct brcmnand_cfg *cfg = &host->hwcfg; - int i, j; - struct nand_ecclayout *layout; - int req; - int sectors; - int sas; - int idx1, idx2; - - layout = devm_kzalloc(&host->pdev->dev, sizeof(*layout), GFP_KERNEL); - if (!layout) - return NULL; - - sectors = cfg->page_size / (512 << cfg->sector_size_1k); - sas = cfg->spare_area_size << cfg->sector_size_1k; - - /* Hamming */ - if (is_hamming_ecc(cfg)) { - for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) { - /* First sector of each page may have BBI */ - if (i == 0) { - layout->oobfree[idx2].offset = i * sas + 1; - /* Small-page NAND use byte 6 for BBI */ - if (cfg->page_size == 512) - layout->oobfree[idx2].offset--; - layout->oobfree[idx2].length = 5; - } else { - layout->oobfree[idx2].offset = i * sas; - layout->oobfree[idx2].length = 6; - } - idx2++; - layout->eccpos[idx1++] = i * sas + 6; - layout->eccpos[idx1++] = i * sas + 7; - layout->eccpos[idx1++] = i * sas + 8; - layout->oobfree[idx2].offset = i * sas + 9; - layout->oobfree[idx2].length = 7; - idx2++; - /* Leave zero-terminated entry for OOBFREE */ - if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE || - idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1) - break; - } + int sas = cfg->spare_area_size << cfg->sector_size_1k; + int sectors = cfg->page_size / (512 << cfg->sector_size_1k); - return layout; - } + if (section >= sectors) + return -ERANGE; - /* - * CONTROLLER_VERSION: - * < v5.0: ECC_REQ = ceil(BCH_T * 13/8) - * >= v5.0: ECC_REQ = ceil(BCH_T * 14/8) - * But we will just be conservative. - */ - req = DIV_ROUND_UP(ecc_level * 14, 8); - if (req >= sas) { - dev_err(&host->pdev->dev, - "error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n", - req, sas); - return NULL; - } + oobregion->offset = (section * sas) + 6; + oobregion->length = 3; + + return 0; +} + +static int brcmnand_hamming_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_cfg *cfg = &host->hwcfg; + int sas = cfg->spare_area_size << cfg->sector_size_1k; + int sectors = cfg->page_size / (512 << cfg->sector_size_1k); + + if (section >= sectors * 2) + return -ERANGE; - layout->eccbytes = req * sectors; - for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) { - for (j = sas - req; j < sas && idx1 < - MTD_MAX_ECCPOS_ENTRIES_LARGE; j++, idx1++) - layout->eccpos[idx1] = i * sas + j; + oobregion->offset = (section / 2) * sas; + + if (section & 1) { + oobregion->offset += 9; + oobregion->length = 7; + } else { + oobregion->length = 6; /* First sector of each page may have BBI */ - if (i == 0) { - if (cfg->page_size == 512 && (sas - req >= 6)) { - /* Small-page NAND use byte 6 for BBI */ - layout->oobfree[idx2].offset = 0; - layout->oobfree[idx2].length = 5; - idx2++; - if (sas - req > 6) { - layout->oobfree[idx2].offset = 6; - layout->oobfree[idx2].length = - sas - req - 6; - idx2++; - } - } else if (sas > req + 1) { - layout->oobfree[idx2].offset = i * sas + 1; - layout->oobfree[idx2].length = sas - req - 1; - idx2++; - } - } else if (sas > req) { - layout->oobfree[idx2].offset = i * sas; - layout->oobfree[idx2].length = sas - req; - idx2++; + if (!section) { + /* + * Small-page NAND use byte 6 for BBI while large-page + * NAND use byte 0. + */ + if (cfg->page_size > 512) + oobregion->offset++; + oobregion->length--; } - /* Leave zero-terminated entry for OOBFREE */ - if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE || - idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1) - break; } - return layout; + return 0; +} + +static const struct mtd_ooblayout_ops brcmnand_hamming_ooblayout_ops = { + .ecc = brcmnand_hamming_ooblayout_ecc, + .free = brcmnand_hamming_ooblayout_free, +}; + +static int brcmnand_bch_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_cfg *cfg = &host->hwcfg; + int sas = cfg->spare_area_size << cfg->sector_size_1k; + int sectors = cfg->page_size / (512 << cfg->sector_size_1k); + + if (section >= sectors) + return -ERANGE; + + oobregion->offset = (section * (sas + 1)) - chip->ecc.bytes; + oobregion->length = chip->ecc.bytes; + + return 0; } -static struct nand_ecclayout *brcmstb_choose_ecc_layout( - struct brcmnand_host *host) +static int brcmnand_bch_ooblayout_free_lp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_cfg *cfg = &host->hwcfg; + int sas = cfg->spare_area_size << cfg->sector_size_1k; + int sectors = cfg->page_size / (512 << cfg->sector_size_1k); + + if (section >= sectors) + return -ERANGE; + + if (sas <= chip->ecc.bytes) + return 0; + + oobregion->offset = section * sas; + oobregion->length = sas - chip->ecc.bytes; + + if (!section) { + oobregion->offset++; + oobregion->length--; + } + + return 0; +} + +static int brcmnand_bch_ooblayout_free_sp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_cfg *cfg = &host->hwcfg; + int sas = cfg->spare_area_size << cfg->sector_size_1k; + + if (section > 1 || sas - chip->ecc.bytes < 6 || + (section && sas - chip->ecc.bytes == 6)) + return -ERANGE; + + if (!section) { + oobregion->offset = 0; + oobregion->length = 5; + } else { + oobregion->offset = 6; + oobregion->length = sas - chip->ecc.bytes - 6; + } + + return 0; +} + +static const struct mtd_ooblayout_ops brcmnand_bch_lp_ooblayout_ops = { + .ecc = brcmnand_bch_ooblayout_ecc, + .free = brcmnand_bch_ooblayout_free_lp, +}; + +static const struct mtd_ooblayout_ops brcmnand_bch_sp_ooblayout_ops = { + .ecc = brcmnand_bch_ooblayout_ecc, + .free = brcmnand_bch_ooblayout_free_sp, +}; + +static int brcmstb_choose_ecc_layout(struct brcmnand_host *host) { - struct nand_ecclayout *layout; struct brcmnand_cfg *p = &host->hwcfg; + struct mtd_info *mtd = nand_to_mtd(&host->chip); + struct nand_ecc_ctrl *ecc = &host->chip.ecc; unsigned int ecc_level = p->ecc_level; + int sas = p->spare_area_size << p->sector_size_1k; + int sectors = p->page_size / (512 << p->sector_size_1k); if (p->sector_size_1k) ecc_level <<= 1; - layout = brcmnand_create_layout(ecc_level, host); - if (!layout) { + if (is_hamming_ecc(p)) { + ecc->bytes = 3 * sectors; + mtd_set_ooblayout(mtd, &brcmnand_hamming_ooblayout_ops); + return 0; + } + + /* + * CONTROLLER_VERSION: + * < v5.0: ECC_REQ = ceil(BCH_T * 13/8) + * >= v5.0: ECC_REQ = ceil(BCH_T * 14/8) + * But we will just be conservative. + */ + ecc->bytes = DIV_ROUND_UP(ecc_level * 14, 8); + if (p->page_size == 512) + mtd_set_ooblayout(mtd, &brcmnand_bch_sp_ooblayout_ops); + else + mtd_set_ooblayout(mtd, &brcmnand_bch_lp_ooblayout_ops); + + if (ecc->bytes >= sas) { dev_err(&host->pdev->dev, - "no proper ecc_layout for this NAND cfg\n"); - return NULL; + "error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n", + ecc->bytes, sas); + return -EINVAL; } - return layout; + return 0; } static void brcmnand_wp(struct mtd_info *mtd, int wp) @@ -2011,9 +2067,9 @@ static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn) /* only use our internal HW threshold */ mtd->bitflip_threshold = 1; - chip->ecc.layout = brcmstb_choose_ecc_layout(host); - if (!chip->ecc.layout) - return -ENXIO; + ret = brcmstb_choose_ecc_layout(host); + if (ret) + return ret; if (nand_scan_tail(mtd)) return -ENXIO;