226 lines
5.4 KiB
C
226 lines
5.4 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2025, Advanced Micro Devices, Inc.
|
|
*/
|
|
|
|
#include <drm/amdxdna_accel.h>
|
|
#include <drm/drm_managed.h>
|
|
#include <linux/iommu.h>
|
|
#include <linux/iova.h>
|
|
|
|
#include "amdxdna_gem.h"
|
|
#include "amdxdna_pci_drv.h"
|
|
|
|
static bool force_iova;
|
|
module_param(force_iova, bool, 0600);
|
|
MODULE_PARM_DESC(force_iova, "Force use IOVA (Default false)");
|
|
|
|
static struct iova *amdxdna_iommu_alloc_iova(struct amdxdna_dev *xdna,
|
|
size_t size,
|
|
dma_addr_t *dma_addr,
|
|
bool size_aligned)
|
|
{
|
|
unsigned long shift, end;
|
|
struct iova *iova;
|
|
|
|
end = xdna->domain->geometry.aperture_end;
|
|
shift = iova_shift(&xdna->iovad);
|
|
size = iova_align(&xdna->iovad, size);
|
|
|
|
iova = alloc_iova(&xdna->iovad, size >> shift, end >> shift, size_aligned);
|
|
if (!iova)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
*dma_addr = iova_dma_addr(&xdna->iovad, iova);
|
|
|
|
return iova;
|
|
}
|
|
|
|
int amdxdna_dma_map_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo)
|
|
{
|
|
unsigned long contig_sz;
|
|
struct sg_table *sgt;
|
|
dma_addr_t dma_addr;
|
|
struct iova *iova;
|
|
ssize_t size;
|
|
|
|
if (abo->type != AMDXDNA_BO_DEV_HEAP && abo->type != AMDXDNA_BO_SHARE)
|
|
return 0;
|
|
|
|
sgt = drm_gem_shmem_get_pages_sgt(&abo->base);
|
|
if (IS_ERR(sgt)) {
|
|
XDNA_ERR(xdna, "Get sgt failed, ret %ld", PTR_ERR(sgt));
|
|
return PTR_ERR(sgt);
|
|
}
|
|
|
|
if (!sgt->orig_nents) {
|
|
XDNA_ERR(xdna, "sgl is zero length");
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
if (amdxdna_iova_on(xdna)) {
|
|
if (!sg_page(sgt->sgl)) {
|
|
XDNA_ERR(xdna, "sgl is not page backed");
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
iova = amdxdna_iommu_alloc_iova(xdna, abo->mem.size, &dma_addr,
|
|
(abo->type == AMDXDNA_BO_DEV_HEAP));
|
|
if (IS_ERR(iova)) {
|
|
XDNA_ERR(xdna, "Alloc iova failed, ret %ld", PTR_ERR(iova));
|
|
return PTR_ERR(iova);
|
|
}
|
|
|
|
size = iommu_map_sgtable(xdna->domain, dma_addr, sgt,
|
|
IOMMU_READ | IOMMU_WRITE);
|
|
if (size < 0) {
|
|
XDNA_ERR(xdna, "iommu_map_sgtable failed: %zd", size);
|
|
__free_iova(&xdna->iovad, iova);
|
|
return size;
|
|
}
|
|
if (size < abo->mem.size) {
|
|
iommu_unmap(xdna->domain, dma_addr, size);
|
|
__free_iova(&xdna->iovad, iova);
|
|
return -ENXIO;
|
|
}
|
|
abo->mem.dma_addr = dma_addr;
|
|
} else {
|
|
/* Device doesn't support scatter/gather list, fail non-contiguous mapping. */
|
|
contig_sz = drm_prime_get_contiguous_size(sgt);
|
|
if (contig_sz < abo->mem.size) {
|
|
XDNA_ERR(xdna,
|
|
"noncontiguous dma addr, contig size:%ld, expected size:%ld",
|
|
contig_sz, abo->mem.size);
|
|
return -EINVAL;
|
|
}
|
|
abo->mem.dma_addr = sg_dma_address(sgt->sgl);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void amdxdna_dma_unmap_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo)
|
|
{
|
|
size_t size;
|
|
|
|
if (abo->mem.dma_addr == AMDXDNA_INVALID_ADDR)
|
|
return;
|
|
|
|
if (amdxdna_iova_on(xdna)) {
|
|
size = iova_align(&xdna->iovad, abo->mem.size);
|
|
iommu_unmap(xdna->domain, abo->mem.dma_addr, size);
|
|
free_iova(&xdna->iovad, iova_pfn(&xdna->iovad, abo->mem.dma_addr));
|
|
}
|
|
abo->mem.dma_addr = AMDXDNA_INVALID_ADDR;
|
|
}
|
|
|
|
void *amdxdna_iommu_alloc(struct amdxdna_dev *xdna, size_t size, dma_addr_t *dma_addr)
|
|
{
|
|
struct iova *iova;
|
|
void *cpu_addr;
|
|
int ret;
|
|
|
|
iova = amdxdna_iommu_alloc_iova(xdna, size, dma_addr, true);
|
|
if (IS_ERR(iova)) {
|
|
XDNA_ERR(xdna, "Alloc iova failed, ret %ld", PTR_ERR(iova));
|
|
return iova;
|
|
}
|
|
|
|
cpu_addr = (void *)__get_free_pages(GFP_KERNEL, get_order(size));
|
|
if (!cpu_addr) {
|
|
ret = -ENOMEM;
|
|
goto free_iova;
|
|
}
|
|
|
|
ret = iommu_map(xdna->domain, *dma_addr, virt_to_phys(cpu_addr),
|
|
iova_align(&xdna->iovad, size),
|
|
IOMMU_READ | IOMMU_WRITE, GFP_KERNEL);
|
|
if (ret)
|
|
goto free_cpu_addr;
|
|
|
|
return cpu_addr;
|
|
|
|
free_cpu_addr:
|
|
free_pages((unsigned long)cpu_addr, get_order(size));
|
|
free_iova:
|
|
__free_iova(&xdna->iovad, iova);
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
void amdxdna_iommu_free(struct amdxdna_dev *xdna, size_t size,
|
|
void *cpu_addr, dma_addr_t dma_addr)
|
|
{
|
|
iommu_unmap(xdna->domain, dma_addr, iova_align(&xdna->iovad, size));
|
|
free_iova(&xdna->iovad, iova_pfn(&xdna->iovad, dma_addr));
|
|
free_pages((unsigned long)cpu_addr, get_order(size));
|
|
}
|
|
|
|
static void amdxdna_cleanup_force_iova(struct drm_device *dev, void *res)
|
|
{
|
|
struct amdxdna_dev *xdna = to_xdna_dev(dev);
|
|
|
|
if (xdna->domain) {
|
|
iommu_detach_group(xdna->domain, xdna->group);
|
|
put_iova_domain(&xdna->iovad);
|
|
iova_cache_put();
|
|
iommu_domain_free(xdna->domain);
|
|
}
|
|
|
|
iommu_group_put(xdna->group);
|
|
}
|
|
|
|
void amdxdna_iommu_fini(struct amdxdna_dev *xdna)
|
|
{
|
|
if (xdna->group && !xdna->domain)
|
|
iommu_group_put(xdna->group);
|
|
}
|
|
|
|
int amdxdna_iommu_init(struct amdxdna_dev *xdna)
|
|
{
|
|
unsigned long order;
|
|
int ret = 0;
|
|
|
|
xdna->group = iommu_group_get(xdna->ddev.dev);
|
|
if (!xdna->group || !force_iova)
|
|
return 0;
|
|
|
|
XDNA_WARN(xdna, "Enabled force_iova mode.");
|
|
xdna->domain = iommu_paging_domain_alloc_flags(xdna->ddev.dev,
|
|
IOMMU_HWPT_ALLOC_PASID);
|
|
if (IS_ERR(xdna->domain)) {
|
|
XDNA_ERR(xdna, "Failed to alloc iommu domain");
|
|
ret = PTR_ERR(xdna->domain);
|
|
goto put_group;
|
|
}
|
|
|
|
ret = iova_cache_get();
|
|
if (ret)
|
|
goto free_domain;
|
|
|
|
order = __ffs(xdna->domain->pgsize_bitmap);
|
|
init_iova_domain(&xdna->iovad, 1UL << order, 0);
|
|
|
|
ret = iommu_attach_group(xdna->domain, xdna->group);
|
|
if (ret)
|
|
goto put_iova;
|
|
|
|
ret = drmm_add_action(&xdna->ddev, amdxdna_cleanup_force_iova, NULL);
|
|
if (ret)
|
|
goto detach_group;
|
|
|
|
return 0;
|
|
|
|
detach_group:
|
|
iommu_detach_group(xdna->domain, xdna->group);
|
|
put_iova:
|
|
put_iova_domain(&xdna->iovad);
|
|
iova_cache_put();
|
|
free_domain:
|
|
iommu_domain_free(xdna->domain);
|
|
put_group:
|
|
iommu_group_put(xdna->group);
|
|
xdna->group = NULL;
|
|
xdna->domain = NULL;
|
|
|
|
return ret;
|
|
}
|