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 | |
12 | #include "linux/fsl/mc.h" |
13 | #include "vfio_fsl_mc_private.h" |
14 | |
15 | static int vfio_fsl_mc_irqs_allocate(struct vfio_fsl_mc_device *vdev) |
16 | { |
17 | struct fsl_mc_device *mc_dev = vdev->mc_dev; |
18 | struct vfio_fsl_mc_irq *mc_irq; |
19 | int irq_count; |
20 | int ret, i; |
21 | |
22 | /* Device does not support any interrupt */ |
23 | if (mc_dev->obj_desc.irq_count == 0) |
24 | return 0; |
25 | |
26 | /* interrupts were already allocated for this device */ |
27 | if (vdev->mc_irqs) |
28 | return 0; |
29 | |
30 | irq_count = mc_dev->obj_desc.irq_count; |
31 | |
32 | mc_irq = kcalloc(n: irq_count, size: sizeof(*mc_irq), GFP_KERNEL_ACCOUNT); |
33 | if (!mc_irq) |
34 | return -ENOMEM; |
35 | |
36 | /* Allocate IRQs */ |
37 | ret = fsl_mc_allocate_irqs(mc_dev); |
38 | if (ret) { |
39 | kfree(objp: mc_irq); |
40 | return ret; |
41 | } |
42 | |
43 | for (i = 0; i < irq_count; i++) { |
44 | mc_irq[i].count = 1; |
45 | mc_irq[i].flags = VFIO_IRQ_INFO_EVENTFD; |
46 | } |
47 | |
48 | vdev->mc_irqs = mc_irq; |
49 | |
50 | return 0; |
51 | } |
52 | |
53 | static irqreturn_t vfio_fsl_mc_irq_handler(int irq_num, void *arg) |
54 | { |
55 | struct vfio_fsl_mc_irq *mc_irq = (struct vfio_fsl_mc_irq *)arg; |
56 | |
57 | eventfd_signal(ctx: mc_irq->trigger); |
58 | return IRQ_HANDLED; |
59 | } |
60 | |
61 | static int vfio_set_trigger(struct vfio_fsl_mc_device *vdev, |
62 | int index, int fd) |
63 | { |
64 | struct vfio_fsl_mc_irq *irq = &vdev->mc_irqs[index]; |
65 | struct eventfd_ctx *trigger; |
66 | int hwirq; |
67 | int ret; |
68 | |
69 | hwirq = vdev->mc_dev->irqs[index]->virq; |
70 | if (irq->trigger) { |
71 | free_irq(hwirq, irq); |
72 | kfree(objp: irq->name); |
73 | eventfd_ctx_put(ctx: irq->trigger); |
74 | irq->trigger = NULL; |
75 | } |
76 | |
77 | if (fd < 0) /* Disable only */ |
78 | return 0; |
79 | |
80 | irq->name = kasprintf(GFP_KERNEL_ACCOUNT, fmt: "vfio-irq[%d](%s)" , |
81 | hwirq, dev_name(dev: &vdev->mc_dev->dev)); |
82 | if (!irq->name) |
83 | return -ENOMEM; |
84 | |
85 | trigger = eventfd_ctx_fdget(fd); |
86 | if (IS_ERR(ptr: trigger)) { |
87 | kfree(objp: irq->name); |
88 | return PTR_ERR(ptr: trigger); |
89 | } |
90 | |
91 | irq->trigger = trigger; |
92 | |
93 | ret = request_irq(irq: hwirq, handler: vfio_fsl_mc_irq_handler, flags: 0, |
94 | name: irq->name, dev: irq); |
95 | if (ret) { |
96 | kfree(objp: irq->name); |
97 | eventfd_ctx_put(ctx: trigger); |
98 | irq->trigger = NULL; |
99 | return ret; |
100 | } |
101 | |
102 | return 0; |
103 | } |
104 | |
105 | static int vfio_fsl_mc_set_irq_trigger(struct vfio_fsl_mc_device *vdev, |
106 | unsigned int index, unsigned int start, |
107 | unsigned int count, u32 flags, |
108 | void *data) |
109 | { |
110 | struct fsl_mc_device *mc_dev = vdev->mc_dev; |
111 | int ret, hwirq; |
112 | struct vfio_fsl_mc_irq *irq; |
113 | struct device *cont_dev = fsl_mc_cont_dev(&mc_dev->dev); |
114 | struct fsl_mc_device *mc_cont = to_fsl_mc_device(cont_dev); |
115 | |
116 | if (!count && (flags & VFIO_IRQ_SET_DATA_NONE)) |
117 | return vfio_set_trigger(vdev, index, fd: -1); |
118 | |
119 | if (start != 0 || count != 1) |
120 | return -EINVAL; |
121 | |
122 | mutex_lock(&vdev->vdev.dev_set->lock); |
123 | ret = fsl_mc_populate_irq_pool(mc_bus_dev: mc_cont, |
124 | FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS); |
125 | if (ret) |
126 | goto unlock; |
127 | |
128 | ret = vfio_fsl_mc_irqs_allocate(vdev); |
129 | if (ret) |
130 | goto unlock; |
131 | mutex_unlock(lock: &vdev->vdev.dev_set->lock); |
132 | |
133 | if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { |
134 | s32 fd = *(s32 *)data; |
135 | |
136 | return vfio_set_trigger(vdev, index, fd); |
137 | } |
138 | |
139 | hwirq = vdev->mc_dev->irqs[index]->virq; |
140 | |
141 | irq = &vdev->mc_irqs[index]; |
142 | |
143 | if (flags & VFIO_IRQ_SET_DATA_NONE) { |
144 | if (irq->trigger) |
145 | eventfd_signal(ctx: irq->trigger); |
146 | |
147 | } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { |
148 | u8 trigger = *(u8 *)data; |
149 | |
150 | if (trigger && irq->trigger) |
151 | eventfd_signal(ctx: irq->trigger); |
152 | } |
153 | |
154 | return 0; |
155 | |
156 | unlock: |
157 | mutex_unlock(lock: &vdev->vdev.dev_set->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, index: i, fd: -1); |
190 | |
191 | fsl_mc_free_irqs(mc_dev); |
192 | kfree(objp: vdev->mc_irqs); |
193 | vdev->mc_irqs = NULL; |
194 | } |
195 | |