18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for FPGA Accelerated Function Unit (AFU) DMA Region Management
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2017-2018 Intel Corporation, Inc.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Authors:
88c2ecf20Sopenharmony_ci *   Wu Hao <hao.wu@intel.com>
98c2ecf20Sopenharmony_ci *   Xiao Guangrong <guangrong.xiao@linux.intel.com>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
138c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
148c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
158c2ecf20Sopenharmony_ci#include <linux/mm.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "dfl-afu.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_civoid afu_dma_region_init(struct dfl_feature_platform_data *pdata)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata);
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci	afu->dma_regions = RB_ROOT;
248c2ecf20Sopenharmony_ci}
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/**
278c2ecf20Sopenharmony_ci * afu_dma_pin_pages - pin pages of given dma memory region
288c2ecf20Sopenharmony_ci * @pdata: feature device platform data
298c2ecf20Sopenharmony_ci * @region: dma memory region to be pinned
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci * Pin all the pages of given dfl_afu_dma_region.
328c2ecf20Sopenharmony_ci * Return 0 for success or negative error code.
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_cistatic int afu_dma_pin_pages(struct dfl_feature_platform_data *pdata,
358c2ecf20Sopenharmony_ci			     struct dfl_afu_dma_region *region)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	int npages = region->length >> PAGE_SHIFT;
388c2ecf20Sopenharmony_ci	struct device *dev = &pdata->dev->dev;
398c2ecf20Sopenharmony_ci	int ret, pinned;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	ret = account_locked_vm(current->mm, npages, true);
428c2ecf20Sopenharmony_ci	if (ret)
438c2ecf20Sopenharmony_ci		return ret;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	region->pages = kcalloc(npages, sizeof(struct page *), GFP_KERNEL);
468c2ecf20Sopenharmony_ci	if (!region->pages) {
478c2ecf20Sopenharmony_ci		ret = -ENOMEM;
488c2ecf20Sopenharmony_ci		goto unlock_vm;
498c2ecf20Sopenharmony_ci	}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	pinned = pin_user_pages_fast(region->user_addr, npages, FOLL_WRITE,
528c2ecf20Sopenharmony_ci				     region->pages);
538c2ecf20Sopenharmony_ci	if (pinned < 0) {
548c2ecf20Sopenharmony_ci		ret = pinned;
558c2ecf20Sopenharmony_ci		goto free_pages;
568c2ecf20Sopenharmony_ci	} else if (pinned != npages) {
578c2ecf20Sopenharmony_ci		ret = -EFAULT;
588c2ecf20Sopenharmony_ci		goto unpin_pages;
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	dev_dbg(dev, "%d pages pinned\n", pinned);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	return 0;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ciunpin_pages:
668c2ecf20Sopenharmony_ci	unpin_user_pages(region->pages, pinned);
678c2ecf20Sopenharmony_cifree_pages:
688c2ecf20Sopenharmony_ci	kfree(region->pages);
698c2ecf20Sopenharmony_ciunlock_vm:
708c2ecf20Sopenharmony_ci	account_locked_vm(current->mm, npages, false);
718c2ecf20Sopenharmony_ci	return ret;
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/**
758c2ecf20Sopenharmony_ci * afu_dma_unpin_pages - unpin pages of given dma memory region
768c2ecf20Sopenharmony_ci * @pdata: feature device platform data
778c2ecf20Sopenharmony_ci * @region: dma memory region to be unpinned
788c2ecf20Sopenharmony_ci *
798c2ecf20Sopenharmony_ci * Unpin all the pages of given dfl_afu_dma_region.
808c2ecf20Sopenharmony_ci * Return 0 for success or negative error code.
818c2ecf20Sopenharmony_ci */
828c2ecf20Sopenharmony_cistatic void afu_dma_unpin_pages(struct dfl_feature_platform_data *pdata,
838c2ecf20Sopenharmony_ci				struct dfl_afu_dma_region *region)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	long npages = region->length >> PAGE_SHIFT;
868c2ecf20Sopenharmony_ci	struct device *dev = &pdata->dev->dev;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	unpin_user_pages(region->pages, npages);
898c2ecf20Sopenharmony_ci	kfree(region->pages);
908c2ecf20Sopenharmony_ci	account_locked_vm(current->mm, npages, false);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	dev_dbg(dev, "%ld pages unpinned\n", npages);
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci/**
968c2ecf20Sopenharmony_ci * afu_dma_check_continuous_pages - check if pages are continuous
978c2ecf20Sopenharmony_ci * @region: dma memory region
988c2ecf20Sopenharmony_ci *
998c2ecf20Sopenharmony_ci * Return true if pages of given dma memory region have continuous physical
1008c2ecf20Sopenharmony_ci * address, otherwise return false.
1018c2ecf20Sopenharmony_ci */
1028c2ecf20Sopenharmony_cistatic bool afu_dma_check_continuous_pages(struct dfl_afu_dma_region *region)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	int npages = region->length >> PAGE_SHIFT;
1058c2ecf20Sopenharmony_ci	int i;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	for (i = 0; i < npages - 1; i++)
1088c2ecf20Sopenharmony_ci		if (page_to_pfn(region->pages[i]) + 1 !=
1098c2ecf20Sopenharmony_ci				page_to_pfn(region->pages[i + 1]))
1108c2ecf20Sopenharmony_ci			return false;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	return true;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/**
1168c2ecf20Sopenharmony_ci * dma_region_check_iova - check if memory area is fully contained in the region
1178c2ecf20Sopenharmony_ci * @region: dma memory region
1188c2ecf20Sopenharmony_ci * @iova: address of the dma memory area
1198c2ecf20Sopenharmony_ci * @size: size of the dma memory area
1208c2ecf20Sopenharmony_ci *
1218c2ecf20Sopenharmony_ci * Compare the dma memory area defined by @iova and @size with given dma region.
1228c2ecf20Sopenharmony_ci * Return true if memory area is fully contained in the region, otherwise false.
1238c2ecf20Sopenharmony_ci */
1248c2ecf20Sopenharmony_cistatic bool dma_region_check_iova(struct dfl_afu_dma_region *region,
1258c2ecf20Sopenharmony_ci				  u64 iova, u64 size)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	if (!size && region->iova != iova)
1288c2ecf20Sopenharmony_ci		return false;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	return (region->iova <= iova) &&
1318c2ecf20Sopenharmony_ci		(region->length + region->iova >= iova + size);
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci/**
1358c2ecf20Sopenharmony_ci * afu_dma_region_add - add given dma region to rbtree
1368c2ecf20Sopenharmony_ci * @pdata: feature device platform data
1378c2ecf20Sopenharmony_ci * @region: dma region to be added
1388c2ecf20Sopenharmony_ci *
1398c2ecf20Sopenharmony_ci * Return 0 for success, -EEXIST if dma region has already been added.
1408c2ecf20Sopenharmony_ci *
1418c2ecf20Sopenharmony_ci * Needs to be called with pdata->lock heold.
1428c2ecf20Sopenharmony_ci */
1438c2ecf20Sopenharmony_cistatic int afu_dma_region_add(struct dfl_feature_platform_data *pdata,
1448c2ecf20Sopenharmony_ci			      struct dfl_afu_dma_region *region)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata);
1478c2ecf20Sopenharmony_ci	struct rb_node **new, *parent = NULL;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	dev_dbg(&pdata->dev->dev, "add region (iova = %llx)\n",
1508c2ecf20Sopenharmony_ci		(unsigned long long)region->iova);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	new = &afu->dma_regions.rb_node;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	while (*new) {
1558c2ecf20Sopenharmony_ci		struct dfl_afu_dma_region *this;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci		this = container_of(*new, struct dfl_afu_dma_region, node);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci		parent = *new;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci		if (dma_region_check_iova(this, region->iova, region->length))
1628c2ecf20Sopenharmony_ci			return -EEXIST;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci		if (region->iova < this->iova)
1658c2ecf20Sopenharmony_ci			new = &((*new)->rb_left);
1668c2ecf20Sopenharmony_ci		else if (region->iova > this->iova)
1678c2ecf20Sopenharmony_ci			new = &((*new)->rb_right);
1688c2ecf20Sopenharmony_ci		else
1698c2ecf20Sopenharmony_ci			return -EEXIST;
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	rb_link_node(&region->node, parent, new);
1738c2ecf20Sopenharmony_ci	rb_insert_color(&region->node, &afu->dma_regions);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	return 0;
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci/**
1798c2ecf20Sopenharmony_ci * afu_dma_region_remove - remove given dma region from rbtree
1808c2ecf20Sopenharmony_ci * @pdata: feature device platform data
1818c2ecf20Sopenharmony_ci * @region: dma region to be removed
1828c2ecf20Sopenharmony_ci *
1838c2ecf20Sopenharmony_ci * Needs to be called with pdata->lock heold.
1848c2ecf20Sopenharmony_ci */
1858c2ecf20Sopenharmony_cistatic void afu_dma_region_remove(struct dfl_feature_platform_data *pdata,
1868c2ecf20Sopenharmony_ci				  struct dfl_afu_dma_region *region)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	struct dfl_afu *afu;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	dev_dbg(&pdata->dev->dev, "del region (iova = %llx)\n",
1918c2ecf20Sopenharmony_ci		(unsigned long long)region->iova);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	afu = dfl_fpga_pdata_get_private(pdata);
1948c2ecf20Sopenharmony_ci	rb_erase(&region->node, &afu->dma_regions);
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci/**
1988c2ecf20Sopenharmony_ci * afu_dma_region_destroy - destroy all regions in rbtree
1998c2ecf20Sopenharmony_ci * @pdata: feature device platform data
2008c2ecf20Sopenharmony_ci *
2018c2ecf20Sopenharmony_ci * Needs to be called with pdata->lock heold.
2028c2ecf20Sopenharmony_ci */
2038c2ecf20Sopenharmony_civoid afu_dma_region_destroy(struct dfl_feature_platform_data *pdata)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata);
2068c2ecf20Sopenharmony_ci	struct rb_node *node = rb_first(&afu->dma_regions);
2078c2ecf20Sopenharmony_ci	struct dfl_afu_dma_region *region;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	while (node) {
2108c2ecf20Sopenharmony_ci		region = container_of(node, struct dfl_afu_dma_region, node);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci		dev_dbg(&pdata->dev->dev, "del region (iova = %llx)\n",
2138c2ecf20Sopenharmony_ci			(unsigned long long)region->iova);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci		rb_erase(node, &afu->dma_regions);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci		if (region->iova)
2188c2ecf20Sopenharmony_ci			dma_unmap_page(dfl_fpga_pdata_to_parent(pdata),
2198c2ecf20Sopenharmony_ci				       region->iova, region->length,
2208c2ecf20Sopenharmony_ci				       DMA_BIDIRECTIONAL);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci		if (region->pages)
2238c2ecf20Sopenharmony_ci			afu_dma_unpin_pages(pdata, region);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci		node = rb_next(node);
2268c2ecf20Sopenharmony_ci		kfree(region);
2278c2ecf20Sopenharmony_ci	}
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci/**
2318c2ecf20Sopenharmony_ci * afu_dma_region_find - find the dma region from rbtree based on iova and size
2328c2ecf20Sopenharmony_ci * @pdata: feature device platform data
2338c2ecf20Sopenharmony_ci * @iova: address of the dma memory area
2348c2ecf20Sopenharmony_ci * @size: size of the dma memory area
2358c2ecf20Sopenharmony_ci *
2368c2ecf20Sopenharmony_ci * It finds the dma region from the rbtree based on @iova and @size:
2378c2ecf20Sopenharmony_ci * - if @size == 0, it finds the dma region which starts from @iova
2388c2ecf20Sopenharmony_ci * - otherwise, it finds the dma region which fully contains
2398c2ecf20Sopenharmony_ci *   [@iova, @iova+size)
2408c2ecf20Sopenharmony_ci * If nothing is matched returns NULL.
2418c2ecf20Sopenharmony_ci *
2428c2ecf20Sopenharmony_ci * Needs to be called with pdata->lock held.
2438c2ecf20Sopenharmony_ci */
2448c2ecf20Sopenharmony_cistruct dfl_afu_dma_region *
2458c2ecf20Sopenharmony_ciafu_dma_region_find(struct dfl_feature_platform_data *pdata, u64 iova, u64 size)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata);
2488c2ecf20Sopenharmony_ci	struct rb_node *node = afu->dma_regions.rb_node;
2498c2ecf20Sopenharmony_ci	struct device *dev = &pdata->dev->dev;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	while (node) {
2528c2ecf20Sopenharmony_ci		struct dfl_afu_dma_region *region;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci		region = container_of(node, struct dfl_afu_dma_region, node);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci		if (dma_region_check_iova(region, iova, size)) {
2578c2ecf20Sopenharmony_ci			dev_dbg(dev, "find region (iova = %llx)\n",
2588c2ecf20Sopenharmony_ci				(unsigned long long)region->iova);
2598c2ecf20Sopenharmony_ci			return region;
2608c2ecf20Sopenharmony_ci		}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci		if (iova < region->iova)
2638c2ecf20Sopenharmony_ci			node = node->rb_left;
2648c2ecf20Sopenharmony_ci		else if (iova > region->iova)
2658c2ecf20Sopenharmony_ci			node = node->rb_right;
2668c2ecf20Sopenharmony_ci		else
2678c2ecf20Sopenharmony_ci			/* the iova region is not fully covered. */
2688c2ecf20Sopenharmony_ci			break;
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	dev_dbg(dev, "region with iova %llx and size %llx is not found\n",
2728c2ecf20Sopenharmony_ci		(unsigned long long)iova, (unsigned long long)size);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	return NULL;
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci/**
2788c2ecf20Sopenharmony_ci * afu_dma_region_find_iova - find the dma region from rbtree by iova
2798c2ecf20Sopenharmony_ci * @pdata: feature device platform data
2808c2ecf20Sopenharmony_ci * @iova: address of the dma region
2818c2ecf20Sopenharmony_ci *
2828c2ecf20Sopenharmony_ci * Needs to be called with pdata->lock held.
2838c2ecf20Sopenharmony_ci */
2848c2ecf20Sopenharmony_cistatic struct dfl_afu_dma_region *
2858c2ecf20Sopenharmony_ciafu_dma_region_find_iova(struct dfl_feature_platform_data *pdata, u64 iova)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	return afu_dma_region_find(pdata, iova, 0);
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci/**
2918c2ecf20Sopenharmony_ci * afu_dma_map_region - map memory region for dma
2928c2ecf20Sopenharmony_ci * @pdata: feature device platform data
2938c2ecf20Sopenharmony_ci * @user_addr: address of the memory region
2948c2ecf20Sopenharmony_ci * @length: size of the memory region
2958c2ecf20Sopenharmony_ci * @iova: pointer of iova address
2968c2ecf20Sopenharmony_ci *
2978c2ecf20Sopenharmony_ci * Map memory region defined by @user_addr and @length, and return dma address
2988c2ecf20Sopenharmony_ci * of the memory region via @iova.
2998c2ecf20Sopenharmony_ci * Return 0 for success, otherwise error code.
3008c2ecf20Sopenharmony_ci */
3018c2ecf20Sopenharmony_ciint afu_dma_map_region(struct dfl_feature_platform_data *pdata,
3028c2ecf20Sopenharmony_ci		       u64 user_addr, u64 length, u64 *iova)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	struct dfl_afu_dma_region *region;
3058c2ecf20Sopenharmony_ci	int ret;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	/*
3088c2ecf20Sopenharmony_ci	 * Check Inputs, only accept page-aligned user memory region with
3098c2ecf20Sopenharmony_ci	 * valid length.
3108c2ecf20Sopenharmony_ci	 */
3118c2ecf20Sopenharmony_ci	if (!PAGE_ALIGNED(user_addr) || !PAGE_ALIGNED(length) || !length)
3128c2ecf20Sopenharmony_ci		return -EINVAL;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	/* Check overflow */
3158c2ecf20Sopenharmony_ci	if (user_addr + length < user_addr)
3168c2ecf20Sopenharmony_ci		return -EINVAL;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	region = kzalloc(sizeof(*region), GFP_KERNEL);
3198c2ecf20Sopenharmony_ci	if (!region)
3208c2ecf20Sopenharmony_ci		return -ENOMEM;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	region->user_addr = user_addr;
3238c2ecf20Sopenharmony_ci	region->length = length;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	/* Pin the user memory region */
3268c2ecf20Sopenharmony_ci	ret = afu_dma_pin_pages(pdata, region);
3278c2ecf20Sopenharmony_ci	if (ret) {
3288c2ecf20Sopenharmony_ci		dev_err(&pdata->dev->dev, "failed to pin memory region\n");
3298c2ecf20Sopenharmony_ci		goto free_region;
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	/* Only accept continuous pages, return error else */
3338c2ecf20Sopenharmony_ci	if (!afu_dma_check_continuous_pages(region)) {
3348c2ecf20Sopenharmony_ci		dev_err(&pdata->dev->dev, "pages are not continuous\n");
3358c2ecf20Sopenharmony_ci		ret = -EINVAL;
3368c2ecf20Sopenharmony_ci		goto unpin_pages;
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	/* As pages are continuous then start to do DMA mapping */
3408c2ecf20Sopenharmony_ci	region->iova = dma_map_page(dfl_fpga_pdata_to_parent(pdata),
3418c2ecf20Sopenharmony_ci				    region->pages[0], 0,
3428c2ecf20Sopenharmony_ci				    region->length,
3438c2ecf20Sopenharmony_ci				    DMA_BIDIRECTIONAL);
3448c2ecf20Sopenharmony_ci	if (dma_mapping_error(dfl_fpga_pdata_to_parent(pdata), region->iova)) {
3458c2ecf20Sopenharmony_ci		dev_err(&pdata->dev->dev, "failed to map for dma\n");
3468c2ecf20Sopenharmony_ci		ret = -EFAULT;
3478c2ecf20Sopenharmony_ci		goto unpin_pages;
3488c2ecf20Sopenharmony_ci	}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	*iova = region->iova;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	mutex_lock(&pdata->lock);
3538c2ecf20Sopenharmony_ci	ret = afu_dma_region_add(pdata, region);
3548c2ecf20Sopenharmony_ci	mutex_unlock(&pdata->lock);
3558c2ecf20Sopenharmony_ci	if (ret) {
3568c2ecf20Sopenharmony_ci		dev_err(&pdata->dev->dev, "failed to add dma region\n");
3578c2ecf20Sopenharmony_ci		goto unmap_dma;
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	return 0;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ciunmap_dma:
3638c2ecf20Sopenharmony_ci	dma_unmap_page(dfl_fpga_pdata_to_parent(pdata),
3648c2ecf20Sopenharmony_ci		       region->iova, region->length, DMA_BIDIRECTIONAL);
3658c2ecf20Sopenharmony_ciunpin_pages:
3668c2ecf20Sopenharmony_ci	afu_dma_unpin_pages(pdata, region);
3678c2ecf20Sopenharmony_cifree_region:
3688c2ecf20Sopenharmony_ci	kfree(region);
3698c2ecf20Sopenharmony_ci	return ret;
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci/**
3738c2ecf20Sopenharmony_ci * afu_dma_unmap_region - unmap dma memory region
3748c2ecf20Sopenharmony_ci * @pdata: feature device platform data
3758c2ecf20Sopenharmony_ci * @iova: dma address of the region
3768c2ecf20Sopenharmony_ci *
3778c2ecf20Sopenharmony_ci * Unmap dma memory region based on @iova.
3788c2ecf20Sopenharmony_ci * Return 0 for success, otherwise error code.
3798c2ecf20Sopenharmony_ci */
3808c2ecf20Sopenharmony_ciint afu_dma_unmap_region(struct dfl_feature_platform_data *pdata, u64 iova)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	struct dfl_afu_dma_region *region;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	mutex_lock(&pdata->lock);
3858c2ecf20Sopenharmony_ci	region = afu_dma_region_find_iova(pdata, iova);
3868c2ecf20Sopenharmony_ci	if (!region) {
3878c2ecf20Sopenharmony_ci		mutex_unlock(&pdata->lock);
3888c2ecf20Sopenharmony_ci		return -EINVAL;
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	if (region->in_use) {
3928c2ecf20Sopenharmony_ci		mutex_unlock(&pdata->lock);
3938c2ecf20Sopenharmony_ci		return -EBUSY;
3948c2ecf20Sopenharmony_ci	}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	afu_dma_region_remove(pdata, region);
3978c2ecf20Sopenharmony_ci	mutex_unlock(&pdata->lock);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	dma_unmap_page(dfl_fpga_pdata_to_parent(pdata),
4008c2ecf20Sopenharmony_ci		       region->iova, region->length, DMA_BIDIRECTIONAL);
4018c2ecf20Sopenharmony_ci	afu_dma_unpin_pages(pdata, region);
4028c2ecf20Sopenharmony_ci	kfree(region);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	return 0;
4058c2ecf20Sopenharmony_ci}
406