SMMUv3 supports a single iommu instance with multiple ids. It has a combined ACPI (via the IORT table) and OF probe path, add iommu_iort_get_single_iommu() to respresent this. It already has a per-instance structure, extend it with the ids[] array and use iommu_fwb_alloc_per_device_ids() to populate it. Convert the rest of the funcs from calling dev_iommu_fwspec_get() to using the per-device data and remove all use of fwspec. 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/acpi/arm64/iort.c | 2 - drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 74 +++++++++------------ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 4 ++ include/linux/iommu-driver.h | 2 +- 4 files changed, 35 insertions(+), 47 deletions(-) diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c index 6b2d50cc9ac180..acd2e48590f37a 100644 --- a/drivers/acpi/arm64/iort.c +++ b/drivers/acpi/arm64/iort.c @@ -1297,8 +1297,6 @@ static void iort_named_component_init(struct device *dev, props[0] = PROPERTY_ENTRY_U32("pasid-num-bits", FIELD_GET(ACPI_IORT_NC_PASID_BITS, nc->node_flags)); - if (nc->node_flags & ACPI_IORT_NC_STALL_SUPPORTED) - props[1] = PROPERTY_ENTRY_BOOL("dma-can-stall"); if (device_create_managed_software_node(dev, props, NULL)) dev_warn(dev, "Could not add device properties\n"); diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c index 1855d3892b15f8..1a43c677e2feaf 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c @@ -26,9 +26,9 @@ #include <linux/pci.h> #include <linux/pci-ats.h> #include <linux/platform_device.h> +#include <linux/iommu-driver.h> #include "arm-smmu-v3.h" -#include "../../dma-iommu.h" #include "../../iommu-sva.h" static bool disable_bypass = true; @@ -2255,12 +2255,11 @@ static bool arm_smmu_ats_supported(struct arm_smmu_master *master) { struct device *dev = master->dev; struct arm_smmu_device *smmu = master->smmu; - struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); if (!(smmu->features & ARM_SMMU_FEAT_ATS)) return false; - if (!(fwspec->flags & IOMMU_FWSPEC_PCI_RC_ATS)) + if (!master->pci_rc_ats) return false; return dev_is_pci(dev) && pci_ats_supported(to_pci_dev(dev)); @@ -2382,14 +2381,10 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) { int ret = 0; unsigned long flags; - struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); struct arm_smmu_device *smmu; struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); struct arm_smmu_master *master; - if (!fwspec) - return -ENOENT; - master = dev_iommu_priv_get(dev); smmu = master->smmu; @@ -2529,15 +2524,6 @@ arm_smmu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova) static struct platform_driver arm_smmu_driver; -static -struct arm_smmu_device *arm_smmu_get_by_fwnode(struct fwnode_handle *fwnode) -{ - struct device *dev = driver_find_device_by_fwnode(&arm_smmu_driver.driver, - fwnode); - put_device(dev); - return dev ? dev_get_drvdata(dev) : NULL; -} - static bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid) { unsigned long limit = smmu->strtab_cfg.num_l1_ents; @@ -2568,17 +2554,16 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu, int ret = 0; struct arm_smmu_stream *new_stream, *cur_stream; struct rb_node **new_node, *parent_node = NULL; - struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(master->dev); - master->streams = kcalloc(fwspec->num_ids, sizeof(*master->streams), + master->streams = kcalloc(master->num_ids, sizeof(*master->streams), GFP_KERNEL); if (!master->streams) return -ENOMEM; - master->num_streams = fwspec->num_ids; + master->num_streams = master->num_ids; mutex_lock(&smmu->streams_mutex); - for (i = 0; i < fwspec->num_ids; i++) { - u32 sid = fwspec->ids[i]; + for (i = 0; i < master->num_ids; i++) { + u32 sid = master->ids[i]; new_stream = &master->streams[i]; new_stream->id = sid; @@ -2627,13 +2612,12 @@ static void arm_smmu_remove_master(struct arm_smmu_master *master) { int i; struct arm_smmu_device *smmu = master->smmu; - struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(master->dev); if (!smmu || !master->streams) return; mutex_lock(&smmu->streams_mutex); - for (i = 0; i < fwspec->num_ids; i++) + for (i = 0; i < master->num_ids; i++) rb_erase(&master->streams[i].node, &smmu->streams); mutex_unlock(&smmu->streams_mutex); @@ -2642,26 +2626,27 @@ static void arm_smmu_remove_master(struct arm_smmu_master *master) static struct iommu_ops arm_smmu_ops; -static struct iommu_device *arm_smmu_probe_device(struct device *dev) +static struct iommu_device *arm_smmu_probe_device(struct iommu_probe_info *pinf) { int ret; + struct device *dev = pinf->dev; struct arm_smmu_device *smmu; struct arm_smmu_master *master; - struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); + struct iort_params params; - if (WARN_ON_ONCE(dev_iommu_priv_get(dev))) - return ERR_PTR(-EBUSY); + smmu = iommu_iort_get_single_iommu(pinf, &arm_smmu_ops, ¶ms, + struct arm_smmu_device, iommu); + if (IS_ERR(smmu)) + return ERR_CAST(smmu); - smmu = arm_smmu_get_by_fwnode(fwspec->iommu_fwnode); - if (!smmu) - return ERR_PTR(-ENODEV); - - master = kzalloc(sizeof(*master), GFP_KERNEL); - if (!master) - return ERR_PTR(-ENOMEM); + master = iommu_fw_alloc_per_device_ids(pinf, master); + if (IS_ERR(master)) + return ERR_CAST(master); master->dev = dev; master->smmu = smmu; + master->pci_rc_ats = params.pci_rc_ats; + master->acpi_fwnode = iommu_fw_acpi_fwnode(pinf); INIT_LIST_HEAD(&master->bonds); dev_iommu_priv_set(dev, master); @@ -2670,7 +2655,8 @@ static struct iommu_device *arm_smmu_probe_device(struct device *dev) goto err_free_master; device_property_read_u32(dev, "pasid-num-bits", &master->ssid_bits); - master->ssid_bits = min(smmu->ssid_bits, master->ssid_bits); + master->ssid_bits = min(smmu->ssid_bits, + max(params.pasid_num_bits, master->ssid_bits)); /* * Note that PASID must be enabled before, and disabled after ATS: @@ -2687,7 +2673,8 @@ static struct iommu_device *arm_smmu_probe_device(struct device *dev) CTXDESC_LINEAR_CDMAX); if ((smmu->features & ARM_SMMU_FEAT_STALLS && - device_property_read_bool(dev, "dma-can-stall")) || + (device_property_read_bool(dev, "dma-can-stall") || + params.dma_can_stall)) || smmu->features & ARM_SMMU_FEAT_STALL_FORCE) master->stall_enabled = true; @@ -2744,14 +2731,10 @@ static int arm_smmu_enable_nesting(struct iommu_domain *domain) return ret; } -static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args) -{ - return iommu_fwspec_add_ids(dev, args->args, 1); -} - static void arm_smmu_get_resv_regions(struct device *dev, struct list_head *head) { + struct arm_smmu_master *master = dev_iommu_priv_get(dev); struct iommu_resv_region *region; int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO; @@ -2762,7 +2745,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 (master->acpi_fwnode) + iort_iommu_get_resv_regions(dev, head, master->acpi_fwnode, + master->ids, master->num_ids); + of_iommu_get_resv_regions(dev, head); } static int arm_smmu_dev_enable_feature(struct device *dev, @@ -2851,10 +2837,10 @@ static void arm_smmu_remove_dev_pasid(struct device *dev, ioasid_t pasid) 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, .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, .remove_dev_pasid = arm_smmu_remove_dev_pasid, .dev_enable_feat = arm_smmu_dev_enable_feature, diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h index 961205ba86d25d..ac293265b21a13 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h @@ -692,6 +692,7 @@ struct arm_smmu_stream { struct arm_smmu_master { struct arm_smmu_device *smmu; struct device *dev; + struct fwnode_handle *acpi_fwnode; struct arm_smmu_domain *domain; struct list_head domain_head; struct arm_smmu_stream *streams; @@ -702,8 +703,11 @@ struct arm_smmu_master { bool stall_enabled; bool sva_enabled; bool iopf_enabled; + bool pci_rc_ats; struct list_head bonds; unsigned int ssid_bits; + unsigned int num_ids; + u32 ids[] __counted_by(num_ids); }; /* SMMU private data for an IOMMU domain */ diff --git a/include/linux/iommu-driver.h b/include/linux/iommu-driver.h index c4e133cdef2c78..8f7089d3bb7135 100644 --- a/include/linux/iommu-driver.h +++ b/include/linux/iommu-driver.h @@ -252,6 +252,6 @@ __iommu_iort_get_single_iommu(struct iommu_probe_info *pinf, pinf, ops, params), \ __iommu_of_get_single_iommu( \ pinf, ops, -1)), \ - drv_struct, member) \ + drv_struct, member); \ }) #endif -- 2.42.0