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