Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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");