From patchwork Wed Jul 17 19:52:06 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: pekon gupta X-Patchwork-Id: 259742 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from casper.infradead.org (casper.infradead.org [IPv6:2001:770:15f::2]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id DB8F42C00B8 for ; Thu, 18 Jul 2013 05:55:23 +1000 (EST) Received: from merlin.infradead.org ([2001:4978:20e::2]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1UzXoI-0005O2-GO; Wed, 17 Jul 2013 19:54:50 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1UzXo8-0006WR-2o; Wed, 17 Jul 2013 19:54:40 +0000 Received: from comal.ext.ti.com ([198.47.26.152]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1UzXny-0006T7-0b for linux-mtd@lists.infradead.org; Wed, 17 Jul 2013 19:54:32 +0000 Received: from dlelxv90.itg.ti.com ([172.17.2.17]) by comal.ext.ti.com (8.13.7/8.13.7) with ESMTP id r6HJs8fh022585; Wed, 17 Jul 2013 14:54:08 -0500 Received: from DFLE72.ent.ti.com (dfle72.ent.ti.com [128.247.5.109]) by dlelxv90.itg.ti.com (8.14.3/8.13.8) with ESMTP id r6HJs83k022050; Wed, 17 Jul 2013 14:54:08 -0500 Received: from dlelxv22.itg.ti.com (172.17.1.197) by DFLE72.ent.ti.com (128.247.5.109) with Microsoft SMTP Server id 14.2.342.3; Wed, 17 Jul 2013 14:54:08 -0500 Received: from psplinux064.india.ti.com (psplinux064.india.ti.com [10.24.100.118]) by dlelxv22.itg.ti.com (8.13.8/8.13.8) with ESMTP id r6HJrrM9005006; Wed, 17 Jul 2013 14:54:05 -0500 From: Pekon Gupta To: , Subject: [PATCH v1 4/4] mtd: nand: omap: add support for BCH16_ECC - NAND driver updates Date: Thu, 18 Jul 2013 01:22:06 +0530 Message-ID: <1374090726-9393-5-git-send-email-pekon@ti.com> X-Mailer: git-send-email 1.8.1 In-Reply-To: <1374090726-9393-1-git-send-email-pekon@ti.com> References: <1374090726-9393-1-git-send-email-pekon@ti.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130717_155430_223257_9568A4C6 X-CRM114-Status: GOOD ( 14.85 ) X-Spam-Score: -7.3 (-------) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-7.3 points) pts rule name description ---- ---------------------- -------------------------------------------------- -5.0 RCVD_IN_DNSWL_HI RBL: Sender listed at http://www.dnswl.org/, high trust [198.47.26.152 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record -0.4 RP_MATCHES_RCVD Envelope sender domain matches handover relay domain -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: dedekind1@gmail.com, tony@atomide.com, devicetree-discuss@lists.ozlabs.org, avinashphilipk@gmail.com, balbi@ti.com, Pekon Gupta , benoit.cousson@linaro.org X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-mtd" Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org With increase in NAND flash densities occurence of bit-flips has increased. Thus stronger ECC schemes are required for detecting and correcting multiple simultaneous bit-flips in same NAND page. But stronger ECC schemes have large ECC syndrome which require more space in OOB/Spare. This patch add support for BCH16_ECC: (a) BCH16_ECC can correct 16 bit-flips per 512Bytes of data. (b) BCH16_ECC generates 26-bytes of ECC syndrome / 512B. Due to (b) this scheme can only be used with NAND devices which have enough OOB to satisfy following equation: OOBsize per page >= 26 * (page-size / 512) Signed-off-by: Pekon Gupta --- drivers/mtd/nand/omap2.c | 104 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 96 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index c2c3c6bb..a0cf487 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -130,9 +130,13 @@ #define BADBLOCK_MARKER_LENGTH 0x2 #ifdef CONFIG_MTD_NAND_OMAP_BCH -static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc, - 0xac, 0x6b, 0xff, 0x99, 0x7b}; -static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10}; +static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10}; +static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc, + 0xac, 0x6b, 0xff, 0x99, 0x7b}; +static u_char bch16_vector[] = {0xf5, 0x24, 0x1c, 0xd0, 0x61, 0xb3, 0xf1, 0x55, + 0x2e, 0x2c, 0x86, 0xa3, 0xed, 0x36, 0x1b, 0x78, + 0x48, 0x76, 0xa9, 0x3b, 0x97, 0xd1, 0x7a, 0x93, + 0x07, 0x0e}; #endif static u8 bch4_polynomial[] = {0x28, 0x13, 0xcc, 0x39, 0x96, 0xac, 0x7f}; static u8 bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2, @@ -1066,6 +1070,19 @@ static void omap_enable_hwecc(struct mtd_info *mtd, int mode) eccsize1 = 28; /* OOB bits in nibbles per sector */ } break; + case OMAP_ECC_BCH16_CODE_HW: + ecc_algo = 0x1; + bch_type = 0x2; + if (mode == GPMC_ECC_READ) { + bch_wrapmode = 0x01; + eccsize0 = 52; /* ECC bits in nibbles per sector */ + eccsize1 = 0; /* non-ECC bits in nibbles per sector */ + } else if (mode == GPMC_ECC_WRITE) { + bch_wrapmode = 0x01; + eccsize0 = 0; /* extra bits in nibbles per sector */ + eccsize1 = 52; /* OOB bits in nibbles per sector */ + } + break; default: pr_err("selected ECC scheme not supported or not enabled\n"); } @@ -1159,6 +1176,41 @@ static int omap_calculate_ecc_bch(struct mtd_info *mtd, const u_char *dat, *(ecc_ptr++) = ((val >> 8) & 0xFF); *(ecc_ptr++) = ((val >> 0) & 0xFF); break; + case OMAP_ECC_BCH16_CODE_HW: + val = readl(gpmc_regs->gpmc_bch_result6[i]); + *(ecc_ptr++) = ((val >> 8) & 0xFF); + *(ecc_ptr++) = ((val >> 0) & 0xFF); + val = readl(gpmc_regs->gpmc_bch_result5[i]); + *(ecc_ptr++) = ((val >> 24) & 0xFF); + *(ecc_ptr++) = ((val >> 16) & 0xFF); + *(ecc_ptr++) = ((val >> 8) & 0xFF); + *(ecc_ptr++) = ((val >> 0) & 0xFF); + val = readl(gpmc_regs->gpmc_bch_result4[i]); + *(ecc_ptr++) = ((val >> 24) & 0xFF); + *(ecc_ptr++) = ((val >> 16) & 0xFF); + *(ecc_ptr++) = ((val >> 8) & 0xFF); + *(ecc_ptr++) = ((val >> 0) & 0xFF); + val = readl(gpmc_regs->gpmc_bch_result3[i]); + *(ecc_ptr++) = ((val >> 24) & 0xFF); + *(ecc_ptr++) = ((val >> 16) & 0xFF); + *(ecc_ptr++) = ((val >> 8) & 0xFF); + *(ecc_ptr++) = ((val >> 0) & 0xFF); + val = readl(gpmc_regs->gpmc_bch_result2[i]); + *(ecc_ptr++) = ((val >> 24) & 0xFF); + *(ecc_ptr++) = ((val >> 16) & 0xFF); + *(ecc_ptr++) = ((val >> 8) & 0xFF); + *(ecc_ptr++) = ((val >> 0) & 0xFF); + val = readl(gpmc_regs->gpmc_bch_result1[i]); + *(ecc_ptr++) = ((val >> 24) & 0xFF); + *(ecc_ptr++) = ((val >> 16) & 0xFF); + *(ecc_ptr++) = ((val >> 8) & 0xFF); + *(ecc_ptr++) = ((val >> 0) & 0xFF); + val = readl(gpmc_regs->gpmc_bch_result0[i]); + *(ecc_ptr++) = ((val >> 24) & 0xFF); + *(ecc_ptr++) = ((val >> 16) & 0xFF); + *(ecc_ptr++) = ((val >> 8) & 0xFF); + *(ecc_ptr++) = ((val >> 0) & 0xFF); + break; default: return -EINVAL; } @@ -1184,6 +1236,8 @@ static int omap_calculate_ecc_bch(struct mtd_info *mtd, const u_char *dat, case OMAP_ECC_BCH8_CODE_HW: *(ecc_ptr++) = 0x00; break; + case OMAP_ECC_BCH16_CODE_HW: + break; } /* update pointer to next sector */ ecc_calc += eccbytes; @@ -1225,7 +1279,6 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, /* Initialize elm error vector to zero */ memset(err_vec, 0, sizeof(err_vec)); - for (i = 0; i < eccsteps ; i++) { flag_read_ecc = 0; ecc = calc_ecc + (i * eccbytes); @@ -1239,12 +1292,16 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, /* check if its a erased-page */ if (flag_read_ecc) { switch (ecc_opt) { + case OMAP_ECC_BCH4_CODE_HW: + if (memcmp(ecc, bch4_vector, eccbytes)) + err_vec[i].error_reported = true; + break; case OMAP_ECC_BCH8_CODE_HW: if (memcmp(ecc, bch8_vector, eccbytes)) err_vec[i].error_reported = true; break; - case OMAP_ECC_BCH4_CODE_HW: - if (memcmp(ecc, bch4_vector, eccbytes)) + case OMAP_ECC_BCH16_CODE_HW: + if (memcmp(ecc, bch16_vector, eccbytes)) err_vec[i].error_reported = true; break; default: @@ -1282,6 +1339,10 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, (eccbytes - 1); pos = err_vec[i].error_loc[j]; break; + case OMAP_ECC_BCH16_CODE_HW: + error_max = SECTOR_BYTES + eccbytes; + pos = err_vec[i].error_loc[j]; + break; default: return -EINVAL; } @@ -1757,6 +1818,33 @@ static int omap_nand_probe(struct platform_device *pdev) omap_oobinfo.eccbytes; goto custom_ecc_layout; #endif +#ifdef CONFIG_MTD_NAND_OMAP_BCH + case OMAP_ECC_BCH16_CODE_HW: + pr_info("using OMAP_ECC_BCH16_CODE_HW ECC scheme\n"); + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.size = 512; + /* 14th bit is kept reserved for ROM-code compatibility */ + chip->ecc.bytes = 26; + chip->ecc.strength = 16; + chip->ecc.hwctl = omap_enable_hwecc; + chip->ecc.correct = omap_elm_correct_data; + chip->ecc.calculate = omap_calculate_ecc_bch; + chip->ecc.read_page = omap_read_page_bch; + chip->ecc.write_page = omap_write_page_bch; + /* ELM H/W engine is used for locating errors */ + if (is_elm_present(info, BCH16_ECC) < 0) { + pr_err("ELM module not detected, required for ECC\n"); + err = -EINVAL; + goto out_release_mem_region; + } + /* define custom ECC layout */ + omap_oobinfo.eccbytes = chip->ecc.bytes * + (mtd->writesize / chip->ecc.size); + omap_oobinfo.eccpos[0] = BADBLOCK_MARKER_LENGTH; + omap_oobinfo.oobfree->offset = omap_oobinfo.eccpos[0] + + omap_oobinfo.eccbytes; + goto custom_ecc_layout; +#endif default: pr_err("%s: selected ECC scheme not supported or not enabled", DRIVER_NAME); @@ -1777,8 +1865,8 @@ custom_ecc_layout: /* check if NAND OOBSIZE meets ECC scheme requirement */ if (mtd->oobsize < (omap_oobinfo.eccbytes + BADBLOCK_MARKER_LENGTH)) { - pr_err("not enough OOB bytes required = %d, available=%d\n", - mtd->oobsize, omap_oobinfo.eccbytes); + pr_err("%s: not enough OOB bytes available=%d, required=%d\n", + DRIVER_NAME, mtd->oobsize, omap_oobinfo.eccbytes); err = -EINVAL; goto out_release_mem_region; }