1 | /***************************************************************************** |
2 | * |
3 | * Author: Xilinx, Inc. |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify it |
6 | * under the terms of the GNU General Public License as published by the |
7 | * Free Software Foundation; either version 2 of the License, or (at your |
8 | * option) any later version. |
9 | * |
10 | * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" |
11 | * AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND |
12 | * SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, |
13 | * OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, |
14 | * APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION |
15 | * THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, |
16 | * AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE |
17 | * FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY |
18 | * WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE |
19 | * IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR |
20 | * REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF |
21 | * INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
22 | * FOR A PARTICULAR PURPOSE. |
23 | * |
24 | * (c) Copyright 2002 Xilinx Inc., Systems Engineering Group |
25 | * (c) Copyright 2004 Xilinx Inc., Systems Engineering Group |
26 | * (c) Copyright 2007-2008 Xilinx Inc. |
27 | * All rights reserved. |
28 | * |
29 | * You should have received a copy of the GNU General Public License along |
30 | * with this program; if not, write to the Free Software Foundation, Inc., |
31 | * 675 Mass Ave, Cambridge, MA 02139, USA. |
32 | * |
33 | *****************************************************************************/ |
34 | |
35 | /* |
36 | * This is the code behind /dev/icap* -- it allows a user-space |
37 | * application to use the Xilinx ICAP subsystem. |
38 | * |
39 | * The following operations are possible: |
40 | * |
41 | * open open the port and initialize for access. |
42 | * release release port |
43 | * write Write a bitstream to the configuration processor. |
44 | * read Read a data stream from the configuration processor. |
45 | * |
46 | * After being opened, the port is initialized and accessed to avoid a |
47 | * corrupted first read which may occur with some hardware. The port |
48 | * is left in a desynched state, requiring that a synch sequence be |
49 | * transmitted before any valid configuration data. A user will have |
50 | * exclusive access to the device while it remains open, and the state |
51 | * of the ICAP cannot be guaranteed after the device is closed. Note |
52 | * that a complete reset of the core and the state of the ICAP cannot |
53 | * be performed on many versions of the cores, hence users of this |
54 | * device should avoid making inconsistent accesses to the device. In |
55 | * particular, accessing the read interface, without first generating |
56 | * a write containing a readback packet can leave the ICAP in an |
57 | * inaccessible state. |
58 | * |
59 | * Note that in order to use the read interface, it is first necessary |
60 | * to write a request packet to the write interface. i.e., it is not |
61 | * possible to simply readback the bitstream (or any configuration |
62 | * bits) from a device without specifically requesting them first. |
63 | * The code to craft such packets is intended to be part of the |
64 | * user-space application code that uses this device. The simplest |
65 | * way to use this interface is simply: |
66 | * |
67 | * cp foo.bit /dev/icap0 |
68 | * |
69 | * Note that unless foo.bit is an appropriately constructed partial |
70 | * bitstream, this has a high likelihood of overwriting the design |
71 | * currently programmed in the FPGA. |
72 | */ |
73 | |
74 | #include <linux/module.h> |
75 | #include <linux/kernel.h> |
76 | #include <linux/types.h> |
77 | #include <linux/ioport.h> |
78 | #include <linux/interrupt.h> |
79 | #include <linux/fcntl.h> |
80 | #include <linux/init.h> |
81 | #include <linux/poll.h> |
82 | #include <linux/proc_fs.h> |
83 | #include <linux/mutex.h> |
84 | #include <linux/sysctl.h> |
85 | #include <linux/fs.h> |
86 | #include <linux/cdev.h> |
87 | #include <linux/of.h> |
88 | #include <linux/platform_device.h> |
89 | #include <linux/property.h> |
90 | #include <linux/slab.h> |
91 | #include <linux/io.h> |
92 | #include <linux/uaccess.h> |
93 | |
94 | #include "xilinx_hwicap.h" |
95 | #include "buffer_icap.h" |
96 | #include "fifo_icap.h" |
97 | |
98 | #define DRIVER_NAME "icap" |
99 | |
100 | #define HWICAP_REGS (0x10000) |
101 | |
102 | #define XHWICAP_MAJOR 259 |
103 | #define XHWICAP_MINOR 0 |
104 | #define HWICAP_DEVICES 1 |
105 | |
106 | /* An array, which is set to true when the device is registered. */ |
107 | static DEFINE_MUTEX(hwicap_mutex); |
108 | static bool probed_devices[HWICAP_DEVICES]; |
109 | static struct mutex icap_sem; |
110 | |
111 | static const struct class icap_class = { |
112 | .name = "xilinx_config" , |
113 | }; |
114 | |
115 | #define UNIMPLEMENTED 0xFFFF |
116 | |
117 | static const struct config_registers v2_config_registers = { |
118 | .CRC = 0, |
119 | .FAR = 1, |
120 | .FDRI = 2, |
121 | .FDRO = 3, |
122 | .CMD = 4, |
123 | .CTL = 5, |
124 | .MASK = 6, |
125 | .STAT = 7, |
126 | .LOUT = 8, |
127 | .COR = 9, |
128 | .MFWR = 10, |
129 | .FLR = 11, |
130 | .KEY = 12, |
131 | .CBC = 13, |
132 | .IDCODE = 14, |
133 | .AXSS = UNIMPLEMENTED, |
134 | .C0R_1 = UNIMPLEMENTED, |
135 | .CSOB = UNIMPLEMENTED, |
136 | .WBSTAR = UNIMPLEMENTED, |
137 | .TIMER = UNIMPLEMENTED, |
138 | .BOOTSTS = UNIMPLEMENTED, |
139 | .CTL_1 = UNIMPLEMENTED, |
140 | }; |
141 | |
142 | static const struct config_registers v4_config_registers = { |
143 | .CRC = 0, |
144 | .FAR = 1, |
145 | .FDRI = 2, |
146 | .FDRO = 3, |
147 | .CMD = 4, |
148 | .CTL = 5, |
149 | .MASK = 6, |
150 | .STAT = 7, |
151 | .LOUT = 8, |
152 | .COR = 9, |
153 | .MFWR = 10, |
154 | .FLR = UNIMPLEMENTED, |
155 | .KEY = UNIMPLEMENTED, |
156 | .CBC = 11, |
157 | .IDCODE = 12, |
158 | .AXSS = 13, |
159 | .C0R_1 = UNIMPLEMENTED, |
160 | .CSOB = UNIMPLEMENTED, |
161 | .WBSTAR = UNIMPLEMENTED, |
162 | .TIMER = UNIMPLEMENTED, |
163 | .BOOTSTS = UNIMPLEMENTED, |
164 | .CTL_1 = UNIMPLEMENTED, |
165 | }; |
166 | |
167 | static const struct config_registers v5_config_registers = { |
168 | .CRC = 0, |
169 | .FAR = 1, |
170 | .FDRI = 2, |
171 | .FDRO = 3, |
172 | .CMD = 4, |
173 | .CTL = 5, |
174 | .MASK = 6, |
175 | .STAT = 7, |
176 | .LOUT = 8, |
177 | .COR = 9, |
178 | .MFWR = 10, |
179 | .FLR = UNIMPLEMENTED, |
180 | .KEY = UNIMPLEMENTED, |
181 | .CBC = 11, |
182 | .IDCODE = 12, |
183 | .AXSS = 13, |
184 | .C0R_1 = 14, |
185 | .CSOB = 15, |
186 | .WBSTAR = 16, |
187 | .TIMER = 17, |
188 | .BOOTSTS = 18, |
189 | .CTL_1 = 19, |
190 | }; |
191 | |
192 | static const struct config_registers v6_config_registers = { |
193 | .CRC = 0, |
194 | .FAR = 1, |
195 | .FDRI = 2, |
196 | .FDRO = 3, |
197 | .CMD = 4, |
198 | .CTL = 5, |
199 | .MASK = 6, |
200 | .STAT = 7, |
201 | .LOUT = 8, |
202 | .COR = 9, |
203 | .MFWR = 10, |
204 | .FLR = UNIMPLEMENTED, |
205 | .KEY = UNIMPLEMENTED, |
206 | .CBC = 11, |
207 | .IDCODE = 12, |
208 | .AXSS = 13, |
209 | .C0R_1 = 14, |
210 | .CSOB = 15, |
211 | .WBSTAR = 16, |
212 | .TIMER = 17, |
213 | .BOOTSTS = 22, |
214 | .CTL_1 = 24, |
215 | }; |
216 | |
217 | /** |
218 | * hwicap_command_desync - Send a DESYNC command to the ICAP port. |
219 | * @drvdata: a pointer to the drvdata. |
220 | * |
221 | * Returns: '0' on success and failure value on error |
222 | * |
223 | * This command desynchronizes the ICAP After this command, a |
224 | * bitstream containing a NULL packet, followed by a SYNCH packet is |
225 | * required before the ICAP will recognize commands. |
226 | */ |
227 | static int hwicap_command_desync(struct hwicap_drvdata *drvdata) |
228 | { |
229 | u32 buffer[4]; |
230 | u32 index = 0; |
231 | |
232 | /* |
233 | * Create the data to be written to the ICAP. |
234 | */ |
235 | buffer[index++] = hwicap_type_1_write(reg: drvdata->config_regs->CMD) | 1; |
236 | buffer[index++] = XHI_CMD_DESYNCH; |
237 | buffer[index++] = XHI_NOOP_PACKET; |
238 | buffer[index++] = XHI_NOOP_PACKET; |
239 | |
240 | /* |
241 | * Write the data to the FIFO and initiate the transfer of data present |
242 | * in the FIFO to the ICAP device. |
243 | */ |
244 | return drvdata->config->set_configuration(drvdata, |
245 | &buffer[0], index); |
246 | } |
247 | |
248 | /** |
249 | * hwicap_get_configuration_register - Query a configuration register. |
250 | * @drvdata: a pointer to the drvdata. |
251 | * @reg: a constant which represents the configuration |
252 | * register value to be returned. |
253 | * Examples: XHI_IDCODE, XHI_FLR. |
254 | * @reg_data: returns the value of the register. |
255 | * |
256 | * Returns: '0' on success and failure value on error |
257 | * |
258 | * Sends a query packet to the ICAP and then receives the response. |
259 | * The icap is left in Synched state. |
260 | */ |
261 | static int hwicap_get_configuration_register(struct hwicap_drvdata *drvdata, |
262 | u32 reg, u32 *reg_data) |
263 | { |
264 | int status; |
265 | u32 buffer[6]; |
266 | u32 index = 0; |
267 | |
268 | /* |
269 | * Create the data to be written to the ICAP. |
270 | */ |
271 | buffer[index++] = XHI_DUMMY_PACKET; |
272 | buffer[index++] = XHI_NOOP_PACKET; |
273 | buffer[index++] = XHI_SYNC_PACKET; |
274 | buffer[index++] = XHI_NOOP_PACKET; |
275 | buffer[index++] = XHI_NOOP_PACKET; |
276 | |
277 | /* |
278 | * Write the data to the FIFO and initiate the transfer of data present |
279 | * in the FIFO to the ICAP device. |
280 | */ |
281 | status = drvdata->config->set_configuration(drvdata, |
282 | &buffer[0], index); |
283 | if (status) |
284 | return status; |
285 | |
286 | /* If the syncword was not found, then we need to start over. */ |
287 | status = drvdata->config->get_status(drvdata); |
288 | if ((status & XHI_SR_DALIGN_MASK) != XHI_SR_DALIGN_MASK) |
289 | return -EIO; |
290 | |
291 | index = 0; |
292 | buffer[index++] = hwicap_type_1_read(reg) | 1; |
293 | buffer[index++] = XHI_NOOP_PACKET; |
294 | buffer[index++] = XHI_NOOP_PACKET; |
295 | |
296 | /* |
297 | * Write the data to the FIFO and initiate the transfer of data present |
298 | * in the FIFO to the ICAP device. |
299 | */ |
300 | status = drvdata->config->set_configuration(drvdata, |
301 | &buffer[0], index); |
302 | if (status) |
303 | return status; |
304 | |
305 | /* |
306 | * Read the configuration register |
307 | */ |
308 | status = drvdata->config->get_configuration(drvdata, reg_data, 1); |
309 | if (status) |
310 | return status; |
311 | |
312 | return 0; |
313 | } |
314 | |
315 | static int hwicap_initialize_hwicap(struct hwicap_drvdata *drvdata) |
316 | { |
317 | int status; |
318 | u32 idcode; |
319 | |
320 | dev_dbg(drvdata->dev, "initializing\n" ); |
321 | |
322 | /* Abort any current transaction, to make sure we have the |
323 | * ICAP in a good state. |
324 | */ |
325 | dev_dbg(drvdata->dev, "Reset...\n" ); |
326 | drvdata->config->reset(drvdata); |
327 | |
328 | dev_dbg(drvdata->dev, "Desync...\n" ); |
329 | status = hwicap_command_desync(drvdata); |
330 | if (status) |
331 | return status; |
332 | |
333 | /* Attempt to read the IDCODE from ICAP. This |
334 | * may not be returned correctly, due to the design of the |
335 | * hardware. |
336 | */ |
337 | dev_dbg(drvdata->dev, "Reading IDCODE...\n" ); |
338 | status = hwicap_get_configuration_register( |
339 | drvdata, reg: drvdata->config_regs->IDCODE, reg_data: &idcode); |
340 | dev_dbg(drvdata->dev, "IDCODE = %x\n" , idcode); |
341 | if (status) |
342 | return status; |
343 | |
344 | dev_dbg(drvdata->dev, "Desync...\n" ); |
345 | status = hwicap_command_desync(drvdata); |
346 | if (status) |
347 | return status; |
348 | |
349 | return 0; |
350 | } |
351 | |
352 | static ssize_t |
353 | hwicap_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) |
354 | { |
355 | struct hwicap_drvdata *drvdata = file->private_data; |
356 | ssize_t bytes_to_read = 0; |
357 | u32 *kbuf; |
358 | u32 words; |
359 | u32 bytes_remaining; |
360 | int status; |
361 | |
362 | status = mutex_lock_interruptible(&drvdata->sem); |
363 | if (status) |
364 | return status; |
365 | |
366 | if (drvdata->read_buffer_in_use) { |
367 | /* If there are leftover bytes in the buffer, just */ |
368 | /* return them and don't try to read more from the */ |
369 | /* ICAP device. */ |
370 | bytes_to_read = |
371 | (count < drvdata->read_buffer_in_use) ? count : |
372 | drvdata->read_buffer_in_use; |
373 | |
374 | /* Return the data currently in the read buffer. */ |
375 | if (copy_to_user(to: buf, from: drvdata->read_buffer, n: bytes_to_read)) { |
376 | status = -EFAULT; |
377 | goto error; |
378 | } |
379 | drvdata->read_buffer_in_use -= bytes_to_read; |
380 | memmove(drvdata->read_buffer, |
381 | drvdata->read_buffer + bytes_to_read, |
382 | 4 - bytes_to_read); |
383 | } else { |
384 | /* Get new data from the ICAP, and return what was requested. */ |
385 | kbuf = (u32 *) get_zeroed_page(GFP_KERNEL); |
386 | if (!kbuf) { |
387 | status = -ENOMEM; |
388 | goto error; |
389 | } |
390 | |
391 | /* The ICAP device is only able to read complete */ |
392 | /* words. If a number of bytes that do not correspond */ |
393 | /* to complete words is requested, then we read enough */ |
394 | /* words to get the required number of bytes, and then */ |
395 | /* save the remaining bytes for the next read. */ |
396 | |
397 | /* Determine the number of words to read, rounding up */ |
398 | /* if necessary. */ |
399 | words = ((count + 3) >> 2); |
400 | bytes_to_read = words << 2; |
401 | |
402 | if (bytes_to_read > PAGE_SIZE) |
403 | bytes_to_read = PAGE_SIZE; |
404 | |
405 | /* Ensure we only read a complete number of words. */ |
406 | bytes_remaining = bytes_to_read & 3; |
407 | bytes_to_read &= ~3; |
408 | words = bytes_to_read >> 2; |
409 | |
410 | status = drvdata->config->get_configuration(drvdata, |
411 | kbuf, words); |
412 | |
413 | /* If we didn't read correctly, then bail out. */ |
414 | if (status) { |
415 | free_page((unsigned long)kbuf); |
416 | goto error; |
417 | } |
418 | |
419 | /* If we fail to return the data to the user, then bail out. */ |
420 | if (copy_to_user(to: buf, from: kbuf, n: bytes_to_read)) { |
421 | free_page((unsigned long)kbuf); |
422 | status = -EFAULT; |
423 | goto error; |
424 | } |
425 | memcpy(drvdata->read_buffer, |
426 | kbuf, |
427 | bytes_remaining); |
428 | drvdata->read_buffer_in_use = bytes_remaining; |
429 | free_page((unsigned long)kbuf); |
430 | } |
431 | status = bytes_to_read; |
432 | error: |
433 | mutex_unlock(lock: &drvdata->sem); |
434 | return status; |
435 | } |
436 | |
437 | static ssize_t |
438 | hwicap_write(struct file *file, const char __user *buf, |
439 | size_t count, loff_t *ppos) |
440 | { |
441 | struct hwicap_drvdata *drvdata = file->private_data; |
442 | ssize_t written = 0; |
443 | ssize_t left = count; |
444 | u32 *kbuf; |
445 | ssize_t len; |
446 | ssize_t status; |
447 | |
448 | status = mutex_lock_interruptible(&drvdata->sem); |
449 | if (status) |
450 | return status; |
451 | |
452 | left += drvdata->write_buffer_in_use; |
453 | |
454 | /* Only write multiples of 4 bytes. */ |
455 | if (left < 4) { |
456 | status = 0; |
457 | goto error; |
458 | } |
459 | |
460 | kbuf = (u32 *) __get_free_page(GFP_KERNEL); |
461 | if (!kbuf) { |
462 | status = -ENOMEM; |
463 | goto error; |
464 | } |
465 | |
466 | while (left > 3) { |
467 | /* only write multiples of 4 bytes, so there might */ |
468 | /* be as many as 3 bytes left (at the end). */ |
469 | len = left; |
470 | |
471 | if (len > PAGE_SIZE) |
472 | len = PAGE_SIZE; |
473 | len &= ~3; |
474 | |
475 | if (drvdata->write_buffer_in_use) { |
476 | memcpy(kbuf, drvdata->write_buffer, |
477 | drvdata->write_buffer_in_use); |
478 | if (copy_from_user( |
479 | to: (((char *)kbuf) + drvdata->write_buffer_in_use), |
480 | from: buf + written, |
481 | n: len - (drvdata->write_buffer_in_use))) { |
482 | free_page((unsigned long)kbuf); |
483 | status = -EFAULT; |
484 | goto error; |
485 | } |
486 | } else { |
487 | if (copy_from_user(to: kbuf, from: buf + written, n: len)) { |
488 | free_page((unsigned long)kbuf); |
489 | status = -EFAULT; |
490 | goto error; |
491 | } |
492 | } |
493 | |
494 | status = drvdata->config->set_configuration(drvdata, |
495 | kbuf, len >> 2); |
496 | |
497 | if (status) { |
498 | free_page((unsigned long)kbuf); |
499 | status = -EFAULT; |
500 | goto error; |
501 | } |
502 | if (drvdata->write_buffer_in_use) { |
503 | len -= drvdata->write_buffer_in_use; |
504 | left -= drvdata->write_buffer_in_use; |
505 | drvdata->write_buffer_in_use = 0; |
506 | } |
507 | written += len; |
508 | left -= len; |
509 | } |
510 | if ((left > 0) && (left < 4)) { |
511 | if (!copy_from_user(to: drvdata->write_buffer, |
512 | from: buf + written, n: left)) { |
513 | drvdata->write_buffer_in_use = left; |
514 | written += left; |
515 | left = 0; |
516 | } |
517 | } |
518 | |
519 | free_page((unsigned long)kbuf); |
520 | status = written; |
521 | error: |
522 | mutex_unlock(lock: &drvdata->sem); |
523 | return status; |
524 | } |
525 | |
526 | static int hwicap_open(struct inode *inode, struct file *file) |
527 | { |
528 | struct hwicap_drvdata *drvdata; |
529 | int status; |
530 | |
531 | mutex_lock(&hwicap_mutex); |
532 | drvdata = container_of(inode->i_cdev, struct hwicap_drvdata, cdev); |
533 | |
534 | status = mutex_lock_interruptible(&drvdata->sem); |
535 | if (status) |
536 | goto out; |
537 | |
538 | if (drvdata->is_open) { |
539 | status = -EBUSY; |
540 | goto error; |
541 | } |
542 | |
543 | status = hwicap_initialize_hwicap(drvdata); |
544 | if (status) { |
545 | dev_err(drvdata->dev, "Failed to open file" ); |
546 | goto error; |
547 | } |
548 | |
549 | file->private_data = drvdata; |
550 | drvdata->write_buffer_in_use = 0; |
551 | drvdata->read_buffer_in_use = 0; |
552 | drvdata->is_open = 1; |
553 | |
554 | error: |
555 | mutex_unlock(lock: &drvdata->sem); |
556 | out: |
557 | mutex_unlock(lock: &hwicap_mutex); |
558 | return status; |
559 | } |
560 | |
561 | static int hwicap_release(struct inode *inode, struct file *file) |
562 | { |
563 | struct hwicap_drvdata *drvdata = file->private_data; |
564 | int i; |
565 | int status = 0; |
566 | |
567 | mutex_lock(&drvdata->sem); |
568 | |
569 | if (drvdata->write_buffer_in_use) { |
570 | /* Flush write buffer. */ |
571 | for (i = drvdata->write_buffer_in_use; i < 4; i++) |
572 | drvdata->write_buffer[i] = 0; |
573 | |
574 | status = drvdata->config->set_configuration(drvdata, |
575 | (u32 *) drvdata->write_buffer, 1); |
576 | if (status) |
577 | goto error; |
578 | } |
579 | |
580 | status = hwicap_command_desync(drvdata); |
581 | if (status) |
582 | goto error; |
583 | |
584 | error: |
585 | drvdata->is_open = 0; |
586 | mutex_unlock(lock: &drvdata->sem); |
587 | return status; |
588 | } |
589 | |
590 | static const struct file_operations hwicap_fops = { |
591 | .owner = THIS_MODULE, |
592 | .write = hwicap_write, |
593 | .read = hwicap_read, |
594 | .open = hwicap_open, |
595 | .release = hwicap_release, |
596 | .llseek = noop_llseek, |
597 | }; |
598 | |
599 | static int hwicap_setup(struct platform_device *pdev, int id, |
600 | const struct hwicap_driver_config *config, |
601 | const struct config_registers *config_regs) |
602 | { |
603 | dev_t devt; |
604 | struct hwicap_drvdata *drvdata = NULL; |
605 | struct device *dev = &pdev->dev; |
606 | int retval; |
607 | |
608 | dev_info(dev, "Xilinx icap port driver\n" ); |
609 | |
610 | mutex_lock(&icap_sem); |
611 | |
612 | if (id < 0) { |
613 | for (id = 0; id < HWICAP_DEVICES; id++) |
614 | if (!probed_devices[id]) |
615 | break; |
616 | } |
617 | if (id < 0 || id >= HWICAP_DEVICES) { |
618 | mutex_unlock(lock: &icap_sem); |
619 | dev_err(dev, "%s%i too large\n" , DRIVER_NAME, id); |
620 | return -EINVAL; |
621 | } |
622 | if (probed_devices[id]) { |
623 | mutex_unlock(lock: &icap_sem); |
624 | dev_err(dev, "cannot assign to %s%i; it is already in use\n" , |
625 | DRIVER_NAME, id); |
626 | return -EBUSY; |
627 | } |
628 | |
629 | probed_devices[id] = 1; |
630 | mutex_unlock(lock: &icap_sem); |
631 | |
632 | devt = MKDEV(XHWICAP_MAJOR, XHWICAP_MINOR + id); |
633 | |
634 | drvdata = devm_kzalloc(dev, size: sizeof(struct hwicap_drvdata), GFP_KERNEL); |
635 | if (!drvdata) { |
636 | retval = -ENOMEM; |
637 | goto failed; |
638 | } |
639 | dev_set_drvdata(dev, data: (void *)drvdata); |
640 | |
641 | drvdata->base_address = devm_platform_ioremap_resource(pdev, index: 0); |
642 | if (!drvdata->base_address) { |
643 | retval = -ENODEV; |
644 | goto failed; |
645 | } |
646 | |
647 | drvdata->devt = devt; |
648 | drvdata->dev = dev; |
649 | drvdata->config = config; |
650 | drvdata->config_regs = config_regs; |
651 | |
652 | mutex_init(&drvdata->sem); |
653 | drvdata->is_open = 0; |
654 | |
655 | cdev_init(&drvdata->cdev, &hwicap_fops); |
656 | drvdata->cdev.owner = THIS_MODULE; |
657 | retval = cdev_add(&drvdata->cdev, devt, 1); |
658 | if (retval) { |
659 | dev_err(dev, "cdev_add() failed\n" ); |
660 | goto failed; |
661 | } |
662 | |
663 | device_create(cls: &icap_class, parent: dev, devt, NULL, fmt: "%s%d" , DRIVER_NAME, id); |
664 | return 0; /* success */ |
665 | |
666 | failed: |
667 | mutex_lock(&icap_sem); |
668 | probed_devices[id] = 0; |
669 | mutex_unlock(lock: &icap_sem); |
670 | |
671 | return retval; |
672 | } |
673 | |
674 | static struct hwicap_driver_config buffer_icap_config = { |
675 | .get_configuration = buffer_icap_get_configuration, |
676 | .set_configuration = buffer_icap_set_configuration, |
677 | .get_status = buffer_icap_get_status, |
678 | .reset = buffer_icap_reset, |
679 | }; |
680 | |
681 | static struct hwicap_driver_config fifo_icap_config = { |
682 | .get_configuration = fifo_icap_get_configuration, |
683 | .set_configuration = fifo_icap_set_configuration, |
684 | .get_status = fifo_icap_get_status, |
685 | .reset = fifo_icap_reset, |
686 | }; |
687 | |
688 | static int hwicap_drv_probe(struct platform_device *pdev) |
689 | { |
690 | const struct config_registers *regs; |
691 | const struct hwicap_driver_config *config; |
692 | const char *family; |
693 | int id = -1; |
694 | |
695 | config = device_get_match_data(dev: &pdev->dev); |
696 | |
697 | of_property_read_u32(np: pdev->dev.of_node, propname: "port-number" , out_value: &id); |
698 | |
699 | /* It's most likely that we're using V4, if the family is not |
700 | * specified |
701 | */ |
702 | regs = &v4_config_registers; |
703 | if (!of_property_read_string(np: pdev->dev.of_node, propname: "xlnx,family" , out_string: &family)) { |
704 | if (!strcmp(family, "virtex2p" )) |
705 | regs = &v2_config_registers; |
706 | else if (!strcmp(family, "virtex4" )) |
707 | regs = &v4_config_registers; |
708 | else if (!strcmp(family, "virtex5" )) |
709 | regs = &v5_config_registers; |
710 | else if (!strcmp(family, "virtex6" )) |
711 | regs = &v6_config_registers; |
712 | } |
713 | return hwicap_setup(pdev, id, config, config_regs: regs); |
714 | } |
715 | |
716 | static void hwicap_drv_remove(struct platform_device *pdev) |
717 | { |
718 | struct device *dev = &pdev->dev; |
719 | struct hwicap_drvdata *drvdata; |
720 | |
721 | drvdata = dev_get_drvdata(dev); |
722 | |
723 | device_destroy(cls: &icap_class, devt: drvdata->devt); |
724 | cdev_del(&drvdata->cdev); |
725 | |
726 | mutex_lock(&icap_sem); |
727 | probed_devices[MINOR(dev->devt)-XHWICAP_MINOR] = 0; |
728 | mutex_unlock(lock: &icap_sem); |
729 | } |
730 | |
731 | /* Match table for device tree binding */ |
732 | static const struct of_device_id hwicap_of_match[] = { |
733 | { .compatible = "xlnx,opb-hwicap-1.00.b" , .data = &buffer_icap_config}, |
734 | { .compatible = "xlnx,xps-hwicap-1.00.a" , .data = &fifo_icap_config}, |
735 | {}, |
736 | }; |
737 | MODULE_DEVICE_TABLE(of, hwicap_of_match); |
738 | |
739 | static struct platform_driver hwicap_platform_driver = { |
740 | .probe = hwicap_drv_probe, |
741 | .remove_new = hwicap_drv_remove, |
742 | .driver = { |
743 | .name = DRIVER_NAME, |
744 | .of_match_table = hwicap_of_match, |
745 | }, |
746 | }; |
747 | |
748 | static int __init hwicap_module_init(void) |
749 | { |
750 | dev_t devt; |
751 | int retval; |
752 | |
753 | retval = class_register(class: &icap_class); |
754 | if (retval) |
755 | return retval; |
756 | mutex_init(&icap_sem); |
757 | |
758 | devt = MKDEV(XHWICAP_MAJOR, XHWICAP_MINOR); |
759 | retval = register_chrdev_region(devt, |
760 | HWICAP_DEVICES, |
761 | DRIVER_NAME); |
762 | if (retval < 0) |
763 | return retval; |
764 | |
765 | retval = platform_driver_register(&hwicap_platform_driver); |
766 | if (retval) |
767 | goto failed; |
768 | |
769 | return retval; |
770 | |
771 | failed: |
772 | unregister_chrdev_region(devt, HWICAP_DEVICES); |
773 | |
774 | return retval; |
775 | } |
776 | |
777 | static void __exit hwicap_module_cleanup(void) |
778 | { |
779 | dev_t devt = MKDEV(XHWICAP_MAJOR, XHWICAP_MINOR); |
780 | |
781 | class_unregister(class: &icap_class); |
782 | |
783 | platform_driver_unregister(&hwicap_platform_driver); |
784 | |
785 | unregister_chrdev_region(devt, HWICAP_DEVICES); |
786 | } |
787 | |
788 | module_init(hwicap_module_init); |
789 | module_exit(hwicap_module_cleanup); |
790 | |
791 | MODULE_AUTHOR("Xilinx, Inc; Xilinx Research Labs Group" ); |
792 | MODULE_DESCRIPTION("Xilinx ICAP Port Driver" ); |
793 | MODULE_LICENSE("GPL" ); |
794 | |