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