mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Changhuang Liang <changhuang.liang@starfivetech.com>
To: Mark Brown <broonie@kernel.org>
Cc: Serge Semin <fancer.lancer@gmail.com>,
	linux-spi@vger.kernel.org, linux-kernel@vger.kernel.org,
	Changhuang Liang <changhuang.liang@starfivetech.com>
Subject: [PATCH v1 1/2] spi: dw: Request DMA channels at runtime instead of probe
Date: Wed, 23 Sep 2026 02:57:04 -0700	[thread overview]
Message-ID: <20260923095705.233297-2-changhuang.liang@starfivetech.com> (raw)
In-Reply-To: <20260923095705.233297-1-changhuang.liang@starfivetech.com>

The DW SPI controller requests its DMA channels in
dw_spi_add_controller(), which ties them up for the entire lifetime of
the controller. Even a controller that is only ever used for standard
SPI transfers and never for enhanced SPI transfers holds the channels
exclusively from probe onwards, preventing them from being shared with
other users.

At probe time, first check whether the controller has DMA channels,
and release them immediately after the check. Remove the DMA channel
release from the remove path. Implement prepare_transfer_hardware()
and unprepare_transfer_hardware() to acquire and release the channels
later at runtime. This ensures that the DMA channels can be
time-multiplexed between enhanced SPI and standard SPI.

Since DMA is no longer set up at probe and torn down at remove.
dw_spi_dma_exit() now also clears txchan/rxchan, the controller's
dma_tx/dma_rx pointers, so that the state is consistent across
repeated prepare/unprepare cycles. Also check whether the channels
are valid in dw_spi_can_dma().

Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
---
 drivers/spi/spi-dw-core.c | 47 ++++++++++++++++++++++++++++++++-------
 drivers/spi/spi-dw-dma.c  |  8 +++++++
 2 files changed, 47 insertions(+), 8 deletions(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index 206d3f9dd83d..04a5b48373e1 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -545,6 +545,39 @@ static int dw_spi_transfer_one(struct spi_controller *ctlr,
 	return 1;
 }
 
+static int dw_spi_prepare_hardware(struct spi_controller *ctlr)
+{
+	struct dw_spi *dws = spi_controller_get_devdata(ctlr);
+	int ret;
+
+	if (!ctlr->can_dma)
+		return 0;
+
+	ret = dws->dma_ops->dma_init(ctlr->dev.parent, dws);
+	if (ret) {
+		/*
+		 * DMA is optional: fall back to the PIO/IRQ path instead of
+		 * failing the message. Use dev_dbg() since this may happen
+		 * on every prepare.
+		 */
+		dev_dbg(&ctlr->dev, "DMA init failed (%d), using PIO\n", ret);
+
+		return 0;
+	}
+
+	return 0;
+}
+
+static int dw_spi_unprepare_hardware(struct spi_controller *ctlr)
+{
+	struct dw_spi *dws = spi_controller_get_devdata(ctlr);
+
+	if (dws->dma_ops && dws->dma_ops->dma_exit)
+		dws->dma_ops->dma_exit(dws);
+
+	return 0;
+}
+
 static inline void dw_spi_abort(struct spi_controller *ctlr)
 {
 	struct dw_spi *dws = spi_controller_get_devdata(ctlr);
@@ -1330,6 +1363,8 @@ int dw_spi_add_controller(struct device *dev, struct dw_spi *dws)
 	ctlr->setup = dw_spi_setup;
 	ctlr->cleanup = dw_spi_cleanup;
 	ctlr->transfer_one = dw_spi_transfer_one;
+	ctlr->prepare_transfer_hardware = dw_spi_prepare_hardware;
+	ctlr->unprepare_transfer_hardware = dw_spi_unprepare_hardware;
 	ctlr->handle_err = dw_spi_handle_err;
 	ctlr->auto_runtime_pm = true;
 
@@ -1354,13 +1389,14 @@ int dw_spi_add_controller(struct device *dev, struct dw_spi *dws)
 	device_property_read_u32(dev, "rx-sample-delay-ns",
 				 &dws->def_rx_sample_dly_ns);
 
-	if (dws->dma_ops && dws->dma_ops->dma_init) {
+	if (dws->dma_ops && dws->dma_ops->dma_init && dws->dma_ops->dma_exit) {
 		ret = dws->dma_ops->dma_init(dev, dws);
 		if (ret == -EPROBE_DEFER) {
 			goto err_free_irq;
 		} else if (ret) {
 			dev_warn(dev, "DMA init failed\n");
 		} else {
+			dws->dma_ops->dma_exit(dws);
 			ctlr->can_dma = dws->dma_ops->can_dma;
 			ctlr->flags |= SPI_CONTROLLER_MUST_TX;
 		}
@@ -1369,15 +1405,13 @@ int dw_spi_add_controller(struct device *dev, struct dw_spi *dws)
 	ret = spi_register_controller(ctlr);
 	if (ret) {
 		dev_err_probe(dev, ret, "problem registering spi controller\n");
-		goto err_dma_exit;
+		goto err_disable_ctlr;
 	}
 
 	dw_spi_debugfs_init(dws);
 	return 0;
 
-err_dma_exit:
-	if (dws->dma_ops && dws->dma_ops->dma_exit)
-		dws->dma_ops->dma_exit(dws);
+err_disable_ctlr:
 	dw_spi_enable_chip(dws, 0);
 err_free_irq:
 	free_irq(dws->irq, ctlr);
@@ -1393,9 +1427,6 @@ void dw_spi_remove_controller(struct dw_spi *dws)
 
 	spi_unregister_controller(dws->ctlr);
 
-	if (dws->dma_ops && dws->dma_ops->dma_exit)
-		dws->dma_ops->dma_exit(dws);
-
 	dw_spi_shutdown_chip(dws);
 
 	free_irq(dws->irq, dws->ctlr);
diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c
index f7d848fec9ab..022eb4fae33d 100644
--- a/drivers/spi/spi-dw-dma.c
+++ b/drivers/spi/spi-dw-dma.c
@@ -211,11 +211,15 @@ static void dw_spi_dma_exit(struct dw_spi *dws)
 	if (dws->txchan) {
 		dmaengine_terminate_sync(dws->txchan);
 		dma_release_channel(dws->txchan);
+		dws->txchan = NULL;
+		dws->ctlr->dma_tx = NULL;
 	}
 
 	if (dws->rxchan) {
 		dmaengine_terminate_sync(dws->rxchan);
 		dma_release_channel(dws->rxchan);
+		dws->rxchan = NULL;
+		dws->ctlr->dma_rx = NULL;
 	}
 }
 
@@ -249,6 +253,10 @@ static bool dw_spi_can_dma(struct spi_controller *ctlr,
 	enum dma_slave_buswidth dma_bus_width;
 	u8 n_bytes = roundup_pow_of_two(BITS_TO_BYTES(xfer->bits_per_word));
 
+	/* Channels are acquired at runtime and may be unavailable */
+	if (!dws->txchan || !dws->rxchan)
+		return false;
+
 	if (xfer->len <= dws->fifo_len)
 		return false;
 
-- 
2.25.1


  reply	other threads:[~2026-09-23 10:12 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-23  9:57 [PATCH v1 0/2] Add enhance SPI DMA support for JHB100 SFC Changhuang Liang
2026-09-23  9:57 ` Changhuang Liang [this message]
2026-09-23  9:57 ` [PATCH v1 2/2] spi: dw: Add DMA support for enhanced memory operations Changhuang Liang

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260923095705.233297-2-changhuang.liang@starfivetech.com \
    --to=changhuang.liang@starfivetech.com \
    --cc=broonie@kernel.org \
    --cc=fancer.lancer@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-spi@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®