From patchwork Wed Sep 4 17:51:13 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: William Zhang X-Patchwork-Id: 1157882 X-Patchwork-Delegate: daniel.schwierzeck@googlemail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=quarantine dis=none) header.from=broadcom.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=broadcom.com header.i=@broadcom.com header.b="oSKxzPsY"; dkim-atps=neutral Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 46Nrxy0cJjz9sDQ for ; Thu, 5 Sep 2019 03:52:38 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id AC575C21E08; Wed, 4 Sep 2019 17:52:35 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=T_DKIM_INVALID autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 8099EC21C2F; Wed, 4 Sep 2019 17:52:32 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 5546CC21C2F; Wed, 4 Sep 2019 17:52:31 +0000 (UTC) Received: from rnd-relay.smtp.broadcom.com (rnd-relay.smtp.broadcom.com [192.19.229.170]) by lists.denx.de (Postfix) with ESMTPS id A158CC21C29 for ; Wed, 4 Sep 2019 17:52:30 +0000 (UTC) Received: from mail-irv-17.broadcom.com (mail-irv-17.lvn.broadcom.net [10.75.242.48]) by rnd-relay.smtp.broadcom.com (Postfix) with ESMTP id C349930C166; Wed, 4 Sep 2019 10:52:15 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.10.3 rnd-relay.smtp.broadcom.com C349930C166 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=broadcom.com; s=dkimrelay; t=1567619535; bh=mw5HXIvLkygL/F+CEDTSzssAwOZAlmPrRFC0AU4Bp8Y=; h=From:To:Cc:Subject:Date:From; b=oSKxzPsYOXa1yhELYqOl+9VlyZxutxE+gS5ZKg4wk1O/3dUn12LXvLRDNcgE9YgDZ q559Ev6sGB0d6XCjz80lam+NmN6AGhqUpU90JPwRiY4QKuBla5BGjNl5QFsR3UkHsw aH34Irop1kj396cONLSRcSYXjd9KGYNjMOj59twM= Received: from bcacpedev-irv-2.broadcom.com (bcacpedev-irv-2.lvn.broadcom.net [10.75.135.192]) by mail-irv-17.broadcom.com (Postfix) with ESMTP id 5938C60D7A; Wed, 4 Sep 2019 10:52:24 -0700 (PDT) Received: by bcacpedev-irv-2.broadcom.com (Postfix, from userid 28376) id 539091D209F2; Wed, 4 Sep 2019 10:52:24 -0700 (PDT) From: William Zhang To: philippe.reynes@softathome.com Date: Wed, 4 Sep 2019 10:51:13 -0700 Message-Id: <20190904175114.37018-1-william.zhang@broadcom.com> X-Mailer: git-send-email 2.18.0 Cc: u-boot@lists.denx.de, William Zhang Subject: [U-Boot] [PATCH] drivers: nand: brcmnand: fix nand_chip ecc layout structure X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" The current brcmnand driver is based on 4.18 linux kernel which uses mtd_set_ooblayout to set ecc layout. But nand base code in u-boot is from old kernel which does not use this new API and expect nand_chip.ecc.layout structure to be set. This cause nand_scan_tail function running into a bug check if the device has a different oob size than the default ones. This patch ports the brcmstb_choose_ecc_layout function from kernel 4.6.7 that supports the ecc layout struture and replaces the mtd_set_ooblayout method Signed-off-by: William Zhang Reviewed-by: Philippe Reynes --- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 260 +++++++++-------------- 1 file changed, 104 insertions(+), 156 deletions(-) diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index faa6da42d5..0745929253 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -888,183 +888,131 @@ static inline bool is_hamming_ecc(struct brcmnand_controller *ctrl, } /* - * Set mtd->ooblayout to the appropriate mtd_ooblayout_ops given - * the layout/configuration. - * Returns -ERRCODE on failure. + * Returns a nand_ecclayout strucutre for the given layout/configuration. + * Returns NULL on failure. */ -static int brcmnand_hamming_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) +static struct nand_ecclayout *brcmnand_create_layout(int ecc_level, + struct brcmnand_host *host) { - 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) + 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); + int i, j; + struct nand_ecclayout *layout; + int req; + int sectors; + int sas; + int idx1, idx2; - if (section >= sectors * 2) - return -ERANGE; - - 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 (!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--; +#ifndef __UBOOT__ + layout = devm_kzalloc(&host->pdev->dev, sizeof(*layout), GFP_KERNEL); +#else + layout = devm_kzalloc(host->pdev, sizeof(*layout), GFP_KERNEL); +#endif + 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(host->ctrl, 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; } - } - - 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 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 layout; } - 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; + /* + * 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; + } - if (section > 1 || sas - chip->ecc.bytes < 6 || - (section && sas - chip->ecc.bytes == 6)) - 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; - if (!section) { - oobregion->offset = 0; - oobregion->length = 5; - } else { - oobregion->offset = 6; - oobregion->length = sas - chip->ecc.bytes - 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++; + } + /* Leave zero-terminated entry for OOBFREE */ + if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE || + idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1) + break; } - return 0; + return layout; } -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) +static struct nand_ecclayout *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; - if (is_hamming_ecc(host->ctrl, 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) { + layout = brcmnand_create_layout(ecc_level, host); + if (!layout) { dev_err(&host->pdev->dev, - "error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n", - ecc->bytes, sas); - return -EINVAL; + "no proper ecc_layout for this NAND cfg\n"); + return NULL; } - return 0; + return layout; } static void brcmnand_wp(struct mtd_info *mtd, int wp) @@ -2383,9 +2331,9 @@ static int brcmnand_init_cs(struct brcmnand_host *host, ofnode dn) /* only use our internal HW threshold */ mtd->bitflip_threshold = 1; - ret = brcmstb_choose_ecc_layout(host); - if (ret) - return ret; + chip->ecc.layout = brcmstb_choose_ecc_layout(host); + if (!chip->ecc.layout) + return -ENXIO; ret = nand_scan_tail(mtd); if (ret)