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(®ion->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