mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v2] i2c: qup: Release DMA channels if interconnect lookup fails
@ 2026-11-06  7:03 Yige Jiang
  0 siblings, 0 replies; only message in thread
From: Yige Jiang @ 2026-11-06  7:03 UTC (permalink / raw)
  To: linux-i2c
  Cc: Yige Jiang, Andi Shyti, linux-arm-msm, linux-kernel,
	Stephan Gerhold, Konrad Dybcio

qup_i2c_probe() acquires both BAM DMA channels before getting its
interconnect path. If devm_of_icc_get() fails, the direct return skips
the DMA cleanup and leaks both channels, including when probe is
deferred. A later probe can no longer acquire those channels.

Use devm_dma_request_chan() so probe failure automatically releases the
channels. Group the two requests so a failed RX request also releases
TX immediately before probe falls back to PIO. Keep reusing the acquired
pair when the transfer path calls qup_i2c_req_dma() again.

Close the successful group so it can be released explicitly at the
existing probe unwind and remove sites without releasing later device
resources. Release the group directly in remove, preserving the channel
pointer state there. The direct interconnect error return is covered by
the automatic devres cleanup.

Fixes: d4f35233a634 ("i2c: qup: Vote for interconnect bandwidth to DRAM")
Signed-off-by: Yige Jiang <yigejiang86@gmail.com>
---
Changes in v2:
- Use devm_dma_request_chan(), as suggested by Konrad.
- Group TX/RX requests to preserve immediate rollback before PIO fallback.
- Reuse an existing pair in the transfer path and release the closed group
  at the existing probe unwind and remove sites.

v1: https://lore.kernel.org/r/rc-review-20261105-vc-48-dbf6a486c74f@gmail.com

Based on linux-next next-20261102 (2637bdec0535).
Validated this package on arm64: the W=1 i2c-qup module build and
a DMA_ENGINE-disabled driver build passed without warnings.
QEMU KUnit/KASAN passed 11 expected observations using real
DMAengine/devres code and a synthetic DMA provider. These checks
do not execute QUP hardware transfers or prove concurrent unbind
safety. No QUP/BAM hardware test was available.
AI assistance was used for analysis and patch preparation.

 drivers/i2c/busses/i2c-qup.c | 54 +++++++++++++++++++++-----------------------
 1 file changed, 26 insertions(+), 28 deletions(-)

diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index ce30634e1f402ddbc092d9b220532d76455fc047..865c03d9aa891b9fc38538ae184541f74c0a2819 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -637,10 +637,7 @@ static int qup_sg_set_buf(struct scatterlist *sg, void *buf,
 
 static void qup_i2c_rel_dma(struct qup_i2c_dev *qup)
 {
-	if (qup->btx.dma)
-		dma_release_channel(qup->btx.dma);
-	if (qup->brx.dma)
-		dma_release_channel(qup->brx.dma);
+	devres_release_group(qup->dev, qup);
 	qup->btx.dma = NULL;
 	qup->brx.dma = NULL;
 }
@@ -649,27 +646,32 @@ static int qup_i2c_req_dma(struct qup_i2c_dev *qup)
 {
 	int err;
 
-	if (!qup->btx.dma) {
-		qup->btx.dma = dma_request_chan(qup->dev, "tx");
-		if (IS_ERR(qup->btx.dma)) {
-			err = PTR_ERR(qup->btx.dma);
-			qup->btx.dma = NULL;
-			dev_err(qup->dev, "\n tx channel not available");
-			return err;
-		}
+	if (qup->btx.dma && qup->brx.dma)
+		return 0;
+
+	if (!devres_open_group(qup->dev, qup, GFP_KERNEL))
+		return -ENOMEM;
+
+	qup->btx.dma = devm_dma_request_chan(qup->dev, "tx");
+	if (IS_ERR(qup->btx.dma)) {
+		err = PTR_ERR(qup->btx.dma);
+		dev_err(qup->dev, "\n tx channel not available");
+		goto err_release_group;
 	}
 
-	if (!qup->brx.dma) {
-		qup->brx.dma = dma_request_chan(qup->dev, "rx");
-		if (IS_ERR(qup->brx.dma)) {
-			dev_err(qup->dev, "\n rx channel not available");
-			err = PTR_ERR(qup->brx.dma);
-			qup->brx.dma = NULL;
-			qup_i2c_rel_dma(qup);
-			return err;
-		}
+	qup->brx.dma = devm_dma_request_chan(qup->dev, "rx");
+	if (IS_ERR(qup->brx.dma)) {
+		err = PTR_ERR(qup->brx.dma);
+		dev_err(qup->dev, "\n rx channel not available");
+		goto err_release_group;
 	}
+
+	devres_close_group(qup->dev, qup);
 	return 0;
+
+err_release_group:
+	qup_i2c_rel_dma(qup);
+	return err;
 }
 
 static int qup_i2c_bam_make_desc(struct qup_i2c_dev *qup, struct i2c_msg *msg)
@@ -1952,9 +1954,7 @@ fail:
 	qup_i2c_disable_clocks(qup);
 fail_dma:
 	if (qup->btx.dma)
-		dma_release_channel(qup->btx.dma);
-	if (qup->brx.dma)
-		dma_release_channel(qup->brx.dma);
+		qup_i2c_rel_dma(qup);
 	return ret;
 }
 
@@ -1962,10 +1962,8 @@ static void qup_i2c_remove(struct platform_device *pdev)
 {
 	struct qup_i2c_dev *qup = platform_get_drvdata(pdev);
 
-	if (qup->is_dma) {
-		dma_release_channel(qup->btx.dma);
-		dma_release_channel(qup->brx.dma);
-	}
+	if (qup->is_dma)
+		devres_release_group(qup->dev, qup);
 
 	disable_irq(qup->irq);
 	qup_i2c_disable_clocks(qup);

base-commit: 2637bdec0535a2044cd830eba9a79363651d9043

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2026-11-06  7:03 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-06  7:03 [PATCH v2] i2c: qup: Release DMA channels if interconnect lookup fails Yige Jiang

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®