vfio_fsl_mc_intr.c (4227B)
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 16static 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 54static 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 62static 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]->virq; 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 106static 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->vdev.dev_set->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->vdev.dev_set->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]->virq; 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 156unlock: 157 mutex_unlock(&vdev->vdev.dev_set->lock); 158 return ret; 159 160} 161 162int 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 */ 175void 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}