diff options
Diffstat (limited to 'drivers/fpga/of-fpga-region.c')
-rw-r--r-- | drivers/fpga/of-fpga-region.c | 496 |
1 files changed, 496 insertions, 0 deletions
diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c new file mode 100644 index 000000000000..1533506ef0e4 --- /dev/null +++ b/drivers/fpga/of-fpga-region.c | |||
@@ -0,0 +1,496 @@ | |||
1 | /* | ||
2 | * FPGA Region - Device Tree support for FPGA programming under Linux | ||
3 | * | ||
4 | * Copyright (C) 2013-2016 Altera Corporation | ||
5 | * Copyright (C) 2017 Intel Corporation | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms and conditions of the GNU General Public License, | ||
9 | * version 2, as published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
14 | * more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along with | ||
17 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
18 | */ | ||
19 | |||
20 | #include <linux/fpga/fpga-bridge.h> | ||
21 | #include <linux/fpga/fpga-mgr.h> | ||
22 | #include <linux/fpga/fpga-region.h> | ||
23 | #include <linux/idr.h> | ||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/list.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/of_platform.h> | ||
28 | #include <linux/slab.h> | ||
29 | #include <linux/spinlock.h> | ||
30 | |||
31 | static const struct of_device_id fpga_region_of_match[] = { | ||
32 | { .compatible = "fpga-region", }, | ||
33 | {}, | ||
34 | }; | ||
35 | MODULE_DEVICE_TABLE(of, fpga_region_of_match); | ||
36 | |||
37 | static int fpga_region_of_node_match(struct device *dev, const void *data) | ||
38 | { | ||
39 | return dev->of_node == data; | ||
40 | } | ||
41 | |||
42 | /** | ||
43 | * of_fpga_region_find - find FPGA region | ||
44 | * @np: device node of FPGA Region | ||
45 | * | ||
46 | * Caller will need to put_device(®ion->dev) when done. | ||
47 | * | ||
48 | * Returns FPGA Region struct or NULL | ||
49 | */ | ||
50 | static struct fpga_region *of_fpga_region_find(struct device_node *np) | ||
51 | { | ||
52 | return fpga_region_class_find(NULL, np, fpga_region_of_node_match); | ||
53 | } | ||
54 | |||
55 | /** | ||
56 | * of_fpga_region_get_mgr - get reference for FPGA manager | ||
57 | * @np: device node of FPGA region | ||
58 | * | ||
59 | * Get FPGA Manager from "fpga-mgr" property or from ancestor region. | ||
60 | * | ||
61 | * Caller should call fpga_mgr_put() when done with manager. | ||
62 | * | ||
63 | * Return: fpga manager struct or IS_ERR() condition containing error code. | ||
64 | */ | ||
65 | static struct fpga_manager *of_fpga_region_get_mgr(struct device_node *np) | ||
66 | { | ||
67 | struct device_node *mgr_node; | ||
68 | struct fpga_manager *mgr; | ||
69 | |||
70 | of_node_get(np); | ||
71 | while (np) { | ||
72 | if (of_device_is_compatible(np, "fpga-region")) { | ||
73 | mgr_node = of_parse_phandle(np, "fpga-mgr", 0); | ||
74 | if (mgr_node) { | ||
75 | mgr = of_fpga_mgr_get(mgr_node); | ||
76 | of_node_put(np); | ||
77 | return mgr; | ||
78 | } | ||
79 | } | ||
80 | np = of_get_next_parent(np); | ||
81 | } | ||
82 | of_node_put(np); | ||
83 | |||
84 | return ERR_PTR(-EINVAL); | ||
85 | } | ||
86 | |||
87 | /** | ||
88 | * of_fpga_region_get_bridges - create a list of bridges | ||
89 | * @region: FPGA region | ||
90 | * | ||
91 | * Create a list of bridges including the parent bridge and the bridges | ||
92 | * specified by "fpga-bridges" property. Note that the | ||
93 | * fpga_bridges_enable/disable/put functions are all fine with an empty list | ||
94 | * if that happens. | ||
95 | * | ||
96 | * Caller should call fpga_bridges_put(®ion->bridge_list) when | ||
97 | * done with the bridges. | ||
98 | * | ||
99 | * Return 0 for success (even if there are no bridges specified) | ||
100 | * or -EBUSY if any of the bridges are in use. | ||
101 | */ | ||
102 | static int of_fpga_region_get_bridges(struct fpga_region *region) | ||
103 | { | ||
104 | struct device *dev = ®ion->dev; | ||
105 | struct device_node *region_np = dev->of_node; | ||
106 | struct fpga_image_info *info = region->info; | ||
107 | struct device_node *br, *np, *parent_br = NULL; | ||
108 | int i, ret; | ||
109 | |||
110 | /* If parent is a bridge, add to list */ | ||
111 | ret = of_fpga_bridge_get_to_list(region_np->parent, info, | ||
112 | ®ion->bridge_list); | ||
113 | |||
114 | /* -EBUSY means parent is a bridge that is under use. Give up. */ | ||
115 | if (ret == -EBUSY) | ||
116 | return ret; | ||
117 | |||
118 | /* Zero return code means parent was a bridge and was added to list. */ | ||
119 | if (!ret) | ||
120 | parent_br = region_np->parent; | ||
121 | |||
122 | /* If overlay has a list of bridges, use it. */ | ||
123 | if (of_parse_phandle(info->overlay, "fpga-bridges", 0)) | ||
124 | np = info->overlay; | ||
125 | else | ||
126 | np = region_np; | ||
127 | |||
128 | for (i = 0; ; i++) { | ||
129 | br = of_parse_phandle(np, "fpga-bridges", i); | ||
130 | if (!br) | ||
131 | break; | ||
132 | |||
133 | /* If parent bridge is in list, skip it. */ | ||
134 | if (br == parent_br) | ||
135 | continue; | ||
136 | |||
137 | /* If node is a bridge, get it and add to list */ | ||
138 | ret = of_fpga_bridge_get_to_list(br, info, | ||
139 | ®ion->bridge_list); | ||
140 | |||
141 | /* If any of the bridges are in use, give up */ | ||
142 | if (ret == -EBUSY) { | ||
143 | fpga_bridges_put(®ion->bridge_list); | ||
144 | return -EBUSY; | ||
145 | } | ||
146 | } | ||
147 | |||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | /** | ||
152 | * child_regions_with_firmware | ||
153 | * @overlay: device node of the overlay | ||
154 | * | ||
155 | * If the overlay adds child FPGA regions, they are not allowed to have | ||
156 | * firmware-name property. | ||
157 | * | ||
158 | * Return 0 for OK or -EINVAL if child FPGA region adds firmware-name. | ||
159 | */ | ||
160 | static int child_regions_with_firmware(struct device_node *overlay) | ||
161 | { | ||
162 | struct device_node *child_region; | ||
163 | const char *child_firmware_name; | ||
164 | int ret = 0; | ||
165 | |||
166 | of_node_get(overlay); | ||
167 | |||
168 | child_region = of_find_matching_node(overlay, fpga_region_of_match); | ||
169 | while (child_region) { | ||
170 | if (!of_property_read_string(child_region, "firmware-name", | ||
171 | &child_firmware_name)) { | ||
172 | ret = -EINVAL; | ||
173 | break; | ||
174 | } | ||
175 | child_region = of_find_matching_node(child_region, | ||
176 | fpga_region_of_match); | ||
177 | } | ||
178 | |||
179 | of_node_put(child_region); | ||
180 | |||
181 | if (ret) | ||
182 | pr_err("firmware-name not allowed in child FPGA region: %pOF", | ||
183 | child_region); | ||
184 | |||
185 | return ret; | ||
186 | } | ||
187 | |||
188 | /** | ||
189 | * of_fpga_region_parse_ov - parse and check overlay applied to region | ||
190 | * | ||
191 | * @region: FPGA region | ||
192 | * @overlay: overlay applied to the FPGA region | ||
193 | * | ||
194 | * Given an overlay applied to a FPGA region, parse the FPGA image specific | ||
195 | * info in the overlay and do some checking. | ||
196 | * | ||
197 | * Returns: | ||
198 | * NULL if overlay doesn't direct us to program the FPGA. | ||
199 | * fpga_image_info struct if there is an image to program. | ||
200 | * error code for invalid overlay. | ||
201 | */ | ||
202 | static struct fpga_image_info *of_fpga_region_parse_ov( | ||
203 | struct fpga_region *region, | ||
204 | struct device_node *overlay) | ||
205 | { | ||
206 | struct device *dev = ®ion->dev; | ||
207 | struct fpga_image_info *info; | ||
208 | const char *firmware_name; | ||
209 | int ret; | ||
210 | |||
211 | if (region->info) { | ||
212 | dev_err(dev, "Region already has overlay applied.\n"); | ||
213 | return ERR_PTR(-EINVAL); | ||
214 | } | ||
215 | |||
216 | /* | ||
217 | * Reject overlay if child FPGA Regions added in the overlay have | ||
218 | * firmware-name property (would mean that an FPGA region that has | ||
219 | * not been added to the live tree yet is doing FPGA programming). | ||
220 | */ | ||
221 | ret = child_regions_with_firmware(overlay); | ||
222 | if (ret) | ||
223 | return ERR_PTR(ret); | ||
224 | |||
225 | info = fpga_image_info_alloc(dev); | ||
226 | if (!info) | ||
227 | return ERR_PTR(-ENOMEM); | ||
228 | |||
229 | info->overlay = overlay; | ||
230 | |||
231 | /* Read FPGA region properties from the overlay */ | ||
232 | if (of_property_read_bool(overlay, "partial-fpga-config")) | ||
233 | info->flags |= FPGA_MGR_PARTIAL_RECONFIG; | ||
234 | |||
235 | if (of_property_read_bool(overlay, "external-fpga-config")) | ||
236 | info->flags |= FPGA_MGR_EXTERNAL_CONFIG; | ||
237 | |||
238 | if (of_property_read_bool(overlay, "encrypted-fpga-config")) | ||
239 | info->flags |= FPGA_MGR_ENCRYPTED_BITSTREAM; | ||
240 | |||
241 | if (!of_property_read_string(overlay, "firmware-name", | ||
242 | &firmware_name)) { | ||
243 | info->firmware_name = devm_kstrdup(dev, firmware_name, | ||
244 | GFP_KERNEL); | ||
245 | if (!info->firmware_name) | ||
246 | return ERR_PTR(-ENOMEM); | ||
247 | } | ||
248 | |||
249 | of_property_read_u32(overlay, "region-unfreeze-timeout-us", | ||
250 | &info->enable_timeout_us); | ||
251 | |||
252 | of_property_read_u32(overlay, "region-freeze-timeout-us", | ||
253 | &info->disable_timeout_us); | ||
254 | |||
255 | of_property_read_u32(overlay, "config-complete-timeout-us", | ||
256 | &info->config_complete_timeout_us); | ||
257 | |||
258 | /* If overlay is not programming the FPGA, don't need FPGA image info */ | ||
259 | if (!info->firmware_name) { | ||
260 | ret = 0; | ||
261 | goto ret_no_info; | ||
262 | } | ||
263 | |||
264 | /* | ||
265 | * If overlay informs us FPGA was externally programmed, specifying | ||
266 | * firmware here would be ambiguous. | ||
267 | */ | ||
268 | if (info->flags & FPGA_MGR_EXTERNAL_CONFIG) { | ||
269 | dev_err(dev, "error: specified firmware and external-fpga-config"); | ||
270 | ret = -EINVAL; | ||
271 | goto ret_no_info; | ||
272 | } | ||
273 | |||
274 | return info; | ||
275 | ret_no_info: | ||
276 | fpga_image_info_free(info); | ||
277 | return ERR_PTR(ret); | ||
278 | } | ||
279 | |||
280 | /** | ||
281 | * of_fpga_region_notify_pre_apply - pre-apply overlay notification | ||
282 | * | ||
283 | * @region: FPGA region that the overlay was applied to | ||
284 | * @nd: overlay notification data | ||
285 | * | ||
286 | * Called when an overlay targeted to a FPGA Region is about to be applied. | ||
287 | * Parses the overlay for properties that influence how the FPGA will be | ||
288 | * programmed and does some checking. If the checks pass, programs the FPGA. | ||
289 | * If the checks fail, overlay is rejected and does not get added to the | ||
290 | * live tree. | ||
291 | * | ||
292 | * Returns 0 for success or negative error code for failure. | ||
293 | */ | ||
294 | static int of_fpga_region_notify_pre_apply(struct fpga_region *region, | ||
295 | struct of_overlay_notify_data *nd) | ||
296 | { | ||
297 | struct device *dev = ®ion->dev; | ||
298 | struct fpga_image_info *info; | ||
299 | int ret; | ||
300 | |||
301 | if (region->info) { | ||
302 | dev_err(dev, "Region already has overlay applied.\n"); | ||
303 | return -EINVAL; | ||
304 | } | ||
305 | |||
306 | info = of_fpga_region_parse_ov(region, nd->overlay); | ||
307 | if (IS_ERR(info)) | ||
308 | return PTR_ERR(info); | ||
309 | |||
310 | if (!info) | ||
311 | return 0; | ||
312 | |||
313 | region->info = info; | ||
314 | ret = fpga_region_program_fpga(region); | ||
315 | if (ret) { | ||
316 | /* error; reject overlay */ | ||
317 | fpga_image_info_free(info); | ||
318 | region->info = NULL; | ||
319 | } | ||
320 | |||
321 | return ret; | ||
322 | } | ||
323 | |||
324 | /** | ||
325 | * of_fpga_region_notify_post_remove - post-remove overlay notification | ||
326 | * | ||
327 | * @region: FPGA region that was targeted by the overlay that was removed | ||
328 | * @nd: overlay notification data | ||
329 | * | ||
330 | * Called after an overlay has been removed if the overlay's target was a | ||
331 | * FPGA region. | ||
332 | */ | ||
333 | static void of_fpga_region_notify_post_remove(struct fpga_region *region, | ||
334 | struct of_overlay_notify_data *nd) | ||
335 | { | ||
336 | fpga_bridges_disable(®ion->bridge_list); | ||
337 | fpga_bridges_put(®ion->bridge_list); | ||
338 | fpga_image_info_free(region->info); | ||
339 | region->info = NULL; | ||
340 | } | ||
341 | |||
342 | /** | ||
343 | * of_fpga_region_notify - reconfig notifier for dynamic DT changes | ||
344 | * @nb: notifier block | ||
345 | * @action: notifier action | ||
346 | * @arg: reconfig data | ||
347 | * | ||
348 | * This notifier handles programming a FPGA when a "firmware-name" property is | ||
349 | * added to a fpga-region. | ||
350 | * | ||
351 | * Returns NOTIFY_OK or error if FPGA programming fails. | ||
352 | */ | ||
353 | static int of_fpga_region_notify(struct notifier_block *nb, | ||
354 | unsigned long action, void *arg) | ||
355 | { | ||
356 | struct of_overlay_notify_data *nd = arg; | ||
357 | struct fpga_region *region; | ||
358 | int ret; | ||
359 | |||
360 | switch (action) { | ||
361 | case OF_OVERLAY_PRE_APPLY: | ||
362 | pr_debug("%s OF_OVERLAY_PRE_APPLY\n", __func__); | ||
363 | break; | ||
364 | case OF_OVERLAY_POST_APPLY: | ||
365 | pr_debug("%s OF_OVERLAY_POST_APPLY\n", __func__); | ||
366 | return NOTIFY_OK; /* not for us */ | ||
367 | case OF_OVERLAY_PRE_REMOVE: | ||
368 | pr_debug("%s OF_OVERLAY_PRE_REMOVE\n", __func__); | ||
369 | return NOTIFY_OK; /* not for us */ | ||
370 | case OF_OVERLAY_POST_REMOVE: | ||
371 | pr_debug("%s OF_OVERLAY_POST_REMOVE\n", __func__); | ||
372 | break; | ||
373 | default: /* should not happen */ | ||
374 | return NOTIFY_OK; | ||
375 | } | ||
376 | |||
377 | region = of_fpga_region_find(nd->target); | ||
378 | if (!region) | ||
379 | return NOTIFY_OK; | ||
380 | |||
381 | ret = 0; | ||
382 | switch (action) { | ||
383 | case OF_OVERLAY_PRE_APPLY: | ||
384 | ret = of_fpga_region_notify_pre_apply(region, nd); | ||
385 | break; | ||
386 | |||
387 | case OF_OVERLAY_POST_REMOVE: | ||
388 | of_fpga_region_notify_post_remove(region, nd); | ||
389 | break; | ||
390 | } | ||
391 | |||
392 | put_device(®ion->dev); | ||
393 | |||
394 | if (ret) | ||
395 | return notifier_from_errno(ret); | ||
396 | |||
397 | return NOTIFY_OK; | ||
398 | } | ||
399 | |||
400 | static struct notifier_block fpga_region_of_nb = { | ||
401 | .notifier_call = of_fpga_region_notify, | ||
402 | }; | ||
403 | |||
404 | static int of_fpga_region_probe(struct platform_device *pdev) | ||
405 | { | ||
406 | struct device *dev = &pdev->dev; | ||
407 | struct device_node *np = dev->of_node; | ||
408 | struct fpga_region *region; | ||
409 | struct fpga_manager *mgr; | ||
410 | int ret; | ||
411 | |||
412 | /* Find the FPGA mgr specified by region or parent region. */ | ||
413 | mgr = of_fpga_region_get_mgr(np); | ||
414 | if (IS_ERR(mgr)) | ||
415 | return -EPROBE_DEFER; | ||
416 | |||
417 | region = devm_kzalloc(dev, sizeof(*region), GFP_KERNEL); | ||
418 | if (!region) { | ||
419 | ret = -ENOMEM; | ||
420 | goto eprobe_mgr_put; | ||
421 | } | ||
422 | |||
423 | region->mgr = mgr; | ||
424 | |||
425 | /* Specify how to get bridges for this type of region. */ | ||
426 | region->get_bridges = of_fpga_region_get_bridges; | ||
427 | |||
428 | ret = fpga_region_register(dev, region); | ||
429 | if (ret) | ||
430 | goto eprobe_mgr_put; | ||
431 | |||
432 | of_platform_populate(np, fpga_region_of_match, NULL, ®ion->dev); | ||
433 | |||
434 | dev_info(dev, "FPGA Region probed\n"); | ||
435 | |||
436 | return 0; | ||
437 | |||
438 | eprobe_mgr_put: | ||
439 | fpga_mgr_put(mgr); | ||
440 | return ret; | ||
441 | } | ||
442 | |||
443 | static int of_fpga_region_remove(struct platform_device *pdev) | ||
444 | { | ||
445 | struct fpga_region *region = platform_get_drvdata(pdev); | ||
446 | |||
447 | fpga_region_unregister(region); | ||
448 | fpga_mgr_put(region->mgr); | ||
449 | |||
450 | return 0; | ||
451 | } | ||
452 | |||
453 | static struct platform_driver of_fpga_region_driver = { | ||
454 | .probe = of_fpga_region_probe, | ||
455 | .remove = of_fpga_region_remove, | ||
456 | .driver = { | ||
457 | .name = "of-fpga-region", | ||
458 | .of_match_table = of_match_ptr(fpga_region_of_match), | ||
459 | }, | ||
460 | }; | ||
461 | |||
462 | /** | ||
463 | * fpga_region_init - init function for fpga_region class | ||
464 | * Creates the fpga_region class and registers a reconfig notifier. | ||
465 | */ | ||
466 | static int __init of_fpga_region_init(void) | ||
467 | { | ||
468 | int ret; | ||
469 | |||
470 | ret = of_overlay_notifier_register(&fpga_region_of_nb); | ||
471 | if (ret) | ||
472 | return ret; | ||
473 | |||
474 | ret = platform_driver_register(&of_fpga_region_driver); | ||
475 | if (ret) | ||
476 | goto err_plat; | ||
477 | |||
478 | return 0; | ||
479 | |||
480 | err_plat: | ||
481 | of_overlay_notifier_unregister(&fpga_region_of_nb); | ||
482 | return ret; | ||
483 | } | ||
484 | |||
485 | static void __exit of_fpga_region_exit(void) | ||
486 | { | ||
487 | platform_driver_unregister(&of_fpga_region_driver); | ||
488 | of_overlay_notifier_unregister(&fpga_region_of_nb); | ||
489 | } | ||
490 | |||
491 | subsys_initcall(of_fpga_region_init); | ||
492 | module_exit(of_fpga_region_exit); | ||
493 | |||
494 | MODULE_DESCRIPTION("FPGA Region"); | ||
495 | MODULE_AUTHOR("Alan Tull <atull@kernel.org>"); | ||
496 | MODULE_LICENSE("GPL v2"); | ||