#include <linux/err.h>
 #include <linux/pci.h>
 #include <linux/bitops.h>
+#include <linux/property.h>
 #include <trace/events/iommu.h>
 
 static struct kset *iommu_group_kset;
 
        return ret;
 }
+
+int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
+                     const struct iommu_ops *ops)
+{
+       struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+
+       if (fwspec)
+               return ops == fwspec->ops ? 0 : -EINVAL;
+
+       fwspec = kzalloc(sizeof(*fwspec), GFP_KERNEL);
+       if (!fwspec)
+               return -ENOMEM;
+
+       of_node_get(to_of_node(iommu_fwnode));
+       fwspec->iommu_fwnode = iommu_fwnode;
+       fwspec->ops = ops;
+       dev->iommu_fwspec = fwspec;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(iommu_fwspec_init);
+
+void iommu_fwspec_free(struct device *dev)
+{
+       struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+
+       if (fwspec) {
+               fwnode_handle_put(fwspec->iommu_fwnode);
+               kfree(fwspec);
+               dev->iommu_fwspec = NULL;
+       }
+}
+EXPORT_SYMBOL_GPL(iommu_fwspec_free);
+
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)
+{
+       struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+       size_t size;
+       int i;
+
+       if (!fwspec)
+               return -EINVAL;
+
+       size = offsetof(struct iommu_fwspec, ids[fwspec->num_ids + num_ids]);
+       if (size > sizeof(*fwspec)) {
+               fwspec = krealloc(dev->iommu_fwspec, size, GFP_KERNEL);
+               if (!fwspec)
+                       return -ENOMEM;
+       }
+
+       for (i = 0; i < num_ids; i++)
+               fwspec->ids[fwspec->num_ids + i] = ids[i];
+
+       fwspec->num_ids += num_ids;
+       dev->iommu_fwspec = fwspec;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(iommu_fwspec_add_ids);
 
                return NULL;
 
        ops = of_iommu_get_ops(iommu_spec.np);
-       if (!ops || !ops->of_xlate || ops->of_xlate(&pdev->dev, &iommu_spec))
+       if (!ops || !ops->of_xlate ||
+           iommu_fwspec_init(&pdev->dev, &iommu_spec.np->fwnode, ops) ||
+           ops->of_xlate(&pdev->dev, &iommu_spec))
                ops = NULL;
 
        of_node_put(iommu_spec.np);
                np = iommu_spec.np;
                ops = of_iommu_get_ops(np);
 
-               if (!ops || !ops->of_xlate || ops->of_xlate(dev, &iommu_spec))
+               if (!ops || !ops->of_xlate ||
+                   iommu_fwspec_init(dev, &np->fwnode, ops) ||
+                   ops->of_xlate(dev, &iommu_spec))
                        goto err_put_node;
 
                of_node_put(np);
 
 struct fwnode_handle;
 struct iommu_ops;
 struct iommu_group;
+struct iommu_fwspec;
 
 struct bus_attribute {
        struct attribute        attr;
  *             gone away. This should be set by the allocator of the
  *             device (i.e. the bus driver that discovered the device).
  * @iommu_group: IOMMU group the device belongs to.
+ * @iommu_fwspec: IOMMU-specific properties supplied by firmware.
  *
  * @offline_disabled: If set, the device is permanently online.
  * @offline:   Set after successful invocation of bus type's .offline().
 
        void    (*release)(struct device *dev);
        struct iommu_group      *iommu_group;
+       struct iommu_fwspec     *iommu_fwspec;
 
        bool                    offline_disabled:1;
        bool                    offline:1;
 
 /* Generic device grouping function */
 extern struct iommu_group *generic_device_group(struct device *dev);
 
+/**
+ * struct iommu_fwspec - per-device IOMMU instance data
+ * @ops: ops for this device's IOMMU
+ * @iommu_fwnode: firmware handle for this device's IOMMU
+ * @iommu_priv: IOMMU driver private data for this device
+ * @num_ids: number of associated device IDs
+ * @ids: IDs which this device may present to the IOMMU
+ */
+struct iommu_fwspec {
+       const struct iommu_ops  *ops;
+       struct fwnode_handle    *iommu_fwnode;
+       void                    *iommu_priv;
+       unsigned int            num_ids;
+       u32                     ids[1];
+};
+
+int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
+                     const struct iommu_ops *ops);
+void iommu_fwspec_free(struct device *dev);
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids);
+
 #else /* CONFIG_IOMMU_API */
 
 struct iommu_ops {};
 struct iommu_group {};
+struct iommu_fwspec {};
 
 static inline bool iommu_present(struct bus_type *bus)
 {
 {
 }
 
+static inline int iommu_fwspec_init(struct device *dev,
+                                   struct fwnode_handle *iommu_fwnode,
+                                   const struct iommu_ops *ops)
+{
+       return -ENODEV;
+}
+
+static inline void iommu_fwspec_free(struct device *dev)
+{
+}
+
+static inline int iommu_fwspec_add_ids(struct device *dev, u32 *ids,
+                                      int num_ids)
+{
+       return -ENODEV;
+}
+
 #endif /* CONFIG_IOMMU_API */
 
 #endif /* __LINUX_IOMMU_H */