[PATCH 27/30] iommu/arm-smmu: Move to iommu_of_xlate()
Jason Gunthorpe
jgg at nvidia.com
Wed Nov 29 17:10:34 PST 2023
Compared to every other driver SMMU supports a lot of different formats
for it's firmware description:
- The legacy OF mmu-masters with a 1 cell id
- The OF iommus with a 1 cell id
- The OF iommus with a 2 cell id
- The OF iommus with a 1 cell id and stream-match-mask
- ACPI with a 1 cell id
They all get reduced down to a single array of u32 ids in the driver.
Store the id array as a flex array in the arm_smmu_master_cfg, and change
the smendx to an allocated array. This allows using the
iommu_fw_alloc_per_device_ids() path for ACPI.
Have the legacy flow just allocate the cfg of the proper size and copy the
cells into the ids.
The OF and ACPI flows all call iommu_iort_get_single_iommu(). ACPI will
use iommu_fw_alloc_per_device_ids().
The remaining OF flows will make another pass using iommu_of_xlate() to
parse the complex details and format the IDs list.
Remove fwspec from the other places in the driver.
Directly call iort_iommu_get_resv_regions() and pass in the internal id
array instead of getting it from the fwspec.
Signed-off-by: Jason Gunthorpe <jgg at nvidia.com>
---
drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c | 6 +-
drivers/iommu/arm/arm-smmu/arm-smmu.c | 205 +++++++++++----------
drivers/iommu/arm/arm-smmu/arm-smmu.h | 13 +-
3 files changed, 118 insertions(+), 106 deletions(-)
diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c
index 549ae4dba3a681..95199de33ca865 100644
--- a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c
+++ b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c
@@ -101,15 +101,15 @@ static void qcom_adreno_smmu_resume_translation(const void *cookie, bool termina
static bool qcom_adreno_smmu_is_gpu_device(struct device *dev)
{
- struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+ struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev);
int i;
/*
* The GPU will always use SID 0 so that is a handy way to uniquely
* identify it and configure it for per-instance pagetables
*/
- for (i = 0; i < fwspec->num_ids; i++) {
- u16 sid = FIELD_GET(ARM_SMMU_SMR_ID, fwspec->ids[i]);
+ for (i = 0; i < cfg->num_ids; i++) {
+ u16 sid = FIELD_GET(ARM_SMMU_SMR_ID, cfg->ids[i]);
if (sid == QCOM_ADRENO_SMMU_GPU_SID)
return true;
diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.c b/drivers/iommu/arm/arm-smmu/arm-smmu.c
index 02b8dc4f366aa9..f18d40532af433 100644
--- a/drivers/iommu/arm/arm-smmu/arm-smmu.c
+++ b/drivers/iommu/arm/arm-smmu/arm-smmu.c
@@ -34,11 +34,11 @@
#include <linux/pm_runtime.h>
#include <linux/ratelimit.h>
#include <linux/slab.h>
+#include <linux/iommu-driver.h>
#include <linux/fsl/mc.h>
#include "arm-smmu.h"
-#include "../../dma-iommu.h"
/*
* Apparently, some Qualcomm arm64 platforms which appear to expose their SMMU
@@ -89,6 +89,8 @@ static struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom)
static struct platform_driver arm_smmu_driver;
static struct iommu_ops arm_smmu_ops;
+static int arm_smmu_of_xlate(struct iommu_device *iommu,
+ struct of_phandle_args *args, void *priv);
#ifdef CONFIG_ARM_SMMU_LEGACY_DT_BINDINGS
static struct device_node *dev_get_dev_node(struct device *dev)
@@ -126,21 +128,21 @@ static int __find_legacy_master_phandle(struct device *dev, void *data)
return err == -ENOENT ? 0 : err;
}
-static int arm_smmu_register_legacy_master(struct device *dev,
- struct arm_smmu_device **smmu)
+static struct arm_smmu_master_cfg *
+arm_smmu_register_legacy_master(struct device *dev)
{
+ struct arm_smmu_master_cfg *cfg;
struct device *smmu_dev;
struct device_node *np;
struct of_phandle_iterator it;
void *data = ⁢
- u32 *sids;
__be32 pci_sid;
int err;
np = dev_get_dev_node(dev);
if (!np || !of_property_present(np, "#stream-id-cells")) {
of_node_put(np);
- return -ENODEV;
+ return ERR_PTR(-ENODEV);
}
it.node = np;
@@ -149,9 +151,9 @@ static int arm_smmu_register_legacy_master(struct device *dev,
smmu_dev = data;
of_node_put(np);
if (err == 0)
- return -ENODEV;
+ return ERR_PTR(-ENODEV);
if (err < 0)
- return err;
+ return ERR_PTR(err);
if (dev_is_pci(dev)) {
/* "mmu-masters" assumes Stream ID == Requester ID */
@@ -161,26 +163,20 @@ static int arm_smmu_register_legacy_master(struct device *dev,
it.cur_count = 1;
}
- err = iommu_fwspec_init(dev, &smmu_dev->of_node->fwnode,
- &arm_smmu_ops);
- if (err)
- return err;
+ cfg = kzalloc(struct_size(cfg, ids, it.cur_count), GFP_KERNEL);
+ if (!cfg)
+ return ERR_PTR(-ENOMEM);
- sids = kcalloc(it.cur_count, sizeof(*sids), GFP_KERNEL);
- if (!sids)
- return -ENOMEM;
-
- *smmu = dev_get_drvdata(smmu_dev);
- of_phandle_iterator_args(&it, sids, it.cur_count);
- err = iommu_fwspec_add_ids(dev, sids, it.cur_count);
- kfree(sids);
- return err;
+ cfg->num_ids = it.cur_count;
+ cfg->smmu = dev_get_drvdata(smmu_dev);
+ of_phandle_iterator_args(&it, cfg->ids, it.cur_count);
+ return 0;
}
#else
-static int arm_smmu_register_legacy_master(struct device *dev,
- struct arm_smmu_device **smmu)
+static struct arm_smmu_master_cfg *
+arm_smmu_register_legacy_master(struct device *dev)
{
- return -ENODEV;
+ return ERR_PTR(-ENODEV);
}
#endif /* CONFIG_ARM_SMMU_LEGACY_DT_BINDINGS */
@@ -1019,7 +1015,6 @@ static bool arm_smmu_free_sme(struct arm_smmu_device *smmu, int idx)
static int arm_smmu_master_alloc_smes(struct device *dev)
{
- struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev);
struct arm_smmu_device *smmu = cfg->smmu;
struct arm_smmu_smr *smrs = smmu->smrs;
@@ -1027,9 +1022,9 @@ static int arm_smmu_master_alloc_smes(struct device *dev)
mutex_lock(&smmu->stream_map_mutex);
/* Figure out a viable stream map entry allocation */
- for_each_cfg_sme(cfg, fwspec, i, idx) {
- u16 sid = FIELD_GET(ARM_SMMU_SMR_ID, fwspec->ids[i]);
- u16 mask = FIELD_GET(ARM_SMMU_SMR_MASK, fwspec->ids[i]);
+ for_each_cfg_sme(cfg, i, idx) {
+ u16 sid = FIELD_GET(ARM_SMMU_SMR_ID, cfg->ids[i]);
+ u16 mask = FIELD_GET(ARM_SMMU_SMR_MASK, cfg->ids[i]);
if (idx != INVALID_SMENDX) {
ret = -EEXIST;
@@ -1051,7 +1046,7 @@ static int arm_smmu_master_alloc_smes(struct device *dev)
}
/* It worked! Now, poke the actual hardware */
- for_each_cfg_sme(cfg, fwspec, i, idx)
+ for_each_cfg_sme(cfg, i, idx)
arm_smmu_write_sme(smmu, idx);
mutex_unlock(&smmu->stream_map_mutex);
@@ -1066,14 +1061,13 @@ static int arm_smmu_master_alloc_smes(struct device *dev)
return ret;
}
-static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg,
- struct iommu_fwspec *fwspec)
+static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg)
{
struct arm_smmu_device *smmu = cfg->smmu;
int i, idx;
mutex_lock(&smmu->stream_map_mutex);
- for_each_cfg_sme(cfg, fwspec, i, idx) {
+ for_each_cfg_sme(cfg, i, idx) {
if (arm_smmu_free_sme(smmu, idx))
arm_smmu_write_sme(smmu, idx);
cfg->smendx[i] = INVALID_SMENDX;
@@ -1082,8 +1076,7 @@ static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg,
}
static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
- struct arm_smmu_master_cfg *cfg,
- struct iommu_fwspec *fwspec)
+ struct arm_smmu_master_cfg *cfg)
{
struct arm_smmu_device *smmu = smmu_domain->smmu;
struct arm_smmu_s2cr *s2cr = smmu->s2crs;
@@ -1096,7 +1089,7 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
else
type = S2CR_TYPE_TRANS;
- for_each_cfg_sme(cfg, fwspec, i, idx) {
+ for_each_cfg_sme(cfg, i, idx) {
if (type == s2cr[idx].type && cbndx == s2cr[idx].cbndx)
continue;
@@ -1111,24 +1104,10 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
{
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
- struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
- struct arm_smmu_master_cfg *cfg;
- struct arm_smmu_device *smmu;
+ struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev);
+ struct arm_smmu_device *smmu = cfg->smmu;
int ret;
- /*
- * FIXME: The arch/arm DMA API code tries to attach devices to its own
- * domains between of_xlate() and probe_device() - we have no way to cope
- * with that, so until ARM gets converted to rely on groups and default
- * domains, just say no (but more politely than by dereferencing NULL).
- * This should be at least a WARN_ON once that's sorted.
- */
- cfg = dev_iommu_priv_get(dev);
- if (!cfg)
- return -ENODEV;
-
- smmu = cfg->smmu;
-
ret = arm_smmu_rpm_get(smmu);
if (ret < 0)
return ret;
@@ -1148,7 +1127,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
}
/* Looks ok, so add the device to the domain */
- ret = arm_smmu_domain_add_master(smmu_domain, cfg, fwspec);
+ ret = arm_smmu_domain_add_master(smmu_domain, cfg);
/*
* Setup an autosuspend delay to avoid bouncing runpm state.
@@ -1325,59 +1304,85 @@ static bool arm_smmu_capable(struct device *dev, enum iommu_cap cap)
}
}
-static
-struct arm_smmu_device *arm_smmu_get_by_fwnode(struct fwnode_handle *fwnode)
+static struct arm_smmu_master_cfg *
+arm_smmu_probe_new_master(struct iommu_probe_info *pinf)
{
- struct device *dev = driver_find_device_by_fwnode(&arm_smmu_driver.driver,
- fwnode);
- put_device(dev);
- return dev ? dev_get_drvdata(dev) : NULL;
-}
-
-static struct iommu_device *arm_smmu_probe_device(struct device *dev)
-{
- struct arm_smmu_device *smmu = NULL;
struct arm_smmu_master_cfg *cfg;
- struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
- int i, ret;
+ struct arm_smmu_device *smmu;
- if (using_legacy_binding) {
- ret = arm_smmu_register_legacy_master(dev, &smmu);
+ smmu = iommu_iort_get_single_iommu(pinf, &arm_smmu_ops, NULL,
+ struct arm_smmu_device, iommu);
+ if (IS_ERR(smmu))
+ return ERR_CAST(smmu);
+
+ if (!pinf->of_master_np) {
+ /* In ACPI mode the smmu uses the usual u32 format */
+ cfg = iommu_fw_alloc_per_device_ids(pinf, cfg);
+ if (IS_ERR(cfg))
+ return cfg;
+ cfg->acpi_fwnode = iommu_fw_acpi_fwnode(pinf);
+ } else {
+ unsigned int num_ids;
+ int ret;
/*
- * If dev->iommu_fwspec is initally NULL, arm_smmu_register_legacy_master()
- * will allocate/initialise a new one. Thus we need to update fwspec for
- * later use.
+ * In OF mode it supports several different formats for the arg,
+ * pass through arm_smmu_of_xlate to extract it.
*/
- fwspec = dev_iommu_fwspec_get(dev);
- if (ret)
- goto out_free;
- } else {
- smmu = arm_smmu_get_by_fwnode(fwspec->iommu_fwnode);
+ num_ids = iommu_of_num_ids(pinf);
+ cfg = kzalloc(struct_size(cfg, ids, num_ids), GFP_KERNEL);
+ if (!cfg)
+ return ERR_PTR(-ENOMEM);
+
+ ret = iommu_of_xlate(pinf, &arm_smmu_ops, -1,
+ &arm_smmu_of_xlate, cfg);
+ if (ret) {
+ kfree(cfg);
+ return ERR_PTR(ret);
+ }
}
+ cfg->smmu = smmu;
+ return cfg;
+}
+
+static struct iommu_device *arm_smmu_probe_device(struct iommu_probe_info *pinf)
+{
+ struct arm_smmu_master_cfg *cfg;
+ struct device *dev = pinf->dev;
+ struct arm_smmu_device *smmu;
+ int i, ret;
+
+ if (using_legacy_binding)
+ cfg = arm_smmu_register_legacy_master(dev);
+ else
+ cfg = arm_smmu_probe_new_master(pinf);
+ if (IS_ERR(cfg))
+ return ERR_CAST(cfg);
+ smmu = cfg->smmu;
+
ret = -EINVAL;
- for (i = 0; i < fwspec->num_ids; i++) {
- u16 sid = FIELD_GET(ARM_SMMU_SMR_ID, fwspec->ids[i]);
- u16 mask = FIELD_GET(ARM_SMMU_SMR_MASK, fwspec->ids[i]);
+ for (i = 0; i < cfg->num_ids; i++) {
+ u16 sid = FIELD_GET(ARM_SMMU_SMR_ID, cfg->ids[i]);
+ u16 mask = FIELD_GET(ARM_SMMU_SMR_MASK, cfg->ids[i]);
if (sid & ~smmu->streamid_mask) {
dev_err(dev, "stream ID 0x%x out of range for SMMU (0x%x)\n",
sid, smmu->streamid_mask);
- goto out_free;
+ goto out_cfg_free;
}
if (mask & ~smmu->smr_mask_mask) {
dev_err(dev, "SMR mask 0x%x out of range for SMMU (0x%x)\n",
mask, smmu->smr_mask_mask);
- goto out_free;
+ goto out_cfg_free;
}
}
ret = -ENOMEM;
- cfg = kzalloc(offsetof(struct arm_smmu_master_cfg, smendx[i]),
- GFP_KERNEL);
+ cfg->smendx = kzalloc(array_size(sizeof(*cfg->smendx), cfg->num_ids),
+ GFP_KERNEL);
if (!cfg)
- goto out_free;
+ goto out_cfg_free;
cfg->smmu = smmu;
dev_iommu_priv_set(dev, cfg);
@@ -1400,15 +1405,13 @@ static struct iommu_device *arm_smmu_probe_device(struct device *dev)
return &smmu->iommu;
out_cfg_free:
+ kfree(cfg->smendx);
kfree(cfg);
-out_free:
- iommu_fwspec_free(dev);
return ERR_PTR(ret);
}
static void arm_smmu_release_device(struct device *dev)
{
- struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev);
int ret;
@@ -1416,10 +1419,11 @@ static void arm_smmu_release_device(struct device *dev)
if (ret < 0)
return;
- arm_smmu_master_free_smes(cfg, fwspec);
+ arm_smmu_master_free_smes(cfg);
arm_smmu_rpm_put(cfg->smmu);
+ kfree(cfg->smendx);
kfree(cfg);
}
@@ -1438,13 +1442,12 @@ static void arm_smmu_probe_finalize(struct device *dev)
static struct iommu_group *arm_smmu_device_group(struct device *dev)
{
struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev);
- struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
struct arm_smmu_device *smmu = cfg->smmu;
struct iommu_group *group = NULL;
int i, idx;
mutex_lock(&smmu->stream_map_mutex);
- for_each_cfg_sme(cfg, fwspec, i, idx) {
+ for_each_cfg_sme(cfg, i, idx) {
if (group && smmu->s2crs[idx].group &&
group != smmu->s2crs[idx].group) {
mutex_unlock(&smmu->stream_map_mutex);
@@ -1468,7 +1471,7 @@ static struct iommu_group *arm_smmu_device_group(struct device *dev)
/* Remember group for faster lookups */
if (!IS_ERR(group))
- for_each_cfg_sme(cfg, fwspec, i, idx)
+ for_each_cfg_sme(cfg, i, idx)
smmu->s2crs[idx].group = group;
mutex_unlock(&smmu->stream_map_mutex);
@@ -1506,8 +1509,10 @@ static int arm_smmu_set_pgtable_quirks(struct iommu_domain *domain,
return ret;
}
-static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args)
+static int arm_smmu_of_xlate(struct iommu_device *iommu,
+ struct of_phandle_args *args, void *priv)
{
+ struct arm_smmu_master_cfg *cfg = priv;
u32 mask, fwid = 0;
if (args->args_count > 0)
@@ -1517,13 +1522,14 @@ static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args)
fwid |= FIELD_PREP(ARM_SMMU_SMR_MASK, args->args[1]);
else if (!of_property_read_u32(args->np, "stream-match-mask", &mask))
fwid |= FIELD_PREP(ARM_SMMU_SMR_MASK, mask);
-
- return iommu_fwspec_add_ids(dev, &fwid, 1);
+ cfg->ids[cfg->num_ids++] = fwid;
+ return 0;
}
static void arm_smmu_get_resv_regions(struct device *dev,
struct list_head *head)
{
+ struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev);
struct iommu_resv_region *region;
int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
@@ -1534,7 +1540,10 @@ static void arm_smmu_get_resv_regions(struct device *dev,
list_add_tail(®ion->list, head);
- iommu_dma_get_resv_regions(dev, head);
+ if (cfg->acpi_fwnode)
+ iort_iommu_get_resv_regions(dev, head, cfg->acpi_fwnode,
+ cfg->ids, cfg->num_ids);
+ of_iommu_get_resv_regions(dev, head);
}
static int arm_smmu_def_domain_type(struct device *dev)
@@ -1553,22 +1562,22 @@ static int arm_smmu_def_domain_type(struct device *dev)
static bool arm_smmu_get_stream_id(struct device *dev, u32 *stream_id)
{
- struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+ struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev);
- if (fwspec->num_ids != 1)
+ if (cfg->num_ids != 1)
return false;
- *stream_id = fwspec->ids[0] & 0xffff;
+ *stream_id = cfg->ids[0] & 0xffff;
return true;
}
static struct iommu_ops arm_smmu_ops = {
.capable = arm_smmu_capable,
.domain_alloc = arm_smmu_domain_alloc,
- .probe_device = arm_smmu_probe_device,
+ .probe_device_pinf = arm_smmu_probe_device,
.release_device = arm_smmu_release_device,
.probe_finalize = arm_smmu_probe_finalize,
.device_group = arm_smmu_device_group,
- .of_xlate = arm_smmu_of_xlate,
+ .of_xlate = iommu_dummy_of_xlate,
.get_resv_regions = arm_smmu_get_resv_regions,
.def_domain_type = arm_smmu_def_domain_type,
.tegra_dev_iommu_get_stream_id = arm_smmu_get_stream_id,
diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.h b/drivers/iommu/arm/arm-smmu/arm-smmu.h
index 703fd5817ec11f..ba8224751fdcdc 100644
--- a/drivers/iommu/arm/arm-smmu/arm-smmu.h
+++ b/drivers/iommu/arm/arm-smmu/arm-smmu.h
@@ -378,7 +378,10 @@ struct arm_smmu_domain {
struct arm_smmu_master_cfg {
struct arm_smmu_device *smmu;
- s16 smendx[];
+ struct fwnode_handle *acpi_fwnode;
+ s16 *smendx;
+ unsigned int num_ids;
+ u32 ids[] __counted_by(num_ids);
};
static inline u32 arm_smmu_lpae_tcr(const struct io_pgtable_cfg *cfg)
@@ -446,10 +449,10 @@ struct arm_smmu_impl {
};
#define INVALID_SMENDX -1
-#define cfg_smendx(cfg, fw, i) \
- (i >= fw->num_ids ? INVALID_SMENDX : cfg->smendx[i])
-#define for_each_cfg_sme(cfg, fw, i, idx) \
- for (i = 0; idx = cfg_smendx(cfg, fw, i), i < fw->num_ids; ++i)
+#define cfg_smendx(cfg, i) \
+ (i >= cfg->num_ids ? INVALID_SMENDX : cfg->smendx[i])
+#define for_each_cfg_sme(cfg, i, idx) \
+ for (i = 0; idx = cfg_smendx(cfg, i), i < cfg->num_ids; ++i)
static inline int __arm_smmu_alloc_bitmap(unsigned long *map, int start, int end)
{
--
2.42.0
More information about the Linux-rockchip
mailing list