@@ -966,6 +966,48 @@ static struct iommu_ops viommu_ops = {
.of_xlate = viommu_of_xlate,
};
+static int viommu_set_fwnode(struct viommu_dev *viommu)
+{
+ /*
+ * viommu->dev is the virtio device, its parent is the associated
+ * transport device.
+ */
+ struct device *dev = viommu->dev->parent;
+
+ /*
+ * With device tree a fwnode is always present. With ACPI, on some
+ * platforms a PCI device has a DSDT node describing the slot. On other
+ * platforms, no fwnode is created and we have to do it ourselves.
+ */
+ if (!dev->fwnode) {
+ struct fwnode_handle *fwnode;
+
+ fwnode = fwnode_create_software_node(NULL, NULL);
+ if (IS_ERR(fwnode))
+ return PTR_ERR(fwnode);
+
+ set_primary_fwnode(dev, fwnode);
+ }
+
+ iommu_device_set_fwnode(&viommu->iommu, dev->fwnode);
+ return 0;
+}
+
+static void viommu_clear_fwnode(struct viommu_dev *viommu)
+{
+ struct device *dev = viommu->dev->parent;
+
+ if (!dev->fwnode)
+ return;
+
+ if (is_software_node(dev->fwnode)) {
+ struct fwnode_handle *fwnode = dev->fwnode;
+
+ set_primary_fwnode(dev, NULL);
+ fwnode_remove_software_node(fwnode);
+ }
+}
+
static int viommu_init_vqs(struct viommu_dev *viommu)
{
struct virtio_device *vdev = dev_to_virtio(viommu->dev);
@@ -1004,7 +1046,6 @@ static int viommu_fill_evtq(struct viommu_dev *viommu)
static int viommu_probe(struct virtio_device *vdev)
{
- struct device *parent_dev = vdev->dev.parent;
struct viommu_dev *viommu = NULL;
struct device *dev = &vdev->dev;
u64 input_start = 0;
@@ -1084,9 +1125,11 @@ static int viommu_probe(struct virtio_device *vdev)
if (ret)
goto err_free_vqs;
- iommu_device_set_ops(&viommu->iommu, &viommu_ops);
- iommu_device_set_fwnode(&viommu->iommu, parent_dev->fwnode);
+ ret = viommu_set_fwnode(viommu);
+ if (ret)
+ goto err_sysfs_remove;
+ iommu_device_set_ops(&viommu->iommu, &viommu_ops);
iommu_device_register(&viommu->iommu);
#ifdef CONFIG_PCI
@@ -1119,8 +1162,10 @@ static int viommu_probe(struct virtio_device *vdev)
return 0;
err_unregister:
- iommu_device_sysfs_remove(&viommu->iommu);
iommu_device_unregister(&viommu->iommu);
+ viommu_clear_fwnode(viommu);
+err_sysfs_remove:
+ iommu_device_sysfs_remove(&viommu->iommu);
err_free_vqs:
vdev->config->del_vqs(vdev);
@@ -1131,8 +1176,9 @@ static void viommu_remove(struct virtio_device *vdev)
{
struct viommu_dev *viommu = vdev->priv;
- iommu_device_sysfs_remove(&viommu->iommu);
iommu_device_unregister(&viommu->iommu);
+ viommu_clear_fwnode(viommu);
+ iommu_device_sysfs_remove(&viommu->iommu);
/* Stop all virtqueues */
vdev->config->reset(vdev);
The presence of a fwnode on a PCI device depends on the platform. QEMU q35, for example, creates an ACPI description for each PCI slot, but QEMU virt (aarch64) doesn't. Since the IOMMU subsystem relies heavily on fwnode to discover the DMA topology, create a fwnode for the virtio-iommu if necessary, using the software_node framework. Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org> --- drivers/iommu/virtio-iommu.c | 56 ++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 5 deletions(-) -- 2.24.0