diff options
author | Alan Tull <atull@opensource.altera.com> | 2016-11-01 15:14:29 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2016-11-10 11:03:35 -0500 |
commit | 0fa20cdfcc1f68847cdfc47824476301eedc8297 (patch) | |
tree | f84bb6cad048727a76554a84ecf095ce073e12b1 /drivers/fpga/fpga-region.c | |
parent | 21aeda950c5f84a8351b862816d832120b217a9b (diff) |
fpga: fpga-region: device tree control for FPGA
FPGA Regions support programming FPGA under control of the Device
Tree.
Signed-off-by: Alan Tull <atull@opensource.altera.com>
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 | 603 |
1 files changed, 603 insertions, 0 deletions
diff --git a/drivers/fpga/fpga-region.c b/drivers/fpga/fpga-region.c new file mode 100644 index 000000000000..3222fdbad75a --- /dev/null +++ b/drivers/fpga/fpga-region.c | |||
@@ -0,0 +1,603 @@ | |||
1 | /* | ||
2 | * FPGA Region - Device Tree support for FPGA programming under Linux | ||
3 | * | ||
4 | * Copyright (C) 2013-2016 Altera Corporation | ||
5 | * | ||
6 | * 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 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License along with | ||
16 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | */ | ||
18 | |||
19 | #include <linux/fpga/fpga-bridge.h> | ||
20 | #include <linux/fpga/fpga-mgr.h> | ||
21 | #include <linux/idr.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/list.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/of_platform.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/spinlock.h> | ||
28 | |||
29 | /** | ||
30 | * struct fpga_region - FPGA Region structure | ||
31 | * @dev: FPGA Region device | ||
32 | * @mutex: enforces exclusive reference to region | ||
33 | * @bridge_list: list of FPGA bridges specified in region | ||
34 | * @info: fpga image specific information | ||
35 | */ | ||
36 | struct fpga_region { | ||
37 | struct device dev; | ||
38 | struct mutex mutex; /* for exclusive reference to region */ | ||
39 | struct list_head bridge_list; | ||
40 | struct fpga_image_info *info; | ||
41 | }; | ||
42 | |||
43 | #define to_fpga_region(d) container_of(d, struct fpga_region, dev) | ||
44 | |||
45 | static DEFINE_IDA(fpga_region_ida); | ||
46 | static struct class *fpga_region_class; | ||
47 | |||
48 | static const struct of_device_id fpga_region_of_match[] = { | ||
49 | { .compatible = "fpga-region", }, | ||
50 | {}, | ||
51 | }; | ||
52 | MODULE_DEVICE_TABLE(of, fpga_region_of_match); | ||
53 | |||
54 | static int fpga_region_of_node_match(struct device *dev, const void *data) | ||
55 | { | ||
56 | return dev->of_node == data; | ||
57 | } | ||
58 | |||
59 | /** | ||
60 | * fpga_region_find - find FPGA region | ||
61 | * @np: device node of FPGA Region | ||
62 | * Caller will need to put_device(®ion->dev) when done. | ||
63 | * Returns FPGA Region struct or NULL | ||
64 | */ | ||
65 | static struct fpga_region *fpga_region_find(struct device_node *np) | ||
66 | { | ||
67 | struct device *dev; | ||
68 | |||
69 | dev = class_find_device(fpga_region_class, NULL, np, | ||
70 | fpga_region_of_node_match); | ||
71 | if (!dev) | ||
72 | return NULL; | ||
73 | |||
74 | return to_fpga_region(dev); | ||
75 | } | ||
76 | |||
77 | /** | ||
78 | * fpga_region_get - get an exclusive reference to a fpga region | ||
79 | * @region: FPGA Region struct | ||
80 | * | ||
81 | * Caller should call fpga_region_put() when done with region. | ||
82 | * | ||
83 | * Return fpga_region struct if successful. | ||
84 | * Return -EBUSY if someone already has a reference to the region. | ||
85 | * Return -ENODEV if @np is not a FPGA Region. | ||
86 | */ | ||
87 | static struct fpga_region *fpga_region_get(struct fpga_region *region) | ||
88 | { | ||
89 | struct device *dev = ®ion->dev; | ||
90 | |||
91 | if (!mutex_trylock(®ion->mutex)) { | ||
92 | dev_dbg(dev, "%s: FPGA Region already in use\n", __func__); | ||
93 | return ERR_PTR(-EBUSY); | ||
94 | } | ||
95 | |||
96 | get_device(dev); | ||
97 | of_node_get(dev->of_node); | ||
98 | if (!try_module_get(dev->parent->driver->owner)) { | ||
99 | of_node_put(dev->of_node); | ||
100 | put_device(dev); | ||
101 | mutex_unlock(®ion->mutex); | ||
102 | return ERR_PTR(-ENODEV); | ||
103 | } | ||
104 | |||
105 | dev_dbg(®ion->dev, "get\n"); | ||
106 | |||
107 | return region; | ||
108 | } | ||
109 | |||
110 | /** | ||
111 | * fpga_region_put - release a reference to a region | ||
112 | * | ||
113 | * @region: FPGA region | ||
114 | */ | ||
115 | static void fpga_region_put(struct fpga_region *region) | ||
116 | { | ||
117 | struct device *dev = ®ion->dev; | ||
118 | |||
119 | dev_dbg(®ion->dev, "put\n"); | ||
120 | |||
121 | module_put(dev->parent->driver->owner); | ||
122 | of_node_put(dev->of_node); | ||
123 | put_device(dev); | ||
124 | mutex_unlock(®ion->mutex); | ||
125 | } | ||
126 | |||
127 | /** | ||
128 | * fpga_region_get_manager - get exclusive reference for FPGA manager | ||
129 | * @region: FPGA region | ||
130 | * | ||
131 | * Get FPGA Manager from "fpga-mgr" property or from ancestor region. | ||
132 | * | ||
133 | * Caller should call fpga_mgr_put() when done with manager. | ||
134 | * | ||
135 | * Return: fpga manager struct or IS_ERR() condition containing error code. | ||
136 | */ | ||
137 | static struct fpga_manager *fpga_region_get_manager(struct fpga_region *region) | ||
138 | { | ||
139 | struct device *dev = ®ion->dev; | ||
140 | struct device_node *np = dev->of_node; | ||
141 | struct device_node *mgr_node; | ||
142 | struct fpga_manager *mgr; | ||
143 | |||
144 | of_node_get(np); | ||
145 | while (np) { | ||
146 | if (of_device_is_compatible(np, "fpga-region")) { | ||
147 | mgr_node = of_parse_phandle(np, "fpga-mgr", 0); | ||
148 | if (mgr_node) { | ||
149 | mgr = of_fpga_mgr_get(mgr_node); | ||
150 | of_node_put(np); | ||
151 | return mgr; | ||
152 | } | ||
153 | } | ||
154 | np = of_get_next_parent(np); | ||
155 | } | ||
156 | of_node_put(np); | ||
157 | |||
158 | return ERR_PTR(-EINVAL); | ||
159 | } | ||
160 | |||
161 | /** | ||
162 | * fpga_region_get_bridges - create a list of bridges | ||
163 | * @region: FPGA region | ||
164 | * @overlay: device node of the overlay | ||
165 | * | ||
166 | * Create a list of bridges including the parent bridge and the bridges | ||
167 | * specified by "fpga-bridges" property. Note that the | ||
168 | * fpga_bridges_enable/disable/put functions are all fine with an empty list | ||
169 | * if that happens. | ||
170 | * | ||
171 | * Caller should call fpga_bridges_put(®ion->bridge_list) when | ||
172 | * done with the bridges. | ||
173 | * | ||
174 | * Return 0 for success (even if there are no bridges specified) | ||
175 | * or -EBUSY if any of the bridges are in use. | ||
176 | */ | ||
177 | static int fpga_region_get_bridges(struct fpga_region *region, | ||
178 | struct device_node *overlay) | ||
179 | { | ||
180 | struct device *dev = ®ion->dev; | ||
181 | struct device_node *region_np = dev->of_node; | ||
182 | struct device_node *br, *np, *parent_br = NULL; | ||
183 | int i, ret; | ||
184 | |||
185 | /* If parent is a bridge, add to list */ | ||
186 | ret = fpga_bridge_get_to_list(region_np->parent, region->info, | ||
187 | ®ion->bridge_list); | ||
188 | if (ret == -EBUSY) | ||
189 | return ret; | ||
190 | |||
191 | if (!ret) | ||
192 | parent_br = region_np->parent; | ||
193 | |||
194 | /* If overlay has a list of bridges, use it. */ | ||
195 | if (of_parse_phandle(overlay, "fpga-bridges", 0)) | ||
196 | np = overlay; | ||
197 | else | ||
198 | np = region_np; | ||
199 | |||
200 | for (i = 0; ; i++) { | ||
201 | br = of_parse_phandle(np, "fpga-bridges", i); | ||
202 | if (!br) | ||
203 | break; | ||
204 | |||
205 | /* If parent bridge is in list, skip it. */ | ||
206 | if (br == parent_br) | ||
207 | continue; | ||
208 | |||
209 | /* If node is a bridge, get it and add to list */ | ||
210 | ret = fpga_bridge_get_to_list(br, region->info, | ||
211 | ®ion->bridge_list); | ||
212 | |||
213 | /* If any of the bridges are in use, give up */ | ||
214 | if (ret == -EBUSY) { | ||
215 | fpga_bridges_put(®ion->bridge_list); | ||
216 | return -EBUSY; | ||
217 | } | ||
218 | } | ||
219 | |||
220 | return 0; | ||
221 | } | ||
222 | |||
223 | /** | ||
224 | * fpga_region_program_fpga - program FPGA | ||
225 | * @region: FPGA region | ||
226 | * @firmware_name: name of FPGA image firmware file | ||
227 | * @overlay: device node of the overlay | ||
228 | * Program an FPGA using information in the device tree. | ||
229 | * Function assumes that there is a firmware-name property. | ||
230 | * Return 0 for success or negative error code. | ||
231 | */ | ||
232 | static int fpga_region_program_fpga(struct fpga_region *region, | ||
233 | const char *firmware_name, | ||
234 | struct device_node *overlay) | ||
235 | { | ||
236 | struct fpga_manager *mgr; | ||
237 | int ret; | ||
238 | |||
239 | region = fpga_region_get(region); | ||
240 | if (IS_ERR(region)) { | ||
241 | pr_err("failed to get fpga region\n"); | ||
242 | return PTR_ERR(region); | ||
243 | } | ||
244 | |||
245 | mgr = fpga_region_get_manager(region); | ||
246 | if (IS_ERR(mgr)) { | ||
247 | pr_err("failed to get fpga region manager\n"); | ||
248 | return PTR_ERR(mgr); | ||
249 | } | ||
250 | |||
251 | ret = fpga_region_get_bridges(region, overlay); | ||
252 | if (ret) { | ||
253 | pr_err("failed to get fpga region bridges\n"); | ||
254 | goto err_put_mgr; | ||
255 | } | ||
256 | |||
257 | ret = fpga_bridges_disable(®ion->bridge_list); | ||
258 | if (ret) { | ||
259 | pr_err("failed to disable region bridges\n"); | ||
260 | goto err_put_br; | ||
261 | } | ||
262 | |||
263 | ret = fpga_mgr_firmware_load(mgr, region->info, firmware_name); | ||
264 | if (ret) { | ||
265 | pr_err("failed to load fpga image\n"); | ||
266 | goto err_put_br; | ||
267 | } | ||
268 | |||
269 | ret = fpga_bridges_enable(®ion->bridge_list); | ||
270 | if (ret) { | ||
271 | pr_err("failed to enable region bridges\n"); | ||
272 | goto err_put_br; | ||
273 | } | ||
274 | |||
275 | fpga_mgr_put(mgr); | ||
276 | fpga_region_put(region); | ||
277 | |||
278 | return 0; | ||
279 | |||
280 | err_put_br: | ||
281 | fpga_bridges_put(®ion->bridge_list); | ||
282 | err_put_mgr: | ||
283 | fpga_mgr_put(mgr); | ||
284 | fpga_region_put(region); | ||
285 | |||
286 | return ret; | ||
287 | } | ||
288 | |||
289 | /** | ||
290 | * child_regions_with_firmware | ||
291 | * @overlay: device node of the overlay | ||
292 | * | ||
293 | * If the overlay adds child FPGA regions, they are not allowed to have | ||
294 | * firmware-name property. | ||
295 | * | ||
296 | * Return 0 for OK or -EINVAL if child FPGA region adds firmware-name. | ||
297 | */ | ||
298 | static int child_regions_with_firmware(struct device_node *overlay) | ||
299 | { | ||
300 | struct device_node *child_region; | ||
301 | const char *child_firmware_name; | ||
302 | int ret = 0; | ||
303 | |||
304 | of_node_get(overlay); | ||
305 | |||
306 | child_region = of_find_matching_node(overlay, fpga_region_of_match); | ||
307 | while (child_region) { | ||
308 | if (!of_property_read_string(child_region, "firmware-name", | ||
309 | &child_firmware_name)) { | ||
310 | ret = -EINVAL; | ||
311 | break; | ||
312 | } | ||
313 | child_region = of_find_matching_node(child_region, | ||
314 | fpga_region_of_match); | ||
315 | } | ||
316 | |||
317 | of_node_put(child_region); | ||
318 | |||
319 | if (ret) | ||
320 | pr_err("firmware-name not allowed in child FPGA region: %s", | ||
321 | child_region->full_name); | ||
322 | |||
323 | return ret; | ||
324 | } | ||
325 | |||
326 | /** | ||
327 | * fpga_region_notify_pre_apply - pre-apply overlay notification | ||
328 | * | ||
329 | * @region: FPGA region that the overlay was applied to | ||
330 | * @nd: overlay notification data | ||
331 | * | ||
332 | * Called after when an overlay targeted to a FPGA Region is about to be | ||
333 | * applied. Function will check the properties that will be added to the FPGA | ||
334 | * region. If the checks pass, it will program the FPGA. | ||
335 | * | ||
336 | * The checks are: | ||
337 | * The overlay must add either firmware-name or external-fpga-config property | ||
338 | * to the FPGA Region. | ||
339 | * | ||
340 | * firmware-name : program the FPGA | ||
341 | * external-fpga-config : FPGA is already programmed | ||
342 | * | ||
343 | * The overlay can add other FPGA regions, but child FPGA regions cannot have a | ||
344 | * firmware-name property since those regions don't exist yet. | ||
345 | * | ||
346 | * If the overlay that breaks the rules, notifier returns an error and the | ||
347 | * overlay is rejected before it goes into the main tree. | ||
348 | * | ||
349 | * Returns 0 for success or negative error code for failure. | ||
350 | */ | ||
351 | static int fpga_region_notify_pre_apply(struct fpga_region *region, | ||
352 | struct of_overlay_notify_data *nd) | ||
353 | { | ||
354 | const char *firmware_name = NULL; | ||
355 | struct fpga_image_info *info; | ||
356 | int ret; | ||
357 | |||
358 | info = devm_kzalloc(®ion->dev, sizeof(*info), GFP_KERNEL); | ||
359 | if (!info) | ||
360 | return -ENOMEM; | ||
361 | |||
362 | region->info = info; | ||
363 | |||
364 | /* Reject overlay if child FPGA Regions have firmware-name property */ | ||
365 | ret = child_regions_with_firmware(nd->overlay); | ||
366 | if (ret) | ||
367 | return ret; | ||
368 | |||
369 | /* Read FPGA region properties from the overlay */ | ||
370 | if (of_property_read_bool(nd->overlay, "partial-fpga-config")) | ||
371 | info->flags |= FPGA_MGR_PARTIAL_RECONFIG; | ||
372 | |||
373 | if (of_property_read_bool(nd->overlay, "external-fpga-config")) | ||
374 | info->flags |= FPGA_MGR_EXTERNAL_CONFIG; | ||
375 | |||
376 | of_property_read_string(nd->overlay, "firmware-name", &firmware_name); | ||
377 | |||
378 | of_property_read_u32(nd->overlay, "region-unfreeze-timeout-us", | ||
379 | &info->enable_timeout_us); | ||
380 | |||
381 | of_property_read_u32(nd->overlay, "region-freeze-timeout-us", | ||
382 | &info->disable_timeout_us); | ||
383 | |||
384 | /* If FPGA was externally programmed, don't specify firmware */ | ||
385 | if ((info->flags & FPGA_MGR_EXTERNAL_CONFIG) && firmware_name) { | ||
386 | pr_err("error: specified firmware and external-fpga-config"); | ||
387 | return -EINVAL; | ||
388 | } | ||
389 | |||
390 | /* FPGA is already configured externally. We're done. */ | ||
391 | if (info->flags & FPGA_MGR_EXTERNAL_CONFIG) | ||
392 | return 0; | ||
393 | |||
394 | /* If we got this far, we should be programming the FPGA */ | ||
395 | if (!firmware_name) { | ||
396 | pr_err("should specify firmware-name or external-fpga-config\n"); | ||
397 | return -EINVAL; | ||
398 | } | ||
399 | |||
400 | return fpga_region_program_fpga(region, firmware_name, nd->overlay); | ||
401 | } | ||
402 | |||
403 | /** | ||
404 | * fpga_region_notify_post_remove - post-remove overlay notification | ||
405 | * | ||
406 | * @region: FPGA region that was targeted by the overlay that was removed | ||
407 | * @nd: overlay notification data | ||
408 | * | ||
409 | * Called after an overlay has been removed if the overlay's target was a | ||
410 | * FPGA region. | ||
411 | */ | ||
412 | static void fpga_region_notify_post_remove(struct fpga_region *region, | ||
413 | struct of_overlay_notify_data *nd) | ||
414 | { | ||
415 | fpga_bridges_disable(®ion->bridge_list); | ||
416 | fpga_bridges_put(®ion->bridge_list); | ||
417 | devm_kfree(®ion->dev, region->info); | ||
418 | region->info = NULL; | ||
419 | } | ||
420 | |||
421 | /** | ||
422 | * of_fpga_region_notify - reconfig notifier for dynamic DT changes | ||
423 | * @nb: notifier block | ||
424 | * @action: notifier action | ||
425 | * @arg: reconfig data | ||
426 | * | ||
427 | * This notifier handles programming a FPGA when a "firmware-name" property is | ||
428 | * added to a fpga-region. | ||
429 | * | ||
430 | * Returns NOTIFY_OK or error if FPGA programming fails. | ||
431 | */ | ||
432 | static int of_fpga_region_notify(struct notifier_block *nb, | ||
433 | unsigned long action, void *arg) | ||
434 | { | ||
435 | struct of_overlay_notify_data *nd = arg; | ||
436 | struct fpga_region *region; | ||
437 | int ret; | ||
438 | |||
439 | switch (action) { | ||
440 | case OF_OVERLAY_PRE_APPLY: | ||
441 | pr_debug("%s OF_OVERLAY_PRE_APPLY\n", __func__); | ||
442 | break; | ||
443 | case OF_OVERLAY_POST_APPLY: | ||
444 | pr_debug("%s OF_OVERLAY_POST_APPLY\n", __func__); | ||
445 | return NOTIFY_OK; /* not for us */ | ||
446 | case OF_OVERLAY_PRE_REMOVE: | ||
447 | pr_debug("%s OF_OVERLAY_PRE_REMOVE\n", __func__); | ||
448 | return NOTIFY_OK; /* not for us */ | ||
449 | case OF_OVERLAY_POST_REMOVE: | ||
450 | pr_debug("%s OF_OVERLAY_POST_REMOVE\n", __func__); | ||
451 | break; | ||
452 | default: /* should not happen */ | ||
453 | return NOTIFY_OK; | ||
454 | } | ||
455 | |||
456 | region = fpga_region_find(nd->target); | ||
457 | if (!region) | ||
458 | return NOTIFY_OK; | ||
459 | |||
460 | ret = 0; | ||
461 | switch (action) { | ||
462 | case OF_OVERLAY_PRE_APPLY: | ||
463 | ret = fpga_region_notify_pre_apply(region, nd); | ||
464 | break; | ||
465 | |||
466 | case OF_OVERLAY_POST_REMOVE: | ||
467 | fpga_region_notify_post_remove(region, nd); | ||
468 | break; | ||
469 | } | ||
470 | |||
471 | put_device(®ion->dev); | ||
472 | |||
473 | if (ret) | ||
474 | return notifier_from_errno(ret); | ||
475 | |||
476 | return NOTIFY_OK; | ||
477 | } | ||
478 | |||
479 | static struct notifier_block fpga_region_of_nb = { | ||
480 | .notifier_call = of_fpga_region_notify, | ||
481 | }; | ||
482 | |||
483 | static int fpga_region_probe(struct platform_device *pdev) | ||
484 | { | ||
485 | struct device *dev = &pdev->dev; | ||
486 | struct device_node *np = dev->of_node; | ||
487 | struct fpga_region *region; | ||
488 | int id, ret = 0; | ||
489 | |||
490 | region = kzalloc(sizeof(*region), GFP_KERNEL); | ||
491 | if (!region) | ||
492 | return -ENOMEM; | ||
493 | |||
494 | id = ida_simple_get(&fpga_region_ida, 0, 0, GFP_KERNEL); | ||
495 | if (id < 0) { | ||
496 | ret = id; | ||
497 | goto err_kfree; | ||
498 | } | ||
499 | |||
500 | mutex_init(®ion->mutex); | ||
501 | INIT_LIST_HEAD(®ion->bridge_list); | ||
502 | |||
503 | device_initialize(®ion->dev); | ||
504 | region->dev.class = fpga_region_class; | ||
505 | region->dev.parent = dev; | ||
506 | region->dev.of_node = np; | ||
507 | region->dev.id = id; | ||
508 | dev_set_drvdata(dev, region); | ||
509 | |||
510 | ret = dev_set_name(®ion->dev, "region%d", id); | ||
511 | if (ret) | ||
512 | goto err_remove; | ||
513 | |||
514 | ret = device_add(®ion->dev); | ||
515 | if (ret) | ||
516 | goto err_remove; | ||
517 | |||
518 | of_platform_populate(np, fpga_region_of_match, NULL, ®ion->dev); | ||
519 | |||
520 | dev_info(dev, "FPGA Region probed\n"); | ||
521 | |||
522 | return 0; | ||
523 | |||
524 | err_remove: | ||
525 | ida_simple_remove(&fpga_region_ida, id); | ||
526 | err_kfree: | ||
527 | kfree(region); | ||
528 | |||
529 | return ret; | ||
530 | } | ||
531 | |||
532 | static int fpga_region_remove(struct platform_device *pdev) | ||
533 | { | ||
534 | struct fpga_region *region = platform_get_drvdata(pdev); | ||
535 | |||
536 | device_unregister(®ion->dev); | ||
537 | |||
538 | return 0; | ||
539 | } | ||
540 | |||
541 | static struct platform_driver fpga_region_driver = { | ||
542 | .probe = fpga_region_probe, | ||
543 | .remove = fpga_region_remove, | ||
544 | .driver = { | ||
545 | .name = "fpga-region", | ||
546 | .of_match_table = of_match_ptr(fpga_region_of_match), | ||
547 | }, | ||
548 | }; | ||
549 | |||
550 | static void fpga_region_dev_release(struct device *dev) | ||
551 | { | ||
552 | struct fpga_region *region = to_fpga_region(dev); | ||
553 | |||
554 | ida_simple_remove(&fpga_region_ida, region->dev.id); | ||
555 | kfree(region); | ||
556 | } | ||
557 | |||
558 | /** | ||
559 | * fpga_region_init - init function for fpga_region class | ||
560 | * Creates the fpga_region class and registers a reconfig notifier. | ||
561 | */ | ||
562 | static int __init fpga_region_init(void) | ||
563 | { | ||
564 | int ret; | ||
565 | |||
566 | fpga_region_class = class_create(THIS_MODULE, "fpga_region"); | ||
567 | if (IS_ERR(fpga_region_class)) | ||
568 | return PTR_ERR(fpga_region_class); | ||
569 | |||
570 | fpga_region_class->dev_release = fpga_region_dev_release; | ||
571 | |||
572 | ret = of_overlay_notifier_register(&fpga_region_of_nb); | ||
573 | if (ret) | ||
574 | goto err_class; | ||
575 | |||
576 | ret = platform_driver_register(&fpga_region_driver); | ||
577 | if (ret) | ||
578 | goto err_plat; | ||
579 | |||
580 | return 0; | ||
581 | |||
582 | err_plat: | ||
583 | of_overlay_notifier_unregister(&fpga_region_of_nb); | ||
584 | err_class: | ||
585 | class_destroy(fpga_region_class); | ||
586 | ida_destroy(&fpga_region_ida); | ||
587 | return ret; | ||
588 | } | ||
589 | |||
590 | static void __exit fpga_region_exit(void) | ||
591 | { | ||
592 | platform_driver_unregister(&fpga_region_driver); | ||
593 | of_overlay_notifier_unregister(&fpga_region_of_nb); | ||
594 | class_destroy(fpga_region_class); | ||
595 | ida_destroy(&fpga_region_ida); | ||
596 | } | ||
597 | |||
598 | subsys_initcall(fpga_region_init); | ||
599 | module_exit(fpga_region_exit); | ||
600 | |||
601 | MODULE_DESCRIPTION("FPGA Region"); | ||
602 | MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>"); | ||
603 | MODULE_LICENSE("GPL v2"); | ||