diff options
Diffstat (limited to 'drivers/staging/axis-fifo/axis-fifo.c')
| -rw-r--r-- | drivers/staging/axis-fifo/axis-fifo.c | 294 |
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); } |
