* [PATCH v6 0/9] dmaengine: add DMA channel bus and fsl-edma per-channel IOMMU support
@ 2026-11-12 7:53 Peng Fan (OSS)
2026-11-12 7:53 ` [PATCH v6 1/9] dmaengine: fsl-edma: unregister channels before vchan cleanup Peng Fan (OSS)
` (8 more replies)
0 siblings, 9 replies; 14+ messages in thread
From: Peng Fan (OSS) @ 2026-11-12 7:53 UTC (permalink / raw)
To: Frank Li, Vinod Koul, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Laxman Dewangan, Jon Hunter,
Thierry Reding, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Frank Li, Larisa IIeana Grigore, imx, dmaengine, linux-kernel,
iommu, linux-tegra, devicetree, linux-arm-kernel, Peng Fan
I used iommu-map for eDMA, but iommu experts rejected it:
"Using iommu-map to describe synthetic dma_chan_dev devices that the
kernel creates is the same kind of DT abuse from over here:"
More info: https://lore.kernel.org/all/20260917144804.GI3196566@ziepe.ca/
So I followed here [2] as Krzysztof wrote for QCOM VPU:
"Thanks Jason for context and detailed arguments. Robin did not chime in
yet, but from what I understood the DT-child-node approach will be the
way we should go here.
I accept above point of view and I am fine with this, thus Vishnu and
Vikash - please go ahead with DT-child-node solution we had some time ago."
[2] https://lore.kernel.org/linux-iommu/c7b956a9-d3e8-4e18-b780-5d08f5cd2ca1@kernel.org/
This series adds per-channel DT child node support to fsl-edma and, as a
prerequisite, introduces a proper bus for DMA channel devices.
The fsl-edma change allows each channel to carry its own reg, interrupts
and iommus properties. Power domains remain attached at the parent level.
Parsing the child nodes is straightforward, but per-channel IOMMU
configuration is not: the DMA engine currently creates struct dma_chan_dev
as a class device without any bus, so of_dma_configure() has no bus to probe
the IOMMU with, and the IOMMU core does not have a bus notifier for DMA
channel devices.
Drivers that need per-channel IOMMU support currently work around this by
assigning dev->bus after dmaenginem_async_device_register(). That is unsafe
because bus_add_device() already ran with bus == NULL and did not take a bus
subsystem reference. When the channel device is later unregistered,
bus_remove_device() drops a reference that was never acquired, leading to a
reference count underflow and a potential use-after-free. Using
platform_bus_type is also wrong: platform_match() and platform_uevent()
expect the device to be embedded in a struct platform_device.
A dedicated bus for DMA channel devices solves this. The channel devices are
converted from class devices to bus devices:
- the bus is registered early, before iommu_subsys_init() consumes
iommu_buses[];
- the match callback always reports no match, so no driver binds to a
channel device;
- channel devices are registered on the bus before device_register(), so
bus_add_device() and bus_remove_device() are balanced;
- the bus is added to iommu_buses[], so the existing IOMMU notifier
handles per-channel IOMMU probe and release.
The existing /sys/class/dma interface is preserved with class_compat, the
mechanism provided by the driver core for converting a class device family to
bus devices. A compatibility class named "dma" keeps
/sys/class/dma/dmaNchanM available, while the devices themselves live on the
DMA bus. The bus is named "dmaengine-chan" to avoid colliding with the SuperH DMA
bus, which is also named "dma". The uevent SUBSYSTEM and "subsystem" link
therefore use "dmaengine-chan", while /sys/class/dma is unchanged. dma_dev_groups
moves from the old class to the bus, and chan_dev_release is installed
directly as the device release callback.
While testing controller unbind, a pre-existing fsl-edma ordering bug was
found: fsl_edma_remove() removed all channels from dma_dev.channels before the
managed dma_async_device_unregister() action ran, so the channel devices and
their IOMMU groups were never released. Patch 1 fixes that ordering, which is
needed to make the bind/unbind lifecycle test meaningful.
Runtime-tested on i.MX95: repeated controller bind/unbind cycles with a
local DT based on the child-node conversion removed and recreated the
channel devices as expected, with no refcount, BUG, WARNING or suspicious
kobject diagnostics. The per-channel IOMMU path was exercised with a local
DT adding iommus; that DT update is not part of this series.
The binding and driver support per-channel iommus, but patch 9 does not add
them to the i.MX95 DTS yet. That DT update will be sent separately when the
shared stream ID support are finalized.
I put all the patches in one patchset to give people a full picture.
If need to separate patches into multiple patchsets, I could do that
in next version.
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
Changes in v6:
- Patch 2:
Update commit message include more info regarding bus introduction. (Frank)
name the DMA channel bus "dmaengine-chan" instead of "dma-chan" (Frank).
- Patch 6: new prep patch converting chan_masked tests from BIT() to
BIT_ULL() for channels 32-63 (Frank).
- Patch 7: new prep patch converting fsl-edma to dmaengine_get_dma_device() (Frank).
- Patch 8: Use scoped api, avoid scan channel twice. (Frank)
- Link to v5: https://patch.msgid.link/20261111-edma-iommu-v3-v5-0-2537cebe6ca2@nxp.com
Changes in v5:
- Patch 2: Rename bus name to dma-chan per report from Sashiko.
- Patch 6: Address the issue reported by kernel bot:
symbol '__udivdi3' undefined, using div_u64_rem
- Rebased to next-20261110
- Link to v4: https://lore.kernel.org/all/20261103-edma-iommu-v3-v4-0-470415d418c5@Example.Com/
Changes in v4:
- Patch 1, patch 4 and patch 5 are unchanged.
- Patch 2:
Register the DMA channel bus from core_initcall so it exists before any
IOMMU driver can call iommu_device_register(), which iterates
iommu_buses[].
Clean up the DMA unmap pools if dmaengine initialisation fails.
Clarify that the bus match callback always reports no match.
- Patch 3:
Update the ordering rationale for IOMMU drivers that register before
arch_initcall. No functional change beyond patch 2's initcall move.
- Patch 6:
- Validate child register resources: require them to be contained in the
controller resource and aligned to chreg_space_sz.
- Use div_u64() for the channel index calculation and reject values that
do not fit in an int, avoiding 32-bit __aeabi_uldivmod/__udivdi3 issues.
- Ignore child nodes beyond dma-channels, so imx95-navq.dts with
dma-channels = <30> still probes.
- Use BIT_ULL() for the 64-bit chan_masked bitmap, including channels
32-63 on 32-bit builds.
- Set chdev->dma_parms to the controller dma_parms so the per-channel
device keeps the 0x7fff segment size limit.
- Use dev_err_probe() for of_dma_configure() failures.
- Reject channels for which iommus did not result in an IOMMU mapping
instead of setting chan_dma_dev = true silently.
- If child nodes are used, require every unmasked channel to have an
available child node with a valid interrupt; otherwise fail probe with
an error asking for dma-channel-mask.
- Do not fall back to the parent flat interrupt array in child-node mode.
- Keep the shared error IRQ on the parent node.
- Clarify that child power-domains are not handled by this patch.
- Patch 7:
- No functional change. The out-of-range child nodes used by
imx95-navq.dts are handled by patch 6.
- The i.MX95 DTS still does not add per-channel iommus; that update will be
submitted separately.
- Board test overlays are kept out of the upstream series.
- Link to v3: https://patch.msgid.link/20261102-edma-iommu-v3-v3-0-9d0e62a954eb@Example.Com
Changes in V3:
- Patch [1-4] are new patches
- Add R-b for patch 5
- Patch 6: handle of_irq_get() -EPROBE_DEFER, drop the explicit bus
assignment to "chdev->bus" which is resolved by patch [2-4].
- Rebased to linus/master v7.4-rc1
Changes in V2:
- Switch to add child node for dma channel.
- Link to V1: https://lore.kernel.org/dmaengine/20260916-edma-iommu-v1-0-e1731968081e@nxp.com/
---
Peng Fan (9):
dmaengine: fsl-edma: unregister channels before vchan cleanup
dmaengine: add a bus for DMA channel devices
iommu: register the DMA channel bus for IOMMU notifications
dmaengine: move channel devices to the DMA channel bus
dt-bindings: dma: fsl,edma: add per-channel child node support
dmaengine: fsl-edma: use BIT_ULL for 64-bit channel mask
dmaengine: fsl-edma: use dmaengine_get_dma_device()
dmaengine: fsl-edma: support per-channel DT child nodes
arm64: dts: imx95: convert eDMA to per-channel child nodes
.../devicetree/bindings/dma/fsl,edma.yaml | 70 +-
arch/arm64/boot/dts/freescale/imx95.dtsi | 774 +++++++++++++++++----
drivers/dma/dmaengine.c | 49 +-
drivers/dma/fsl-edma-common.c | 10 +-
drivers/dma/fsl-edma-common.h | 1 +
drivers/dma/fsl-edma-main.c | 225 +++++-
drivers/dma/tegra186-gpc-dma.c | 1 -
drivers/iommu/iommu.c | 4 +
include/linux/dmaengine.h | 4 +
9 files changed, 979 insertions(+), 159 deletions(-)
---
base-commit: aaa2f954c21b205a0bcd0961e5b2be4e82e58455
change-id: 20261102-edma-iommu-v3-22b673717cf4
Best regards,
--
Peng Fan <peng.fan@nxp.com>
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v6 1/9] dmaengine: fsl-edma: unregister channels before vchan cleanup
2026-11-12 7:53 [PATCH v6 0/9] dmaengine: add DMA channel bus and fsl-edma per-channel IOMMU support Peng Fan (OSS)
@ 2026-11-12 7:53 ` Peng Fan (OSS)
2026-11-12 7:53 ` [PATCH v6 2/9] dmaengine: add a bus for DMA channel devices Peng Fan (OSS)
` (7 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Peng Fan (OSS) @ 2026-11-12 7:53 UTC (permalink / raw)
To: Frank Li, Vinod Koul, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Laxman Dewangan, Jon Hunter,
Thierry Reding, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Frank Li, Larisa IIeana Grigore, imx, dmaengine, linux-kernel,
iommu, linux-tegra, devicetree, linux-arm-kernel, Peng Fan
From: Peng Fan <peng.fan@nxp.com>
Since commit 4035726a6b72 ("dmaengine: fsl-edma: Use managed API
dmaenginem_async_device_register()"), dma_async_device_unregister() runs
as a devres action after fsl_edma_remove(). However, fsl_edma_remove()
still calls fsl_edma_cleanup_vchan() first, which removes every channel
from dma_dev.channels. When the devres action later runs,
dma_async_device_unregister() sees an empty channel list and never
unregisters the channel devices. They remain visible in /sys/class/dma.
Keep the vchan cleanup, but register it as a devres action before
dmaenginem_async_device_register(). Devres actions run in reverse
order, so the DMA channels are unregistered first and the vchan list is
cleaned up afterwards. Remove the premature cleanup call from
fsl_edma_remove().
Fixes: 4035726a6b72 ("dmaengine: fsl-edma: Use managed API dmaenginem_async_device_register()")
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
drivers/dma/fsl-edma-main.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/drivers/dma/fsl-edma-main.c b/drivers/dma/fsl-edma-main.c
index da5139911488..7f82dcb7831d 100644
--- a/drivers/dma/fsl-edma-main.c
+++ b/drivers/dma/fsl-edma-main.c
@@ -31,6 +31,11 @@ static void fsl_edma_synchronize(struct dma_chan *chan)
vchan_synchronize(&fsl_chan->vchan);
}
+static void fsl_edma_cleanup_vchan_action(void *data)
+{
+ fsl_edma_cleanup_vchan(data);
+}
+
static irqreturn_t fsl_edma_tx_handler(int irq, void *dev_id)
{
struct fsl_edma_engine *fsl_edma = dev_id;
@@ -881,6 +886,12 @@ static int fsl_edma_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, fsl_edma);
+ ret = devm_add_action_or_reset(&pdev->dev,
+ fsl_edma_cleanup_vchan_action,
+ &fsl_edma->dma_dev);
+ if (ret)
+ return ret;
+
ret = dmaenginem_async_device_register(&fsl_edma->dma_dev);
if (ret)
return dev_err_probe(&pdev->dev, ret,
@@ -905,7 +916,6 @@ static void fsl_edma_remove(struct platform_device *pdev)
struct fsl_edma_engine *fsl_edma = platform_get_drvdata(pdev);
fsl_edma_irq_exit(pdev, fsl_edma);
- fsl_edma_cleanup_vchan(&fsl_edma->dma_dev);
}
static int fsl_edma_suspend_late(struct device *dev)
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v6 2/9] dmaengine: add a bus for DMA channel devices
2026-11-12 7:53 [PATCH v6 0/9] dmaengine: add DMA channel bus and fsl-edma per-channel IOMMU support Peng Fan (OSS)
2026-11-12 7:53 ` [PATCH v6 1/9] dmaengine: fsl-edma: unregister channels before vchan cleanup Peng Fan (OSS)
@ 2026-11-12 7:53 ` Peng Fan (OSS)
2026-11-12 15:47 ` Frank Li
2026-11-12 7:53 ` [PATCH v6 3/9] iommu: register the DMA channel bus for IOMMU notifications Peng Fan (OSS)
` (6 subsequent siblings)
8 siblings, 1 reply; 14+ messages in thread
From: Peng Fan (OSS) @ 2026-11-12 7:53 UTC (permalink / raw)
To: Frank Li, Vinod Koul, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Laxman Dewangan, Jon Hunter,
Thierry Reding, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Frank Li, Larisa IIeana Grigore, imx, dmaengine, linux-kernel,
iommu, linux-tegra, devicetree, linux-arm-kernel, Peng Fan
From: Peng Fan <peng.fan@nxp.com>
DMA channel devices are currently created as class devices without a bus.
This works for sysfs, but it prevents per-channel DMA/IOMMU configuration:
of_dma_configure() only probes the IOMMU when dev->bus is set, and the
IOMMU core only installs probe/release notifiers for buses listed in
iommu_buses[].
Drivers that need per-channel IOMMU support currently work around this by
assigning dev->bus after device_register(). That leaves the bus subsystem
reference count unbalanced because bus_add_device() already ran with a NULL
bus, and bus_remove_device() will later drop a reference that was never
taken. For example, tegra186-gpc-dma.c assigns chdev->bus after channel
registration. Review noted that this can underflow the platform bus
reference count and lead to a use-after-free when the device is removed.
Add a dedicated bus for DMA channel devices and register it during
dmaengine initialisation. Use core_initcall so the bus exists before any
IOMMU driver can call iommu_device_register(), which iterates iommu_buses[]
and fails on an unregistered bus. The match callback always reports no
match, so no driver can bind to these devices. A follow-up patch will
register the channel devices on this bus.
Clean up the unmap pools if dmaengine initialisation fails after they have
been allocated.
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
drivers/dma/dmaengine.c | 28 +++++++++++++++++++++++++---
include/linux/dmaengine.h | 4 ++++
2 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index 34be53487a21..e1022d297674 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -1643,6 +1643,17 @@ void dma_run_dependencies(struct dma_async_tx_descriptor *tx)
}
EXPORT_SYMBOL_GPL(dma_run_dependencies);
+static int dma_chan_bus_match(struct device *dev,
+ const struct device_driver *drv)
+{
+ return 0;
+}
+
+const struct bus_type dmaengine_chan_bus_type = {
+ .name = "dmaengine-chan",
+ .match = dma_chan_bus_match,
+};
+
static int __init dma_bus_init(void)
{
int err = dmaengine_init_unmap_pool();
@@ -1650,10 +1661,21 @@ static int __init dma_bus_init(void)
if (err)
return err;
+ err = bus_register(&dmaengine_chan_bus_type);
+ if (err)
+ goto err_destroy_unmap_pool;
+
err = class_register(&dma_devclass);
- if (!err)
- dmaengine_debugfs_init();
+ if (err)
+ goto err_unregister_bus;
+ dmaengine_debugfs_init();
+ return 0;
+
+err_unregister_bus:
+ bus_unregister(&dmaengine_chan_bus_type);
+err_destroy_unmap_pool:
+ dmaengine_destroy_unmap_pool();
return err;
}
-arch_initcall(dma_bus_init);
+core_initcall(dma_bus_init);
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index 805668fbe437..be9308739676 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -412,6 +412,10 @@ struct dma_chan_dev {
bool chan_dma_dev;
};
+#ifdef CONFIG_DMA_ENGINE
+extern const struct bus_type dmaengine_chan_bus_type;
+#endif
+
/**
* enum dma_slave_buswidth - defines bus width of the DMA slave
* device, source or target buses
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v6 3/9] iommu: register the DMA channel bus for IOMMU notifications
2026-11-12 7:53 [PATCH v6 0/9] dmaengine: add DMA channel bus and fsl-edma per-channel IOMMU support Peng Fan (OSS)
2026-11-12 7:53 ` [PATCH v6 1/9] dmaengine: fsl-edma: unregister channels before vchan cleanup Peng Fan (OSS)
2026-11-12 7:53 ` [PATCH v6 2/9] dmaengine: add a bus for DMA channel devices Peng Fan (OSS)
@ 2026-11-12 7:53 ` Peng Fan (OSS)
2026-11-12 7:53 ` [PATCH v6 4/9] dmaengine: move channel devices to the DMA channel bus Peng Fan (OSS)
` (5 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Peng Fan (OSS) @ 2026-11-12 7:53 UTC (permalink / raw)
To: Frank Li, Vinod Koul, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Laxman Dewangan, Jon Hunter,
Thierry Reding, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Frank Li, Larisa IIeana Grigore, imx, dmaengine, linux-kernel,
iommu, linux-tegra, devicetree, linux-arm-kernel, Peng Fan
From: Peng Fan <peng.fan@nxp.com>
DMA channel devices are about to be registered on a dedicated DMA channel
bus so that they get a properly balanced bus association before
device_register().
Add that bus to iommu_buses[] so the IOMMU core installs its bus notifier
for it. This allows of_dma_configure() to probe a per-channel IOMMU and,
more importantly, lets BUS_NOTIFY_REMOVED_DEVICE release the IOMMU group
and domain when the channel device is unregistered.
The dmaengine bus is registered from a core_initcall, before any IOMMU
driver can call iommu_device_register(), which iterates iommu_buses[] and
fails if a bus has not been registered yet. Guard the entry with
CONFIG_DMA_ENGINE because the bus does not exist when the DMA engine core
is not built.
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
drivers/iommu/iommu.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index b486b8bbd1fc..ab31cd15d1d5 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -8,6 +8,7 @@
#include <linux/amba/bus.h>
#include <linux/device.h>
+#include <linux/dmaengine.h>
#include <linux/kernel.h>
#include <linux/bits.h>
#include <linux/bug.h>
@@ -191,6 +192,9 @@ static const struct bus_type * const iommu_buses[] = {
#ifdef CONFIG_ARM_AMBA
&amba_bustype,
#endif
+#ifdef CONFIG_DMA_ENGINE
+ &dmaengine_chan_bus_type,
+#endif
#ifdef CONFIG_FSL_MC_BUS
&fsl_mc_bus_type,
#endif
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v6 4/9] dmaengine: move channel devices to the DMA channel bus
2026-11-12 7:53 [PATCH v6 0/9] dmaengine: add DMA channel bus and fsl-edma per-channel IOMMU support Peng Fan (OSS)
` (2 preceding siblings ...)
2026-11-12 7:53 ` [PATCH v6 3/9] iommu: register the DMA channel bus for IOMMU notifications Peng Fan (OSS)
@ 2026-11-12 7:53 ` Peng Fan (OSS)
2026-11-12 16:04 ` Frank Li
2026-11-12 7:53 ` [PATCH v6 5/9] dt-bindings: dma: fsl,edma: add per-channel child node support Peng Fan (OSS)
` (4 subsequent siblings)
8 siblings, 1 reply; 14+ messages in thread
From: Peng Fan (OSS) @ 2026-11-12 7:53 UTC (permalink / raw)
To: Frank Li, Vinod Koul, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Laxman Dewangan, Jon Hunter,
Thierry Reding, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Frank Li, Larisa IIeana Grigore, imx, dmaengine, linux-kernel,
iommu, linux-tegra, devicetree, linux-arm-kernel, Peng Fan
From: Peng Fan <peng.fan@nxp.com>
Register each dma_chan_dev on dmaengine_chan_bus_type before
device_register() and make it a bus device instead of a class device.
Setting the bus before registration lets bus_add_device() take the bus
subsystem reference, so the matching bus_remove_device() calls drop
references that were actually acquired. Attach dma_dev_groups to the bus
and set the device release callback directly.
Keep the existing /sys/class/dma interface by registering a class_compat
named "dma" and creating a link for each channel device. The channel
devices now live on the DMA bus, while /sys/class/dma/dmaNchanM remains
available to userspace.
The bus is named "dmaengine-chan" rather than "dma" to avoid colliding
with the SuperH DMA bus, which is also named "dma" and is registered later
from a postcore_initcall. As a result the device's uevent SUBSYSTEM and
"subsystem" link use "dmaengine-chan", while /sys/class/dma is unchanged.
Remove the post-registration dev->bus assignment from the Tegra GPC DMA
driver. Leaving it in place would overwrite the bus selected by the DMA
engine core and reintroduce the reference imbalance.
The DMA channel bus match callback never matches, so no driver can bind
to these channel devices.
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
drivers/dma/dmaengine.c | 23 +++++++++++++++--------
drivers/dma/tegra186-gpc-dma.c | 1 -
2 files changed, 15 insertions(+), 9 deletions(-)
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index e1022d297674..45e328d28689 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -239,11 +239,7 @@ static void chan_dev_release(struct device *dev)
kfree(chan_dev);
}
-static struct class dma_devclass = {
- .name = "dma",
- .dev_groups = dma_dev_groups,
- .dev_release = chan_dev_release,
-};
+static struct class_compat *dma_chan_class;
/* --- client and device registration --- */
@@ -1115,8 +1111,9 @@ static int __dma_async_device_channel_register(struct dma_device *device,
goto err_free_dev;
}
- dmaengine_chan_dev(chan)->class = &dma_devclass;
dmaengine_chan_dev(chan)->parent = device->dev;
+ dmaengine_chan_dev(chan)->bus = &dmaengine_chan_bus_type;
+ dmaengine_chan_dev(chan)->release = chan_dev_release;
chan->chan_dev->chan = chan;
chan->chan_dev->dev_id = device->dev_id;
spin_lock_init(&chan->lock);
@@ -1128,6 +1125,11 @@ static int __dma_async_device_channel_register(struct dma_device *device,
rc = device_register(dmaengine_chan_dev(chan));
if (rc)
goto err_out_ida;
+
+ rc = class_compat_create_link(dma_chan_class, dmaengine_chan_dev(chan));
+ if (rc)
+ dev_warn(device->dev, "Failed to create compatibility class link\n");
+
chan->client_count = 0;
device->chancnt++;
@@ -1172,6 +1174,7 @@ static void __dma_async_device_channel_unregister(struct dma_device *device,
chan->chan_dev->chan = NULL;
mutex_unlock(&dma_list_mutex);
ida_free(&device->chan_ida, chan->chan_id);
+ class_compat_remove_link(dma_chan_class, dmaengine_chan_dev(chan));
device_unregister(dmaengine_chan_dev(chan));
free_percpu(chan->local);
}
@@ -1306,6 +1309,7 @@ int dma_async_device_register(struct dma_device *device)
mutex_lock(&dma_list_mutex);
chan->chan_dev->chan = NULL;
mutex_unlock(&dma_list_mutex);
+ class_compat_remove_link(dma_chan_class, dmaengine_chan_dev(chan));
device_unregister(dmaengine_chan_dev(chan));
free_percpu(chan->local);
}
@@ -1652,6 +1656,7 @@ static int dma_chan_bus_match(struct device *dev,
const struct bus_type dmaengine_chan_bus_type = {
.name = "dmaengine-chan",
.match = dma_chan_bus_match,
+ .dev_groups = dma_dev_groups,
};
static int __init dma_bus_init(void)
@@ -1665,9 +1670,11 @@ static int __init dma_bus_init(void)
if (err)
goto err_destroy_unmap_pool;
- err = class_register(&dma_devclass);
- if (err)
+ dma_chan_class = class_compat_register("dma");
+ if (!dma_chan_class) {
+ err = -ENOMEM;
goto err_unregister_bus;
+ }
dmaengine_debugfs_init();
return 0;
diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
index ec12b7288b43..93b136f875d4 100644
--- a/drivers/dma/tegra186-gpc-dma.c
+++ b/drivers/dma/tegra186-gpc-dma.c
@@ -1540,7 +1540,6 @@ static int tegra_dma_probe(struct platform_device *pdev)
tdc = to_tegra_dma_chan(chan);
if (use_iommu_map) {
- chdev->bus = pdev->dev.bus;
dma_coerce_mask_and_coherent(chdev, DMA_BIT_MASK(cdata->addr_bits));
ret = of_dma_configure_id(chdev, pdev->dev.of_node,
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v6 5/9] dt-bindings: dma: fsl,edma: add per-channel child node support
2026-11-12 7:53 [PATCH v6 0/9] dmaengine: add DMA channel bus and fsl-edma per-channel IOMMU support Peng Fan (OSS)
` (3 preceding siblings ...)
2026-11-12 7:53 ` [PATCH v6 4/9] dmaengine: move channel devices to the DMA channel bus Peng Fan (OSS)
@ 2026-11-12 7:53 ` Peng Fan (OSS)
2026-11-12 7:53 ` [PATCH v6 6/9] dmaengine: fsl-edma: use BIT_ULL for 64-bit channel mask Peng Fan (OSS)
` (3 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Peng Fan (OSS) @ 2026-11-12 7:53 UTC (permalink / raw)
To: Frank Li, Vinod Koul, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Laxman Dewangan, Jon Hunter,
Thierry Reding, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Frank Li, Larisa IIeana Grigore, imx, dmaengine, linux-kernel,
iommu, linux-tegra, devicetree, linux-arm-kernel, Peng Fan
From: Peng Fan <peng.fan@nxp.com>
Add support for optional per-channel child nodes (dma-channel@<addr>)
to the fsl,edma binding. Each child node can carry its own reg,
interrupts, iommus, and power-domains properties, describing a real
per-channel MMIO region.
Enables per-channel IOMMU stream ID assignment via the standard
iommus property on each child.
The interrupts property is removed from the required list since
channels with child nodes carry their own interrupts. Existing DTS
without child nodes remains valid.
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
.../devicetree/bindings/dma/fsl,edma.yaml | 70 +++++++++++++++++++++-
1 file changed, 69 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/dma/fsl,edma.yaml b/Documentation/devicetree/bindings/dma/fsl,edma.yaml
index 0260d177c7d0..65877e78330a 100644
--- a/Documentation/devicetree/bindings/dma/fsl,edma.yaml
+++ b/Documentation/devicetree/bindings/dma/fsl,edma.yaml
@@ -104,11 +104,40 @@ properties:
Bit semantics: 0 means channel available, 1 means channel unavailable
default: 0
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 1
+
+ ranges: true
+
+patternProperties:
+ "^dma-channel@[0-9a-f]+$":
+ type: object
+ additionalProperties: false
+
+ properties:
+ reg:
+ maxItems: 1
+ description: Per-channel register region within the eDMA block.
+
+ interrupts:
+ maxItems: 1
+
+ iommus:
+ maxItems: 1
+
+ power-domains:
+ maxItems: 1
+
+ required:
+ - reg
+
required:
- "#dma-cells"
- compatible
- reg
- - interrupts
- dma-channels
allOf:
@@ -351,3 +380,42 @@ examples:
<&pd IMX_SC_R_DMA_3_CH6>,
<&pd IMX_SC_R_DMA_3_CH7>;
};
+
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ dma-controller@42000000 {
+ compatible = "fsl,imx95-edma5";
+ reg = <0x42000000 0x210000>;
+ #dma-cells = <3>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ dma-channels = <64>;
+ clocks = <&clk 0>;
+ clock-names = "dma";
+
+ dma-channel@42020000 {
+ reg = <0x42020000 0x8000>;
+ interrupts = <GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>;
+ iommus = <&smmu 0x30>;
+ };
+
+ dma-channel@42028000 {
+ reg = <0x42028000 0x8000>;
+ interrupts = <GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>;
+ iommus = <&smmu 0x30>;
+ };
+
+ dma-channel@42030000 {
+ reg = <0x42030000 0x8000>;
+ interrupts = <GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH>;
+ iommus = <&smmu 0x31>;
+ };
+
+ dma-channel@42038000 {
+ reg = <0x42038000 0x8000>;
+ interrupts = <GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH>;
+ iommus = <&smmu 0x31>;
+ };
+ };
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v6 6/9] dmaengine: fsl-edma: use BIT_ULL for 64-bit channel mask
2026-11-12 7:53 [PATCH v6 0/9] dmaengine: add DMA channel bus and fsl-edma per-channel IOMMU support Peng Fan (OSS)
` (4 preceding siblings ...)
2026-11-12 7:53 ` [PATCH v6 5/9] dt-bindings: dma: fsl,edma: add per-channel child node support Peng Fan (OSS)
@ 2026-11-12 7:53 ` Peng Fan (OSS)
2026-11-12 16:05 ` Frank Li
2026-11-12 7:53 ` [PATCH v6 7/9] dmaengine: fsl-edma: use dmaengine_get_dma_device() Peng Fan (OSS)
` (2 subsequent siblings)
8 siblings, 1 reply; 14+ messages in thread
From: Peng Fan (OSS) @ 2026-11-12 7:53 UTC (permalink / raw)
To: Frank Li, Vinod Koul, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Laxman Dewangan, Jon Hunter,
Thierry Reding, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Frank Li, Larisa IIeana Grigore, imx, dmaengine, linux-kernel,
iommu, linux-tegra, devicetree, linux-arm-kernel, Peng Fan
From: Peng Fan <peng.fan@nxp.com>
chan_masked is a u64 bitmap, but several checks use BIT(), which uses
unsigned long and overflows on 32-bit architectures for channels >= 32.
Use BIT_ULL() so the upper channels are tested correctly.
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
drivers/dma/fsl-edma-main.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/dma/fsl-edma-main.c b/drivers/dma/fsl-edma-main.c
index 7f82dcb7831d..818b9b986f13 100644
--- a/drivers/dma/fsl-edma-main.c
+++ b/drivers/dma/fsl-edma-main.c
@@ -123,7 +123,7 @@ static irqreturn_t fsl_edma3_err_handler_shared(int irq, void *dev_id)
unsigned int ch;
for (ch = 0; ch < fsl_edma->n_chans; ch++) {
- if (fsl_edma->chan_masked & BIT(ch))
+ if (fsl_edma->chan_masked & BIT_ULL(ch))
continue;
fsl_edma3_err_check(&fsl_edma->chans[ch]);
@@ -397,7 +397,7 @@ static int fsl_edma3_irq_init(struct platform_device *pdev, struct fsl_edma_engi
struct fsl_edma_chan *fsl_chan = &fsl_edma->chans[i];
- if (fsl_edma->chan_masked & BIT(i))
+ if (fsl_edma->chan_masked & BIT_ULL(i))
continue;
/* request channel irq */
@@ -641,7 +641,7 @@ static void fsl_edma3_detach_pd(struct fsl_edma_engine *fsl_edma)
int i;
for (i = 0; i < fsl_edma->n_chans; i++) {
- if (fsl_edma->chan_masked & BIT(i))
+ if (fsl_edma->chan_masked & BIT_ULL(i))
continue;
fsl_chan = &fsl_edma->chans[i];
if (fsl_chan->pd_dev_link)
@@ -669,7 +669,7 @@ static int fsl_edma3_attach_pd(struct platform_device *pdev, struct fsl_edma_eng
dev = &pdev->dev;
for (i = 0; i < fsl_edma->n_chans; i++) {
- if (fsl_edma->chan_masked & BIT(i))
+ if (fsl_edma->chan_masked & BIT_ULL(i))
continue;
fsl_chan = &fsl_edma->chans[i];
@@ -797,7 +797,7 @@ static int fsl_edma_probe(struct platform_device *pdev)
struct fsl_edma_chan *fsl_chan = &fsl_edma->chans[i];
int len;
- if (fsl_edma->chan_masked & BIT(i))
+ if (fsl_edma->chan_masked & BIT_ULL(i))
continue;
snprintf(fsl_chan->chan_name, sizeof(fsl_chan->chan_name), "%s-CH%02d",
@@ -927,7 +927,7 @@ static int fsl_edma_suspend_late(struct device *dev)
for (i = 0; i < fsl_edma->n_chans; i++) {
fsl_chan = &fsl_edma->chans[i];
- if (fsl_edma->chan_masked & BIT(i))
+ if (fsl_edma->chan_masked & BIT_ULL(i))
continue;
spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
/* Make sure chan is idle or will force disable. */
@@ -953,7 +953,7 @@ static int fsl_edma_resume_early(struct device *dev)
for (i = 0; i < fsl_edma->n_chans; i++) {
fsl_chan = &fsl_edma->chans[i];
- if (fsl_edma->chan_masked & BIT(i))
+ if (fsl_edma->chan_masked & BIT_ULL(i))
continue;
fsl_chan->pm_state = RUNNING;
edma_write_tcdreg(fsl_chan, 0, csr);
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v6 7/9] dmaengine: fsl-edma: use dmaengine_get_dma_device()
2026-11-12 7:53 [PATCH v6 0/9] dmaengine: add DMA channel bus and fsl-edma per-channel IOMMU support Peng Fan (OSS)
` (5 preceding siblings ...)
2026-11-12 7:53 ` [PATCH v6 6/9] dmaengine: fsl-edma: use BIT_ULL for 64-bit channel mask Peng Fan (OSS)
@ 2026-11-12 7:53 ` Peng Fan (OSS)
2026-11-12 16:06 ` Frank Li
2026-11-12 7:53 ` [PATCH v6 8/9] dmaengine: fsl-edma: support per-channel DT child nodes Peng Fan (OSS)
2026-11-12 7:53 ` [PATCH v6 9/9] arm64: dts: imx95: convert eDMA to per-channel " Peng Fan (OSS)
8 siblings, 1 reply; 14+ messages in thread
From: Peng Fan (OSS) @ 2026-11-12 7:53 UTC (permalink / raw)
To: Frank Li, Vinod Koul, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Laxman Dewangan, Jon Hunter,
Thierry Reding, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Frank Li, Larisa IIeana Grigore, imx, dmaengine, linux-kernel,
iommu, linux-tegra, devicetree, linux-arm-kernel, Peng Fan
From: Peng Fan <peng.fan@nxp.com>
Use dmaengine_get_dma_device() instead of chan->device->dev when creating
the TCD pool and when mapping the slave resource. This lets the driver use
the per-channel device once per-channel IOMMU support enables it, without
changing behaviour for existing users.
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
drivers/dma/fsl-edma-common.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c
index 513b0f4e5be2..62cd60179bc6 100644
--- a/drivers/dma/fsl-edma-common.c
+++ b/drivers/dma/fsl-edma-common.c
@@ -310,10 +310,10 @@ int fsl_edma_resume(struct dma_chan *chan)
static void fsl_edma_unprep_slave_dma(struct fsl_edma_chan *fsl_chan)
{
+ struct device *dev = dmaengine_get_dma_device(&fsl_chan->vchan.chan);
+
if (fsl_chan->dma_dir != DMA_NONE)
- dma_unmap_resource(fsl_chan->vchan.chan.device->dev,
- fsl_chan->dma_dev_addr,
- fsl_chan->dma_dev_size,
+ dma_unmap_resource(dev, fsl_chan->dma_dev_addr, fsl_chan->dma_dev_size,
fsl_chan->dma_dir, 0);
fsl_chan->dma_dir = DMA_NONE;
}
@@ -321,7 +321,7 @@ static void fsl_edma_unprep_slave_dma(struct fsl_edma_chan *fsl_chan)
static bool fsl_edma_prep_slave_dma(struct fsl_edma_chan *fsl_chan,
enum dma_transfer_direction dir)
{
- struct device *dev = fsl_chan->vchan.chan.device->dev;
+ struct device *dev = dmaengine_get_dma_device(&fsl_chan->vchan.chan);
enum dma_data_direction dma_dir;
phys_addr_t addr = 0;
u32 size = 0;
@@ -1090,7 +1090,7 @@ int fsl_edma_alloc_chan_resources(struct dma_chan *chan)
if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_HAS_CHCLK)
clk_prepare_enable(fsl_chan->clk);
- fsl_chan->tcd_pool = dma_pool_create("tcd_pool", chan->device->dev,
+ fsl_chan->tcd_pool = dma_pool_create("tcd_pool", dmaengine_get_dma_device(chan),
fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_TCD64 ?
sizeof(struct fsl_edma_hw_tcd64) : sizeof(struct fsl_edma_hw_tcd),
32, 0);
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v6 8/9] dmaengine: fsl-edma: support per-channel DT child nodes
2026-11-12 7:53 [PATCH v6 0/9] dmaengine: add DMA channel bus and fsl-edma per-channel IOMMU support Peng Fan (OSS)
` (6 preceding siblings ...)
2026-11-12 7:53 ` [PATCH v6 7/9] dmaengine: fsl-edma: use dmaengine_get_dma_device() Peng Fan (OSS)
@ 2026-11-12 7:53 ` Peng Fan (OSS)
2026-11-12 7:53 ` [PATCH v6 9/9] arm64: dts: imx95: convert eDMA to per-channel " Peng Fan (OSS)
8 siblings, 0 replies; 14+ messages in thread
From: Peng Fan (OSS) @ 2026-11-12 7:53 UTC (permalink / raw)
To: Frank Li, Vinod Koul, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Laxman Dewangan, Jon Hunter,
Thierry Reding, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Frank Li, Larisa IIeana Grigore, imx, dmaengine, linux-kernel,
iommu, linux-tegra, devicetree, linux-arm-kernel, Peng Fan
From: Peng Fan <peng.fan@nxp.com>
Add support for parsing per-channel child nodes from devicetree.
Each child node can carry its own interrupts and iommus properties.
Child power-domains are not handled here; power domains remain attached
at the parent level.
The implementation is split into three functions:
- fsl_edma_child_to_chan_id()
- fsl_edma_configure_chan_from_dt()
- fsl_edma_configure_chan_iommu()
Child nodes beyond the configured dma-channels are ignored, which allows
boards to reduce dma-channels without disabling every unused child node.
If child nodes are present, every unmasked channel must have a child node
with a valid interrupt; otherwise probe fails with an error asking for
dma-channel-mask. In child-node mode fsl_edma3_irq_init() does not fall
back to the parent's flat interrupt array.
The shared error IRQ remains a parent-node interrupt. A child-node-only
DTS without that entry simply has no shared error IRQ.
Use dev_err_probe() so -EPROBE_DEFER is not logged as an error, and reject
a channel whose iommus property did not result in an IOMMU mapping.
Existing DTS without child nodes continues to work unchanged.
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
drivers/dma/fsl-edma-common.h | 1 +
drivers/dma/fsl-edma-main.c | 199 ++++++++++++++++++++++++++++++++++++++++--
2 files changed, 194 insertions(+), 6 deletions(-)
diff --git a/drivers/dma/fsl-edma-common.h b/drivers/dma/fsl-edma-common.h
index cbb077e98f55..8843dd5661fb 100644
--- a/drivers/dma/fsl-edma-common.h
+++ b/drivers/dma/fsl-edma-common.h
@@ -271,6 +271,7 @@ struct fsl_edma_engine {
int txirq_16_31;
int errirq;
bool big_endian;
+ bool per_chan_irqs;
struct edma_regs regs;
u64 chan_masked;
struct fsl_edma_chan chans[] __counted_by(n_chans);
diff --git a/drivers/dma/fsl-edma-main.c b/drivers/dma/fsl-edma-main.c
index 818b9b986f13..88d2b9d6b6ab 100644
--- a/drivers/dma/fsl-edma-main.c
+++ b/drivers/dma/fsl-edma-main.c
@@ -12,11 +12,15 @@
#include <dt-bindings/dma/fsl-edma.h>
#include <linux/bitfield.h>
+#include <linux/math64.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
#include <linux/of_dma.h>
+#include <linux/of_irq.h>
#include <linux/dma-mapping.h>
#include <linux/pm_runtime.h>
#include <linux/pm_domain.h>
@@ -400,12 +404,21 @@ static int fsl_edma3_irq_init(struct platform_device *pdev, struct fsl_edma_engi
if (fsl_edma->chan_masked & BIT_ULL(i))
continue;
- /* request channel irq */
- fsl_chan->txirq = platform_get_irq(pdev, i);
- if (fsl_chan->txirq < 0)
- return -EINVAL;
+ if (fsl_chan->txirq <= 0) {
+ /*
+ * In child-node mode, missing child nodes are rejected
+ * by fsl_edma_configure_chan_from_dt(); do not fall
+ * back to the parent's flat interrupt array.
+ */
+ if (fsl_edma->per_chan_irqs)
+ continue;
- fsl_chan->irq_handler = fsl_edma3_tx_handler;
+ fsl_chan->txirq = platform_get_irq(pdev, i);
+ if (fsl_chan->txirq < 0)
+ return fsl_chan->txirq;
+
+ fsl_chan->irq_handler = fsl_edma3_tx_handler;
+ }
if (!(fsl_edma->drvdata->flags & FSL_EDMA_DRV_ERRIRQ_SHARE)) {
fsl_chan->errirq = fsl_chan->txirq;
@@ -415,7 +428,11 @@ static int fsl_edma3_irq_init(struct platform_device *pdev, struct fsl_edma_engi
/* All channel err use one irq number */
if (fsl_edma->drvdata->flags & FSL_EDMA_DRV_ERRIRQ_SHARE) {
- /* last one is error irq */
+ /*
+ * The shared error IRQ remains a parent-node interrupt at
+ * index n_chans. A child-node-only DTS without that entry
+ * simply has no shared error IRQ.
+ */
fsl_edma->errirq = platform_get_irq_optional(pdev, fsl_edma->n_chans);
if (fsl_edma->errirq < 0)
return 0; /* dts miss err irq, treat as no err irq case */
@@ -703,6 +720,168 @@ static int fsl_edma3_attach_pd(struct platform_device *pdev, struct fsl_edma_eng
return -EINVAL;
}
+static int fsl_edma_child_to_chan_id(struct fsl_edma_engine *fsl_edma,
+ struct device_node *child,
+ struct resource *parent_res)
+{
+ const struct fsl_edma_drvdata *drvdata = fsl_edma->drvdata;
+ struct resource child_res;
+ u64 offset, ch_id;
+ u32 rem;
+ int ret;
+
+ ret = of_address_to_resource(child, 0, &child_res);
+ if (ret)
+ return ret;
+
+ if (!resource_contains(parent_res, &child_res))
+ return -EINVAL;
+
+ if (child_res.start < parent_res->start + drvdata->chreg_off)
+ return -EINVAL;
+
+ offset = child_res.start - parent_res->start - drvdata->chreg_off;
+ ch_id = div_u64_rem(offset, drvdata->chreg_space_sz, &rem);
+ if (rem)
+ return -EINVAL;
+ if (ch_id > INT_MAX)
+ return -EINVAL;
+
+ return ch_id;
+}
+
+static int fsl_edma_configure_chan_from_dt(struct platform_device *pdev,
+ struct fsl_edma_engine *fsl_edma)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct resource parent_res;
+ u64 child_mask = 0;
+ bool has_children = false;
+ int i, ret;
+
+ ret = of_address_to_resource(np, 0, &parent_res);
+ if (ret)
+ return ret;
+
+ for_each_available_child_of_node_scoped(np, child) {
+ struct fsl_edma_chan *fsl_chan;
+ int ch_id, irq;
+
+ has_children = true;
+
+ ch_id = fsl_edma_child_to_chan_id(fsl_edma, child,
+ &parent_res);
+ if (ch_id < 0) {
+ dev_err(&pdev->dev, "invalid channel in %pOFn\n", child);
+ return -EINVAL;
+ }
+
+ if (ch_id >= fsl_edma->n_chans)
+ continue;
+
+ if (fsl_edma->chan_masked & BIT_ULL(ch_id))
+ continue;
+
+ child_mask |= BIT_ULL(ch_id);
+
+ fsl_chan = &fsl_edma->chans[ch_id];
+
+ irq = of_irq_get(child, 0);
+ if (irq == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ if (irq <= 0) {
+ dev_err(&pdev->dev,
+ "channel %d child node has no valid interrupt; add interrupts or disable it with dma-channel-mask\n",
+ ch_id);
+ return irq < 0 ? irq : -EINVAL;
+ }
+
+ fsl_chan->txirq = irq;
+ fsl_chan->irq_handler = fsl_edma3_tx_handler;
+ }
+
+ if (!has_children)
+ return 0;
+
+ fsl_edma->per_chan_irqs = true;
+
+ for (i = 0; i < fsl_edma->n_chans; i++) {
+ if (fsl_edma->chan_masked & BIT_ULL(i))
+ continue;
+ if (child_mask & BIT_ULL(i))
+ continue;
+
+ dev_err(&pdev->dev,
+ "channel %d has no dma-channel child node; add one or disable it with dma-channel-mask\n",
+ i);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fsl_edma_configure_chan_iommu(struct platform_device *pdev,
+ struct fsl_edma_engine *fsl_edma)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct resource parent_res;
+ int ret;
+
+ ret = of_address_to_resource(np, 0, &parent_res);
+ if (ret)
+ return ret;
+
+ for_each_available_child_of_node_scoped(np, child) {
+ struct fsl_edma_chan *fsl_chan;
+ struct dma_chan *chan;
+ struct device *chdev;
+ int ch_id;
+
+ if (!of_property_present(child, "iommus"))
+ continue;
+
+ ch_id = fsl_edma_child_to_chan_id(fsl_edma, child,
+ &parent_res);
+ if (ch_id < 0)
+ continue;
+
+ if (ch_id >= fsl_edma->n_chans)
+ continue;
+
+ if (fsl_edma->chan_masked & BIT_ULL(ch_id))
+ continue;
+
+ fsl_chan = &fsl_edma->chans[ch_id];
+ chan = &fsl_chan->vchan.chan;
+ chdev = dmaengine_chan_dev(chan);
+ chdev->dma_parms = pdev->dev.dma_parms;
+ if (fsl_edma->drvdata->flags & FSL_EDMA_DRV_TCD64)
+ dma_coerce_mask_and_coherent(chdev,
+ DMA_BIT_MASK(64));
+ else
+ dma_coerce_mask_and_coherent(chdev,
+ DMA_BIT_MASK(32));
+ ret = of_dma_configure(chdev, child, true);
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret,
+ "IOMMU config failed for channel %d\n",
+ ch_id);
+ return ret;
+ }
+
+ if (!device_iommu_mapped(chdev)) {
+ dev_err_probe(&pdev->dev, -ENODEV,
+ "channel %d is not behind an IOMMU\n",
+ ch_id);
+ return -ENODEV;
+ }
+
+ chan->chan_dev->chan_dma_dev = true;
+ }
+
+ return 0;
+}
+
static int fsl_edma_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@@ -835,6 +1014,10 @@ static int fsl_edma_probe(struct platform_device *pdev)
clk_disable_unprepare(fsl_chan->clk);
}
+ ret = fsl_edma_configure_chan_from_dt(pdev, fsl_edma);
+ if (ret)
+ return ret;
+
ret = fsl_edma->drvdata->setup_irq(pdev, fsl_edma);
if (ret)
return ret;
@@ -897,6 +1080,10 @@ static int fsl_edma_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, ret,
"Can't register Freescale eDMA engine.\n");
+ ret = fsl_edma_configure_chan_iommu(pdev, fsl_edma);
+ if (ret)
+ return ret;
+
ret = devm_of_dma_controller_register(&pdev->dev, np,
drvdata->dmamuxs ? fsl_edma_xlate : fsl_edma3_xlate,
fsl_edma);
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v6 9/9] arm64: dts: imx95: convert eDMA to per-channel child nodes
2026-11-12 7:53 [PATCH v6 0/9] dmaengine: add DMA channel bus and fsl-edma per-channel IOMMU support Peng Fan (OSS)
` (7 preceding siblings ...)
2026-11-12 7:53 ` [PATCH v6 8/9] dmaengine: fsl-edma: support per-channel DT child nodes Peng Fan (OSS)
@ 2026-11-12 7:53 ` Peng Fan (OSS)
8 siblings, 0 replies; 14+ messages in thread
From: Peng Fan (OSS) @ 2026-11-12 7:53 UTC (permalink / raw)
To: Frank Li, Vinod Koul, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Laxman Dewangan, Jon Hunter,
Thierry Reding, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Frank Li, Larisa IIeana Grigore, imx, dmaengine, linux-kernel,
iommu, linux-tegra, devicetree, linux-arm-kernel, Peng Fan
From: Peng Fan <peng.fan@nxp.com>
Convert edma2 and edma3 from flat interrupt arrays at the parent node
to per-channel child nodes (dma-channel@<addr>). Each child carries
its own reg (real per-channel MMIO region) and interrupts property.
edma2 (64 channels at 0x42000000):
- Channel N at 0x42010000 + N * 0x8000 (size 0x8000)
- Paired interrupts: SPI 128-159, two channels per SPI
edma3 (64 channels at 0x42210000):
- Channel N at 0x42220000 + N * 0x8000 (size 0x8000)
- Paired interrupts: SPI 256-287, two channels per SPI
edma1 (imx93-edma3, 31 channels with 1:1 non-paired interrupts) is
left unchanged as it uses a different interrupt scheme.
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
arch/arm64/boot/dts/freescale/imx95.dtsi | 774 ++++++++++++++++++++++++++-----
1 file changed, 646 insertions(+), 128 deletions(-)
diff --git a/arch/arm64/boot/dts/freescale/imx95.dtsi b/arch/arm64/boot/dts/freescale/imx95.dtsi
index d20c2498b9aa..64570703ce6c 100644
--- a/arch/arm64/boot/dts/freescale/imx95.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx95.dtsi
@@ -635,148 +635,666 @@ edma2: dma-controller@42000000 {
compatible = "fsl,imx95-edma5";
reg = <0x42000000 0x210000>;
#dma-cells = <3>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
dma-channels = <64>;
/* channels 0 and 1 reserved for V2X fast hash */
dma-channel-mask = <0x3>;
- interrupts = <GIC_SPI 128 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 128 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 132 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 132 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 134 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 134 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 135 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 135 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 136 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 136 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 137 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 137 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 139 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 139 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 145 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 145 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 149 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 149 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 150 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 150 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 151 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 151 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 152 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 152 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 153 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 153 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 154 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 154 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 155 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 155 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 156 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 156 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 157 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 157 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 158 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 158 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 159 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 159 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&scmi_clk IMX95_CLK_BUSWAKEUP>;
clock-names = "dma";
+
+ dma-channel@42010000 {
+ reg = <0x42010000 0x8000>;
+ interrupts = <GIC_SPI 128 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42018000 {
+ reg = <0x42018000 0x8000>;
+ interrupts = <GIC_SPI 128 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42020000 {
+ reg = <0x42020000 0x8000>;
+ interrupts = <GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42028000 {
+ reg = <0x42028000 0x8000>;
+ interrupts = <GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42030000 {
+ reg = <0x42030000 0x8000>;
+ interrupts = <GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42038000 {
+ reg = <0x42038000 0x8000>;
+ interrupts = <GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42040000 {
+ reg = <0x42040000 0x8000>;
+ interrupts = <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42048000 {
+ reg = <0x42048000 0x8000>;
+ interrupts = <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42050000 {
+ reg = <0x42050000 0x8000>;
+ interrupts = <GIC_SPI 132 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42058000 {
+ reg = <0x42058000 0x8000>;
+ interrupts = <GIC_SPI 132 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42060000 {
+ reg = <0x42060000 0x8000>;
+ interrupts = <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42068000 {
+ reg = <0x42068000 0x8000>;
+ interrupts = <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42070000 {
+ reg = <0x42070000 0x8000>;
+ interrupts = <GIC_SPI 134 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42078000 {
+ reg = <0x42078000 0x8000>;
+ interrupts = <GIC_SPI 134 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42080000 {
+ reg = <0x42080000 0x8000>;
+ interrupts = <GIC_SPI 135 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42088000 {
+ reg = <0x42088000 0x8000>;
+ interrupts = <GIC_SPI 135 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42090000 {
+ reg = <0x42090000 0x8000>;
+ interrupts = <GIC_SPI 136 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42098000 {
+ reg = <0x42098000 0x8000>;
+ interrupts = <GIC_SPI 136 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@420a0000 {
+ reg = <0x420a0000 0x8000>;
+ interrupts = <GIC_SPI 137 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@420a8000 {
+ reg = <0x420a8000 0x8000>;
+ interrupts = <GIC_SPI 137 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@420b0000 {
+ reg = <0x420b0000 0x8000>;
+ interrupts = <GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@420b8000 {
+ reg = <0x420b8000 0x8000>;
+ interrupts = <GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@420c0000 {
+ reg = <0x420c0000 0x8000>;
+ interrupts = <GIC_SPI 139 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@420c8000 {
+ reg = <0x420c8000 0x8000>;
+ interrupts = <GIC_SPI 139 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@420d0000 {
+ reg = <0x420d0000 0x8000>;
+ interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@420d8000 {
+ reg = <0x420d8000 0x8000>;
+ interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@420e0000 {
+ reg = <0x420e0000 0x8000>;
+ interrupts = <GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@420e8000 {
+ reg = <0x420e8000 0x8000>;
+ interrupts = <GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@420f0000 {
+ reg = <0x420f0000 0x8000>;
+ interrupts = <GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@420f8000 {
+ reg = <0x420f8000 0x8000>;
+ interrupts = <GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42100000 {
+ reg = <0x42100000 0x8000>;
+ interrupts = <GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42108000 {
+ reg = <0x42108000 0x8000>;
+ interrupts = <GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42110000 {
+ reg = <0x42110000 0x8000>;
+ interrupts = <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42118000 {
+ reg = <0x42118000 0x8000>;
+ interrupts = <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42120000 {
+ reg = <0x42120000 0x8000>;
+ interrupts = <GIC_SPI 145 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42128000 {
+ reg = <0x42128000 0x8000>;
+ interrupts = <GIC_SPI 145 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42130000 {
+ reg = <0x42130000 0x8000>;
+ interrupts = <GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42138000 {
+ reg = <0x42138000 0x8000>;
+ interrupts = <GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42140000 {
+ reg = <0x42140000 0x8000>;
+ interrupts = <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42148000 {
+ reg = <0x42148000 0x8000>;
+ interrupts = <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42150000 {
+ reg = <0x42150000 0x8000>;
+ interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42158000 {
+ reg = <0x42158000 0x8000>;
+ interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42160000 {
+ reg = <0x42160000 0x8000>;
+ interrupts = <GIC_SPI 149 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42168000 {
+ reg = <0x42168000 0x8000>;
+ interrupts = <GIC_SPI 149 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42170000 {
+ reg = <0x42170000 0x8000>;
+ interrupts = <GIC_SPI 150 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42178000 {
+ reg = <0x42178000 0x8000>;
+ interrupts = <GIC_SPI 150 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42180000 {
+ reg = <0x42180000 0x8000>;
+ interrupts = <GIC_SPI 151 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42188000 {
+ reg = <0x42188000 0x8000>;
+ interrupts = <GIC_SPI 151 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42190000 {
+ reg = <0x42190000 0x8000>;
+ interrupts = <GIC_SPI 152 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42198000 {
+ reg = <0x42198000 0x8000>;
+ interrupts = <GIC_SPI 152 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@421a0000 {
+ reg = <0x421a0000 0x8000>;
+ interrupts = <GIC_SPI 153 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@421a8000 {
+ reg = <0x421a8000 0x8000>;
+ interrupts = <GIC_SPI 153 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@421b0000 {
+ reg = <0x421b0000 0x8000>;
+ interrupts = <GIC_SPI 154 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@421b8000 {
+ reg = <0x421b8000 0x8000>;
+ interrupts = <GIC_SPI 154 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@421c0000 {
+ reg = <0x421c0000 0x8000>;
+ interrupts = <GIC_SPI 155 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@421c8000 {
+ reg = <0x421c8000 0x8000>;
+ interrupts = <GIC_SPI 155 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@421d0000 {
+ reg = <0x421d0000 0x8000>;
+ interrupts = <GIC_SPI 156 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@421d8000 {
+ reg = <0x421d8000 0x8000>;
+ interrupts = <GIC_SPI 156 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@421e0000 {
+ reg = <0x421e0000 0x8000>;
+ interrupts = <GIC_SPI 157 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@421e8000 {
+ reg = <0x421e8000 0x8000>;
+ interrupts = <GIC_SPI 157 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@421f0000 {
+ reg = <0x421f0000 0x8000>;
+ interrupts = <GIC_SPI 158 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@421f8000 {
+ reg = <0x421f8000 0x8000>;
+ interrupts = <GIC_SPI 158 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42200000 {
+ reg = <0x42200000 0x8000>;
+ interrupts = <GIC_SPI 159 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42208000 {
+ reg = <0x42208000 0x8000>;
+ interrupts = <GIC_SPI 159 IRQ_TYPE_LEVEL_HIGH>;
+ };
};
edma3: dma-controller@42210000 {
compatible = "fsl,imx95-edma5";
reg = <0x42210000 0x210000>;
#dma-cells = <3>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
dma-channels = <64>;
- interrupts = <GIC_SPI 256 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 256 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 257 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 257 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 258 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 258 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 259 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 259 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 260 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 260 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 261 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 261 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 262 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 262 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 263 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 263 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 264 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 264 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 265 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 265 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 266 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 266 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 267 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 267 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 268 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 268 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 269 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 269 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 270 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 270 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 271 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 271 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 272 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 272 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 273 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 273 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 274 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 274 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 275 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 275 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 276 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 276 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 277 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 277 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 278 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 278 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 279 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 279 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 280 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 280 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 281 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 281 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 282 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 282 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 283 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 283 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 284 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 284 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 285 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 285 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 286 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 286 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 287 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 287 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&scmi_clk IMX95_CLK_BUSWAKEUP>;
clock-names = "dma";
+
+ dma-channel@42220000 {
+ reg = <0x42220000 0x8000>;
+ interrupts = <GIC_SPI 256 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42228000 {
+ reg = <0x42228000 0x8000>;
+ interrupts = <GIC_SPI 256 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42230000 {
+ reg = <0x42230000 0x8000>;
+ interrupts = <GIC_SPI 257 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42238000 {
+ reg = <0x42238000 0x8000>;
+ interrupts = <GIC_SPI 257 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42240000 {
+ reg = <0x42240000 0x8000>;
+ interrupts = <GIC_SPI 258 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42248000 {
+ reg = <0x42248000 0x8000>;
+ interrupts = <GIC_SPI 258 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42250000 {
+ reg = <0x42250000 0x8000>;
+ interrupts = <GIC_SPI 259 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42258000 {
+ reg = <0x42258000 0x8000>;
+ interrupts = <GIC_SPI 259 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42260000 {
+ reg = <0x42260000 0x8000>;
+ interrupts = <GIC_SPI 260 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42268000 {
+ reg = <0x42268000 0x8000>;
+ interrupts = <GIC_SPI 260 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42270000 {
+ reg = <0x42270000 0x8000>;
+ interrupts = <GIC_SPI 261 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42278000 {
+ reg = <0x42278000 0x8000>;
+ interrupts = <GIC_SPI 261 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42280000 {
+ reg = <0x42280000 0x8000>;
+ interrupts = <GIC_SPI 262 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42288000 {
+ reg = <0x42288000 0x8000>;
+ interrupts = <GIC_SPI 262 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42290000 {
+ reg = <0x42290000 0x8000>;
+ interrupts = <GIC_SPI 263 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42298000 {
+ reg = <0x42298000 0x8000>;
+ interrupts = <GIC_SPI 263 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@422a0000 {
+ reg = <0x422a0000 0x8000>;
+ interrupts = <GIC_SPI 264 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@422a8000 {
+ reg = <0x422a8000 0x8000>;
+ interrupts = <GIC_SPI 264 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@422b0000 {
+ reg = <0x422b0000 0x8000>;
+ interrupts = <GIC_SPI 265 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@422b8000 {
+ reg = <0x422b8000 0x8000>;
+ interrupts = <GIC_SPI 265 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@422c0000 {
+ reg = <0x422c0000 0x8000>;
+ interrupts = <GIC_SPI 266 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@422c8000 {
+ reg = <0x422c8000 0x8000>;
+ interrupts = <GIC_SPI 266 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@422d0000 {
+ reg = <0x422d0000 0x8000>;
+ interrupts = <GIC_SPI 267 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@422d8000 {
+ reg = <0x422d8000 0x8000>;
+ interrupts = <GIC_SPI 267 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@422e0000 {
+ reg = <0x422e0000 0x8000>;
+ interrupts = <GIC_SPI 268 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@422e8000 {
+ reg = <0x422e8000 0x8000>;
+ interrupts = <GIC_SPI 268 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@422f0000 {
+ reg = <0x422f0000 0x8000>;
+ interrupts = <GIC_SPI 269 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@422f8000 {
+ reg = <0x422f8000 0x8000>;
+ interrupts = <GIC_SPI 269 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42300000 {
+ reg = <0x42300000 0x8000>;
+ interrupts = <GIC_SPI 270 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42308000 {
+ reg = <0x42308000 0x8000>;
+ interrupts = <GIC_SPI 270 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42310000 {
+ reg = <0x42310000 0x8000>;
+ interrupts = <GIC_SPI 271 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42318000 {
+ reg = <0x42318000 0x8000>;
+ interrupts = <GIC_SPI 271 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42320000 {
+ reg = <0x42320000 0x8000>;
+ interrupts = <GIC_SPI 272 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42328000 {
+ reg = <0x42328000 0x8000>;
+ interrupts = <GIC_SPI 272 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42330000 {
+ reg = <0x42330000 0x8000>;
+ interrupts = <GIC_SPI 273 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42338000 {
+ reg = <0x42338000 0x8000>;
+ interrupts = <GIC_SPI 273 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42340000 {
+ reg = <0x42340000 0x8000>;
+ interrupts = <GIC_SPI 274 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42348000 {
+ reg = <0x42348000 0x8000>;
+ interrupts = <GIC_SPI 274 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42350000 {
+ reg = <0x42350000 0x8000>;
+ interrupts = <GIC_SPI 275 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42358000 {
+ reg = <0x42358000 0x8000>;
+ interrupts = <GIC_SPI 275 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42360000 {
+ reg = <0x42360000 0x8000>;
+ interrupts = <GIC_SPI 276 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42368000 {
+ reg = <0x42368000 0x8000>;
+ interrupts = <GIC_SPI 276 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42370000 {
+ reg = <0x42370000 0x8000>;
+ interrupts = <GIC_SPI 277 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42378000 {
+ reg = <0x42378000 0x8000>;
+ interrupts = <GIC_SPI 277 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42380000 {
+ reg = <0x42380000 0x8000>;
+ interrupts = <GIC_SPI 278 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42388000 {
+ reg = <0x42388000 0x8000>;
+ interrupts = <GIC_SPI 278 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42390000 {
+ reg = <0x42390000 0x8000>;
+ interrupts = <GIC_SPI 279 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42398000 {
+ reg = <0x42398000 0x8000>;
+ interrupts = <GIC_SPI 279 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@423a0000 {
+ reg = <0x423a0000 0x8000>;
+ interrupts = <GIC_SPI 280 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@423a8000 {
+ reg = <0x423a8000 0x8000>;
+ interrupts = <GIC_SPI 280 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@423b0000 {
+ reg = <0x423b0000 0x8000>;
+ interrupts = <GIC_SPI 281 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@423b8000 {
+ reg = <0x423b8000 0x8000>;
+ interrupts = <GIC_SPI 281 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@423c0000 {
+ reg = <0x423c0000 0x8000>;
+ interrupts = <GIC_SPI 282 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@423c8000 {
+ reg = <0x423c8000 0x8000>;
+ interrupts = <GIC_SPI 282 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@423d0000 {
+ reg = <0x423d0000 0x8000>;
+ interrupts = <GIC_SPI 283 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@423d8000 {
+ reg = <0x423d8000 0x8000>;
+ interrupts = <GIC_SPI 283 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@423e0000 {
+ reg = <0x423e0000 0x8000>;
+ interrupts = <GIC_SPI 284 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@423e8000 {
+ reg = <0x423e8000 0x8000>;
+ interrupts = <GIC_SPI 284 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@423f0000 {
+ reg = <0x423f0000 0x8000>;
+ interrupts = <GIC_SPI 285 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@423f8000 {
+ reg = <0x423f8000 0x8000>;
+ interrupts = <GIC_SPI 285 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42400000 {
+ reg = <0x42400000 0x8000>;
+ interrupts = <GIC_SPI 286 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42408000 {
+ reg = <0x42408000 0x8000>;
+ interrupts = <GIC_SPI 286 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42410000 {
+ reg = <0x42410000 0x8000>;
+ interrupts = <GIC_SPI 287 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ dma-channel@42418000 {
+ reg = <0x42418000 0x8000>;
+ interrupts = <GIC_SPI 287 IRQ_TYPE_LEVEL_HIGH>;
+ };
};
mu7: mailbox@42430000 {
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v6 2/9] dmaengine: add a bus for DMA channel devices
2026-11-12 7:53 ` [PATCH v6 2/9] dmaengine: add a bus for DMA channel devices Peng Fan (OSS)
@ 2026-11-12 15:47 ` Frank Li
0 siblings, 0 replies; 14+ messages in thread
From: Frank Li @ 2026-11-12 15:47 UTC (permalink / raw)
To: Peng Fan (OSS)
Cc: Frank Li, Vinod Koul, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Laxman Dewangan, Jon Hunter,
Thierry Reding, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, Frank Li,
Larisa IIeana Grigore, imx, dmaengine, linux-kernel, iommu,
linux-tegra, devicetree, linux-arm-kernel, Peng Fan
On Thu, Nov 12, 2026 at 03:53:05PM +0800, Peng Fan (OSS) wrote:
> From: Peng Fan <peng.fan@nxp.com>
>
> DMA channel devices are currently created as class devices without a bus.
> This works for sysfs, but it prevents per-channel DMA/IOMMU configuration:
> of_dma_configure() only probes the IOMMU when dev->bus is set, and the
> IOMMU core only installs probe/release notifiers for buses listed in
> iommu_buses[].
>
> Drivers that need per-channel IOMMU support currently work around this by
> assigning dev->bus after device_register(). That leaves the bus subsystem
> reference count unbalanced because bus_add_device() already ran with a NULL
> bus, and bus_remove_device() will later drop a reference that was never
> taken. For example, tegra186-gpc-dma.c assigns chdev->bus after channel
> registration. Review noted that this can underflow the platform bus
> reference count and lead to a use-after-free when the device is removed.
>
> Add a dedicated bus for DMA channel devices and register it during
> dmaengine initialisation. Use core_initcall so the bus exists before any
> IOMMU driver can call iommu_device_register(), which iterates iommu_buses[]
> and fails on an unregistered bus. The match callback always reports no
> match, so no driver can bind to these devices. A follow-up patch will
> register the channel devices on this bus.
>
> Clean up the unmap pools if dmaengine initialisation fails after they have
> been allocated.
>
> Signed-off-by: Peng Fan <peng.fan@nxp.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> drivers/dma/dmaengine.c | 28 +++++++++++++++++++++++++---
> include/linux/dmaengine.h | 4 ++++
> 2 files changed, 29 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
> index 34be53487a21..e1022d297674 100644
> --- a/drivers/dma/dmaengine.c
> +++ b/drivers/dma/dmaengine.c
> @@ -1643,6 +1643,17 @@ void dma_run_dependencies(struct dma_async_tx_descriptor *tx)
> }
> EXPORT_SYMBOL_GPL(dma_run_dependencies);
>
> +static int dma_chan_bus_match(struct device *dev,
> + const struct device_driver *drv)
> +{
> + return 0;
> +}
> +
> +const struct bus_type dmaengine_chan_bus_type = {
> + .name = "dmaengine-chan",
> + .match = dma_chan_bus_match,
> +};
> +
> static int __init dma_bus_init(void)
> {
> int err = dmaengine_init_unmap_pool();
> @@ -1650,10 +1661,21 @@ static int __init dma_bus_init(void)
> if (err)
> return err;
>
> + err = bus_register(&dmaengine_chan_bus_type);
> + if (err)
> + goto err_destroy_unmap_pool;
> +
> err = class_register(&dma_devclass);
> - if (!err)
> - dmaengine_debugfs_init();
> + if (err)
> + goto err_unregister_bus;
>
> + dmaengine_debugfs_init();
> + return 0;
> +
> +err_unregister_bus:
> + bus_unregister(&dmaengine_chan_bus_type);
> +err_destroy_unmap_pool:
> + dmaengine_destroy_unmap_pool();
> return err;
> }
> -arch_initcall(dma_bus_init);
> +core_initcall(dma_bus_init);
> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
> index 805668fbe437..be9308739676 100644
> --- a/include/linux/dmaengine.h
> +++ b/include/linux/dmaengine.h
> @@ -412,6 +412,10 @@ struct dma_chan_dev {
> bool chan_dma_dev;
> };
>
> +#ifdef CONFIG_DMA_ENGINE
> +extern const struct bus_type dmaengine_chan_bus_type;
> +#endif
> +
> /**
> * enum dma_slave_buswidth - defines bus width of the DMA slave
> * device, source or target buses
>
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v6 4/9] dmaengine: move channel devices to the DMA channel bus
2026-11-12 7:53 ` [PATCH v6 4/9] dmaengine: move channel devices to the DMA channel bus Peng Fan (OSS)
@ 2026-11-12 16:04 ` Frank Li
0 siblings, 0 replies; 14+ messages in thread
From: Frank Li @ 2026-11-12 16:04 UTC (permalink / raw)
To: Peng Fan (OSS)
Cc: Frank Li, Vinod Koul, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Laxman Dewangan, Jon Hunter,
Thierry Reding, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, Frank Li,
Larisa IIeana Grigore, imx, dmaengine, linux-kernel, iommu,
linux-tegra, devicetree, linux-arm-kernel, Peng Fan
On Thu, Nov 12, 2026 at 03:53:07PM +0800, Peng Fan (OSS) wrote:
> From: Peng Fan <peng.fan@nxp.com>
>
> Register each dma_chan_dev on dmaengine_chan_bus_type before
> device_register() and make it a bus device instead of a class device.
> Setting the bus before registration lets bus_add_device() take the bus
> subsystem reference, so the matching bus_remove_device() calls drop
> references that were actually acquired. Attach dma_dev_groups to the bus
> and set the device release callback directly.
>
> Keep the existing /sys/class/dma interface by registering a class_compat
> named "dma" and creating a link for each channel device. The channel
> devices now live on the DMA bus, while /sys/class/dma/dmaNchanM remains
> available to userspace.
>
> The bus is named "dmaengine-chan" rather than "dma" to avoid colliding
> with the SuperH DMA bus, which is also named "dma" and is registered later
> from a postcore_initcall. As a result the device's uevent SUBSYSTEM and
> "subsystem" link use "dmaengine-chan", while /sys/class/dma is unchanged.
>
> Remove the post-registration dev->bus assignment from the Tegra GPC DMA
> driver. Leaving it in place would overwrite the bus selected by the DMA
> engine core and reintroduce the reference imbalance.
>
> The DMA channel bus match callback never matches, so no driver can bind
> to these channel devices.
>
> Signed-off-by: Peng Fan <peng.fan@nxp.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> drivers/dma/dmaengine.c | 23 +++++++++++++++--------
> drivers/dma/tegra186-gpc-dma.c | 1 -
> 2 files changed, 15 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
> index e1022d297674..45e328d28689 100644
> --- a/drivers/dma/dmaengine.c
> +++ b/drivers/dma/dmaengine.c
> @@ -239,11 +239,7 @@ static void chan_dev_release(struct device *dev)
> kfree(chan_dev);
> }
>
> -static struct class dma_devclass = {
> - .name = "dma",
> - .dev_groups = dma_dev_groups,
> - .dev_release = chan_dev_release,
> -};
> +static struct class_compat *dma_chan_class;
>
> /* --- client and device registration --- */
>
> @@ -1115,8 +1111,9 @@ static int __dma_async_device_channel_register(struct dma_device *device,
> goto err_free_dev;
> }
>
> - dmaengine_chan_dev(chan)->class = &dma_devclass;
> dmaengine_chan_dev(chan)->parent = device->dev;
> + dmaengine_chan_dev(chan)->bus = &dmaengine_chan_bus_type;
> + dmaengine_chan_dev(chan)->release = chan_dev_release;
> chan->chan_dev->chan = chan;
> chan->chan_dev->dev_id = device->dev_id;
> spin_lock_init(&chan->lock);
> @@ -1128,6 +1125,11 @@ static int __dma_async_device_channel_register(struct dma_device *device,
> rc = device_register(dmaengine_chan_dev(chan));
> if (rc)
> goto err_out_ida;
> +
> + rc = class_compat_create_link(dma_chan_class, dmaengine_chan_dev(chan));
> + if (rc)
> + dev_warn(device->dev, "Failed to create compatibility class link\n");
> +
> chan->client_count = 0;
> device->chancnt++;
>
> @@ -1172,6 +1174,7 @@ static void __dma_async_device_channel_unregister(struct dma_device *device,
> chan->chan_dev->chan = NULL;
> mutex_unlock(&dma_list_mutex);
> ida_free(&device->chan_ida, chan->chan_id);
> + class_compat_remove_link(dma_chan_class, dmaengine_chan_dev(chan));
> device_unregister(dmaengine_chan_dev(chan));
> free_percpu(chan->local);
> }
> @@ -1306,6 +1309,7 @@ int dma_async_device_register(struct dma_device *device)
> mutex_lock(&dma_list_mutex);
> chan->chan_dev->chan = NULL;
> mutex_unlock(&dma_list_mutex);
> + class_compat_remove_link(dma_chan_class, dmaengine_chan_dev(chan));
> device_unregister(dmaengine_chan_dev(chan));
> free_percpu(chan->local);
> }
> @@ -1652,6 +1656,7 @@ static int dma_chan_bus_match(struct device *dev,
> const struct bus_type dmaengine_chan_bus_type = {
> .name = "dmaengine-chan",
> .match = dma_chan_bus_match,
> + .dev_groups = dma_dev_groups,
> };
>
> static int __init dma_bus_init(void)
> @@ -1665,9 +1670,11 @@ static int __init dma_bus_init(void)
> if (err)
> goto err_destroy_unmap_pool;
>
> - err = class_register(&dma_devclass);
> - if (err)
> + dma_chan_class = class_compat_register("dma");
> + if (!dma_chan_class) {
> + err = -ENOMEM;
> goto err_unregister_bus;
> + }
>
> dmaengine_debugfs_init();
> return 0;
> diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
> index ec12b7288b43..93b136f875d4 100644
> --- a/drivers/dma/tegra186-gpc-dma.c
> +++ b/drivers/dma/tegra186-gpc-dma.c
> @@ -1540,7 +1540,6 @@ static int tegra_dma_probe(struct platform_device *pdev)
> tdc = to_tegra_dma_chan(chan);
>
> if (use_iommu_map) {
> - chdev->bus = pdev->dev.bus;
> dma_coerce_mask_and_coherent(chdev, DMA_BIT_MASK(cdata->addr_bits));
>
> ret = of_dma_configure_id(chdev, pdev->dev.of_node,
>
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v6 6/9] dmaengine: fsl-edma: use BIT_ULL for 64-bit channel mask
2026-11-12 7:53 ` [PATCH v6 6/9] dmaengine: fsl-edma: use BIT_ULL for 64-bit channel mask Peng Fan (OSS)
@ 2026-11-12 16:05 ` Frank Li
0 siblings, 0 replies; 14+ messages in thread
From: Frank Li @ 2026-11-12 16:05 UTC (permalink / raw)
To: Peng Fan (OSS)
Cc: Frank Li, Vinod Koul, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Laxman Dewangan, Jon Hunter,
Thierry Reding, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, Frank Li,
Larisa IIeana Grigore, imx, dmaengine, linux-kernel, iommu,
linux-tegra, devicetree, linux-arm-kernel, Peng Fan
On Thu, Nov 12, 2026 at 03:53:09PM +0800, Peng Fan (OSS) wrote:
> From: Peng Fan <peng.fan@nxp.com>
>
> chan_masked is a u64 bitmap, but several checks use BIT(), which uses
> unsigned long and overflows on 32-bit architectures for channels >= 32.
> Use BIT_ULL() so the upper channels are tested correctly.
>
> Signed-off-by: Peng Fan <peng.fan@nxp.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> drivers/dma/fsl-edma-main.c | 14 +++++++-------
> 1 file changed, 7 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/dma/fsl-edma-main.c b/drivers/dma/fsl-edma-main.c
> index 7f82dcb7831d..818b9b986f13 100644
> --- a/drivers/dma/fsl-edma-main.c
> +++ b/drivers/dma/fsl-edma-main.c
> @@ -123,7 +123,7 @@ static irqreturn_t fsl_edma3_err_handler_shared(int irq, void *dev_id)
> unsigned int ch;
>
> for (ch = 0; ch < fsl_edma->n_chans; ch++) {
> - if (fsl_edma->chan_masked & BIT(ch))
> + if (fsl_edma->chan_masked & BIT_ULL(ch))
> continue;
>
> fsl_edma3_err_check(&fsl_edma->chans[ch]);
> @@ -397,7 +397,7 @@ static int fsl_edma3_irq_init(struct platform_device *pdev, struct fsl_edma_engi
>
> struct fsl_edma_chan *fsl_chan = &fsl_edma->chans[i];
>
> - if (fsl_edma->chan_masked & BIT(i))
> + if (fsl_edma->chan_masked & BIT_ULL(i))
> continue;
>
> /* request channel irq */
> @@ -641,7 +641,7 @@ static void fsl_edma3_detach_pd(struct fsl_edma_engine *fsl_edma)
> int i;
>
> for (i = 0; i < fsl_edma->n_chans; i++) {
> - if (fsl_edma->chan_masked & BIT(i))
> + if (fsl_edma->chan_masked & BIT_ULL(i))
> continue;
> fsl_chan = &fsl_edma->chans[i];
> if (fsl_chan->pd_dev_link)
> @@ -669,7 +669,7 @@ static int fsl_edma3_attach_pd(struct platform_device *pdev, struct fsl_edma_eng
> dev = &pdev->dev;
>
> for (i = 0; i < fsl_edma->n_chans; i++) {
> - if (fsl_edma->chan_masked & BIT(i))
> + if (fsl_edma->chan_masked & BIT_ULL(i))
> continue;
>
> fsl_chan = &fsl_edma->chans[i];
> @@ -797,7 +797,7 @@ static int fsl_edma_probe(struct platform_device *pdev)
> struct fsl_edma_chan *fsl_chan = &fsl_edma->chans[i];
> int len;
>
> - if (fsl_edma->chan_masked & BIT(i))
> + if (fsl_edma->chan_masked & BIT_ULL(i))
> continue;
>
> snprintf(fsl_chan->chan_name, sizeof(fsl_chan->chan_name), "%s-CH%02d",
> @@ -927,7 +927,7 @@ static int fsl_edma_suspend_late(struct device *dev)
>
> for (i = 0; i < fsl_edma->n_chans; i++) {
> fsl_chan = &fsl_edma->chans[i];
> - if (fsl_edma->chan_masked & BIT(i))
> + if (fsl_edma->chan_masked & BIT_ULL(i))
> continue;
> spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
> /* Make sure chan is idle or will force disable. */
> @@ -953,7 +953,7 @@ static int fsl_edma_resume_early(struct device *dev)
>
> for (i = 0; i < fsl_edma->n_chans; i++) {
> fsl_chan = &fsl_edma->chans[i];
> - if (fsl_edma->chan_masked & BIT(i))
> + if (fsl_edma->chan_masked & BIT_ULL(i))
> continue;
> fsl_chan->pm_state = RUNNING;
> edma_write_tcdreg(fsl_chan, 0, csr);
>
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v6 7/9] dmaengine: fsl-edma: use dmaengine_get_dma_device()
2026-11-12 7:53 ` [PATCH v6 7/9] dmaengine: fsl-edma: use dmaengine_get_dma_device() Peng Fan (OSS)
@ 2026-11-12 16:06 ` Frank Li
0 siblings, 0 replies; 14+ messages in thread
From: Frank Li @ 2026-11-12 16:06 UTC (permalink / raw)
To: Peng Fan (OSS)
Cc: Frank Li, Vinod Koul, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Laxman Dewangan, Jon Hunter,
Thierry Reding, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, Frank Li,
Larisa IIeana Grigore, imx, dmaengine, linux-kernel, iommu,
linux-tegra, devicetree, linux-arm-kernel, Peng Fan
On Thu, Nov 12, 2026 at 03:53:10PM +0800, Peng Fan (OSS) wrote:
> From: Peng Fan <peng.fan@nxp.com>
>
> Use dmaengine_get_dma_device() instead of chan->device->dev when creating
> the TCD pool and when mapping the slave resource. This lets the driver use
> the per-channel device once per-channel IOMMU support enables it, without
> changing behaviour for existing users.
>
> Signed-off-by: Peng Fan <peng.fan@nxp.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> drivers/dma/fsl-edma-common.c | 10 +++++-----
> 1 file changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c
> index 513b0f4e5be2..62cd60179bc6 100644
> --- a/drivers/dma/fsl-edma-common.c
> +++ b/drivers/dma/fsl-edma-common.c
> @@ -310,10 +310,10 @@ int fsl_edma_resume(struct dma_chan *chan)
>
> static void fsl_edma_unprep_slave_dma(struct fsl_edma_chan *fsl_chan)
> {
> + struct device *dev = dmaengine_get_dma_device(&fsl_chan->vchan.chan);
> +
> if (fsl_chan->dma_dir != DMA_NONE)
> - dma_unmap_resource(fsl_chan->vchan.chan.device->dev,
> - fsl_chan->dma_dev_addr,
> - fsl_chan->dma_dev_size,
> + dma_unmap_resource(dev, fsl_chan->dma_dev_addr, fsl_chan->dma_dev_size,
> fsl_chan->dma_dir, 0);
> fsl_chan->dma_dir = DMA_NONE;
> }
> @@ -321,7 +321,7 @@ static void fsl_edma_unprep_slave_dma(struct fsl_edma_chan *fsl_chan)
> static bool fsl_edma_prep_slave_dma(struct fsl_edma_chan *fsl_chan,
> enum dma_transfer_direction dir)
> {
> - struct device *dev = fsl_chan->vchan.chan.device->dev;
> + struct device *dev = dmaengine_get_dma_device(&fsl_chan->vchan.chan);
> enum dma_data_direction dma_dir;
> phys_addr_t addr = 0;
> u32 size = 0;
> @@ -1090,7 +1090,7 @@ int fsl_edma_alloc_chan_resources(struct dma_chan *chan)
> if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_HAS_CHCLK)
> clk_prepare_enable(fsl_chan->clk);
>
> - fsl_chan->tcd_pool = dma_pool_create("tcd_pool", chan->device->dev,
> + fsl_chan->tcd_pool = dma_pool_create("tcd_pool", dmaengine_get_dma_device(chan),
> fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_TCD64 ?
> sizeof(struct fsl_edma_hw_tcd64) : sizeof(struct fsl_edma_hw_tcd),
> 32, 0);
>
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2026-11-12 16:06 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-12 7:53 [PATCH v6 0/9] dmaengine: add DMA channel bus and fsl-edma per-channel IOMMU support Peng Fan (OSS)
2026-11-12 7:53 ` [PATCH v6 1/9] dmaengine: fsl-edma: unregister channels before vchan cleanup Peng Fan (OSS)
2026-11-12 7:53 ` [PATCH v6 2/9] dmaengine: add a bus for DMA channel devices Peng Fan (OSS)
2026-11-12 15:47 ` Frank Li
2026-11-12 7:53 ` [PATCH v6 3/9] iommu: register the DMA channel bus for IOMMU notifications Peng Fan (OSS)
2026-11-12 7:53 ` [PATCH v6 4/9] dmaengine: move channel devices to the DMA channel bus Peng Fan (OSS)
2026-11-12 16:04 ` Frank Li
2026-11-12 7:53 ` [PATCH v6 5/9] dt-bindings: dma: fsl,edma: add per-channel child node support Peng Fan (OSS)
2026-11-12 7:53 ` [PATCH v6 6/9] dmaengine: fsl-edma: use BIT_ULL for 64-bit channel mask Peng Fan (OSS)
2026-11-12 16:05 ` Frank Li
2026-11-12 7:53 ` [PATCH v6 7/9] dmaengine: fsl-edma: use dmaengine_get_dma_device() Peng Fan (OSS)
2026-11-12 16:06 ` Frank Li
2026-11-12 7:53 ` [PATCH v6 8/9] dmaengine: fsl-edma: support per-channel DT child nodes Peng Fan (OSS)
2026-11-12 7:53 ` [PATCH v6 9/9] arm64: dts: imx95: convert eDMA to per-channel " Peng Fan (OSS)
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®