xref: /openbmc/linux/drivers/acpi/viot.c (revision 47d26684)
13cf48554SJean-Philippe Brucker // SPDX-License-Identifier: GPL-2.0
23cf48554SJean-Philippe Brucker /*
33cf48554SJean-Philippe Brucker  * Virtual I/O topology
43cf48554SJean-Philippe Brucker  *
53cf48554SJean-Philippe Brucker  * The Virtual I/O Translation Table (VIOT) describes the topology of
63cf48554SJean-Philippe Brucker  * para-virtual IOMMUs and the endpoints they manage. The OS uses it to
73cf48554SJean-Philippe Brucker  * initialize devices in the right order, preventing endpoints from issuing DMA
83cf48554SJean-Philippe Brucker  * before their IOMMU is ready.
93cf48554SJean-Philippe Brucker  *
103cf48554SJean-Philippe Brucker  * When binding a driver to a device, before calling the device driver's probe()
113cf48554SJean-Philippe Brucker  * method, the driver infrastructure calls dma_configure(). At that point the
123cf48554SJean-Philippe Brucker  * VIOT driver looks for an IOMMU associated to the device in the VIOT table.
133cf48554SJean-Philippe Brucker  * If an IOMMU exists and has been initialized, the VIOT driver initializes the
143cf48554SJean-Philippe Brucker  * device's IOMMU fwspec, allowing the DMA infrastructure to invoke the IOMMU
153cf48554SJean-Philippe Brucker  * ops when the device driver configures DMA mappings. If an IOMMU exists and
163cf48554SJean-Philippe Brucker  * hasn't yet been initialized, VIOT returns -EPROBE_DEFER to postpone probing
173cf48554SJean-Philippe Brucker  * the device until the IOMMU is available.
183cf48554SJean-Philippe Brucker  */
193cf48554SJean-Philippe Brucker #define pr_fmt(fmt) "ACPI: VIOT: " fmt
203cf48554SJean-Philippe Brucker 
213cf48554SJean-Philippe Brucker #include <linux/acpi_viot.h>
223cf48554SJean-Philippe Brucker #include <linux/fwnode.h>
233cf48554SJean-Philippe Brucker #include <linux/iommu.h>
243cf48554SJean-Philippe Brucker #include <linux/list.h>
253cf48554SJean-Philippe Brucker #include <linux/pci.h>
263cf48554SJean-Philippe Brucker #include <linux/platform_device.h>
273cf48554SJean-Philippe Brucker 
283cf48554SJean-Philippe Brucker struct viot_iommu {
293cf48554SJean-Philippe Brucker 	/* Node offset within the table */
303cf48554SJean-Philippe Brucker 	unsigned int			offset;
313cf48554SJean-Philippe Brucker 	struct fwnode_handle		*fwnode;
323cf48554SJean-Philippe Brucker 	struct list_head		list;
333cf48554SJean-Philippe Brucker };
343cf48554SJean-Philippe Brucker 
353cf48554SJean-Philippe Brucker struct viot_endpoint {
363cf48554SJean-Philippe Brucker 	union {
373cf48554SJean-Philippe Brucker 		/* PCI range */
383cf48554SJean-Philippe Brucker 		struct {
393cf48554SJean-Philippe Brucker 			u16		segment_start;
403cf48554SJean-Philippe Brucker 			u16		segment_end;
413cf48554SJean-Philippe Brucker 			u16		bdf_start;
423cf48554SJean-Philippe Brucker 			u16		bdf_end;
433cf48554SJean-Philippe Brucker 		};
443cf48554SJean-Philippe Brucker 		/* MMIO */
453cf48554SJean-Philippe Brucker 		u64			address;
463cf48554SJean-Philippe Brucker 	};
473cf48554SJean-Philippe Brucker 	u32				endpoint_id;
483cf48554SJean-Philippe Brucker 	struct viot_iommu		*viommu;
493cf48554SJean-Philippe Brucker 	struct list_head		list;
503cf48554SJean-Philippe Brucker };
513cf48554SJean-Philippe Brucker 
523cf48554SJean-Philippe Brucker static struct acpi_table_viot *viot;
533cf48554SJean-Philippe Brucker static LIST_HEAD(viot_iommus);
543cf48554SJean-Philippe Brucker static LIST_HEAD(viot_pci_ranges);
553cf48554SJean-Philippe Brucker static LIST_HEAD(viot_mmio_endpoints);
563cf48554SJean-Philippe Brucker 
viot_check_bounds(const struct acpi_viot_header * hdr)573cf48554SJean-Philippe Brucker static int __init viot_check_bounds(const struct acpi_viot_header *hdr)
583cf48554SJean-Philippe Brucker {
593cf48554SJean-Philippe Brucker 	struct acpi_viot_header *start, *end, *hdr_end;
603cf48554SJean-Philippe Brucker 
613cf48554SJean-Philippe Brucker 	start = ACPI_ADD_PTR(struct acpi_viot_header, viot,
623cf48554SJean-Philippe Brucker 			     max_t(size_t, sizeof(*viot), viot->node_offset));
633cf48554SJean-Philippe Brucker 	end = ACPI_ADD_PTR(struct acpi_viot_header, viot, viot->header.length);
643cf48554SJean-Philippe Brucker 	hdr_end = ACPI_ADD_PTR(struct acpi_viot_header, hdr, sizeof(*hdr));
653cf48554SJean-Philippe Brucker 
663cf48554SJean-Philippe Brucker 	if (hdr < start || hdr_end > end) {
673cf48554SJean-Philippe Brucker 		pr_err(FW_BUG "Node pointer overflows\n");
683cf48554SJean-Philippe Brucker 		return -EOVERFLOW;
693cf48554SJean-Philippe Brucker 	}
703cf48554SJean-Philippe Brucker 	if (hdr->length < sizeof(*hdr)) {
713cf48554SJean-Philippe Brucker 		pr_err(FW_BUG "Empty node\n");
723cf48554SJean-Philippe Brucker 		return -EINVAL;
733cf48554SJean-Philippe Brucker 	}
743cf48554SJean-Philippe Brucker 	return 0;
753cf48554SJean-Philippe Brucker }
763cf48554SJean-Philippe Brucker 
viot_get_pci_iommu_fwnode(struct viot_iommu * viommu,u16 segment,u16 bdf)773cf48554SJean-Philippe Brucker static int __init viot_get_pci_iommu_fwnode(struct viot_iommu *viommu,
783cf48554SJean-Philippe Brucker 					    u16 segment, u16 bdf)
793cf48554SJean-Philippe Brucker {
803cf48554SJean-Philippe Brucker 	struct pci_dev *pdev;
813cf48554SJean-Philippe Brucker 	struct fwnode_handle *fwnode;
823cf48554SJean-Philippe Brucker 
833cf48554SJean-Philippe Brucker 	pdev = pci_get_domain_bus_and_slot(segment, PCI_BUS_NUM(bdf),
843cf48554SJean-Philippe Brucker 					   bdf & 0xff);
853cf48554SJean-Philippe Brucker 	if (!pdev) {
863cf48554SJean-Philippe Brucker 		pr_err("Could not find PCI IOMMU\n");
873cf48554SJean-Philippe Brucker 		return -ENODEV;
883cf48554SJean-Philippe Brucker 	}
893cf48554SJean-Philippe Brucker 
90b0f2fe5aSAndy Shevchenko 	fwnode = dev_fwnode(&pdev->dev);
913cf48554SJean-Philippe Brucker 	if (!fwnode) {
923cf48554SJean-Philippe Brucker 		/*
933cf48554SJean-Philippe Brucker 		 * PCI devices aren't necessarily described by ACPI. Create a
943cf48554SJean-Philippe Brucker 		 * fwnode so the IOMMU subsystem can identify this device.
953cf48554SJean-Philippe Brucker 		 */
963cf48554SJean-Philippe Brucker 		fwnode = acpi_alloc_fwnode_static();
973cf48554SJean-Philippe Brucker 		if (!fwnode) {
983cf48554SJean-Philippe Brucker 			pci_dev_put(pdev);
993cf48554SJean-Philippe Brucker 			return -ENOMEM;
1003cf48554SJean-Philippe Brucker 		}
1013cf48554SJean-Philippe Brucker 		set_primary_fwnode(&pdev->dev, fwnode);
1023cf48554SJean-Philippe Brucker 	}
103b0f2fe5aSAndy Shevchenko 	viommu->fwnode = dev_fwnode(&pdev->dev);
1043cf48554SJean-Philippe Brucker 	pci_dev_put(pdev);
1053cf48554SJean-Philippe Brucker 	return 0;
1063cf48554SJean-Philippe Brucker }
1073cf48554SJean-Philippe Brucker 
viot_get_mmio_iommu_fwnode(struct viot_iommu * viommu,u64 address)1083cf48554SJean-Philippe Brucker static int __init viot_get_mmio_iommu_fwnode(struct viot_iommu *viommu,
1093cf48554SJean-Philippe Brucker 					     u64 address)
1103cf48554SJean-Philippe Brucker {
1113cf48554SJean-Philippe Brucker 	struct acpi_device *adev;
1123cf48554SJean-Philippe Brucker 	struct resource res = {
1133cf48554SJean-Philippe Brucker 		.start	= address,
1143cf48554SJean-Philippe Brucker 		.end	= address,
1153cf48554SJean-Philippe Brucker 		.flags	= IORESOURCE_MEM,
1163cf48554SJean-Philippe Brucker 	};
1173cf48554SJean-Philippe Brucker 
1183cf48554SJean-Philippe Brucker 	adev = acpi_resource_consumer(&res);
1193cf48554SJean-Philippe Brucker 	if (!adev) {
1203cf48554SJean-Philippe Brucker 		pr_err("Could not find MMIO IOMMU\n");
1213cf48554SJean-Philippe Brucker 		return -EINVAL;
1223cf48554SJean-Philippe Brucker 	}
1233cf48554SJean-Philippe Brucker 	viommu->fwnode = &adev->fwnode;
1243cf48554SJean-Philippe Brucker 	return 0;
1253cf48554SJean-Philippe Brucker }
1263cf48554SJean-Philippe Brucker 
viot_get_iommu(unsigned int offset)1273cf48554SJean-Philippe Brucker static struct viot_iommu * __init viot_get_iommu(unsigned int offset)
1283cf48554SJean-Philippe Brucker {
1293cf48554SJean-Philippe Brucker 	int ret;
1303cf48554SJean-Philippe Brucker 	struct viot_iommu *viommu;
1313cf48554SJean-Philippe Brucker 	struct acpi_viot_header *hdr = ACPI_ADD_PTR(struct acpi_viot_header,
1323cf48554SJean-Philippe Brucker 						    viot, offset);
1333cf48554SJean-Philippe Brucker 	union {
1343cf48554SJean-Philippe Brucker 		struct acpi_viot_virtio_iommu_pci pci;
1353cf48554SJean-Philippe Brucker 		struct acpi_viot_virtio_iommu_mmio mmio;
1363cf48554SJean-Philippe Brucker 	} *node = (void *)hdr;
1373cf48554SJean-Philippe Brucker 
1383cf48554SJean-Philippe Brucker 	list_for_each_entry(viommu, &viot_iommus, list)
1393cf48554SJean-Philippe Brucker 		if (viommu->offset == offset)
1403cf48554SJean-Philippe Brucker 			return viommu;
1413cf48554SJean-Philippe Brucker 
1423cf48554SJean-Philippe Brucker 	if (viot_check_bounds(hdr))
1433cf48554SJean-Philippe Brucker 		return NULL;
1443cf48554SJean-Philippe Brucker 
1453cf48554SJean-Philippe Brucker 	viommu = kzalloc(sizeof(*viommu), GFP_KERNEL);
1463cf48554SJean-Philippe Brucker 	if (!viommu)
1473cf48554SJean-Philippe Brucker 		return NULL;
1483cf48554SJean-Philippe Brucker 
1493cf48554SJean-Philippe Brucker 	viommu->offset = offset;
1503cf48554SJean-Philippe Brucker 	switch (hdr->type) {
1513cf48554SJean-Philippe Brucker 	case ACPI_VIOT_NODE_VIRTIO_IOMMU_PCI:
1523cf48554SJean-Philippe Brucker 		if (hdr->length < sizeof(node->pci))
1533cf48554SJean-Philippe Brucker 			goto err_free;
1543cf48554SJean-Philippe Brucker 
1553cf48554SJean-Philippe Brucker 		ret = viot_get_pci_iommu_fwnode(viommu, node->pci.segment,
1563cf48554SJean-Philippe Brucker 						node->pci.bdf);
1573cf48554SJean-Philippe Brucker 		break;
1583cf48554SJean-Philippe Brucker 	case ACPI_VIOT_NODE_VIRTIO_IOMMU_MMIO:
1593cf48554SJean-Philippe Brucker 		if (hdr->length < sizeof(node->mmio))
1603cf48554SJean-Philippe Brucker 			goto err_free;
1613cf48554SJean-Philippe Brucker 
1623cf48554SJean-Philippe Brucker 		ret = viot_get_mmio_iommu_fwnode(viommu,
1633cf48554SJean-Philippe Brucker 						 node->mmio.base_address);
1643cf48554SJean-Philippe Brucker 		break;
1653cf48554SJean-Philippe Brucker 	default:
1663cf48554SJean-Philippe Brucker 		ret = -EINVAL;
1673cf48554SJean-Philippe Brucker 	}
1683cf48554SJean-Philippe Brucker 	if (ret)
1693cf48554SJean-Philippe Brucker 		goto err_free;
1703cf48554SJean-Philippe Brucker 
1713cf48554SJean-Philippe Brucker 	list_add(&viommu->list, &viot_iommus);
1723cf48554SJean-Philippe Brucker 	return viommu;
1733cf48554SJean-Philippe Brucker 
1743cf48554SJean-Philippe Brucker err_free:
1753cf48554SJean-Philippe Brucker 	kfree(viommu);
1763cf48554SJean-Philippe Brucker 	return NULL;
1773cf48554SJean-Philippe Brucker }
1783cf48554SJean-Philippe Brucker 
viot_parse_node(const struct acpi_viot_header * hdr)1793cf48554SJean-Philippe Brucker static int __init viot_parse_node(const struct acpi_viot_header *hdr)
1803cf48554SJean-Philippe Brucker {
1813cf48554SJean-Philippe Brucker 	int ret = -EINVAL;
1823cf48554SJean-Philippe Brucker 	struct list_head *list;
1833cf48554SJean-Philippe Brucker 	struct viot_endpoint *ep;
1843cf48554SJean-Philippe Brucker 	union {
1853cf48554SJean-Philippe Brucker 		struct acpi_viot_mmio mmio;
1863cf48554SJean-Philippe Brucker 		struct acpi_viot_pci_range pci;
1873cf48554SJean-Philippe Brucker 	} *node = (void *)hdr;
1883cf48554SJean-Philippe Brucker 
1893cf48554SJean-Philippe Brucker 	if (viot_check_bounds(hdr))
1903cf48554SJean-Philippe Brucker 		return -EINVAL;
1913cf48554SJean-Philippe Brucker 
1923cf48554SJean-Philippe Brucker 	if (hdr->type == ACPI_VIOT_NODE_VIRTIO_IOMMU_PCI ||
1933cf48554SJean-Philippe Brucker 	    hdr->type == ACPI_VIOT_NODE_VIRTIO_IOMMU_MMIO)
1943cf48554SJean-Philippe Brucker 		return 0;
1953cf48554SJean-Philippe Brucker 
1963cf48554SJean-Philippe Brucker 	ep = kzalloc(sizeof(*ep), GFP_KERNEL);
1973cf48554SJean-Philippe Brucker 	if (!ep)
1983cf48554SJean-Philippe Brucker 		return -ENOMEM;
1993cf48554SJean-Philippe Brucker 
2003cf48554SJean-Philippe Brucker 	switch (hdr->type) {
2013cf48554SJean-Philippe Brucker 	case ACPI_VIOT_NODE_PCI_RANGE:
2023cf48554SJean-Philippe Brucker 		if (hdr->length < sizeof(node->pci)) {
2033cf48554SJean-Philippe Brucker 			pr_err(FW_BUG "Invalid PCI node size\n");
2043cf48554SJean-Philippe Brucker 			goto err_free;
2053cf48554SJean-Philippe Brucker 		}
2063cf48554SJean-Philippe Brucker 
2073cf48554SJean-Philippe Brucker 		ep->segment_start = node->pci.segment_start;
2083cf48554SJean-Philippe Brucker 		ep->segment_end = node->pci.segment_end;
2093cf48554SJean-Philippe Brucker 		ep->bdf_start = node->pci.bdf_start;
2103cf48554SJean-Philippe Brucker 		ep->bdf_end = node->pci.bdf_end;
2113cf48554SJean-Philippe Brucker 		ep->endpoint_id = node->pci.endpoint_start;
2123cf48554SJean-Philippe Brucker 		ep->viommu = viot_get_iommu(node->pci.output_node);
2133cf48554SJean-Philippe Brucker 		list = &viot_pci_ranges;
2143cf48554SJean-Philippe Brucker 		break;
2153cf48554SJean-Philippe Brucker 	case ACPI_VIOT_NODE_MMIO:
2163cf48554SJean-Philippe Brucker 		if (hdr->length < sizeof(node->mmio)) {
2173cf48554SJean-Philippe Brucker 			pr_err(FW_BUG "Invalid MMIO node size\n");
2183cf48554SJean-Philippe Brucker 			goto err_free;
2193cf48554SJean-Philippe Brucker 		}
2203cf48554SJean-Philippe Brucker 
2213cf48554SJean-Philippe Brucker 		ep->address = node->mmio.base_address;
2223cf48554SJean-Philippe Brucker 		ep->endpoint_id = node->mmio.endpoint;
2233cf48554SJean-Philippe Brucker 		ep->viommu = viot_get_iommu(node->mmio.output_node);
2243cf48554SJean-Philippe Brucker 		list = &viot_mmio_endpoints;
2253cf48554SJean-Philippe Brucker 		break;
2263cf48554SJean-Philippe Brucker 	default:
2273cf48554SJean-Philippe Brucker 		pr_warn("Unsupported node %x\n", hdr->type);
2283cf48554SJean-Philippe Brucker 		ret = 0;
2293cf48554SJean-Philippe Brucker 		goto err_free;
2303cf48554SJean-Philippe Brucker 	}
2313cf48554SJean-Philippe Brucker 
2323cf48554SJean-Philippe Brucker 	if (!ep->viommu) {
2333cf48554SJean-Philippe Brucker 		pr_warn("No IOMMU node found\n");
2343cf48554SJean-Philippe Brucker 		/*
2353cf48554SJean-Philippe Brucker 		 * A future version of the table may use the node for other
2363cf48554SJean-Philippe Brucker 		 * purposes. Keep parsing.
2373cf48554SJean-Philippe Brucker 		 */
2383cf48554SJean-Philippe Brucker 		ret = 0;
2393cf48554SJean-Philippe Brucker 		goto err_free;
2403cf48554SJean-Philippe Brucker 	}
2413cf48554SJean-Philippe Brucker 
2423cf48554SJean-Philippe Brucker 	list_add(&ep->list, list);
2433cf48554SJean-Philippe Brucker 	return 0;
2443cf48554SJean-Philippe Brucker 
2453cf48554SJean-Philippe Brucker err_free:
2463cf48554SJean-Philippe Brucker 	kfree(ep);
2473cf48554SJean-Philippe Brucker 	return ret;
2483cf48554SJean-Philippe Brucker }
2493cf48554SJean-Philippe Brucker 
2503cf48554SJean-Philippe Brucker /**
2513dcb861dSEric Auger  * acpi_viot_early_init - Test the presence of VIOT and enable ACS
2523dcb861dSEric Auger  *
2533dcb861dSEric Auger  * If the VIOT does exist, ACS must be enabled. This cannot be
2543dcb861dSEric Auger  * done in acpi_viot_init() which is called after the bus scan
2553dcb861dSEric Auger  */
acpi_viot_early_init(void)2563dcb861dSEric Auger void __init acpi_viot_early_init(void)
2573dcb861dSEric Auger {
2583dcb861dSEric Auger #ifdef CONFIG_PCI
2593dcb861dSEric Auger 	acpi_status status;
2603dcb861dSEric Auger 	struct acpi_table_header *hdr;
2613dcb861dSEric Auger 
2623dcb861dSEric Auger 	status = acpi_get_table(ACPI_SIG_VIOT, 0, &hdr);
2633dcb861dSEric Auger 	if (ACPI_FAILURE(status))
2643dcb861dSEric Auger 		return;
2653dcb861dSEric Auger 	pci_request_acs();
2663dcb861dSEric Auger 	acpi_put_table(hdr);
2673dcb861dSEric Auger #endif
2683dcb861dSEric Auger }
2693dcb861dSEric Auger 
2703dcb861dSEric Auger /**
2713cf48554SJean-Philippe Brucker  * acpi_viot_init - Parse the VIOT table
2723cf48554SJean-Philippe Brucker  *
2733cf48554SJean-Philippe Brucker  * Parse the VIOT table, prepare the list of endpoints to be used during DMA
2743cf48554SJean-Philippe Brucker  * setup of devices.
2753cf48554SJean-Philippe Brucker  */
acpi_viot_init(void)2763cf48554SJean-Philippe Brucker void __init acpi_viot_init(void)
2773cf48554SJean-Philippe Brucker {
2783cf48554SJean-Philippe Brucker 	int i;
2793cf48554SJean-Philippe Brucker 	acpi_status status;
2803cf48554SJean-Philippe Brucker 	struct acpi_table_header *hdr;
2813cf48554SJean-Philippe Brucker 	struct acpi_viot_header *node;
2823cf48554SJean-Philippe Brucker 
2833cf48554SJean-Philippe Brucker 	status = acpi_get_table(ACPI_SIG_VIOT, 0, &hdr);
2843cf48554SJean-Philippe Brucker 	if (ACPI_FAILURE(status)) {
2853cf48554SJean-Philippe Brucker 		if (status != AE_NOT_FOUND) {
2863cf48554SJean-Philippe Brucker 			const char *msg = acpi_format_exception(status);
2873cf48554SJean-Philippe Brucker 
2883cf48554SJean-Philippe Brucker 			pr_err("Failed to get table, %s\n", msg);
2893cf48554SJean-Philippe Brucker 		}
2903cf48554SJean-Philippe Brucker 		return;
2913cf48554SJean-Philippe Brucker 	}
2923cf48554SJean-Philippe Brucker 
2933cf48554SJean-Philippe Brucker 	viot = (void *)hdr;
2943cf48554SJean-Philippe Brucker 
2953cf48554SJean-Philippe Brucker 	node = ACPI_ADD_PTR(struct acpi_viot_header, viot, viot->node_offset);
2963cf48554SJean-Philippe Brucker 	for (i = 0; i < viot->node_count; i++) {
2973cf48554SJean-Philippe Brucker 		if (viot_parse_node(node))
2983cf48554SJean-Philippe Brucker 			return;
2993cf48554SJean-Philippe Brucker 
3003cf48554SJean-Philippe Brucker 		node = ACPI_ADD_PTR(struct acpi_viot_header, node,
3013cf48554SJean-Philippe Brucker 				    node->length);
3023cf48554SJean-Philippe Brucker 	}
3033cf48554SJean-Philippe Brucker 
3043cf48554SJean-Philippe Brucker 	acpi_put_table(hdr);
3053cf48554SJean-Philippe Brucker }
3063cf48554SJean-Philippe Brucker 
viot_dev_iommu_init(struct device * dev,struct viot_iommu * viommu,u32 epid)3073cf48554SJean-Philippe Brucker static int viot_dev_iommu_init(struct device *dev, struct viot_iommu *viommu,
3083cf48554SJean-Philippe Brucker 			       u32 epid)
3093cf48554SJean-Philippe Brucker {
3103cf48554SJean-Philippe Brucker 	const struct iommu_ops *ops;
3113cf48554SJean-Philippe Brucker 
3123cf48554SJean-Philippe Brucker 	if (!viommu)
3133cf48554SJean-Philippe Brucker 		return -ENODEV;
3143cf48554SJean-Philippe Brucker 
3153cf48554SJean-Philippe Brucker 	/* We're not translating ourself */
316b0f2fe5aSAndy Shevchenko 	if (device_match_fwnode(dev, viommu->fwnode))
3173cf48554SJean-Philippe Brucker 		return -EINVAL;
3183cf48554SJean-Philippe Brucker 
3193cf48554SJean-Philippe Brucker 	ops = iommu_ops_from_fwnode(viommu->fwnode);
3203cf48554SJean-Philippe Brucker 	if (!ops)
3213cf48554SJean-Philippe Brucker 		return IS_ENABLED(CONFIG_VIRTIO_IOMMU) ?
3223cf48554SJean-Philippe Brucker 			-EPROBE_DEFER : -ENODEV;
3233cf48554SJean-Philippe Brucker 
3243cf48554SJean-Philippe Brucker 	return acpi_iommu_fwspec_init(dev, epid, viommu->fwnode, ops);
3253cf48554SJean-Philippe Brucker }
3263cf48554SJean-Philippe Brucker 
viot_pci_dev_iommu_init(struct pci_dev * pdev,u16 dev_id,void * data)3273cf48554SJean-Philippe Brucker static int viot_pci_dev_iommu_init(struct pci_dev *pdev, u16 dev_id, void *data)
3283cf48554SJean-Philippe Brucker {
3293cf48554SJean-Philippe Brucker 	u32 epid;
3303cf48554SJean-Philippe Brucker 	struct viot_endpoint *ep;
331*47d26684SJean-Philippe Brucker 	struct device *aliased_dev = data;
3323cf48554SJean-Philippe Brucker 	u32 domain_nr = pci_domain_nr(pdev->bus);
3333cf48554SJean-Philippe Brucker 
3343cf48554SJean-Philippe Brucker 	list_for_each_entry(ep, &viot_pci_ranges, list) {
3353cf48554SJean-Philippe Brucker 		if (domain_nr >= ep->segment_start &&
3363cf48554SJean-Philippe Brucker 		    domain_nr <= ep->segment_end &&
3373cf48554SJean-Philippe Brucker 		    dev_id >= ep->bdf_start &&
3383cf48554SJean-Philippe Brucker 		    dev_id <= ep->bdf_end) {
3393cf48554SJean-Philippe Brucker 			epid = ((domain_nr - ep->segment_start) << 16) +
3403cf48554SJean-Philippe Brucker 				dev_id - ep->bdf_start + ep->endpoint_id;
3413cf48554SJean-Philippe Brucker 
342*47d26684SJean-Philippe Brucker 			return viot_dev_iommu_init(aliased_dev, ep->viommu,
3433cf48554SJean-Philippe Brucker 						   epid);
3443cf48554SJean-Philippe Brucker 		}
3453cf48554SJean-Philippe Brucker 	}
3463cf48554SJean-Philippe Brucker 	return -ENODEV;
3473cf48554SJean-Philippe Brucker }
3483cf48554SJean-Philippe Brucker 
viot_mmio_dev_iommu_init(struct platform_device * pdev)3493cf48554SJean-Philippe Brucker static int viot_mmio_dev_iommu_init(struct platform_device *pdev)
3503cf48554SJean-Philippe Brucker {
3513cf48554SJean-Philippe Brucker 	struct resource *mem;
3523cf48554SJean-Philippe Brucker 	struct viot_endpoint *ep;
3533cf48554SJean-Philippe Brucker 
3543cf48554SJean-Philippe Brucker 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
3553cf48554SJean-Philippe Brucker 	if (!mem)
3563cf48554SJean-Philippe Brucker 		return -ENODEV;
3573cf48554SJean-Philippe Brucker 
3583cf48554SJean-Philippe Brucker 	list_for_each_entry(ep, &viot_mmio_endpoints, list) {
3593cf48554SJean-Philippe Brucker 		if (ep->address == mem->start)
3603cf48554SJean-Philippe Brucker 			return viot_dev_iommu_init(&pdev->dev, ep->viommu,
3613cf48554SJean-Philippe Brucker 						   ep->endpoint_id);
3623cf48554SJean-Philippe Brucker 	}
3633cf48554SJean-Philippe Brucker 	return -ENODEV;
3643cf48554SJean-Philippe Brucker }
3653cf48554SJean-Philippe Brucker 
3663cf48554SJean-Philippe Brucker /**
3673cf48554SJean-Philippe Brucker  * viot_iommu_configure - Setup IOMMU ops for an endpoint described by VIOT
3683cf48554SJean-Philippe Brucker  * @dev: the endpoint
3693cf48554SJean-Philippe Brucker  *
3703cf48554SJean-Philippe Brucker  * Return: 0 on success, <0 on failure
3713cf48554SJean-Philippe Brucker  */
viot_iommu_configure(struct device * dev)3723cf48554SJean-Philippe Brucker int viot_iommu_configure(struct device *dev)
3733cf48554SJean-Philippe Brucker {
3743cf48554SJean-Philippe Brucker 	if (dev_is_pci(dev))
3753cf48554SJean-Philippe Brucker 		return pci_for_each_dma_alias(to_pci_dev(dev),
376*47d26684SJean-Philippe Brucker 					      viot_pci_dev_iommu_init, dev);
3773cf48554SJean-Philippe Brucker 	else if (dev_is_platform(dev))
3783cf48554SJean-Philippe Brucker 		return viot_mmio_dev_iommu_init(to_platform_device(dev));
3793cf48554SJean-Philippe Brucker 	return -ENODEV;
3803cf48554SJean-Philippe Brucker }
381