xref: /linux/drivers/fpga/dfl-afu-region.c (revision 13845bdc869f136f92ad3d40ea09b867bb4ce467)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Driver for FPGA Accelerated Function Unit (AFU) MMIO Region Management
4  *
5  * Copyright (C) 2017-2018 Intel Corporation, Inc.
6  *
7  * Authors:
8  *   Wu Hao <hao.wu@intel.com>
9  *   Xiao Guangrong <guangrong.xiao@linux.intel.com>
10  */
11 #include "dfl-afu.h"
12 
13 /**
14  * afu_mmio_region_init - init function for afu mmio region support
15  * @fdata: afu feature dev data
16  */
17 void afu_mmio_region_init(struct dfl_feature_dev_data *fdata)
18 {
19 	struct dfl_afu *afu = dfl_fpga_fdata_get_private(fdata);
20 
21 	INIT_LIST_HEAD(&afu->regions);
22 }
23 
24 #define for_each_region(region, afu)	\
25 	list_for_each_entry((region), &(afu)->regions, node)
26 
27 static struct dfl_afu_mmio_region *get_region_by_index(struct dfl_afu *afu,
28 						       u32 region_index)
29 {
30 	struct dfl_afu_mmio_region *region;
31 
32 	for_each_region(region, afu)
33 		if (region->index == region_index)
34 			return region;
35 
36 	return NULL;
37 }
38 
39 /**
40  * afu_mmio_region_add - add a mmio region to given feature dev.
41  *
42  * @fdata: afu feature dev data
43  * @region_index: region index.
44  * @region_size: region size.
45  * @phys: region's physical address of this region.
46  * @flags: region flags (access permission).
47  *
48  * Return: 0 on success, negative error code otherwise.
49  */
50 int afu_mmio_region_add(struct dfl_feature_dev_data *fdata,
51 			u32 region_index, u64 region_size, u64 phys, u32 flags)
52 {
53 	struct device *dev = &fdata->dev->dev;
54 	struct dfl_afu_mmio_region *region;
55 	struct dfl_afu *afu;
56 	int ret = 0;
57 
58 	region = devm_kzalloc(dev, sizeof(*region), GFP_KERNEL);
59 	if (!region)
60 		return -ENOMEM;
61 
62 	region->index = region_index;
63 	region->size = region_size;
64 	region->phys = phys;
65 	region->flags = flags;
66 
67 	mutex_lock(&fdata->lock);
68 
69 	afu = dfl_fpga_fdata_get_private(fdata);
70 
71 	/* check if @index already exists */
72 	if (get_region_by_index(afu, region_index)) {
73 		mutex_unlock(&fdata->lock);
74 		ret = -EEXIST;
75 		goto exit;
76 	}
77 
78 	region_size = PAGE_ALIGN(region_size);
79 	region->offset = afu->region_cur_offset;
80 	list_add(&region->node, &afu->regions);
81 
82 	afu->region_cur_offset += region_size;
83 	afu->num_regions++;
84 	mutex_unlock(&fdata->lock);
85 
86 	return 0;
87 
88 exit:
89 	devm_kfree(dev, region);
90 	return ret;
91 }
92 
93 /**
94  * afu_mmio_region_destroy - destroy all mmio regions under given feature dev.
95  * @fdata: afu feature dev data
96  */
97 void afu_mmio_region_destroy(struct dfl_feature_dev_data *fdata)
98 {
99 	struct dfl_afu *afu = dfl_fpga_fdata_get_private(fdata);
100 	struct dfl_afu_mmio_region *tmp, *region;
101 
102 	list_for_each_entry_safe(region, tmp, &afu->regions, node)
103 		devm_kfree(&fdata->dev->dev, region);
104 }
105 
106 /**
107  * afu_mmio_region_get_by_index - find an afu region by index.
108  * @fdata: afu feature dev data
109  * @region_index: region index.
110  * @pregion: ptr to region for result.
111  *
112  * Return: 0 on success, negative error code otherwise.
113  */
114 int afu_mmio_region_get_by_index(struct dfl_feature_dev_data *fdata,
115 				 u32 region_index,
116 				 struct dfl_afu_mmio_region *pregion)
117 {
118 	struct dfl_afu_mmio_region *region;
119 	struct dfl_afu *afu;
120 	int ret = 0;
121 
122 	mutex_lock(&fdata->lock);
123 	afu = dfl_fpga_fdata_get_private(fdata);
124 	region = get_region_by_index(afu, region_index);
125 	if (!region) {
126 		ret = -EINVAL;
127 		goto exit;
128 	}
129 	*pregion = *region;
130 exit:
131 	mutex_unlock(&fdata->lock);
132 	return ret;
133 }
134 
135 /**
136  * afu_mmio_region_get_by_offset - find an afu mmio region by offset and size
137  *
138  * @fdata: afu feature dev data
139  * @offset: region offset from start of the device fd.
140  * @size: region size.
141  * @pregion: ptr to region for result.
142  *
143  * Find the region which fully contains the region described by input
144  * parameters (offset and size) from the feature dev's region linked list.
145  *
146  * Return: 0 on success, negative error code otherwise.
147  */
148 int afu_mmio_region_get_by_offset(struct dfl_feature_dev_data *fdata,
149 				  u64 offset, u64 size,
150 				  struct dfl_afu_mmio_region *pregion)
151 {
152 	struct dfl_afu_mmio_region *region;
153 	struct dfl_afu *afu;
154 	int ret = 0;
155 
156 	mutex_lock(&fdata->lock);
157 	afu = dfl_fpga_fdata_get_private(fdata);
158 	for_each_region(region, afu)
159 		if (region->offset <= offset &&
160 		    region->offset + region->size >= offset + size) {
161 			*pregion = *region;
162 			goto exit;
163 		}
164 	ret = -EINVAL;
165 exit:
166 	mutex_unlock(&fdata->lock);
167 	return ret;
168 }
169