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 | * @pdata: afu platform device's pdata. |
16 | */ |
17 | void afu_mmio_region_init(struct dfl_feature_platform_data *pdata) |
18 | { |
19 | struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata); |
20 | |
21 | INIT_LIST_HEAD(list: &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 | * @pdata: afu platform device's pdata. |
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_platform_data *pdata, |
51 | u32 region_index, u64 region_size, u64 phys, u32 flags) |
52 | { |
53 | struct dfl_afu_mmio_region *region; |
54 | struct dfl_afu *afu; |
55 | int ret = 0; |
56 | |
57 | region = devm_kzalloc(dev: &pdata->dev->dev, size: sizeof(*region), GFP_KERNEL); |
58 | if (!region) |
59 | return -ENOMEM; |
60 | |
61 | region->index = region_index; |
62 | region->size = region_size; |
63 | region->phys = phys; |
64 | region->flags = flags; |
65 | |
66 | mutex_lock(&pdata->lock); |
67 | |
68 | afu = dfl_fpga_pdata_get_private(pdata); |
69 | |
70 | /* check if @index already exists */ |
71 | if (get_region_by_index(afu, region_index)) { |
72 | mutex_unlock(lock: &pdata->lock); |
73 | ret = -EEXIST; |
74 | goto exit; |
75 | } |
76 | |
77 | region_size = PAGE_ALIGN(region_size); |
78 | region->offset = afu->region_cur_offset; |
79 | list_add(new: ®ion->node, head: &afu->regions); |
80 | |
81 | afu->region_cur_offset += region_size; |
82 | afu->num_regions++; |
83 | mutex_unlock(lock: &pdata->lock); |
84 | |
85 | return 0; |
86 | |
87 | exit: |
88 | devm_kfree(dev: &pdata->dev->dev, p: region); |
89 | return ret; |
90 | } |
91 | |
92 | /** |
93 | * afu_mmio_region_destroy - destroy all mmio regions under given feature dev. |
94 | * @pdata: afu platform device's pdata. |
95 | */ |
96 | void afu_mmio_region_destroy(struct dfl_feature_platform_data *pdata) |
97 | { |
98 | struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata); |
99 | struct dfl_afu_mmio_region *tmp, *region; |
100 | |
101 | list_for_each_entry_safe(region, tmp, &afu->regions, node) |
102 | devm_kfree(dev: &pdata->dev->dev, p: region); |
103 | } |
104 | |
105 | /** |
106 | * afu_mmio_region_get_by_index - find an afu region by index. |
107 | * @pdata: afu platform device's pdata. |
108 | * @region_index: region index. |
109 | * @pregion: ptr to region for result. |
110 | * |
111 | * Return: 0 on success, negative error code otherwise. |
112 | */ |
113 | int afu_mmio_region_get_by_index(struct dfl_feature_platform_data *pdata, |
114 | u32 region_index, |
115 | struct dfl_afu_mmio_region *pregion) |
116 | { |
117 | struct dfl_afu_mmio_region *region; |
118 | struct dfl_afu *afu; |
119 | int ret = 0; |
120 | |
121 | mutex_lock(&pdata->lock); |
122 | afu = dfl_fpga_pdata_get_private(pdata); |
123 | region = get_region_by_index(afu, region_index); |
124 | if (!region) { |
125 | ret = -EINVAL; |
126 | goto exit; |
127 | } |
128 | *pregion = *region; |
129 | exit: |
130 | mutex_unlock(lock: &pdata->lock); |
131 | return ret; |
132 | } |
133 | |
134 | /** |
135 | * afu_mmio_region_get_by_offset - find an afu mmio region by offset and size |
136 | * |
137 | * @pdata: afu platform device's pdata. |
138 | * @offset: region offset from start of the device fd. |
139 | * @size: region size. |
140 | * @pregion: ptr to region for result. |
141 | * |
142 | * Find the region which fully contains the region described by input |
143 | * parameters (offset and size) from the feature dev's region linked list. |
144 | * |
145 | * Return: 0 on success, negative error code otherwise. |
146 | */ |
147 | int afu_mmio_region_get_by_offset(struct dfl_feature_platform_data *pdata, |
148 | u64 offset, u64 size, |
149 | struct dfl_afu_mmio_region *pregion) |
150 | { |
151 | struct dfl_afu_mmio_region *region; |
152 | struct dfl_afu *afu; |
153 | int ret = 0; |
154 | |
155 | mutex_lock(&pdata->lock); |
156 | afu = dfl_fpga_pdata_get_private(pdata); |
157 | for_each_region(region, afu) |
158 | if (region->offset <= offset && |
159 | region->offset + region->size >= offset + size) { |
160 | *pregion = *region; |
161 | goto exit; |
162 | } |
163 | ret = -EINVAL; |
164 | exit: |
165 | mutex_unlock(lock: &pdata->lock); |
166 | return ret; |
167 | } |
168 | |