162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * IOMMU API for s390 PCI devices
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright IBM Corp. 2015
662306a36Sopenharmony_ci * Author(s): Gerald Schaefer <gerald.schaefer@de.ibm.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/pci.h>
1062306a36Sopenharmony_ci#include <linux/iommu.h>
1162306a36Sopenharmony_ci#include <linux/iommu-helper.h>
1262306a36Sopenharmony_ci#include <linux/sizes.h>
1362306a36Sopenharmony_ci#include <linux/rculist.h>
1462306a36Sopenharmony_ci#include <linux/rcupdate.h>
1562306a36Sopenharmony_ci#include <asm/pci_dma.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic const struct iommu_ops s390_iommu_ops;
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistruct s390_domain {
2062306a36Sopenharmony_ci	struct iommu_domain	domain;
2162306a36Sopenharmony_ci	struct list_head	devices;
2262306a36Sopenharmony_ci	unsigned long		*dma_table;
2362306a36Sopenharmony_ci	spinlock_t		list_lock;
2462306a36Sopenharmony_ci	struct rcu_head		rcu;
2562306a36Sopenharmony_ci};
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic struct s390_domain *to_s390_domain(struct iommu_domain *dom)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	return container_of(dom, struct s390_domain, domain);
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic bool s390_iommu_capable(struct device *dev, enum iommu_cap cap)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	switch (cap) {
3562306a36Sopenharmony_ci	case IOMMU_CAP_CACHE_COHERENCY:
3662306a36Sopenharmony_ci		return true;
3762306a36Sopenharmony_ci	default:
3862306a36Sopenharmony_ci		return false;
3962306a36Sopenharmony_ci	}
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic struct iommu_domain *s390_domain_alloc(unsigned domain_type)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	struct s390_domain *s390_domain;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	if (domain_type != IOMMU_DOMAIN_UNMANAGED)
4762306a36Sopenharmony_ci		return NULL;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	s390_domain = kzalloc(sizeof(*s390_domain), GFP_KERNEL);
5062306a36Sopenharmony_ci	if (!s390_domain)
5162306a36Sopenharmony_ci		return NULL;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	s390_domain->dma_table = dma_alloc_cpu_table(GFP_KERNEL);
5462306a36Sopenharmony_ci	if (!s390_domain->dma_table) {
5562306a36Sopenharmony_ci		kfree(s390_domain);
5662306a36Sopenharmony_ci		return NULL;
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci	s390_domain->domain.geometry.force_aperture = true;
5962306a36Sopenharmony_ci	s390_domain->domain.geometry.aperture_start = 0;
6062306a36Sopenharmony_ci	s390_domain->domain.geometry.aperture_end = ZPCI_TABLE_SIZE_RT - 1;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	spin_lock_init(&s390_domain->list_lock);
6362306a36Sopenharmony_ci	INIT_LIST_HEAD_RCU(&s390_domain->devices);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	return &s390_domain->domain;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic void s390_iommu_rcu_free_domain(struct rcu_head *head)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	struct s390_domain *s390_domain = container_of(head, struct s390_domain, rcu);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	dma_cleanup_tables(s390_domain->dma_table);
7362306a36Sopenharmony_ci	kfree(s390_domain);
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic void s390_domain_free(struct iommu_domain *domain)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct s390_domain *s390_domain = to_s390_domain(domain);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	rcu_read_lock();
8162306a36Sopenharmony_ci	WARN_ON(!list_empty(&s390_domain->devices));
8262306a36Sopenharmony_ci	rcu_read_unlock();
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	call_rcu(&s390_domain->rcu, s390_iommu_rcu_free_domain);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic void __s390_iommu_detach_device(struct zpci_dev *zdev)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct s390_domain *s390_domain = zdev->s390_domain;
9062306a36Sopenharmony_ci	unsigned long flags;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (!s390_domain)
9362306a36Sopenharmony_ci		return;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	spin_lock_irqsave(&s390_domain->list_lock, flags);
9662306a36Sopenharmony_ci	list_del_rcu(&zdev->iommu_list);
9762306a36Sopenharmony_ci	spin_unlock_irqrestore(&s390_domain->list_lock, flags);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	zpci_unregister_ioat(zdev, 0);
10062306a36Sopenharmony_ci	zdev->s390_domain = NULL;
10162306a36Sopenharmony_ci	zdev->dma_table = NULL;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic int s390_iommu_attach_device(struct iommu_domain *domain,
10562306a36Sopenharmony_ci				    struct device *dev)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	struct s390_domain *s390_domain = to_s390_domain(domain);
10862306a36Sopenharmony_ci	struct zpci_dev *zdev = to_zpci_dev(dev);
10962306a36Sopenharmony_ci	unsigned long flags;
11062306a36Sopenharmony_ci	u8 status;
11162306a36Sopenharmony_ci	int cc;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	if (!zdev)
11462306a36Sopenharmony_ci		return -ENODEV;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (WARN_ON(domain->geometry.aperture_start > zdev->end_dma ||
11762306a36Sopenharmony_ci		domain->geometry.aperture_end < zdev->start_dma))
11862306a36Sopenharmony_ci		return -EINVAL;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	if (zdev->s390_domain)
12162306a36Sopenharmony_ci		__s390_iommu_detach_device(zdev);
12262306a36Sopenharmony_ci	else if (zdev->dma_table)
12362306a36Sopenharmony_ci		zpci_dma_exit_device(zdev);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	cc = zpci_register_ioat(zdev, 0, zdev->start_dma, zdev->end_dma,
12662306a36Sopenharmony_ci				virt_to_phys(s390_domain->dma_table), &status);
12762306a36Sopenharmony_ci	/*
12862306a36Sopenharmony_ci	 * If the device is undergoing error recovery the reset code
12962306a36Sopenharmony_ci	 * will re-establish the new domain.
13062306a36Sopenharmony_ci	 */
13162306a36Sopenharmony_ci	if (cc && status != ZPCI_PCI_ST_FUNC_NOT_AVAIL)
13262306a36Sopenharmony_ci		return -EIO;
13362306a36Sopenharmony_ci	zdev->dma_table = s390_domain->dma_table;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	zdev->dma_table = s390_domain->dma_table;
13662306a36Sopenharmony_ci	zdev->s390_domain = s390_domain;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	spin_lock_irqsave(&s390_domain->list_lock, flags);
13962306a36Sopenharmony_ci	list_add_rcu(&zdev->iommu_list, &s390_domain->devices);
14062306a36Sopenharmony_ci	spin_unlock_irqrestore(&s390_domain->list_lock, flags);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	return 0;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic void s390_iommu_set_platform_dma(struct device *dev)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	struct zpci_dev *zdev = to_zpci_dev(dev);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	__s390_iommu_detach_device(zdev);
15062306a36Sopenharmony_ci	zpci_dma_init_device(zdev);
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic void s390_iommu_get_resv_regions(struct device *dev,
15462306a36Sopenharmony_ci					struct list_head *list)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	struct zpci_dev *zdev = to_zpci_dev(dev);
15762306a36Sopenharmony_ci	struct iommu_resv_region *region;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if (zdev->start_dma) {
16062306a36Sopenharmony_ci		region = iommu_alloc_resv_region(0, zdev->start_dma, 0,
16162306a36Sopenharmony_ci						 IOMMU_RESV_RESERVED, GFP_KERNEL);
16262306a36Sopenharmony_ci		if (!region)
16362306a36Sopenharmony_ci			return;
16462306a36Sopenharmony_ci		list_add_tail(&region->list, list);
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (zdev->end_dma < ZPCI_TABLE_SIZE_RT - 1) {
16862306a36Sopenharmony_ci		region = iommu_alloc_resv_region(zdev->end_dma + 1,
16962306a36Sopenharmony_ci						 ZPCI_TABLE_SIZE_RT - zdev->end_dma - 1,
17062306a36Sopenharmony_ci						 0, IOMMU_RESV_RESERVED, GFP_KERNEL);
17162306a36Sopenharmony_ci		if (!region)
17262306a36Sopenharmony_ci			return;
17362306a36Sopenharmony_ci		list_add_tail(&region->list, list);
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic struct iommu_device *s390_iommu_probe_device(struct device *dev)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	struct zpci_dev *zdev;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	if (!dev_is_pci(dev))
18262306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	zdev = to_zpci_dev(dev);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	if (zdev->start_dma > zdev->end_dma ||
18762306a36Sopenharmony_ci	    zdev->start_dma > ZPCI_TABLE_SIZE_RT - 1)
18862306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	if (zdev->end_dma > ZPCI_TABLE_SIZE_RT - 1)
19162306a36Sopenharmony_ci		zdev->end_dma = ZPCI_TABLE_SIZE_RT - 1;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	return &zdev->iommu_dev;
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic void s390_iommu_release_device(struct device *dev)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct zpci_dev *zdev = to_zpci_dev(dev);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	/*
20162306a36Sopenharmony_ci	 * release_device is expected to detach any domain currently attached
20262306a36Sopenharmony_ci	 * to the device, but keep it attached to other devices in the group.
20362306a36Sopenharmony_ci	 */
20462306a36Sopenharmony_ci	if (zdev)
20562306a36Sopenharmony_ci		__s390_iommu_detach_device(zdev);
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic void s390_iommu_flush_iotlb_all(struct iommu_domain *domain)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	struct s390_domain *s390_domain = to_s390_domain(domain);
21162306a36Sopenharmony_ci	struct zpci_dev *zdev;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	rcu_read_lock();
21462306a36Sopenharmony_ci	list_for_each_entry_rcu(zdev, &s390_domain->devices, iommu_list) {
21562306a36Sopenharmony_ci		zpci_refresh_trans((u64)zdev->fh << 32, zdev->start_dma,
21662306a36Sopenharmony_ci				   zdev->end_dma - zdev->start_dma + 1);
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci	rcu_read_unlock();
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic void s390_iommu_iotlb_sync(struct iommu_domain *domain,
22262306a36Sopenharmony_ci				  struct iommu_iotlb_gather *gather)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	struct s390_domain *s390_domain = to_s390_domain(domain);
22562306a36Sopenharmony_ci	size_t size = gather->end - gather->start + 1;
22662306a36Sopenharmony_ci	struct zpci_dev *zdev;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	/* If gather was never added to there is nothing to flush */
22962306a36Sopenharmony_ci	if (!gather->end)
23062306a36Sopenharmony_ci		return;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	rcu_read_lock();
23362306a36Sopenharmony_ci	list_for_each_entry_rcu(zdev, &s390_domain->devices, iommu_list) {
23462306a36Sopenharmony_ci		zpci_refresh_trans((u64)zdev->fh << 32, gather->start,
23562306a36Sopenharmony_ci				   size);
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci	rcu_read_unlock();
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic void s390_iommu_iotlb_sync_map(struct iommu_domain *domain,
24162306a36Sopenharmony_ci				      unsigned long iova, size_t size)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct s390_domain *s390_domain = to_s390_domain(domain);
24462306a36Sopenharmony_ci	struct zpci_dev *zdev;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	rcu_read_lock();
24762306a36Sopenharmony_ci	list_for_each_entry_rcu(zdev, &s390_domain->devices, iommu_list) {
24862306a36Sopenharmony_ci		if (!zdev->tlb_refresh)
24962306a36Sopenharmony_ci			continue;
25062306a36Sopenharmony_ci		zpci_refresh_trans((u64)zdev->fh << 32,
25162306a36Sopenharmony_ci				   iova, size);
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci	rcu_read_unlock();
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic int s390_iommu_validate_trans(struct s390_domain *s390_domain,
25762306a36Sopenharmony_ci				     phys_addr_t pa, dma_addr_t dma_addr,
25862306a36Sopenharmony_ci				     unsigned long nr_pages, int flags,
25962306a36Sopenharmony_ci				     gfp_t gfp)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	phys_addr_t page_addr = pa & PAGE_MASK;
26262306a36Sopenharmony_ci	unsigned long *entry;
26362306a36Sopenharmony_ci	unsigned long i;
26462306a36Sopenharmony_ci	int rc;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	for (i = 0; i < nr_pages; i++) {
26762306a36Sopenharmony_ci		entry = dma_walk_cpu_trans(s390_domain->dma_table, dma_addr,
26862306a36Sopenharmony_ci					   gfp);
26962306a36Sopenharmony_ci		if (unlikely(!entry)) {
27062306a36Sopenharmony_ci			rc = -ENOMEM;
27162306a36Sopenharmony_ci			goto undo_cpu_trans;
27262306a36Sopenharmony_ci		}
27362306a36Sopenharmony_ci		dma_update_cpu_trans(entry, page_addr, flags);
27462306a36Sopenharmony_ci		page_addr += PAGE_SIZE;
27562306a36Sopenharmony_ci		dma_addr += PAGE_SIZE;
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	return 0;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ciundo_cpu_trans:
28162306a36Sopenharmony_ci	while (i-- > 0) {
28262306a36Sopenharmony_ci		dma_addr -= PAGE_SIZE;
28362306a36Sopenharmony_ci		entry = dma_walk_cpu_trans(s390_domain->dma_table,
28462306a36Sopenharmony_ci					   dma_addr, gfp);
28562306a36Sopenharmony_ci		if (!entry)
28662306a36Sopenharmony_ci			break;
28762306a36Sopenharmony_ci		dma_update_cpu_trans(entry, 0, ZPCI_PTE_INVALID);
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	return rc;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic int s390_iommu_invalidate_trans(struct s390_domain *s390_domain,
29462306a36Sopenharmony_ci				       dma_addr_t dma_addr, unsigned long nr_pages)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	unsigned long *entry;
29762306a36Sopenharmony_ci	unsigned long i;
29862306a36Sopenharmony_ci	int rc = 0;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	for (i = 0; i < nr_pages; i++) {
30162306a36Sopenharmony_ci		entry = dma_walk_cpu_trans(s390_domain->dma_table, dma_addr,
30262306a36Sopenharmony_ci					   GFP_ATOMIC);
30362306a36Sopenharmony_ci		if (unlikely(!entry)) {
30462306a36Sopenharmony_ci			rc = -EINVAL;
30562306a36Sopenharmony_ci			break;
30662306a36Sopenharmony_ci		}
30762306a36Sopenharmony_ci		dma_update_cpu_trans(entry, 0, ZPCI_PTE_INVALID);
30862306a36Sopenharmony_ci		dma_addr += PAGE_SIZE;
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	return rc;
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic int s390_iommu_map_pages(struct iommu_domain *domain,
31562306a36Sopenharmony_ci				unsigned long iova, phys_addr_t paddr,
31662306a36Sopenharmony_ci				size_t pgsize, size_t pgcount,
31762306a36Sopenharmony_ci				int prot, gfp_t gfp, size_t *mapped)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct s390_domain *s390_domain = to_s390_domain(domain);
32062306a36Sopenharmony_ci	size_t size = pgcount << __ffs(pgsize);
32162306a36Sopenharmony_ci	int flags = ZPCI_PTE_VALID, rc = 0;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	if (pgsize != SZ_4K)
32462306a36Sopenharmony_ci		return -EINVAL;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	if (iova < s390_domain->domain.geometry.aperture_start ||
32762306a36Sopenharmony_ci	    (iova + size - 1) > s390_domain->domain.geometry.aperture_end)
32862306a36Sopenharmony_ci		return -EINVAL;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (!IS_ALIGNED(iova | paddr, pgsize))
33162306a36Sopenharmony_ci		return -EINVAL;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	if (!(prot & IOMMU_READ))
33462306a36Sopenharmony_ci		return -EINVAL;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	if (!(prot & IOMMU_WRITE))
33762306a36Sopenharmony_ci		flags |= ZPCI_TABLE_PROTECTED;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	rc = s390_iommu_validate_trans(s390_domain, paddr, iova,
34062306a36Sopenharmony_ci				       pgcount, flags, gfp);
34162306a36Sopenharmony_ci	if (!rc)
34262306a36Sopenharmony_ci		*mapped = size;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	return rc;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic phys_addr_t s390_iommu_iova_to_phys(struct iommu_domain *domain,
34862306a36Sopenharmony_ci					   dma_addr_t iova)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	struct s390_domain *s390_domain = to_s390_domain(domain);
35162306a36Sopenharmony_ci	unsigned long *rto, *sto, *pto;
35262306a36Sopenharmony_ci	unsigned long ste, pte, rte;
35362306a36Sopenharmony_ci	unsigned int rtx, sx, px;
35462306a36Sopenharmony_ci	phys_addr_t phys = 0;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	if (iova < domain->geometry.aperture_start ||
35762306a36Sopenharmony_ci	    iova > domain->geometry.aperture_end)
35862306a36Sopenharmony_ci		return 0;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	rtx = calc_rtx(iova);
36162306a36Sopenharmony_ci	sx = calc_sx(iova);
36262306a36Sopenharmony_ci	px = calc_px(iova);
36362306a36Sopenharmony_ci	rto = s390_domain->dma_table;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	rte = READ_ONCE(rto[rtx]);
36662306a36Sopenharmony_ci	if (reg_entry_isvalid(rte)) {
36762306a36Sopenharmony_ci		sto = get_rt_sto(rte);
36862306a36Sopenharmony_ci		ste = READ_ONCE(sto[sx]);
36962306a36Sopenharmony_ci		if (reg_entry_isvalid(ste)) {
37062306a36Sopenharmony_ci			pto = get_st_pto(ste);
37162306a36Sopenharmony_ci			pte = READ_ONCE(pto[px]);
37262306a36Sopenharmony_ci			if (pt_entry_isvalid(pte))
37362306a36Sopenharmony_ci				phys = pte & ZPCI_PTE_ADDR_MASK;
37462306a36Sopenharmony_ci		}
37562306a36Sopenharmony_ci	}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	return phys;
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic size_t s390_iommu_unmap_pages(struct iommu_domain *domain,
38162306a36Sopenharmony_ci				     unsigned long iova,
38262306a36Sopenharmony_ci				     size_t pgsize, size_t pgcount,
38362306a36Sopenharmony_ci				     struct iommu_iotlb_gather *gather)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	struct s390_domain *s390_domain = to_s390_domain(domain);
38662306a36Sopenharmony_ci	size_t size = pgcount << __ffs(pgsize);
38762306a36Sopenharmony_ci	int rc;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	if (WARN_ON(iova < s390_domain->domain.geometry.aperture_start ||
39062306a36Sopenharmony_ci	    (iova + size - 1) > s390_domain->domain.geometry.aperture_end))
39162306a36Sopenharmony_ci		return 0;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	rc = s390_iommu_invalidate_trans(s390_domain, iova, pgcount);
39462306a36Sopenharmony_ci	if (rc)
39562306a36Sopenharmony_ci		return 0;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	iommu_iotlb_gather_add_range(gather, iova, size);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	return size;
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ciint zpci_init_iommu(struct zpci_dev *zdev)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	int rc = 0;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	rc = iommu_device_sysfs_add(&zdev->iommu_dev, NULL, NULL,
40762306a36Sopenharmony_ci				    "s390-iommu.%08x", zdev->fid);
40862306a36Sopenharmony_ci	if (rc)
40962306a36Sopenharmony_ci		goto out_err;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	rc = iommu_device_register(&zdev->iommu_dev, &s390_iommu_ops, NULL);
41262306a36Sopenharmony_ci	if (rc)
41362306a36Sopenharmony_ci		goto out_sysfs;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	return 0;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ciout_sysfs:
41862306a36Sopenharmony_ci	iommu_device_sysfs_remove(&zdev->iommu_dev);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ciout_err:
42162306a36Sopenharmony_ci	return rc;
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_civoid zpci_destroy_iommu(struct zpci_dev *zdev)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	iommu_device_unregister(&zdev->iommu_dev);
42762306a36Sopenharmony_ci	iommu_device_sysfs_remove(&zdev->iommu_dev);
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_cistatic const struct iommu_ops s390_iommu_ops = {
43162306a36Sopenharmony_ci	.capable = s390_iommu_capable,
43262306a36Sopenharmony_ci	.domain_alloc = s390_domain_alloc,
43362306a36Sopenharmony_ci	.probe_device = s390_iommu_probe_device,
43462306a36Sopenharmony_ci	.release_device = s390_iommu_release_device,
43562306a36Sopenharmony_ci	.device_group = generic_device_group,
43662306a36Sopenharmony_ci	.set_platform_dma_ops = s390_iommu_set_platform_dma,
43762306a36Sopenharmony_ci	.pgsize_bitmap = SZ_4K,
43862306a36Sopenharmony_ci	.get_resv_regions = s390_iommu_get_resv_regions,
43962306a36Sopenharmony_ci	.default_domain_ops = &(const struct iommu_domain_ops) {
44062306a36Sopenharmony_ci		.attach_dev	= s390_iommu_attach_device,
44162306a36Sopenharmony_ci		.map_pages	= s390_iommu_map_pages,
44262306a36Sopenharmony_ci		.unmap_pages	= s390_iommu_unmap_pages,
44362306a36Sopenharmony_ci		.flush_iotlb_all = s390_iommu_flush_iotlb_all,
44462306a36Sopenharmony_ci		.iotlb_sync      = s390_iommu_iotlb_sync,
44562306a36Sopenharmony_ci		.iotlb_sync_map  = s390_iommu_iotlb_sync_map,
44662306a36Sopenharmony_ci		.iova_to_phys	= s390_iommu_iova_to_phys,
44762306a36Sopenharmony_ci		.free		= s390_domain_free,
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci};
450