Merge remote-tracking branch 'net/master'
[linux-2.6-microblaze.git] / drivers / vfio / fsl-mc / vfio_fsl_mc_intr.c
1 // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
2 /*
3  * Copyright 2013-2016 Freescale Semiconductor Inc.
4  * Copyright 2019 NXP
5  */
6
7 #include <linux/vfio.h>
8 #include <linux/slab.h>
9 #include <linux/types.h>
10 #include <linux/eventfd.h>
11 #include <linux/msi.h>
12
13 #include "linux/fsl/mc.h"
14 #include "vfio_fsl_mc_private.h"
15
16 static int vfio_fsl_mc_irqs_allocate(struct vfio_fsl_mc_device *vdev)
17 {
18         struct fsl_mc_device *mc_dev = vdev->mc_dev;
19         struct vfio_fsl_mc_irq *mc_irq;
20         int irq_count;
21         int ret, i;
22
23         /* Device does not support any interrupt */
24         if (mc_dev->obj_desc.irq_count == 0)
25                 return 0;
26
27         /* interrupts were already allocated for this device */
28         if (vdev->mc_irqs)
29                 return 0;
30
31         irq_count = mc_dev->obj_desc.irq_count;
32
33         mc_irq = kcalloc(irq_count, sizeof(*mc_irq), GFP_KERNEL);
34         if (!mc_irq)
35                 return -ENOMEM;
36
37         /* Allocate IRQs */
38         ret = fsl_mc_allocate_irqs(mc_dev);
39         if (ret) {
40                 kfree(mc_irq);
41                 return ret;
42         }
43
44         for (i = 0; i < irq_count; i++) {
45                 mc_irq[i].count = 1;
46                 mc_irq[i].flags = VFIO_IRQ_INFO_EVENTFD;
47         }
48
49         vdev->mc_irqs = mc_irq;
50
51         return 0;
52 }
53
54 static irqreturn_t vfio_fsl_mc_irq_handler(int irq_num, void *arg)
55 {
56         struct vfio_fsl_mc_irq *mc_irq = (struct vfio_fsl_mc_irq *)arg;
57
58         eventfd_signal(mc_irq->trigger, 1);
59         return IRQ_HANDLED;
60 }
61
62 static int vfio_set_trigger(struct vfio_fsl_mc_device *vdev,
63                                                    int index, int fd)
64 {
65         struct vfio_fsl_mc_irq *irq = &vdev->mc_irqs[index];
66         struct eventfd_ctx *trigger;
67         int hwirq;
68         int ret;
69
70         hwirq = vdev->mc_dev->irqs[index]->msi_desc->irq;
71         if (irq->trigger) {
72                 free_irq(hwirq, irq);
73                 kfree(irq->name);
74                 eventfd_ctx_put(irq->trigger);
75                 irq->trigger = NULL;
76         }
77
78         if (fd < 0) /* Disable only */
79                 return 0;
80
81         irq->name = kasprintf(GFP_KERNEL, "vfio-irq[%d](%s)",
82                             hwirq, dev_name(&vdev->mc_dev->dev));
83         if (!irq->name)
84                 return -ENOMEM;
85
86         trigger = eventfd_ctx_fdget(fd);
87         if (IS_ERR(trigger)) {
88                 kfree(irq->name);
89                 return PTR_ERR(trigger);
90         }
91
92         irq->trigger = trigger;
93
94         ret = request_irq(hwirq, vfio_fsl_mc_irq_handler, 0,
95                   irq->name, irq);
96         if (ret) {
97                 kfree(irq->name);
98                 eventfd_ctx_put(trigger);
99                 irq->trigger = NULL;
100                 return ret;
101         }
102
103         return 0;
104 }
105
106 static int vfio_fsl_mc_set_irq_trigger(struct vfio_fsl_mc_device *vdev,
107                                        unsigned int index, unsigned int start,
108                                        unsigned int count, u32 flags,
109                                        void *data)
110 {
111         struct fsl_mc_device *mc_dev = vdev->mc_dev;
112         int ret, hwirq;
113         struct vfio_fsl_mc_irq *irq;
114         struct device *cont_dev = fsl_mc_cont_dev(&mc_dev->dev);
115         struct fsl_mc_device *mc_cont = to_fsl_mc_device(cont_dev);
116
117         if (!count && (flags & VFIO_IRQ_SET_DATA_NONE))
118                 return vfio_set_trigger(vdev, index, -1);
119
120         if (start != 0 || count != 1)
121                 return -EINVAL;
122
123         mutex_lock(&vdev->reflck->lock);
124         ret = fsl_mc_populate_irq_pool(mc_cont,
125                         FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS);
126         if (ret)
127                 goto unlock;
128
129         ret = vfio_fsl_mc_irqs_allocate(vdev);
130         if (ret)
131                 goto unlock;
132         mutex_unlock(&vdev->reflck->lock);
133
134         if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
135                 s32 fd = *(s32 *)data;
136
137                 return vfio_set_trigger(vdev, index, fd);
138         }
139
140         hwirq = vdev->mc_dev->irqs[index]->msi_desc->irq;
141
142         irq = &vdev->mc_irqs[index];
143
144         if (flags & VFIO_IRQ_SET_DATA_NONE) {
145                 vfio_fsl_mc_irq_handler(hwirq, irq);
146
147         } else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
148                 u8 trigger = *(u8 *)data;
149
150                 if (trigger)
151                         vfio_fsl_mc_irq_handler(hwirq, irq);
152         }
153
154         return 0;
155
156 unlock:
157         mutex_unlock(&vdev->reflck->lock);
158         return ret;
159
160 }
161
162 int vfio_fsl_mc_set_irqs_ioctl(struct vfio_fsl_mc_device *vdev,
163                                u32 flags, unsigned int index,
164                                unsigned int start, unsigned int count,
165                                void *data)
166 {
167         if (flags & VFIO_IRQ_SET_ACTION_TRIGGER)
168                 return  vfio_fsl_mc_set_irq_trigger(vdev, index, start,
169                           count, flags, data);
170         else
171                 return -EINVAL;
172 }
173
174 /* Free All IRQs for the given MC object */
175 void vfio_fsl_mc_irqs_cleanup(struct vfio_fsl_mc_device *vdev)
176 {
177         struct fsl_mc_device *mc_dev = vdev->mc_dev;
178         int irq_count = mc_dev->obj_desc.irq_count;
179         int i;
180
181         /*
182          * Device does not support any interrupt or the interrupts
183          * were not configured
184          */
185         if (!vdev->mc_irqs)
186                 return;
187
188         for (i = 0; i < irq_count; i++)
189                 vfio_set_trigger(vdev, i, -1);
190
191         fsl_mc_free_irqs(mc_dev);
192         kfree(vdev->mc_irqs);
193         vdev->mc_irqs = NULL;
194 }