193 lines · c
1// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)2/*3 * Copyright 2013-2016 Freescale Semiconductor Inc.4 * Copyright 2019 NXP5 */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 15static 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(irq_count, 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(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 53static 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(mc_irq->trigger);58 return IRQ_HANDLED;59}60 61static 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(irq->name);73 eventfd_ctx_put(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, "vfio-irq[%d](%s)",81 hwirq, dev_name(&vdev->mc_dev->dev));82 if (!irq->name)83 return -ENOMEM;84 85 trigger = eventfd_ctx_fdget(fd);86 if (IS_ERR(trigger)) {87 kfree(irq->name);88 return PTR_ERR(trigger);89 }90 91 irq->trigger = trigger;92 93 ret = request_irq(hwirq, vfio_fsl_mc_irq_handler, 0,94 irq->name, irq);95 if (ret) {96 kfree(irq->name);97 eventfd_ctx_put(trigger);98 irq->trigger = NULL;99 return ret;100 }101 102 return 0;103}104 105static 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 struct vfio_fsl_mc_irq *irq;112 struct device *cont_dev = fsl_mc_cont_dev(&mc_dev->dev);113 struct fsl_mc_device *mc_cont = to_fsl_mc_device(cont_dev);114 int ret;115 116 if (!count && (flags & VFIO_IRQ_SET_DATA_NONE))117 return vfio_set_trigger(vdev, index, -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_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(&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 irq = &vdev->mc_irqs[index];140 141 if (flags & VFIO_IRQ_SET_DATA_NONE) {142 if (irq->trigger)143 eventfd_signal(irq->trigger);144 145 } else if (flags & VFIO_IRQ_SET_DATA_BOOL) {146 u8 trigger = *(u8 *)data;147 148 if (trigger && irq->trigger)149 eventfd_signal(irq->trigger);150 }151 152 return 0;153 154unlock:155 mutex_unlock(&vdev->vdev.dev_set->lock);156 return ret;157 158}159 160int vfio_fsl_mc_set_irqs_ioctl(struct vfio_fsl_mc_device *vdev,161 u32 flags, unsigned int index,162 unsigned int start, unsigned int count,163 void *data)164{165 if (flags & VFIO_IRQ_SET_ACTION_TRIGGER)166 return vfio_fsl_mc_set_irq_trigger(vdev, index, start,167 count, flags, data);168 else169 return -EINVAL;170}171 172/* Free All IRQs for the given MC object */173void vfio_fsl_mc_irqs_cleanup(struct vfio_fsl_mc_device *vdev)174{175 struct fsl_mc_device *mc_dev = vdev->mc_dev;176 int irq_count = mc_dev->obj_desc.irq_count;177 int i;178 179 /*180 * Device does not support any interrupt or the interrupts181 * were not configured182 */183 if (!vdev->mc_irqs)184 return;185 186 for (i = 0; i < irq_count; i++)187 vfio_set_trigger(vdev, i, -1);188 189 fsl_mc_free_irqs(mc_dev);190 kfree(vdev->mc_irqs);191 vdev->mc_irqs = NULL;192}193