From nobody Mon Dec 1 21:31:53 2025 Received: from relmlie5.idc.renesas.com (relmlor1.renesas.com [210.160.252.171]) by smtp.subspace.kernel.org (Postfix) with ESMTP id C13263164A8; Mon, 1 Dec 2025 13:44:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=210.160.252.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764596665; cv=none; b=Zu8NkZ7il3H75LMzVvst47LQWkPPaoLDpDQ1lgDXeSuHeGEm36zOIJpsLJBAB+hzJS1j9pr1ZOwieO6zSYLV095KCrcVXFbGvMMuSEG+cPJEJqhLhxdaIRgjMjDakf7uUJEg00DaiJXlPYksyIbZZQ1hU6y1nc6vsUxex8PXO80= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764596665; c=relaxed/simple; bh=gfZE5pDNxjjD/cfrOYBJmPe45UkIw4FE6AEb2aCh7fM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=GAh6i7RVlhVRQtiwUH791vBZxWZ+uaJP0+lqTWsweDA0n8sdVsQKyjdKbp+fU0x8ow4JDgj4fySfU4qebt4V7uGcKoEs2d0GhgnR5zUhD3iIDlhbUV/dZZKzr5+NPL5L30NDuMpXhCj+wCzkP0ddLjJg4vi/SFDal2WJZpRGhMA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=renesas.com; spf=pass smtp.mailfrom=renesas.com; arc=none smtp.client-ip=210.160.252.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=renesas.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=renesas.com X-CSE-ConnectionGUID: aBdb9Ha7ROSDbYGSYFRvtA== X-CSE-MsgGUID: dX5G5KoqR2mPVxB0Q2aokg== Received: from unknown (HELO relmlir6.idc.renesas.com) ([10.200.68.152]) by relmlie5.idc.renesas.com with ESMTP; 01 Dec 2025 22:44:18 +0900 Received: from demon-pc.localdomain (unknown [10.226.93.83]) by relmlir6.idc.renesas.com (Postfix) with ESMTP id E9AA44215A4E; Mon, 1 Dec 2025 22:44:13 +0900 (JST) From: Cosmin Tanislav To: Fabrizio Castro , Mark Brown , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Geert Uytterhoeven , Magnus Damm , Philipp Zabel Cc: linux-spi@vger.kernel.org, linux-renesas-soc@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Cosmin Tanislav Subject: [PATCH 11/13] spi: rzv2h-rspi: add support for DMA mode Date: Mon, 1 Dec 2025 15:42:27 +0200 Message-ID: <20251201134229.600817-12-cosmin-gabriel.tanislav.xa@renesas.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20251201134229.600817-1-cosmin-gabriel.tanislav.xa@renesas.com> References: <20251201134229.600817-1-cosmin-gabriel.tanislav.xa@renesas.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The DMA controller can be used to transfer data to and from the SPI controller without involving the CPU for each word of a SPI transfer. Add support for DMA mode. Signed-off-by: Cosmin Tanislav --- drivers/spi/spi-rzv2h-rspi.c | 169 ++++++++++++++++++++++++++++++++++- 1 file changed, 168 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-rzv2h-rspi.c b/drivers/spi/spi-rzv2h-rspi.c index 9f5bc047b485..aae916882915 100644 --- a/drivers/spi/spi-rzv2h-rspi.c +++ b/drivers/spi/spi-rzv2h-rspi.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,8 @@ #include #include =20 +#include "internals.h" + /* Registers */ #define RSPI_SPDR 0x00 #define RSPI_SPCR 0x08 @@ -96,6 +99,7 @@ struct rzv2h_rspi_info { struct rzv2h_rspi_priv { struct spi_controller *controller; const struct rzv2h_rspi_info *info; + struct platform_device *pdev; void __iomem *base; struct clk *tclk; struct clk *pclk; @@ -108,6 +112,7 @@ struct rzv2h_rspi_priv { u8 spr; u8 brdv; bool use_pclk; + bool dma_callbacked; }; =20 #define RZV2H_RSPI_TX(func, type) \ @@ -219,6 +224,20 @@ static int rzv2h_rspi_receive(struct rzv2h_rspi_priv *= rspi, void *rxbuf, return 0; } =20 +static bool rzv2h_rspi_can_dma(struct spi_controller *ctlr, struct spi_dev= ice *spi, + struct spi_transfer *xfer) +{ + struct rzv2h_rspi_priv *rspi =3D spi_controller_get_devdata(ctlr); + + if (ctlr->fallback) + return false; + + if (!ctlr->dma_tx || !ctlr->dma_rx) + return false; + + return xfer->len > rspi->info->fifo_size; +} + static int rzv2h_rspi_transfer_pio(struct rzv2h_rspi_priv *rspi, struct spi_device *spi, struct spi_transfer *transfer, @@ -240,21 +259,149 @@ static int rzv2h_rspi_transfer_pio(struct rzv2h_rspi= _priv *rspi, return ret; } =20 +static void rzv2h_rspi_dma_complete(void *arg) +{ + struct rzv2h_rspi_priv *rspi =3D arg; + + rspi->dma_callbacked =3D 1; + wake_up_interruptible(&rspi->wait); +} + +static struct dma_async_tx_descriptor * +rzv2h_rspi_setup_dma_channel(struct rzv2h_rspi_priv *rspi, + struct dma_chan *chan, struct sg_table *sg, + enum dma_slave_buswidth width, + enum dma_transfer_direction direction) +{ + struct dma_slave_config config =3D { + .dst_addr =3D rspi->pdev->resource->start + RSPI_SPDR, + .src_addr =3D rspi->pdev->resource->start + RSPI_SPDR, + .dst_addr_width =3D width, + .src_addr_width =3D width, + .direction =3D direction, + }; + struct dma_async_tx_descriptor *desc; + int ret; + + ret =3D dmaengine_slave_config(chan, &config); + if (ret) + return ERR_PTR(ret); + + desc =3D dmaengine_prep_slave_sg(chan, sg->sgl, sg->nents, direction, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) + return ERR_PTR(-EAGAIN); + + if (direction =3D=3D DMA_DEV_TO_MEM) { + desc->callback =3D rzv2h_rspi_dma_complete; + desc->callback_param =3D rspi; + } + + return desc; +} + +static enum dma_slave_buswidth +rzv2h_rspi_dma_width(struct rzv2h_rspi_priv *rspi) +{ + switch (rspi->bytes_per_word) { + case 4: + return DMA_SLAVE_BUSWIDTH_4_BYTES; + case 2: + return DMA_SLAVE_BUSWIDTH_2_BYTES; + case 1: + return DMA_SLAVE_BUSWIDTH_1_BYTE; + default: + return DMA_SLAVE_BUSWIDTH_UNDEFINED; + } +} + +static int rzv2h_rspi_transfer_dma(struct rzv2h_rspi_priv *rspi, + struct spi_device *spi, + struct spi_transfer *transfer, + unsigned int words_to_transfer) +{ + struct dma_async_tx_descriptor *tx_desc =3D NULL, *rx_desc =3D NULL; + enum dma_slave_buswidth width; + dma_cookie_t cookie; + int ret; + + width =3D rzv2h_rspi_dma_width(rspi); + if (width =3D=3D DMA_SLAVE_BUSWIDTH_UNDEFINED) + return -EINVAL; + + rx_desc =3D rzv2h_rspi_setup_dma_channel(rspi, rspi->controller->dma_rx, + &transfer->rx_sg, width, + DMA_DEV_TO_MEM); + if (IS_ERR(rx_desc)) + return PTR_ERR(rx_desc); + + tx_desc =3D rzv2h_rspi_setup_dma_channel(rspi, rspi->controller->dma_tx, + &transfer->tx_sg, width, + DMA_MEM_TO_DEV); + if (IS_ERR(tx_desc)) + return PTR_ERR(tx_desc); + + cookie =3D dmaengine_submit(rx_desc); + if (dma_submit_error(cookie)) + return cookie; + + cookie =3D dmaengine_submit(tx_desc); + if (dma_submit_error(cookie)) { + dmaengine_terminate_sync(rspi->controller->dma_rx); + return cookie; + } + + /* + * DMA transfer does not need IRQs to be enabled. + * For PIO, we only use RX IRQ, so disable that. + */ + disable_irq(rspi->irq_rx); + + rspi->dma_callbacked =3D 0; + + dma_async_issue_pending(rspi->controller->dma_rx); + dma_async_issue_pending(rspi->controller->dma_tx); + rzv2h_rspi_clear_all_irqs(rspi); + + ret =3D wait_event_interruptible_timeout(rspi->wait, rspi->dma_callbacked= , HZ); + if (ret) { + dmaengine_synchronize(rspi->controller->dma_tx); + dmaengine_synchronize(rspi->controller->dma_rx); + ret =3D 0; + } else { + dmaengine_terminate_sync(rspi->controller->dma_tx); + dmaengine_terminate_sync(rspi->controller->dma_rx); + ret =3D -ETIMEDOUT; + } + + enable_irq(rspi->irq_rx); + + return ret; +} + static int rzv2h_rspi_transfer_one(struct spi_controller *controller, struct spi_device *spi, struct spi_transfer *transfer) { struct rzv2h_rspi_priv *rspi =3D spi_controller_get_devdata(controller); + bool is_dma =3D spi_xfer_is_dma_mapped(controller, spi, transfer); unsigned int words_to_transfer; int ret; =20 transfer->effective_speed_hz =3D rspi->freq; words_to_transfer =3D transfer->len / rspi->bytes_per_word; =20 - ret =3D rzv2h_rspi_transfer_pio(rspi, spi, transfer, words_to_transfer); + if (is_dma) + ret =3D rzv2h_rspi_transfer_dma(rspi, spi, transfer, words_to_transfer); + else + ret =3D rzv2h_rspi_transfer_pio(rspi, spi, transfer, words_to_transfer); =20 rzv2h_rspi_clear_all_irqs(rspi); =20 + if (is_dma && ret =3D=3D -EAGAIN) + /* Retry with PIO */ + transfer->error =3D SPI_TRANS_FAIL_NO_START; + return ret; } =20 @@ -557,6 +704,7 @@ static int rzv2h_rspi_probe(struct platform_device *pde= v) platform_set_drvdata(pdev, rspi); =20 rspi->controller =3D controller; + rspi->pdev =3D pdev; =20 rspi->info =3D device_get_match_data(dev); =20 @@ -613,6 +761,7 @@ static int rzv2h_rspi_probe(struct platform_device *pde= v) controller->unprepare_message =3D rzv2h_rspi_unprepare_message; controller->num_chipselect =3D 4; controller->transfer_one =3D rzv2h_rspi_transfer_one; + controller->can_dma =3D rzv2h_rspi_can_dma; =20 tclk_rate =3D clk_round_rate(rspi->tclk, 0); if (tclk_rate < 0) @@ -630,6 +779,24 @@ static int rzv2h_rspi_probe(struct platform_device *pd= ev) RSPI_SPBR_SPR_MIN, RSPI_SPCMD_BRDV_MIN); =20 + controller->dma_tx =3D devm_dma_request_chan(dev, "tx"); + if (IS_ERR(controller->dma_tx)) { + ret =3D dev_warn_probe(dev, PTR_ERR(controller->dma_tx), + "failed to request TX DMA channel\n"); + if (ret =3D=3D -EPROBE_DEFER) + return ret; + controller->dma_tx =3D NULL; + } + + controller->dma_rx =3D devm_dma_request_chan(dev, "rx"); + if (IS_ERR(controller->dma_rx)) { + ret =3D dev_warn_probe(dev, PTR_ERR(controller->dma_rx), + "failed to request RX DMA channel\n"); + if (ret =3D=3D -EPROBE_DEFER) + return ret; + controller->dma_rx =3D NULL; + } + device_set_node(&controller->dev, dev_fwnode(dev)); =20 ret =3D devm_spi_register_controller(dev, controller); --=20 2.52.0