xref: /openbmc/linux/drivers/fpga/dfl-afu-region.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
1857a2622SXiao Guangrong // SPDX-License-Identifier: GPL-2.0
2857a2622SXiao Guangrong /*
3857a2622SXiao Guangrong  * Driver for FPGA Accelerated Function Unit (AFU) MMIO Region Management
4857a2622SXiao Guangrong  *
5857a2622SXiao Guangrong  * Copyright (C) 2017-2018 Intel Corporation, Inc.
6857a2622SXiao Guangrong  *
7857a2622SXiao Guangrong  * Authors:
8857a2622SXiao Guangrong  *   Wu Hao <hao.wu@intel.com>
9857a2622SXiao Guangrong  *   Xiao Guangrong <guangrong.xiao@linux.intel.com>
10857a2622SXiao Guangrong  */
11857a2622SXiao Guangrong #include "dfl-afu.h"
12857a2622SXiao Guangrong 
13857a2622SXiao Guangrong /**
14857a2622SXiao Guangrong  * afu_mmio_region_init - init function for afu mmio region support
15857a2622SXiao Guangrong  * @pdata: afu platform device's pdata.
16857a2622SXiao Guangrong  */
afu_mmio_region_init(struct dfl_feature_platform_data * pdata)17857a2622SXiao Guangrong void afu_mmio_region_init(struct dfl_feature_platform_data *pdata)
18857a2622SXiao Guangrong {
19857a2622SXiao Guangrong 	struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata);
20857a2622SXiao Guangrong 
21857a2622SXiao Guangrong 	INIT_LIST_HEAD(&afu->regions);
22857a2622SXiao Guangrong }
23857a2622SXiao Guangrong 
24857a2622SXiao Guangrong #define for_each_region(region, afu)	\
25857a2622SXiao Guangrong 	list_for_each_entry((region), &(afu)->regions, node)
26857a2622SXiao Guangrong 
get_region_by_index(struct dfl_afu * afu,u32 region_index)27857a2622SXiao Guangrong static struct dfl_afu_mmio_region *get_region_by_index(struct dfl_afu *afu,
28857a2622SXiao Guangrong 						       u32 region_index)
29857a2622SXiao Guangrong {
30857a2622SXiao Guangrong 	struct dfl_afu_mmio_region *region;
31857a2622SXiao Guangrong 
32857a2622SXiao Guangrong 	for_each_region(region, afu)
33857a2622SXiao Guangrong 		if (region->index == region_index)
34857a2622SXiao Guangrong 			return region;
35857a2622SXiao Guangrong 
36857a2622SXiao Guangrong 	return NULL;
37857a2622SXiao Guangrong }
38857a2622SXiao Guangrong 
39857a2622SXiao Guangrong /**
40857a2622SXiao Guangrong  * afu_mmio_region_add - add a mmio region to given feature dev.
41857a2622SXiao Guangrong  *
42*a73c125bSXu Yilun  * @pdata: afu platform device's pdata.
43857a2622SXiao Guangrong  * @region_index: region index.
44857a2622SXiao Guangrong  * @region_size: region size.
45857a2622SXiao Guangrong  * @phys: region's physical address of this region.
46857a2622SXiao Guangrong  * @flags: region flags (access permission).
47857a2622SXiao Guangrong  *
48857a2622SXiao Guangrong  * Return: 0 on success, negative error code otherwise.
49857a2622SXiao Guangrong  */
afu_mmio_region_add(struct dfl_feature_platform_data * pdata,u32 region_index,u64 region_size,u64 phys,u32 flags)50857a2622SXiao Guangrong int afu_mmio_region_add(struct dfl_feature_platform_data *pdata,
51857a2622SXiao Guangrong 			u32 region_index, u64 region_size, u64 phys, u32 flags)
52857a2622SXiao Guangrong {
53857a2622SXiao Guangrong 	struct dfl_afu_mmio_region *region;
54857a2622SXiao Guangrong 	struct dfl_afu *afu;
55857a2622SXiao Guangrong 	int ret = 0;
56857a2622SXiao Guangrong 
57857a2622SXiao Guangrong 	region = devm_kzalloc(&pdata->dev->dev, sizeof(*region), GFP_KERNEL);
58857a2622SXiao Guangrong 	if (!region)
59857a2622SXiao Guangrong 		return -ENOMEM;
60857a2622SXiao Guangrong 
61857a2622SXiao Guangrong 	region->index = region_index;
62857a2622SXiao Guangrong 	region->size = region_size;
63857a2622SXiao Guangrong 	region->phys = phys;
64857a2622SXiao Guangrong 	region->flags = flags;
65857a2622SXiao Guangrong 
66857a2622SXiao Guangrong 	mutex_lock(&pdata->lock);
67857a2622SXiao Guangrong 
68857a2622SXiao Guangrong 	afu = dfl_fpga_pdata_get_private(pdata);
69857a2622SXiao Guangrong 
70857a2622SXiao Guangrong 	/* check if @index already exists */
71857a2622SXiao Guangrong 	if (get_region_by_index(afu, region_index)) {
72857a2622SXiao Guangrong 		mutex_unlock(&pdata->lock);
73857a2622SXiao Guangrong 		ret = -EEXIST;
74857a2622SXiao Guangrong 		goto exit;
75857a2622SXiao Guangrong 	}
76857a2622SXiao Guangrong 
77857a2622SXiao Guangrong 	region_size = PAGE_ALIGN(region_size);
78857a2622SXiao Guangrong 	region->offset = afu->region_cur_offset;
79857a2622SXiao Guangrong 	list_add(&region->node, &afu->regions);
80857a2622SXiao Guangrong 
81857a2622SXiao Guangrong 	afu->region_cur_offset += region_size;
82857a2622SXiao Guangrong 	afu->num_regions++;
83857a2622SXiao Guangrong 	mutex_unlock(&pdata->lock);
84857a2622SXiao Guangrong 
85857a2622SXiao Guangrong 	return 0;
86857a2622SXiao Guangrong 
87857a2622SXiao Guangrong exit:
88857a2622SXiao Guangrong 	devm_kfree(&pdata->dev->dev, region);
89857a2622SXiao Guangrong 	return ret;
90857a2622SXiao Guangrong }
91857a2622SXiao Guangrong 
92857a2622SXiao Guangrong /**
93857a2622SXiao Guangrong  * afu_mmio_region_destroy - destroy all mmio regions under given feature dev.
94857a2622SXiao Guangrong  * @pdata: afu platform device's pdata.
95857a2622SXiao Guangrong  */
afu_mmio_region_destroy(struct dfl_feature_platform_data * pdata)96857a2622SXiao Guangrong void afu_mmio_region_destroy(struct dfl_feature_platform_data *pdata)
97857a2622SXiao Guangrong {
98857a2622SXiao Guangrong 	struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata);
99857a2622SXiao Guangrong 	struct dfl_afu_mmio_region *tmp, *region;
100857a2622SXiao Guangrong 
101857a2622SXiao Guangrong 	list_for_each_entry_safe(region, tmp, &afu->regions, node)
102857a2622SXiao Guangrong 		devm_kfree(&pdata->dev->dev, region);
103857a2622SXiao Guangrong }
104857a2622SXiao Guangrong 
105857a2622SXiao Guangrong /**
106857a2622SXiao Guangrong  * afu_mmio_region_get_by_index - find an afu region by index.
107857a2622SXiao Guangrong  * @pdata: afu platform device's pdata.
108857a2622SXiao Guangrong  * @region_index: region index.
109857a2622SXiao Guangrong  * @pregion: ptr to region for result.
110857a2622SXiao Guangrong  *
111857a2622SXiao Guangrong  * Return: 0 on success, negative error code otherwise.
112857a2622SXiao Guangrong  */
afu_mmio_region_get_by_index(struct dfl_feature_platform_data * pdata,u32 region_index,struct dfl_afu_mmio_region * pregion)113857a2622SXiao Guangrong int afu_mmio_region_get_by_index(struct dfl_feature_platform_data *pdata,
114857a2622SXiao Guangrong 				 u32 region_index,
115857a2622SXiao Guangrong 				 struct dfl_afu_mmio_region *pregion)
116857a2622SXiao Guangrong {
117857a2622SXiao Guangrong 	struct dfl_afu_mmio_region *region;
118857a2622SXiao Guangrong 	struct dfl_afu *afu;
119857a2622SXiao Guangrong 	int ret = 0;
120857a2622SXiao Guangrong 
121857a2622SXiao Guangrong 	mutex_lock(&pdata->lock);
122857a2622SXiao Guangrong 	afu = dfl_fpga_pdata_get_private(pdata);
123857a2622SXiao Guangrong 	region = get_region_by_index(afu, region_index);
124857a2622SXiao Guangrong 	if (!region) {
125857a2622SXiao Guangrong 		ret = -EINVAL;
126857a2622SXiao Guangrong 		goto exit;
127857a2622SXiao Guangrong 	}
128857a2622SXiao Guangrong 	*pregion = *region;
129857a2622SXiao Guangrong exit:
130857a2622SXiao Guangrong 	mutex_unlock(&pdata->lock);
131857a2622SXiao Guangrong 	return ret;
132857a2622SXiao Guangrong }
133857a2622SXiao Guangrong 
134857a2622SXiao Guangrong /**
135857a2622SXiao Guangrong  * afu_mmio_region_get_by_offset - find an afu mmio region by offset and size
136857a2622SXiao Guangrong  *
137857a2622SXiao Guangrong  * @pdata: afu platform device's pdata.
138857a2622SXiao Guangrong  * @offset: region offset from start of the device fd.
139857a2622SXiao Guangrong  * @size: region size.
140857a2622SXiao Guangrong  * @pregion: ptr to region for result.
141857a2622SXiao Guangrong  *
142857a2622SXiao Guangrong  * Find the region which fully contains the region described by input
143857a2622SXiao Guangrong  * parameters (offset and size) from the feature dev's region linked list.
144857a2622SXiao Guangrong  *
145857a2622SXiao Guangrong  * Return: 0 on success, negative error code otherwise.
146857a2622SXiao Guangrong  */
afu_mmio_region_get_by_offset(struct dfl_feature_platform_data * pdata,u64 offset,u64 size,struct dfl_afu_mmio_region * pregion)147857a2622SXiao Guangrong int afu_mmio_region_get_by_offset(struct dfl_feature_platform_data *pdata,
148857a2622SXiao Guangrong 				  u64 offset, u64 size,
149857a2622SXiao Guangrong 				  struct dfl_afu_mmio_region *pregion)
150857a2622SXiao Guangrong {
151857a2622SXiao Guangrong 	struct dfl_afu_mmio_region *region;
152857a2622SXiao Guangrong 	struct dfl_afu *afu;
153857a2622SXiao Guangrong 	int ret = 0;
154857a2622SXiao Guangrong 
155857a2622SXiao Guangrong 	mutex_lock(&pdata->lock);
156857a2622SXiao Guangrong 	afu = dfl_fpga_pdata_get_private(pdata);
157857a2622SXiao Guangrong 	for_each_region(region, afu)
158857a2622SXiao Guangrong 		if (region->offset <= offset &&
159857a2622SXiao Guangrong 		    region->offset + region->size >= offset + size) {
160857a2622SXiao Guangrong 			*pregion = *region;
161857a2622SXiao Guangrong 			goto exit;
162857a2622SXiao Guangrong 		}
163857a2622SXiao Guangrong 	ret = -EINVAL;
164857a2622SXiao Guangrong exit:
165857a2622SXiao Guangrong 	mutex_unlock(&pdata->lock);
166857a2622SXiao Guangrong 	return ret;
167857a2622SXiao Guangrong }
168