Received: by 2002:ad5:474a:0:0:0:0:0 with SMTP id i10csp1898519imu; Wed, 21 Nov 2018 03:48:12 -0800 (PST) X-Google-Smtp-Source: AFSGD/ULeodrbd+rF3i1RELMak3KsmMBnwMBlXLzilUbH3ja7aI7o2E3a437ix5jAIox3MuBbFk6 X-Received: by 2002:a17:902:24e7:: with SMTP id l36-v6mr6423053plg.243.1542800892240; Wed, 21 Nov 2018 03:48:12 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1542800892; cv=none; d=google.com; s=arc-20160816; b=uNiWT8NwXV4IQCHDWgSRW/12Vx6AjGRowSoGAU7UpQZ7jpkjjPg2rNHVtNWqoIZcUO KcWwt2hDbPD7wz9B5T8AzD0/zHzFkq6z769ODvAFBsyoBXZO0Ez0zIClHT3q2ZqBoVl0 AH5rQmaIgsQB33/sanKy08+kFMfcyBtRZtLzoxLfK+WN3597lECtOcadaZiRlwtoUOwQ sbAKEXwfdKCU6imVsqWMxvR6CGO63Aa7LQ6n/9GHuA7UaS5YYOqnX5bi33KgSLr+ibEb gEzw/4VYyAqjq9qs4f/WnMvDn3hecO1yBp8J921jaXvK9cZ5PkhxK5FCAlRL8NlM+2HD xGaw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature:dkim-filter; bh=pzEV/otGhqrfXtvwHgR3GL7T2gkpwJyBM09+/rgZ5Yk=; b=PGYjTe+ZCyoficqJxR4Z+0DGi90EplwpMA4Ydp4S4IgiyeeQ9GXQwGLK+K/hwH9wVp glt7eRYjxJW3XbUsmvtEgScOFiiOX3EpQI0e4XtAYYf2N2X1JyVMtSWhv/8gBJzjzNCM X1cVZ6lxGAzcnwI+d0N0gofv+xLtuMmT/7PXepSMlURiB/lDHInbLEBsva7vekKy9qrM 7Gyzv27AUsSCjF8LhuNIQi7i7b/NiNOqnejGKtyIXbqCDvlAFGDAhYc8EZh2CnokdT/m Xka+fVUoTSo5v4dFPSqRxmko9TrYuCNNpKKVDOzpb8IhB5pF9GgZz58y3W+4afY//pDa oLLg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@upb.ro header.s=96342B8A-77E4-11E5-BA93-D93D0963A2DF header.b=GF5VJ8JF; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id h3si46171613pgi.391.2018.11.21.03.47.56; Wed, 21 Nov 2018 03:48:12 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@upb.ro header.s=96342B8A-77E4-11E5-BA93-D93D0963A2DF header.b=GF5VJ8JF; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730316AbeKUWNe (ORCPT + 99 others); Wed, 21 Nov 2018 17:13:34 -0500 Received: from mail-sender220.upb.ro ([141.85.13.220]:38940 "EHLO mx.upb.ro" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1727628AbeKUWNV (ORCPT ); Wed, 21 Nov 2018 17:13:21 -0500 X-Greylist: delayed 621 seconds by postgrey-1.27 at vger.kernel.org; Wed, 21 Nov 2018 17:13:18 EST Received: from mx.upb.ro (localhost [127.0.0.1]) by mx.upb.ro (Postfix) with ESMTPS id EDBBCB560065; Wed, 21 Nov 2018 13:28:53 +0200 (EET) Received: from localhost (localhost [127.0.0.1]) by mx.upb.ro (Postfix) with ESMTP id D0EE6B560070; Wed, 21 Nov 2018 13:28:53 +0200 (EET) DKIM-Filter: OpenDKIM Filter v2.10.3 mx.upb.ro D0EE6B560070 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=upb.ro; s=96342B8A-77E4-11E5-BA93-D93D0963A2DF; t=1542799733; bh=pzEV/otGhqrfXtvwHgR3GL7T2gkpwJyBM09+/rgZ5Yk=; h=From:To:Date:Message-Id:MIME-Version; b=GF5VJ8JF5YdSLmbcnXFEWRQlCyzzh4j8pWl+hmOs9Qk+e99fGnB3Xzqk3KCWRGAHO 7WW5QGgKHeS0DvPfTULvpOgKojZuAMeY2wsGaI2AMsbdY8kdu5VyVj2MGjbKp42RCT taEhPZ4IyTpm2MbDvC2J23OhX62teSH+ki+dWOwI= Received: from mx.upb.ro ([127.0.0.1]) by localhost (mx.upb.ro [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id B7M91qedbv-3; Wed, 21 Nov 2018 13:28:53 +0200 (EET) Received: from asus-rog.localdomain (arh.pub.ro [141.85.160.17]) by mx.upb.ro (Postfix) with ESMTPSA id A6496B560065; Wed, 21 Nov 2018 13:28:53 +0200 (EET) From: Radu Pirea To: richard.genoud@gmail.com, lee.jones@linaro.org, robh+dt@kernel.org, mark.rutland@arm.com, nicolas.ferre@microchip.com, alexandre.belloni@bootlin.com, ludovic.desroches@microchip.co, broonie@kernel.org Cc: linux-kernel@vger.kernel.org, linux-spi@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Radu Pirea Subject: [PATCH 3/3] spi: at91-usart: add DMA support Date: Wed, 21 Nov 2018 13:27:32 +0200 Message-Id: <20181121112732.15690-4-radu_nicolae.pirea@upb.ro> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20181121112732.15690-1-radu_nicolae.pirea@upb.ro> References: <20181121112732.15690-1-radu_nicolae.pirea@upb.ro> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This patch adds support for DMA. Transfers are done with dma only if they are longer than 16 bytes in order to achieve a better performance. DMA setup introduces a little overhead and for transfers shorter than 16 bytes there is no performance improvement. Signed-off-by: Radu Pirea --- drivers/spi/spi-at91-usart.c | 223 ++++++++++++++++++++++++++++++++++- 1 file changed, 221 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-at91-usart.c b/drivers/spi/spi-at91-usart.c index 0b07c746453d..4d908afeaec9 100644 --- a/drivers/spi/spi-at91-usart.c +++ b/drivers/spi/spi-at91-usart.c @@ -8,9 +8,12 @@ =20 #include #include +#include +#include #include #include #include +#include #include #include #include @@ -58,6 +61,8 @@ =20 #define US_INIT \ (US_MR_SPI_MASTER | US_MR_CHRL | US_MR_CLKO | US_MR_WRDBT) +#define US_DMA_MIN_BYTES 16 +#define US_DMA_TIMEOUT (msecs_to_jiffies(1000)) =20 /* Register access macros */ #define at91_usart_spi_readl(port, reg) \ @@ -71,14 +76,19 @@ writeb_relaxed((value), (port)->regs + US_##reg) =20 struct at91_usart_spi { + struct platform_device *mpdev; struct spi_transfer *current_transfer; void __iomem *regs; struct device *dev; struct clk *clk; =20 + struct completion xfer_completion; + /*used in interrupt to protect data reading*/ spinlock_t lock; =20 + phys_addr_t phybase; + int irq; unsigned int current_tx_remaining_bytes; unsigned int current_rx_remaining_bytes; @@ -87,8 +97,184 @@ struct at91_usart_spi { u32 status; =20 bool xfer_failed; + bool use_dma; }; =20 +static void dma_callback(void *data) +{ + struct spi_controller *ctlr =3D data; + struct at91_usart_spi *aus =3D spi_master_get_devdata(ctlr); + + at91_usart_spi_writel(aus, IER, US_IR_RXRDY); + aus->current_rx_remaining_bytes =3D 0; + complete(&aus->xfer_completion); +} + +static bool at91_usart_spi_can_dma(struct spi_controller *ctrl, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct at91_usart_spi *aus =3D spi_master_get_devdata(ctrl); + + return aus->use_dma && xfer->len >=3D US_DMA_MIN_BYTES; +} + +static int at91_usart_spi_configure_dma(struct spi_controller *ctlr, + struct at91_usart_spi *aus) +{ + struct dma_slave_config slave_config; + struct device *dev =3D &aus->mpdev->dev; + phys_addr_t phybase =3D aus->phybase; + dma_cap_mask_t mask; + int err =3D 0; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + ctlr->dma_tx =3D dma_request_slave_channel_reason(dev, "tx"); + if (IS_ERR_OR_NULL(ctlr->dma_tx)) { + if (IS_ERR(ctlr->dma_tx)) { + err =3D PTR_ERR(ctlr->dma_tx); + goto at91_usart_spi_error_clear; + } + + dev_dbg(dev, + "DMA TX channel not available, SPI unable to use DMA\n"); + err =3D -EBUSY; + goto at91_usart_spi_error_clear; + } + + ctlr->dma_rx =3D dma_request_slave_channel_reason(dev, "rx"); + if (IS_ERR_OR_NULL(ctlr->dma_rx)) { + if (IS_ERR(ctlr->dma_rx)) { + err =3D PTR_ERR(ctlr->dma_rx); + goto at91_usart_spi_error; + } + + dev_dbg(dev, + "DMA RX channel not available, SPI unable to use DMA\n"); + err =3D -EBUSY; + goto at91_usart_spi_error; + } + + slave_config.dst_addr_width =3D DMA_SLAVE_BUSWIDTH_1_BYTE; + slave_config.src_addr_width =3D DMA_SLAVE_BUSWIDTH_1_BYTE; + slave_config.dst_addr =3D (dma_addr_t)phybase + US_THR; + slave_config.src_addr =3D (dma_addr_t)phybase + US_RHR; + slave_config.src_maxburst =3D 1; + slave_config.dst_maxburst =3D 1; + slave_config.device_fc =3D false; + + slave_config.direction =3D DMA_DEV_TO_MEM; + if (dmaengine_slave_config(ctlr->dma_rx, &slave_config)) { + dev_err(&ctlr->dev, + "failed to configure rx dma channel\n"); + err =3D -EINVAL; + goto at91_usart_spi_error; + } + + slave_config.direction =3D DMA_MEM_TO_DEV; + if (dmaengine_slave_config(ctlr->dma_tx, &slave_config)) { + dev_err(&ctlr->dev, + "failed to configure tx dma channel\n"); + err =3D -EINVAL; + goto at91_usart_spi_error; + } + + aus->use_dma =3D true; + return 0; + +at91_usart_spi_error: + if (!IS_ERR_OR_NULL(ctlr->dma_tx)) + dma_release_channel(ctlr->dma_tx); + if (!IS_ERR_OR_NULL(ctlr->dma_rx)) + dma_release_channel(ctlr->dma_rx); + ctlr->dma_tx =3D NULL; + ctlr->dma_rx =3D NULL; + +at91_usart_spi_error_clear: + return err; +} + +static void at91_usart_spi_release_dma(struct spi_controller *ctlr) +{ + if (ctlr->dma_rx) + dma_release_channel(ctlr->dma_rx); + if (ctlr->dma_tx) + dma_release_channel(ctlr->dma_tx); +} + +static void at91_usart_spi_stop_dma(struct spi_controller *ctlr) +{ + if (ctlr->dma_rx) + dmaengine_terminate_all(ctlr->dma_rx); + if (ctlr->dma_tx) + dmaengine_terminate_all(ctlr->dma_tx); +} + +static int at91_usart_spi_dma_transfer(struct spi_controller *ctlr, + struct spi_transfer *xfer) +{ + struct at91_usart_spi *aus =3D spi_master_get_devdata(ctlr); + struct dma_chan *rxchan =3D ctlr->dma_rx; + struct dma_chan *txchan =3D ctlr->dma_tx; + struct dma_async_tx_descriptor *rxdesc; + struct dma_async_tx_descriptor *txdesc; + dma_cookie_t cookie; + + rxdesc =3D dmaengine_prep_slave_sg(rxchan, + xfer->rx_sg.sgl, + xfer->rx_sg.nents, + DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT | + DMA_CTRL_ACK); + if (!rxdesc) + goto at91_usart_spi_err_dma; + + txdesc =3D dmaengine_prep_slave_sg(txchan, + xfer->tx_sg.sgl, + xfer->tx_sg.nents, + DMA_TO_DEVICE, + DMA_PREP_INTERRUPT | + DMA_CTRL_ACK); + if (!txdesc) + goto at91_usart_spi_err_dma; + + /* Disable RX interrupt */ + at91_usart_spi_writel(aus, IDR, US_IR_RXRDY); + + rxdesc->callback =3D dma_callback; + rxdesc->callback_param =3D ctlr; + + cookie =3D rxdesc->tx_submit(rxdesc); + if (dma_submit_error(cookie)) + goto at91_usart_spi_err_dma; + + cookie =3D txdesc->tx_submit(txdesc); + if (dma_submit_error(cookie)) + goto at91_usart_spi_err_dma; + + rxchan->device->device_issue_pending(rxchan); + txchan->device->device_issue_pending(txchan); + + return 0; + +at91_usart_spi_err_dma: + /* Enable RX interrupt if submission of any of descriptors fails + * and fallback to PIO + */ + at91_usart_spi_writel(aus, IER, US_IR_RXRDY); + at91_usart_spi_stop_dma(ctlr); + + return -ENOMEM; +} + +static unsigned long at91_usart_spi_dma_timeout(struct at91_usart_spi *a= us) +{ + return wait_for_completion_timeout(&aus->xfer_completion, + US_DMA_TIMEOUT); +} + static inline u32 at91_usart_spi_tx_ready(struct at91_usart_spi *aus) { return aus->status & US_IR_TXRDY; @@ -221,6 +407,8 @@ static int at91_usart_spi_transfer_one(struct spi_con= troller *ctlr, struct spi_transfer *xfer) { struct at91_usart_spi *aus =3D spi_master_get_devdata(ctlr); + unsigned long dma_timeout =3D 0; + int ret =3D 0; =20 at91_usart_spi_set_xfer_speed(aus, xfer); aus->xfer_failed =3D false; @@ -230,8 +418,25 @@ static int at91_usart_spi_transfer_one(struct spi_co= ntroller *ctlr, =20 while ((aus->current_tx_remaining_bytes || aus->current_rx_remaining_bytes) && !aus->xfer_failed) { - at91_usart_spi_read_status(aus); - at91_usart_spi_tx(aus); + reinit_completion(&aus->xfer_completion); + if (at91_usart_spi_can_dma(ctlr, spi, xfer) && + !ret) { + ret =3D at91_usart_spi_dma_transfer(ctlr, xfer); + if (ret) + continue; + + dma_timeout =3D at91_usart_spi_dma_timeout(aus); + + if (WARN_ON(dma_timeout =3D=3D 0)) { + dev_err(&spi->dev, "DMA transfer timeout\n"); + return -EIO; + } + aus->current_tx_remaining_bytes =3D 0; + } else { + at91_usart_spi_read_status(aus); + at91_usart_spi_tx(aus); + } + cpu_relax(); } =20 @@ -350,6 +555,7 @@ static int at91_usart_spi_probe(struct platform_devic= e *pdev) controller->transfer_one =3D at91_usart_spi_transfer_one; controller->prepare_message =3D at91_usart_spi_prepare_message; controller->unprepare_message =3D at91_usart_spi_unprepare_message; + controller->can_dma =3D at91_usart_spi_can_dma; controller->cleanup =3D at91_usart_spi_cleanup; controller->max_speed_hz =3D DIV_ROUND_UP(clk_get_rate(clk), US_MIN_CLK_DIV); @@ -381,7 +587,17 @@ static int at91_usart_spi_probe(struct platform_devi= ce *pdev) aus->spi_clk =3D clk_get_rate(clk); at91_usart_spi_init(aus); =20 + aus->phybase =3D regs->start; + + aus->mpdev =3D to_platform_device(pdev->dev.parent); + + ret =3D at91_usart_spi_configure_dma(controller, aus); + if (ret) + goto at91_usart_fail_dma; + spin_lock_init(&aus->lock); + init_completion(&aus->xfer_completion); + ret =3D devm_spi_register_master(&pdev->dev, controller); if (ret) goto at91_usart_fail_register_master; @@ -394,6 +610,8 @@ static int at91_usart_spi_probe(struct platform_devic= e *pdev) return 0; =20 at91_usart_fail_register_master: + at91_usart_spi_release_dma(controller); +at91_usart_fail_dma: clk_disable_unprepare(clk); at91_usart_spi_probe_fail: spi_master_put(controller); @@ -458,6 +676,7 @@ static int at91_usart_spi_remove(struct platform_devi= ce *pdev) struct spi_controller *ctlr =3D platform_get_drvdata(pdev); struct at91_usart_spi *aus =3D spi_master_get_devdata(ctlr); =20 + at91_usart_spi_release_dma(ctlr); clk_disable_unprepare(aus->clk); =20 return 0; --=20 2.19.1