summaryrefslogtreecommitdiff
path: root/drivers/staging/axis-fifo/axis-fifo.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/axis-fifo/axis-fifo.c')
-rw-r--r--drivers/staging/axis-fifo/axis-fifo.c294
1 files changed, 77 insertions, 217 deletions
diff --git a/drivers/staging/axis-fifo/axis-fifo.c b/drivers/staging/axis-fifo/axis-fifo.c
index 509d620d6ce7..aa90b27197cf 100644
--- a/drivers/staging/axis-fifo/axis-fifo.c
+++ b/drivers/staging/axis-fifo/axis-fifo.c
@@ -9,11 +9,6 @@
* See Xilinx PG080 document for IP details
*/
-/* ----------------------------
- * includes
- * ----------------------------
- */
-
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@@ -25,20 +20,14 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/io.h>
-#include <linux/moduleparam.h>
#include <linux/interrupt.h>
-#include <linux/param.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/jiffies.h>
#include <linux/miscdevice.h>
#include <linux/debugfs.h>
-
-/* ----------------------------
- * driver parameters
- * ----------------------------
- */
+#include <linux/poll.h>
#define DRIVER_NAME "axis_fifo"
@@ -46,90 +35,51 @@
#define AXIS_FIFO_DEBUG_REG_NAME_MAX_LEN 4
-/* ----------------------------
- * IP register offsets
- * ----------------------------
- */
-
-#define XLLF_ISR_OFFSET 0x00000000 /* Interrupt Status */
-#define XLLF_IER_OFFSET 0x00000004 /* Interrupt Enable */
-
-#define XLLF_TDFR_OFFSET 0x00000008 /* Transmit Reset */
-#define XLLF_TDFV_OFFSET 0x0000000c /* Transmit Vacancy */
-#define XLLF_TDFD_OFFSET 0x00000010 /* Transmit Data */
-#define XLLF_TLR_OFFSET 0x00000014 /* Transmit Length */
-
-#define XLLF_RDFR_OFFSET 0x00000018 /* Receive Reset */
-#define XLLF_RDFO_OFFSET 0x0000001c /* Receive Occupancy */
-#define XLLF_RDFD_OFFSET 0x00000020 /* Receive Data */
-#define XLLF_RLR_OFFSET 0x00000024 /* Receive Length */
-#define XLLF_SRR_OFFSET 0x00000028 /* Local Link Reset */
-#define XLLF_TDR_OFFSET 0x0000002C /* Transmit Destination */
-#define XLLF_RDR_OFFSET 0x00000030 /* Receive Destination */
-
-/* ----------------------------
- * reset register masks
- * ----------------------------
- */
-
-#define XLLF_RDFR_RESET_MASK 0x000000a5 /* receive reset value */
-#define XLLF_TDFR_RESET_MASK 0x000000a5 /* Transmit reset value */
-#define XLLF_SRR_RESET_MASK 0x000000a5 /* Local Link reset value */
-
-/* ----------------------------
- * interrupt masks
- * ----------------------------
- */
-
-#define XLLF_INT_RPURE_MASK 0x80000000 /* Receive under-read */
-#define XLLF_INT_RPORE_MASK 0x40000000 /* Receive over-read */
-#define XLLF_INT_RPUE_MASK 0x20000000 /* Receive underrun (empty) */
-#define XLLF_INT_TPOE_MASK 0x10000000 /* Transmit overrun */
-#define XLLF_INT_TC_MASK 0x08000000 /* Transmit complete */
-#define XLLF_INT_RC_MASK 0x04000000 /* Receive complete */
-#define XLLF_INT_TSE_MASK 0x02000000 /* Transmit length mismatch */
+#define XLLF_ISR_OFFSET 0x00 /* Interrupt Status */
+#define XLLF_IER_OFFSET 0x04 /* Interrupt Enable */
+#define XLLF_TDFR_OFFSET 0x08 /* Transmit Reset */
+#define XLLF_TDFV_OFFSET 0x0c /* Transmit Vacancy */
+#define XLLF_TDFD_OFFSET 0x10 /* Transmit Data */
+#define XLLF_TLR_OFFSET 0x14 /* Transmit Length */
+#define XLLF_RDFR_OFFSET 0x18 /* Receive Reset */
+#define XLLF_RDFO_OFFSET 0x1c /* Receive Occupancy */
+#define XLLF_RDFD_OFFSET 0x20 /* Receive Data */
+#define XLLF_RLR_OFFSET 0x24 /* Receive Length */
+#define XLLF_SRR_OFFSET 0x28 /* Local Link Reset */
+#define XLLF_TDR_OFFSET 0x2C /* Transmit Destination */
+#define XLLF_RDR_OFFSET 0x30 /* Receive Destination */
+
+#define XLLF_RDFR_RESET_MASK 0xa5 /* Receive reset value */
+#define XLLF_TDFR_RESET_MASK 0xa5 /* Transmit reset value */
+#define XLLF_SRR_RESET_MASK 0xa5 /* Local Link reset value */
+
+#define XLLF_INT_RPURE_MASK BIT(31) /* Receive under-read */
+#define XLLF_INT_RPORE_MASK BIT(30) /* Receive over-read */
+#define XLLF_INT_RPUE_MASK BIT(29) /* Receive underrun (empty) */
+#define XLLF_INT_TPOE_MASK BIT(28) /* Transmit overrun */
+#define XLLF_INT_TC_MASK BIT(27) /* Transmit complete */
+#define XLLF_INT_RC_MASK BIT(26) /* Receive complete */
+#define XLLF_INT_TSE_MASK BIT(25) /* Transmit length mismatch */
#define XLLF_INT_CLEAR_ALL GENMASK(31, 0)
-/* ----------------------------
- * globals
- * ----------------------------
- */
-static long read_timeout = 1000; /* ms to wait before read() times out */
-static long write_timeout = 1000; /* ms to wait before write() times out */
-
static DEFINE_IDA(axis_fifo_ida);
-/* ----------------------------
- * module command-line arguments
- * ----------------------------
- */
-
-module_param(read_timeout, long, 0444);
-MODULE_PARM_DESC(read_timeout, "ms to wait before blocking read() timing out; set to -1 for no timeout");
-module_param(write_timeout, long, 0444);
-MODULE_PARM_DESC(write_timeout, "ms to wait before blocking write() timing out; set to -1 for no timeout");
-
-/* ----------------------------
- * types
- * ----------------------------
- */
-
struct axis_fifo {
int id;
- void __iomem *base_addr; /* kernel space memory */
+ void __iomem *base_addr;
- unsigned int rx_fifo_depth; /* max words in the receive fifo */
- unsigned int tx_fifo_depth; /* max words in the transmit fifo */
- int has_rx_fifo; /* whether the IP has the rx fifo enabled */
- int has_tx_fifo; /* whether the IP has the tx fifo enabled */
+ unsigned int rx_fifo_depth;
+ unsigned int tx_fifo_depth;
+ int has_rx_fifo;
+ int has_tx_fifo;
- wait_queue_head_t read_queue; /* wait queue for asynchronos read */
+ wait_queue_head_t read_queue;
struct mutex read_lock; /* lock for reading */
- wait_queue_head_t write_queue; /* wait queue for asynchronos write */
+ wait_queue_head_t write_queue;
struct mutex write_lock; /* lock for writing */
- struct device *dt_device; /* device created from the device tree */
+ struct device *dt_device;
struct miscdevice miscdev;
struct dentry *debugfs_dir;
@@ -140,11 +90,6 @@ struct axis_fifo_debug_reg {
unsigned int offset;
};
-/* ----------------------------
- * implementation
- * ----------------------------
- */
-
static void reset_ip_core(struct axis_fifo *fifo)
{
iowrite32(XLLF_SRR_RESET_MASK, fifo->base_addr + XLLF_SRR_OFFSET);
@@ -175,7 +120,7 @@ static void reset_ip_core(struct axis_fifo *fifo)
static ssize_t axis_fifo_read(struct file *f, char __user *buf,
size_t len, loff_t *off)
{
- struct axis_fifo *fifo = (struct axis_fifo *)f->private_data;
+ struct axis_fifo *fifo = f->private_data;
size_t bytes_available;
unsigned int words_available;
unsigned int copied;
@@ -185,10 +130,6 @@ static ssize_t axis_fifo_read(struct file *f, char __user *buf,
u32 tmp_buf[READ_BUF_SIZE];
if (f->f_flags & O_NONBLOCK) {
- /*
- * Device opened in non-blocking mode. Try to lock it and then
- * check if any packet is available.
- */
if (!mutex_trylock(&fifo->read_lock))
return -EAGAIN;
@@ -197,38 +138,18 @@ static ssize_t axis_fifo_read(struct file *f, char __user *buf,
goto end_unlock;
}
} else {
- /* opened in blocking mode
- * wait for a packet available interrupt (or timeout)
- * if nothing is currently available
- */
mutex_lock(&fifo->read_lock);
- ret = wait_event_interruptible_timeout(fifo->read_queue,
- ioread32(fifo->base_addr + XLLF_RDFO_OFFSET),
- read_timeout);
-
- if (ret <= 0) {
- if (ret == 0) {
- ret = -EAGAIN;
- } else if (ret != -ERESTARTSYS) {
- dev_err(fifo->dt_device, "wait_event_interruptible_timeout() error in read (ret=%i)\n",
- ret);
- }
+ ret = wait_event_interruptible(fifo->read_queue,
+ ioread32(fifo->base_addr + XLLF_RDFO_OFFSET));
+ if (ret)
goto end_unlock;
- }
}
bytes_available = ioread32(fifo->base_addr + XLLF_RLR_OFFSET);
words_available = bytes_available / sizeof(u32);
- if (!bytes_available) {
- dev_err(fifo->dt_device, "received a packet of length 0\n");
- ret = -EIO;
- goto end_unlock;
- }
if (bytes_available > len) {
- dev_err(fifo->dt_device, "user read buffer too small (available bytes=%zu user buffer bytes=%zu)\n",
- bytes_available, len);
ret = -EINVAL;
goto err_flush_rx;
}
@@ -242,9 +163,6 @@ static ssize_t axis_fifo_read(struct file *f, char __user *buf,
goto err_flush_rx;
}
- /* read data into an intermediate buffer, copying the contents
- * to userspace when the buffer is full
- */
copied = 0;
while (words_available > 0) {
copy = min(words_available, READ_BUF_SIZE);
@@ -295,25 +213,13 @@ end_unlock:
static ssize_t axis_fifo_write(struct file *f, const char __user *buf,
size_t len, loff_t *off)
{
- struct axis_fifo *fifo = (struct axis_fifo *)f->private_data;
+ struct axis_fifo *fifo = f->private_data;
unsigned int words_to_write;
u32 *txbuf;
int ret;
- if (len % sizeof(u32)) {
- dev_err(fifo->dt_device,
- "tried to send a packet that isn't word-aligned\n");
- return -EINVAL;
- }
-
words_to_write = len / sizeof(u32);
- if (!words_to_write) {
- dev_err(fifo->dt_device,
- "tried to send a packet of length 0\n");
- return -EINVAL;
- }
-
/*
* In 'Store-and-Forward' mode, the maximum packet that can be
* transmitted is limited by the size of the FIFO, which is
@@ -323,14 +229,11 @@ static ssize_t axis_fifo_write(struct file *f, const char __user *buf,
* otherwise a 'Transmit Packet Overrun Error' interrupt will be
* raised, which requires a reset of the TX circuit to recover.
*/
- if (words_to_write > (fifo->tx_fifo_depth - 4))
+ if (!words_to_write || (len % sizeof(u32)) ||
+ (words_to_write > (fifo->tx_fifo_depth - 4)))
return -EINVAL;
if (f->f_flags & O_NONBLOCK) {
- /*
- * Device opened in non-blocking mode. Try to lock it and then
- * check if there is any room to write the given buffer.
- */
if (!mutex_trylock(&fifo->write_lock))
return -EAGAIN;
@@ -340,27 +243,12 @@ static ssize_t axis_fifo_write(struct file *f, const char __user *buf,
goto end_unlock;
}
} else {
- /* opened in blocking mode */
-
- /* wait for an interrupt (or timeout) if there isn't
- * currently enough room in the fifo
- */
mutex_lock(&fifo->write_lock);
- ret = wait_event_interruptible_timeout(fifo->write_queue,
- ioread32(fifo->base_addr + XLLF_TDFV_OFFSET)
- >= words_to_write,
- write_timeout);
-
- if (ret <= 0) {
- if (ret == 0) {
- ret = -EAGAIN;
- } else if (ret != -ERESTARTSYS) {
- dev_err(fifo->dt_device, "wait_event_interruptible_timeout() error in write (ret=%i)\n",
- ret);
- }
+ ret = wait_event_interruptible(fifo->write_queue,
+ ioread32(fifo->base_addr + XLLF_TDFV_OFFSET) >= words_to_write);
+ if (ret)
goto end_unlock;
- }
}
txbuf = vmemdup_user(buf, len);
@@ -372,7 +260,6 @@ static ssize_t axis_fifo_write(struct file *f, const char __user *buf,
for (int i = 0; i < words_to_write; ++i)
iowrite32(txbuf[i], fifo->base_addr + XLLF_TDFD_OFFSET);
- /* write packet size to fifo */
iowrite32(len, fifo->base_addr + XLLF_TLR_OFFSET);
ret = len;
@@ -383,6 +270,28 @@ end_unlock:
return ret;
}
+static __poll_t axis_fifo_poll(struct file *f, poll_table *wait)
+{
+ struct axis_fifo *fifo = f->private_data;
+ __poll_t mask = 0;
+
+ if (fifo->has_rx_fifo) {
+ poll_wait(f, &fifo->read_queue, wait);
+
+ if (ioread32(fifo->base_addr + XLLF_RDFO_OFFSET))
+ mask |= EPOLLIN | EPOLLRDNORM;
+ }
+
+ if (fifo->has_tx_fifo) {
+ poll_wait(f, &fifo->write_queue, wait);
+
+ if (ioread32(fifo->base_addr + XLLF_TDFV_OFFSET))
+ mask |= EPOLLOUT | EPOLLWRNORM;
+ }
+
+ return mask;
+}
+
static irqreturn_t axis_fifo_irq(int irq, void *dw)
{
struct axis_fifo *fifo = dw;
@@ -436,19 +345,12 @@ static int axis_fifo_open(struct inode *inod, struct file *f)
return 0;
}
-static int axis_fifo_close(struct inode *inod, struct file *f)
-{
- f->private_data = NULL;
-
- return 0;
-}
-
static const struct file_operations fops = {
.owner = THIS_MODULE,
.open = axis_fifo_open,
- .release = axis_fifo_close,
.read = axis_fifo_read,
- .write = axis_fifo_write
+ .write = axis_fifo_write,
+ .poll = axis_fifo_poll,
};
static int axis_fifo_debugfs_regs_show(struct seq_file *m, void *p)
@@ -548,23 +450,12 @@ end:
static int axis_fifo_probe(struct platform_device *pdev)
{
- struct resource *r_mem; /* IO mem resources */
- struct device *dev = &pdev->dev; /* OS device (from device tree) */
+ struct resource *r_mem;
+ struct device *dev = &pdev->dev;
struct axis_fifo *fifo = NULL;
- char *device_name;
int rc = 0; /* error return value */
int irq;
- /* ----------------------------
- * init wrapper device
- * ----------------------------
- */
-
- device_name = devm_kzalloc(dev, 32, GFP_KERNEL);
- if (!device_name)
- return -ENOMEM;
-
- /* allocate device wrapper memory */
fifo = devm_kzalloc(dev, sizeof(*fifo), GFP_KERNEL);
if (!fifo)
return -ENOMEM;
@@ -578,38 +469,20 @@ static int axis_fifo_probe(struct platform_device *pdev)
mutex_init(&fifo->read_lock);
mutex_init(&fifo->write_lock);
- /* ----------------------------
- * init device memory space
- * ----------------------------
- */
-
- /* get iospace for the device and request physical memory */
fifo->base_addr = devm_platform_get_and_ioremap_resource(pdev, 0, &r_mem);
if (IS_ERR(fifo->base_addr))
return PTR_ERR(fifo->base_addr);
- /* ----------------------------
- * init IP
- * ----------------------------
- */
-
rc = axis_fifo_parse_dt(fifo);
if (rc)
return rc;
reset_ip_core(fifo);
- /* ----------------------------
- * init device interrupts
- * ----------------------------
- */
-
- /* get IRQ resource */
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
- /* request IRQ */
rc = devm_request_irq(fifo->dt_device, irq, &axis_fifo_irq, 0,
DRIVER_NAME, fifo);
if (rc) {
@@ -618,21 +491,20 @@ static int axis_fifo_probe(struct platform_device *pdev)
return rc;
}
- /* ----------------------------
- * init char device
- * ----------------------------
- */
fifo->id = ida_alloc(&axis_fifo_ida, GFP_KERNEL);
if (fifo->id < 0)
return fifo->id;
- snprintf(device_name, 32, "%s%d", DRIVER_NAME, fifo->id);
-
- /* create character device */
fifo->miscdev.fops = &fops;
fifo->miscdev.minor = MISC_DYNAMIC_MINOR;
- fifo->miscdev.name = device_name;
fifo->miscdev.parent = dev;
+ fifo->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s%d",
+ DRIVER_NAME, fifo->id);
+ if (!fifo->miscdev.name) {
+ ida_free(&axis_fifo_ida, fifo->id);
+ return -ENOMEM;
+ }
+
rc = misc_register(&fifo->miscdev);
if (rc < 0) {
ida_free(&axis_fifo_ida, fifo->id);
@@ -673,18 +545,6 @@ static struct platform_driver axis_fifo_driver = {
static int __init axis_fifo_init(void)
{
- if (read_timeout >= 0)
- read_timeout = msecs_to_jiffies(read_timeout);
- else
- read_timeout = MAX_SCHEDULE_TIMEOUT;
-
- if (write_timeout >= 0)
- write_timeout = msecs_to_jiffies(write_timeout);
- else
- write_timeout = MAX_SCHEDULE_TIMEOUT;
-
- pr_info("axis-fifo driver loaded with parameters read_timeout = %li, write_timeout = %li\n",
- read_timeout, write_timeout);
return platform_driver_register(&axis_fifo_driver);
}