]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/vfio/platform/vfio_platform_irq.c
Merge remote-tracking branch 'vfio/next'
[karo-tx-linux.git] / drivers / vfio / platform / vfio_platform_irq.c
1 /*
2  * VFIO platform devices interrupt handling
3  *
4  * Copyright (C) 2013 - Virtual Open Systems
5  * Author: Antonios Motakis <a.motakis@virtualopensystems.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License, version 2, as
9  * published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  */
16
17 #include <linux/eventfd.h>
18 #include <linux/interrupt.h>
19 #include <linux/slab.h>
20 #include <linux/types.h>
21 #include <linux/vfio.h>
22 #include <linux/irq.h>
23
24 #include "vfio_platform_private.h"
25
26 static void vfio_platform_mask(struct vfio_platform_irq *irq_ctx)
27 {
28         unsigned long flags;
29
30         spin_lock_irqsave(&irq_ctx->lock, flags);
31
32         if (!irq_ctx->masked) {
33                 disable_irq_nosync(irq_ctx->hwirq);
34                 irq_ctx->masked = true;
35         }
36
37         spin_unlock_irqrestore(&irq_ctx->lock, flags);
38 }
39
40 static int vfio_platform_mask_handler(void *opaque, void *unused)
41 {
42         struct vfio_platform_irq *irq_ctx = opaque;
43
44         vfio_platform_mask(irq_ctx);
45
46         return 0;
47 }
48
49 static int vfio_platform_set_irq_mask(struct vfio_platform_device *vdev,
50                                       unsigned index, unsigned start,
51                                       unsigned count, uint32_t flags,
52                                       void *data)
53 {
54         if (start != 0 || count != 1)
55                 return -EINVAL;
56
57         if (!(vdev->irqs[index].flags & VFIO_IRQ_INFO_MASKABLE))
58                 return -EINVAL;
59
60         if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
61                 int32_t fd = *(int32_t *)data;
62
63                 if (fd >= 0)
64                         return vfio_virqfd_enable((void *) &vdev->irqs[index],
65                                                   vfio_platform_mask_handler,
66                                                   NULL, NULL,
67                                                   &vdev->irqs[index].mask, fd);
68
69                 vfio_virqfd_disable(&vdev->irqs[index].mask);
70                 return 0;
71         }
72
73         if (flags & VFIO_IRQ_SET_DATA_NONE) {
74                 vfio_platform_mask(&vdev->irqs[index]);
75
76         } else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
77                 uint8_t mask = *(uint8_t *)data;
78
79                 if (mask)
80                         vfio_platform_mask(&vdev->irqs[index]);
81         }
82
83         return 0;
84 }
85
86 static void vfio_platform_unmask(struct vfio_platform_irq *irq_ctx)
87 {
88         unsigned long flags;
89
90         spin_lock_irqsave(&irq_ctx->lock, flags);
91
92         if (irq_ctx->masked) {
93                 enable_irq(irq_ctx->hwirq);
94                 irq_ctx->masked = false;
95         }
96
97         spin_unlock_irqrestore(&irq_ctx->lock, flags);
98 }
99
100 static int vfio_platform_unmask_handler(void *opaque, void *unused)
101 {
102         struct vfio_platform_irq *irq_ctx = opaque;
103
104         vfio_platform_unmask(irq_ctx);
105
106         return 0;
107 }
108
109 static int vfio_platform_set_irq_unmask(struct vfio_platform_device *vdev,
110                                         unsigned index, unsigned start,
111                                         unsigned count, uint32_t flags,
112                                         void *data)
113 {
114         if (start != 0 || count != 1)
115                 return -EINVAL;
116
117         if (!(vdev->irqs[index].flags & VFIO_IRQ_INFO_MASKABLE))
118                 return -EINVAL;
119
120         if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
121                 int32_t fd = *(int32_t *)data;
122
123                 if (fd >= 0)
124                         return vfio_virqfd_enable((void *) &vdev->irqs[index],
125                                                   vfio_platform_unmask_handler,
126                                                   NULL, NULL,
127                                                   &vdev->irqs[index].unmask,
128                                                   fd);
129
130                 vfio_virqfd_disable(&vdev->irqs[index].unmask);
131                 return 0;
132         }
133
134         if (flags & VFIO_IRQ_SET_DATA_NONE) {
135                 vfio_platform_unmask(&vdev->irqs[index]);
136
137         } else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
138                 uint8_t unmask = *(uint8_t *)data;
139
140                 if (unmask)
141                         vfio_platform_unmask(&vdev->irqs[index]);
142         }
143
144         return 0;
145 }
146
147 static irqreturn_t vfio_automasked_irq_handler(int irq, void *dev_id)
148 {
149         struct vfio_platform_irq *irq_ctx = dev_id;
150         unsigned long flags;
151         int ret = IRQ_NONE;
152
153         spin_lock_irqsave(&irq_ctx->lock, flags);
154
155         if (!irq_ctx->masked) {
156                 ret = IRQ_HANDLED;
157
158                 /* automask maskable interrupts */
159                 disable_irq_nosync(irq_ctx->hwirq);
160                 irq_ctx->masked = true;
161         }
162
163         spin_unlock_irqrestore(&irq_ctx->lock, flags);
164
165         if (ret == IRQ_HANDLED)
166                 eventfd_signal(irq_ctx->trigger, 1);
167
168         return ret;
169 }
170
171 static irqreturn_t vfio_irq_handler(int irq, void *dev_id)
172 {
173         struct vfio_platform_irq *irq_ctx = dev_id;
174
175         eventfd_signal(irq_ctx->trigger, 1);
176
177         return IRQ_HANDLED;
178 }
179
180 static int vfio_set_trigger(struct vfio_platform_device *vdev, int index,
181                             int fd, irq_handler_t handler)
182 {
183         struct vfio_platform_irq *irq = &vdev->irqs[index];
184         struct eventfd_ctx *trigger;
185         int ret;
186
187         if (irq->trigger) {
188                 irq_clear_status_flags(irq->hwirq, IRQ_NOAUTOEN);
189                 free_irq(irq->hwirq, irq);
190                 kfree(irq->name);
191                 eventfd_ctx_put(irq->trigger);
192                 irq->trigger = NULL;
193         }
194
195         if (fd < 0) /* Disable only */
196                 return 0;
197
198         irq->name = kasprintf(GFP_KERNEL, "vfio-irq[%d](%s)",
199                                                 irq->hwirq, vdev->name);
200         if (!irq->name)
201                 return -ENOMEM;
202
203         trigger = eventfd_ctx_fdget(fd);
204         if (IS_ERR(trigger)) {
205                 kfree(irq->name);
206                 return PTR_ERR(trigger);
207         }
208
209         irq->trigger = trigger;
210
211         irq_set_status_flags(irq->hwirq, IRQ_NOAUTOEN);
212         ret = request_irq(irq->hwirq, handler, 0, irq->name, irq);
213         if (ret) {
214                 kfree(irq->name);
215                 eventfd_ctx_put(trigger);
216                 irq->trigger = NULL;
217                 return ret;
218         }
219
220         if (!irq->masked)
221                 enable_irq(irq->hwirq);
222
223         return 0;
224 }
225
226 static int vfio_platform_set_irq_trigger(struct vfio_platform_device *vdev,
227                                          unsigned index, unsigned start,
228                                          unsigned count, uint32_t flags,
229                                          void *data)
230 {
231         struct vfio_platform_irq *irq = &vdev->irqs[index];
232         irq_handler_t handler;
233
234         if (vdev->irqs[index].flags & VFIO_IRQ_INFO_AUTOMASKED)
235                 handler = vfio_automasked_irq_handler;
236         else
237                 handler = vfio_irq_handler;
238
239         if (!count && (flags & VFIO_IRQ_SET_DATA_NONE))
240                 return vfio_set_trigger(vdev, index, -1, handler);
241
242         if (start != 0 || count != 1)
243                 return -EINVAL;
244
245         if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
246                 int32_t fd = *(int32_t *)data;
247
248                 return vfio_set_trigger(vdev, index, fd, handler);
249         }
250
251         if (flags & VFIO_IRQ_SET_DATA_NONE) {
252                 handler(irq->hwirq, irq);
253
254         } else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
255                 uint8_t trigger = *(uint8_t *)data;
256
257                 if (trigger)
258                         handler(irq->hwirq, irq);
259         }
260
261         return 0;
262 }
263
264 int vfio_platform_set_irqs_ioctl(struct vfio_platform_device *vdev,
265                                  uint32_t flags, unsigned index, unsigned start,
266                                  unsigned count, void *data)
267 {
268         int (*func)(struct vfio_platform_device *vdev, unsigned index,
269                     unsigned start, unsigned count, uint32_t flags,
270                     void *data) = NULL;
271
272         switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {
273         case VFIO_IRQ_SET_ACTION_MASK:
274                 func = vfio_platform_set_irq_mask;
275                 break;
276         case VFIO_IRQ_SET_ACTION_UNMASK:
277                 func = vfio_platform_set_irq_unmask;
278                 break;
279         case VFIO_IRQ_SET_ACTION_TRIGGER:
280                 func = vfio_platform_set_irq_trigger;
281                 break;
282         }
283
284         if (!func)
285                 return -ENOTTY;
286
287         return func(vdev, index, start, count, flags, data);
288 }
289
290 int vfio_platform_irq_init(struct vfio_platform_device *vdev)
291 {
292         int cnt = 0, i;
293
294         while (vdev->get_irq(vdev, cnt) >= 0)
295                 cnt++;
296
297         vdev->irqs = kcalloc(cnt, sizeof(struct vfio_platform_irq), GFP_KERNEL);
298         if (!vdev->irqs)
299                 return -ENOMEM;
300
301         for (i = 0; i < cnt; i++) {
302                 int hwirq = vdev->get_irq(vdev, i);
303
304                 if (hwirq < 0)
305                         goto err;
306
307                 spin_lock_init(&vdev->irqs[i].lock);
308
309                 vdev->irqs[i].flags = VFIO_IRQ_INFO_EVENTFD;
310
311                 if (irq_get_trigger_type(hwirq) & IRQ_TYPE_LEVEL_MASK)
312                         vdev->irqs[i].flags |= VFIO_IRQ_INFO_MASKABLE
313                                                 | VFIO_IRQ_INFO_AUTOMASKED;
314
315                 vdev->irqs[i].count = 1;
316                 vdev->irqs[i].hwirq = hwirq;
317                 vdev->irqs[i].masked = false;
318         }
319
320         vdev->num_irqs = cnt;
321
322         return 0;
323 err:
324         kfree(vdev->irqs);
325         return -EINVAL;
326 }
327
328 void vfio_platform_irq_cleanup(struct vfio_platform_device *vdev)
329 {
330         int i;
331
332         for (i = 0; i < vdev->num_irqs; i++)
333                 vfio_set_trigger(vdev, i, -1, NULL);
334
335         vdev->num_irqs = 0;
336         kfree(vdev->irqs);
337 }