]> rtime.felk.cvut.cz Git - zynq/linux.git/commitdiff
staging: apf: add preliminary support for accelerator interrupts
authorMichael Gill <michael.gill@xilinx.com>
Fri, 19 Oct 2018 00:54:10 +0000 (17:54 -0700)
committerMichal Simek <michal.simek@xilinx.com>
Wed, 7 Nov 2018 10:06:09 +0000 (11:06 +0100)
This patch introduces support for dynamic allocation of
interrupts to c-callable IP.  This is not full support for the
SDSoC adapter IP interrupts, or general HLS interrupt support.

Signed-off-by: Michael Gill <michael.gill@xilinx.com>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
drivers/staging/apf/xilinx-dma-apf.h
drivers/staging/apf/xlnk-ioctl.h
drivers/staging/apf/xlnk.c
drivers/staging/apf/xlnk.h

index a1fe93c7fb5c96f6d3e0566224f3e202294e94f4..8837fec017793509f6449ef0cf7e03169c0c766f 100644 (file)
@@ -229,5 +229,6 @@ int xdma_getconfig(struct xdma_chan *chan,
 int xdma_setconfig(struct xdma_chan *chan,
                   unsigned char irq_thresh,
                   unsigned char irq_delay);
+unsigned int xlate_irq(unsigned int hwirq);
 
 #endif
index 6cbd277655d8b8d4e1b90e7c440668c9fa49afdb..d909fa65459fba44ecffb218c081e50b5d094a70 100644 (file)
 #define XLNK_IOCMCDMAREGISTER  _IOWR(XLNK_IOC_MAGIC, 23, unsigned long)
 #define XLNK_IOCCACHECTRL      _IOWR(XLNK_IOC_MAGIC, 24, unsigned long)
 
+#define XLNK_IOCIRQREGISTER    _IOWR(XLNK_IOC_MAGIC, 35, unsigned long)
+#define XLNK_IOCIRQUNREGISTER  _IOWR(XLNK_IOC_MAGIC, 36, unsigned long)
+#define XLNK_IOCIRQWAIT                _IOWR(XLNK_IOC_MAGIC, 37, unsigned long)
+
 #define XLNK_IOCSHUTDOWN       _IOWR(XLNK_IOC_MAGIC, 100, unsigned long)
 #define XLNK_IOCRECRES         _IOWR(XLNK_IOC_MAGIC, 101, unsigned long)
 #define XLNK_IOC_MAXNR         101
index c19c5591fbd928287e8f97725b171e60130f19c4..4701898cc5eca830f41620f66e1e6a38e62916c0 100644 (file)
@@ -88,6 +88,10 @@ static size_t xlnk_buflen[XLNK_BUF_POOL_SIZE];
 static unsigned int xlnk_bufcacheable[XLNK_BUF_POOL_SIZE];
 static spinlock_t xlnk_buf_lock;
 
+#define XLNK_IRQ_POOL_SIZE 256
+static struct xlnk_irq_control *xlnk_irq_set[XLNK_IRQ_POOL_SIZE];
+static spinlock_t xlnk_irq_lock;
+
 static int xlnk_open(struct inode *ip, struct file *filp);
 static int xlnk_release(struct inode *ip, struct file *filp);
 static long xlnk_ioctl(struct file *filp, unsigned int code,
@@ -101,6 +105,7 @@ static void xlnk_vma_open(struct vm_area_struct *vma);
 static void xlnk_vma_close(struct vm_area_struct *vma);
 
 static int xlnk_init_bufpool(void);
+static void xlnk_init_irqpool(void);
 
 LIST_HEAD(xlnk_dmabuf_list);
 
@@ -267,7 +272,14 @@ static int xlnk_probe(struct platform_device *pdev)
        device_create(xlnk_class, NULL, MKDEV(driver_major, 0),
                      NULL, "xlnk");
 
-       xlnk_init_bufpool();
+       err = xlnk_init_bufpool();
+       if (err) {
+               dev_err(&pdev->dev, "%s: Failed to allocate buffer pool\n",
+                       __func__);
+               goto err3;
+       }
+
+       xlnk_init_irqpool();
 
        dev_info(&pdev->dev, "%s driver loaded\n", DRIVER_NAME);
 
@@ -388,6 +400,15 @@ static int xlnk_init_bufpool(void)
        return 0;
 }
 
+static void xlnk_init_irqpool(void)
+{
+       int i;
+
+       spin_lock_init(&xlnk_irq_lock);
+       for (i = 0; i < XLNK_IRQ_POOL_SIZE; i++)
+               xlnk_irq_set[i] = NULL;
+}
+
 #define XLNK_SUSPEND NULL
 #define XLNK_RESUME NULL
 
@@ -1084,6 +1105,175 @@ static int xlnk_devunregister_ioctl(struct file *filp,
        return 0;
 }
 
+static irqreturn_t xlnk_accel_isr(int irq, void *arg)
+{
+       struct xlnk_irq_control *irq_control = (struct xlnk_irq_control *)arg;
+
+       disable_irq_nosync(irq);
+       complete(&irq_control->cmp);
+
+       return IRQ_HANDLED;
+}
+
+static int xlnk_irq_register_ioctl(struct file *filp, unsigned int code,
+                                  unsigned long args)
+{
+       union xlnk_args temp_args;
+       int status;
+       int i;
+       struct xlnk_irq_control *ctrl;
+       int irq_id = -1;
+       int irq_entry_new = 0;
+
+       status = copy_from_user(&temp_args,
+                               (void __user *)args,
+                               sizeof(temp_args.irqregister));
+       if (status)
+               return -ENOMEM;
+
+       if (temp_args.irqregister.type !=
+           (XLNK_IRQ_LEVEL | XLNK_IRQ_ACTIVE_HIGH)) {
+               dev_err(xlnk_dev, "Unsupported interrupt type %x\n",
+                       temp_args.irqregister.type);
+               return -EINVAL;
+       }
+
+       ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
+       if (!ctrl)
+               return -ENOMEM;
+
+       ctrl->irq = xlate_irq(temp_args.irqregister.irq);
+       ctrl->enabled = 0;
+       init_completion(&ctrl->cmp);
+
+       spin_lock(&xlnk_irq_lock);
+       for (i = 0; i < XLNK_IRQ_POOL_SIZE; i++) {
+               if (!xlnk_irq_set[i] && irq_id == -1) {
+                       irq_entry_new = 1;
+                       irq_id = i;
+                       xlnk_irq_set[i] = ctrl;
+               } else if (xlnk_irq_set[i] &&
+                          xlnk_irq_set[i]->irq == ctrl->irq) {
+                       irq_id = i;
+                       break;
+               }
+       }
+       spin_unlock(&xlnk_irq_lock);
+
+       if (irq_id == -1) {
+               kfree(ctrl);
+               return -ENOMEM;
+       }
+
+       if (!irq_entry_new) {
+               kfree(ctrl);
+       } else {
+               status = request_irq(ctrl->irq,
+                                    xlnk_accel_isr,
+                                    IRQF_SHARED,
+                                    "xlnk",
+                                    ctrl);
+               if (status) {
+                       enable_irq(ctrl->irq);
+                       xlnk_irq_set[irq_id] = NULL;
+                       kfree(ctrl);
+                       return -EINVAL;
+               }
+               disable_irq_nosync(ctrl->irq);
+       }
+
+       temp_args.irqregister.irq_id = irq_id;
+
+       status = copy_to_user((void __user *)args,
+                             &temp_args,
+                             sizeof(temp_args.irqregister));
+
+       return status;
+}
+
+static int xlnk_irq_unregister_ioctl(struct file *filp, unsigned int code,
+                                    unsigned long args)
+{
+       union xlnk_args temp_args;
+       int status;
+       int irq_id;
+       struct xlnk_irq_control *ctrl;
+
+       status = copy_from_user(&temp_args,
+                               (void __user *)args,
+                               sizeof(union xlnk_args));
+       if (status)
+               return -ENOMEM;
+
+       irq_id = temp_args.irqunregister.irq_id;
+       if (irq_id < 0 || irq_id >= XLNK_IRQ_POOL_SIZE)
+               return -EINVAL;
+
+       ctrl = xlnk_irq_set[irq_id];
+       if (!ctrl)
+               return -EINVAL;
+
+       xlnk_irq_set[irq_id] = NULL;
+
+       if (ctrl->enabled) {
+               disable_irq_nosync(ctrl->irq);
+               complete(&ctrl->cmp);
+       }
+       free_irq(ctrl->irq, ctrl);
+       kfree(ctrl);
+
+       return 0;
+}
+
+static int xlnk_irq_wait_ioctl(struct file *filp, unsigned int code,
+                              unsigned long args)
+{
+       union xlnk_args temp_args;
+       int status;
+       int irq_id;
+       struct xlnk_irq_control *ctrl;
+
+       status = copy_from_user(&temp_args,
+                               (void __user *)args,
+                               sizeof(temp_args.irqwait));
+       if (status)
+               return -ENOMEM;
+
+       irq_id = temp_args.irqwait.irq_id;
+       if (irq_id < 0 || irq_id >= XLNK_IRQ_POOL_SIZE)
+               return -EINVAL;
+
+       ctrl = xlnk_irq_set[irq_id];
+       if (!ctrl)
+               return -EINVAL;
+
+       if (!ctrl->enabled) {
+               ctrl->enabled = 1;
+               enable_irq(ctrl->irq);
+       }
+
+       if (temp_args.irqwait.polling) {
+               if (!try_wait_for_completion(&ctrl->cmp))
+                       temp_args.irqwait.success = 0;
+               else
+                       temp_args.irqwait.success = 1;
+       } else {
+               wait_for_completion(&ctrl->cmp);
+               temp_args.irqwait.success = 1;
+       }
+
+       if (temp_args.irqwait.success) {
+               reinit_completion(&ctrl->cmp);
+               ctrl->enabled = 0;
+       }
+
+       status = copy_to_user((void __user *)args,
+                             &temp_args,
+                             sizeof(temp_args.irqwait));
+
+       return status;
+}
+
 static int xlnk_cachecontrol_ioctl(struct file *filp, unsigned int code,
                                   unsigned long args)
 {
@@ -1298,6 +1488,12 @@ static long xlnk_ioctl(struct file *filp,
                return xlnk_devunregister_ioctl(filp, code, args);
        case XLNK_IOCCACHECTRL:
                return xlnk_cachecontrol_ioctl(filp, code, args);
+       case XLNK_IOCIRQREGISTER:
+               return xlnk_irq_register_ioctl(filp, code, args);
+       case XLNK_IOCIRQUNREGISTER:
+               return xlnk_irq_unregister_ioctl(filp, code, args);
+       case XLNK_IOCIRQWAIT:
+               return xlnk_irq_wait_ioctl(filp, code, args);
        case XLNK_IOCSHUTDOWN:
                return xlnk_shutdown(args);
        case XLNK_IOCRECRES:
index 9d0b79b703ad2633c0acefa01e4147cc9fea4ded..cbc2334c2e826aaf9221220353577d96d924c1e2 100644 (file)
 #define CF_FLAG_CACHE_FLUSH_INVALIDATE 0x00000001
 #define CF_FLAG_PHYSICALLY_CONTIGUOUS  0x00000002
 #define CF_FLAG_DMAPOLLING             0x00000004
+#define XLNK_IRQ_LEVEL                 0x00000001
+#define XLNK_IRQ_EDGE                  0x00000002
+#define XLNK_IRQ_ACTIVE_HIGH           0x00000004
+#define XLNK_IRQ_ACTIVE_LOW            0x00000008
+#define XLNK_IRQ_RESET_REG_VALID       0x00000010
 
 enum xlnk_dma_direction {
        XLNK_DMA_BI = 0,
@@ -50,6 +55,12 @@ struct xlnk_dmabuf_reg {
        struct list_head list;
 };
 
+struct xlnk_irq_control {
+       int irq;
+       int enabled;
+       struct completion cmp;
+};
+
 /* CROSSES KERNEL-USER BOUNDARY */
 union xlnk_args {
        struct __attribute__ ((__packed__)) {
@@ -140,6 +151,25 @@ union xlnk_args {
                xlnk_intptr_type phys_addr;
                xlnk_intptr_type token;
        } memop;
+       struct __attribute__ ((__packed__)) {
+               xlnk_int_type irq;
+               xlnk_int_type subirq;
+               xlnk_uint_type type;
+               xlnk_intptr_type control_base;
+               xlnk_intptr_type reset_reg_base;
+               xlnk_uint_type reset_offset;
+               xlnk_uint_type reset_valid_high;
+               xlnk_uint_type reset_valid_low;
+               xlnk_int_type irq_id;
+       } irqregister;
+       struct __attribute__ ((__packed__)) {
+               xlnk_int_type irq_id;
+       } irqunregister;
+       struct __attribute__ ((__packed__)) {
+               xlnk_int_type irq_id;
+               xlnk_int_type polling;
+               xlnk_int_type success;
+       } irqwait;
 };
 
 #endif