Commit | Line | Data |
---|---|---|
857a2622 XG |
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(&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 | * | |
a73c125b | 42 | * @pdata: afu platform device's pdata. |
857a2622 XG |
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(&pdata->dev->dev, 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(&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(®ion->node, &afu->regions); | |
80 | ||
81 | afu->region_cur_offset += region_size; | |
82 | afu->num_regions++; | |
83 | mutex_unlock(&pdata->lock); | |
84 | ||
85 | return 0; | |
86 | ||
87 | exit: | |
88 | devm_kfree(&pdata->dev->dev, 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(&pdata->dev->dev, 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(&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(&pdata->lock); | |
166 | return ret; | |
167 | } |