[PATCH 11/11] iommu/tegra-smmu: Use a DMA API default domain per swgroup
From: Mikko Perttunen
Date: Mon Sep 14 2026 - 22:18:20 EST
Everything is now in place for the Tegra SMMU to give devices a real
IOMMU_DOMAIN_DMA default domain.
Some device drivers still rely on explicit domains (and being able to
detach a legacy ARM DMA domain if any), so DMA default domains are
enabled on a per-swgroup basis via a use_dma_api flag. As drivers are
converted to DMA API usage we can eventually get rid of this. Initially
the flag is enabled for TegraDRM and Host1x devices.
On multi_v7_defconfig (where CONFIG_ARM_DMA_USE_IOMMU was always
enabled), this will fix TegraDRM probing with certain probe orderings
where the legacy implementation's lack of support for multiple devices
in one IOMMU group would cause breakage.
On tegra_defconfig, as this now enables CONFIG_ARM_DMA_USE_IOMMU, all
devices move to either the legacy ARM DMA path or the new one. This
should in principle not cause issues as the drivers would already have
been verified on multi_v7_defconfig.
Signed-off-by: Mikko Perttunen <mperttunen@xxxxxxxxxx>
---
drivers/iommu/Kconfig | 1 +
drivers/iommu/tegra-smmu.c | 58 ++++++++++++++++++++++++++++++++++++++---
drivers/memory/tegra/tegra114.c | 13 +++++----
drivers/memory/tegra/tegra124.c | 10 ++++---
drivers/memory/tegra/tegra30.c | 16 +++++++-----
include/soc/tegra/mc.h | 7 +++++
6 files changed, 86 insertions(+), 19 deletions(-)
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 6e07bd69467a..817e84e0e6ab 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -252,6 +252,7 @@ config TEGRA_IOMMU_SMMU
depends on TEGRA_AHB
depends on TEGRA_MC
select IOMMU_API
+ select ARM_DMA_USE_IOMMU if ARM
help
This driver supports the IOMMU hardware (SMMU) found on NVIDIA Tegra
SoCs (Tegra30 up to Tegra210).
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
index 6636a044905f..8a5c3f80fcff 100644
--- a/drivers/iommu/tegra-smmu.c
+++ b/drivers/iommu/tegra-smmu.c
@@ -1037,12 +1037,26 @@ static int tegra_smmu_of_xlate(struct device *dev,
static int tegra_smmu_def_domain_type(struct device *dev)
{
+ struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+ struct tegra_smmu *smmu = dev_iommu_priv_get(dev);
+ unsigned int i;
+
+ if (!fwspec || !smmu)
+ return IOMMU_DOMAIN_IDENTITY;
+
/*
- * FIXME: For now we want to run all translation in IDENTITY mode, due
- * to some device quirks. Better would be to just quirk the troubled
- * devices.
+ * Only clients known to work with the DMA API get a DMA default domain.
+ * The rest keep running in identity mode.
*/
- return IOMMU_DOMAIN_IDENTITY;
+ for (i = 0; i < fwspec->num_ids; i++) {
+ const struct tegra_smmu_swgroup *group;
+
+ group = tegra_smmu_find_swgroup(smmu, fwspec->ids[i]);
+ if (!group || !group->use_dma_api)
+ return IOMMU_DOMAIN_IDENTITY;
+ }
+
+ return IOMMU_DOMAIN_DMA;
}
static const struct iommu_ops tegra_smmu_ops = {
@@ -1151,6 +1165,40 @@ static void tegra_smmu_debugfs_exit(struct tegra_smmu *smmu)
debugfs_remove_recursive(smmu->debugfs);
}
+/*
+ * The default domain type is a property of the IOMMU group, so all swgroups in
+ * one group must agree on use_dma_api. Otherwise the core sees conflicting
+ * def_domain_type and falls back to identity for the whole group.
+ */
+static void tegra_smmu_check_groups(struct tegra_smmu *smmu)
+{
+ const struct tegra_smmu_soc *soc = smmu->soc;
+ unsigned int i, j;
+
+ for (i = 0; i < soc->num_groups; i++) {
+ const struct tegra_smmu_group_soc *group = &soc->groups[i];
+ int dma_api = -1;
+
+ for (j = 0; j < group->num_swgroups; j++) {
+ const struct tegra_smmu_swgroup *swgrp;
+
+ swgrp = tegra_smmu_find_swgroup(smmu,
+ group->swgroups[j]);
+ if (!swgrp)
+ continue;
+
+ if (dma_api < 0)
+ dma_api = swgrp->use_dma_api;
+ else if (swgrp->use_dma_api != !!dma_api)
+ break;
+ }
+
+ WARN(j < group->num_swgroups,
+ "SMMU group %s mixes DMA API and identity swgroups\n",
+ group->name);
+ }
+}
+
struct tegra_smmu *tegra_smmu_probe(struct device *dev,
const struct tegra_smmu_soc *soc,
struct tegra_mc *mc)
@@ -1185,6 +1233,8 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev,
smmu->dev = dev;
smmu->mc = mc;
+ tegra_smmu_check_groups(smmu);
+
smmu->pfn_mask =
BIT_MASK(mc->soc->num_address_bits - SMMU_PTE_SHIFT) - 1;
dev_dbg(dev, "address bits: %u, PFN mask: %#lx\n",
diff --git a/drivers/memory/tegra/tegra114.c b/drivers/memory/tegra/tegra114.c
index 2883c4f91144..024c63bdd480 100644
--- a/drivers/memory/tegra/tegra114.c
+++ b/drivers/memory/tegra/tegra114.c
@@ -1029,15 +1029,18 @@ static const struct tegra_mc_client tegra114_mc_clients[] = {
static const struct tegra_smmu_swgroup tegra114_swgroups[] = {
{ .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240,
- .defer_enable = true },
+ .defer_enable = true, .use_dma_api = true },
{ .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244,
- .defer_enable = true },
+ .defer_enable = true, .use_dma_api = true },
{ .name = "epp", .swgroup = TEGRA_SWGROUP_EPP, .reg = 0x248 },
- { .name = "g2", .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c },
+ { .name = "g2", .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c,
+ .use_dma_api = true },
{ .name = "avpc", .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c },
- { .name = "nv", .swgroup = TEGRA_SWGROUP_NV, .reg = 0x268 },
+ { .name = "nv", .swgroup = TEGRA_SWGROUP_NV, .reg = 0x268,
+ .use_dma_api = true },
{ .name = "hda", .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 },
- { .name = "hc", .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 },
+ { .name = "hc", .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250,
+ .use_dma_api = true },
{ .name = "msenc", .swgroup = TEGRA_SWGROUP_MSENC, .reg = 0x264 },
{ .name = "ppcs", .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 },
{ .name = "vde", .swgroup = TEGRA_SWGROUP_VDE, .reg = 0x27c },
diff --git a/drivers/memory/tegra/tegra124.c b/drivers/memory/tegra/tegra124.c
index a38fbf2baf6c..17068aaf163f 100644
--- a/drivers/memory/tegra/tegra124.c
+++ b/drivers/memory/tegra/tegra124.c
@@ -1071,13 +1071,14 @@ static const struct tegra_mc_client tegra124_mc_clients[] = {
static const struct tegra_smmu_swgroup tegra124_swgroups[] = {
{ .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240,
- .defer_enable = true },
+ .defer_enable = true, .use_dma_api = true },
{ .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244,
- .defer_enable = true },
+ .defer_enable = true, .use_dma_api = true },
{ .name = "afi", .swgroup = TEGRA_SWGROUP_AFI, .reg = 0x238 },
{ .name = "avpc", .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c },
{ .name = "hda", .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 },
- { .name = "hc", .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 },
+ { .name = "hc", .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250,
+ .use_dma_api = true },
{ .name = "msenc", .swgroup = TEGRA_SWGROUP_MSENC, .reg = 0x264 },
{ .name = "ppcs", .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 },
{ .name = "sata", .swgroup = TEGRA_SWGROUP_SATA, .reg = 0x274 },
@@ -1093,7 +1094,8 @@ static const struct tegra_smmu_swgroup tegra124_swgroups[] = {
{ .name = "sdmmc2a", .swgroup = TEGRA_SWGROUP_SDMMC2A, .reg = 0xa98 },
{ .name = "sdmmc3a", .swgroup = TEGRA_SWGROUP_SDMMC3A, .reg = 0xa9c },
{ .name = "sdmmc4a", .swgroup = TEGRA_SWGROUP_SDMMC4A, .reg = 0xaa0 },
- { .name = "vic", .swgroup = TEGRA_SWGROUP_VIC, .reg = 0x284 },
+ { .name = "vic", .swgroup = TEGRA_SWGROUP_VIC, .reg = 0x284,
+ .use_dma_api = true },
{ .name = "vi", .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 },
};
diff --git a/drivers/memory/tegra/tegra30.c b/drivers/memory/tegra/tegra30.c
index 0ee4715413ba..df6b5a499ccc 100644
--- a/drivers/memory/tegra/tegra30.c
+++ b/drivers/memory/tegra/tegra30.c
@@ -1140,19 +1140,23 @@ static const struct tegra_mc_client tegra30_mc_clients[] = {
static const struct tegra_smmu_swgroup tegra30_swgroups[] = {
{ .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240,
- .defer_enable = true },
+ .defer_enable = true, .use_dma_api = true },
{ .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244,
- .defer_enable = true },
+ .defer_enable = true, .use_dma_api = true },
{ .name = "epp", .swgroup = TEGRA_SWGROUP_EPP, .reg = 0x248 },
- { .name = "g2", .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c },
+ { .name = "g2", .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c,
+ .use_dma_api = true },
{ .name = "mpe", .swgroup = TEGRA_SWGROUP_MPE, .reg = 0x264 },
{ .name = "vi", .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 },
{ .name = "afi", .swgroup = TEGRA_SWGROUP_AFI, .reg = 0x238 },
{ .name = "avpc", .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c },
- { .name = "nv", .swgroup = TEGRA_SWGROUP_NV, .reg = 0x268 },
- { .name = "nv2", .swgroup = TEGRA_SWGROUP_NV2, .reg = 0x26c },
+ { .name = "nv", .swgroup = TEGRA_SWGROUP_NV, .reg = 0x268,
+ .use_dma_api = true },
+ { .name = "nv2", .swgroup = TEGRA_SWGROUP_NV2, .reg = 0x26c,
+ .use_dma_api = true },
{ .name = "hda", .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 },
- { .name = "hc", .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 },
+ { .name = "hc", .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250,
+ .use_dma_api = true },
{ .name = "ppcs", .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 },
{ .name = "sata", .swgroup = TEGRA_SWGROUP_SATA, .reg = 0x278 },
{ .name = "vde", .swgroup = TEGRA_SWGROUP_VDE, .reg = 0x27c },
diff --git a/include/soc/tegra/mc.h b/include/soc/tegra/mc.h
index ea67972fd585..1cbd990fc0f4 100644
--- a/include/soc/tegra/mc.h
+++ b/include/soc/tegra/mc.h
@@ -78,6 +78,13 @@ struct tegra_smmu_swgroup {
* tegra_smmu_enable_translation().
*/
bool defer_enable;
+
+ /*
+ * Set once the clients behind this swgroup are known to work with an
+ * IOMMU_DOMAIN_DMA default domain. All swgroups of a
+ * tegra_smmu_group_soc must agree.
+ */
+ bool use_dma_api;
};
struct tegra_smmu_group_soc {
--
2.55.0