From patchwork Mon Aug 18 07:08:40 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Josh Wu X-Patchwork-Id: 380687 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-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id C6F6F140076 for ; Mon, 18 Aug 2014 17:13:56 +1000 (EST) 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 1XJH6e-0004pM-IH; Mon, 18 Aug 2014 07:11:52 +0000 Received: from nasmtp01.atmel.com ([192.199.1.245] helo=DVREDG01.corp.atmel.com) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1XJH6c-0004lY-QJ for linux-mtd@lists.infradead.org; Mon, 18 Aug 2014 07:11:51 +0000 Received: from apsmtp01.atmel.com (10.168.254.30) by DVREDG01.corp.atmel.com (10.42.103.30) with Microsoft SMTP Server (TLS) id 14.2.347.0; Mon, 18 Aug 2014 01:11:27 -0600 Received: from melon.corp.atmel.com (10.168.254.13) by apsmtp01.corp.atmel.com (10.168.254.30) with Microsoft SMTP Server id 14.2.347.0; Mon, 18 Aug 2014 15:10:38 +0800 From: Josh Wu To: Subject: [PATCH v2] mtd: atmel_nand: make PMECC lookup table and offset property optional Date: Mon, 18 Aug 2014 15:08:40 +0800 Message-ID: <1408345720-5402-1-git-send-email-josh.wu@atmel.com> X-Mailer: git-send-email 1.9.1 MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140818_001150_961786_CED1FFC4 X-CRM114-Status: GOOD ( 22.64 ) X-Spam-Score: -0.7 (/) X-Spam-Report: SpamAssassin version 3.4.0 on bombadil.infradead.org summary: Content analysis details: (-0.7 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RP_MATCHES_RCVD Envelope sender domain matches handover relay domain -0.0 SPF_PASS SPF: sender matches SPF record Cc: mark.rutland@arm.com, devicetree@vger.kernel.org, pawel.moll@arm.com, ijc+devicetree@hellion.org.uk, Josh Wu , robh+dt@kernel.org, galak@codeaurora.org, computersforpeace@gmail.com X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.18-1 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 If there is no PMECC lookup table stored in ROM, or lookup table offset is not specified, PMECC driver should build it in DDR by itself. That make the PMECC driver work for some board which doesn't has PMECC lookup table in ROM. Signed-off-by: Josh Wu Cc: devicetree@vger.kernel.org --- This patch is based on mtd-l2/next branch v1 -> v2: make create_lookup_table() static. .../devicetree/bindings/mtd/atmel-nand.txt | 6 +- drivers/mtd/nand/atmel_nand.c | 136 ++++++++++++++++++++- 2 files changed, 136 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/mtd/atmel-nand.txt b/Documentation/devicetree/bindings/mtd/atmel-nand.txt index c472883..75d1847 100644 --- a/Documentation/devicetree/bindings/mtd/atmel-nand.txt +++ b/Documentation/devicetree/bindings/mtd/atmel-nand.txt @@ -5,7 +5,9 @@ Required properties: - reg : should specify localbus address and size used for the chip, and hardware ECC controller if available. If the hardware ECC is PMECC, it should contain address and size for - PMECC, PMECC Error Location controller and ROM which has lookup tables. + PMECC and PMECC Error Location controller. + The PMECC lookup table address and size in ROM is optional. If not + specified, driver will build it in runtime. - atmel,nand-addr-offset : offset for the address latch. - atmel,nand-cmd-offset : offset for the command latch. - #address-cells, #size-cells : Must be present if the device has sub-nodes @@ -27,7 +29,7 @@ Optional properties: are: 512, 1024. - atmel,pmecc-lookup-table-offset : includes two offsets of lookup table in ROM for different sector size. First one is for sector size 512, the next is for - sector size 1024. + sector size 1024. If not specified, driver will build the table in runtime. - nand-bus-width : 8 or 16 bus width if not present 8 - nand-on-flash-bbt: boolean to enable on flash bbt option if not present false - Nand Flash Controller(NFC) is a slave driver under Atmel nand flash diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index effa7a29..84af313 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -124,6 +124,7 @@ struct atmel_nand_host { bool has_pmecc; u8 pmecc_corr_cap; u16 pmecc_sector_size; + bool has_no_lookup_table; u32 pmecc_lookup_table_offset; u32 pmecc_lookup_table_offset_512; u32 pmecc_lookup_table_offset_1024; @@ -1109,12 +1110,121 @@ static int pmecc_choose_ecc(struct atmel_nand_host *host, return 0; } +static int pmecc_build_galois_table(int mm, + int16_t *index_of, int16_t *alpha_to) +{ + unsigned int i, mask, nn; + unsigned int p[PMECC_GF_DIMENSION_14 + 1]; + + nn = (1 << mm) - 1; + /* set default value */ + for (i = 1; i < mm; i++) + p[i] = 0; + + /* 1 + X^mm */ + p[0] = 1; + p[mm] = 1; + + /* others */ + switch (mm) { + case 3: + case 4: + case 6: + case 15: + p[1] = 1; + break; + case 5: + case 11: + p[2] = 1; + break; + case 7: + case 10: + p[3] = 1; + break; + case 8: + p[2] = p[3] = p[4] = 1; + break; + case 9: + p[4] = 1; + break; + case 12: + p[1] = p[4] = p[6] = 1; + break; + case 13: + p[1] = p[3] = p[4] = 1; + break; + case 14: + p[1] = p[6] = p[10] = 1; + break; + default: + /* Error */ + return -EINVAL; + } + + /* Build alpha ^ mm it will help to generate the field (primitiv) */ + alpha_to[mm] = 0; + for (i = 0; i < mm; i++) + if (p[i]) + alpha_to[mm] |= 1 << i; + + /* + * Then build elements from 0 to mm - 1. As degree is less than mm + * so it is just a logical shift. + */ + mask = 1; + for (i = 0; i < mm; i++) { + alpha_to[i] = mask; + index_of[alpha_to[i]] = i; + mask <<= 1; + } + + index_of[alpha_to[mm]] = mm; + + /* use a mask to select the MSB bit of the LFSR */ + mask >>= 1; + + /* then finish the building */ + for (i = mm + 1; i <= nn; i++) { + /* check if the msb bit of the lfsr is set */ + if (alpha_to[i - 1] & mask) + alpha_to[i] = alpha_to[mm] ^ + ((alpha_to[i - 1] ^ mask) << 1); + else + alpha_to[i] = alpha_to[i - 1] << 1; + + index_of[alpha_to[i]] = i % nn; + } + + /* index of 0 is undefined in a multiplicative field */ + index_of[0] = -1; + + return 0; +} + +static uint16_t *create_lookup_table(struct device *dev, int sector_size) +{ + int table_size = (sector_size == 512) ? + PMECC_LOOKUP_TABLE_SIZE_512 : + PMECC_LOOKUP_TABLE_SIZE_1024; + int degree = (sector_size == 512) ? + PMECC_GF_DIMENSION_13 : + PMECC_GF_DIMENSION_14; + uint16_t *addr = devm_kzalloc(dev, 2 * table_size * sizeof(uint16_t), + GFP_KERNEL); + + if (addr) + pmecc_build_galois_table(degree, addr, addr + table_size); + + return addr; +} + static int atmel_pmecc_nand_init_params(struct platform_device *pdev, struct atmel_nand_host *host) { struct mtd_info *mtd = &host->mtd; struct nand_chip *nand_chip = &host->nand_chip; struct resource *regs, *regs_pmerr, *regs_rom; + uint16_t *galois_table; int cap, sector_size, err_no; err_no = pmecc_choose_ecc(host, &cap, §or_size); @@ -1160,8 +1270,24 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev, regs_rom = platform_get_resource(pdev, IORESOURCE_MEM, 3); host->pmecc_rom_base = devm_ioremap_resource(&pdev->dev, regs_rom); if (IS_ERR(host->pmecc_rom_base)) { - err_no = PTR_ERR(host->pmecc_rom_base); - goto err; + if (!host->has_no_lookup_table) + /* Don't display the information again */ + dev_err(host->dev, "Can not get I/O resource for ROM, will build a lookup table in runtime!\n"); + + host->has_no_lookup_table = true; + } + + if (host->has_no_lookup_table) { + /* Build the look-up table in runtime */ + galois_table = create_lookup_table(host->dev, sector_size); + if (!galois_table) { + dev_err(host->dev, "Failed to build a lookup table in runtime!\n"); + err_no = -ENOMEM; + goto err; + } + + host->pmecc_rom_base = galois_table; + host->pmecc_lookup_table_offset = 0; } nand_chip->ecc.size = sector_size; @@ -1498,8 +1624,10 @@ static int atmel_of_init_port(struct atmel_nand_host *host, if (of_property_read_u32_array(np, "atmel,pmecc-lookup-table-offset", offset, 2) != 0) { - dev_err(host->dev, "Cannot get PMECC lookup table offset\n"); - return -EINVAL; + dev_err(host->dev, "Cannot get PMECC lookup table offset, will build a lookup table in runtime.\n"); + host->has_no_lookup_table = true; + /* Will build a lookup table and initialize the offset later */ + return 0; } if (!offset[0] && !offset[1]) { dev_err(host->dev, "Invalid PMECC lookup table offset\n");