diff options
author | Alan Tull <atull@kernel.org> | 2017-11-15 15:20:25 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-11-28 10:30:38 -0500 |
commit | ef3acdd820752e0abb5f1ec899025967d0dccf3d (patch) | |
tree | 4e41f8f8ca7b04a60e62f1220747b7dd077324f5 /drivers/fpga/fpga-region.c | |
parent | 503d4b7a446b3838785fa7f21e339941a5d1c2d5 (diff) |
fpga: region: move device tree support to of-fpga-region.c
Create of-fpga-region.c and move the following functions without
modification from fpga-region.c.
* of_fpga_region_find
* of_fpga_region_get_mgr
* of_fpga_region_get_bridges
* child_regions_with_firmware
* of_fpga_region_parse_ov
* of_fpga_region_notify_pre_apply
* of_fpga_region_notify_post_remove
* of_fpga_region_notify
* of_fpga_region_probe
* of_fpga_region_remove
Create two new functions with some code from fpga_region_init/exit.
* of_fpga_region_init
* of_fpga_region_exit
Signed-off-by: Alan Tull <atull@kernel.org>
Acked-by: Moritz Fischer <mdf@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/fpga/fpga-region.c')
-rw-r--r-- | drivers/fpga/fpga-region.c | 452 |
1 files changed, 1 insertions, 451 deletions
diff --git a/drivers/fpga/fpga-region.c b/drivers/fpga/fpga-region.c index 5c0869576cd1..afc61885a601 100644 --- a/drivers/fpga/fpga-region.c +++ b/drivers/fpga/fpga-region.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * FPGA Region - Device Tree support for FPGA programming under Linux | 2 | * FPGA Region - Device Tree support for FPGA programming under Linux |
3 | * | 3 | * |
4 | * Copyright (C) 2013-2016 Altera Corporation | 4 | * Copyright (C) 2013-2016 Altera Corporation |
5 | * Copyright (C) 2017 Intel Corporation | ||
5 | * | 6 | * |
6 | * This program is free software; you can redistribute it and/or modify it | 7 | * This program is free software; you can redistribute it and/or modify it |
7 | * under the terms and conditions of the GNU General Public License, | 8 | * under the terms and conditions of the GNU General Public License, |
@@ -23,7 +24,6 @@ | |||
23 | #include <linux/kernel.h> | 24 | #include <linux/kernel.h> |
24 | #include <linux/list.h> | 25 | #include <linux/list.h> |
25 | #include <linux/module.h> | 26 | #include <linux/module.h> |
26 | #include <linux/of_platform.h> | ||
27 | #include <linux/slab.h> | 27 | #include <linux/slab.h> |
28 | #include <linux/spinlock.h> | 28 | #include <linux/spinlock.h> |
29 | 29 | ||
@@ -44,30 +44,6 @@ struct fpga_region *fpga_region_class_find( | |||
44 | } | 44 | } |
45 | EXPORT_SYMBOL_GPL(fpga_region_class_find); | 45 | EXPORT_SYMBOL_GPL(fpga_region_class_find); |
46 | 46 | ||
47 | static const struct of_device_id fpga_region_of_match[] = { | ||
48 | { .compatible = "fpga-region", }, | ||
49 | {}, | ||
50 | }; | ||
51 | MODULE_DEVICE_TABLE(of, fpga_region_of_match); | ||
52 | |||
53 | static int fpga_region_of_node_match(struct device *dev, const void *data) | ||
54 | { | ||
55 | return dev->of_node == data; | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * of_fpga_region_find - find FPGA region | ||
60 | * @np: device node of FPGA Region | ||
61 | * | ||
62 | * Caller will need to put_device(®ion->dev) when done. | ||
63 | * | ||
64 | * Returns FPGA Region struct or NULL | ||
65 | */ | ||
66 | static struct fpga_region *of_fpga_region_find(struct device_node *np) | ||
67 | { | ||
68 | return fpga_region_class_find(NULL, np, fpga_region_of_node_match); | ||
69 | } | ||
70 | |||
71 | /** | 47 | /** |
72 | * fpga_region_get - get an exclusive reference to a fpga region | 48 | * fpga_region_get - get an exclusive reference to a fpga region |
73 | * @region: FPGA Region struct | 49 | * @region: FPGA Region struct |
@@ -116,102 +92,6 @@ static void fpga_region_put(struct fpga_region *region) | |||
116 | } | 92 | } |
117 | 93 | ||
118 | /** | 94 | /** |
119 | * of_fpga_region_get_mgr - get reference for FPGA manager | ||
120 | * @np: device node of FPGA region | ||
121 | * | ||
122 | * Get FPGA Manager from "fpga-mgr" property or from ancestor region. | ||
123 | * | ||
124 | * Caller should call fpga_mgr_put() when done with manager. | ||
125 | * | ||
126 | * Return: fpga manager struct or IS_ERR() condition containing error code. | ||
127 | */ | ||
128 | static struct fpga_manager *of_fpga_region_get_mgr(struct device_node *np) | ||
129 | { | ||
130 | struct device_node *mgr_node; | ||
131 | struct fpga_manager *mgr; | ||
132 | |||
133 | of_node_get(np); | ||
134 | while (np) { | ||
135 | if (of_device_is_compatible(np, "fpga-region")) { | ||
136 | mgr_node = of_parse_phandle(np, "fpga-mgr", 0); | ||
137 | if (mgr_node) { | ||
138 | mgr = of_fpga_mgr_get(mgr_node); | ||
139 | of_node_put(np); | ||
140 | return mgr; | ||
141 | } | ||
142 | } | ||
143 | np = of_get_next_parent(np); | ||
144 | } | ||
145 | of_node_put(np); | ||
146 | |||
147 | return ERR_PTR(-EINVAL); | ||
148 | } | ||
149 | |||
150 | /** | ||
151 | * of_fpga_region_get_bridges - create a list of bridges | ||
152 | * @region: FPGA region | ||
153 | * | ||
154 | * Create a list of bridges including the parent bridge and the bridges | ||
155 | * specified by "fpga-bridges" property. Note that the | ||
156 | * fpga_bridges_enable/disable/put functions are all fine with an empty list | ||
157 | * if that happens. | ||
158 | * | ||
159 | * Caller should call fpga_bridges_put(®ion->bridge_list) when | ||
160 | * done with the bridges. | ||
161 | * | ||
162 | * Return 0 for success (even if there are no bridges specified) | ||
163 | * or -EBUSY if any of the bridges are in use. | ||
164 | */ | ||
165 | static int of_fpga_region_get_bridges(struct fpga_region *region) | ||
166 | { | ||
167 | struct device *dev = ®ion->dev; | ||
168 | struct device_node *region_np = dev->of_node; | ||
169 | struct fpga_image_info *info = region->info; | ||
170 | struct device_node *br, *np, *parent_br = NULL; | ||
171 | int i, ret; | ||
172 | |||
173 | /* If parent is a bridge, add to list */ | ||
174 | ret = of_fpga_bridge_get_to_list(region_np->parent, info, | ||
175 | ®ion->bridge_list); | ||
176 | |||
177 | /* -EBUSY means parent is a bridge that is under use. Give up. */ | ||
178 | if (ret == -EBUSY) | ||
179 | return ret; | ||
180 | |||
181 | /* Zero return code means parent was a bridge and was added to list. */ | ||
182 | if (!ret) | ||
183 | parent_br = region_np->parent; | ||
184 | |||
185 | /* If overlay has a list of bridges, use it. */ | ||
186 | if (of_parse_phandle(info->overlay, "fpga-bridges", 0)) | ||
187 | np = info->overlay; | ||
188 | else | ||
189 | np = region_np; | ||
190 | |||
191 | for (i = 0; ; i++) { | ||
192 | br = of_parse_phandle(np, "fpga-bridges", i); | ||
193 | if (!br) | ||
194 | break; | ||
195 | |||
196 | /* If parent bridge is in list, skip it. */ | ||
197 | if (br == parent_br) | ||
198 | continue; | ||
199 | |||
200 | /* If node is a bridge, get it and add to list */ | ||
201 | ret = of_fpga_bridge_get_to_list(br, info, | ||
202 | ®ion->bridge_list); | ||
203 | |||
204 | /* If any of the bridges are in use, give up */ | ||
205 | if (ret == -EBUSY) { | ||
206 | fpga_bridges_put(®ion->bridge_list); | ||
207 | return -EBUSY; | ||
208 | } | ||
209 | } | ||
210 | |||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | /** | ||
215 | * fpga_region_program_fpga - program FPGA | 95 | * fpga_region_program_fpga - program FPGA |
216 | * @region: FPGA region | 96 | * @region: FPGA region |
217 | * Program an FPGA using fpga image info (region->info). | 97 | * Program an FPGA using fpga image info (region->info). |
@@ -282,259 +162,6 @@ err_put_region: | |||
282 | } | 162 | } |
283 | EXPORT_SYMBOL_GPL(fpga_region_program_fpga); | 163 | EXPORT_SYMBOL_GPL(fpga_region_program_fpga); |
284 | 164 | ||
285 | /** | ||
286 | * child_regions_with_firmware | ||
287 | * @overlay: device node of the overlay | ||
288 | * | ||
289 | * If the overlay adds child FPGA regions, they are not allowed to have | ||
290 | * firmware-name property. | ||
291 | * | ||
292 | * Return 0 for OK or -EINVAL if child FPGA region adds firmware-name. | ||
293 | */ | ||
294 | static int child_regions_with_firmware(struct device_node *overlay) | ||
295 | { | ||
296 | struct device_node *child_region; | ||
297 | const char *child_firmware_name; | ||
298 | int ret = 0; | ||
299 | |||
300 | of_node_get(overlay); | ||
301 | |||
302 | child_region = of_find_matching_node(overlay, fpga_region_of_match); | ||
303 | while (child_region) { | ||
304 | if (!of_property_read_string(child_region, "firmware-name", | ||
305 | &child_firmware_name)) { | ||
306 | ret = -EINVAL; | ||
307 | break; | ||
308 | } | ||
309 | child_region = of_find_matching_node(child_region, | ||
310 | fpga_region_of_match); | ||
311 | } | ||
312 | |||
313 | of_node_put(child_region); | ||
314 | |||
315 | if (ret) | ||
316 | pr_err("firmware-name not allowed in child FPGA region: %pOF", | ||
317 | child_region); | ||
318 | |||
319 | return ret; | ||
320 | } | ||
321 | |||
322 | /** | ||
323 | * of_fpga_region_parse_ov - parse and check overlay applied to region | ||
324 | * | ||
325 | * @region: FPGA region | ||
326 | * @overlay: overlay applied to the FPGA region | ||
327 | * | ||
328 | * Given an overlay applied to a FPGA region, parse the FPGA image specific | ||
329 | * info in the overlay and do some checking. | ||
330 | * | ||
331 | * Returns: | ||
332 | * NULL if overlay doesn't direct us to program the FPGA. | ||
333 | * fpga_image_info struct if there is an image to program. | ||
334 | * error code for invalid overlay. | ||
335 | */ | ||
336 | static struct fpga_image_info *of_fpga_region_parse_ov( | ||
337 | struct fpga_region *region, | ||
338 | struct device_node *overlay) | ||
339 | { | ||
340 | struct device *dev = ®ion->dev; | ||
341 | struct fpga_image_info *info; | ||
342 | const char *firmware_name; | ||
343 | int ret; | ||
344 | |||
345 | if (region->info) { | ||
346 | dev_err(dev, "Region already has overlay applied.\n"); | ||
347 | return ERR_PTR(-EINVAL); | ||
348 | } | ||
349 | |||
350 | /* | ||
351 | * Reject overlay if child FPGA Regions added in the overlay have | ||
352 | * firmware-name property (would mean that an FPGA region that has | ||
353 | * not been added to the live tree yet is doing FPGA programming). | ||
354 | */ | ||
355 | ret = child_regions_with_firmware(overlay); | ||
356 | if (ret) | ||
357 | return ERR_PTR(ret); | ||
358 | |||
359 | info = fpga_image_info_alloc(dev); | ||
360 | if (!info) | ||
361 | return ERR_PTR(-ENOMEM); | ||
362 | |||
363 | info->overlay = overlay; | ||
364 | |||
365 | /* Read FPGA region properties from the overlay */ | ||
366 | if (of_property_read_bool(overlay, "partial-fpga-config")) | ||
367 | info->flags |= FPGA_MGR_PARTIAL_RECONFIG; | ||
368 | |||
369 | if (of_property_read_bool(overlay, "external-fpga-config")) | ||
370 | info->flags |= FPGA_MGR_EXTERNAL_CONFIG; | ||
371 | |||
372 | if (of_property_read_bool(overlay, "encrypted-fpga-config")) | ||
373 | info->flags |= FPGA_MGR_ENCRYPTED_BITSTREAM; | ||
374 | |||
375 | if (!of_property_read_string(overlay, "firmware-name", | ||
376 | &firmware_name)) { | ||
377 | info->firmware_name = devm_kstrdup(dev, firmware_name, | ||
378 | GFP_KERNEL); | ||
379 | if (!info->firmware_name) | ||
380 | return ERR_PTR(-ENOMEM); | ||
381 | } | ||
382 | |||
383 | of_property_read_u32(overlay, "region-unfreeze-timeout-us", | ||
384 | &info->enable_timeout_us); | ||
385 | |||
386 | of_property_read_u32(overlay, "region-freeze-timeout-us", | ||
387 | &info->disable_timeout_us); | ||
388 | |||
389 | of_property_read_u32(overlay, "config-complete-timeout-us", | ||
390 | &info->config_complete_timeout_us); | ||
391 | |||
392 | /* If overlay is not programming the FPGA, don't need FPGA image info */ | ||
393 | if (!info->firmware_name) { | ||
394 | ret = 0; | ||
395 | goto ret_no_info; | ||
396 | } | ||
397 | |||
398 | /* | ||
399 | * If overlay informs us FPGA was externally programmed, specifying | ||
400 | * firmware here would be ambiguous. | ||
401 | */ | ||
402 | if (info->flags & FPGA_MGR_EXTERNAL_CONFIG) { | ||
403 | dev_err(dev, "error: specified firmware and external-fpga-config"); | ||
404 | ret = -EINVAL; | ||
405 | goto ret_no_info; | ||
406 | } | ||
407 | |||
408 | return info; | ||
409 | ret_no_info: | ||
410 | fpga_image_info_free(info); | ||
411 | return ERR_PTR(ret); | ||
412 | } | ||
413 | |||
414 | /** | ||
415 | * of_fpga_region_notify_pre_apply - pre-apply overlay notification | ||
416 | * | ||
417 | * @region: FPGA region that the overlay was applied to | ||
418 | * @nd: overlay notification data | ||
419 | * | ||
420 | * Called when an overlay targeted to a FPGA Region is about to be applied. | ||
421 | * Parses the overlay for properties that influence how the FPGA will be | ||
422 | * programmed and does some checking. If the checks pass, programs the FPGA. | ||
423 | * If the checks fail, overlay is rejected and does not get added to the | ||
424 | * live tree. | ||
425 | * | ||
426 | * Returns 0 for success or negative error code for failure. | ||
427 | */ | ||
428 | static int of_fpga_region_notify_pre_apply(struct fpga_region *region, | ||
429 | struct of_overlay_notify_data *nd) | ||
430 | { | ||
431 | struct device *dev = ®ion->dev; | ||
432 | struct fpga_image_info *info; | ||
433 | int ret; | ||
434 | |||
435 | if (region->info) { | ||
436 | dev_err(dev, "Region already has overlay applied.\n"); | ||
437 | return -EINVAL; | ||
438 | } | ||
439 | |||
440 | info = of_fpga_region_parse_ov(region, nd->overlay); | ||
441 | if (IS_ERR(info)) | ||
442 | return PTR_ERR(info); | ||
443 | |||
444 | if (!info) | ||
445 | return 0; | ||
446 | |||
447 | region->info = info; | ||
448 | ret = fpga_region_program_fpga(region); | ||
449 | if (ret) { | ||
450 | /* error; reject overlay */ | ||
451 | fpga_image_info_free(info); | ||
452 | region->info = NULL; | ||
453 | } | ||
454 | |||
455 | return ret; | ||
456 | } | ||
457 | |||
458 | /** | ||
459 | * of_fpga_region_notify_post_remove - post-remove overlay notification | ||
460 | * | ||
461 | * @region: FPGA region that was targeted by the overlay that was removed | ||
462 | * @nd: overlay notification data | ||
463 | * | ||
464 | * Called after an overlay has been removed if the overlay's target was a | ||
465 | * FPGA region. | ||
466 | */ | ||
467 | static void of_fpga_region_notify_post_remove(struct fpga_region *region, | ||
468 | struct of_overlay_notify_data *nd) | ||
469 | { | ||
470 | fpga_bridges_disable(®ion->bridge_list); | ||
471 | fpga_bridges_put(®ion->bridge_list); | ||
472 | fpga_image_info_free(region->info); | ||
473 | region->info = NULL; | ||
474 | } | ||
475 | |||
476 | /** | ||
477 | * of_fpga_region_notify - reconfig notifier for dynamic DT changes | ||
478 | * @nb: notifier block | ||
479 | * @action: notifier action | ||
480 | * @arg: reconfig data | ||
481 | * | ||
482 | * This notifier handles programming a FPGA when a "firmware-name" property is | ||
483 | * added to a fpga-region. | ||
484 | * | ||
485 | * Returns NOTIFY_OK or error if FPGA programming fails. | ||
486 | */ | ||
487 | static int of_fpga_region_notify(struct notifier_block *nb, | ||
488 | unsigned long action, void *arg) | ||
489 | { | ||
490 | struct of_overlay_notify_data *nd = arg; | ||
491 | struct fpga_region *region; | ||
492 | int ret; | ||
493 | |||
494 | switch (action) { | ||
495 | case OF_OVERLAY_PRE_APPLY: | ||
496 | pr_debug("%s OF_OVERLAY_PRE_APPLY\n", __func__); | ||
497 | break; | ||
498 | case OF_OVERLAY_POST_APPLY: | ||
499 | pr_debug("%s OF_OVERLAY_POST_APPLY\n", __func__); | ||
500 | return NOTIFY_OK; /* not for us */ | ||
501 | case OF_OVERLAY_PRE_REMOVE: | ||
502 | pr_debug("%s OF_OVERLAY_PRE_REMOVE\n", __func__); | ||
503 | return NOTIFY_OK; /* not for us */ | ||
504 | case OF_OVERLAY_POST_REMOVE: | ||
505 | pr_debug("%s OF_OVERLAY_POST_REMOVE\n", __func__); | ||
506 | break; | ||
507 | default: /* should not happen */ | ||
508 | return NOTIFY_OK; | ||
509 | } | ||
510 | |||
511 | region = of_fpga_region_find(nd->target); | ||
512 | if (!region) | ||
513 | return NOTIFY_OK; | ||
514 | |||
515 | ret = 0; | ||
516 | switch (action) { | ||
517 | case OF_OVERLAY_PRE_APPLY: | ||
518 | ret = of_fpga_region_notify_pre_apply(region, nd); | ||
519 | break; | ||
520 | |||
521 | case OF_OVERLAY_POST_REMOVE: | ||
522 | of_fpga_region_notify_post_remove(region, nd); | ||
523 | break; | ||
524 | } | ||
525 | |||
526 | put_device(®ion->dev); | ||
527 | |||
528 | if (ret) | ||
529 | return notifier_from_errno(ret); | ||
530 | |||
531 | return NOTIFY_OK; | ||
532 | } | ||
533 | |||
534 | static struct notifier_block fpga_region_of_nb = { | ||
535 | .notifier_call = of_fpga_region_notify, | ||
536 | }; | ||
537 | |||
538 | int fpga_region_register(struct device *dev, struct fpga_region *region) | 165 | int fpga_region_register(struct device *dev, struct fpga_region *region) |
539 | { | 166 | { |
540 | int id, ret = 0; | 167 | int id, ret = 0; |
@@ -576,64 +203,6 @@ int fpga_region_unregister(struct fpga_region *region) | |||
576 | } | 203 | } |
577 | EXPORT_SYMBOL_GPL(fpga_region_unregister); | 204 | EXPORT_SYMBOL_GPL(fpga_region_unregister); |
578 | 205 | ||
579 | static int of_fpga_region_probe(struct platform_device *pdev) | ||
580 | { | ||
581 | struct device *dev = &pdev->dev; | ||
582 | struct device_node *np = dev->of_node; | ||
583 | struct fpga_region *region; | ||
584 | struct fpga_manager *mgr; | ||
585 | int ret; | ||
586 | |||
587 | /* Find the FPGA mgr specified by region or parent region. */ | ||
588 | mgr = of_fpga_region_get_mgr(np); | ||
589 | if (IS_ERR(mgr)) | ||
590 | return -EPROBE_DEFER; | ||
591 | |||
592 | region = devm_kzalloc(dev, sizeof(*region), GFP_KERNEL); | ||
593 | if (!region) { | ||
594 | ret = -ENOMEM; | ||
595 | goto eprobe_mgr_put; | ||
596 | } | ||
597 | |||
598 | region->mgr = mgr; | ||
599 | |||
600 | /* Specify how to get bridges for this type of region. */ | ||
601 | region->get_bridges = of_fpga_region_get_bridges; | ||
602 | |||
603 | ret = fpga_region_register(dev, region); | ||
604 | if (ret) | ||
605 | goto eprobe_mgr_put; | ||
606 | |||
607 | of_platform_populate(np, fpga_region_of_match, NULL, ®ion->dev); | ||
608 | |||
609 | dev_info(dev, "FPGA Region probed\n"); | ||
610 | |||
611 | return 0; | ||
612 | |||
613 | eprobe_mgr_put: | ||
614 | fpga_mgr_put(mgr); | ||
615 | return ret; | ||
616 | } | ||
617 | |||
618 | static int of_fpga_region_remove(struct platform_device *pdev) | ||
619 | { | ||
620 | struct fpga_region *region = platform_get_drvdata(pdev); | ||
621 | |||
622 | fpga_region_unregister(region); | ||
623 | fpga_mgr_put(region->mgr); | ||
624 | |||
625 | return 0; | ||
626 | } | ||
627 | |||
628 | static struct platform_driver of_fpga_region_driver = { | ||
629 | .probe = of_fpga_region_probe, | ||
630 | .remove = of_fpga_region_remove, | ||
631 | .driver = { | ||
632 | .name = "fpga-region", | ||
633 | .of_match_table = of_match_ptr(fpga_region_of_match), | ||
634 | }, | ||
635 | }; | ||
636 | |||
637 | static void fpga_region_dev_release(struct device *dev) | 206 | static void fpga_region_dev_release(struct device *dev) |
638 | { | 207 | { |
639 | struct fpga_region *region = to_fpga_region(dev); | 208 | struct fpga_region *region = to_fpga_region(dev); |
@@ -647,36 +216,17 @@ static void fpga_region_dev_release(struct device *dev) | |||
647 | */ | 216 | */ |
648 | static int __init fpga_region_init(void) | 217 | static int __init fpga_region_init(void) |
649 | { | 218 | { |
650 | int ret; | ||
651 | |||
652 | fpga_region_class = class_create(THIS_MODULE, "fpga_region"); | 219 | fpga_region_class = class_create(THIS_MODULE, "fpga_region"); |
653 | if (IS_ERR(fpga_region_class)) | 220 | if (IS_ERR(fpga_region_class)) |
654 | return PTR_ERR(fpga_region_class); | 221 | return PTR_ERR(fpga_region_class); |
655 | 222 | ||
656 | fpga_region_class->dev_release = fpga_region_dev_release; | 223 | fpga_region_class->dev_release = fpga_region_dev_release; |
657 | 224 | ||
658 | ret = of_overlay_notifier_register(&fpga_region_of_nb); | ||
659 | if (ret) | ||
660 | goto err_class; | ||
661 | |||
662 | ret = platform_driver_register(&of_fpga_region_driver); | ||
663 | if (ret) | ||
664 | goto err_plat; | ||
665 | |||
666 | return 0; | 225 | return 0; |
667 | |||
668 | err_plat: | ||
669 | of_overlay_notifier_unregister(&fpga_region_of_nb); | ||
670 | err_class: | ||
671 | class_destroy(fpga_region_class); | ||
672 | ida_destroy(&fpga_region_ida); | ||
673 | return ret; | ||
674 | } | 226 | } |
675 | 227 | ||
676 | static void __exit fpga_region_exit(void) | 228 | static void __exit fpga_region_exit(void) |
677 | { | 229 | { |
678 | platform_driver_unregister(&of_fpga_region_driver); | ||
679 | of_overlay_notifier_unregister(&fpga_region_of_nb); | ||
680 | class_destroy(fpga_region_class); | 230 | class_destroy(fpga_region_class); |
681 | ida_destroy(&fpga_region_ida); | 231 | ida_destroy(&fpga_region_ida); |
682 | } | 232 | } |