1 // SPDX-License-Identifier: GPL-2.0-only
3 * VFIO platform devices interrupt handling
5 * Copyright (C) 2013 - Virtual Open Systems
6 * Author: Antonios Motakis <a.motakis@virtualopensystems.com>
9 #include <linux/eventfd.h>
10 #include <linux/interrupt.h>
11 #include <linux/slab.h>
12 #include <linux/types.h>
13 #include <linux/vfio.h>
14 #include <linux/irq.h>
16 #include "vfio_platform_private.h"
18 static void vfio_platform_mask(struct vfio_platform_irq
*irq_ctx
)
22 spin_lock_irqsave(&irq_ctx
->lock
, flags
);
24 if (!irq_ctx
->masked
) {
25 disable_irq_nosync(irq_ctx
->hwirq
);
26 irq_ctx
->masked
= true;
29 spin_unlock_irqrestore(&irq_ctx
->lock
, flags
);
32 static int vfio_platform_mask_handler(void *opaque
, void *unused
)
34 struct vfio_platform_irq
*irq_ctx
= opaque
;
36 vfio_platform_mask(irq_ctx
);
41 static int vfio_platform_set_irq_mask(struct vfio_platform_device
*vdev
,
42 unsigned index
, unsigned start
,
43 unsigned count
, uint32_t flags
,
46 if (start
!= 0 || count
!= 1)
49 if (!(vdev
->irqs
[index
].flags
& VFIO_IRQ_INFO_MASKABLE
))
52 if (flags
& VFIO_IRQ_SET_DATA_EVENTFD
) {
53 int32_t fd
= *(int32_t *)data
;
56 return vfio_virqfd_enable((void *) &vdev
->irqs
[index
],
57 vfio_platform_mask_handler
,
59 &vdev
->irqs
[index
].mask
, fd
);
61 vfio_virqfd_disable(&vdev
->irqs
[index
].mask
);
65 if (flags
& VFIO_IRQ_SET_DATA_NONE
) {
66 vfio_platform_mask(&vdev
->irqs
[index
]);
68 } else if (flags
& VFIO_IRQ_SET_DATA_BOOL
) {
69 uint8_t mask
= *(uint8_t *)data
;
72 vfio_platform_mask(&vdev
->irqs
[index
]);
78 static void vfio_platform_unmask(struct vfio_platform_irq
*irq_ctx
)
82 spin_lock_irqsave(&irq_ctx
->lock
, flags
);
84 if (irq_ctx
->masked
) {
85 enable_irq(irq_ctx
->hwirq
);
86 irq_ctx
->masked
= false;
89 spin_unlock_irqrestore(&irq_ctx
->lock
, flags
);
92 static int vfio_platform_unmask_handler(void *opaque
, void *unused
)
94 struct vfio_platform_irq
*irq_ctx
= opaque
;
96 vfio_platform_unmask(irq_ctx
);
101 static int vfio_platform_set_irq_unmask(struct vfio_platform_device
*vdev
,
102 unsigned index
, unsigned start
,
103 unsigned count
, uint32_t flags
,
106 if (start
!= 0 || count
!= 1)
109 if (!(vdev
->irqs
[index
].flags
& VFIO_IRQ_INFO_MASKABLE
))
112 if (flags
& VFIO_IRQ_SET_DATA_EVENTFD
) {
113 int32_t fd
= *(int32_t *)data
;
116 return vfio_virqfd_enable((void *) &vdev
->irqs
[index
],
117 vfio_platform_unmask_handler
,
119 &vdev
->irqs
[index
].unmask
,
122 vfio_virqfd_disable(&vdev
->irqs
[index
].unmask
);
126 if (flags
& VFIO_IRQ_SET_DATA_NONE
) {
127 vfio_platform_unmask(&vdev
->irqs
[index
]);
129 } else if (flags
& VFIO_IRQ_SET_DATA_BOOL
) {
130 uint8_t unmask
= *(uint8_t *)data
;
133 vfio_platform_unmask(&vdev
->irqs
[index
]);
139 static irqreturn_t
vfio_automasked_irq_handler(int irq
, void *dev_id
)
141 struct vfio_platform_irq
*irq_ctx
= dev_id
;
145 spin_lock_irqsave(&irq_ctx
->lock
, flags
);
147 if (!irq_ctx
->masked
) {
150 /* automask maskable interrupts */
151 disable_irq_nosync(irq_ctx
->hwirq
);
152 irq_ctx
->masked
= true;
155 spin_unlock_irqrestore(&irq_ctx
->lock
, flags
);
157 if (ret
== IRQ_HANDLED
)
158 eventfd_signal(irq_ctx
->trigger
, 1);
163 static irqreturn_t
vfio_irq_handler(int irq
, void *dev_id
)
165 struct vfio_platform_irq
*irq_ctx
= dev_id
;
167 eventfd_signal(irq_ctx
->trigger
, 1);
172 static int vfio_set_trigger(struct vfio_platform_device
*vdev
, int index
,
173 int fd
, irq_handler_t handler
)
175 struct vfio_platform_irq
*irq
= &vdev
->irqs
[index
];
176 struct eventfd_ctx
*trigger
;
180 irq_clear_status_flags(irq
->hwirq
, IRQ_NOAUTOEN
);
181 free_irq(irq
->hwirq
, irq
);
183 eventfd_ctx_put(irq
->trigger
);
187 if (fd
< 0) /* Disable only */
190 irq
->name
= kasprintf(GFP_KERNEL
, "vfio-irq[%d](%s)",
191 irq
->hwirq
, vdev
->name
);
195 trigger
= eventfd_ctx_fdget(fd
);
196 if (IS_ERR(trigger
)) {
198 return PTR_ERR(trigger
);
201 irq
->trigger
= trigger
;
203 irq_set_status_flags(irq
->hwirq
, IRQ_NOAUTOEN
);
204 ret
= request_irq(irq
->hwirq
, handler
, 0, irq
->name
, irq
);
207 eventfd_ctx_put(trigger
);
213 enable_irq(irq
->hwirq
);
218 static int vfio_platform_set_irq_trigger(struct vfio_platform_device
*vdev
,
219 unsigned index
, unsigned start
,
220 unsigned count
, uint32_t flags
,
223 struct vfio_platform_irq
*irq
= &vdev
->irqs
[index
];
224 irq_handler_t handler
;
226 if (vdev
->irqs
[index
].flags
& VFIO_IRQ_INFO_AUTOMASKED
)
227 handler
= vfio_automasked_irq_handler
;
229 handler
= vfio_irq_handler
;
231 if (!count
&& (flags
& VFIO_IRQ_SET_DATA_NONE
))
232 return vfio_set_trigger(vdev
, index
, -1, handler
);
234 if (start
!= 0 || count
!= 1)
237 if (flags
& VFIO_IRQ_SET_DATA_EVENTFD
) {
238 int32_t fd
= *(int32_t *)data
;
240 return vfio_set_trigger(vdev
, index
, fd
, handler
);
243 if (flags
& VFIO_IRQ_SET_DATA_NONE
) {
244 handler(irq
->hwirq
, irq
);
246 } else if (flags
& VFIO_IRQ_SET_DATA_BOOL
) {
247 uint8_t trigger
= *(uint8_t *)data
;
250 handler(irq
->hwirq
, irq
);
256 int vfio_platform_set_irqs_ioctl(struct vfio_platform_device
*vdev
,
257 uint32_t flags
, unsigned index
, unsigned start
,
258 unsigned count
, void *data
)
260 int (*func
)(struct vfio_platform_device
*vdev
, unsigned index
,
261 unsigned start
, unsigned count
, uint32_t flags
,
264 switch (flags
& VFIO_IRQ_SET_ACTION_TYPE_MASK
) {
265 case VFIO_IRQ_SET_ACTION_MASK
:
266 func
= vfio_platform_set_irq_mask
;
268 case VFIO_IRQ_SET_ACTION_UNMASK
:
269 func
= vfio_platform_set_irq_unmask
;
271 case VFIO_IRQ_SET_ACTION_TRIGGER
:
272 func
= vfio_platform_set_irq_trigger
;
279 return func(vdev
, index
, start
, count
, flags
, data
);
282 int vfio_platform_irq_init(struct vfio_platform_device
*vdev
)
286 while (vdev
->get_irq(vdev
, cnt
) >= 0)
289 vdev
->irqs
= kcalloc(cnt
, sizeof(struct vfio_platform_irq
), GFP_KERNEL
);
293 for (i
= 0; i
< cnt
; i
++) {
294 int hwirq
= vdev
->get_irq(vdev
, i
);
299 spin_lock_init(&vdev
->irqs
[i
].lock
);
301 vdev
->irqs
[i
].flags
= VFIO_IRQ_INFO_EVENTFD
;
303 if (irq_get_trigger_type(hwirq
) & IRQ_TYPE_LEVEL_MASK
)
304 vdev
->irqs
[i
].flags
|= VFIO_IRQ_INFO_MASKABLE
305 | VFIO_IRQ_INFO_AUTOMASKED
;
307 vdev
->irqs
[i
].count
= 1;
308 vdev
->irqs
[i
].hwirq
= hwirq
;
309 vdev
->irqs
[i
].masked
= false;
312 vdev
->num_irqs
= cnt
;
320 void vfio_platform_irq_cleanup(struct vfio_platform_device
*vdev
)
324 for (i
= 0; i
< vdev
->num_irqs
; i
++)
325 vfio_set_trigger(vdev
, i
, -1, NULL
);