1*ece3e898SLizhi Hou // SPDX-License-Identifier: GPL-2.0 2*ece3e898SLizhi Hou /* 3*ece3e898SLizhi Hou * Copyright (C) 2025, Advanced Micro Devices, Inc. 4*ece3e898SLizhi Hou */ 5*ece3e898SLizhi Hou 6*ece3e898SLizhi Hou #include <drm/amdxdna_accel.h> 7*ece3e898SLizhi Hou #include <linux/iommu.h> 8*ece3e898SLizhi Hou #include <linux/iova.h> 9*ece3e898SLizhi Hou 10*ece3e898SLizhi Hou #include "amdxdna_gem.h" 11*ece3e898SLizhi Hou #include "amdxdna_pci_drv.h" 12*ece3e898SLizhi Hou 13*ece3e898SLizhi Hou static bool force_iova; 14*ece3e898SLizhi Hou module_param(force_iova, bool, 0600); 15*ece3e898SLizhi Hou MODULE_PARM_DESC(force_iova, "Force use IOVA (Default false)"); 16*ece3e898SLizhi Hou 17*ece3e898SLizhi Hou static struct iova *amdxdna_iommu_alloc_iova(struct amdxdna_dev *xdna, 18*ece3e898SLizhi Hou size_t size, 19*ece3e898SLizhi Hou dma_addr_t *dma_addr, 20*ece3e898SLizhi Hou bool size_aligned) 21*ece3e898SLizhi Hou { 22*ece3e898SLizhi Hou unsigned long shift, end; 23*ece3e898SLizhi Hou struct iova *iova; 24*ece3e898SLizhi Hou 25*ece3e898SLizhi Hou end = xdna->domain->geometry.aperture_end; 26*ece3e898SLizhi Hou shift = iova_shift(&xdna->iovad); 27*ece3e898SLizhi Hou size = iova_align(&xdna->iovad, size); 28*ece3e898SLizhi Hou 29*ece3e898SLizhi Hou iova = alloc_iova(&xdna->iovad, size >> shift, end >> shift, size_aligned); 30*ece3e898SLizhi Hou if (!iova) 31*ece3e898SLizhi Hou return ERR_PTR(-ENOMEM); 32*ece3e898SLizhi Hou 33*ece3e898SLizhi Hou *dma_addr = iova_dma_addr(&xdna->iovad, iova); 34*ece3e898SLizhi Hou 35*ece3e898SLizhi Hou return iova; 36*ece3e898SLizhi Hou } 37*ece3e898SLizhi Hou 38*ece3e898SLizhi Hou int amdxdna_iommu_map_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo) 39*ece3e898SLizhi Hou { 40*ece3e898SLizhi Hou struct sg_table *sgt; 41*ece3e898SLizhi Hou dma_addr_t dma_addr; 42*ece3e898SLizhi Hou struct iova *iova; 43*ece3e898SLizhi Hou size_t size; 44*ece3e898SLizhi Hou 45*ece3e898SLizhi Hou if (abo->type != AMDXDNA_BO_DEV_HEAP && abo->type != AMDXDNA_BO_SHMEM) 46*ece3e898SLizhi Hou return 0; 47*ece3e898SLizhi Hou 48*ece3e898SLizhi Hou sgt = drm_gem_shmem_get_pages_sgt(&abo->base); 49*ece3e898SLizhi Hou if (IS_ERR(sgt)) { 50*ece3e898SLizhi Hou XDNA_ERR(xdna, "Get sgt failed, ret %ld", PTR_ERR(sgt)); 51*ece3e898SLizhi Hou return PTR_ERR(sgt); 52*ece3e898SLizhi Hou } 53*ece3e898SLizhi Hou 54*ece3e898SLizhi Hou if (!sgt->orig_nents || !sg_page(sgt->sgl)) { 55*ece3e898SLizhi Hou XDNA_ERR(xdna, "sgl is zero length or not page backed"); 56*ece3e898SLizhi Hou return -EOPNOTSUPP; 57*ece3e898SLizhi Hou } 58*ece3e898SLizhi Hou 59*ece3e898SLizhi Hou iova = amdxdna_iommu_alloc_iova(xdna, abo->mem.size, &dma_addr, 60*ece3e898SLizhi Hou (abo->type == AMDXDNA_BO_DEV_HEAP)); 61*ece3e898SLizhi Hou if (IS_ERR(iova)) { 62*ece3e898SLizhi Hou XDNA_ERR(xdna, "Alloc iova failed, ret %ld", PTR_ERR(iova)); 63*ece3e898SLizhi Hou return PTR_ERR(iova); 64*ece3e898SLizhi Hou } 65*ece3e898SLizhi Hou 66*ece3e898SLizhi Hou size = iommu_map_sgtable(xdna->domain, dma_addr, sgt, 67*ece3e898SLizhi Hou IOMMU_READ | IOMMU_WRITE); 68*ece3e898SLizhi Hou if (size < abo->mem.size) { 69*ece3e898SLizhi Hou __free_iova(&xdna->iovad, iova); 70*ece3e898SLizhi Hou return -ENXIO; 71*ece3e898SLizhi Hou } 72*ece3e898SLizhi Hou 73*ece3e898SLizhi Hou abo->mem.dma_addr = dma_addr; 74*ece3e898SLizhi Hou 75*ece3e898SLizhi Hou return 0; 76*ece3e898SLizhi Hou } 77*ece3e898SLizhi Hou 78*ece3e898SLizhi Hou void amdxdna_iommu_unmap_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo) 79*ece3e898SLizhi Hou { 80*ece3e898SLizhi Hou size_t size; 81*ece3e898SLizhi Hou 82*ece3e898SLizhi Hou if (abo->mem.dma_addr == AMDXDNA_INVALID_ADDR) 83*ece3e898SLizhi Hou return; 84*ece3e898SLizhi Hou 85*ece3e898SLizhi Hou size = iova_align(&xdna->iovad, abo->mem.size); 86*ece3e898SLizhi Hou iommu_unmap(xdna->domain, abo->mem.dma_addr, size); 87*ece3e898SLizhi Hou free_iova(&xdna->iovad, iova_pfn(&xdna->iovad, abo->mem.dma_addr)); 88*ece3e898SLizhi Hou abo->mem.dma_addr = AMDXDNA_INVALID_ADDR; 89*ece3e898SLizhi Hou } 90*ece3e898SLizhi Hou 91*ece3e898SLizhi Hou void *amdxdna_iommu_alloc(struct amdxdna_dev *xdna, size_t size, dma_addr_t *dma_addr) 92*ece3e898SLizhi Hou { 93*ece3e898SLizhi Hou struct iova *iova; 94*ece3e898SLizhi Hou void *cpu_addr; 95*ece3e898SLizhi Hou int ret; 96*ece3e898SLizhi Hou 97*ece3e898SLizhi Hou iova = amdxdna_iommu_alloc_iova(xdna, size, dma_addr, true); 98*ece3e898SLizhi Hou if (IS_ERR(iova)) { 99*ece3e898SLizhi Hou XDNA_ERR(xdna, "Alloc iova failed, ret %ld", PTR_ERR(iova)); 100*ece3e898SLizhi Hou return iova; 101*ece3e898SLizhi Hou } 102*ece3e898SLizhi Hou 103*ece3e898SLizhi Hou cpu_addr = (void *)__get_free_pages(GFP_KERNEL, get_order(size)); 104*ece3e898SLizhi Hou if (!cpu_addr) { 105*ece3e898SLizhi Hou ret = -ENOMEM; 106*ece3e898SLizhi Hou goto free_iova; 107*ece3e898SLizhi Hou } 108*ece3e898SLizhi Hou 109*ece3e898SLizhi Hou ret = iommu_map(xdna->domain, *dma_addr, virt_to_phys(cpu_addr), 110*ece3e898SLizhi Hou iova_align(&xdna->iovad, size), 111*ece3e898SLizhi Hou IOMMU_READ | IOMMU_WRITE, GFP_KERNEL); 112*ece3e898SLizhi Hou if (ret) 113*ece3e898SLizhi Hou goto free_iova; 114*ece3e898SLizhi Hou 115*ece3e898SLizhi Hou return cpu_addr; 116*ece3e898SLizhi Hou 117*ece3e898SLizhi Hou free_iova: 118*ece3e898SLizhi Hou __free_iova(&xdna->iovad, iova); 119*ece3e898SLizhi Hou return ERR_PTR(ret); 120*ece3e898SLizhi Hou } 121*ece3e898SLizhi Hou 122*ece3e898SLizhi Hou void amdxdna_iommu_free(struct amdxdna_dev *xdna, size_t size, 123*ece3e898SLizhi Hou void *cpu_addr, dma_addr_t dma_addr) 124*ece3e898SLizhi Hou { 125*ece3e898SLizhi Hou iommu_unmap(xdna->domain, dma_addr, iova_align(&xdna->iovad, size)); 126*ece3e898SLizhi Hou free_iova(&xdna->iovad, iova_pfn(&xdna->iovad, dma_addr)); 127*ece3e898SLizhi Hou free_pages((unsigned long)cpu_addr, get_order(size)); 128*ece3e898SLizhi Hou } 129*ece3e898SLizhi Hou 130*ece3e898SLizhi Hou int amdxdna_iommu_init(struct amdxdna_dev *xdna) 131*ece3e898SLizhi Hou { 132*ece3e898SLizhi Hou unsigned long order; 133*ece3e898SLizhi Hou int ret; 134*ece3e898SLizhi Hou 135*ece3e898SLizhi Hou xdna->group = iommu_group_get(xdna->ddev.dev); 136*ece3e898SLizhi Hou if (!xdna->group || !force_iova) 137*ece3e898SLizhi Hou return 0; 138*ece3e898SLizhi Hou 139*ece3e898SLizhi Hou XDNA_WARN(xdna, "Enabled force_iova mode."); 140*ece3e898SLizhi Hou xdna->domain = iommu_paging_domain_alloc_flags(xdna->ddev.dev, 141*ece3e898SLizhi Hou IOMMU_HWPT_ALLOC_PASID); 142*ece3e898SLizhi Hou if (IS_ERR(xdna->domain)) { 143*ece3e898SLizhi Hou XDNA_ERR(xdna, "Failed to alloc iommu domain"); 144*ece3e898SLizhi Hou ret = PTR_ERR(xdna->domain); 145*ece3e898SLizhi Hou goto put_group; 146*ece3e898SLizhi Hou } 147*ece3e898SLizhi Hou 148*ece3e898SLizhi Hou ret = iova_cache_get(); 149*ece3e898SLizhi Hou if (ret) 150*ece3e898SLizhi Hou goto free_domain; 151*ece3e898SLizhi Hou 152*ece3e898SLizhi Hou order = __ffs(xdna->domain->pgsize_bitmap); 153*ece3e898SLizhi Hou init_iova_domain(&xdna->iovad, 1UL << order, 0); 154*ece3e898SLizhi Hou 155*ece3e898SLizhi Hou ret = iommu_attach_group(xdna->domain, xdna->group); 156*ece3e898SLizhi Hou if (ret) 157*ece3e898SLizhi Hou goto put_iova; 158*ece3e898SLizhi Hou 159*ece3e898SLizhi Hou return 0; 160*ece3e898SLizhi Hou 161*ece3e898SLizhi Hou put_iova: 162*ece3e898SLizhi Hou put_iova_domain(&xdna->iovad); 163*ece3e898SLizhi Hou iova_cache_put(); 164*ece3e898SLizhi Hou free_domain: 165*ece3e898SLizhi Hou iommu_domain_free(xdna->domain); 166*ece3e898SLizhi Hou put_group: 167*ece3e898SLizhi Hou iommu_group_put(xdna->group); 168*ece3e898SLizhi Hou xdna->domain = NULL; 169*ece3e898SLizhi Hou 170*ece3e898SLizhi Hou return ret; 171*ece3e898SLizhi Hou } 172*ece3e898SLizhi Hou 173*ece3e898SLizhi Hou void amdxdna_iommu_fini(struct amdxdna_dev *xdna) 174*ece3e898SLizhi Hou { 175*ece3e898SLizhi Hou if (xdna->domain) { 176*ece3e898SLizhi Hou iommu_detach_group(xdna->domain, xdna->group); 177*ece3e898SLizhi Hou put_iova_domain(&xdna->iovad); 178*ece3e898SLizhi Hou iova_cache_put(); 179*ece3e898SLizhi Hou iommu_domain_free(xdna->domain); 180*ece3e898SLizhi Hou } 181*ece3e898SLizhi Hou 182*ece3e898SLizhi Hou if (xdna->group) 183*ece3e898SLizhi Hou iommu_group_put(xdna->group); 184*ece3e898SLizhi Hou } 185