linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] Add PM and DMA support for AT91 USART as SPI
@ 2018-11-21 11:27 Radu Pirea
  2018-11-21 11:27 ` [PATCH 1/3] spi: at91-usart: add power management support Radu Pirea
                   ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Radu Pirea @ 2018-11-21 11:27 UTC (permalink / raw)
  To: richard.genoud, lee.jones, robh+dt, mark.rutland, nicolas.ferre,
	alexandre.belloni, ludovic.desroches, broonie
  Cc: linux-kernel, linux-spi, devicetree, linux-arm-kernel, Radu Pirea

Hi,

This patch series improves the SPI driver for AT91 USART IP and
adds support for DMA transfers, updates the bindings of driver
and PM callback functions.

I decided to use DMA only for transfers long than 16 bytes. DMA
setup introduces a little overhead and is better to use PIO for
short transfers.

Radu Pirea (3):
  spi: at91-usart: add power management support
  dt-bindings: mfd: atmel-usart: add DMA bindings for SPI mode
  spi: at91-usart: add DMA support

 .../devicetree/bindings/mfd/atmel-usart.txt   |  15 +-
 drivers/spi/spi-at91-usart.c                  | 284 +++++++++++++++++-
 2 files changed, 292 insertions(+), 7 deletions(-)

-- 
2.19.1


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 1/3] spi: at91-usart: add power management support
  2018-11-21 11:27 [PATCH 0/3] Add PM and DMA support for AT91 USART as SPI Radu Pirea
@ 2018-11-21 11:27 ` Radu Pirea
  2018-11-28 16:05   ` Applied "spi: at91-usart: add power management support" to the spi tree Mark Brown
  2018-11-21 11:27 ` [PATCH 2/3] dt-bindings: mfd: atmel-usart: add DMA bindings for SPI mode Radu Pirea
  2018-11-21 11:27 ` [PATCH 3/3] spi: at91-usart: add DMA support Radu Pirea
  2 siblings, 1 reply; 12+ messages in thread
From: Radu Pirea @ 2018-11-21 11:27 UTC (permalink / raw)
  To: richard.genoud, lee.jones, robh+dt, mark.rutland, nicolas.ferre,
	alexandre.belloni, ludovic.desroches, broonie
  Cc: linux-kernel, linux-spi, devicetree, linux-arm-kernel, Radu Pirea

This patch implements power management callback function for USART as
SPI driver.

Signed-off-by: Radu Pirea <radu_nicolae.pirea@upb.ro>
---
 drivers/spi/spi-at91-usart.c | 61 ++++++++++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/drivers/spi/spi-at91-usart.c b/drivers/spi/spi-at91-usart.c
index a924657642fa..0b07c746453d 100644
--- a/drivers/spi/spi-at91-usart.c
+++ b/drivers/spi/spi-at91-usart.c
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/of_gpio.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 
 #include <linux/spi/spi.h>
 
@@ -399,6 +400,59 @@ static int at91_usart_spi_probe(struct platform_device *pdev)
 	return ret;
 }
 
+__maybe_unused static int at91_usart_spi_runtime_suspend(struct device *dev)
+{
+	struct spi_controller *ctlr = dev_get_drvdata(dev);
+	struct at91_usart_spi *aus = spi_master_get_devdata(ctlr);
+
+	clk_disable_unprepare(aus->clk);
+	pinctrl_pm_select_sleep_state(dev);
+
+	return 0;
+}
+
+__maybe_unused static int at91_usart_spi_runtime_resume(struct device *dev)
+{
+	struct spi_controller *ctrl = dev_get_drvdata(dev);
+	struct at91_usart_spi *aus = spi_master_get_devdata(ctrl);
+
+	pinctrl_pm_select_default_state(dev);
+
+	return clk_prepare_enable(aus->clk);
+}
+
+__maybe_unused static int at91_usart_spi_suspend(struct device *dev)
+{
+	struct spi_controller *ctrl = dev_get_drvdata(dev);
+	int ret;
+
+	ret = spi_controller_suspend(ctrl);
+	if (ret)
+		return ret;
+
+	if (!pm_runtime_suspended(dev))
+		at91_usart_spi_runtime_suspend(dev);
+
+	return 0;
+}
+
+__maybe_unused static int at91_usart_spi_resume(struct device *dev)
+{
+	struct spi_controller *ctrl = dev_get_drvdata(dev);
+	struct at91_usart_spi *aus = spi_master_get_devdata(ctrl);
+	int ret;
+
+	if (!pm_runtime_suspended(dev)) {
+		ret = at91_usart_spi_runtime_resume(dev);
+		if (ret)
+			return ret;
+	}
+
+	at91_usart_spi_init(aus);
+
+	return spi_controller_resume(ctrl);
+}
+
 static int at91_usart_spi_remove(struct platform_device *pdev)
 {
 	struct spi_controller *ctlr = platform_get_drvdata(pdev);
@@ -409,6 +463,12 @@ static int at91_usart_spi_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static const struct dev_pm_ops at91_usart_spi_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(at91_usart_spi_suspend, at91_usart_spi_resume)
+	SET_RUNTIME_PM_OPS(at91_usart_spi_runtime_suspend,
+			   at91_usart_spi_runtime_resume, NULL)
+};
+
 static const struct of_device_id at91_usart_spi_dt_ids[] = {
 	{ .compatible = "microchip,at91sam9g45-usart-spi"},
 	{ /* sentinel */}
@@ -419,6 +479,7 @@ MODULE_DEVICE_TABLE(of, at91_usart_spi_dt_ids);
 static struct platform_driver at91_usart_spi_driver = {
 	.driver = {
 		.name = "at91_usart_spi",
+		.pm = &at91_usart_spi_pm_ops,
 	},
 	.probe = at91_usart_spi_probe,
 	.remove = at91_usart_spi_remove,
-- 
2.19.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 2/3] dt-bindings: mfd: atmel-usart: add DMA bindings for SPI mode
  2018-11-21 11:27 [PATCH 0/3] Add PM and DMA support for AT91 USART as SPI Radu Pirea
  2018-11-21 11:27 ` [PATCH 1/3] spi: at91-usart: add power management support Radu Pirea
@ 2018-11-21 11:27 ` Radu Pirea
  2018-11-21 16:41   ` Rob Herring
  2018-11-21 11:27 ` [PATCH 3/3] spi: at91-usart: add DMA support Radu Pirea
  2 siblings, 1 reply; 12+ messages in thread
From: Radu Pirea @ 2018-11-21 11:27 UTC (permalink / raw)
  To: richard.genoud, lee.jones, robh+dt, mark.rutland, nicolas.ferre,
	alexandre.belloni, ludovic.desroches, broonie
  Cc: linux-kernel, linux-spi, devicetree, linux-arm-kernel, Radu Pirea

The bindings for DMA are now common for both drivers of the USART
IP.

The node given as an example for USART in SPI mode has been updated in
order to include DMA bindings.

Signed-off-by: Radu Pirea <radu_nicolae.pirea@upb.ro>
---
 .../devicetree/bindings/mfd/atmel-usart.txt       | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/Documentation/devicetree/bindings/mfd/atmel-usart.txt b/Documentation/devicetree/bindings/mfd/atmel-usart.txt
index 7f0cd72f47d2..8ad008175343 100644
--- a/Documentation/devicetree/bindings/mfd/atmel-usart.txt
+++ b/Documentation/devicetree/bindings/mfd/atmel-usart.txt
@@ -17,17 +17,19 @@ Required properties for USART in SPI mode:
 - cs-gpios: chipselects (internal cs not supported)
 - atmel,usart-mode : Must be <AT91_USART_MODE_SPI> (found in dt-bindings/mfd/at91-usart.h)
 
+Optional properties in serial and SPI mode:
+- dma bindings for dma transfer:
+	- dmas: DMA specifier, consisting of a phandle to DMA controller node,
+		memory peripheral interface and USART DMA channel ID, FIFO configuration.
+		Refer to dma.txt and atmel-dma.txt for details.
+	- dma-names: "rx" for RX channel, "tx" for TX channel.
+
 Optional properties in serial mode:
 - atmel,use-dma-rx: use of PDC or DMA for receiving data
 - atmel,use-dma-tx: use of PDC or DMA for transmitting data
 - {rts,cts,dtr,dsr,rng,dcd}-gpios: specify a GPIO for RTS/CTS/DTR/DSR/RI/DCD line respectively.
   It will use specified PIO instead of the peripheral function pin for the USART feature.
   If unsure, don't specify this property.
-- add dma bindings for dma transfer:
-	- dmas: DMA specifier, consisting of a phandle to DMA controller node,
-		memory peripheral interface and USART DMA channel ID, FIFO configuration.
-		Refer to dma.txt and atmel-dma.txt for details.
-	- dma-names: "rx" for RX channel, "tx" for TX channel.
 - atmel,fifo-size: maximum number of data the RX and TX FIFOs can store for FIFO
   capable USARTs.
 - rs485-rts-delay, rs485-rx-during-tx, linux,rs485-enabled-at-boot-time: see rs485.txt
@@ -81,5 +83,8 @@ Example:
 		interrupts = <12 IRQ_TYPE_LEVEL_HIGH 5>;
 		clocks = <&usart0_clk>;
 		clock-names = "usart";
+		dmas = <&dma0 2 AT91_DMA_CFG_PER_ID(3)>,
+		       <&dma0 2 (AT91_DMA_CFG_PER_ID(4) | AT91_DMA_CFG_FIFOCFG_ASAP)>;
+		dma-names = "tx", "rx";
 		cs-gpios = <&pioB 3 0>;
 	};
-- 
2.19.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 3/3] spi: at91-usart: add DMA support
  2018-11-21 11:27 [PATCH 0/3] Add PM and DMA support for AT91 USART as SPI Radu Pirea
  2018-11-21 11:27 ` [PATCH 1/3] spi: at91-usart: add power management support Radu Pirea
  2018-11-21 11:27 ` [PATCH 2/3] dt-bindings: mfd: atmel-usart: add DMA bindings for SPI mode Radu Pirea
@ 2018-11-21 11:27 ` Radu Pirea
  2018-11-21 17:38   ` Robin Murphy
  2 siblings, 1 reply; 12+ messages in thread
From: Radu Pirea @ 2018-11-21 11:27 UTC (permalink / raw)
  To: richard.genoud, lee.jones, robh+dt, mark.rutland, nicolas.ferre,
	alexandre.belloni, ludovic.desroches, broonie
  Cc: linux-kernel, linux-spi, devicetree, linux-arm-kernel, Radu Pirea

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 <radu_nicolae.pirea@upb.ro>
---
 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 @@
 
 #include <linux/clk.h>
 #include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-direction.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/of_platform.h>
 #include <linux/of_gpio.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
@@ -58,6 +61,8 @@
 
 #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))
 
 /* Register access macros */
 #define at91_usart_spi_readl(port, reg) \
@@ -71,14 +76,19 @@
 	writeb_relaxed((value), (port)->regs + US_##reg)
 
 struct at91_usart_spi {
+	struct platform_device  *mpdev;
 	struct spi_transfer	*current_transfer;
 	void __iomem		*regs;
 	struct device		*dev;
 	struct clk		*clk;
 
+	struct completion	xfer_completion;
+
 	/*used in interrupt to protect data reading*/
 	spinlock_t		lock;
 
+	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;
 
 	bool			xfer_failed;
+	bool			use_dma;
 };
 
+static void dma_callback(void *data)
+{
+	struct spi_controller   *ctlr = data;
+	struct at91_usart_spi   *aus = spi_master_get_devdata(ctlr);
+
+	at91_usart_spi_writel(aus, IER, US_IR_RXRDY);
+	aus->current_rx_remaining_bytes = 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 = spi_master_get_devdata(ctrl);
+
+	return aus->use_dma && xfer->len >= 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 = &aus->mpdev->dev;
+	phys_addr_t phybase = aus->phybase;
+	dma_cap_mask_t mask;
+	int err = 0;
+
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	ctlr->dma_tx = dma_request_slave_channel_reason(dev, "tx");
+	if (IS_ERR_OR_NULL(ctlr->dma_tx)) {
+		if (IS_ERR(ctlr->dma_tx)) {
+			err = 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 = -EBUSY;
+		goto at91_usart_spi_error_clear;
+	}
+
+	ctlr->dma_rx = dma_request_slave_channel_reason(dev, "rx");
+	if (IS_ERR_OR_NULL(ctlr->dma_rx)) {
+		if (IS_ERR(ctlr->dma_rx)) {
+			err = 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 = -EBUSY;
+		goto at91_usart_spi_error;
+	}
+
+	slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+	slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+	slave_config.dst_addr = (dma_addr_t)phybase + US_THR;
+	slave_config.src_addr = (dma_addr_t)phybase + US_RHR;
+	slave_config.src_maxburst = 1;
+	slave_config.dst_maxburst = 1;
+	slave_config.device_fc = false;
+
+	slave_config.direction = 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 = -EINVAL;
+		goto at91_usart_spi_error;
+	}
+
+	slave_config.direction = 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 = -EINVAL;
+		goto at91_usart_spi_error;
+	}
+
+	aus->use_dma = 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 = NULL;
+	ctlr->dma_rx = 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 = spi_master_get_devdata(ctlr);
+	struct dma_chan	 *rxchan = ctlr->dma_rx;
+	struct dma_chan *txchan = ctlr->dma_tx;
+	struct dma_async_tx_descriptor *rxdesc;
+	struct dma_async_tx_descriptor *txdesc;
+	dma_cookie_t cookie;
+
+	rxdesc = 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 = 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 = dma_callback;
+	rxdesc->callback_param = ctlr;
+
+	cookie = rxdesc->tx_submit(rxdesc);
+	if (dma_submit_error(cookie))
+		goto at91_usart_spi_err_dma;
+
+	cookie = 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 *aus)
+{
+	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_controller *ctlr,
 				       struct spi_transfer *xfer)
 {
 	struct at91_usart_spi *aus = spi_master_get_devdata(ctlr);
+	unsigned long dma_timeout = 0;
+	int ret = 0;
 
 	at91_usart_spi_set_xfer_speed(aus, xfer);
 	aus->xfer_failed = false;
@@ -230,8 +418,25 @@ static int at91_usart_spi_transfer_one(struct spi_controller *ctlr,
 
 	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 = at91_usart_spi_dma_transfer(ctlr, xfer);
+			if (ret)
+				continue;
+
+			dma_timeout = at91_usart_spi_dma_timeout(aus);
+
+			if (WARN_ON(dma_timeout == 0)) {
+				dev_err(&spi->dev, "DMA transfer timeout\n");
+				return -EIO;
+			}
+			aus->current_tx_remaining_bytes = 0;
+		} else {
+			at91_usart_spi_read_status(aus);
+			at91_usart_spi_tx(aus);
+		}
+
 		cpu_relax();
 	}
 
@@ -350,6 +555,7 @@ static int at91_usart_spi_probe(struct platform_device *pdev)
 	controller->transfer_one = at91_usart_spi_transfer_one;
 	controller->prepare_message = at91_usart_spi_prepare_message;
 	controller->unprepare_message = at91_usart_spi_unprepare_message;
+	controller->can_dma = at91_usart_spi_can_dma;
 	controller->cleanup = at91_usart_spi_cleanup;
 	controller->max_speed_hz = DIV_ROUND_UP(clk_get_rate(clk),
 						US_MIN_CLK_DIV);
@@ -381,7 +587,17 @@ static int at91_usart_spi_probe(struct platform_device *pdev)
 	aus->spi_clk = clk_get_rate(clk);
 	at91_usart_spi_init(aus);
 
+	aus->phybase = regs->start;
+
+	aus->mpdev = to_platform_device(pdev->dev.parent);
+
+	ret = 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 = 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_device *pdev)
 	return 0;
 
 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_device *pdev)
 	struct spi_controller *ctlr = platform_get_drvdata(pdev);
 	struct at91_usart_spi *aus = spi_master_get_devdata(ctlr);
 
+	at91_usart_spi_release_dma(ctlr);
 	clk_disable_unprepare(aus->clk);
 
 	return 0;
-- 
2.19.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH 2/3] dt-bindings: mfd: atmel-usart: add DMA bindings for SPI mode
  2018-11-21 11:27 ` [PATCH 2/3] dt-bindings: mfd: atmel-usart: add DMA bindings for SPI mode Radu Pirea
@ 2018-11-21 16:41   ` Rob Herring
  2018-11-21 16:48     ` Alexandre Belloni
  2018-11-23 16:07     ` Radu Nicolae Pirea
  0 siblings, 2 replies; 12+ messages in thread
From: Rob Herring @ 2018-11-21 16:41 UTC (permalink / raw)
  To: radu_nicolae.pirea
  Cc: Richard Genoud, Lee Jones, Mark Rutland, Nicolas Ferre,
	Alexandre Belloni, Ludovic Desroches, Mark Brown, devicetree,
	linux-kernel,
	moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
	linux-spi

On Wed, Nov 21, 2018 at 5:29 AM Radu Pirea <radu_nicolae.pirea@upb.ro> wrote:
>
> The bindings for DMA are now common for both drivers of the USART
> IP.
>
> The node given as an example for USART in SPI mode has been updated in
> order to include DMA bindings.
>
> Signed-off-by: Radu Pirea <radu_nicolae.pirea@upb.ro>
> ---
>  .../devicetree/bindings/mfd/atmel-usart.txt       | 15 ++++++++++-----
>  1 file changed, 10 insertions(+), 5 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/mfd/atmel-usart.txt b/Documentation/devicetree/bindings/mfd/atmel-usart.txt
> index 7f0cd72f47d2..8ad008175343 100644
> --- a/Documentation/devicetree/bindings/mfd/atmel-usart.txt
> +++ b/Documentation/devicetree/bindings/mfd/atmel-usart.txt
> @@ -17,17 +17,19 @@ Required properties for USART in SPI mode:
>  - cs-gpios: chipselects (internal cs not supported)
>  - atmel,usart-mode : Must be <AT91_USART_MODE_SPI> (found in dt-bindings/mfd/at91-usart.h)
>
> +Optional properties in serial and SPI mode:
> +- dma bindings for dma transfer:
> +       - dmas: DMA specifier, consisting of a phandle to DMA controller node,
> +               memory peripheral interface and USART DMA channel ID, FIFO configuration.
> +               Refer to dma.txt and atmel-dma.txt for details.
> +       - dma-names: "rx" for RX channel, "tx" for TX channel.

> +               dma-names = "tx", "rx";

The dma-names should have a defined order.

Rob

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 2/3] dt-bindings: mfd: atmel-usart: add DMA bindings for SPI mode
  2018-11-21 16:41   ` Rob Herring
@ 2018-11-21 16:48     ` Alexandre Belloni
  2018-11-21 20:52       ` Rob Herring
  2018-11-23 16:07     ` Radu Nicolae Pirea
  1 sibling, 1 reply; 12+ messages in thread
From: Alexandre Belloni @ 2018-11-21 16:48 UTC (permalink / raw)
  To: Rob Herring
  Cc: radu_nicolae.pirea, Richard Genoud, Lee Jones, Mark Rutland,
	Nicolas Ferre, Ludovic Desroches, Mark Brown, devicetree,
	linux-kernel,
	moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
	linux-spi

Hi Rob,

On 21/11/2018 10:41:01-0600, Rob Herring wrote:
> > +Optional properties in serial and SPI mode:
> > +- dma bindings for dma transfer:
> > +       - dmas: DMA specifier, consisting of a phandle to DMA controller node,
> > +               memory peripheral interface and USART DMA channel ID, FIFO configuration.
> > +               Refer to dma.txt and atmel-dma.txt for details.
> > +       - dma-names: "rx" for RX channel, "tx" for TX channel.
> 
> > +               dma-names = "tx", "rx";
> 
> The dma-names should have a defined order.
> 

Why is that? Isn't the purpose of names to get rid of any particular
ordering?


-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 3/3] spi: at91-usart: add DMA support
  2018-11-21 11:27 ` [PATCH 3/3] spi: at91-usart: add DMA support Radu Pirea
@ 2018-11-21 17:38   ` Robin Murphy
  2018-11-23 16:19     ` Radu Nicolae Pirea
  0 siblings, 1 reply; 12+ messages in thread
From: Robin Murphy @ 2018-11-21 17:38 UTC (permalink / raw)
  To: Radu Pirea, richard.genoud, lee.jones, robh+dt, mark.rutland,
	nicolas.ferre, alexandre.belloni, ludovic.desroches, broonie
  Cc: devicetree, linux-kernel, linux-arm-kernel, linux-spi

On 21/11/2018 11:27, Radu Pirea wrote:
> 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 <radu_nicolae.pirea@upb.ro>
> ---
>   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 @@
>   
>   #include <linux/clk.h>
>   #include <linux/delay.h>
> +#include <linux/dmaengine.h>
> +#include <linux/dma-direction.h>

It looks rather odd to include this from a driver that isn't otherwise 
touching anything from linux/dma-mapping.h.

>   #include <linux/interrupt.h>
>   #include <linux/kernel.h>
>   #include <linux/module.h>
> +#include <linux/of_platform.h>
>   #include <linux/of_gpio.h>
>   #include <linux/platform_device.h>
>   #include <linux/pm_runtime.h>
> @@ -58,6 +61,8 @@
>   
>   #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))
>   
>   /* Register access macros */
>   #define at91_usart_spi_readl(port, reg) \
> @@ -71,14 +76,19 @@
>   	writeb_relaxed((value), (port)->regs + US_##reg)
>   
>   struct at91_usart_spi {
> +	struct platform_device  *mpdev;
>   	struct spi_transfer	*current_transfer;
>   	void __iomem		*regs;
>   	struct device		*dev;
>   	struct clk		*clk;
>   
> +	struct completion	xfer_completion;
> +
>   	/*used in interrupt to protect data reading*/
>   	spinlock_t		lock;
>   
> +	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;
>   
>   	bool			xfer_failed;
> +	bool			use_dma;
>   };
>   
> +static void dma_callback(void *data)
> +{
> +	struct spi_controller   *ctlr = data;
> +	struct at91_usart_spi   *aus = spi_master_get_devdata(ctlr);
> +
> +	at91_usart_spi_writel(aus, IER, US_IR_RXRDY);
> +	aus->current_rx_remaining_bytes = 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 = spi_master_get_devdata(ctrl);
> +
> +	return aus->use_dma && xfer->len >= 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 = &aus->mpdev->dev;
> +	phys_addr_t phybase = aus->phybase;
> +	dma_cap_mask_t mask;
> +	int err = 0;
> +
> +	dma_cap_zero(mask);
> +	dma_cap_set(DMA_SLAVE, mask);
> +
> +	ctlr->dma_tx = dma_request_slave_channel_reason(dev, "tx");
> +	if (IS_ERR_OR_NULL(ctlr->dma_tx)) {
> +		if (IS_ERR(ctlr->dma_tx)) {
> +			err = 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 = -EBUSY;
> +		goto at91_usart_spi_error_clear;
> +	}
> +
> +	ctlr->dma_rx = dma_request_slave_channel_reason(dev, "rx");
> +	if (IS_ERR_OR_NULL(ctlr->dma_rx)) {
> +		if (IS_ERR(ctlr->dma_rx)) {
> +			err = 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 = -EBUSY;
> +		goto at91_usart_spi_error;
> +	}
> +
> +	slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
> +	slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
> +	slave_config.dst_addr = (dma_addr_t)phybase + US_THR;
> +	slave_config.src_addr = (dma_addr_t)phybase + US_RHR;
> +	slave_config.src_maxburst = 1;
> +	slave_config.dst_maxburst = 1;
> +	slave_config.device_fc = false;
> +
> +	slave_config.direction = 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 = -EINVAL;
> +		goto at91_usart_spi_error;
> +	}
> +
> +	slave_config.direction = 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 = -EINVAL;
> +		goto at91_usart_spi_error;
> +	}
> +
> +	aus->use_dma = 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 = NULL;
> +	ctlr->dma_rx = 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 = spi_master_get_devdata(ctlr);
> +	struct dma_chan	 *rxchan = ctlr->dma_rx;
> +	struct dma_chan *txchan = ctlr->dma_tx;
> +	struct dma_async_tx_descriptor *rxdesc;
> +	struct dma_async_tx_descriptor *txdesc;
> +	dma_cookie_t cookie;
> +
> +	rxdesc = dmaengine_prep_slave_sg(rxchan,
> +					 xfer->rx_sg.sgl,
> +					 xfer->rx_sg.nents,
> +					 DMA_FROM_DEVICE,

Ah, this argument should be a dma_transfer_direction, not a 
dma_data_direction (confusing I know, but they belong to different 
APIs). I assume you mean DMA_DEV_TO_MEM here...

> +					 DMA_PREP_INTERRUPT |
> +					 DMA_CTRL_ACK);
> +	if (!rxdesc)
> +		goto at91_usart_spi_err_dma;
> +
> +	txdesc = dmaengine_prep_slave_sg(txchan,
> +					 xfer->tx_sg.sgl,
> +					 xfer->tx_sg.nents,
> +					 DMA_TO_DEVICE,

...and DMA_MEM_TO_DEV here.

Robin.

> +					 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 = dma_callback;
> +	rxdesc->callback_param = ctlr;
> +
> +	cookie = rxdesc->tx_submit(rxdesc);
> +	if (dma_submit_error(cookie))
> +		goto at91_usart_spi_err_dma;
> +
> +	cookie = 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 *aus)
> +{
> +	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_controller *ctlr,
>   				       struct spi_transfer *xfer)
>   {
>   	struct at91_usart_spi *aus = spi_master_get_devdata(ctlr);
> +	unsigned long dma_timeout = 0;
> +	int ret = 0;
>   
>   	at91_usart_spi_set_xfer_speed(aus, xfer);
>   	aus->xfer_failed = false;
> @@ -230,8 +418,25 @@ static int at91_usart_spi_transfer_one(struct spi_controller *ctlr,
>   
>   	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 = at91_usart_spi_dma_transfer(ctlr, xfer);
> +			if (ret)
> +				continue;
> +
> +			dma_timeout = at91_usart_spi_dma_timeout(aus);
> +
> +			if (WARN_ON(dma_timeout == 0)) {
> +				dev_err(&spi->dev, "DMA transfer timeout\n");
> +				return -EIO;
> +			}
> +			aus->current_tx_remaining_bytes = 0;
> +		} else {
> +			at91_usart_spi_read_status(aus);
> +			at91_usart_spi_tx(aus);
> +		}
> +
>   		cpu_relax();
>   	}
>   
> @@ -350,6 +555,7 @@ static int at91_usart_spi_probe(struct platform_device *pdev)
>   	controller->transfer_one = at91_usart_spi_transfer_one;
>   	controller->prepare_message = at91_usart_spi_prepare_message;
>   	controller->unprepare_message = at91_usart_spi_unprepare_message;
> +	controller->can_dma = at91_usart_spi_can_dma;
>   	controller->cleanup = at91_usart_spi_cleanup;
>   	controller->max_speed_hz = DIV_ROUND_UP(clk_get_rate(clk),
>   						US_MIN_CLK_DIV);
> @@ -381,7 +587,17 @@ static int at91_usart_spi_probe(struct platform_device *pdev)
>   	aus->spi_clk = clk_get_rate(clk);
>   	at91_usart_spi_init(aus);
>   
> +	aus->phybase = regs->start;
> +
> +	aus->mpdev = to_platform_device(pdev->dev.parent);
> +
> +	ret = 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 = 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_device *pdev)
>   	return 0;
>   
>   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_device *pdev)
>   	struct spi_controller *ctlr = platform_get_drvdata(pdev);
>   	struct at91_usart_spi *aus = spi_master_get_devdata(ctlr);
>   
> +	at91_usart_spi_release_dma(ctlr);
>   	clk_disable_unprepare(aus->clk);
>   
>   	return 0;
> 

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 2/3] dt-bindings: mfd: atmel-usart: add DMA bindings for SPI mode
  2018-11-21 16:48     ` Alexandre Belloni
@ 2018-11-21 20:52       ` Rob Herring
  2018-11-21 21:21         ` Alexandre Belloni
  0 siblings, 1 reply; 12+ messages in thread
From: Rob Herring @ 2018-11-21 20:52 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: radu_nicolae.pirea, Richard Genoud, Lee Jones, Mark Rutland,
	Nicolas Ferre, Ludovic Desroches, Mark Brown, devicetree,
	linux-kernel,
	moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
	linux-spi

On Wed, Nov 21, 2018 at 10:48 AM Alexandre Belloni
<alexandre.belloni@bootlin.com> wrote:
>
> Hi Rob,
>
> On 21/11/2018 10:41:01-0600, Rob Herring wrote:
> > > +Optional properties in serial and SPI mode:
> > > +- dma bindings for dma transfer:
> > > +       - dmas: DMA specifier, consisting of a phandle to DMA controller node,
> > > +               memory peripheral interface and USART DMA channel ID, FIFO configuration.
> > > +               Refer to dma.txt and atmel-dma.txt for details.
> > > +       - dma-names: "rx" for RX channel, "tx" for TX channel.
> >
> > > +               dma-names = "tx", "rx";
> >
> > The dma-names should have a defined order.
> >
>
> Why is that? Isn't the purpose of names to get rid of any particular
> ordering?

Because a fundamental rule of DT is the order is defined and
important. The purpose was to allow for cases that had a variable
number of entries, but even a variable number still have a defined
order.

Rob

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 2/3] dt-bindings: mfd: atmel-usart: add DMA bindings for SPI mode
  2018-11-21 20:52       ` Rob Herring
@ 2018-11-21 21:21         ` Alexandre Belloni
  0 siblings, 0 replies; 12+ messages in thread
From: Alexandre Belloni @ 2018-11-21 21:21 UTC (permalink / raw)
  To: Rob Herring
  Cc: radu_nicolae.pirea, Richard Genoud, Lee Jones, Mark Rutland,
	Nicolas Ferre, Ludovic Desroches, Mark Brown, devicetree,
	linux-kernel,
	moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
	linux-spi

On 21/11/2018 14:52:29-0600, Rob Herring wrote:
> On Wed, Nov 21, 2018 at 10:48 AM Alexandre Belloni
> <alexandre.belloni@bootlin.com> wrote:
> >
> > Hi Rob,
> >
> > On 21/11/2018 10:41:01-0600, Rob Herring wrote:
> > > > +Optional properties in serial and SPI mode:
> > > > +- dma bindings for dma transfer:
> > > > +       - dmas: DMA specifier, consisting of a phandle to DMA controller node,
> > > > +               memory peripheral interface and USART DMA channel ID, FIFO configuration.
> > > > +               Refer to dma.txt and atmel-dma.txt for details.
> > > > +       - dma-names: "rx" for RX channel, "tx" for TX channel.
> > >
> > > > +               dma-names = "tx", "rx";
> > >
> > > The dma-names should have a defined order.
> > >
> >
> > Why is that? Isn't the purpose of names to get rid of any particular
> > ordering?
> 
> Because a fundamental rule of DT is the order is defined and
> important. The purpose was to allow for cases that had a variable
> number of entries, but even a variable number still have a defined
> order.
> 

Thank you for the clarification!


-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 2/3] dt-bindings: mfd: atmel-usart: add DMA bindings for SPI mode
  2018-11-21 16:41   ` Rob Herring
  2018-11-21 16:48     ` Alexandre Belloni
@ 2018-11-23 16:07     ` Radu Nicolae Pirea
  1 sibling, 0 replies; 12+ messages in thread
From: Radu Nicolae Pirea @ 2018-11-23 16:07 UTC (permalink / raw)
  To: Rob Herring
  Cc: Richard Genoud, Lee Jones, Mark Rutland, Nicolas Ferre,
	Alexandre Belloni, Ludovic Desroches, Mark Brown, devicetree,
	linux-kernel,
	moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
	linux-spi

On Wed, 2018-11-21 at 10:41 -0600, Rob Herring wrote:
> On Wed, Nov 21, 2018 at 5:29 AM Radu Pirea <radu_nicolae.pirea@upb.ro
> > wrote:
> > The bindings for DMA are now common for both drivers of the USART
> > IP.
> > 
> > The node given as an example for USART in SPI mode has been updated
> > in
> > order to include DMA bindings.
> > 
> > Signed-off-by: Radu Pirea <radu_nicolae.pirea@upb.ro>
> > ---
> >  .../devicetree/bindings/mfd/atmel-usart.txt       | 15 ++++++++++-
> > ----
> >  1 file changed, 10 insertions(+), 5 deletions(-)
> > 
> > diff --git a/Documentation/devicetree/bindings/mfd/atmel-usart.txt
> > b/Documentation/devicetree/bindings/mfd/atmel-usart.txt
> > index 7f0cd72f47d2..8ad008175343 100644
> > --- a/Documentation/devicetree/bindings/mfd/atmel-usart.txt
> > +++ b/Documentation/devicetree/bindings/mfd/atmel-usart.txt
> > @@ -17,17 +17,19 @@ Required properties for USART in SPI mode:
> >  - cs-gpios: chipselects (internal cs not supported)
> >  - atmel,usart-mode : Must be <AT91_USART_MODE_SPI> (found in dt-
> > bindings/mfd/at91-usart.h)
> > 
> > +Optional properties in serial and SPI mode:
> > +- dma bindings for dma transfer:
> > +       - dmas: DMA specifier, consisting of a phandle to DMA
> > controller node,
> > +               memory peripheral interface and USART DMA channel
> > ID, FIFO configuration.
> > +               Refer to dma.txt and atmel-dma.txt for details.
> > +       - dma-names: "rx" for RX channel, "tx" for TX channel.
> > +               dma-names = "tx", "rx";
> 
> The dma-names should have a defined order.

Thanks Rob.
I will specify in bindings a fixed order for dmas and dma-names.

> 
> Rob


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 3/3] spi: at91-usart: add DMA support
  2018-11-21 17:38   ` Robin Murphy
@ 2018-11-23 16:19     ` Radu Nicolae Pirea
  0 siblings, 0 replies; 12+ messages in thread
From: Radu Nicolae Pirea @ 2018-11-23 16:19 UTC (permalink / raw)
  To: Robin Murphy, richard.genoud, lee.jones, robh+dt, mark.rutland,
	nicolas.ferre, alexandre.belloni, ludovic.desroches, broonie
  Cc: devicetree, linux-kernel, linux-arm-kernel, linux-spi

On Wed, 2018-11-21 at 17:38 +0000, Robin Murphy wrote:
> On 21/11/2018 11:27, Radu Pirea wrote:
> > 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 <radu_nicolae.pirea@upb.ro>
> > ---
> >   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 @@
> >   
> >   #include <linux/clk.h>
> >   #include <linux/delay.h>
> > +#include <linux/dmaengine.h>
> > +#include <linux/dma-direction.h>
> 
> It looks rather odd to include this from a driver that isn't
> otherwise 
> touching anything from linux/dma-mapping.h.
> 
> >   #include <linux/interrupt.h>
> >   #include <linux/kernel.h>
> >   #include <linux/module.h>
> > +#include <linux/of_platform.h>
> >   #include <linux/of_gpio.h>
> >   #include <linux/platform_device.h>
> >   #include <linux/pm_runtime.h>
> > @@ -58,6 +61,8 @@
> >   
> >   #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))
> >   
> >   /* Register access macros */
> >   #define at91_usart_spi_readl(port, reg) \
> > @@ -71,14 +76,19 @@
> >   	writeb_relaxed((value), (port)->regs + US_##reg)
> >   
> >   struct at91_usart_spi {
> > +	struct platform_device  *mpdev;
> >   	struct spi_transfer	*current_transfer;
> >   	void __iomem		*regs;
> >   	struct device		*dev;
> >   	struct clk		*clk;
> >   
> > +	struct completion	xfer_completion;
> > +
> >   	/*used in interrupt to protect data reading*/
> >   	spinlock_t		lock;
> >   
> > +	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;
> >   
> >   	bool			xfer_failed;
> > +	bool			use_dma;
> >   };
> >   
> > +static void dma_callback(void *data)
> > +{
> > +	struct spi_controller   *ctlr = data;
> > +	struct at91_usart_spi   *aus = spi_master_get_devdata(ctlr);
> > +
> > +	at91_usart_spi_writel(aus, IER, US_IR_RXRDY);
> > +	aus->current_rx_remaining_bytes = 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 = spi_master_get_devdata(ctrl);
> > +
> > +	return aus->use_dma && xfer->len >= 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 = &aus->mpdev->dev;
> > +	phys_addr_t phybase = aus->phybase;
> > +	dma_cap_mask_t mask;
> > +	int err = 0;
> > +
> > +	dma_cap_zero(mask);
> > +	dma_cap_set(DMA_SLAVE, mask);
> > +
> > +	ctlr->dma_tx = dma_request_slave_channel_reason(dev, "tx");
> > +	if (IS_ERR_OR_NULL(ctlr->dma_tx)) {
> > +		if (IS_ERR(ctlr->dma_tx)) {
> > +			err = 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 = -EBUSY;
> > +		goto at91_usart_spi_error_clear;
> > +	}
> > +
> > +	ctlr->dma_rx = dma_request_slave_channel_reason(dev, "rx");
> > +	if (IS_ERR_OR_NULL(ctlr->dma_rx)) {
> > +		if (IS_ERR(ctlr->dma_rx)) {
> > +			err = 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 = -EBUSY;
> > +		goto at91_usart_spi_error;
> > +	}
> > +
> > +	slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
> > +	slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
> > +	slave_config.dst_addr = (dma_addr_t)phybase + US_THR;
> > +	slave_config.src_addr = (dma_addr_t)phybase + US_RHR;
> > +	slave_config.src_maxburst = 1;
> > +	slave_config.dst_maxburst = 1;
> > +	slave_config.device_fc = false;
> > +
> > +	slave_config.direction = 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 = -EINVAL;
> > +		goto at91_usart_spi_error;
> > +	}
> > +
> > +	slave_config.direction = 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 = -EINVAL;
> > +		goto at91_usart_spi_error;
> > +	}
> > +
> > +	aus->use_dma = 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 = NULL;
> > +	ctlr->dma_rx = 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 = spi_master_get_devdata(ctlr);
> > +	struct dma_chan	 *rxchan = ctlr->dma_rx;
> > +	struct dma_chan *txchan = ctlr->dma_tx;
> > +	struct dma_async_tx_descriptor *rxdesc;
> > +	struct dma_async_tx_descriptor *txdesc;
> > +	dma_cookie_t cookie;
> > +
> > +	rxdesc = dmaengine_prep_slave_sg(rxchan,
> > +					 xfer->rx_sg.sgl,
> > +					 xfer->rx_sg.nents,
> > +					 DMA_FROM_DEVICE,
> 
> Ah, this argument should be a dma_transfer_direction, not a 
> dma_data_direction (confusing I know, but they belong to different 
> APIs). I assume you mean DMA_DEV_TO_MEM here...

Hi Robin,

I hoped I used the correct values here, but it seems not.

Thanks. I will fix.

> 
> > +					 DMA_PREP_INTERRUPT |
> > +					 DMA_CTRL_ACK);
> > +	if (!rxdesc)
> > +		goto at91_usart_spi_err_dma;
> > +
> > +	txdesc = dmaengine_prep_slave_sg(txchan,
> > +					 xfer->tx_sg.sgl,
> > +					 xfer->tx_sg.nents,
> > +					 DMA_TO_DEVICE,
> 
> ...and DMA_MEM_TO_DEV here.
> 
> Robin.
> 
> > +					 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 = dma_callback;
> > +	rxdesc->callback_param = ctlr;
> > +
> > +	cookie = rxdesc->tx_submit(rxdesc);
> > +	if (dma_submit_error(cookie))
> > +		goto at91_usart_spi_err_dma;
> > +
> > +	cookie = 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 *aus)
> > +{
> > +	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_controller *ctlr,
> >   				       struct spi_transfer *xfer)
> >   {
> >   	struct at91_usart_spi *aus = spi_master_get_devdata(ctlr);
> > +	unsigned long dma_timeout = 0;
> > +	int ret = 0;
> >   
> >   	at91_usart_spi_set_xfer_speed(aus, xfer);
> >   	aus->xfer_failed = false;
> > @@ -230,8 +418,25 @@ static int at91_usart_spi_transfer_one(struct
> > spi_controller *ctlr,
> >   
> >   	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 = at91_usart_spi_dma_transfer(ctlr, xfer);
> > +			if (ret)
> > +				continue;
> > +
> > +			dma_timeout = at91_usart_spi_dma_timeout(aus);
> > +
> > +			if (WARN_ON(dma_timeout == 0)) {
> > +				dev_err(&spi->dev, "DMA transfer
> > timeout\n");
> > +				return -EIO;
> > +			}
> > +			aus->current_tx_remaining_bytes = 0;
> > +		} else {
> > +			at91_usart_spi_read_status(aus);
> > +			at91_usart_spi_tx(aus);
> > +		}
> > +
> >   		cpu_relax();
> >   	}
> >   
> > @@ -350,6 +555,7 @@ static int at91_usart_spi_probe(struct
> > platform_device *pdev)
> >   	controller->transfer_one = at91_usart_spi_transfer_one;
> >   	controller->prepare_message = at91_usart_spi_prepare_message;
> >   	controller->unprepare_message =
> > at91_usart_spi_unprepare_message;
> > +	controller->can_dma = at91_usart_spi_can_dma;
> >   	controller->cleanup = at91_usart_spi_cleanup;
> >   	controller->max_speed_hz = DIV_ROUND_UP(clk_get_rate(clk),
> >   						US_MIN_CLK_DIV);
> > @@ -381,7 +587,17 @@ static int at91_usart_spi_probe(struct
> > platform_device *pdev)
> >   	aus->spi_clk = clk_get_rate(clk);
> >   	at91_usart_spi_init(aus);
> >   
> > +	aus->phybase = regs->start;
> > +
> > +	aus->mpdev = to_platform_device(pdev->dev.parent);
> > +
> > +	ret = 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 = 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_device *pdev)
> >   	return 0;
> >   
> >   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_device *pdev)
> >   	struct spi_controller *ctlr = platform_get_drvdata(pdev);
> >   	struct at91_usart_spi *aus = spi_master_get_devdata(ctlr);
> >   
> > +	at91_usart_spi_release_dma(ctlr);
> >   	clk_disable_unprepare(aus->clk);
> >   
> >   	return 0;
> > 


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Applied "spi: at91-usart: add power management support" to the spi tree
  2018-11-21 11:27 ` [PATCH 1/3] spi: at91-usart: add power management support Radu Pirea
@ 2018-11-28 16:05   ` Mark Brown
  0 siblings, 0 replies; 12+ messages in thread
From: Mark Brown @ 2018-11-28 16:05 UTC (permalink / raw)
  To: Radu Pirea
  Cc: Mark Brown, richard.genoud, lee.jones, robh+dt, mark.rutland,
	nicolas.ferre, alexandre.belloni, ludovic.desroches, broonie,
	linux-kernel, linux-spi, devicetree, linux-arm-kernel, linux-spi

The patch

   spi: at91-usart: add power management support

has been applied to the spi tree at

   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

From 96ed3ecde2c0911ffa11838050a058c2a40742f0 Mon Sep 17 00:00:00 2001
From: Radu Pirea <radu_nicolae.pirea@upb.ro>
Date: Wed, 21 Nov 2018 13:27:30 +0200
Subject: [PATCH] spi: at91-usart: add power management support

This patch implements power management callback function for USART as
SPI driver.

Signed-off-by: Radu Pirea <radu_nicolae.pirea@upb.ro>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
 drivers/spi/spi-at91-usart.c | 61 ++++++++++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/drivers/spi/spi-at91-usart.c b/drivers/spi/spi-at91-usart.c
index a924657642fa..0b07c746453d 100644
--- a/drivers/spi/spi-at91-usart.c
+++ b/drivers/spi/spi-at91-usart.c
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/of_gpio.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 
 #include <linux/spi/spi.h>
 
@@ -399,6 +400,59 @@ static int at91_usart_spi_probe(struct platform_device *pdev)
 	return ret;
 }
 
+__maybe_unused static int at91_usart_spi_runtime_suspend(struct device *dev)
+{
+	struct spi_controller *ctlr = dev_get_drvdata(dev);
+	struct at91_usart_spi *aus = spi_master_get_devdata(ctlr);
+
+	clk_disable_unprepare(aus->clk);
+	pinctrl_pm_select_sleep_state(dev);
+
+	return 0;
+}
+
+__maybe_unused static int at91_usart_spi_runtime_resume(struct device *dev)
+{
+	struct spi_controller *ctrl = dev_get_drvdata(dev);
+	struct at91_usart_spi *aus = spi_master_get_devdata(ctrl);
+
+	pinctrl_pm_select_default_state(dev);
+
+	return clk_prepare_enable(aus->clk);
+}
+
+__maybe_unused static int at91_usart_spi_suspend(struct device *dev)
+{
+	struct spi_controller *ctrl = dev_get_drvdata(dev);
+	int ret;
+
+	ret = spi_controller_suspend(ctrl);
+	if (ret)
+		return ret;
+
+	if (!pm_runtime_suspended(dev))
+		at91_usart_spi_runtime_suspend(dev);
+
+	return 0;
+}
+
+__maybe_unused static int at91_usart_spi_resume(struct device *dev)
+{
+	struct spi_controller *ctrl = dev_get_drvdata(dev);
+	struct at91_usart_spi *aus = spi_master_get_devdata(ctrl);
+	int ret;
+
+	if (!pm_runtime_suspended(dev)) {
+		ret = at91_usart_spi_runtime_resume(dev);
+		if (ret)
+			return ret;
+	}
+
+	at91_usart_spi_init(aus);
+
+	return spi_controller_resume(ctrl);
+}
+
 static int at91_usart_spi_remove(struct platform_device *pdev)
 {
 	struct spi_controller *ctlr = platform_get_drvdata(pdev);
@@ -409,6 +463,12 @@ static int at91_usart_spi_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static const struct dev_pm_ops at91_usart_spi_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(at91_usart_spi_suspend, at91_usart_spi_resume)
+	SET_RUNTIME_PM_OPS(at91_usart_spi_runtime_suspend,
+			   at91_usart_spi_runtime_resume, NULL)
+};
+
 static const struct of_device_id at91_usart_spi_dt_ids[] = {
 	{ .compatible = "microchip,at91sam9g45-usart-spi"},
 	{ /* sentinel */}
@@ -419,6 +479,7 @@ MODULE_DEVICE_TABLE(of, at91_usart_spi_dt_ids);
 static struct platform_driver at91_usart_spi_driver = {
 	.driver = {
 		.name = "at91_usart_spi",
+		.pm = &at91_usart_spi_pm_ops,
 	},
 	.probe = at91_usart_spi_probe,
 	.remove = at91_usart_spi_remove,
-- 
2.19.0.rc2


^ permalink raw reply related	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2018-11-28 16:06 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-11-21 11:27 [PATCH 0/3] Add PM and DMA support for AT91 USART as SPI Radu Pirea
2018-11-21 11:27 ` [PATCH 1/3] spi: at91-usart: add power management support Radu Pirea
2018-11-28 16:05   ` Applied "spi: at91-usart: add power management support" to the spi tree Mark Brown
2018-11-21 11:27 ` [PATCH 2/3] dt-bindings: mfd: atmel-usart: add DMA bindings for SPI mode Radu Pirea
2018-11-21 16:41   ` Rob Herring
2018-11-21 16:48     ` Alexandre Belloni
2018-11-21 20:52       ` Rob Herring
2018-11-21 21:21         ` Alexandre Belloni
2018-11-23 16:07     ` Radu Nicolae Pirea
2018-11-21 11:27 ` [PATCH 3/3] spi: at91-usart: add DMA support Radu Pirea
2018-11-21 17:38   ` Robin Murphy
2018-11-23 16:19     ` Radu Nicolae Pirea

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).