When virtio-iommu uses the PCI transport, IORT doesn't instantiate the
device and doesn't create a fwnode. They will be created later by the
PCI subsystem. Store the information needed to identify the IOMMU in
iort_fwnode_list.

Signed-off-by: Jean-Philippe Brucker <jean-philippe.bruc...@arm.com>
---
 drivers/acpi/iort.c | 117 +++++++++++++++++++++++++++++++++++---------
 1 file changed, 93 insertions(+), 24 deletions(-)

diff --git a/drivers/acpi/iort.c b/drivers/acpi/iort.c
index adc5953fffa5..b517aa4e83ba 100644
--- a/drivers/acpi/iort.c
+++ b/drivers/acpi/iort.c
@@ -30,10 +30,17 @@ struct iort_its_msi_chip {
        u32                     translation_id;
 };
 
+struct iort_pci_devid {
+       u16 segment;
+       u8 bus;
+       u8 devfn;
+};
+
 struct iort_fwnode {
        struct list_head list;
        struct acpi_iort_node *iort_node;
        struct fwnode_handle *fwnode;
+       struct iort_pci_devid *pci_devid;
 };
 static LIST_HEAD(iort_fwnode_list);
 static DEFINE_SPINLOCK(iort_fwnode_lock);
@@ -44,7 +51,8 @@ static bool iort_type_matches(u8 type, enum 
iort_node_category category)
        case IORT_IOMMU_TYPE:
                return type == ACPI_IORT_NODE_SMMU ||
                       type == ACPI_IORT_NODE_SMMU_V3 ||
-                      type == ACPI_VIOT_IORT_NODE_VIRTIO_MMIO_IOMMU;
+                      type == ACPI_VIOT_IORT_NODE_VIRTIO_MMIO_IOMMU ||
+                      type == ACPI_VIOT_IORT_NODE_VIRTIO_PCI_IOMMU;
        case IORT_MSI_TYPE:
                return type == ACPI_IORT_NODE_ITS_GROUP;
        default:
@@ -59,12 +67,14 @@ static bool iort_type_matches(u8 type, enum 
iort_node_category category)
  *
  * @node: IORT table node associated with the IOMMU
  * @fwnode: fwnode associated with the IORT node
+ * @pci_devid: pci device ID associated with the IORT node, may be NULL
  *
  * Returns: 0 on success
  *          <0 on failure
  */
 static inline int iort_set_fwnode(struct acpi_iort_node *iort_node,
-                                 struct fwnode_handle *fwnode)
+                                 struct fwnode_handle *fwnode,
+                                 struct iort_pci_devid *pci_devid)
 {
        struct iort_fwnode *np;
 
@@ -76,6 +86,7 @@ static inline int iort_set_fwnode(struct acpi_iort_node 
*iort_node,
        INIT_LIST_HEAD(&np->list);
        np->iort_node = iort_node;
        np->fwnode = fwnode;
+       np->pci_devid = pci_devid;
 
        spin_lock(&iort_fwnode_lock);
        list_add_tail(&np->list, &iort_fwnode_list);
@@ -121,6 +132,7 @@ static inline void iort_delete_fwnode(struct acpi_iort_node 
*node)
        spin_lock(&iort_fwnode_lock);
        list_for_each_entry_safe(curr, tmp, &iort_fwnode_list, list) {
                if (curr->iort_node == node) {
+                       kfree(curr->pci_devid);
                        list_del(&curr->list);
                        kfree(curr);
                        break;
@@ -870,6 +882,7 @@ static inline bool iort_iommu_driver_enabled(u8 type)
        case ACPI_IORT_NODE_SMMU:
                return IS_BUILTIN(CONFIG_ARM_SMMU);
        case ACPI_VIOT_IORT_NODE_VIRTIO_MMIO_IOMMU:
+       case ACPI_VIOT_IORT_NODE_VIRTIO_PCI_IOMMU:
                return IS_ENABLED(CONFIG_VIRTIO_IOMMU);
        default:
                pr_warn("IORT node type %u does not describe an IOMMU\n", type);
@@ -1451,6 +1464,28 @@ static void __init viommu_mmio_dma_configure(struct 
device *dev,
        acpi_dma_configure(dev, attr);
 }
 
+static __init struct iort_pci_devid *
+viommu_pci_get_devid(struct acpi_iort_node *node)
+{
+       unsigned int val;
+       struct iort_pci_devid *devid;
+       struct acpi_viot_iort_virtio_pci_iommu *viommu;
+
+       viommu = (struct acpi_viot_iort_virtio_pci_iommu *)node->node_data;
+
+       val = le32_to_cpu(viommu->devid);
+
+       devid = kzalloc(sizeof(*devid), GFP_KERNEL);
+       if (!devid)
+               return ERR_PTR(-ENOMEM);
+
+       devid->segment = val >> 16;
+       devid->bus = PCI_BUS_NUM(val);
+       devid->devfn = val & 0xff;
+
+       return devid;
+}
+
 struct iort_dev_config {
        const char *name;
        int (*dev_init)(struct acpi_iort_node *node);
@@ -1462,6 +1497,7 @@ struct iort_dev_config {
        int (*dev_set_proximity)(struct device *dev,
                                    struct acpi_iort_node *node);
        int (*dev_add_platdata)(struct platform_device *pdev);
+       struct iort_pci_devid *(*dev_get_pci_devid)(struct acpi_iort_node 
*node);
 };
 
 static const struct iort_dev_config iort_arm_smmu_v3_cfg __initconst = {
@@ -1494,6 +1530,10 @@ static const struct iort_dev_config iort_viommu_mmio_cfg 
__initconst = {
        .dev_init_resources = viommu_mmio_init_resources,
 };
 
+static const struct iort_dev_config iort_viommu_pci_cfg __initconst = {
+       .dev_get_pci_devid = viommu_pci_get_devid,
+};
+
 static __init const struct iort_dev_config *iort_get_dev_cfg(
                        struct acpi_iort_node *node)
 {
@@ -1510,6 +1550,8 @@ static __init const struct iort_dev_config 
*iort_get_dev_cfg(
                switch (node->type) {
                case ACPI_VIOT_IORT_NODE_VIRTIO_MMIO_IOMMU:
                        return &iort_viommu_mmio_cfg;
+               case ACPI_VIOT_IORT_NODE_VIRTIO_PCI_IOMMU:
+                       return &iort_viommu_pci_cfg;
                }
        }
 
@@ -1641,13 +1683,55 @@ static void __init iort_enable_acs(struct 
acpi_iort_node *iort_node)
 static inline void iort_enable_acs(struct acpi_iort_node *iort_node) { }
 #endif
 
-static void __init iort_init_platform_devices(void)
+static int __init iort_init_node(struct acpi_iort_node *iort_node)
+{
+       int ret;
+       const struct iort_dev_config *ops;
+       struct fwnode_handle *fwnode;
+
+       iort_enable_acs(iort_node);
+
+       ops = iort_get_dev_cfg(iort_node);
+       if (!ops)
+               return 0;
+
+       if (ops->dev_get_pci_devid) {
+               struct iort_pci_devid *pci_devid =
+                       ops->dev_get_pci_devid(iort_node);
+
+               if (IS_ERR(pci_devid))
+                       return PTR_ERR(pci_devid);
+               /*
+                * For a PCI-based IOMMU, set the pci_devid handle now, but
+                * leave the fwnode empty. It will be completed later when the
+                * PCI device gets probed.
+                */
+               iort_set_fwnode(iort_node, NULL, pci_devid);
+
+               return 0;
+       }
+
+       fwnode = acpi_alloc_fwnode_static();
+       if (!fwnode)
+               return -ENOMEM;
+
+       iort_set_fwnode(iort_node, fwnode, NULL);
+
+       ret = iort_add_platform_device(iort_node, ops);
+       if (ret) {
+               iort_delete_fwnode(iort_node);
+               acpi_free_fwnode_static(fwnode);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void __init iort_init_devices(void)
 {
        struct acpi_iort_node *iort_node, *iort_end;
        struct acpi_table_iort *iort;
-       struct fwnode_handle *fwnode;
-       int i, ret;
-       const struct iort_dev_config *ops;
+       int i;
 
        /*
         * iort_table and iort both point to the start of IORT table, but
@@ -1667,23 +1751,8 @@ static void __init iort_init_platform_devices(void)
                        return;
                }
 
-               iort_enable_acs(iort_node);
-
-               ops = iort_get_dev_cfg(iort_node);
-               if (ops) {
-                       fwnode = acpi_alloc_fwnode_static();
-                       if (!fwnode)
-                               return;
-
-                       iort_set_fwnode(iort_node, fwnode);
-
-                       ret = iort_add_platform_device(iort_node, ops);
-                       if (ret) {
-                               iort_delete_fwnode(iort_node);
-                               acpi_free_fwnode_static(fwnode);
-                               return;
-                       }
-               }
+               if (iort_init_node(iort_node))
+                       return;
 
                iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node,
                                         iort_node->length);
@@ -1703,7 +1772,7 @@ void __init acpi_iort_register_table(struct 
acpi_table_header *table,
        iort_table = table;
        iort_table_source = source;
 
-       iort_init_platform_devices();
+       iort_init_devices();
 }
 
 void __init acpi_iort_init(void)
-- 
2.24.0

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Reply via email to