diff options
Diffstat (limited to 'drivers/char/xilinx_hwicap/xilinx_hwicap.c')
-rw-r--r-- | drivers/char/xilinx_hwicap/xilinx_hwicap.c | 904 |
1 files changed, 904 insertions, 0 deletions
diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c new file mode 100644 index 000000000000..24f6aef0fd3c --- /dev/null +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c | |||
@@ -0,0 +1,904 @@ | |||
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 | * Xilinx products are not intended for use in life support appliances, | ||
25 | * devices, or systems. Use in such applications is expressly prohibited. | ||
26 | * | ||
27 | * (c) Copyright 2002 Xilinx Inc., Systems Engineering Group | ||
28 | * (c) Copyright 2004 Xilinx Inc., Systems Engineering Group | ||
29 | * (c) Copyright 2007-2008 Xilinx Inc. | ||
30 | * All rights reserved. | ||
31 | * | ||
32 | * You should have received a copy of the GNU General Public License along | ||
33 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
34 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
35 | * | ||
36 | *****************************************************************************/ | ||
37 | |||
38 | /* | ||
39 | * This is the code behind /dev/xilinx_icap -- it allows a user-space | ||
40 | * application to use the Xilinx ICAP subsystem. | ||
41 | * | ||
42 | * The following operations are possible: | ||
43 | * | ||
44 | * open open the port and initialize for access. | ||
45 | * release release port | ||
46 | * write Write a bitstream to the configuration processor. | ||
47 | * read Read a data stream from the configuration processor. | ||
48 | * | ||
49 | * After being opened, the port is initialized and accessed to avoid a | ||
50 | * corrupted first read which may occur with some hardware. The port | ||
51 | * is left in a desynched state, requiring that a synch sequence be | ||
52 | * transmitted before any valid configuration data. A user will have | ||
53 | * exclusive access to the device while it remains open, and the state | ||
54 | * of the ICAP cannot be guaranteed after the device is closed. Note | ||
55 | * that a complete reset of the core and the state of the ICAP cannot | ||
56 | * be performed on many versions of the cores, hence users of this | ||
57 | * device should avoid making inconsistent accesses to the device. In | ||
58 | * particular, accessing the read interface, without first generating | ||
59 | * a write containing a readback packet can leave the ICAP in an | ||
60 | * inaccessible state. | ||
61 | * | ||
62 | * Note that in order to use the read interface, it is first necessary | ||
63 | * to write a request packet to the write interface. i.e., it is not | ||
64 | * possible to simply readback the bitstream (or any configuration | ||
65 | * bits) from a device without specifically requesting them first. | ||
66 | * The code to craft such packets is intended to be part of the | ||
67 | * user-space application code that uses this device. The simplest | ||
68 | * way to use this interface is simply: | ||
69 | * | ||
70 | * cp foo.bit /dev/xilinx_icap | ||
71 | * | ||
72 | * Note that unless foo.bit is an appropriately constructed partial | ||
73 | * bitstream, this has a high likelyhood of overwriting the design | ||
74 | * currently programmed in the FPGA. | ||
75 | */ | ||
76 | |||
77 | #include <linux/version.h> | ||
78 | #include <linux/module.h> | ||
79 | #include <linux/kernel.h> | ||
80 | #include <linux/types.h> | ||
81 | #include <linux/ioport.h> | ||
82 | #include <linux/interrupt.h> | ||
83 | #include <linux/fcntl.h> | ||
84 | #include <linux/init.h> | ||
85 | #include <linux/poll.h> | ||
86 | #include <linux/proc_fs.h> | ||
87 | #include <asm/semaphore.h> | ||
88 | #include <linux/sysctl.h> | ||
89 | #include <linux/version.h> | ||
90 | #include <linux/fs.h> | ||
91 | #include <linux/cdev.h> | ||
92 | #include <linux/platform_device.h> | ||
93 | |||
94 | #include <asm/io.h> | ||
95 | #include <asm/uaccess.h> | ||
96 | #include <asm/system.h> | ||
97 | |||
98 | #ifdef CONFIG_OF | ||
99 | /* For open firmware. */ | ||
100 | #include <linux/of_device.h> | ||
101 | #include <linux/of_platform.h> | ||
102 | #endif | ||
103 | |||
104 | #include "xilinx_hwicap.h" | ||
105 | #include "buffer_icap.h" | ||
106 | #include "fifo_icap.h" | ||
107 | |||
108 | #define DRIVER_NAME "xilinx_icap" | ||
109 | |||
110 | #define HWICAP_REGS (0x10000) | ||
111 | |||
112 | /* dynamically allocate device number */ | ||
113 | static int xhwicap_major; | ||
114 | static int xhwicap_minor; | ||
115 | #define HWICAP_DEVICES 1 | ||
116 | |||
117 | module_param(xhwicap_major, int, S_IRUGO); | ||
118 | module_param(xhwicap_minor, int, S_IRUGO); | ||
119 | |||
120 | /* An array, which is set to true when the device is registered. */ | ||
121 | static bool probed_devices[HWICAP_DEVICES]; | ||
122 | |||
123 | static struct class *icap_class; | ||
124 | |||
125 | #define UNIMPLEMENTED 0xFFFF | ||
126 | |||
127 | static const struct config_registers v2_config_registers = { | ||
128 | .CRC = 0, | ||
129 | .FAR = 1, | ||
130 | .FDRI = 2, | ||
131 | .FDRO = 3, | ||
132 | .CMD = 4, | ||
133 | .CTL = 5, | ||
134 | .MASK = 6, | ||
135 | .STAT = 7, | ||
136 | .LOUT = 8, | ||
137 | .COR = 9, | ||
138 | .MFWR = 10, | ||
139 | .FLR = 11, | ||
140 | .KEY = 12, | ||
141 | .CBC = 13, | ||
142 | .IDCODE = 14, | ||
143 | .AXSS = UNIMPLEMENTED, | ||
144 | .C0R_1 = UNIMPLEMENTED, | ||
145 | .CSOB = UNIMPLEMENTED, | ||
146 | .WBSTAR = UNIMPLEMENTED, | ||
147 | .TIMER = UNIMPLEMENTED, | ||
148 | .BOOTSTS = UNIMPLEMENTED, | ||
149 | .CTL_1 = UNIMPLEMENTED, | ||
150 | }; | ||
151 | |||
152 | static const struct config_registers v4_config_registers = { | ||
153 | .CRC = 0, | ||
154 | .FAR = 1, | ||
155 | .FDRI = 2, | ||
156 | .FDRO = 3, | ||
157 | .CMD = 4, | ||
158 | .CTL = 5, | ||
159 | .MASK = 6, | ||
160 | .STAT = 7, | ||
161 | .LOUT = 8, | ||
162 | .COR = 9, | ||
163 | .MFWR = 10, | ||
164 | .FLR = UNIMPLEMENTED, | ||
165 | .KEY = UNIMPLEMENTED, | ||
166 | .CBC = 11, | ||
167 | .IDCODE = 12, | ||
168 | .AXSS = 13, | ||
169 | .C0R_1 = UNIMPLEMENTED, | ||
170 | .CSOB = UNIMPLEMENTED, | ||
171 | .WBSTAR = UNIMPLEMENTED, | ||
172 | .TIMER = UNIMPLEMENTED, | ||
173 | .BOOTSTS = UNIMPLEMENTED, | ||
174 | .CTL_1 = UNIMPLEMENTED, | ||
175 | }; | ||
176 | static const struct config_registers v5_config_registers = { | ||
177 | .CRC = 0, | ||
178 | .FAR = 1, | ||
179 | .FDRI = 2, | ||
180 | .FDRO = 3, | ||
181 | .CMD = 4, | ||
182 | .CTL = 5, | ||
183 | .MASK = 6, | ||
184 | .STAT = 7, | ||
185 | .LOUT = 8, | ||
186 | .COR = 9, | ||
187 | .MFWR = 10, | ||
188 | .FLR = UNIMPLEMENTED, | ||
189 | .KEY = UNIMPLEMENTED, | ||
190 | .CBC = 11, | ||
191 | .IDCODE = 12, | ||
192 | .AXSS = 13, | ||
193 | .C0R_1 = 14, | ||
194 | .CSOB = 15, | ||
195 | .WBSTAR = 16, | ||
196 | .TIMER = 17, | ||
197 | .BOOTSTS = 18, | ||
198 | .CTL_1 = 19, | ||
199 | }; | ||
200 | |||
201 | /** | ||
202 | * hwicap_command_desync: Send a DESYNC command to the ICAP port. | ||
203 | * @parameter drvdata: a pointer to the drvdata. | ||
204 | * | ||
205 | * This command desynchronizes the ICAP After this command, a | ||
206 | * bitstream containing a NULL packet, followed by a SYNCH packet is | ||
207 | * required before the ICAP will recognize commands. | ||
208 | */ | ||
209 | int hwicap_command_desync(struct hwicap_drvdata *drvdata) | ||
210 | { | ||
211 | u32 buffer[4]; | ||
212 | u32 index = 0; | ||
213 | |||
214 | /* | ||
215 | * Create the data to be written to the ICAP. | ||
216 | */ | ||
217 | buffer[index++] = hwicap_type_1_write(drvdata->config_regs->CMD) | 1; | ||
218 | buffer[index++] = XHI_CMD_DESYNCH; | ||
219 | buffer[index++] = XHI_NOOP_PACKET; | ||
220 | buffer[index++] = XHI_NOOP_PACKET; | ||
221 | |||
222 | /* | ||
223 | * Write the data to the FIFO and intiate the transfer of data present | ||
224 | * in the FIFO to the ICAP device. | ||
225 | */ | ||
226 | return drvdata->config->set_configuration(drvdata, | ||
227 | &buffer[0], index); | ||
228 | } | ||
229 | |||
230 | /** | ||
231 | * hwicap_command_capture: Send a CAPTURE command to the ICAP port. | ||
232 | * @parameter drvdata: a pointer to the drvdata. | ||
233 | * | ||
234 | * This command captures all of the flip flop states so they will be | ||
235 | * available during readback. One can use this command instead of | ||
236 | * enabling the CAPTURE block in the design. | ||
237 | */ | ||
238 | int hwicap_command_capture(struct hwicap_drvdata *drvdata) | ||
239 | { | ||
240 | u32 buffer[7]; | ||
241 | u32 index = 0; | ||
242 | |||
243 | /* | ||
244 | * Create the data to be written to the ICAP. | ||
245 | */ | ||
246 | buffer[index++] = XHI_DUMMY_PACKET; | ||
247 | buffer[index++] = XHI_SYNC_PACKET; | ||
248 | buffer[index++] = XHI_NOOP_PACKET; | ||
249 | buffer[index++] = hwicap_type_1_write(drvdata->config_regs->CMD) | 1; | ||
250 | buffer[index++] = XHI_CMD_GCAPTURE; | ||
251 | buffer[index++] = XHI_DUMMY_PACKET; | ||
252 | buffer[index++] = XHI_DUMMY_PACKET; | ||
253 | |||
254 | /* | ||
255 | * Write the data to the FIFO and intiate the transfer of data | ||
256 | * present in the FIFO to the ICAP device. | ||
257 | */ | ||
258 | return drvdata->config->set_configuration(drvdata, | ||
259 | &buffer[0], index); | ||
260 | |||
261 | } | ||
262 | |||
263 | /** | ||
264 | * hwicap_get_configuration_register: Query a configuration register. | ||
265 | * @parameter drvdata: a pointer to the drvdata. | ||
266 | * @parameter reg: a constant which represents the configuration | ||
267 | * register value to be returned. | ||
268 | * Examples: XHI_IDCODE, XHI_FLR. | ||
269 | * @parameter RegData: returns the value of the register. | ||
270 | * | ||
271 | * Sends a query packet to the ICAP and then receives the response. | ||
272 | * The icap is left in Synched state. | ||
273 | */ | ||
274 | int hwicap_get_configuration_register(struct hwicap_drvdata *drvdata, | ||
275 | u32 reg, u32 *RegData) | ||
276 | { | ||
277 | int status; | ||
278 | u32 buffer[6]; | ||
279 | u32 index = 0; | ||
280 | |||
281 | /* | ||
282 | * Create the data to be written to the ICAP. | ||
283 | */ | ||
284 | buffer[index++] = XHI_DUMMY_PACKET; | ||
285 | buffer[index++] = XHI_SYNC_PACKET; | ||
286 | buffer[index++] = XHI_NOOP_PACKET; | ||
287 | buffer[index++] = hwicap_type_1_read(reg) | 1; | ||
288 | buffer[index++] = XHI_NOOP_PACKET; | ||
289 | buffer[index++] = XHI_NOOP_PACKET; | ||
290 | |||
291 | /* | ||
292 | * Write the data to the FIFO and intiate the transfer of data present | ||
293 | * in the FIFO to the ICAP device. | ||
294 | */ | ||
295 | status = drvdata->config->set_configuration(drvdata, | ||
296 | &buffer[0], index); | ||
297 | if (status) | ||
298 | return status; | ||
299 | |||
300 | /* | ||
301 | * Read the configuration register | ||
302 | */ | ||
303 | status = drvdata->config->get_configuration(drvdata, RegData, 1); | ||
304 | if (status) | ||
305 | return status; | ||
306 | |||
307 | return 0; | ||
308 | } | ||
309 | |||
310 | int hwicap_initialize_hwicap(struct hwicap_drvdata *drvdata) | ||
311 | { | ||
312 | int status; | ||
313 | u32 idcode; | ||
314 | |||
315 | dev_dbg(drvdata->dev, "initializing\n"); | ||
316 | |||
317 | /* Abort any current transaction, to make sure we have the | ||
318 | * ICAP in a good state. */ | ||
319 | dev_dbg(drvdata->dev, "Reset...\n"); | ||
320 | drvdata->config->reset(drvdata); | ||
321 | |||
322 | dev_dbg(drvdata->dev, "Desync...\n"); | ||
323 | status = hwicap_command_desync(drvdata); | ||
324 | if (status) | ||
325 | return status; | ||
326 | |||
327 | /* Attempt to read the IDCODE from ICAP. This | ||
328 | * may not be returned correctly, due to the design of the | ||
329 | * hardware. | ||
330 | */ | ||
331 | dev_dbg(drvdata->dev, "Reading IDCODE...\n"); | ||
332 | status = hwicap_get_configuration_register( | ||
333 | drvdata, drvdata->config_regs->IDCODE, &idcode); | ||
334 | dev_dbg(drvdata->dev, "IDCODE = %x\n", idcode); | ||
335 | if (status) | ||
336 | return status; | ||
337 | |||
338 | dev_dbg(drvdata->dev, "Desync...\n"); | ||
339 | status = hwicap_command_desync(drvdata); | ||
340 | if (status) | ||
341 | return status; | ||
342 | |||
343 | return 0; | ||
344 | } | ||
345 | |||
346 | static ssize_t | ||
347 | hwicap_read(struct file *file, char *buf, size_t count, loff_t *ppos) | ||
348 | { | ||
349 | struct hwicap_drvdata *drvdata = file->private_data; | ||
350 | ssize_t bytes_to_read = 0; | ||
351 | u32 *kbuf; | ||
352 | u32 words; | ||
353 | u32 bytes_remaining; | ||
354 | int status; | ||
355 | |||
356 | if (down_interruptible(&drvdata->sem)) | ||
357 | return -ERESTARTSYS; | ||
358 | |||
359 | if (drvdata->read_buffer_in_use) { | ||
360 | /* If there are leftover bytes in the buffer, just */ | ||
361 | /* return them and don't try to read more from the */ | ||
362 | /* ICAP device. */ | ||
363 | bytes_to_read = | ||
364 | (count < drvdata->read_buffer_in_use) ? count : | ||
365 | drvdata->read_buffer_in_use; | ||
366 | |||
367 | /* Return the data currently in the read buffer. */ | ||
368 | if (copy_to_user(buf, drvdata->read_buffer, bytes_to_read)) { | ||
369 | status = -EFAULT; | ||
370 | goto error; | ||
371 | } | ||
372 | drvdata->read_buffer_in_use -= bytes_to_read; | ||
373 | memcpy(drvdata->read_buffer + bytes_to_read, | ||
374 | drvdata->read_buffer, 4 - bytes_to_read); | ||
375 | } else { | ||
376 | /* Get new data from the ICAP, and return was was requested. */ | ||
377 | kbuf = (u32 *) get_zeroed_page(GFP_KERNEL); | ||
378 | if (!kbuf) { | ||
379 | status = -ENOMEM; | ||
380 | goto error; | ||
381 | } | ||
382 | |||
383 | /* The ICAP device is only able to read complete */ | ||
384 | /* words. If a number of bytes that do not correspond */ | ||
385 | /* to complete words is requested, then we read enough */ | ||
386 | /* words to get the required number of bytes, and then */ | ||
387 | /* save the remaining bytes for the next read. */ | ||
388 | |||
389 | /* Determine the number of words to read, rounding up */ | ||
390 | /* if necessary. */ | ||
391 | words = ((count + 3) >> 2); | ||
392 | bytes_to_read = words << 2; | ||
393 | |||
394 | if (bytes_to_read > PAGE_SIZE) | ||
395 | bytes_to_read = PAGE_SIZE; | ||
396 | |||
397 | /* Ensure we only read a complete number of words. */ | ||
398 | bytes_remaining = bytes_to_read & 3; | ||
399 | bytes_to_read &= ~3; | ||
400 | words = bytes_to_read >> 2; | ||
401 | |||
402 | status = drvdata->config->get_configuration(drvdata, | ||
403 | kbuf, words); | ||
404 | |||
405 | /* If we didn't read correctly, then bail out. */ | ||
406 | if (status) { | ||
407 | free_page((unsigned long)kbuf); | ||
408 | goto error; | ||
409 | } | ||
410 | |||
411 | /* If we fail to return the data to the user, then bail out. */ | ||
412 | if (copy_to_user(buf, kbuf, bytes_to_read)) { | ||
413 | free_page((unsigned long)kbuf); | ||
414 | status = -EFAULT; | ||
415 | goto error; | ||
416 | } | ||
417 | memcpy(kbuf, drvdata->read_buffer, bytes_remaining); | ||
418 | drvdata->read_buffer_in_use = bytes_remaining; | ||
419 | free_page((unsigned long)kbuf); | ||
420 | } | ||
421 | status = bytes_to_read; | ||
422 | error: | ||
423 | up(&drvdata->sem); | ||
424 | return status; | ||
425 | } | ||
426 | |||
427 | static ssize_t | ||
428 | hwicap_write(struct file *file, const char *buf, | ||
429 | size_t count, loff_t *ppos) | ||
430 | { | ||
431 | struct hwicap_drvdata *drvdata = file->private_data; | ||
432 | ssize_t written = 0; | ||
433 | ssize_t left = count; | ||
434 | u32 *kbuf; | ||
435 | ssize_t len; | ||
436 | ssize_t status; | ||
437 | |||
438 | if (down_interruptible(&drvdata->sem)) | ||
439 | return -ERESTARTSYS; | ||
440 | |||
441 | left += drvdata->write_buffer_in_use; | ||
442 | |||
443 | /* Only write multiples of 4 bytes. */ | ||
444 | if (left < 4) { | ||
445 | status = 0; | ||
446 | goto error; | ||
447 | } | ||
448 | |||
449 | kbuf = (u32 *) __get_free_page(GFP_KERNEL); | ||
450 | if (!kbuf) { | ||
451 | status = -ENOMEM; | ||
452 | goto error; | ||
453 | } | ||
454 | |||
455 | while (left > 3) { | ||
456 | /* only write multiples of 4 bytes, so there might */ | ||
457 | /* be as many as 3 bytes left (at the end). */ | ||
458 | len = left; | ||
459 | |||
460 | if (len > PAGE_SIZE) | ||
461 | len = PAGE_SIZE; | ||
462 | len &= ~3; | ||
463 | |||
464 | if (drvdata->write_buffer_in_use) { | ||
465 | memcpy(kbuf, drvdata->write_buffer, | ||
466 | drvdata->write_buffer_in_use); | ||
467 | if (copy_from_user( | ||
468 | (((char *)kbuf) + (drvdata->write_buffer_in_use)), | ||
469 | buf + written, | ||
470 | len - (drvdata->write_buffer_in_use))) { | ||
471 | free_page((unsigned long)kbuf); | ||
472 | status = -EFAULT; | ||
473 | goto error; | ||
474 | } | ||
475 | } else { | ||
476 | if (copy_from_user(kbuf, buf + written, len)) { | ||
477 | free_page((unsigned long)kbuf); | ||
478 | status = -EFAULT; | ||
479 | goto error; | ||
480 | } | ||
481 | } | ||
482 | |||
483 | status = drvdata->config->set_configuration(drvdata, | ||
484 | kbuf, len >> 2); | ||
485 | |||
486 | if (status) { | ||
487 | free_page((unsigned long)kbuf); | ||
488 | status = -EFAULT; | ||
489 | goto error; | ||
490 | } | ||
491 | if (drvdata->write_buffer_in_use) { | ||
492 | len -= drvdata->write_buffer_in_use; | ||
493 | left -= drvdata->write_buffer_in_use; | ||
494 | drvdata->write_buffer_in_use = 0; | ||
495 | } | ||
496 | written += len; | ||
497 | left -= len; | ||
498 | } | ||
499 | if ((left > 0) && (left < 4)) { | ||
500 | if (!copy_from_user(drvdata->write_buffer, | ||
501 | buf + written, left)) { | ||
502 | drvdata->write_buffer_in_use = left; | ||
503 | written += left; | ||
504 | left = 0; | ||
505 | } | ||
506 | } | ||
507 | |||
508 | free_page((unsigned long)kbuf); | ||
509 | status = written; | ||
510 | error: | ||
511 | up(&drvdata->sem); | ||
512 | return status; | ||
513 | } | ||
514 | |||
515 | static int hwicap_open(struct inode *inode, struct file *file) | ||
516 | { | ||
517 | struct hwicap_drvdata *drvdata; | ||
518 | int status; | ||
519 | |||
520 | drvdata = container_of(inode->i_cdev, struct hwicap_drvdata, cdev); | ||
521 | |||
522 | if (down_interruptible(&drvdata->sem)) | ||
523 | return -ERESTARTSYS; | ||
524 | |||
525 | if (drvdata->is_open) { | ||
526 | status = -EBUSY; | ||
527 | goto error; | ||
528 | } | ||
529 | |||
530 | status = hwicap_initialize_hwicap(drvdata); | ||
531 | if (status) { | ||
532 | dev_err(drvdata->dev, "Failed to open file"); | ||
533 | goto error; | ||
534 | } | ||
535 | |||
536 | file->private_data = drvdata; | ||
537 | drvdata->write_buffer_in_use = 0; | ||
538 | drvdata->read_buffer_in_use = 0; | ||
539 | drvdata->is_open = 1; | ||
540 | |||
541 | error: | ||
542 | up(&drvdata->sem); | ||
543 | return status; | ||
544 | } | ||
545 | |||
546 | static int hwicap_release(struct inode *inode, struct file *file) | ||
547 | { | ||
548 | struct hwicap_drvdata *drvdata = file->private_data; | ||
549 | int i; | ||
550 | int status = 0; | ||
551 | |||
552 | if (down_interruptible(&drvdata->sem)) | ||
553 | return -ERESTARTSYS; | ||
554 | |||
555 | if (drvdata->write_buffer_in_use) { | ||
556 | /* Flush write buffer. */ | ||
557 | for (i = drvdata->write_buffer_in_use; i < 4; i++) | ||
558 | drvdata->write_buffer[i] = 0; | ||
559 | |||
560 | status = drvdata->config->set_configuration(drvdata, | ||
561 | (u32 *) drvdata->write_buffer, 1); | ||
562 | if (status) | ||
563 | goto error; | ||
564 | } | ||
565 | |||
566 | status = hwicap_command_desync(drvdata); | ||
567 | if (status) | ||
568 | goto error; | ||
569 | |||
570 | error: | ||
571 | drvdata->is_open = 0; | ||
572 | up(&drvdata->sem); | ||
573 | return status; | ||
574 | } | ||
575 | |||
576 | static struct file_operations hwicap_fops = { | ||
577 | .owner = THIS_MODULE, | ||
578 | .write = hwicap_write, | ||
579 | .read = hwicap_read, | ||
580 | .open = hwicap_open, | ||
581 | .release = hwicap_release, | ||
582 | }; | ||
583 | |||
584 | static int __devinit hwicap_setup(struct device *dev, int id, | ||
585 | const struct resource *regs_res, | ||
586 | const struct hwicap_driver_config *config, | ||
587 | const struct config_registers *config_regs) | ||
588 | { | ||
589 | dev_t devt; | ||
590 | struct hwicap_drvdata *drvdata = NULL; | ||
591 | int retval = 0; | ||
592 | |||
593 | dev_info(dev, "Xilinx icap port driver\n"); | ||
594 | |||
595 | if (id < 0) { | ||
596 | for (id = 0; id < HWICAP_DEVICES; id++) | ||
597 | if (!probed_devices[id]) | ||
598 | break; | ||
599 | } | ||
600 | if (id < 0 || id >= HWICAP_DEVICES) { | ||
601 | dev_err(dev, "%s%i too large\n", DRIVER_NAME, id); | ||
602 | return -EINVAL; | ||
603 | } | ||
604 | if (probed_devices[id]) { | ||
605 | dev_err(dev, "cannot assign to %s%i; it is already in use\n", | ||
606 | DRIVER_NAME, id); | ||
607 | return -EBUSY; | ||
608 | } | ||
609 | |||
610 | probed_devices[id] = 1; | ||
611 | |||
612 | devt = MKDEV(xhwicap_major, xhwicap_minor + id); | ||
613 | |||
614 | drvdata = kmalloc(sizeof(struct hwicap_drvdata), GFP_KERNEL); | ||
615 | if (!drvdata) { | ||
616 | dev_err(dev, "Couldn't allocate device private record\n"); | ||
617 | return -ENOMEM; | ||
618 | } | ||
619 | memset((void *)drvdata, 0, sizeof(struct hwicap_drvdata)); | ||
620 | dev_set_drvdata(dev, (void *)drvdata); | ||
621 | |||
622 | if (!regs_res) { | ||
623 | dev_err(dev, "Couldn't get registers resource\n"); | ||
624 | retval = -EFAULT; | ||
625 | goto failed1; | ||
626 | } | ||
627 | |||
628 | drvdata->mem_start = regs_res->start; | ||
629 | drvdata->mem_end = regs_res->end; | ||
630 | drvdata->mem_size = regs_res->end - regs_res->start + 1; | ||
631 | |||
632 | if (!request_mem_region(drvdata->mem_start, | ||
633 | drvdata->mem_size, DRIVER_NAME)) { | ||
634 | dev_err(dev, "Couldn't lock memory region at %p\n", | ||
635 | (void *)regs_res->start); | ||
636 | retval = -EBUSY; | ||
637 | goto failed1; | ||
638 | } | ||
639 | |||
640 | drvdata->devt = devt; | ||
641 | drvdata->dev = dev; | ||
642 | drvdata->base_address = ioremap(drvdata->mem_start, drvdata->mem_size); | ||
643 | if (!drvdata->base_address) { | ||
644 | dev_err(dev, "ioremap() failed\n"); | ||
645 | goto failed2; | ||
646 | } | ||
647 | |||
648 | drvdata->config = config; | ||
649 | drvdata->config_regs = config_regs; | ||
650 | |||
651 | init_MUTEX(&drvdata->sem); | ||
652 | drvdata->is_open = 0; | ||
653 | |||
654 | dev_info(dev, "ioremap %lx to %p with size %x\n", | ||
655 | (unsigned long int)drvdata->mem_start, | ||
656 | drvdata->base_address, drvdata->mem_size); | ||
657 | |||
658 | cdev_init(&drvdata->cdev, &hwicap_fops); | ||
659 | drvdata->cdev.owner = THIS_MODULE; | ||
660 | retval = cdev_add(&drvdata->cdev, devt, 1); | ||
661 | if (retval) { | ||
662 | dev_err(dev, "cdev_add() failed\n"); | ||
663 | goto failed3; | ||
664 | } | ||
665 | /* devfs_mk_cdev(devt, S_IFCHR|S_IRUGO|S_IWUGO, DRIVER_NAME); */ | ||
666 | class_device_create(icap_class, NULL, devt, NULL, DRIVER_NAME); | ||
667 | return 0; /* success */ | ||
668 | |||
669 | failed3: | ||
670 | iounmap(drvdata->base_address); | ||
671 | |||
672 | failed2: | ||
673 | release_mem_region(regs_res->start, drvdata->mem_size); | ||
674 | |||
675 | failed1: | ||
676 | kfree(drvdata); | ||
677 | |||
678 | return retval; | ||
679 | } | ||
680 | |||
681 | static struct hwicap_driver_config buffer_icap_config = { | ||
682 | .get_configuration = buffer_icap_get_configuration, | ||
683 | .set_configuration = buffer_icap_set_configuration, | ||
684 | .reset = buffer_icap_reset, | ||
685 | }; | ||
686 | |||
687 | static struct hwicap_driver_config fifo_icap_config = { | ||
688 | .get_configuration = fifo_icap_get_configuration, | ||
689 | .set_configuration = fifo_icap_set_configuration, | ||
690 | .reset = fifo_icap_reset, | ||
691 | }; | ||
692 | |||
693 | static int __devexit hwicap_remove(struct device *dev) | ||
694 | { | ||
695 | struct hwicap_drvdata *drvdata; | ||
696 | |||
697 | drvdata = (struct hwicap_drvdata *)dev_get_drvdata(dev); | ||
698 | |||
699 | if (!drvdata) | ||
700 | return 0; | ||
701 | |||
702 | class_device_destroy(icap_class, drvdata->devt); | ||
703 | cdev_del(&drvdata->cdev); | ||
704 | iounmap(drvdata->base_address); | ||
705 | release_mem_region(drvdata->mem_start, drvdata->mem_size); | ||
706 | kfree(drvdata); | ||
707 | dev_set_drvdata(dev, NULL); | ||
708 | probed_devices[MINOR(dev->devt)-xhwicap_minor] = 0; | ||
709 | |||
710 | return 0; /* success */ | ||
711 | } | ||
712 | |||
713 | static int __devinit hwicap_drv_probe(struct platform_device *pdev) | ||
714 | { | ||
715 | struct resource *res; | ||
716 | const struct config_registers *regs; | ||
717 | const char *family; | ||
718 | |||
719 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
720 | if (!res) | ||
721 | return -ENODEV; | ||
722 | |||
723 | /* It's most likely that we're using V4, if the family is not | ||
724 | specified */ | ||
725 | regs = &v4_config_registers; | ||
726 | family = pdev->dev.platform_data; | ||
727 | |||
728 | if (family) { | ||
729 | if (!strcmp(family, "virtex2p")) { | ||
730 | regs = &v2_config_registers; | ||
731 | } else if (!strcmp(family, "virtex4")) { | ||
732 | regs = &v4_config_registers; | ||
733 | } else if (!strcmp(family, "virtex5")) { | ||
734 | regs = &v5_config_registers; | ||
735 | } | ||
736 | } | ||
737 | |||
738 | return hwicap_setup(&pdev->dev, pdev->id, res, | ||
739 | &buffer_icap_config, regs); | ||
740 | } | ||
741 | |||
742 | static int __devexit hwicap_drv_remove(struct platform_device *pdev) | ||
743 | { | ||
744 | return hwicap_remove(&pdev->dev); | ||
745 | } | ||
746 | |||
747 | static struct platform_driver hwicap_platform_driver = { | ||
748 | .probe = hwicap_drv_probe, | ||
749 | .remove = hwicap_drv_remove, | ||
750 | .driver = { | ||
751 | .owner = THIS_MODULE, | ||
752 | .name = DRIVER_NAME, | ||
753 | }, | ||
754 | }; | ||
755 | |||
756 | /* --------------------------------------------------------------------- | ||
757 | * OF bus binding | ||
758 | */ | ||
759 | |||
760 | #if defined(CONFIG_OF) | ||
761 | static int __devinit | ||
762 | hwicap_of_probe(struct of_device *op, const struct of_device_id *match) | ||
763 | { | ||
764 | struct resource res; | ||
765 | const unsigned int *id; | ||
766 | const char *family; | ||
767 | int rc; | ||
768 | const struct hwicap_driver_config *config = match->data; | ||
769 | const struct config_registers *regs; | ||
770 | |||
771 | dev_dbg(&op->dev, "hwicap_of_probe(%p, %p)\n", op, match); | ||
772 | |||
773 | rc = of_address_to_resource(op->node, 0, &res); | ||
774 | if (rc) { | ||
775 | dev_err(&op->dev, "invalid address\n"); | ||
776 | return rc; | ||
777 | } | ||
778 | |||
779 | id = of_get_property(op->node, "port-number", NULL); | ||
780 | |||
781 | /* It's most likely that we're using V4, if the family is not | ||
782 | specified */ | ||
783 | regs = &v4_config_registers; | ||
784 | family = of_get_property(op->node, "xlnx,family", NULL); | ||
785 | |||
786 | if (family) { | ||
787 | if (!strcmp(family, "virtex2p")) { | ||
788 | regs = &v2_config_registers; | ||
789 | } else if (!strcmp(family, "virtex4")) { | ||
790 | regs = &v4_config_registers; | ||
791 | } else if (!strcmp(family, "virtex5")) { | ||
792 | regs = &v5_config_registers; | ||
793 | } | ||
794 | } | ||
795 | return hwicap_setup(&op->dev, id ? *id : -1, &res, config, | ||
796 | regs); | ||
797 | } | ||
798 | |||
799 | static int __devexit hwicap_of_remove(struct of_device *op) | ||
800 | { | ||
801 | return hwicap_remove(&op->dev); | ||
802 | } | ||
803 | |||
804 | /* Match table for of_platform binding */ | ||
805 | static const struct of_device_id __devinit hwicap_of_match[] = { | ||
806 | { .compatible = "xlnx,opb-hwicap-1.00.b", .data = &buffer_icap_config}, | ||
807 | { .compatible = "xlnx,xps-hwicap-1.00.a", .data = &fifo_icap_config}, | ||
808 | {}, | ||
809 | }; | ||
810 | MODULE_DEVICE_TABLE(of, hwicap_of_match); | ||
811 | |||
812 | static struct of_platform_driver hwicap_of_driver = { | ||
813 | .owner = THIS_MODULE, | ||
814 | .name = DRIVER_NAME, | ||
815 | .match_table = hwicap_of_match, | ||
816 | .probe = hwicap_of_probe, | ||
817 | .remove = __devexit_p(hwicap_of_remove), | ||
818 | .driver = { | ||
819 | .name = DRIVER_NAME, | ||
820 | }, | ||
821 | }; | ||
822 | |||
823 | /* Registration helpers to keep the number of #ifdefs to a minimum */ | ||
824 | static inline int __devinit hwicap_of_register(void) | ||
825 | { | ||
826 | pr_debug("hwicap: calling of_register_platform_driver()\n"); | ||
827 | return of_register_platform_driver(&hwicap_of_driver); | ||
828 | } | ||
829 | |||
830 | static inline void __devexit hwicap_of_unregister(void) | ||
831 | { | ||
832 | of_unregister_platform_driver(&hwicap_of_driver); | ||
833 | } | ||
834 | #else /* CONFIG_OF */ | ||
835 | /* CONFIG_OF not enabled; do nothing helpers */ | ||
836 | static inline int __devinit hwicap_of_register(void) { return 0; } | ||
837 | static inline void __devexit hwicap_of_unregister(void) { } | ||
838 | #endif /* CONFIG_OF */ | ||
839 | |||
840 | static int __devinit hwicap_module_init(void) | ||
841 | { | ||
842 | dev_t devt; | ||
843 | int retval; | ||
844 | |||
845 | icap_class = class_create(THIS_MODULE, "xilinx_config"); | ||
846 | |||
847 | if (xhwicap_major) { | ||
848 | devt = MKDEV(xhwicap_major, xhwicap_minor); | ||
849 | retval = register_chrdev_region( | ||
850 | devt, | ||
851 | HWICAP_DEVICES, | ||
852 | DRIVER_NAME); | ||
853 | if (retval < 0) | ||
854 | return retval; | ||
855 | } else { | ||
856 | retval = alloc_chrdev_region(&devt, | ||
857 | xhwicap_minor, | ||
858 | HWICAP_DEVICES, | ||
859 | DRIVER_NAME); | ||
860 | if (retval < 0) | ||
861 | return retval; | ||
862 | xhwicap_major = MAJOR(devt); | ||
863 | } | ||
864 | |||
865 | retval = platform_driver_register(&hwicap_platform_driver); | ||
866 | |||
867 | if (retval) | ||
868 | goto failed1; | ||
869 | |||
870 | retval = hwicap_of_register(); | ||
871 | |||
872 | if (retval) | ||
873 | goto failed2; | ||
874 | |||
875 | return retval; | ||
876 | |||
877 | failed2: | ||
878 | platform_driver_unregister(&hwicap_platform_driver); | ||
879 | |||
880 | failed1: | ||
881 | unregister_chrdev_region(devt, HWICAP_DEVICES); | ||
882 | |||
883 | return retval; | ||
884 | } | ||
885 | |||
886 | static void __devexit hwicap_module_cleanup(void) | ||
887 | { | ||
888 | dev_t devt = MKDEV(xhwicap_major, xhwicap_minor); | ||
889 | |||
890 | class_destroy(icap_class); | ||
891 | |||
892 | platform_driver_unregister(&hwicap_platform_driver); | ||
893 | |||
894 | hwicap_of_unregister(); | ||
895 | |||
896 | unregister_chrdev_region(devt, HWICAP_DEVICES); | ||
897 | } | ||
898 | |||
899 | module_init(hwicap_module_init); | ||
900 | module_exit(hwicap_module_cleanup); | ||
901 | |||
902 | MODULE_AUTHOR("Xilinx, Inc; Xilinx Research Labs Group"); | ||
903 | MODULE_DESCRIPTION("Xilinx ICAP Port Driver"); | ||
904 | MODULE_LICENSE("GPL"); | ||