From patchwork Fri Jan 18 01:06:34 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Cercueil X-Patchwork-Id: 1027078 X-Patchwork-Delegate: miquel.raynal@bootlin.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.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=crapouillou.net Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="sD8M9Vnt"; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=crapouillou.net header.i=@crapouillou.net header.b="FqGBXvaX"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 43gjWt4GjWz9sDr for ; Fri, 18 Jan 2019 12:09:14 +1100 (AEDT) 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:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id: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=E4foSKKOyEScjMiv0ZBKEHV2W/Uk4y7DedgF2Sz03O4=; b=sD8M9VntrjpIFZVv72EsGVVRYF JSXYhav2NJ5+QauZY1Drhxo7aFQcNYDVhQtY14ngSpSM7kUakuJhhw0jmxCagRO+soYQW3IZtHr6n ipw9Anmer8Pa/Lbp/L3uXyNlNAhSsU15WteD/x/s4L9zrUwAj3kM9tRtWsyI6EOUH4tZMMXNNyWPB bosIwYzDRsMnKPdZb+fszR5wqapxvJQIoXaG2G8iHvtD1rXr0OT32yEVHhQ/R+wXtyAhq2MBgfqy3 uP0OTQu7gwVLlqhMktv59tO6BcqOW6RavDdJY6Ck2EaRrihwUTwRN9tdM0N3WDiaFMNE8r7MR+C0p lLL7zwwg==; 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 1gkIeg-0000Pt-R2; Fri, 18 Jan 2019 01:09:06 +0000 Received: from outils.crapouillou.net ([89.234.176.41] helo=crapouillou.net) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gkIdA-0007RP-9Z for linux-mtd@lists.infradead.org; Fri, 18 Jan 2019 01:08:12 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=crapouillou.net; s=mail; t=1547773650; h=from:from:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:content-type: content-transfer-encoding:in-reply-to:in-reply-to: references:references; bh=Omx15frAzVyJadifK39eZ16uWs9JSfFkiSFAT6zYU5E=; b=FqGBXvaXhmCXUjYeAdj8WO/ayVTHPfE+zGjg5eaHxzY0soHFZDC/NP2PZFb20LAelnkZRH rEw4jFm76ZyNEaASn35GpF+Jsj9lvtDCpyIX7HArMlFpOBP11tRX3w7BIYPc5qLFu5qVpv 6V8nYcrjWwObICPVKvUNdumOi3iz3lY= From: Paul Cercueil To: David Woodhouse , Brian Norris , Boris Brezillon , Marek Vasut , Richard Weinberger , Rob Herring , Mark Rutland , Ralf Baechle , Paul Burton , James Hogan , Miquel Raynal , Harvey Hunt Subject: [PATCH 8/8] mtd: rawnand: jz4780-bch: Add support for the JZ4725B Date: Thu, 17 Jan 2019 22:06:34 -0300 Message-Id: <20190118010634.27399-8-paul@crapouillou.net> In-Reply-To: <20190118010634.27399-1-paul@crapouillou.net> References: <20190118010634.27399-1-paul@crapouillou.net> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190117_170733_584350_6054178C X-CRM114-Status: GOOD ( 21.66 ) X-Spam-Score: -0.2 (/) X-Spam-Report: SpamAssassin version 3.4.2 on bombadil.infradead.org summary: Content analysis details: (-0.2 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_PASS SPF: sender matches SPF record -0.0 SPF_HELO_PASS SPF: HELO matches SPF record -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Paul Cercueil , devicetree@vger.kernel.org, linux-mtd@lists.infradead.org, linux-kernel@vger.kernel.org, linux-mips@vger.kernel.org MIME-Version: 1.0 Sender: "linux-mtd" Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Add the backend code for the jz4780-bch driver to support the JZ4725B SoC from Ingenic. Signed-off-by: Paul Cercueil --- drivers/mtd/nand/raw/Makefile | 2 +- drivers/mtd/nand/raw/jz4725b_bch.c | 234 +++++++++++++++++++++++++++++ drivers/mtd/nand/raw/jz4780_bch_common.c | 1 + drivers/mtd/nand/raw/jz4780_bch_internal.h | 1 + 4 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/raw/jz4725b_bch.c diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index 6dacc9cf38d5..99cc9317a218 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -47,7 +47,7 @@ obj-$(CONFIG_MTD_NAND_VF610_NFC) += vf610_nfc.o obj-$(CONFIG_MTD_NAND_RICOH) += r852.o obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o obj-$(CONFIG_MTD_NAND_JZ4780) += jz4780_nand.o jz4780_bch_common.o \ - jz4780_bch.o + jz4780_bch.o jz4725b_bch.o obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/ diff --git a/drivers/mtd/nand/raw/jz4725b_bch.c b/drivers/mtd/nand/raw/jz4725b_bch.c new file mode 100644 index 000000000000..54f9c5796e83 --- /dev/null +++ b/drivers/mtd/nand/raw/jz4725b_bch.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * JZ4780 backend code for the jz4780-bch driver + * + * Copyright (C) 2018 Paul Cercueil + * + * Based on jz4780_bch.c + */ + +#include +#include +#include +#include +#include + +#include "jz4780_bch.h" +#include "jz4780_bch_internal.h" + +#define BCH_BHCR 0x0 +#define BCH_BHCSR 0x4 +#define BCH_BHCCR 0x8 +#define BCH_BHCNT 0xc +#define BCH_BHDR 0x10 +#define BCH_BHPAR0 0x14 +#define BCH_BHERR0 0x28 +#define BCH_BHINT 0x24 +#define BCH_BHINTES 0x3c +#define BCH_BHINTEC 0x40 +#define BCH_BHINTE 0x38 + +#define BCH_BHCR_BSEL_SHIFT 2 +#define BCH_BHCR_BSEL_MASK (0x1 << BCH_BHCR_BSEL_SHIFT) +#define BCH_BHCR_ENCE BIT(3) +#define BCH_BHCR_INIT BIT(1) +#define BCH_BHCR_BCHE BIT(0) + +#define BCH_BHCNT_DEC_COUNT_SHIFT 16 +#define BCH_BHCNT_DEC_COUNT_MASK (0x3ff << BCH_BHCNT_DEC_COUNT_SHIFT) +#define BCH_BHCNT_ENC_COUNT_SHIFT 0 +#define BCH_BHCNT_ENC_COUNT_MASK (0x3ff << BCH_BHCNT_ENC_COUNT_SHIFT) + +#define BCH_BHERR_INDEX0_SHIFT 0 +#define BCH_BHERR_INDEX0_MASK (0x1fff << BCH_BHERR_INDEX0_SHIFT) +#define BCH_BHERR_INDEX1_SHIFT 16 +#define BCH_BHERR_INDEX1_MASK (0x1fff << BCH_BHERR_INDEX1_SHIFT) + +#define BCH_BHINT_ERRC_SHIFT 28 +#define BCH_BHINT_ERRC_MASK (0xf << BCH_BHINT_ERRC_SHIFT) +#define BCH_BHINT_TERRC_SHIFT 16 +#define BCH_BHINT_TERRC_MASK (0x7f << BCH_BHINT_TERRC_SHIFT) +#define BCH_BHINT_ALL_0 BIT(5) +#define BCH_BHINT_ALL_F BIT(4) +#define BCH_BHINT_DECF BIT(3) +#define BCH_BHINT_ENCF BIT(2) +#define BCH_BHINT_UNCOR BIT(1) +#define BCH_BHINT_ERR BIT(0) + +/* Timeout for BCH calculation/correction. */ +#define BCH_TIMEOUT_US 100000 + +static void jz4725b_bch_init(struct jz4780_bch *bch, + struct jz4780_bch_params *params, bool encode) +{ + u32 reg; + + /* Clear interrupt status. */ + writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT); + + /* Initialise and enable BCH. */ + writel(0x1f, bch->base + BCH_BHCCR); + writel(BCH_BHCR_BCHE, bch->base + BCH_BHCSR); + + if (params->strength == 8) + writel(BCH_BHCR_BSEL_MASK, bch->base + BCH_BHCSR); + else + writel(BCH_BHCR_BSEL_MASK, bch->base + BCH_BHCCR); + + if (encode) + writel(BCH_BHCR_ENCE, bch->base + BCH_BHCSR); + else + writel(BCH_BHCR_ENCE, bch->base + BCH_BHCCR); + + writel(BCH_BHCR_INIT, bch->base + BCH_BHCSR); + + /* Set up BCH count register. */ + reg = params->size << BCH_BHCNT_ENC_COUNT_SHIFT; + reg |= (params->size + params->bytes) << BCH_BHCNT_DEC_COUNT_SHIFT; + writel(reg, bch->base + BCH_BHCNT); +} + +static void jz4725b_bch_disable(struct jz4780_bch *bch) +{ + writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT); + writel(BCH_BHCR_BCHE, bch->base + BCH_BHCCR); +} + +static void jz4725b_bch_write_data(struct jz4780_bch *bch, const u8 *buf, + size_t size) +{ + while (size--) + writeb(*buf++, bch->base + BCH_BHDR); +} + +static void jz4725b_bch_read_parity(struct jz4780_bch *bch, u8 *buf, + size_t size) +{ + size_t size32 = size / sizeof(u32); + size_t size8 = size % sizeof(u32); + u32 *dest32; + u8 *dest8; + u32 val, offset = 0; + + dest32 = (u32 *)buf; + while (size32--) { + *dest32++ = readl(bch->base + BCH_BHPAR0 + offset); + offset += sizeof(u32); + } + + dest8 = (u8 *)dest32; + val = readl(bch->base + BCH_BHPAR0 + offset); + switch (size8) { + case 3: + dest8[2] = (val >> 16) & 0xff; + case 2: + dest8[1] = (val >> 8) & 0xff; + case 1: + dest8[0] = val & 0xff; + break; + } +} + +static bool jz4725b_bch_wait_complete(struct jz4780_bch *bch, unsigned int irq, + u32 *status) +{ + u32 reg; + int ret; + + /* + * While we could use interrupts here and sleep until the operation + * completes, the controller works fairly quickly (usually a few + * microseconds) and so the overhead of sleeping until we get an + * interrupt quite noticeably decreases performance. + */ + ret = readl_poll_timeout(bch->base + BCH_BHINT, reg, + (reg & irq) == irq, 0, BCH_TIMEOUT_US); + if (ret) + return false; + + if (status) + *status = reg; + + writel(reg, bch->base + BCH_BHINT); + return true; +} + +static int jz4725b_calculate(struct jz4780_bch *bch, + struct jz4780_bch_params *params, + const u8 *buf, u8 *ecc_code) +{ + int ret = 0; + + mutex_lock(&bch->lock); + jz4725b_bch_init(bch, params, true); + jz4725b_bch_write_data(bch, buf, params->size); + + if (jz4725b_bch_wait_complete(bch, BCH_BHINT_ENCF, NULL)) { + jz4725b_bch_read_parity(bch, ecc_code, params->bytes); + } else { + dev_err(bch->dev, "timed out while calculating ECC\n"); + ret = -ETIMEDOUT; + } + + jz4725b_bch_disable(bch); + mutex_unlock(&bch->lock); + return ret; +} + +static int jz4725b_correct(struct jz4780_bch *bch, + struct jz4780_bch_params *params, + u8 *buf, u8 *ecc_code) +{ + u32 reg, errors, bit; + unsigned int i; + int ret = 0; + + mutex_lock(&bch->lock); + + jz4725b_bch_init(bch, params, false); + jz4725b_bch_write_data(bch, buf, params->size); + jz4725b_bch_write_data(bch, ecc_code, params->bytes); + + if (!jz4725b_bch_wait_complete(bch, BCH_BHINT_DECF, ®)) { + dev_err(bch->dev, "timed out while correcting data\n"); + ret = -ETIMEDOUT; + goto out; + } + + if (reg & (BCH_BHINT_ALL_F | BCH_BHINT_ALL_0)) { + /* Data and ECC is all 0xff or 0x00 - nothing to correct */ + ret = 0; + goto out; + } + + if (reg & BCH_BHINT_UNCOR) { + /* Uncorrectable ECC error */ + ret = -EBADMSG; + goto out; + } + + errors = (reg & BCH_BHINT_ERRC_MASK) >> BCH_BHINT_ERRC_SHIFT; + + /* Correct any detected errors. */ + for (i = 0; i < errors; i++) { + if (i & 1) { + bit = (reg & BCH_BHERR_INDEX1_MASK) >> BCH_BHERR_INDEX1_SHIFT; + } else { + reg = readl(bch->base + BCH_BHERR0 + (i * 4)); + bit = (reg & BCH_BHERR_INDEX0_MASK) >> BCH_BHERR_INDEX0_SHIFT; + } + + buf[(bit >> 3)] ^= BIT(bit & 0x7); + } + +out: + jz4725b_bch_disable(bch); + mutex_unlock(&bch->lock); + return ret; +} + +const struct jz4780_bch_ops jz4780_bch_jz4725b_ops = { + .disable = jz4725b_bch_disable, + .calculate = jz4725b_calculate, + .correct = jz4725b_correct, +}; diff --git a/drivers/mtd/nand/raw/jz4780_bch_common.c b/drivers/mtd/nand/raw/jz4780_bch_common.c index 573b079e6cbe..5b5ab4e66c49 100644 --- a/drivers/mtd/nand/raw/jz4780_bch_common.c +++ b/drivers/mtd/nand/raw/jz4780_bch_common.c @@ -152,6 +152,7 @@ static int jz4780_bch_probe(struct platform_device *pdev) } static const struct of_device_id jz4780_bch_dt_match[] = { + { .compatible = "ingenic,jz4725b-bch", .data = &jz4780_bch_jz4725b_ops}, { .compatible = "ingenic,jz4780-bch", .data = &jz4780_bch_jz4780_ops }, {}, }; diff --git a/drivers/mtd/nand/raw/jz4780_bch_internal.h b/drivers/mtd/nand/raw/jz4780_bch_internal.h index 7162e4f872f4..cc12b782a8d9 100644 --- a/drivers/mtd/nand/raw/jz4780_bch_internal.h +++ b/drivers/mtd/nand/raw/jz4780_bch_internal.h @@ -29,6 +29,7 @@ struct jz4780_bch { struct mutex lock; }; +extern const struct jz4780_bch_ops jz4780_bch_jz4725b_ops; extern const struct jz4780_bch_ops jz4780_bch_jz4780_ops; #endif /* __DRIVERS_MTD_NAND_JZ4780_BCH_INTERNAL_H__ */