FILE
axis-fifo.c
File preview with syntax highlighting.
linux-6.19.14/drivers/staging/axis-fifo/axis-fifo.c
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Xilinx AXIS FIFO: interface to the Xilinx AXI-Stream FIFO IP core 4 * 5 * Copyright (C) 2018 Jacob Feder 6 * 7 * Authors: Jacob Feder <jacobsfeder@gmail.com> 8 * 9 * See Xilinx PG080 document for IP details 10 */ 11 12/* ---------------------------- 13 * includes 14 * ---------------------------- 15 */ 16 17#include <linux/kernel.h> 18#include <linux/of.h> 19#include <linux/platform_device.h> 20#include <linux/wait.h> 21#include <linux/mutex.h> 22#include <linux/device.h> 23#include <linux/cdev.h> 24#include <linux/init.h> 25#include <linux/module.h> 26#include <linux/slab.h> 27#include <linux/io.h> 28#include <linux/moduleparam.h> 29#include <linux/interrupt.h> 30#include <linux/param.h> 31#include <linux/fs.h> 32#include <linux/types.h> 33#include <linux/uaccess.h> 34#include <linux/jiffies.h> 35#include <linux/miscdevice.h> 36#include <linux/debugfs.h> 37 38/* ---------------------------- 39 * driver parameters 40 * ---------------------------- 41 */ 42 43#define DRIVER_NAME "axis_fifo" 44 45#define READ_BUF_SIZE 128U /* read buffer length in words */ 46 47#define AXIS_FIFO_DEBUG_REG_NAME_MAX_LEN 4 48 49/* ---------------------------- 50 * IP register offsets 51 * ---------------------------- 52 */ 53 54#define XLLF_ISR_OFFSET 0x00000000 /* Interrupt Status */ 55#define XLLF_IER_OFFSET 0x00000004 /* Interrupt Enable */ 56 57#define XLLF_TDFR_OFFSET 0x00000008 /* Transmit Reset */ 58#define XLLF_TDFV_OFFSET 0x0000000c /* Transmit Vacancy */ 59#define XLLF_TDFD_OFFSET 0x00000010 /* Transmit Data */ 60#define XLLF_TLR_OFFSET 0x00000014 /* Transmit Length */ 61 62#define XLLF_RDFR_OFFSET 0x00000018 /* Receive Reset */ 63#define XLLF_RDFO_OFFSET 0x0000001c /* Receive Occupancy */ 64#define XLLF_RDFD_OFFSET 0x00000020 /* Receive Data */ 65#define XLLF_RLR_OFFSET 0x00000024 /* Receive Length */ 66#define XLLF_SRR_OFFSET 0x00000028 /* Local Link Reset */ 67#define XLLF_TDR_OFFSET 0x0000002C /* Transmit Destination */ 68#define XLLF_RDR_OFFSET 0x00000030 /* Receive Destination */ 69 70/* ---------------------------- 71 * reset register masks 72 * ---------------------------- 73 */ 74 75#define XLLF_RDFR_RESET_MASK 0x000000a5 /* receive reset value */ 76#define XLLF_TDFR_RESET_MASK 0x000000a5 /* Transmit reset value */ 77#define XLLF_SRR_RESET_MASK 0x000000a5 /* Local Link reset value */ 78 79/* ---------------------------- 80 * interrupt masks 81 * ---------------------------- 82 */ 83 84#define XLLF_INT_RPURE_MASK 0x80000000 /* Receive under-read */ 85#define XLLF_INT_RPORE_MASK 0x40000000 /* Receive over-read */ 86#define XLLF_INT_RPUE_MASK 0x20000000 /* Receive underrun (empty) */ 87#define XLLF_INT_TPOE_MASK 0x10000000 /* Transmit overrun */ 88#define XLLF_INT_TC_MASK 0x08000000 /* Transmit complete */ 89#define XLLF_INT_RC_MASK 0x04000000 /* Receive complete */ 90#define XLLF_INT_TSE_MASK 0x02000000 /* Transmit length mismatch */ 91 92#define XLLF_INT_CLEAR_ALL GENMASK(31, 0) 93 94/* ---------------------------- 95 * globals 96 * ---------------------------- 97 */ 98static long read_timeout = 1000; /* ms to wait before read() times out */ 99static long write_timeout = 1000; /* ms to wait before write() times out */ 100 101static DEFINE_IDA(axis_fifo_ida); 102 103/* ---------------------------- 104 * module command-line arguments 105 * ---------------------------- 106 */ 107 108module_param(read_timeout, long, 0444); 109MODULE_PARM_DESC(read_timeout, "ms to wait before blocking read() timing out; set to -1 for no timeout"); 110module_param(write_timeout, long, 0444); 111MODULE_PARM_DESC(write_timeout, "ms to wait before blocking write() timing out; set to -1 for no timeout"); 112 113/* ---------------------------- 114 * types 115 * ---------------------------- 116 */ 117 118struct axis_fifo { 119 int id; 120 void __iomem *base_addr; /* kernel space memory */ 121 122 unsigned int rx_fifo_depth; /* max words in the receive fifo */ 123 unsigned int tx_fifo_depth; /* max words in the transmit fifo */ 124 int has_rx_fifo; /* whether the IP has the rx fifo enabled */ 125 int has_tx_fifo; /* whether the IP has the tx fifo enabled */ 126 127 wait_queue_head_t read_queue; /* wait queue for asynchronos read */ 128 struct mutex read_lock; /* lock for reading */ 129 wait_queue_head_t write_queue; /* wait queue for asynchronos write */ 130 struct mutex write_lock; /* lock for writing */ 131 132 struct device *dt_device; /* device created from the device tree */ 133 struct miscdevice miscdev; 134 135 struct dentry *debugfs_dir; 136}; 137 138struct axis_fifo_debug_reg { 139 const char * const name; 140 unsigned int offset; 141}; 142 143/* ---------------------------- 144 * implementation 145 * ---------------------------- 146 */ 147 148static void reset_ip_core(struct axis_fifo *fifo) 149{ 150 iowrite32(XLLF_SRR_RESET_MASK, fifo->base_addr + XLLF_SRR_OFFSET); 151 iowrite32(XLLF_TDFR_RESET_MASK, fifo->base_addr + XLLF_TDFR_OFFSET); 152 iowrite32(XLLF_RDFR_RESET_MASK, fifo->base_addr + XLLF_RDFR_OFFSET); 153 iowrite32(XLLF_INT_TC_MASK | XLLF_INT_RC_MASK | XLLF_INT_RPURE_MASK | 154 XLLF_INT_RPORE_MASK | XLLF_INT_RPUE_MASK | 155 XLLF_INT_TPOE_MASK | XLLF_INT_TSE_MASK, 156 fifo->base_addr + XLLF_IER_OFFSET); 157 iowrite32(XLLF_INT_CLEAR_ALL, fifo->base_addr + XLLF_ISR_OFFSET); 158} 159 160/** 161 * axis_fifo_read() - Read a packet from AXIS-FIFO character device. 162 * @f: Open file. 163 * @buf: User space buffer to read to. 164 * @len: User space buffer length. 165 * @off: Buffer offset. 166 * 167 * As defined by the device's documentation, we need to check the device's 168 * occupancy before reading the length register and then the data. All these 169 * operations must be executed atomically, in order and one after the other 170 * without missing any. 171 * 172 * Returns the number of bytes read from the device or negative error code 173 * on failure. 174 */ 175static ssize_t axis_fifo_read(struct file *f, char __user *buf, 176 size_t len, loff_t *off) 177{ 178 struct axis_fifo *fifo = (struct axis_fifo *)f->private_data; 179 size_t bytes_available; 180 unsigned int words_available; 181 unsigned int copied; 182 unsigned int copy; 183 unsigned int i; 184 int ret; 185 u32 tmp_buf[READ_BUF_SIZE]; 186 187 if (f->f_flags & O_NONBLOCK) { 188 /* 189 * Device opened in non-blocking mode. Try to lock it and then 190 * check if any packet is available. 191 */ 192 if (!mutex_trylock(&fifo->read_lock)) 193 return -EAGAIN; 194 195 if (!ioread32(fifo->base_addr + XLLF_RDFO_OFFSET)) { 196 ret = -EAGAIN; 197 goto end_unlock; 198 } 199 } else { 200 /* opened in blocking mode 201 * wait for a packet available interrupt (or timeout) 202 * if nothing is currently available 203 */ 204 mutex_lock(&fifo->read_lock); 205 ret = wait_event_interruptible_timeout(fifo->read_queue, 206 ioread32(fifo->base_addr + XLLF_RDFO_OFFSET), 207 read_timeout); 208 209 if (ret <= 0) { 210 if (ret == 0) { 211 ret = -EAGAIN; 212 } else if (ret != -ERESTARTSYS) { 213 dev_err(fifo->dt_device, "wait_event_interruptible_timeout() error in read (ret=%i)\n", 214 ret); 215 } 216 217 goto end_unlock; 218 } 219 } 220 221 bytes_available = ioread32(fifo->base_addr + XLLF_RLR_OFFSET); 222 words_available = bytes_available / sizeof(u32); 223 if (!bytes_available) { 224 dev_err(fifo->dt_device, "received a packet of length 0\n"); 225 ret = -EIO; 226 goto end_unlock; 227 } 228 229 if (bytes_available > len) { 230 dev_err(fifo->dt_device, "user read buffer too small (available bytes=%zu user buffer bytes=%zu)\n", 231 bytes_available, len); 232 ret = -EINVAL; 233 goto err_flush_rx; 234 } 235 236 if (bytes_available % sizeof(u32)) { 237 /* this probably can't happen unless IP 238 * registers were previously mishandled 239 */ 240 dev_err(fifo->dt_device, "received a packet that isn't word-aligned\n"); 241 ret = -EIO; 242 goto err_flush_rx; 243 } 244 245 /* read data into an intermediate buffer, copying the contents 246 * to userspace when the buffer is full 247 */ 248 copied = 0; 249 while (words_available > 0) { 250 copy = min(words_available, READ_BUF_SIZE); 251 252 for (i = 0; i < copy; i++) { 253 tmp_buf[i] = ioread32(fifo->base_addr + 254 XLLF_RDFD_OFFSET); 255 } 256 words_available -= copy; 257 258 if (copy_to_user(buf + copied * sizeof(u32), tmp_buf, 259 copy * sizeof(u32))) { 260 ret = -EFAULT; 261 goto err_flush_rx; 262 } 263 264 copied += copy; 265 } 266 mutex_unlock(&fifo->read_lock); 267 268 return bytes_available; 269 270err_flush_rx: 271 while (words_available--) 272 ioread32(fifo->base_addr + XLLF_RDFD_OFFSET); 273 274end_unlock: 275 mutex_unlock(&fifo->read_lock); 276 277 return ret; 278} 279 280/** 281 * axis_fifo_write() - Write buffer to AXIS-FIFO character device. 282 * @f: Open file. 283 * @buf: User space buffer to write to the device. 284 * @len: User space buffer length. 285 * @off: Buffer offset. 286 * 287 * As defined by the device's documentation, we need to write to the device's 288 * data buffer then to the device's packet length register atomically. Also, 289 * we need to lock before checking if the device has available space to avoid 290 * any concurrency issue. 291 * 292 * Returns the number of bytes written to the device or negative error code 293 * on failure. 294 */ 295static ssize_t axis_fifo_write(struct file *f, const char __user *buf, 296 size_t len, loff_t *off) 297{ 298 struct axis_fifo *fifo = (struct axis_fifo *)f->private_data; 299 unsigned int words_to_write; 300 u32 *txbuf; 301 int ret; 302 303 if (len % sizeof(u32)) { 304 dev_err(fifo->dt_device, 305 "tried to send a packet that isn't word-aligned\n"); 306 return -EINVAL; 307 } 308 309 words_to_write = len / sizeof(u32); 310 311 if (!words_to_write) { 312 dev_err(fifo->dt_device, 313 "tried to send a packet of length 0\n"); 314 return -EINVAL; 315 } 316 317 /* 318 * In 'Store-and-Forward' mode, the maximum packet that can be 319 * transmitted is limited by the size of the FIFO, which is 320 * (C_TX_FIFO_DEPTH–4)*(data interface width/8) bytes. 321 * 322 * Do not attempt to send a packet larger than 'tx_fifo_depth - 4', 323 * otherwise a 'Transmit Packet Overrun Error' interrupt will be 324 * raised, which requires a reset of the TX circuit to recover. 325 */ 326 if (words_to_write > (fifo->tx_fifo_depth - 4)) 327 return -EINVAL; 328 329 if (f->f_flags & O_NONBLOCK) { 330 /* 331 * Device opened in non-blocking mode. Try to lock it and then 332 * check if there is any room to write the given buffer. 333 */ 334 if (!mutex_trylock(&fifo->write_lock)) 335 return -EAGAIN; 336 337 if (words_to_write > ioread32(fifo->base_addr + 338 XLLF_TDFV_OFFSET)) { 339 ret = -EAGAIN; 340 goto end_unlock; 341 } 342 } else { 343 /* opened in blocking mode */ 344 345 /* wait for an interrupt (or timeout) if there isn't 346 * currently enough room in the fifo 347 */ 348 mutex_lock(&fifo->write_lock); 349 ret = wait_event_interruptible_timeout(fifo->write_queue, 350 ioread32(fifo->base_addr + XLLF_TDFV_OFFSET) 351 >= words_to_write, 352 write_timeout); 353 354 if (ret <= 0) { 355 if (ret == 0) { 356 ret = -EAGAIN; 357 } else if (ret != -ERESTARTSYS) { 358 dev_err(fifo->dt_device, "wait_event_interruptible_timeout() error in write (ret=%i)\n", 359 ret); 360 } 361 362 goto end_unlock; 363 } 364 } 365 366 txbuf = vmemdup_user(buf, len); 367 if (IS_ERR(txbuf)) { 368 ret = PTR_ERR(txbuf); 369 goto end_unlock; 370 } 371 372 for (int i = 0; i < words_to_write; ++i) 373 iowrite32(txbuf[i], fifo->base_addr + XLLF_TDFD_OFFSET); 374 375 /* write packet size to fifo */ 376 iowrite32(len, fifo->base_addr + XLLF_TLR_OFFSET); 377 378 ret = len; 379 kvfree(txbuf); 380end_unlock: 381 mutex_unlock(&fifo->write_lock); 382 383 return ret; 384} 385 386static irqreturn_t axis_fifo_irq(int irq, void *dw) 387{ 388 struct axis_fifo *fifo = dw; 389 u32 isr, ier, intr; 390 391 ier = ioread32(fifo->base_addr + XLLF_IER_OFFSET); 392 isr = ioread32(fifo->base_addr + XLLF_ISR_OFFSET); 393 intr = ier & isr; 394 395 if (intr & XLLF_INT_RC_MASK) 396 wake_up(&fifo->read_queue); 397 398 if (intr & XLLF_INT_TC_MASK) 399 wake_up(&fifo->write_queue); 400 401 if (intr & XLLF_INT_RPURE_MASK) 402 dev_err(fifo->dt_device, "receive under-read interrupt\n"); 403 404 if (intr & XLLF_INT_RPORE_MASK) 405 dev_err(fifo->dt_device, "receive over-read interrupt\n"); 406 407 if (intr & XLLF_INT_RPUE_MASK) 408 dev_err(fifo->dt_device, "receive underrun error interrupt\n"); 409 410 if (intr & XLLF_INT_TPOE_MASK) 411 dev_err(fifo->dt_device, "transmit overrun error interrupt\n"); 412 413 if (intr & XLLF_INT_TSE_MASK) 414 dev_err(fifo->dt_device, 415 "transmit length mismatch error interrupt\n"); 416 417 iowrite32(XLLF_INT_CLEAR_ALL, fifo->base_addr + XLLF_ISR_OFFSET); 418 419 return IRQ_HANDLED; 420} 421 422static int axis_fifo_open(struct inode *inod, struct file *f) 423{ 424 struct axis_fifo *fifo = container_of(f->private_data, 425 struct axis_fifo, miscdev); 426 unsigned int flags = f->f_flags & O_ACCMODE; 427 428 f->private_data = fifo; 429 430 if ((flags == O_WRONLY || flags == O_RDWR) && !fifo->has_tx_fifo) 431 return -EPERM; 432 433 if ((flags == O_RDONLY || flags == O_RDWR) && !fifo->has_rx_fifo) 434 return -EPERM; 435 436 return 0; 437} 438 439static int axis_fifo_close(struct inode *inod, struct file *f) 440{ 441 f->private_data = NULL; 442 443 return 0; 444} 445 446static const struct file_operations fops = { 447 .owner = THIS_MODULE, 448 .open = axis_fifo_open, 449 .release = axis_fifo_close, 450 .read = axis_fifo_read, 451 .write = axis_fifo_write 452}; 453 454static int axis_fifo_debugfs_regs_show(struct seq_file *m, void *p) 455{ 456 static const struct axis_fifo_debug_reg regs[] = { 457 {"isr", XLLF_ISR_OFFSET}, 458 {"ier", XLLF_IER_OFFSET}, 459 {"tdfv", XLLF_TDFV_OFFSET}, 460 {"rdfo", XLLF_RDFO_OFFSET}, 461 { /* Sentinel */ }, 462 }; 463 const struct axis_fifo_debug_reg *reg; 464 struct axis_fifo *fifo = m->private; 465 466 for (reg = regs; reg->name; ++reg) { 467 u32 val = ioread32(fifo->base_addr + reg->offset); 468 469 seq_printf(m, "%*s: 0x%08x\n", AXIS_FIFO_DEBUG_REG_NAME_MAX_LEN, 470 reg->name, val); 471 } 472 473 return 0; 474} 475DEFINE_SHOW_ATTRIBUTE(axis_fifo_debugfs_regs); 476 477static void axis_fifo_debugfs_init(struct axis_fifo *fifo) 478{ 479 fifo->debugfs_dir = debugfs_create_dir(dev_name(fifo->dt_device), NULL); 480 481 debugfs_create_file("regs", 0444, fifo->debugfs_dir, fifo, 482 &axis_fifo_debugfs_regs_fops); 483} 484 485static int axis_fifo_parse_dt(struct axis_fifo *fifo) 486{ 487 int ret; 488 unsigned int value; 489 struct device_node *node = fifo->dt_device->of_node; 490 491 ret = of_property_read_u32(node, "xlnx,axi-str-rxd-tdata-width", 492 &value); 493 if (ret) { 494 dev_err(fifo->dt_device, "missing xlnx,axi-str-rxd-tdata-width property\n"); 495 goto end; 496 } else if (value != 32) { 497 dev_err(fifo->dt_device, "xlnx,axi-str-rxd-tdata-width only supports 32 bits\n"); 498 ret = -EIO; 499 goto end; 500 } 501 502 ret = of_property_read_u32(node, "xlnx,axi-str-txd-tdata-width", 503 &value); 504 if (ret) { 505 dev_err(fifo->dt_device, "missing xlnx,axi-str-txd-tdata-width property\n"); 506 goto end; 507 } else if (value != 32) { 508 dev_err(fifo->dt_device, "xlnx,axi-str-txd-tdata-width only supports 32 bits\n"); 509 ret = -EIO; 510 goto end; 511 } 512 513 ret = of_property_read_u32(node, "xlnx,rx-fifo-depth", 514 &fifo->rx_fifo_depth); 515 if (ret) { 516 dev_err(fifo->dt_device, "missing xlnx,rx-fifo-depth property\n"); 517 ret = -EIO; 518 goto end; 519 } 520 521 ret = of_property_read_u32(node, "xlnx,tx-fifo-depth", 522 &fifo->tx_fifo_depth); 523 if (ret) { 524 dev_err(fifo->dt_device, "missing xlnx,tx-fifo-depth property\n"); 525 ret = -EIO; 526 goto end; 527 } 528 529 ret = of_property_read_u32(node, "xlnx,use-rx-data", 530 &fifo->has_rx_fifo); 531 if (ret) { 532 dev_err(fifo->dt_device, "missing xlnx,use-rx-data property\n"); 533 ret = -EIO; 534 goto end; 535 } 536 537 ret = of_property_read_u32(node, "xlnx,use-tx-data", 538 &fifo->has_tx_fifo); 539 if (ret) { 540 dev_err(fifo->dt_device, "missing xlnx,use-tx-data property\n"); 541 ret = -EIO; 542 goto end; 543 } 544 545end: 546 return ret; 547} 548 549static int axis_fifo_probe(struct platform_device *pdev) 550{ 551 struct resource *r_mem; /* IO mem resources */ 552 struct device *dev = &pdev->dev; /* OS device (from device tree) */ 553 struct axis_fifo *fifo = NULL; 554 char *device_name; 555 int rc = 0; /* error return value */ 556 int irq; 557 558 /* ---------------------------- 559 * init wrapper device 560 * ---------------------------- 561 */ 562 563 device_name = devm_kzalloc(dev, 32, GFP_KERNEL); 564 if (!device_name) 565 return -ENOMEM; 566 567 /* allocate device wrapper memory */ 568 fifo = devm_kzalloc(dev, sizeof(*fifo), GFP_KERNEL); 569 if (!fifo) 570 return -ENOMEM; 571 572 dev_set_drvdata(dev, fifo); 573 fifo->dt_device = dev; 574 575 init_waitqueue_head(&fifo->read_queue); 576 init_waitqueue_head(&fifo->write_queue); 577 578 mutex_init(&fifo->read_lock); 579 mutex_init(&fifo->write_lock); 580 581 /* ---------------------------- 582 * init device memory space 583 * ---------------------------- 584 */ 585 586 /* get iospace for the device and request physical memory */ 587 fifo->base_addr = devm_platform_get_and_ioremap_resource(pdev, 0, &r_mem); 588 if (IS_ERR(fifo->base_addr)) 589 return PTR_ERR(fifo->base_addr); 590 591 /* ---------------------------- 592 * init IP 593 * ---------------------------- 594 */ 595 596 rc = axis_fifo_parse_dt(fifo); 597 if (rc) 598 return rc; 599 600 reset_ip_core(fifo); 601 602 /* ---------------------------- 603 * init device interrupts 604 * ---------------------------- 605 */ 606 607 /* get IRQ resource */ 608 irq = platform_get_irq(pdev, 0); 609 if (irq < 0) 610 return irq; 611 612 /* request IRQ */ 613 rc = devm_request_irq(fifo->dt_device, irq, &axis_fifo_irq, 0, 614 DRIVER_NAME, fifo); 615 if (rc) { 616 dev_err(fifo->dt_device, "couldn't allocate interrupt %i\n", 617 irq); 618 return rc; 619 } 620 621 /* ---------------------------- 622 * init char device 623 * ---------------------------- 624 */ 625 fifo->id = ida_alloc(&axis_fifo_ida, GFP_KERNEL); 626 if (fifo->id < 0) 627 return fifo->id; 628 629 snprintf(device_name, 32, "%s%d", DRIVER_NAME, fifo->id); 630 631 /* create character device */ 632 fifo->miscdev.fops = &fops; 633 fifo->miscdev.minor = MISC_DYNAMIC_MINOR; 634 fifo->miscdev.name = device_name; 635 fifo->miscdev.parent = dev; 636 rc = misc_register(&fifo->miscdev); 637 if (rc < 0) { 638 ida_free(&axis_fifo_ida, fifo->id); 639 return rc; 640 } 641 642 axis_fifo_debugfs_init(fifo); 643 644 return 0; 645} 646 647static void axis_fifo_remove(struct platform_device *pdev) 648{ 649 struct device *dev = &pdev->dev; 650 struct axis_fifo *fifo = dev_get_drvdata(dev); 651 652 debugfs_remove(fifo->debugfs_dir); 653 misc_deregister(&fifo->miscdev); 654 ida_free(&axis_fifo_ida, fifo->id); 655} 656 657static const struct of_device_id axis_fifo_of_match[] = { 658 { .compatible = "xlnx,axi-fifo-mm-s-4.1", }, 659 { .compatible = "xlnx,axi-fifo-mm-s-4.2", }, 660 { .compatible = "xlnx,axi-fifo-mm-s-4.3", }, 661 {}, 662}; 663MODULE_DEVICE_TABLE(of, axis_fifo_of_match); 664 665static struct platform_driver axis_fifo_driver = { 666 .driver = { 667 .name = DRIVER_NAME, 668 .of_match_table = axis_fifo_of_match, 669 }, 670 .probe = axis_fifo_probe, 671 .remove = axis_fifo_remove, 672}; 673 674static int __init axis_fifo_init(void) 675{ 676 if (read_timeout >= 0) 677 read_timeout = msecs_to_jiffies(read_timeout); 678 else 679 read_timeout = MAX_SCHEDULE_TIMEOUT; 680 681 if (write_timeout >= 0) 682 write_timeout = msecs_to_jiffies(write_timeout); 683 else 684 write_timeout = MAX_SCHEDULE_TIMEOUT; 685 686 pr_info("axis-fifo driver loaded with parameters read_timeout = %li, write_timeout = %li\n", 687 read_timeout, write_timeout); 688 return platform_driver_register(&axis_fifo_driver); 689} 690 691module_init(axis_fifo_init); 692 693static void __exit axis_fifo_exit(void) 694{ 695 platform_driver_unregister(&axis_fifo_driver); 696 ida_destroy(&axis_fifo_ida); 697} 698 699module_exit(axis_fifo_exit); 700 701MODULE_LICENSE("GPL"); 702MODULE_AUTHOR("Jacob Feder <jacobsfeder@gmail.com>"); 703MODULE_DESCRIPTION("Xilinx AXI-Stream FIFO IP core driver");
~/
Read-only file preview.