18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * IOMMU API for s390 PCI devices
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2015
68c2ecf20Sopenharmony_ci * Author(s): Gerald Schaefer <gerald.schaefer@de.ibm.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/pci.h>
108c2ecf20Sopenharmony_ci#include <linux/iommu.h>
118c2ecf20Sopenharmony_ci#include <linux/iommu-helper.h>
128c2ecf20Sopenharmony_ci#include <linux/sizes.h>
138c2ecf20Sopenharmony_ci#include <asm/pci_dma.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci/*
168c2ecf20Sopenharmony_ci * Physically contiguous memory regions can be mapped with 4 KiB alignment,
178c2ecf20Sopenharmony_ci * we allow all page sizes that are an order of 4KiB (no special large page
188c2ecf20Sopenharmony_ci * support so far).
198c2ecf20Sopenharmony_ci */
208c2ecf20Sopenharmony_ci#define S390_IOMMU_PGSIZES	(~0xFFFUL)
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic const struct iommu_ops s390_iommu_ops;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistruct s390_domain {
258c2ecf20Sopenharmony_ci	struct iommu_domain	domain;
268c2ecf20Sopenharmony_ci	struct list_head	devices;
278c2ecf20Sopenharmony_ci	unsigned long		*dma_table;
288c2ecf20Sopenharmony_ci	spinlock_t		dma_table_lock;
298c2ecf20Sopenharmony_ci	spinlock_t		list_lock;
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct s390_domain_device {
338c2ecf20Sopenharmony_ci	struct list_head	list;
348c2ecf20Sopenharmony_ci	struct zpci_dev		*zdev;
358c2ecf20Sopenharmony_ci};
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic struct s390_domain *to_s390_domain(struct iommu_domain *dom)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	return container_of(dom, struct s390_domain, domain);
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic bool s390_iommu_capable(enum iommu_cap cap)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	switch (cap) {
458c2ecf20Sopenharmony_ci	case IOMMU_CAP_CACHE_COHERENCY:
468c2ecf20Sopenharmony_ci		return true;
478c2ecf20Sopenharmony_ci	case IOMMU_CAP_INTR_REMAP:
488c2ecf20Sopenharmony_ci		return true;
498c2ecf20Sopenharmony_ci	default:
508c2ecf20Sopenharmony_ci		return false;
518c2ecf20Sopenharmony_ci	}
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic struct iommu_domain *s390_domain_alloc(unsigned domain_type)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	struct s390_domain *s390_domain;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	if (domain_type != IOMMU_DOMAIN_UNMANAGED)
598c2ecf20Sopenharmony_ci		return NULL;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	s390_domain = kzalloc(sizeof(*s390_domain), GFP_KERNEL);
628c2ecf20Sopenharmony_ci	if (!s390_domain)
638c2ecf20Sopenharmony_ci		return NULL;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	s390_domain->dma_table = dma_alloc_cpu_table();
668c2ecf20Sopenharmony_ci	if (!s390_domain->dma_table) {
678c2ecf20Sopenharmony_ci		kfree(s390_domain);
688c2ecf20Sopenharmony_ci		return NULL;
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	spin_lock_init(&s390_domain->dma_table_lock);
728c2ecf20Sopenharmony_ci	spin_lock_init(&s390_domain->list_lock);
738c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&s390_domain->devices);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	return &s390_domain->domain;
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic void s390_domain_free(struct iommu_domain *domain)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	struct s390_domain *s390_domain = to_s390_domain(domain);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	dma_cleanup_tables(s390_domain->dma_table);
838c2ecf20Sopenharmony_ci	kfree(s390_domain);
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic int s390_iommu_attach_device(struct iommu_domain *domain,
878c2ecf20Sopenharmony_ci				    struct device *dev)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	struct s390_domain *s390_domain = to_s390_domain(domain);
908c2ecf20Sopenharmony_ci	struct zpci_dev *zdev = to_zpci_dev(dev);
918c2ecf20Sopenharmony_ci	struct s390_domain_device *domain_device;
928c2ecf20Sopenharmony_ci	unsigned long flags;
938c2ecf20Sopenharmony_ci	int rc;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (!zdev)
968c2ecf20Sopenharmony_ci		return -ENODEV;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	domain_device = kzalloc(sizeof(*domain_device), GFP_KERNEL);
998c2ecf20Sopenharmony_ci	if (!domain_device)
1008c2ecf20Sopenharmony_ci		return -ENOMEM;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	if (zdev->dma_table)
1038c2ecf20Sopenharmony_ci		zpci_dma_exit_device(zdev);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	zdev->dma_table = s390_domain->dma_table;
1068c2ecf20Sopenharmony_ci	rc = zpci_register_ioat(zdev, 0, zdev->start_dma, zdev->end_dma,
1078c2ecf20Sopenharmony_ci				(u64) zdev->dma_table);
1088c2ecf20Sopenharmony_ci	if (rc)
1098c2ecf20Sopenharmony_ci		goto out_restore;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	spin_lock_irqsave(&s390_domain->list_lock, flags);
1128c2ecf20Sopenharmony_ci	/* First device defines the DMA range limits */
1138c2ecf20Sopenharmony_ci	if (list_empty(&s390_domain->devices)) {
1148c2ecf20Sopenharmony_ci		domain->geometry.aperture_start = zdev->start_dma;
1158c2ecf20Sopenharmony_ci		domain->geometry.aperture_end = zdev->end_dma;
1168c2ecf20Sopenharmony_ci		domain->geometry.force_aperture = true;
1178c2ecf20Sopenharmony_ci	/* Allow only devices with identical DMA range limits */
1188c2ecf20Sopenharmony_ci	} else if (domain->geometry.aperture_start != zdev->start_dma ||
1198c2ecf20Sopenharmony_ci		   domain->geometry.aperture_end != zdev->end_dma) {
1208c2ecf20Sopenharmony_ci		rc = -EINVAL;
1218c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&s390_domain->list_lock, flags);
1228c2ecf20Sopenharmony_ci		goto out_restore;
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci	domain_device->zdev = zdev;
1258c2ecf20Sopenharmony_ci	zdev->s390_domain = s390_domain;
1268c2ecf20Sopenharmony_ci	list_add(&domain_device->list, &s390_domain->devices);
1278c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&s390_domain->list_lock, flags);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	return 0;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ciout_restore:
1328c2ecf20Sopenharmony_ci	zpci_dma_init_device(zdev);
1338c2ecf20Sopenharmony_ci	kfree(domain_device);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	return rc;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic void s390_iommu_detach_device(struct iommu_domain *domain,
1398c2ecf20Sopenharmony_ci				     struct device *dev)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	struct s390_domain *s390_domain = to_s390_domain(domain);
1428c2ecf20Sopenharmony_ci	struct zpci_dev *zdev = to_zpci_dev(dev);
1438c2ecf20Sopenharmony_ci	struct s390_domain_device *domain_device, *tmp;
1448c2ecf20Sopenharmony_ci	unsigned long flags;
1458c2ecf20Sopenharmony_ci	int found = 0;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	if (!zdev)
1488c2ecf20Sopenharmony_ci		return;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	spin_lock_irqsave(&s390_domain->list_lock, flags);
1518c2ecf20Sopenharmony_ci	list_for_each_entry_safe(domain_device, tmp, &s390_domain->devices,
1528c2ecf20Sopenharmony_ci				 list) {
1538c2ecf20Sopenharmony_ci		if (domain_device->zdev == zdev) {
1548c2ecf20Sopenharmony_ci			list_del(&domain_device->list);
1558c2ecf20Sopenharmony_ci			kfree(domain_device);
1568c2ecf20Sopenharmony_ci			found = 1;
1578c2ecf20Sopenharmony_ci			break;
1588c2ecf20Sopenharmony_ci		}
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&s390_domain->list_lock, flags);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (found) {
1638c2ecf20Sopenharmony_ci		zdev->s390_domain = NULL;
1648c2ecf20Sopenharmony_ci		zpci_unregister_ioat(zdev, 0);
1658c2ecf20Sopenharmony_ci		zpci_dma_init_device(zdev);
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic struct iommu_device *s390_iommu_probe_device(struct device *dev)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	struct zpci_dev *zdev = to_zpci_dev(dev);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	return &zdev->iommu_dev;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic void s390_iommu_release_device(struct device *dev)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	struct zpci_dev *zdev = to_zpci_dev(dev);
1798c2ecf20Sopenharmony_ci	struct iommu_domain *domain;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	/*
1828c2ecf20Sopenharmony_ci	 * This is a workaround for a scenario where the IOMMU API common code
1838c2ecf20Sopenharmony_ci	 * "forgets" to call the detach_dev callback: After binding a device
1848c2ecf20Sopenharmony_ci	 * to vfio-pci and completing the VFIO_SET_IOMMU ioctl (which triggers
1858c2ecf20Sopenharmony_ci	 * the attach_dev), removing the device via
1868c2ecf20Sopenharmony_ci	 * "echo 1 > /sys/bus/pci/devices/.../remove" won't trigger detach_dev,
1878c2ecf20Sopenharmony_ci	 * only release_device will be called via the BUS_NOTIFY_REMOVED_DEVICE
1888c2ecf20Sopenharmony_ci	 * notifier.
1898c2ecf20Sopenharmony_ci	 *
1908c2ecf20Sopenharmony_ci	 * So let's call detach_dev from here if it hasn't been called before.
1918c2ecf20Sopenharmony_ci	 */
1928c2ecf20Sopenharmony_ci	if (zdev && zdev->s390_domain) {
1938c2ecf20Sopenharmony_ci		domain = iommu_get_domain_for_dev(dev);
1948c2ecf20Sopenharmony_ci		if (domain)
1958c2ecf20Sopenharmony_ci			s390_iommu_detach_device(domain, dev);
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic int s390_iommu_update_trans(struct s390_domain *s390_domain,
2008c2ecf20Sopenharmony_ci				   unsigned long pa, dma_addr_t dma_addr,
2018c2ecf20Sopenharmony_ci				   size_t size, int flags)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	struct s390_domain_device *domain_device;
2048c2ecf20Sopenharmony_ci	u8 *page_addr = (u8 *) (pa & PAGE_MASK);
2058c2ecf20Sopenharmony_ci	dma_addr_t start_dma_addr = dma_addr;
2068c2ecf20Sopenharmony_ci	unsigned long irq_flags, nr_pages, i;
2078c2ecf20Sopenharmony_ci	unsigned long *entry;
2088c2ecf20Sopenharmony_ci	int rc = 0;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if (dma_addr < s390_domain->domain.geometry.aperture_start ||
2118c2ecf20Sopenharmony_ci	    dma_addr + size > s390_domain->domain.geometry.aperture_end)
2128c2ecf20Sopenharmony_ci		return -EINVAL;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
2158c2ecf20Sopenharmony_ci	if (!nr_pages)
2168c2ecf20Sopenharmony_ci		return 0;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	spin_lock_irqsave(&s390_domain->dma_table_lock, irq_flags);
2198c2ecf20Sopenharmony_ci	for (i = 0; i < nr_pages; i++) {
2208c2ecf20Sopenharmony_ci		entry = dma_walk_cpu_trans(s390_domain->dma_table, dma_addr);
2218c2ecf20Sopenharmony_ci		if (!entry) {
2228c2ecf20Sopenharmony_ci			rc = -ENOMEM;
2238c2ecf20Sopenharmony_ci			goto undo_cpu_trans;
2248c2ecf20Sopenharmony_ci		}
2258c2ecf20Sopenharmony_ci		dma_update_cpu_trans(entry, page_addr, flags);
2268c2ecf20Sopenharmony_ci		page_addr += PAGE_SIZE;
2278c2ecf20Sopenharmony_ci		dma_addr += PAGE_SIZE;
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	spin_lock(&s390_domain->list_lock);
2318c2ecf20Sopenharmony_ci	list_for_each_entry(domain_device, &s390_domain->devices, list) {
2328c2ecf20Sopenharmony_ci		rc = zpci_refresh_trans((u64) domain_device->zdev->fh << 32,
2338c2ecf20Sopenharmony_ci					start_dma_addr, nr_pages * PAGE_SIZE);
2348c2ecf20Sopenharmony_ci		if (rc)
2358c2ecf20Sopenharmony_ci			break;
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci	spin_unlock(&s390_domain->list_lock);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ciundo_cpu_trans:
2408c2ecf20Sopenharmony_ci	if (rc && ((flags & ZPCI_PTE_VALID_MASK) == ZPCI_PTE_VALID)) {
2418c2ecf20Sopenharmony_ci		flags = ZPCI_PTE_INVALID;
2428c2ecf20Sopenharmony_ci		while (i-- > 0) {
2438c2ecf20Sopenharmony_ci			page_addr -= PAGE_SIZE;
2448c2ecf20Sopenharmony_ci			dma_addr -= PAGE_SIZE;
2458c2ecf20Sopenharmony_ci			entry = dma_walk_cpu_trans(s390_domain->dma_table,
2468c2ecf20Sopenharmony_ci						   dma_addr);
2478c2ecf20Sopenharmony_ci			if (!entry)
2488c2ecf20Sopenharmony_ci				break;
2498c2ecf20Sopenharmony_ci			dma_update_cpu_trans(entry, page_addr, flags);
2508c2ecf20Sopenharmony_ci		}
2518c2ecf20Sopenharmony_ci	}
2528c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&s390_domain->dma_table_lock, irq_flags);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	return rc;
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic int s390_iommu_map(struct iommu_domain *domain, unsigned long iova,
2588c2ecf20Sopenharmony_ci			  phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	struct s390_domain *s390_domain = to_s390_domain(domain);
2618c2ecf20Sopenharmony_ci	int flags = ZPCI_PTE_VALID, rc = 0;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	if (!(prot & IOMMU_READ))
2648c2ecf20Sopenharmony_ci		return -EINVAL;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (!(prot & IOMMU_WRITE))
2678c2ecf20Sopenharmony_ci		flags |= ZPCI_TABLE_PROTECTED;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	rc = s390_iommu_update_trans(s390_domain, (unsigned long) paddr, iova,
2708c2ecf20Sopenharmony_ci				     size, flags);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	return rc;
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cistatic phys_addr_t s390_iommu_iova_to_phys(struct iommu_domain *domain,
2768c2ecf20Sopenharmony_ci					   dma_addr_t iova)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	struct s390_domain *s390_domain = to_s390_domain(domain);
2798c2ecf20Sopenharmony_ci	unsigned long *sto, *pto, *rto, flags;
2808c2ecf20Sopenharmony_ci	unsigned int rtx, sx, px;
2818c2ecf20Sopenharmony_ci	phys_addr_t phys = 0;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	if (iova < domain->geometry.aperture_start ||
2848c2ecf20Sopenharmony_ci	    iova > domain->geometry.aperture_end)
2858c2ecf20Sopenharmony_ci		return 0;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	rtx = calc_rtx(iova);
2888c2ecf20Sopenharmony_ci	sx = calc_sx(iova);
2898c2ecf20Sopenharmony_ci	px = calc_px(iova);
2908c2ecf20Sopenharmony_ci	rto = s390_domain->dma_table;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	spin_lock_irqsave(&s390_domain->dma_table_lock, flags);
2938c2ecf20Sopenharmony_ci	if (rto && reg_entry_isvalid(rto[rtx])) {
2948c2ecf20Sopenharmony_ci		sto = get_rt_sto(rto[rtx]);
2958c2ecf20Sopenharmony_ci		if (sto && reg_entry_isvalid(sto[sx])) {
2968c2ecf20Sopenharmony_ci			pto = get_st_pto(sto[sx]);
2978c2ecf20Sopenharmony_ci			if (pto && pt_entry_isvalid(pto[px]))
2988c2ecf20Sopenharmony_ci				phys = pto[px] & ZPCI_PTE_ADDR_MASK;
2998c2ecf20Sopenharmony_ci		}
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&s390_domain->dma_table_lock, flags);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	return phys;
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic size_t s390_iommu_unmap(struct iommu_domain *domain,
3078c2ecf20Sopenharmony_ci			       unsigned long iova, size_t size,
3088c2ecf20Sopenharmony_ci			       struct iommu_iotlb_gather *gather)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	struct s390_domain *s390_domain = to_s390_domain(domain);
3118c2ecf20Sopenharmony_ci	int flags = ZPCI_PTE_INVALID;
3128c2ecf20Sopenharmony_ci	phys_addr_t paddr;
3138c2ecf20Sopenharmony_ci	int rc;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	paddr = s390_iommu_iova_to_phys(domain, iova);
3168c2ecf20Sopenharmony_ci	if (!paddr)
3178c2ecf20Sopenharmony_ci		return 0;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	rc = s390_iommu_update_trans(s390_domain, (unsigned long) paddr, iova,
3208c2ecf20Sopenharmony_ci				     size, flags);
3218c2ecf20Sopenharmony_ci	if (rc)
3228c2ecf20Sopenharmony_ci		return 0;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	return size;
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ciint zpci_init_iommu(struct zpci_dev *zdev)
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	int rc = 0;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	rc = iommu_device_sysfs_add(&zdev->iommu_dev, NULL, NULL,
3328c2ecf20Sopenharmony_ci				    "s390-iommu.%08x", zdev->fid);
3338c2ecf20Sopenharmony_ci	if (rc)
3348c2ecf20Sopenharmony_ci		goto out_err;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	iommu_device_set_ops(&zdev->iommu_dev, &s390_iommu_ops);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	rc = iommu_device_register(&zdev->iommu_dev);
3398c2ecf20Sopenharmony_ci	if (rc)
3408c2ecf20Sopenharmony_ci		goto out_sysfs;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	return 0;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ciout_sysfs:
3458c2ecf20Sopenharmony_ci	iommu_device_sysfs_remove(&zdev->iommu_dev);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ciout_err:
3488c2ecf20Sopenharmony_ci	return rc;
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_civoid zpci_destroy_iommu(struct zpci_dev *zdev)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	iommu_device_unregister(&zdev->iommu_dev);
3548c2ecf20Sopenharmony_ci	iommu_device_sysfs_remove(&zdev->iommu_dev);
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic const struct iommu_ops s390_iommu_ops = {
3588c2ecf20Sopenharmony_ci	.capable = s390_iommu_capable,
3598c2ecf20Sopenharmony_ci	.domain_alloc = s390_domain_alloc,
3608c2ecf20Sopenharmony_ci	.domain_free = s390_domain_free,
3618c2ecf20Sopenharmony_ci	.attach_dev = s390_iommu_attach_device,
3628c2ecf20Sopenharmony_ci	.detach_dev = s390_iommu_detach_device,
3638c2ecf20Sopenharmony_ci	.map = s390_iommu_map,
3648c2ecf20Sopenharmony_ci	.unmap = s390_iommu_unmap,
3658c2ecf20Sopenharmony_ci	.iova_to_phys = s390_iommu_iova_to_phys,
3668c2ecf20Sopenharmony_ci	.probe_device = s390_iommu_probe_device,
3678c2ecf20Sopenharmony_ci	.release_device = s390_iommu_release_device,
3688c2ecf20Sopenharmony_ci	.device_group = generic_device_group,
3698c2ecf20Sopenharmony_ci	.pgsize_bitmap = S390_IOMMU_PGSIZES,
3708c2ecf20Sopenharmony_ci};
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic int __init s390_iommu_init(void)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	return bus_set_iommu(&pci_bus_type, &s390_iommu_ops);
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_cisubsys_initcall(s390_iommu_init);
377