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