]>
Commit | Line | Data |
---|---|---|
6a8c3be7 AT |
1 | /* |
2 | * FPGA Manager Core | |
3 | * | |
4 | * Copyright (C) 2013-2015 Altera Corporation | |
5 | * | |
6 | * With code from the mailing list: | |
7 | * Copyright (C) 2013 Xilinx, Inc. | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify it | |
10 | * under the terms and conditions of the GNU General Public License, | |
11 | * version 2, as published by the Free Software Foundation. | |
12 | * | |
13 | * This program is distributed in the hope it will be useful, but WITHOUT | |
14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
16 | * more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License along with | |
19 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
20 | */ | |
21 | #include <linux/firmware.h> | |
22 | #include <linux/fpga/fpga-mgr.h> | |
23 | #include <linux/idr.h> | |
24 | #include <linux/module.h> | |
25 | #include <linux/of.h> | |
26 | #include <linux/mutex.h> | |
27 | #include <linux/slab.h> | |
baa6d396 JG |
28 | #include <linux/scatterlist.h> |
29 | #include <linux/highmem.h> | |
6a8c3be7 AT |
30 | |
31 | static DEFINE_IDA(fpga_mgr_ida); | |
32 | static struct class *fpga_mgr_class; | |
33 | ||
baa6d396 JG |
34 | /* |
35 | * Call the low level driver's write_init function. This will do the | |
36 | * device-specific things to get the FPGA into the state where it is ready to | |
37 | * receive an FPGA image. The low level driver only gets to see the first | |
38 | * initial_header_size bytes in the buffer. | |
39 | */ | |
40 | static int fpga_mgr_write_init_buf(struct fpga_manager *mgr, | |
41 | struct fpga_image_info *info, | |
42 | const char *buf, size_t count) | |
43 | { | |
44 | int ret; | |
45 | ||
46 | mgr->state = FPGA_MGR_STATE_WRITE_INIT; | |
47 | if (!mgr->mops->initial_header_size) | |
48 | ret = mgr->mops->write_init(mgr, info, NULL, 0); | |
49 | else | |
50 | ret = mgr->mops->write_init( | |
51 | mgr, info, buf, min(mgr->mops->initial_header_size, count)); | |
52 | ||
53 | if (ret) { | |
54 | dev_err(&mgr->dev, "Error preparing FPGA for writing\n"); | |
55 | mgr->state = FPGA_MGR_STATE_WRITE_INIT_ERR; | |
56 | return ret; | |
57 | } | |
58 | ||
59 | return 0; | |
60 | } | |
61 | ||
62 | static int fpga_mgr_write_init_sg(struct fpga_manager *mgr, | |
63 | struct fpga_image_info *info, | |
64 | struct sg_table *sgt) | |
65 | { | |
66 | struct sg_mapping_iter miter; | |
67 | size_t len; | |
68 | char *buf; | |
69 | int ret; | |
70 | ||
71 | if (!mgr->mops->initial_header_size) | |
72 | return fpga_mgr_write_init_buf(mgr, info, NULL, 0); | |
73 | ||
74 | /* | |
75 | * First try to use miter to map the first fragment to access the | |
76 | * header, this is the typical path. | |
77 | */ | |
78 | sg_miter_start(&miter, sgt->sgl, sgt->nents, SG_MITER_FROM_SG); | |
79 | if (sg_miter_next(&miter) && | |
80 | miter.length >= mgr->mops->initial_header_size) { | |
81 | ret = fpga_mgr_write_init_buf(mgr, info, miter.addr, | |
82 | miter.length); | |
83 | sg_miter_stop(&miter); | |
84 | return ret; | |
85 | } | |
86 | sg_miter_stop(&miter); | |
87 | ||
88 | /* Otherwise copy the fragments into temporary memory. */ | |
89 | buf = kmalloc(mgr->mops->initial_header_size, GFP_KERNEL); | |
90 | if (!buf) | |
91 | return -ENOMEM; | |
92 | ||
93 | len = sg_copy_to_buffer(sgt->sgl, sgt->nents, buf, | |
94 | mgr->mops->initial_header_size); | |
95 | ret = fpga_mgr_write_init_buf(mgr, info, buf, len); | |
96 | ||
97 | kfree(buf); | |
98 | ||
99 | return ret; | |
100 | } | |
101 | ||
102 | /* | |
103 | * After all the FPGA image has been written, do the device specific steps to | |
104 | * finish and set the FPGA into operating mode. | |
105 | */ | |
106 | static int fpga_mgr_write_complete(struct fpga_manager *mgr, | |
107 | struct fpga_image_info *info) | |
108 | { | |
109 | int ret; | |
110 | ||
111 | mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE; | |
112 | ret = mgr->mops->write_complete(mgr, info); | |
113 | if (ret) { | |
114 | dev_err(&mgr->dev, "Error after writing image data to FPGA\n"); | |
115 | mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE_ERR; | |
116 | return ret; | |
117 | } | |
118 | mgr->state = FPGA_MGR_STATE_OPERATING; | |
119 | ||
120 | return 0; | |
121 | } | |
122 | ||
6a8c3be7 | 123 | /** |
baa6d396 | 124 | * fpga_mgr_buf_load_sg - load fpga from image in buffer from a scatter list |
6a8c3be7 | 125 | * @mgr: fpga manager |
1df2865f | 126 | * @info: fpga image specific information |
baa6d396 | 127 | * @sgt: scatterlist table |
6a8c3be7 AT |
128 | * |
129 | * Step the low level fpga manager through the device-specific steps of getting | |
130 | * an FPGA ready to be configured, writing the image to it, then doing whatever | |
92d94a7e | 131 | * post-configuration steps necessary. This code assumes the caller got the |
9dce0287 AT |
132 | * mgr pointer from of_fpga_mgr_get() or fpga_mgr_get() and checked that it is |
133 | * not an error code. | |
6a8c3be7 | 134 | * |
baa6d396 JG |
135 | * This is the preferred entry point for FPGA programming, it does not require |
136 | * any contiguous kernel memory. | |
137 | * | |
6a8c3be7 AT |
138 | * Return: 0 on success, negative error code otherwise. |
139 | */ | |
baa6d396 JG |
140 | int fpga_mgr_buf_load_sg(struct fpga_manager *mgr, struct fpga_image_info *info, |
141 | struct sg_table *sgt) | |
6a8c3be7 | 142 | { |
6a8c3be7 AT |
143 | int ret; |
144 | ||
baa6d396 JG |
145 | ret = fpga_mgr_write_init_sg(mgr, info, sgt); |
146 | if (ret) | |
147 | return ret; | |
148 | ||
149 | /* Write the FPGA image to the FPGA. */ | |
150 | mgr->state = FPGA_MGR_STATE_WRITE; | |
151 | if (mgr->mops->write_sg) { | |
152 | ret = mgr->mops->write_sg(mgr, sgt); | |
153 | } else { | |
154 | struct sg_mapping_iter miter; | |
155 | ||
156 | sg_miter_start(&miter, sgt->sgl, sgt->nents, SG_MITER_FROM_SG); | |
157 | while (sg_miter_next(&miter)) { | |
158 | ret = mgr->mops->write(mgr, miter.addr, miter.length); | |
159 | if (ret) | |
160 | break; | |
161 | } | |
162 | sg_miter_stop(&miter); | |
163 | } | |
164 | ||
6a8c3be7 | 165 | if (ret) { |
baa6d396 JG |
166 | dev_err(&mgr->dev, "Error while writing image data to FPGA\n"); |
167 | mgr->state = FPGA_MGR_STATE_WRITE_ERR; | |
6a8c3be7 AT |
168 | return ret; |
169 | } | |
170 | ||
baa6d396 JG |
171 | return fpga_mgr_write_complete(mgr, info); |
172 | } | |
173 | EXPORT_SYMBOL_GPL(fpga_mgr_buf_load_sg); | |
174 | ||
175 | static int fpga_mgr_buf_load_mapped(struct fpga_manager *mgr, | |
176 | struct fpga_image_info *info, | |
177 | const char *buf, size_t count) | |
178 | { | |
179 | int ret; | |
180 | ||
181 | ret = fpga_mgr_write_init_buf(mgr, info, buf, count); | |
182 | if (ret) | |
183 | return ret; | |
184 | ||
6a8c3be7 AT |
185 | /* |
186 | * Write the FPGA image to the FPGA. | |
187 | */ | |
188 | mgr->state = FPGA_MGR_STATE_WRITE; | |
189 | ret = mgr->mops->write(mgr, buf, count); | |
190 | if (ret) { | |
baa6d396 | 191 | dev_err(&mgr->dev, "Error while writing image data to FPGA\n"); |
6a8c3be7 AT |
192 | mgr->state = FPGA_MGR_STATE_WRITE_ERR; |
193 | return ret; | |
194 | } | |
195 | ||
baa6d396 JG |
196 | return fpga_mgr_write_complete(mgr, info); |
197 | } | |
198 | ||
199 | /** | |
200 | * fpga_mgr_buf_load - load fpga from image in buffer | |
201 | * @mgr: fpga manager | |
202 | * @flags: flags setting fpga confuration modes | |
203 | * @buf: buffer contain fpga image | |
204 | * @count: byte count of buf | |
205 | * | |
206 | * Step the low level fpga manager through the device-specific steps of getting | |
207 | * an FPGA ready to be configured, writing the image to it, then doing whatever | |
208 | * post-configuration steps necessary. This code assumes the caller got the | |
209 | * mgr pointer from of_fpga_mgr_get() and checked that it is not an error code. | |
210 | * | |
211 | * Return: 0 on success, negative error code otherwise. | |
212 | */ | |
213 | int fpga_mgr_buf_load(struct fpga_manager *mgr, struct fpga_image_info *info, | |
214 | const char *buf, size_t count) | |
215 | { | |
216 | struct page **pages; | |
217 | struct sg_table sgt; | |
218 | const void *p; | |
219 | int nr_pages; | |
220 | int index; | |
221 | int rc; | |
222 | ||
6a8c3be7 | 223 | /* |
baa6d396 JG |
224 | * This is just a fast path if the caller has already created a |
225 | * contiguous kernel buffer and the driver doesn't require SG, non-SG | |
226 | * drivers will still work on the slow path. | |
6a8c3be7 | 227 | */ |
baa6d396 JG |
228 | if (mgr->mops->write) |
229 | return fpga_mgr_buf_load_mapped(mgr, info, buf, count); | |
230 | ||
231 | /* | |
232 | * Convert the linear kernel pointer into a sg_table of pages for use | |
233 | * by the driver. | |
234 | */ | |
235 | nr_pages = DIV_ROUND_UP((unsigned long)buf + count, PAGE_SIZE) - | |
236 | (unsigned long)buf / PAGE_SIZE; | |
237 | pages = kmalloc_array(nr_pages, sizeof(struct page *), GFP_KERNEL); | |
238 | if (!pages) | |
239 | return -ENOMEM; | |
240 | ||
241 | p = buf - offset_in_page(buf); | |
242 | for (index = 0; index < nr_pages; index++) { | |
243 | if (is_vmalloc_addr(p)) | |
244 | pages[index] = vmalloc_to_page(p); | |
245 | else | |
246 | pages[index] = kmap_to_page((void *)p); | |
247 | if (!pages[index]) { | |
248 | kfree(pages); | |
249 | return -EFAULT; | |
250 | } | |
251 | p += PAGE_SIZE; | |
6a8c3be7 | 252 | } |
6a8c3be7 | 253 | |
baa6d396 JG |
254 | /* |
255 | * The temporary pages list is used to code share the merging algorithm | |
256 | * in sg_alloc_table_from_pages | |
257 | */ | |
258 | rc = sg_alloc_table_from_pages(&sgt, pages, index, offset_in_page(buf), | |
259 | count, GFP_KERNEL); | |
260 | kfree(pages); | |
261 | if (rc) | |
262 | return rc; | |
263 | ||
264 | rc = fpga_mgr_buf_load_sg(mgr, info, &sgt); | |
265 | sg_free_table(&sgt); | |
266 | ||
267 | return rc; | |
6a8c3be7 AT |
268 | } |
269 | EXPORT_SYMBOL_GPL(fpga_mgr_buf_load); | |
270 | ||
271 | /** | |
272 | * fpga_mgr_firmware_load - request firmware and load to fpga | |
273 | * @mgr: fpga manager | |
1df2865f | 274 | * @info: fpga image specific information |
6a8c3be7 AT |
275 | * @image_name: name of image file on the firmware search path |
276 | * | |
277 | * Request an FPGA image using the firmware class, then write out to the FPGA. | |
278 | * Update the state before each step to provide info on what step failed if | |
92d94a7e | 279 | * there is a failure. This code assumes the caller got the mgr pointer |
9dce0287 AT |
280 | * from of_fpga_mgr_get() or fpga_mgr_get() and checked that it is not an error |
281 | * code. | |
6a8c3be7 AT |
282 | * |
283 | * Return: 0 on success, negative error code otherwise. | |
284 | */ | |
1df2865f AT |
285 | int fpga_mgr_firmware_load(struct fpga_manager *mgr, |
286 | struct fpga_image_info *info, | |
6a8c3be7 AT |
287 | const char *image_name) |
288 | { | |
289 | struct device *dev = &mgr->dev; | |
290 | const struct firmware *fw; | |
291 | int ret; | |
292 | ||
6a8c3be7 AT |
293 | dev_info(dev, "writing %s to %s\n", image_name, mgr->name); |
294 | ||
295 | mgr->state = FPGA_MGR_STATE_FIRMWARE_REQ; | |
296 | ||
297 | ret = request_firmware(&fw, image_name, dev); | |
298 | if (ret) { | |
299 | mgr->state = FPGA_MGR_STATE_FIRMWARE_REQ_ERR; | |
300 | dev_err(dev, "Error requesting firmware %s\n", image_name); | |
301 | return ret; | |
302 | } | |
303 | ||
1df2865f | 304 | ret = fpga_mgr_buf_load(mgr, info, fw->data, fw->size); |
6a8c3be7 AT |
305 | |
306 | release_firmware(fw); | |
307 | ||
e8c77bda | 308 | return ret; |
6a8c3be7 AT |
309 | } |
310 | EXPORT_SYMBOL_GPL(fpga_mgr_firmware_load); | |
311 | ||
312 | static const char * const state_str[] = { | |
313 | [FPGA_MGR_STATE_UNKNOWN] = "unknown", | |
314 | [FPGA_MGR_STATE_POWER_OFF] = "power off", | |
315 | [FPGA_MGR_STATE_POWER_UP] = "power up", | |
316 | [FPGA_MGR_STATE_RESET] = "reset", | |
317 | ||
318 | /* requesting FPGA image from firmware */ | |
319 | [FPGA_MGR_STATE_FIRMWARE_REQ] = "firmware request", | |
320 | [FPGA_MGR_STATE_FIRMWARE_REQ_ERR] = "firmware request error", | |
321 | ||
322 | /* Preparing FPGA to receive image */ | |
323 | [FPGA_MGR_STATE_WRITE_INIT] = "write init", | |
324 | [FPGA_MGR_STATE_WRITE_INIT_ERR] = "write init error", | |
325 | ||
326 | /* Writing image to FPGA */ | |
327 | [FPGA_MGR_STATE_WRITE] = "write", | |
328 | [FPGA_MGR_STATE_WRITE_ERR] = "write error", | |
329 | ||
330 | /* Finishing configuration after image has been written */ | |
331 | [FPGA_MGR_STATE_WRITE_COMPLETE] = "write complete", | |
332 | [FPGA_MGR_STATE_WRITE_COMPLETE_ERR] = "write complete error", | |
333 | ||
334 | /* FPGA reports to be in normal operating mode */ | |
335 | [FPGA_MGR_STATE_OPERATING] = "operating", | |
336 | }; | |
337 | ||
338 | static ssize_t name_show(struct device *dev, | |
339 | struct device_attribute *attr, char *buf) | |
340 | { | |
341 | struct fpga_manager *mgr = to_fpga_manager(dev); | |
342 | ||
343 | return sprintf(buf, "%s\n", mgr->name); | |
344 | } | |
345 | ||
346 | static ssize_t state_show(struct device *dev, | |
347 | struct device_attribute *attr, char *buf) | |
348 | { | |
349 | struct fpga_manager *mgr = to_fpga_manager(dev); | |
350 | ||
351 | return sprintf(buf, "%s\n", state_str[mgr->state]); | |
352 | } | |
353 | ||
354 | static DEVICE_ATTR_RO(name); | |
355 | static DEVICE_ATTR_RO(state); | |
356 | ||
357 | static struct attribute *fpga_mgr_attrs[] = { | |
358 | &dev_attr_name.attr, | |
359 | &dev_attr_state.attr, | |
360 | NULL, | |
361 | }; | |
362 | ATTRIBUTE_GROUPS(fpga_mgr); | |
363 | ||
47910a49 | 364 | static struct fpga_manager *__fpga_mgr_get(struct device *dev) |
6a8c3be7 AT |
365 | { |
366 | struct fpga_manager *mgr; | |
654ba4cc | 367 | int ret = -ENODEV; |
6a8c3be7 | 368 | |
6a8c3be7 | 369 | mgr = to_fpga_manager(dev); |
6a8c3be7 | 370 | if (!mgr) |
654ba4cc | 371 | goto err_dev; |
6a8c3be7 AT |
372 | |
373 | /* Get exclusive use of fpga manager */ | |
654ba4cc AT |
374 | if (!mutex_trylock(&mgr->ref_mutex)) { |
375 | ret = -EBUSY; | |
376 | goto err_dev; | |
6a8c3be7 AT |
377 | } |
378 | ||
654ba4cc AT |
379 | if (!try_module_get(dev->parent->driver->owner)) |
380 | goto err_ll_mod; | |
381 | ||
6a8c3be7 | 382 | return mgr; |
654ba4cc AT |
383 | |
384 | err_ll_mod: | |
385 | mutex_unlock(&mgr->ref_mutex); | |
386 | err_dev: | |
387 | put_device(dev); | |
388 | return ERR_PTR(ret); | |
6a8c3be7 | 389 | } |
9dce0287 AT |
390 | |
391 | static int fpga_mgr_dev_match(struct device *dev, const void *data) | |
392 | { | |
393 | return dev->parent == data; | |
394 | } | |
395 | ||
396 | /** | |
397 | * fpga_mgr_get - get an exclusive reference to a fpga mgr | |
398 | * @dev: parent device that fpga mgr was registered with | |
399 | * | |
400 | * Given a device, get an exclusive reference to a fpga mgr. | |
401 | * | |
402 | * Return: fpga manager struct or IS_ERR() condition containing error code. | |
403 | */ | |
404 | struct fpga_manager *fpga_mgr_get(struct device *dev) | |
405 | { | |
406 | struct device *mgr_dev = class_find_device(fpga_mgr_class, NULL, dev, | |
407 | fpga_mgr_dev_match); | |
408 | if (!mgr_dev) | |
409 | return ERR_PTR(-ENODEV); | |
410 | ||
411 | return __fpga_mgr_get(mgr_dev); | |
412 | } | |
413 | EXPORT_SYMBOL_GPL(fpga_mgr_get); | |
414 | ||
415 | static int fpga_mgr_of_node_match(struct device *dev, const void *data) | |
416 | { | |
417 | return dev->of_node == data; | |
418 | } | |
419 | ||
420 | /** | |
421 | * of_fpga_mgr_get - get an exclusive reference to a fpga mgr | |
422 | * @node: device node | |
423 | * | |
424 | * Given a device node, get an exclusive reference to a fpga mgr. | |
425 | * | |
426 | * Return: fpga manager struct or IS_ERR() condition containing error code. | |
427 | */ | |
428 | struct fpga_manager *of_fpga_mgr_get(struct device_node *node) | |
429 | { | |
430 | struct device *dev; | |
431 | ||
432 | dev = class_find_device(fpga_mgr_class, NULL, node, | |
433 | fpga_mgr_of_node_match); | |
434 | if (!dev) | |
435 | return ERR_PTR(-ENODEV); | |
436 | ||
437 | return __fpga_mgr_get(dev); | |
438 | } | |
6a8c3be7 AT |
439 | EXPORT_SYMBOL_GPL(of_fpga_mgr_get); |
440 | ||
441 | /** | |
442 | * fpga_mgr_put - release a reference to a fpga manager | |
443 | * @mgr: fpga manager structure | |
444 | */ | |
445 | void fpga_mgr_put(struct fpga_manager *mgr) | |
446 | { | |
654ba4cc AT |
447 | module_put(mgr->dev.parent->driver->owner); |
448 | mutex_unlock(&mgr->ref_mutex); | |
449 | put_device(&mgr->dev); | |
6a8c3be7 AT |
450 | } |
451 | EXPORT_SYMBOL_GPL(fpga_mgr_put); | |
452 | ||
453 | /** | |
454 | * fpga_mgr_register - register a low level fpga manager driver | |
455 | * @dev: fpga manager device from pdev | |
456 | * @name: fpga manager name | |
457 | * @mops: pointer to structure of fpga manager ops | |
458 | * @priv: fpga manager private data | |
459 | * | |
460 | * Return: 0 on success, negative error code otherwise. | |
461 | */ | |
462 | int fpga_mgr_register(struct device *dev, const char *name, | |
463 | const struct fpga_manager_ops *mops, | |
464 | void *priv) | |
465 | { | |
466 | struct fpga_manager *mgr; | |
6a8c3be7 AT |
467 | int id, ret; |
468 | ||
baa6d396 JG |
469 | if (!mops || !mops->write_complete || !mops->state || |
470 | !mops->write_init || (!mops->write && !mops->write_sg) || | |
471 | (mops->write && mops->write_sg)) { | |
6a8c3be7 AT |
472 | dev_err(dev, "Attempt to register without fpga_manager_ops\n"); |
473 | return -EINVAL; | |
474 | } | |
475 | ||
476 | if (!name || !strlen(name)) { | |
477 | dev_err(dev, "Attempt to register with no name!\n"); | |
478 | return -EINVAL; | |
479 | } | |
480 | ||
481 | mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); | |
482 | if (!mgr) | |
483 | return -ENOMEM; | |
484 | ||
485 | id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL); | |
486 | if (id < 0) { | |
487 | ret = id; | |
488 | goto error_kfree; | |
489 | } | |
490 | ||
491 | mutex_init(&mgr->ref_mutex); | |
492 | ||
493 | mgr->name = name; | |
494 | mgr->mops = mops; | |
495 | mgr->priv = priv; | |
496 | ||
497 | /* | |
498 | * Initialize framework state by requesting low level driver read state | |
499 | * from device. FPGA may be in reset mode or may have been programmed | |
500 | * by bootloader or EEPROM. | |
501 | */ | |
502 | mgr->state = mgr->mops->state(mgr); | |
503 | ||
504 | device_initialize(&mgr->dev); | |
505 | mgr->dev.class = fpga_mgr_class; | |
506 | mgr->dev.parent = dev; | |
507 | mgr->dev.of_node = dev->of_node; | |
508 | mgr->dev.id = id; | |
509 | dev_set_drvdata(dev, mgr); | |
510 | ||
07687c03 AT |
511 | ret = dev_set_name(&mgr->dev, "fpga%d", id); |
512 | if (ret) | |
513 | goto error_device; | |
6a8c3be7 AT |
514 | |
515 | ret = device_add(&mgr->dev); | |
516 | if (ret) | |
517 | goto error_device; | |
518 | ||
519 | dev_info(&mgr->dev, "%s registered\n", mgr->name); | |
520 | ||
521 | return 0; | |
522 | ||
523 | error_device: | |
524 | ida_simple_remove(&fpga_mgr_ida, id); | |
525 | error_kfree: | |
526 | kfree(mgr); | |
527 | ||
528 | return ret; | |
529 | } | |
530 | EXPORT_SYMBOL_GPL(fpga_mgr_register); | |
531 | ||
532 | /** | |
533 | * fpga_mgr_unregister - unregister a low level fpga manager driver | |
534 | * @dev: fpga manager device from pdev | |
535 | */ | |
536 | void fpga_mgr_unregister(struct device *dev) | |
537 | { | |
538 | struct fpga_manager *mgr = dev_get_drvdata(dev); | |
539 | ||
540 | dev_info(&mgr->dev, "%s %s\n", __func__, mgr->name); | |
541 | ||
542 | /* | |
543 | * If the low level driver provides a method for putting fpga into | |
544 | * a desired state upon unregister, do it. | |
545 | */ | |
546 | if (mgr->mops->fpga_remove) | |
547 | mgr->mops->fpga_remove(mgr); | |
548 | ||
549 | device_unregister(&mgr->dev); | |
550 | } | |
551 | EXPORT_SYMBOL_GPL(fpga_mgr_unregister); | |
552 | ||
553 | static void fpga_mgr_dev_release(struct device *dev) | |
554 | { | |
555 | struct fpga_manager *mgr = to_fpga_manager(dev); | |
556 | ||
557 | ida_simple_remove(&fpga_mgr_ida, mgr->dev.id); | |
558 | kfree(mgr); | |
559 | } | |
560 | ||
561 | static int __init fpga_mgr_class_init(void) | |
562 | { | |
563 | pr_info("FPGA manager framework\n"); | |
564 | ||
565 | fpga_mgr_class = class_create(THIS_MODULE, "fpga_manager"); | |
566 | if (IS_ERR(fpga_mgr_class)) | |
567 | return PTR_ERR(fpga_mgr_class); | |
568 | ||
569 | fpga_mgr_class->dev_groups = fpga_mgr_groups; | |
570 | fpga_mgr_class->dev_release = fpga_mgr_dev_release; | |
571 | ||
572 | return 0; | |
573 | } | |
574 | ||
575 | static void __exit fpga_mgr_class_exit(void) | |
576 | { | |
577 | class_destroy(fpga_mgr_class); | |
578 | ida_destroy(&fpga_mgr_ida); | |
579 | } | |
580 | ||
581 | MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>"); | |
582 | MODULE_DESCRIPTION("FPGA manager framework"); | |
583 | MODULE_LICENSE("GPL v2"); | |
584 | ||
585 | subsys_initcall(fpga_mgr_class_init); | |
586 | module_exit(fpga_mgr_class_exit); |