Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 542 lines 14 kB view raw
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#include <linux/kernel.h> 13#include <linux/of.h> 14#include <linux/platform_device.h> 15#include <linux/wait.h> 16#include <linux/mutex.h> 17#include <linux/device.h> 18#include <linux/cdev.h> 19#include <linux/init.h> 20#include <linux/module.h> 21#include <linux/slab.h> 22#include <linux/io.h> 23#include <linux/interrupt.h> 24#include <linux/fs.h> 25#include <linux/types.h> 26#include <linux/uaccess.h> 27#include <linux/jiffies.h> 28#include <linux/miscdevice.h> 29#include <linux/debugfs.h> 30#include <linux/poll.h> 31 32#define DRIVER_NAME "axis_fifo" 33 34#define READ_BUF_SIZE 128U /* read buffer length in words */ 35 36#define AXIS_FIFO_DEBUG_REG_NAME_MAX_LEN 4 37 38#define XLLF_ISR_OFFSET 0x00 /* Interrupt Status */ 39#define XLLF_IER_OFFSET 0x04 /* Interrupt Enable */ 40#define XLLF_TDFR_OFFSET 0x08 /* Transmit Reset */ 41#define XLLF_TDFV_OFFSET 0x0c /* Transmit Vacancy */ 42#define XLLF_TDFD_OFFSET 0x10 /* Transmit Data */ 43#define XLLF_TLR_OFFSET 0x14 /* Transmit Length */ 44#define XLLF_RDFR_OFFSET 0x18 /* Receive Reset */ 45#define XLLF_RDFO_OFFSET 0x1c /* Receive Occupancy */ 46#define XLLF_RDFD_OFFSET 0x20 /* Receive Data */ 47#define XLLF_RLR_OFFSET 0x24 /* Receive Length */ 48#define XLLF_SRR_OFFSET 0x28 /* Local Link Reset */ 49#define XLLF_TDR_OFFSET 0x2C /* Transmit Destination */ 50#define XLLF_RDR_OFFSET 0x30 /* Receive Destination */ 51 52#define XLLF_RDFR_RESET_MASK 0xa5 /* Receive reset value */ 53#define XLLF_TDFR_RESET_MASK 0xa5 /* Transmit reset value */ 54#define XLLF_SRR_RESET_MASK 0xa5 /* Local Link reset value */ 55 56#define XLLF_INT_RPURE_MASK BIT(31) /* Receive under-read */ 57#define XLLF_INT_RPORE_MASK BIT(30) /* Receive over-read */ 58#define XLLF_INT_RPUE_MASK BIT(29) /* Receive underrun (empty) */ 59#define XLLF_INT_TPOE_MASK BIT(28) /* Transmit overrun */ 60#define XLLF_INT_TC_MASK BIT(27) /* Transmit complete */ 61#define XLLF_INT_RC_MASK BIT(26) /* Receive complete */ 62#define XLLF_INT_TSE_MASK BIT(25) /* Transmit length mismatch */ 63 64#define XLLF_INT_CLEAR_ALL GENMASK(31, 0) 65 66static DEFINE_IDA(axis_fifo_ida); 67 68struct axis_fifo { 69 int id; 70 void __iomem *base_addr; 71 72 unsigned int rx_fifo_depth; 73 unsigned int tx_fifo_depth; 74 u32 has_rx_fifo; 75 u32 has_tx_fifo; 76 77 wait_queue_head_t read_queue; 78 struct mutex read_lock; /* lock for reading */ 79 wait_queue_head_t write_queue; 80 struct mutex write_lock; /* lock for writing */ 81 82 struct device *dt_device; 83 struct miscdevice miscdev; 84 85 struct dentry *debugfs_dir; 86}; 87 88struct axis_fifo_debug_reg { 89 const char * const name; 90 unsigned int offset; 91}; 92 93static void reset_ip_core(struct axis_fifo *fifo) 94{ 95 iowrite32(XLLF_SRR_RESET_MASK, fifo->base_addr + XLLF_SRR_OFFSET); 96 iowrite32(XLLF_TDFR_RESET_MASK, fifo->base_addr + XLLF_TDFR_OFFSET); 97 iowrite32(XLLF_RDFR_RESET_MASK, fifo->base_addr + XLLF_RDFR_OFFSET); 98 iowrite32(XLLF_INT_TC_MASK | XLLF_INT_RC_MASK | XLLF_INT_RPURE_MASK | 99 XLLF_INT_RPORE_MASK | XLLF_INT_RPUE_MASK | 100 XLLF_INT_TPOE_MASK | XLLF_INT_TSE_MASK, 101 fifo->base_addr + XLLF_IER_OFFSET); 102 iowrite32(XLLF_INT_CLEAR_ALL, fifo->base_addr + XLLF_ISR_OFFSET); 103} 104 105/** 106 * axis_fifo_read() - Read a packet from AXIS-FIFO character device. 107 * @f: Open file. 108 * @buf: User space buffer to read to. 109 * @len: User space buffer length. 110 * @off: Buffer offset. 111 * 112 * As defined by the device's documentation, we need to check the device's 113 * occupancy before reading the length register and then the data. All these 114 * operations must be executed atomically, in order and one after the other 115 * without missing any. 116 * 117 * Returns the number of bytes read from the device or negative error code 118 * on failure. 119 */ 120static ssize_t axis_fifo_read(struct file *f, char __user *buf, 121 size_t len, loff_t *off) 122{ 123 struct axis_fifo *fifo = f->private_data; 124 size_t bytes_available; 125 unsigned int words_available; 126 unsigned int copied; 127 unsigned int copy; 128 unsigned int i; 129 int ret; 130 u32 tmp_buf[READ_BUF_SIZE]; 131 132 if (f->f_flags & O_NONBLOCK) { 133 if (!mutex_trylock(&fifo->read_lock)) 134 return -EAGAIN; 135 136 if (!ioread32(fifo->base_addr + XLLF_RDFO_OFFSET)) { 137 ret = -EAGAIN; 138 goto end_unlock; 139 } 140 } else { 141 mutex_lock(&fifo->read_lock); 142 143 ret = wait_event_interruptible(fifo->read_queue, 144 ioread32(fifo->base_addr + XLLF_RDFO_OFFSET)); 145 if (ret) 146 goto end_unlock; 147 } 148 149 bytes_available = ioread32(fifo->base_addr + XLLF_RLR_OFFSET); 150 words_available = bytes_available / sizeof(u32); 151 152 if (bytes_available > len) { 153 ret = -EINVAL; 154 goto err_flush_rx; 155 } 156 157 if (bytes_available % sizeof(u32)) { 158 /* this probably can't happen unless IP 159 * registers were previously mishandled 160 */ 161 dev_err(fifo->dt_device, "received a packet that isn't word-aligned\n"); 162 ret = -EIO; 163 goto err_flush_rx; 164 } 165 166 copied = 0; 167 while (words_available > 0) { 168 copy = min(words_available, READ_BUF_SIZE); 169 170 for (i = 0; i < copy; i++) { 171 tmp_buf[i] = ioread32(fifo->base_addr + 172 XLLF_RDFD_OFFSET); 173 } 174 words_available -= copy; 175 176 if (copy_to_user(buf + copied * sizeof(u32), tmp_buf, 177 copy * sizeof(u32))) { 178 ret = -EFAULT; 179 goto err_flush_rx; 180 } 181 182 copied += copy; 183 } 184 mutex_unlock(&fifo->read_lock); 185 186 return bytes_available; 187 188err_flush_rx: 189 while (words_available--) 190 ioread32(fifo->base_addr + XLLF_RDFD_OFFSET); 191 192end_unlock: 193 mutex_unlock(&fifo->read_lock); 194 195 return ret; 196} 197 198/** 199 * axis_fifo_write() - Write buffer to AXIS-FIFO character device. 200 * @f: Open file. 201 * @buf: User space buffer to write to the device. 202 * @len: User space buffer length. 203 * @off: Buffer offset. 204 * 205 * As defined by the device's documentation, we need to write to the device's 206 * data buffer then to the device's packet length register atomically. Also, 207 * we need to lock before checking if the device has available space to avoid 208 * any concurrency issue. 209 * 210 * Returns the number of bytes written to the device or negative error code 211 * on failure. 212 */ 213static ssize_t axis_fifo_write(struct file *f, const char __user *buf, 214 size_t len, loff_t *off) 215{ 216 struct axis_fifo *fifo = f->private_data; 217 unsigned int words_to_write; 218 u32 *txbuf; 219 int ret; 220 221 words_to_write = len / sizeof(u32); 222 223 /* 224 * In 'Store-and-Forward' mode, the maximum packet that can be 225 * transmitted is limited by the size of the FIFO, which is 226 * (C_TX_FIFO_DEPTH–4)*(data interface width/8) bytes. 227 * 228 * Do not attempt to send a packet larger than 'tx_fifo_depth - 4', 229 * otherwise a 'Transmit Packet Overrun Error' interrupt will be 230 * raised, which requires a reset of the TX circuit to recover. 231 */ 232 if (!words_to_write || (len % sizeof(u32)) || 233 (words_to_write > (fifo->tx_fifo_depth - 4))) 234 return -EINVAL; 235 236 if (f->f_flags & O_NONBLOCK) { 237 if (!mutex_trylock(&fifo->write_lock)) 238 return -EAGAIN; 239 240 if (words_to_write > ioread32(fifo->base_addr + 241 XLLF_TDFV_OFFSET)) { 242 ret = -EAGAIN; 243 goto end_unlock; 244 } 245 } else { 246 mutex_lock(&fifo->write_lock); 247 248 ret = wait_event_interruptible(fifo->write_queue, 249 ioread32(fifo->base_addr + XLLF_TDFV_OFFSET) >= words_to_write); 250 if (ret) 251 goto end_unlock; 252 } 253 254 txbuf = vmemdup_user(buf, len); 255 if (IS_ERR(txbuf)) { 256 ret = PTR_ERR(txbuf); 257 goto end_unlock; 258 } 259 260 for (int i = 0; i < words_to_write; ++i) 261 iowrite32(txbuf[i], fifo->base_addr + XLLF_TDFD_OFFSET); 262 263 iowrite32(len, fifo->base_addr + XLLF_TLR_OFFSET); 264 265 ret = len; 266 kvfree(txbuf); 267end_unlock: 268 mutex_unlock(&fifo->write_lock); 269 270 return ret; 271} 272 273static __poll_t axis_fifo_poll(struct file *f, poll_table *wait) 274{ 275 struct axis_fifo *fifo = f->private_data; 276 __poll_t mask = 0; 277 278 if (fifo->has_rx_fifo) { 279 poll_wait(f, &fifo->read_queue, wait); 280 281 if (ioread32(fifo->base_addr + XLLF_RDFO_OFFSET)) 282 mask |= EPOLLIN | EPOLLRDNORM; 283 } 284 285 if (fifo->has_tx_fifo) { 286 poll_wait(f, &fifo->write_queue, wait); 287 288 if (ioread32(fifo->base_addr + XLLF_TDFV_OFFSET)) 289 mask |= EPOLLOUT | EPOLLWRNORM; 290 } 291 292 return mask; 293} 294 295static irqreturn_t axis_fifo_irq(int irq, void *dw) 296{ 297 struct axis_fifo *fifo = dw; 298 u32 isr, ier, intr; 299 300 ier = ioread32(fifo->base_addr + XLLF_IER_OFFSET); 301 isr = ioread32(fifo->base_addr + XLLF_ISR_OFFSET); 302 intr = ier & isr; 303 304 if (intr & XLLF_INT_RC_MASK) 305 wake_up(&fifo->read_queue); 306 307 if (intr & XLLF_INT_TC_MASK) 308 wake_up(&fifo->write_queue); 309 310 if (intr & XLLF_INT_RPURE_MASK) 311 dev_err(fifo->dt_device, "receive under-read interrupt\n"); 312 313 if (intr & XLLF_INT_RPORE_MASK) 314 dev_err(fifo->dt_device, "receive over-read interrupt\n"); 315 316 if (intr & XLLF_INT_RPUE_MASK) 317 dev_err(fifo->dt_device, "receive underrun error interrupt\n"); 318 319 if (intr & XLLF_INT_TPOE_MASK) 320 dev_err(fifo->dt_device, "transmit overrun error interrupt\n"); 321 322 if (intr & XLLF_INT_TSE_MASK) 323 dev_err(fifo->dt_device, 324 "transmit length mismatch error interrupt\n"); 325 326 iowrite32(XLLF_INT_CLEAR_ALL, fifo->base_addr + XLLF_ISR_OFFSET); 327 328 return IRQ_HANDLED; 329} 330 331static int axis_fifo_open(struct inode *inod, struct file *f) 332{ 333 struct axis_fifo *fifo = container_of(f->private_data, 334 struct axis_fifo, miscdev); 335 unsigned int flags = f->f_flags & O_ACCMODE; 336 337 f->private_data = fifo; 338 339 if ((flags == O_WRONLY || flags == O_RDWR) && !fifo->has_tx_fifo) 340 return -EPERM; 341 342 if ((flags == O_RDONLY || flags == O_RDWR) && !fifo->has_rx_fifo) 343 return -EPERM; 344 345 return 0; 346} 347 348static const struct file_operations fops = { 349 .owner = THIS_MODULE, 350 .open = axis_fifo_open, 351 .read = axis_fifo_read, 352 .write = axis_fifo_write, 353 .poll = axis_fifo_poll, 354}; 355 356static int axis_fifo_debugfs_regs_show(struct seq_file *m, void *p) 357{ 358 static const struct axis_fifo_debug_reg regs[] = { 359 {"isr", XLLF_ISR_OFFSET}, 360 {"ier", XLLF_IER_OFFSET}, 361 {"tdfv", XLLF_TDFV_OFFSET}, 362 {"rdfo", XLLF_RDFO_OFFSET}, 363 { /* Sentinel */ }, 364 }; 365 const struct axis_fifo_debug_reg *reg; 366 struct axis_fifo *fifo = m->private; 367 368 for (reg = regs; reg->name; ++reg) { 369 u32 val = ioread32(fifo->base_addr + reg->offset); 370 371 seq_printf(m, "%*s: 0x%08x\n", AXIS_FIFO_DEBUG_REG_NAME_MAX_LEN, 372 reg->name, val); 373 } 374 375 return 0; 376} 377DEFINE_SHOW_ATTRIBUTE(axis_fifo_debugfs_regs); 378 379static void axis_fifo_debugfs_init(struct axis_fifo *fifo) 380{ 381 fifo->debugfs_dir = debugfs_create_dir(dev_name(fifo->dt_device), NULL); 382 383 debugfs_create_file("regs", 0444, fifo->debugfs_dir, fifo, 384 &axis_fifo_debugfs_regs_fops); 385} 386 387static int axis_fifo_parse_dt(struct axis_fifo *fifo) 388{ 389 int ret; 390 unsigned int value; 391 struct device_node *node = fifo->dt_device->of_node; 392 393 ret = of_property_read_u32(node, "xlnx,axi-str-rxd-tdata-width", 394 &value); 395 if (ret) 396 return ret; 397 if (value != 32) 398 return -EINVAL; 399 400 ret = of_property_read_u32(node, "xlnx,axi-str-txd-tdata-width", 401 &value); 402 if (ret) 403 return ret; 404 if (value != 32) 405 return -EINVAL; 406 407 ret = of_property_read_u32(node, "xlnx,rx-fifo-depth", 408 &fifo->rx_fifo_depth); 409 if (ret) 410 return ret; 411 412 ret = of_property_read_u32(node, "xlnx,tx-fifo-depth", 413 &fifo->tx_fifo_depth); 414 if (ret) 415 return ret; 416 417 ret = of_property_read_u32(node, "xlnx,use-rx-data", 418 &fifo->has_rx_fifo); 419 if (ret) 420 return ret; 421 422 ret = of_property_read_u32(node, "xlnx,use-tx-data", 423 &fifo->has_tx_fifo); 424 if (ret) 425 return ret; 426 427 return 0; 428} 429 430static int axis_fifo_probe(struct platform_device *pdev) 431{ 432 struct resource *r_mem; 433 struct device *dev = &pdev->dev; 434 struct axis_fifo *fifo = NULL; 435 int rc = 0; /* error return value */ 436 int irq; 437 438 fifo = devm_kzalloc(dev, sizeof(*fifo), GFP_KERNEL); 439 if (!fifo) 440 return -ENOMEM; 441 442 dev_set_drvdata(dev, fifo); 443 fifo->dt_device = dev; 444 445 init_waitqueue_head(&fifo->read_queue); 446 init_waitqueue_head(&fifo->write_queue); 447 448 mutex_init(&fifo->read_lock); 449 mutex_init(&fifo->write_lock); 450 451 fifo->base_addr = devm_platform_get_and_ioremap_resource(pdev, 0, &r_mem); 452 if (IS_ERR(fifo->base_addr)) 453 return PTR_ERR(fifo->base_addr); 454 455 rc = axis_fifo_parse_dt(fifo); 456 if (rc) 457 return rc; 458 459 reset_ip_core(fifo); 460 461 irq = platform_get_irq(pdev, 0); 462 if (irq < 0) 463 return irq; 464 465 rc = devm_request_irq(fifo->dt_device, irq, &axis_fifo_irq, 0, 466 DRIVER_NAME, fifo); 467 if (rc) { 468 dev_err(fifo->dt_device, "couldn't allocate interrupt %i\n", 469 irq); 470 return rc; 471 } 472 473 fifo->id = ida_alloc(&axis_fifo_ida, GFP_KERNEL); 474 if (fifo->id < 0) 475 return fifo->id; 476 477 fifo->miscdev.fops = &fops; 478 fifo->miscdev.minor = MISC_DYNAMIC_MINOR; 479 fifo->miscdev.parent = dev; 480 fifo->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s%d", 481 DRIVER_NAME, fifo->id); 482 if (!fifo->miscdev.name) { 483 ida_free(&axis_fifo_ida, fifo->id); 484 return -ENOMEM; 485 } 486 487 rc = misc_register(&fifo->miscdev); 488 if (rc < 0) { 489 ida_free(&axis_fifo_ida, fifo->id); 490 return rc; 491 } 492 493 axis_fifo_debugfs_init(fifo); 494 495 return 0; 496} 497 498static void axis_fifo_remove(struct platform_device *pdev) 499{ 500 struct device *dev = &pdev->dev; 501 struct axis_fifo *fifo = dev_get_drvdata(dev); 502 503 debugfs_remove(fifo->debugfs_dir); 504 misc_deregister(&fifo->miscdev); 505 ida_free(&axis_fifo_ida, fifo->id); 506} 507 508static const struct of_device_id axis_fifo_of_match[] = { 509 { .compatible = "xlnx,axi-fifo-mm-s-4.1", }, 510 { .compatible = "xlnx,axi-fifo-mm-s-4.2", }, 511 { .compatible = "xlnx,axi-fifo-mm-s-4.3", }, 512 {}, 513}; 514MODULE_DEVICE_TABLE(of, axis_fifo_of_match); 515 516static struct platform_driver axis_fifo_driver = { 517 .driver = { 518 .name = DRIVER_NAME, 519 .of_match_table = axis_fifo_of_match, 520 }, 521 .probe = axis_fifo_probe, 522 .remove = axis_fifo_remove, 523}; 524 525static int __init axis_fifo_init(void) 526{ 527 return platform_driver_register(&axis_fifo_driver); 528} 529 530module_init(axis_fifo_init); 531 532static void __exit axis_fifo_exit(void) 533{ 534 platform_driver_unregister(&axis_fifo_driver); 535 ida_destroy(&axis_fifo_ida); 536} 537 538module_exit(axis_fifo_exit); 539 540MODULE_LICENSE("GPL"); 541MODULE_AUTHOR("Jacob Feder <jacobsfeder@gmail.com>"); 542MODULE_DESCRIPTION("Xilinx AXI-Stream FIFO IP core driver");