From patchwork Sun Dec 24 04:36:06 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cyrille Pitchen X-Patchwork-Id: 852716 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=65.50.211.133; helo=bombadil.infradead.org; envelope-from=linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="J8s3VCDJ"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.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 3z48dB6LxBz9s0g for ; Sun, 24 Dec 2017 15:38:22 +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=k2+ICU8ee/2YHNbuvKINkMFQ0h6hy3iU6cQJG2PTGkg=; b=J8s3VCDJoEgZfnH4ALbdvhKgBd bBB/KV/8H4ASTH8F2oXQIiL9QGNkL26WOI457O7rvZ1gCRpWK6U4cTtB5h6JDln20ajprthbN9dPD EXWjcZ/EA/zRuxq5GAgNFwS701rScFxMq5mZOJT3nClnUlod9C7HztJxDolXooVFNBKFmXmVDzrg7 UCorU+IPh0oZUqtbDjNfr/IqFdNYiRhYybUQMQLESdB9gHsWyC/PdYIAaIxtkjF30rb5uBmDNIjIo Oh2LweBC/FEOpMiv+juNFVKgggK9kloEKKWbCTyiZPMhaS41aa8x3b98i+r7aRg81VPYx+UHxZefS 4ghx7Bhg==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.89 #1 (Red Hat Linux)) id 1eSy3A-00035s-4t; Sun, 24 Dec 2017 04:38:12 +0000 Received: from 4.mo69.mail-out.ovh.net ([46.105.42.102]) by bombadil.infradead.org with esmtps (Exim 4.89 #1 (Red Hat Linux)) id 1eSy2R-0002Xs-Ta for linux-mtd@lists.infradead.org; Sun, 24 Dec 2017 04:37:42 +0000 Received: from player699.ha.ovh.net (gw6.ovh.net [213.251.189.206]) by mo69.mail-out.ovh.net (Postfix) with ESMTP id 9840B44DA3 for ; Sun, 24 Dec 2017 05:37:15 +0100 (CET) Received: from mountainer.wedev4u.int (cor13-1-82-232-94-13.fbx.proxad.net [82.232.94.13]) (Authenticated sender: cyrille.pitchen@wedev4u.fr) by player699.ha.ovh.net (Postfix) with ESMTPSA id A343924007E; Sun, 24 Dec 2017 05:36:58 +0100 (CET) From: Cyrille Pitchen To: computersforpeace@gmail.com, dwmw2@infradead.org, richard@nod.at, boris.brezillon@free-electrons.com, marek.vasut@gmail.com, linux-mtd@lists.infradead.org, broonie@kernel.org, vigneshr@ti.com, linux@armlinux.org.uk Subject: [PATCH 3/3] mtd: atmel-quadspi: add support of DMA memcpy() Date: Sun, 24 Dec 2017 05:36:06 +0100 Message-Id: <9546f3b76cb5333de1977bf463e92a995a2d974f.1514087323.git.cyrille.pitchen@wedev4u.fr> X-Mailer: git-send-email 2.11.0 In-Reply-To: References: In-Reply-To: References: X-Ovh-Tracer-Id: 3385299546982864862 X-VR-SPAMSTATE: OK X-VR-SPAMSCORE: 25 X-VR-SPAMCAUSE: gggruggvucftvghtrhhoucdtuddrgedtuddrhedugddulecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfqggfjpdevjffgvefmvefgnecuuegrihhlohhuthemuceftddtnecuogfthfevqddqjfgurhdqufhushhpvggtthdqlhhoficuldehmdenogfthfevqddqjfgurhdqufhushhpvggtthculddvtddm X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20171223_203728_815128_7C4D03B2 X-CRM114-Status: GOOD ( 18.44 ) X-Spam-Score: -2.6 (--) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-2.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_MSPIKE_H3 RBL: Good reputation (+3) [46.105.42.102 listed in wl.mailspike.net] -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [46.105.42.102 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] -0.0 RCVD_IN_MSPIKE_WL Mailspike good senders 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: robh@kernel.org, devicetree@vger.kernel.org, nicolas.ferre@microchip.com, linux-kernel@vger.kernel.org, Cyrille Pitchen , radu.pirea@microchip.com, linux-spi@vger.kernel.org MIME-Version: 1.0 Sender: "linux-mtd" Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org This patch takes advantage of the new bounce buffer helper from the spi-nor framework to add support of memcpy() operations using the DMA controller. Since the number of DMA channels is limited and to avoid changing how those DMA channels are used in existing boards, this new DMA memcpy() feature is enabled only if the "dmacap,memcpy" boolean property is set in the device-tree. Signed-off-by: Cyrille Pitchen --- drivers/mtd/spi-nor/atmel-quadspi.c | 132 +++++++++++++++++++++++++++++++++++- 1 file changed, 129 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/spi-nor/atmel-quadspi.c b/drivers/mtd/spi-nor/atmel-quadspi.c index 6c5708bacad8..5443e4dba416 100644 --- a/drivers/mtd/spi-nor/atmel-quadspi.c +++ b/drivers/mtd/spi-nor/atmel-quadspi.c @@ -22,6 +22,8 @@ #include #include +#include +#include #include #include #include @@ -36,6 +38,8 @@ #include #include +#define QSPI_DMA_THRESHOLD 32 + /* QSPI register offsets */ #define QSPI_CR 0x0000 /* Control Register */ #define QSPI_MR 0x0004 /* Mode Register */ @@ -161,6 +165,11 @@ struct atmel_qspi { struct spi_nor nor; u32 clk_rate; struct completion cmd_completion; + + /* DMA transfers */ + struct dma_chan *chan; + dma_addr_t dma_mem; + struct completion transfer_complete; }; struct atmel_qspi_command { @@ -197,11 +206,96 @@ static inline void qspi_writel(struct atmel_qspi *aq, u32 reg, u32 value) writel_relaxed(value, aq->regs + reg); } +static void atmel_qspi_dma_callback(void *param) +{ + struct atmel_qspi *aq = param; + + complete(&aq->transfer_complete); +} + +static int atmel_qspi_run_dma_transfer(struct atmel_qspi *aq, + const struct atmel_qspi_command *cmd) +{ + struct device *dev = &aq->pdev->dev; + enum dma_data_direction dir; + struct dma_async_tx_descriptor *desc; + dma_addr_t src, dst, dma_addr; + dma_cookie_t cookie; + u32 offset; + void *ptr; + int err = 0; + + if (cmd->tx_buf) { + ptr = (void *)cmd->tx_buf; + dir = DMA_TO_DEVICE; + } else if (cmd->rx_buf) { + ptr = cmd->rx_buf; + dir = DMA_FROM_DEVICE; + } else { + return -EINVAL; + } + + dma_addr = dma_map_single(dev, ptr, cmd->buf_len, dir); + if (dma_mapping_error(dev, dma_addr)) + return -ENOMEM; + + offset = cmd->enable.bits.address ? cmd->address : 0; + if (cmd->tx_buf) { + dst = aq->dma_mem + offset; + src = dma_addr; + } else { + dst = dma_addr; + src = aq->dma_mem + offset; + } + + desc = dmaengine_prep_dma_memcpy(aq->chan, dst, src, cmd->buf_len, + DMA_CTRL_ACK | DMA_PREP_INTERRUPT); + if (!desc) { + err = -ENOMEM; + goto unmap; + } + + reinit_completion(&aq->transfer_complete); + desc->callback = atmel_qspi_dma_callback; + desc->callback_param = aq; + + cookie = dmaengine_submit(desc); + err = dma_submit_error(cookie); + if (err) { + err = -EIO; + goto unmap; + } + + dma_async_issue_pending(aq->chan); + err = wait_for_completion_timeout(&aq->transfer_complete, + msecs_to_jiffies(cmd->buf_len)); + if (err <= 0) { + dmaengine_terminate_sync(aq->chan); + err = -ETIMEDOUT; + goto unmap; + } + + err = 0; + + unmap: + dma_unmap_single(dev, dma_addr, cmd->buf_len, dir); + return err; +} + static int atmel_qspi_run_transfer(struct atmel_qspi *aq, const struct atmel_qspi_command *cmd) { void __iomem *ahb_mem; + /* Try DMA transfer first. */ + if (aq->chan && cmd->buf_len >= QSPI_DMA_THRESHOLD) { + int err = atmel_qspi_run_dma_transfer(aq, cmd); + + /* If the DMA transfer has started, stop here. */ + if (err != -ENOMEM) + return err; + } + /* Then fallback to a PIO transfer (memcpy() DOES NOT work!) */ ahb_mem = aq->mem; if (cmd->enable.bits.address) @@ -604,7 +698,7 @@ static irqreturn_t atmel_qspi_interrupt(int irq, void *dev_id) static int atmel_qspi_probe(struct platform_device *pdev) { - const struct spi_nor_hwcaps hwcaps = { + struct spi_nor_hwcaps hwcaps = { .mask = SNOR_HWCAPS_READ | SNOR_HWCAPS_READ_FAST | SNOR_HWCAPS_READ_1_1_2 | @@ -657,19 +751,44 @@ static int atmel_qspi_probe(struct platform_device *pdev) goto exit; } + aq->dma_mem = (dma_addr_t)res->start; + + /* Reserve a DMA channel for memcpy(), only if requested */ + if (of_property_read_bool(np, "dmacap,memcpy")) { + dma_cap_mask_t mask; + + dma_cap_zero(mask); + dma_cap_set(DMA_MEMCPY, mask); + + aq->chan = dma_request_chan_by_mask(&mask); + if (IS_ERR(aq->chan)) { + if (PTR_ERR(aq->chan) == -EPROBE_DEFER) { + err = -EPROBE_DEFER; + goto exit; + } + dev_warn(&pdev->dev, "no available DMA channel\n"); + aq->chan = NULL; + } else { + hwcaps.mask |= SNOR_HWCAPS_RD_BOUNCE | + SNOR_HWCAPS_WR_BOUNCE; + } + } + + init_completion(&aq->transfer_complete); + /* Get the peripheral clock */ aq->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(aq->clk)) { dev_err(&pdev->dev, "missing peripheral clock\n"); err = PTR_ERR(aq->clk); - goto exit; + goto release_dma_chan; } /* Enable the peripheral clock */ err = clk_prepare_enable(aq->clk); if (err) { dev_err(&pdev->dev, "failed to enable the peripheral clock\n"); - goto exit; + goto release_dma_chan; } /* Request the IRQ */ @@ -721,6 +840,9 @@ static int atmel_qspi_probe(struct platform_device *pdev) disable_clk: clk_disable_unprepare(aq->clk); +release_dma_chan: + if (aq->chan) + dma_release_channel(aq->chan); exit: of_node_put(child); @@ -734,6 +856,10 @@ static int atmel_qspi_remove(struct platform_device *pdev) mtd_device_unregister(&aq->nor.mtd); qspi_writel(aq, QSPI_CR, QSPI_CR_QSPIDIS); clk_disable_unprepare(aq->clk); + + if (aq->chan) + dma_release_channel(aq->chan); + return 0; }