1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * FPGA Region - Support for FPGA programming under Linux |
4 | * |
5 | * Copyright (C) 2013-2016 Altera Corporation |
6 | * Copyright (C) 2017 Intel Corporation |
7 | */ |
8 | #include <linux/fpga/fpga-bridge.h> |
9 | #include <linux/fpga/fpga-mgr.h> |
10 | #include <linux/fpga/fpga-region.h> |
11 | #include <linux/idr.h> |
12 | #include <linux/kernel.h> |
13 | #include <linux/list.h> |
14 | #include <linux/module.h> |
15 | #include <linux/slab.h> |
16 | #include <linux/spinlock.h> |
17 | |
18 | static DEFINE_IDA(fpga_region_ida); |
19 | static const struct class fpga_region_class; |
20 | |
21 | struct fpga_region * |
22 | fpga_region_class_find(struct device *start, const void *data, |
23 | int (*match)(struct device *, const void *)) |
24 | { |
25 | struct device *dev; |
26 | |
27 | dev = class_find_device(class: &fpga_region_class, start, data, match); |
28 | if (!dev) |
29 | return NULL; |
30 | |
31 | return to_fpga_region(dev); |
32 | } |
33 | EXPORT_SYMBOL_GPL(fpga_region_class_find); |
34 | |
35 | /** |
36 | * fpga_region_get - get an exclusive reference to an fpga region |
37 | * @region: FPGA Region struct |
38 | * |
39 | * Caller should call fpga_region_put() when done with region. |
40 | * |
41 | * Return: |
42 | * * fpga_region struct if successful. |
43 | * * -EBUSY if someone already has a reference to the region. |
44 | * * -ENODEV if can't take parent driver module refcount. |
45 | */ |
46 | static struct fpga_region *fpga_region_get(struct fpga_region *region) |
47 | { |
48 | struct device *dev = ®ion->dev; |
49 | |
50 | if (!mutex_trylock(lock: ®ion->mutex)) { |
51 | dev_dbg(dev, "%s: FPGA Region already in use\n" , __func__); |
52 | return ERR_PTR(error: -EBUSY); |
53 | } |
54 | |
55 | get_device(dev); |
56 | if (!try_module_get(module: dev->parent->driver->owner)) { |
57 | put_device(dev); |
58 | mutex_unlock(lock: ®ion->mutex); |
59 | return ERR_PTR(error: -ENODEV); |
60 | } |
61 | |
62 | dev_dbg(dev, "get\n" ); |
63 | |
64 | return region; |
65 | } |
66 | |
67 | /** |
68 | * fpga_region_put - release a reference to a region |
69 | * |
70 | * @region: FPGA region |
71 | */ |
72 | static void fpga_region_put(struct fpga_region *region) |
73 | { |
74 | struct device *dev = ®ion->dev; |
75 | |
76 | dev_dbg(dev, "put\n" ); |
77 | |
78 | module_put(module: dev->parent->driver->owner); |
79 | put_device(dev); |
80 | mutex_unlock(lock: ®ion->mutex); |
81 | } |
82 | |
83 | /** |
84 | * fpga_region_program_fpga - program FPGA |
85 | * |
86 | * @region: FPGA region |
87 | * |
88 | * Program an FPGA using fpga image info (region->info). |
89 | * If the region has a get_bridges function, the exclusive reference for the |
90 | * bridges will be held if programming succeeds. This is intended to prevent |
91 | * reprogramming the region until the caller considers it safe to do so. |
92 | * The caller will need to call fpga_bridges_put() before attempting to |
93 | * reprogram the region. |
94 | * |
95 | * Return: 0 for success or negative error code. |
96 | */ |
97 | int fpga_region_program_fpga(struct fpga_region *region) |
98 | { |
99 | struct device *dev = ®ion->dev; |
100 | struct fpga_image_info *info = region->info; |
101 | int ret; |
102 | |
103 | region = fpga_region_get(region); |
104 | if (IS_ERR(ptr: region)) { |
105 | dev_err(dev, "failed to get FPGA region\n" ); |
106 | return PTR_ERR(ptr: region); |
107 | } |
108 | |
109 | ret = fpga_mgr_lock(mgr: region->mgr); |
110 | if (ret) { |
111 | dev_err(dev, "FPGA manager is busy\n" ); |
112 | goto err_put_region; |
113 | } |
114 | |
115 | /* |
116 | * In some cases, we already have a list of bridges in the |
117 | * fpga region struct. Or we don't have any bridges. |
118 | */ |
119 | if (region->get_bridges) { |
120 | ret = region->get_bridges(region); |
121 | if (ret) { |
122 | dev_err(dev, "failed to get fpga region bridges\n" ); |
123 | goto err_unlock_mgr; |
124 | } |
125 | } |
126 | |
127 | ret = fpga_bridges_disable(bridge_list: ®ion->bridge_list); |
128 | if (ret) { |
129 | dev_err(dev, "failed to disable bridges\n" ); |
130 | goto err_put_br; |
131 | } |
132 | |
133 | ret = fpga_mgr_load(mgr: region->mgr, info); |
134 | if (ret) { |
135 | dev_err(dev, "failed to load FPGA image\n" ); |
136 | goto err_put_br; |
137 | } |
138 | |
139 | ret = fpga_bridges_enable(bridge_list: ®ion->bridge_list); |
140 | if (ret) { |
141 | dev_err(dev, "failed to enable region bridges\n" ); |
142 | goto err_put_br; |
143 | } |
144 | |
145 | fpga_mgr_unlock(mgr: region->mgr); |
146 | fpga_region_put(region); |
147 | |
148 | return 0; |
149 | |
150 | err_put_br: |
151 | if (region->get_bridges) |
152 | fpga_bridges_put(bridge_list: ®ion->bridge_list); |
153 | err_unlock_mgr: |
154 | fpga_mgr_unlock(mgr: region->mgr); |
155 | err_put_region: |
156 | fpga_region_put(region); |
157 | |
158 | return ret; |
159 | } |
160 | EXPORT_SYMBOL_GPL(fpga_region_program_fpga); |
161 | |
162 | static ssize_t compat_id_show(struct device *dev, |
163 | struct device_attribute *attr, char *buf) |
164 | { |
165 | struct fpga_region *region = to_fpga_region(dev); |
166 | |
167 | if (!region->compat_id) |
168 | return -ENOENT; |
169 | |
170 | return sprintf(buf, fmt: "%016llx%016llx\n" , |
171 | (unsigned long long)region->compat_id->id_h, |
172 | (unsigned long long)region->compat_id->id_l); |
173 | } |
174 | |
175 | static DEVICE_ATTR_RO(compat_id); |
176 | |
177 | static struct attribute *fpga_region_attrs[] = { |
178 | &dev_attr_compat_id.attr, |
179 | NULL, |
180 | }; |
181 | ATTRIBUTE_GROUPS(fpga_region); |
182 | |
183 | /** |
184 | * fpga_region_register_full - create and register an FPGA Region device |
185 | * @parent: device parent |
186 | * @info: parameters for FPGA Region |
187 | * |
188 | * Return: struct fpga_region or ERR_PTR() |
189 | */ |
190 | struct fpga_region * |
191 | fpga_region_register_full(struct device *parent, const struct fpga_region_info *info) |
192 | { |
193 | struct fpga_region *region; |
194 | int id, ret = 0; |
195 | |
196 | if (!info) { |
197 | dev_err(parent, |
198 | "Attempt to register without required info structure\n" ); |
199 | return ERR_PTR(error: -EINVAL); |
200 | } |
201 | |
202 | region = kzalloc(size: sizeof(*region), GFP_KERNEL); |
203 | if (!region) |
204 | return ERR_PTR(error: -ENOMEM); |
205 | |
206 | id = ida_alloc(ida: &fpga_region_ida, GFP_KERNEL); |
207 | if (id < 0) { |
208 | ret = id; |
209 | goto err_free; |
210 | } |
211 | |
212 | region->mgr = info->mgr; |
213 | region->compat_id = info->compat_id; |
214 | region->priv = info->priv; |
215 | region->get_bridges = info->get_bridges; |
216 | |
217 | mutex_init(®ion->mutex); |
218 | INIT_LIST_HEAD(list: ®ion->bridge_list); |
219 | |
220 | region->dev.class = &fpga_region_class; |
221 | region->dev.parent = parent; |
222 | region->dev.of_node = parent->of_node; |
223 | region->dev.id = id; |
224 | |
225 | ret = dev_set_name(dev: ®ion->dev, name: "region%d" , id); |
226 | if (ret) |
227 | goto err_remove; |
228 | |
229 | ret = device_register(dev: ®ion->dev); |
230 | if (ret) { |
231 | put_device(dev: ®ion->dev); |
232 | return ERR_PTR(error: ret); |
233 | } |
234 | |
235 | return region; |
236 | |
237 | err_remove: |
238 | ida_free(&fpga_region_ida, id); |
239 | err_free: |
240 | kfree(objp: region); |
241 | |
242 | return ERR_PTR(error: ret); |
243 | } |
244 | EXPORT_SYMBOL_GPL(fpga_region_register_full); |
245 | |
246 | /** |
247 | * fpga_region_register - create and register an FPGA Region device |
248 | * @parent: device parent |
249 | * @mgr: manager that programs this region |
250 | * @get_bridges: optional function to get bridges to a list |
251 | * |
252 | * This simple version of the register function should be sufficient for most users. |
253 | * The fpga_region_register_full() function is available for users that need to |
254 | * pass additional, optional parameters. |
255 | * |
256 | * Return: struct fpga_region or ERR_PTR() |
257 | */ |
258 | struct fpga_region * |
259 | fpga_region_register(struct device *parent, struct fpga_manager *mgr, |
260 | int (*get_bridges)(struct fpga_region *)) |
261 | { |
262 | struct fpga_region_info info = { 0 }; |
263 | |
264 | info.mgr = mgr; |
265 | info.get_bridges = get_bridges; |
266 | |
267 | return fpga_region_register_full(parent, &info); |
268 | } |
269 | EXPORT_SYMBOL_GPL(fpga_region_register); |
270 | |
271 | /** |
272 | * fpga_region_unregister - unregister an FPGA region |
273 | * @region: FPGA region |
274 | * |
275 | * This function is intended for use in an FPGA region driver's remove function. |
276 | */ |
277 | void fpga_region_unregister(struct fpga_region *region) |
278 | { |
279 | device_unregister(dev: ®ion->dev); |
280 | } |
281 | EXPORT_SYMBOL_GPL(fpga_region_unregister); |
282 | |
283 | static void fpga_region_dev_release(struct device *dev) |
284 | { |
285 | struct fpga_region *region = to_fpga_region(dev); |
286 | |
287 | ida_free(&fpga_region_ida, id: region->dev.id); |
288 | kfree(objp: region); |
289 | } |
290 | |
291 | static const struct class fpga_region_class = { |
292 | .name = "fpga_region" , |
293 | .dev_groups = fpga_region_groups, |
294 | .dev_release = fpga_region_dev_release, |
295 | }; |
296 | |
297 | /** |
298 | * fpga_region_init - creates the fpga_region class. |
299 | * |
300 | * Return: 0 on success or ERR_PTR() on error. |
301 | */ |
302 | static int __init fpga_region_init(void) |
303 | { |
304 | return class_register(class: &fpga_region_class); |
305 | } |
306 | |
307 | static void __exit fpga_region_exit(void) |
308 | { |
309 | class_unregister(class: &fpga_region_class); |
310 | ida_destroy(ida: &fpga_region_ida); |
311 | } |
312 | |
313 | subsys_initcall(fpga_region_init); |
314 | module_exit(fpga_region_exit); |
315 | |
316 | MODULE_DESCRIPTION("FPGA Region" ); |
317 | MODULE_AUTHOR("Alan Tull <atull@kernel.org>" ); |
318 | MODULE_LICENSE("GPL v2" ); |
319 | |