18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for FPGA Accelerated Function Unit (AFU) MMIO 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#include "dfl-afu.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/**
148c2ecf20Sopenharmony_ci * afu_mmio_region_init - init function for afu mmio region support
158c2ecf20Sopenharmony_ci * @pdata: afu platform device's pdata.
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_civoid afu_mmio_region_init(struct dfl_feature_platform_data *pdata)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata);
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&afu->regions);
228c2ecf20Sopenharmony_ci}
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define for_each_region(region, afu)	\
258c2ecf20Sopenharmony_ci	list_for_each_entry((region), &(afu)->regions, node)
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic struct dfl_afu_mmio_region *get_region_by_index(struct dfl_afu *afu,
288c2ecf20Sopenharmony_ci						       u32 region_index)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	struct dfl_afu_mmio_region *region;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	for_each_region(region, afu)
338c2ecf20Sopenharmony_ci		if (region->index == region_index)
348c2ecf20Sopenharmony_ci			return region;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	return NULL;
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/**
408c2ecf20Sopenharmony_ci * afu_mmio_region_add - add a mmio region to given feature dev.
418c2ecf20Sopenharmony_ci *
428c2ecf20Sopenharmony_ci * @region_index: region index.
438c2ecf20Sopenharmony_ci * @region_size: region size.
448c2ecf20Sopenharmony_ci * @phys: region's physical address of this region.
458c2ecf20Sopenharmony_ci * @flags: region flags (access permission).
468c2ecf20Sopenharmony_ci *
478c2ecf20Sopenharmony_ci * Return: 0 on success, negative error code otherwise.
488c2ecf20Sopenharmony_ci */
498c2ecf20Sopenharmony_ciint afu_mmio_region_add(struct dfl_feature_platform_data *pdata,
508c2ecf20Sopenharmony_ci			u32 region_index, u64 region_size, u64 phys, u32 flags)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	struct dfl_afu_mmio_region *region;
538c2ecf20Sopenharmony_ci	struct dfl_afu *afu;
548c2ecf20Sopenharmony_ci	int ret = 0;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	region = devm_kzalloc(&pdata->dev->dev, sizeof(*region), GFP_KERNEL);
578c2ecf20Sopenharmony_ci	if (!region)
588c2ecf20Sopenharmony_ci		return -ENOMEM;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	region->index = region_index;
618c2ecf20Sopenharmony_ci	region->size = region_size;
628c2ecf20Sopenharmony_ci	region->phys = phys;
638c2ecf20Sopenharmony_ci	region->flags = flags;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	mutex_lock(&pdata->lock);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	afu = dfl_fpga_pdata_get_private(pdata);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	/* check if @index already exists */
708c2ecf20Sopenharmony_ci	if (get_region_by_index(afu, region_index)) {
718c2ecf20Sopenharmony_ci		mutex_unlock(&pdata->lock);
728c2ecf20Sopenharmony_ci		ret = -EEXIST;
738c2ecf20Sopenharmony_ci		goto exit;
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	region_size = PAGE_ALIGN(region_size);
778c2ecf20Sopenharmony_ci	region->offset = afu->region_cur_offset;
788c2ecf20Sopenharmony_ci	list_add(&region->node, &afu->regions);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	afu->region_cur_offset += region_size;
818c2ecf20Sopenharmony_ci	afu->num_regions++;
828c2ecf20Sopenharmony_ci	mutex_unlock(&pdata->lock);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	return 0;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ciexit:
878c2ecf20Sopenharmony_ci	devm_kfree(&pdata->dev->dev, region);
888c2ecf20Sopenharmony_ci	return ret;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci/**
928c2ecf20Sopenharmony_ci * afu_mmio_region_destroy - destroy all mmio regions under given feature dev.
938c2ecf20Sopenharmony_ci * @pdata: afu platform device's pdata.
948c2ecf20Sopenharmony_ci */
958c2ecf20Sopenharmony_civoid afu_mmio_region_destroy(struct dfl_feature_platform_data *pdata)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata);
988c2ecf20Sopenharmony_ci	struct dfl_afu_mmio_region *tmp, *region;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	list_for_each_entry_safe(region, tmp, &afu->regions, node)
1018c2ecf20Sopenharmony_ci		devm_kfree(&pdata->dev->dev, region);
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci/**
1058c2ecf20Sopenharmony_ci * afu_mmio_region_get_by_index - find an afu region by index.
1068c2ecf20Sopenharmony_ci * @pdata: afu platform device's pdata.
1078c2ecf20Sopenharmony_ci * @region_index: region index.
1088c2ecf20Sopenharmony_ci * @pregion: ptr to region for result.
1098c2ecf20Sopenharmony_ci *
1108c2ecf20Sopenharmony_ci * Return: 0 on success, negative error code otherwise.
1118c2ecf20Sopenharmony_ci */
1128c2ecf20Sopenharmony_ciint afu_mmio_region_get_by_index(struct dfl_feature_platform_data *pdata,
1138c2ecf20Sopenharmony_ci				 u32 region_index,
1148c2ecf20Sopenharmony_ci				 struct dfl_afu_mmio_region *pregion)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	struct dfl_afu_mmio_region *region;
1178c2ecf20Sopenharmony_ci	struct dfl_afu *afu;
1188c2ecf20Sopenharmony_ci	int ret = 0;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	mutex_lock(&pdata->lock);
1218c2ecf20Sopenharmony_ci	afu = dfl_fpga_pdata_get_private(pdata);
1228c2ecf20Sopenharmony_ci	region = get_region_by_index(afu, region_index);
1238c2ecf20Sopenharmony_ci	if (!region) {
1248c2ecf20Sopenharmony_ci		ret = -EINVAL;
1258c2ecf20Sopenharmony_ci		goto exit;
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci	*pregion = *region;
1288c2ecf20Sopenharmony_ciexit:
1298c2ecf20Sopenharmony_ci	mutex_unlock(&pdata->lock);
1308c2ecf20Sopenharmony_ci	return ret;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci/**
1348c2ecf20Sopenharmony_ci * afu_mmio_region_get_by_offset - find an afu mmio region by offset and size
1358c2ecf20Sopenharmony_ci *
1368c2ecf20Sopenharmony_ci * @pdata: afu platform device's pdata.
1378c2ecf20Sopenharmony_ci * @offset: region offset from start of the device fd.
1388c2ecf20Sopenharmony_ci * @size: region size.
1398c2ecf20Sopenharmony_ci * @pregion: ptr to region for result.
1408c2ecf20Sopenharmony_ci *
1418c2ecf20Sopenharmony_ci * Find the region which fully contains the region described by input
1428c2ecf20Sopenharmony_ci * parameters (offset and size) from the feature dev's region linked list.
1438c2ecf20Sopenharmony_ci *
1448c2ecf20Sopenharmony_ci * Return: 0 on success, negative error code otherwise.
1458c2ecf20Sopenharmony_ci */
1468c2ecf20Sopenharmony_ciint afu_mmio_region_get_by_offset(struct dfl_feature_platform_data *pdata,
1478c2ecf20Sopenharmony_ci				  u64 offset, u64 size,
1488c2ecf20Sopenharmony_ci				  struct dfl_afu_mmio_region *pregion)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	struct dfl_afu_mmio_region *region;
1518c2ecf20Sopenharmony_ci	struct dfl_afu *afu;
1528c2ecf20Sopenharmony_ci	int ret = 0;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	mutex_lock(&pdata->lock);
1558c2ecf20Sopenharmony_ci	afu = dfl_fpga_pdata_get_private(pdata);
1568c2ecf20Sopenharmony_ci	for_each_region(region, afu)
1578c2ecf20Sopenharmony_ci		if (region->offset <= offset &&
1588c2ecf20Sopenharmony_ci		    region->offset + region->size >= offset + size) {
1598c2ecf20Sopenharmony_ci			*pregion = *region;
1608c2ecf20Sopenharmony_ci			goto exit;
1618c2ecf20Sopenharmony_ci		}
1628c2ecf20Sopenharmony_ci	ret = -EINVAL;
1638c2ecf20Sopenharmony_ciexit:
1648c2ecf20Sopenharmony_ci	mutex_unlock(&pdata->lock);
1658c2ecf20Sopenharmony_ci	return ret;
1668c2ecf20Sopenharmony_ci}
167