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@xxxxxxxxxx> --- 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