diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-02-01 13:31:17 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-02-01 13:31:17 -0500 |
commit | f6cff79f1d122f78a4b35bf4b2f0112afcd89ea4 (patch) | |
tree | cf3a38576f9adbb3860982c25f72aebed2bb541a /drivers/fpga/of-fpga-region.c | |
parent | 47fcc0360cfb3fe82e4daddacad3c1cd80b0b75d (diff) | |
parent | 9ff6576e124b1227c27c1da43fe5f8ee908263e0 (diff) |
Merge tag 'char-misc-4.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char/misc driver updates from Greg KH:
"Here is the big pull request for char/misc drivers for 4.16-rc1.
There's a lot of stuff in here. Three new driver subsystems were added
for various types of hardware busses:
- siox
- slimbus
- soundwire
as well as a new vboxguest subsystem for the VirtualBox hypervisor
drivers.
There's also big updates from the FPGA subsystem, lots of Android
binder fixes, the usual handful of hyper-v updates, and lots of other
smaller driver updates.
All of these have been in linux-next for a long time, with no reported
issues"
* tag 'char-misc-4.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (155 commits)
char: lp: use true or false for boolean values
android: binder: use VM_ALLOC to get vm area
android: binder: Use true and false for boolean values
lkdtm: fix handle_irq_event symbol for INT_HW_IRQ_EN
EISA: Delete error message for a failed memory allocation in eisa_probe()
EISA: Whitespace cleanup
misc: remove AVR32 dependencies
virt: vbox: Add error mapping for VERR_INVALID_NAME and VERR_NO_MORE_FILES
soundwire: Fix a signedness bug
uio_hv_generic: fix new type mismatch warnings
uio_hv_generic: fix type mismatch warnings
auxdisplay: img-ascii-lcd: add missing MODULE_DESCRIPTION/AUTHOR/LICENSE
uio_hv_generic: add rescind support
uio_hv_generic: check that host supports monitor page
uio_hv_generic: create send and receive buffers
uio: document uio_hv_generic regions
doc: fix documentation about uio_hv_generic
vmbus: add monitor_id and subchannel_id to sysfs per channel
vmbus: fix ABI documentation
uio_hv_generic: use ISR callback method
...
Diffstat (limited to 'drivers/fpga/of-fpga-region.c')
-rw-r--r-- | drivers/fpga/of-fpga-region.c | 504 |
1 files changed, 504 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..119ff75522f1 --- /dev/null +++ b/drivers/fpga/of-fpga-region.c | |||
@@ -0,0 +1,504 @@ | |||
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(mgr_node); | ||
77 | of_node_put(np); | ||
78 | return mgr; | ||
79 | } | ||
80 | } | ||
81 | np = of_get_next_parent(np); | ||
82 | } | ||
83 | of_node_put(np); | ||
84 | |||
85 | return ERR_PTR(-EINVAL); | ||
86 | } | ||
87 | |||
88 | /** | ||
89 | * of_fpga_region_get_bridges - create a list of bridges | ||
90 | * @region: FPGA region | ||
91 | * | ||
92 | * Create a list of bridges including the parent bridge and the bridges | ||
93 | * specified by "fpga-bridges" property. Note that the | ||
94 | * fpga_bridges_enable/disable/put functions are all fine with an empty list | ||
95 | * if that happens. | ||
96 | * | ||
97 | * Caller should call fpga_bridges_put(®ion->bridge_list) when | ||
98 | * done with the bridges. | ||
99 | * | ||
100 | * Return 0 for success (even if there are no bridges specified) | ||
101 | * or -EBUSY if any of the bridges are in use. | ||
102 | */ | ||
103 | static int of_fpga_region_get_bridges(struct fpga_region *region) | ||
104 | { | ||
105 | struct device *dev = ®ion->dev; | ||
106 | struct device_node *region_np = dev->of_node; | ||
107 | struct fpga_image_info *info = region->info; | ||
108 | struct device_node *br, *np, *parent_br = NULL; | ||
109 | int i, ret; | ||
110 | |||
111 | /* If parent is a bridge, add to list */ | ||
112 | ret = of_fpga_bridge_get_to_list(region_np->parent, info, | ||
113 | ®ion->bridge_list); | ||
114 | |||
115 | /* -EBUSY means parent is a bridge that is under use. Give up. */ | ||
116 | if (ret == -EBUSY) | ||
117 | return ret; | ||
118 | |||
119 | /* Zero return code means parent was a bridge and was added to list. */ | ||
120 | if (!ret) | ||
121 | parent_br = region_np->parent; | ||
122 | |||
123 | /* If overlay has a list of bridges, use it. */ | ||
124 | br = of_parse_phandle(info->overlay, "fpga-bridges", 0); | ||
125 | if (br) { | ||
126 | of_node_put(br); | ||
127 | np = info->overlay; | ||
128 | } else { | ||
129 | np = region_np; | ||
130 | } | ||
131 | |||
132 | for (i = 0; ; i++) { | ||
133 | br = of_parse_phandle(np, "fpga-bridges", i); | ||
134 | if (!br) | ||
135 | break; | ||
136 | |||
137 | /* If parent bridge is in list, skip it. */ | ||
138 | if (br == parent_br) { | ||
139 | of_node_put(br); | ||
140 | continue; | ||
141 | } | ||
142 | |||
143 | /* If node is a bridge, get it and add to list */ | ||
144 | ret = of_fpga_bridge_get_to_list(br, info, | ||
145 | ®ion->bridge_list); | ||
146 | of_node_put(br); | ||
147 | |||
148 | /* If any of the bridges are in use, give up */ | ||
149 | if (ret == -EBUSY) { | ||
150 | fpga_bridges_put(®ion->bridge_list); | ||
151 | return -EBUSY; | ||
152 | } | ||
153 | } | ||
154 | |||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | /** | ||
159 | * child_regions_with_firmware | ||
160 | * @overlay: device node of the overlay | ||
161 | * | ||
162 | * If the overlay adds child FPGA regions, they are not allowed to have | ||
163 | * firmware-name property. | ||
164 | * | ||
165 | * Return 0 for OK or -EINVAL if child FPGA region adds firmware-name. | ||
166 | */ | ||
167 | static int child_regions_with_firmware(struct device_node *overlay) | ||
168 | { | ||
169 | struct device_node *child_region; | ||
170 | const char *child_firmware_name; | ||
171 | int ret = 0; | ||
172 | |||
173 | of_node_get(overlay); | ||
174 | |||
175 | child_region = of_find_matching_node(overlay, fpga_region_of_match); | ||
176 | while (child_region) { | ||
177 | if (!of_property_read_string(child_region, "firmware-name", | ||
178 | &child_firmware_name)) { | ||
179 | ret = -EINVAL; | ||
180 | break; | ||
181 | } | ||
182 | child_region = of_find_matching_node(child_region, | ||
183 | fpga_region_of_match); | ||
184 | } | ||
185 | |||
186 | of_node_put(child_region); | ||
187 | |||
188 | if (ret) | ||
189 | pr_err("firmware-name not allowed in child FPGA region: %pOF", | ||
190 | child_region); | ||
191 | |||
192 | return ret; | ||
193 | } | ||
194 | |||
195 | /** | ||
196 | * of_fpga_region_parse_ov - parse and check overlay applied to region | ||
197 | * | ||
198 | * @region: FPGA region | ||
199 | * @overlay: overlay applied to the FPGA region | ||
200 | * | ||
201 | * Given an overlay applied to a FPGA region, parse the FPGA image specific | ||
202 | * info in the overlay and do some checking. | ||
203 | * | ||
204 | * Returns: | ||
205 | * NULL if overlay doesn't direct us to program the FPGA. | ||
206 | * fpga_image_info struct if there is an image to program. | ||
207 | * error code for invalid overlay. | ||
208 | */ | ||
209 | static struct fpga_image_info *of_fpga_region_parse_ov( | ||
210 | struct fpga_region *region, | ||
211 | struct device_node *overlay) | ||
212 | { | ||
213 | struct device *dev = ®ion->dev; | ||
214 | struct fpga_image_info *info; | ||
215 | const char *firmware_name; | ||
216 | int ret; | ||
217 | |||
218 | if (region->info) { | ||
219 | dev_err(dev, "Region already has overlay applied.\n"); | ||
220 | return ERR_PTR(-EINVAL); | ||
221 | } | ||
222 | |||
223 | /* | ||
224 | * Reject overlay if child FPGA Regions added in the overlay have | ||
225 | * firmware-name property (would mean that an FPGA region that has | ||
226 | * not been added to the live tree yet is doing FPGA programming). | ||
227 | */ | ||
228 | ret = child_regions_with_firmware(overlay); | ||
229 | if (ret) | ||
230 | return ERR_PTR(ret); | ||
231 | |||
232 | info = fpga_image_info_alloc(dev); | ||
233 | if (!info) | ||
234 | return ERR_PTR(-ENOMEM); | ||
235 | |||
236 | info->overlay = overlay; | ||
237 | |||
238 | /* Read FPGA region properties from the overlay */ | ||
239 | if (of_property_read_bool(overlay, "partial-fpga-config")) | ||
240 | info->flags |= FPGA_MGR_PARTIAL_RECONFIG; | ||
241 | |||
242 | if (of_property_read_bool(overlay, "external-fpga-config")) | ||
243 | info->flags |= FPGA_MGR_EXTERNAL_CONFIG; | ||
244 | |||
245 | if (of_property_read_bool(overlay, "encrypted-fpga-config")) | ||
246 | info->flags |= FPGA_MGR_ENCRYPTED_BITSTREAM; | ||
247 | |||
248 | if (!of_property_read_string(overlay, "firmware-name", | ||
249 | &firmware_name)) { | ||
250 | info->firmware_name = devm_kstrdup(dev, firmware_name, | ||
251 | GFP_KERNEL); | ||
252 | if (!info->firmware_name) | ||
253 | return ERR_PTR(-ENOMEM); | ||
254 | } | ||
255 | |||
256 | of_property_read_u32(overlay, "region-unfreeze-timeout-us", | ||
257 | &info->enable_timeout_us); | ||
258 | |||
259 | of_property_read_u32(overlay, "region-freeze-timeout-us", | ||
260 | &info->disable_timeout_us); | ||
261 | |||
262 | of_property_read_u32(overlay, "config-complete-timeout-us", | ||
263 | &info->config_complete_timeout_us); | ||
264 | |||
265 | /* If overlay is not programming the FPGA, don't need FPGA image info */ | ||
266 | if (!info->firmware_name) { | ||
267 | ret = 0; | ||
268 | goto ret_no_info; | ||
269 | } | ||
270 | |||
271 | /* | ||
272 | * If overlay informs us FPGA was externally programmed, specifying | ||
273 | * firmware here would be ambiguous. | ||
274 | */ | ||
275 | if (info->flags & FPGA_MGR_EXTERNAL_CONFIG) { | ||
276 | dev_err(dev, "error: specified firmware and external-fpga-config"); | ||
277 | ret = -EINVAL; | ||
278 | goto ret_no_info; | ||
279 | } | ||
280 | |||
281 | return info; | ||
282 | ret_no_info: | ||
283 | fpga_image_info_free(info); | ||
284 | return ERR_PTR(ret); | ||
285 | } | ||
286 | |||
287 | /** | ||
288 | * of_fpga_region_notify_pre_apply - pre-apply overlay notification | ||
289 | * | ||
290 | * @region: FPGA region that the overlay was applied to | ||
291 | * @nd: overlay notification data | ||
292 | * | ||
293 | * Called when an overlay targeted to a FPGA Region is about to be applied. | ||
294 | * Parses the overlay for properties that influence how the FPGA will be | ||
295 | * programmed and does some checking. If the checks pass, programs the FPGA. | ||
296 | * If the checks fail, overlay is rejected and does not get added to the | ||
297 | * live tree. | ||
298 | * | ||
299 | * Returns 0 for success or negative error code for failure. | ||
300 | */ | ||
301 | static int of_fpga_region_notify_pre_apply(struct fpga_region *region, | ||
302 | struct of_overlay_notify_data *nd) | ||
303 | { | ||
304 | struct device *dev = ®ion->dev; | ||
305 | struct fpga_image_info *info; | ||
306 | int ret; | ||
307 | |||
308 | info = of_fpga_region_parse_ov(region, nd->overlay); | ||
309 | if (IS_ERR(info)) | ||
310 | return PTR_ERR(info); | ||
311 | |||
312 | /* If overlay doesn't program the FPGA, accept it anyway. */ | ||
313 | if (!info) | ||
314 | return 0; | ||
315 | |||
316 | if (region->info) { | ||
317 | dev_err(dev, "Region already has overlay applied.\n"); | ||
318 | return -EINVAL; | ||
319 | } | ||
320 | |||
321 | region->info = info; | ||
322 | ret = fpga_region_program_fpga(region); | ||
323 | if (ret) { | ||
324 | /* error; reject overlay */ | ||
325 | fpga_image_info_free(info); | ||
326 | region->info = NULL; | ||
327 | } | ||
328 | |||
329 | return ret; | ||
330 | } | ||
331 | |||
332 | /** | ||
333 | * of_fpga_region_notify_post_remove - post-remove overlay notification | ||
334 | * | ||
335 | * @region: FPGA region that was targeted by the overlay that was removed | ||
336 | * @nd: overlay notification data | ||
337 | * | ||
338 | * Called after an overlay has been removed if the overlay's target was a | ||
339 | * FPGA region. | ||
340 | */ | ||
341 | static void of_fpga_region_notify_post_remove(struct fpga_region *region, | ||
342 | struct of_overlay_notify_data *nd) | ||
343 | { | ||
344 | fpga_bridges_disable(®ion->bridge_list); | ||
345 | fpga_bridges_put(®ion->bridge_list); | ||
346 | fpga_image_info_free(region->info); | ||
347 | region->info = NULL; | ||
348 | } | ||
349 | |||
350 | /** | ||
351 | * of_fpga_region_notify - reconfig notifier for dynamic DT changes | ||
352 | * @nb: notifier block | ||
353 | * @action: notifier action | ||
354 | * @arg: reconfig data | ||
355 | * | ||
356 | * This notifier handles programming a FPGA when a "firmware-name" property is | ||
357 | * added to a fpga-region. | ||
358 | * | ||
359 | * Returns NOTIFY_OK or error if FPGA programming fails. | ||
360 | */ | ||
361 | static int of_fpga_region_notify(struct notifier_block *nb, | ||
362 | unsigned long action, void *arg) | ||
363 | { | ||
364 | struct of_overlay_notify_data *nd = arg; | ||
365 | struct fpga_region *region; | ||
366 | int ret; | ||
367 | |||
368 | switch (action) { | ||
369 | case OF_OVERLAY_PRE_APPLY: | ||
370 | pr_debug("%s OF_OVERLAY_PRE_APPLY\n", __func__); | ||
371 | break; | ||
372 | case OF_OVERLAY_POST_APPLY: | ||
373 | pr_debug("%s OF_OVERLAY_POST_APPLY\n", __func__); | ||
374 | return NOTIFY_OK; /* not for us */ | ||
375 | case OF_OVERLAY_PRE_REMOVE: | ||
376 | pr_debug("%s OF_OVERLAY_PRE_REMOVE\n", __func__); | ||
377 | return NOTIFY_OK; /* not for us */ | ||
378 | case OF_OVERLAY_POST_REMOVE: | ||
379 | pr_debug("%s OF_OVERLAY_POST_REMOVE\n", __func__); | ||
380 | break; | ||
381 | default: /* should not happen */ | ||
382 | return NOTIFY_OK; | ||
383 | } | ||
384 | |||
385 | region = of_fpga_region_find(nd->target); | ||
386 | if (!region) | ||
387 | return NOTIFY_OK; | ||
388 | |||
389 | ret = 0; | ||
390 | switch (action) { | ||
391 | case OF_OVERLAY_PRE_APPLY: | ||
392 | ret = of_fpga_region_notify_pre_apply(region, nd); | ||
393 | break; | ||
394 | |||
395 | case OF_OVERLAY_POST_REMOVE: | ||
396 | of_fpga_region_notify_post_remove(region, nd); | ||
397 | break; | ||
398 | } | ||
399 | |||
400 | put_device(®ion->dev); | ||
401 | |||
402 | if (ret) | ||
403 | return notifier_from_errno(ret); | ||
404 | |||
405 | return NOTIFY_OK; | ||
406 | } | ||
407 | |||
408 | static struct notifier_block fpga_region_of_nb = { | ||
409 | .notifier_call = of_fpga_region_notify, | ||
410 | }; | ||
411 | |||
412 | static int of_fpga_region_probe(struct platform_device *pdev) | ||
413 | { | ||
414 | struct device *dev = &pdev->dev; | ||
415 | struct device_node *np = dev->of_node; | ||
416 | struct fpga_region *region; | ||
417 | struct fpga_manager *mgr; | ||
418 | int ret; | ||
419 | |||
420 | /* Find the FPGA mgr specified by region or parent region. */ | ||
421 | mgr = of_fpga_region_get_mgr(np); | ||
422 | if (IS_ERR(mgr)) | ||
423 | return -EPROBE_DEFER; | ||
424 | |||
425 | region = devm_kzalloc(dev, sizeof(*region), GFP_KERNEL); | ||
426 | if (!region) { | ||
427 | ret = -ENOMEM; | ||
428 | goto eprobe_mgr_put; | ||
429 | } | ||
430 | |||
431 | region->mgr = mgr; | ||
432 | |||
433 | /* Specify how to get bridges for this type of region. */ | ||
434 | region->get_bridges = of_fpga_region_get_bridges; | ||
435 | |||
436 | ret = fpga_region_register(dev, region); | ||
437 | if (ret) | ||
438 | goto eprobe_mgr_put; | ||
439 | |||
440 | of_platform_populate(np, fpga_region_of_match, NULL, ®ion->dev); | ||
441 | |||
442 | dev_info(dev, "FPGA Region probed\n"); | ||
443 | |||
444 | return 0; | ||
445 | |||
446 | eprobe_mgr_put: | ||
447 | fpga_mgr_put(mgr); | ||
448 | return ret; | ||
449 | } | ||
450 | |||
451 | static int of_fpga_region_remove(struct platform_device *pdev) | ||
452 | { | ||
453 | struct fpga_region *region = platform_get_drvdata(pdev); | ||
454 | |||
455 | fpga_region_unregister(region); | ||
456 | fpga_mgr_put(region->mgr); | ||
457 | |||
458 | return 0; | ||
459 | } | ||
460 | |||
461 | static struct platform_driver of_fpga_region_driver = { | ||
462 | .probe = of_fpga_region_probe, | ||
463 | .remove = of_fpga_region_remove, | ||
464 | .driver = { | ||
465 | .name = "of-fpga-region", | ||
466 | .of_match_table = of_match_ptr(fpga_region_of_match), | ||
467 | }, | ||
468 | }; | ||
469 | |||
470 | /** | ||
471 | * fpga_region_init - init function for fpga_region class | ||
472 | * Creates the fpga_region class and registers a reconfig notifier. | ||
473 | */ | ||
474 | static int __init of_fpga_region_init(void) | ||
475 | { | ||
476 | int ret; | ||
477 | |||
478 | ret = of_overlay_notifier_register(&fpga_region_of_nb); | ||
479 | if (ret) | ||
480 | return ret; | ||
481 | |||
482 | ret = platform_driver_register(&of_fpga_region_driver); | ||
483 | if (ret) | ||
484 | goto err_plat; | ||
485 | |||
486 | return 0; | ||
487 | |||
488 | err_plat: | ||
489 | of_overlay_notifier_unregister(&fpga_region_of_nb); | ||
490 | return ret; | ||
491 | } | ||
492 | |||
493 | static void __exit of_fpga_region_exit(void) | ||
494 | { | ||
495 | platform_driver_unregister(&of_fpga_region_driver); | ||
496 | of_overlay_notifier_unregister(&fpga_region_of_nb); | ||
497 | } | ||
498 | |||
499 | subsys_initcall(of_fpga_region_init); | ||
500 | module_exit(of_fpga_region_exit); | ||
501 | |||
502 | MODULE_DESCRIPTION("FPGA Region"); | ||
503 | MODULE_AUTHOR("Alan Tull <atull@kernel.org>"); | ||
504 | MODULE_LICENSE("GPL v2"); | ||