162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2013 Freescale Semiconductor, Inc.
562306a36Sopenharmony_ci * Author: Varun Sethi <varun.sethi@freescale.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define pr_fmt(fmt)    "fsl-pamu-domain: %s: " fmt, __func__
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "fsl_pamu_domain.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/platform_device.h>
1362306a36Sopenharmony_ci#include <sysdev/fsl_pci.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/*
1662306a36Sopenharmony_ci * Global spinlock that needs to be held while
1762306a36Sopenharmony_ci * configuring PAMU.
1862306a36Sopenharmony_ci */
1962306a36Sopenharmony_cistatic DEFINE_SPINLOCK(iommu_lock);
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic struct kmem_cache *fsl_pamu_domain_cache;
2262306a36Sopenharmony_cistatic struct kmem_cache *iommu_devinfo_cache;
2362306a36Sopenharmony_cistatic DEFINE_SPINLOCK(device_domain_lock);
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistruct iommu_device pamu_iommu;	/* IOMMU core code handle */
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic struct fsl_dma_domain *to_fsl_dma_domain(struct iommu_domain *dom)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	return container_of(dom, struct fsl_dma_domain, iommu_domain);
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic int __init iommu_init_mempool(void)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	fsl_pamu_domain_cache = kmem_cache_create("fsl_pamu_domain",
3562306a36Sopenharmony_ci						  sizeof(struct fsl_dma_domain),
3662306a36Sopenharmony_ci						  0,
3762306a36Sopenharmony_ci						  SLAB_HWCACHE_ALIGN,
3862306a36Sopenharmony_ci						  NULL);
3962306a36Sopenharmony_ci	if (!fsl_pamu_domain_cache) {
4062306a36Sopenharmony_ci		pr_debug("Couldn't create fsl iommu_domain cache\n");
4162306a36Sopenharmony_ci		return -ENOMEM;
4262306a36Sopenharmony_ci	}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	iommu_devinfo_cache = kmem_cache_create("iommu_devinfo",
4562306a36Sopenharmony_ci						sizeof(struct device_domain_info),
4662306a36Sopenharmony_ci						0,
4762306a36Sopenharmony_ci						SLAB_HWCACHE_ALIGN,
4862306a36Sopenharmony_ci						NULL);
4962306a36Sopenharmony_ci	if (!iommu_devinfo_cache) {
5062306a36Sopenharmony_ci		pr_debug("Couldn't create devinfo cache\n");
5162306a36Sopenharmony_ci		kmem_cache_destroy(fsl_pamu_domain_cache);
5262306a36Sopenharmony_ci		return -ENOMEM;
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	return 0;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic int update_liodn_stash(int liodn, struct fsl_dma_domain *dma_domain,
5962306a36Sopenharmony_ci			      u32 val)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	int ret = 0;
6262306a36Sopenharmony_ci	unsigned long flags;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	spin_lock_irqsave(&iommu_lock, flags);
6562306a36Sopenharmony_ci	ret = pamu_update_paace_stash(liodn, val);
6662306a36Sopenharmony_ci	if (ret) {
6762306a36Sopenharmony_ci		pr_debug("Failed to update SPAACE for liodn %d\n ", liodn);
6862306a36Sopenharmony_ci		spin_unlock_irqrestore(&iommu_lock, flags);
6962306a36Sopenharmony_ci		return ret;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	spin_unlock_irqrestore(&iommu_lock, flags);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	return ret;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/* Set the geometry parameters for a LIODN */
7862306a36Sopenharmony_cistatic int pamu_set_liodn(struct fsl_dma_domain *dma_domain, struct device *dev,
7962306a36Sopenharmony_ci			  int liodn)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	u32 omi_index = ~(u32)0;
8262306a36Sopenharmony_ci	unsigned long flags;
8362306a36Sopenharmony_ci	int ret;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	/*
8662306a36Sopenharmony_ci	 * Configure the omi_index at the geometry setup time.
8762306a36Sopenharmony_ci	 * This is a static value which depends on the type of
8862306a36Sopenharmony_ci	 * device and would not change thereafter.
8962306a36Sopenharmony_ci	 */
9062306a36Sopenharmony_ci	get_ome_index(&omi_index, dev);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	spin_lock_irqsave(&iommu_lock, flags);
9362306a36Sopenharmony_ci	ret = pamu_disable_liodn(liodn);
9462306a36Sopenharmony_ci	if (ret)
9562306a36Sopenharmony_ci		goto out_unlock;
9662306a36Sopenharmony_ci	ret = pamu_config_ppaace(liodn, omi_index, dma_domain->stash_id, 0);
9762306a36Sopenharmony_ci	if (ret)
9862306a36Sopenharmony_ci		goto out_unlock;
9962306a36Sopenharmony_ci	ret = pamu_config_ppaace(liodn, ~(u32)0, dma_domain->stash_id,
10062306a36Sopenharmony_ci				 PAACE_AP_PERMS_QUERY | PAACE_AP_PERMS_UPDATE);
10162306a36Sopenharmony_ciout_unlock:
10262306a36Sopenharmony_ci	spin_unlock_irqrestore(&iommu_lock, flags);
10362306a36Sopenharmony_ci	if (ret) {
10462306a36Sopenharmony_ci		pr_debug("PAACE configuration failed for liodn %d\n",
10562306a36Sopenharmony_ci			 liodn);
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci	return ret;
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic void remove_device_ref(struct device_domain_info *info)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	unsigned long flags;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	list_del(&info->link);
11562306a36Sopenharmony_ci	spin_lock_irqsave(&iommu_lock, flags);
11662306a36Sopenharmony_ci	pamu_disable_liodn(info->liodn);
11762306a36Sopenharmony_ci	spin_unlock_irqrestore(&iommu_lock, flags);
11862306a36Sopenharmony_ci	spin_lock_irqsave(&device_domain_lock, flags);
11962306a36Sopenharmony_ci	dev_iommu_priv_set(info->dev, NULL);
12062306a36Sopenharmony_ci	kmem_cache_free(iommu_devinfo_cache, info);
12162306a36Sopenharmony_ci	spin_unlock_irqrestore(&device_domain_lock, flags);
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic void detach_device(struct device *dev, struct fsl_dma_domain *dma_domain)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct device_domain_info *info, *tmp;
12762306a36Sopenharmony_ci	unsigned long flags;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	spin_lock_irqsave(&dma_domain->domain_lock, flags);
13062306a36Sopenharmony_ci	/* Remove the device from the domain device list */
13162306a36Sopenharmony_ci	list_for_each_entry_safe(info, tmp, &dma_domain->devices, link) {
13262306a36Sopenharmony_ci		if (!dev || (info->dev == dev))
13362306a36Sopenharmony_ci			remove_device_ref(info);
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci	spin_unlock_irqrestore(&dma_domain->domain_lock, flags);
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic void attach_device(struct fsl_dma_domain *dma_domain, int liodn, struct device *dev)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	struct device_domain_info *info, *old_domain_info;
14162306a36Sopenharmony_ci	unsigned long flags;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	spin_lock_irqsave(&device_domain_lock, flags);
14462306a36Sopenharmony_ci	/*
14562306a36Sopenharmony_ci	 * Check here if the device is already attached to domain or not.
14662306a36Sopenharmony_ci	 * If the device is already attached to a domain detach it.
14762306a36Sopenharmony_ci	 */
14862306a36Sopenharmony_ci	old_domain_info = dev_iommu_priv_get(dev);
14962306a36Sopenharmony_ci	if (old_domain_info && old_domain_info->domain != dma_domain) {
15062306a36Sopenharmony_ci		spin_unlock_irqrestore(&device_domain_lock, flags);
15162306a36Sopenharmony_ci		detach_device(dev, old_domain_info->domain);
15262306a36Sopenharmony_ci		spin_lock_irqsave(&device_domain_lock, flags);
15362306a36Sopenharmony_ci	}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	info = kmem_cache_zalloc(iommu_devinfo_cache, GFP_ATOMIC);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	info->dev = dev;
15862306a36Sopenharmony_ci	info->liodn = liodn;
15962306a36Sopenharmony_ci	info->domain = dma_domain;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	list_add(&info->link, &dma_domain->devices);
16262306a36Sopenharmony_ci	/*
16362306a36Sopenharmony_ci	 * In case of devices with multiple LIODNs just store
16462306a36Sopenharmony_ci	 * the info for the first LIODN as all
16562306a36Sopenharmony_ci	 * LIODNs share the same domain
16662306a36Sopenharmony_ci	 */
16762306a36Sopenharmony_ci	if (!dev_iommu_priv_get(dev))
16862306a36Sopenharmony_ci		dev_iommu_priv_set(dev, info);
16962306a36Sopenharmony_ci	spin_unlock_irqrestore(&device_domain_lock, flags);
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic phys_addr_t fsl_pamu_iova_to_phys(struct iommu_domain *domain,
17362306a36Sopenharmony_ci					 dma_addr_t iova)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	if (iova < domain->geometry.aperture_start ||
17662306a36Sopenharmony_ci	    iova > domain->geometry.aperture_end)
17762306a36Sopenharmony_ci		return 0;
17862306a36Sopenharmony_ci	return iova;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic bool fsl_pamu_capable(struct device *dev, enum iommu_cap cap)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	return cap == IOMMU_CAP_CACHE_COHERENCY;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic void fsl_pamu_domain_free(struct iommu_domain *domain)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	struct fsl_dma_domain *dma_domain = to_fsl_dma_domain(domain);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	/* remove all the devices from the device list */
19162306a36Sopenharmony_ci	detach_device(NULL, dma_domain);
19262306a36Sopenharmony_ci	kmem_cache_free(fsl_pamu_domain_cache, dma_domain);
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic struct iommu_domain *fsl_pamu_domain_alloc(unsigned type)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	struct fsl_dma_domain *dma_domain;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (type != IOMMU_DOMAIN_UNMANAGED)
20062306a36Sopenharmony_ci		return NULL;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	dma_domain = kmem_cache_zalloc(fsl_pamu_domain_cache, GFP_KERNEL);
20362306a36Sopenharmony_ci	if (!dma_domain)
20462306a36Sopenharmony_ci		return NULL;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	dma_domain->stash_id = ~(u32)0;
20762306a36Sopenharmony_ci	INIT_LIST_HEAD(&dma_domain->devices);
20862306a36Sopenharmony_ci	spin_lock_init(&dma_domain->domain_lock);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	/* default geometry 64 GB i.e. maximum system address */
21162306a36Sopenharmony_ci	dma_domain->iommu_domain. geometry.aperture_start = 0;
21262306a36Sopenharmony_ci	dma_domain->iommu_domain.geometry.aperture_end = (1ULL << 36) - 1;
21362306a36Sopenharmony_ci	dma_domain->iommu_domain.geometry.force_aperture = true;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	return &dma_domain->iommu_domain;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci/* Update stash destination for all LIODNs associated with the domain */
21962306a36Sopenharmony_cistatic int update_domain_stash(struct fsl_dma_domain *dma_domain, u32 val)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	struct device_domain_info *info;
22262306a36Sopenharmony_ci	int ret = 0;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	list_for_each_entry(info, &dma_domain->devices, link) {
22562306a36Sopenharmony_ci		ret = update_liodn_stash(info->liodn, dma_domain, val);
22662306a36Sopenharmony_ci		if (ret)
22762306a36Sopenharmony_ci			break;
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	return ret;
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic int fsl_pamu_attach_device(struct iommu_domain *domain,
23462306a36Sopenharmony_ci				  struct device *dev)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	struct fsl_dma_domain *dma_domain = to_fsl_dma_domain(domain);
23762306a36Sopenharmony_ci	unsigned long flags;
23862306a36Sopenharmony_ci	int len, ret = 0, i;
23962306a36Sopenharmony_ci	const u32 *liodn;
24062306a36Sopenharmony_ci	struct pci_dev *pdev = NULL;
24162306a36Sopenharmony_ci	struct pci_controller *pci_ctl;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	/*
24462306a36Sopenharmony_ci	 * Use LIODN of the PCI controller while attaching a
24562306a36Sopenharmony_ci	 * PCI device.
24662306a36Sopenharmony_ci	 */
24762306a36Sopenharmony_ci	if (dev_is_pci(dev)) {
24862306a36Sopenharmony_ci		pdev = to_pci_dev(dev);
24962306a36Sopenharmony_ci		pci_ctl = pci_bus_to_host(pdev->bus);
25062306a36Sopenharmony_ci		/*
25162306a36Sopenharmony_ci		 * make dev point to pci controller device
25262306a36Sopenharmony_ci		 * so we can get the LIODN programmed by
25362306a36Sopenharmony_ci		 * u-boot.
25462306a36Sopenharmony_ci		 */
25562306a36Sopenharmony_ci		dev = pci_ctl->parent;
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	liodn = of_get_property(dev->of_node, "fsl,liodn", &len);
25962306a36Sopenharmony_ci	if (!liodn) {
26062306a36Sopenharmony_ci		pr_debug("missing fsl,liodn property at %pOF\n", dev->of_node);
26162306a36Sopenharmony_ci		return -ENODEV;
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	spin_lock_irqsave(&dma_domain->domain_lock, flags);
26562306a36Sopenharmony_ci	for (i = 0; i < len / sizeof(u32); i++) {
26662306a36Sopenharmony_ci		/* Ensure that LIODN value is valid */
26762306a36Sopenharmony_ci		if (liodn[i] >= PAACE_NUMBER_ENTRIES) {
26862306a36Sopenharmony_ci			pr_debug("Invalid liodn %d, attach device failed for %pOF\n",
26962306a36Sopenharmony_ci				 liodn[i], dev->of_node);
27062306a36Sopenharmony_ci			ret = -ENODEV;
27162306a36Sopenharmony_ci			break;
27262306a36Sopenharmony_ci		}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci		attach_device(dma_domain, liodn[i], dev);
27562306a36Sopenharmony_ci		ret = pamu_set_liodn(dma_domain, dev, liodn[i]);
27662306a36Sopenharmony_ci		if (ret)
27762306a36Sopenharmony_ci			break;
27862306a36Sopenharmony_ci		ret = pamu_enable_liodn(liodn[i]);
27962306a36Sopenharmony_ci		if (ret)
28062306a36Sopenharmony_ci			break;
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci	spin_unlock_irqrestore(&dma_domain->domain_lock, flags);
28362306a36Sopenharmony_ci	return ret;
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic void fsl_pamu_set_platform_dma(struct device *dev)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
28962306a36Sopenharmony_ci	struct fsl_dma_domain *dma_domain = to_fsl_dma_domain(domain);
29062306a36Sopenharmony_ci	const u32 *prop;
29162306a36Sopenharmony_ci	int len;
29262306a36Sopenharmony_ci	struct pci_dev *pdev = NULL;
29362306a36Sopenharmony_ci	struct pci_controller *pci_ctl;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	/*
29662306a36Sopenharmony_ci	 * Use LIODN of the PCI controller while detaching a
29762306a36Sopenharmony_ci	 * PCI device.
29862306a36Sopenharmony_ci	 */
29962306a36Sopenharmony_ci	if (dev_is_pci(dev)) {
30062306a36Sopenharmony_ci		pdev = to_pci_dev(dev);
30162306a36Sopenharmony_ci		pci_ctl = pci_bus_to_host(pdev->bus);
30262306a36Sopenharmony_ci		/*
30362306a36Sopenharmony_ci		 * make dev point to pci controller device
30462306a36Sopenharmony_ci		 * so we can get the LIODN programmed by
30562306a36Sopenharmony_ci		 * u-boot.
30662306a36Sopenharmony_ci		 */
30762306a36Sopenharmony_ci		dev = pci_ctl->parent;
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	prop = of_get_property(dev->of_node, "fsl,liodn", &len);
31162306a36Sopenharmony_ci	if (prop)
31262306a36Sopenharmony_ci		detach_device(dev, dma_domain);
31362306a36Sopenharmony_ci	else
31462306a36Sopenharmony_ci		pr_debug("missing fsl,liodn property at %pOF\n", dev->of_node);
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci/* Set the domain stash attribute */
31862306a36Sopenharmony_ciint fsl_pamu_configure_l1_stash(struct iommu_domain *domain, u32 cpu)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	struct fsl_dma_domain *dma_domain = to_fsl_dma_domain(domain);
32162306a36Sopenharmony_ci	unsigned long flags;
32262306a36Sopenharmony_ci	int ret;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	spin_lock_irqsave(&dma_domain->domain_lock, flags);
32562306a36Sopenharmony_ci	dma_domain->stash_id = get_stash_id(PAMU_ATTR_CACHE_L1, cpu);
32662306a36Sopenharmony_ci	if (dma_domain->stash_id == ~(u32)0) {
32762306a36Sopenharmony_ci		pr_debug("Invalid stash attributes\n");
32862306a36Sopenharmony_ci		spin_unlock_irqrestore(&dma_domain->domain_lock, flags);
32962306a36Sopenharmony_ci		return -EINVAL;
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci	ret = update_domain_stash(dma_domain, dma_domain->stash_id);
33262306a36Sopenharmony_ci	spin_unlock_irqrestore(&dma_domain->domain_lock, flags);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	return ret;
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic  bool check_pci_ctl_endpt_part(struct pci_controller *pci_ctl)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	u32 version;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	/* Check the PCI controller version number by readding BRR1 register */
34262306a36Sopenharmony_ci	version = in_be32(pci_ctl->cfg_addr + (PCI_FSL_BRR1 >> 2));
34362306a36Sopenharmony_ci	version &= PCI_FSL_BRR1_VER;
34462306a36Sopenharmony_ci	/* If PCI controller version is >= 0x204 we can partition endpoints */
34562306a36Sopenharmony_ci	return version >= 0x204;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic struct iommu_group *fsl_pamu_device_group(struct device *dev)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	struct iommu_group *group;
35162306a36Sopenharmony_ci	struct pci_dev *pdev;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	/*
35462306a36Sopenharmony_ci	 * For platform devices we allocate a separate group for each of the
35562306a36Sopenharmony_ci	 * devices.
35662306a36Sopenharmony_ci	 */
35762306a36Sopenharmony_ci	if (!dev_is_pci(dev))
35862306a36Sopenharmony_ci		return generic_device_group(dev);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	/*
36162306a36Sopenharmony_ci	 * We can partition PCIe devices so assign device group to the device
36262306a36Sopenharmony_ci	 */
36362306a36Sopenharmony_ci	pdev = to_pci_dev(dev);
36462306a36Sopenharmony_ci	if (check_pci_ctl_endpt_part(pci_bus_to_host(pdev->bus)))
36562306a36Sopenharmony_ci		return pci_device_group(&pdev->dev);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	/*
36862306a36Sopenharmony_ci	 * All devices connected to the controller will share the same device
36962306a36Sopenharmony_ci	 * group.
37062306a36Sopenharmony_ci	 *
37162306a36Sopenharmony_ci	 * Due to ordering between fsl_pamu_init() and fsl_pci_init() it is
37262306a36Sopenharmony_ci	 * guaranteed that the pci_ctl->parent platform_device will have the
37362306a36Sopenharmony_ci	 * iommu driver bound and will already have a group set. So we just
37462306a36Sopenharmony_ci	 * re-use this group as the group for every device in the hose.
37562306a36Sopenharmony_ci	 */
37662306a36Sopenharmony_ci	group = iommu_group_get(pci_bus_to_host(pdev->bus)->parent);
37762306a36Sopenharmony_ci	if (WARN_ON(!group))
37862306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
37962306a36Sopenharmony_ci	return group;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic struct iommu_device *fsl_pamu_probe_device(struct device *dev)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	int len;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	/*
38762306a36Sopenharmony_ci	 * uboot must fill the fsl,liodn for platform devices to be supported by
38862306a36Sopenharmony_ci	 * the iommu.
38962306a36Sopenharmony_ci	 */
39062306a36Sopenharmony_ci	if (!dev_is_pci(dev) &&
39162306a36Sopenharmony_ci	    !of_get_property(dev->of_node, "fsl,liodn", &len))
39262306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	return &pamu_iommu;
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic const struct iommu_ops fsl_pamu_ops = {
39862306a36Sopenharmony_ci	.capable	= fsl_pamu_capable,
39962306a36Sopenharmony_ci	.domain_alloc	= fsl_pamu_domain_alloc,
40062306a36Sopenharmony_ci	.probe_device	= fsl_pamu_probe_device,
40162306a36Sopenharmony_ci	.device_group   = fsl_pamu_device_group,
40262306a36Sopenharmony_ci	.set_platform_dma_ops = fsl_pamu_set_platform_dma,
40362306a36Sopenharmony_ci	.default_domain_ops = &(const struct iommu_domain_ops) {
40462306a36Sopenharmony_ci		.attach_dev	= fsl_pamu_attach_device,
40562306a36Sopenharmony_ci		.iova_to_phys	= fsl_pamu_iova_to_phys,
40662306a36Sopenharmony_ci		.free		= fsl_pamu_domain_free,
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci};
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ciint __init pamu_domain_init(void)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	int ret = 0;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	ret = iommu_init_mempool();
41562306a36Sopenharmony_ci	if (ret)
41662306a36Sopenharmony_ci		return ret;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	ret = iommu_device_sysfs_add(&pamu_iommu, NULL, NULL, "iommu0");
41962306a36Sopenharmony_ci	if (ret)
42062306a36Sopenharmony_ci		return ret;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	ret = iommu_device_register(&pamu_iommu, &fsl_pamu_ops, NULL);
42362306a36Sopenharmony_ci	if (ret) {
42462306a36Sopenharmony_ci		iommu_device_sysfs_remove(&pamu_iommu);
42562306a36Sopenharmony_ci		pr_err("Can't register iommu device\n");
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	return ret;
42962306a36Sopenharmony_ci}
430