12e0d2956SDiana Craciun // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) 22e0d2956SDiana Craciun /* 32e0d2956SDiana Craciun * Copyright 2013-2016 Freescale Semiconductor Inc. 42e0d2956SDiana Craciun * Copyright 2019 NXP 52e0d2956SDiana Craciun */ 62e0d2956SDiana Craciun 72e0d2956SDiana Craciun #include <linux/vfio.h> 82e0d2956SDiana Craciun #include <linux/slab.h> 92e0d2956SDiana Craciun #include <linux/types.h> 102e0d2956SDiana Craciun #include <linux/eventfd.h> 112e0d2956SDiana Craciun #include <linux/msi.h> 122e0d2956SDiana Craciun 132e0d2956SDiana Craciun #include "linux/fsl/mc.h" 142e0d2956SDiana Craciun #include "vfio_fsl_mc_private.h" 152e0d2956SDiana Craciun 16cc0ee20bSDiana Craciun int vfio_fsl_mc_irqs_allocate(struct vfio_fsl_mc_device *vdev) 17cc0ee20bSDiana Craciun { 18cc0ee20bSDiana Craciun struct fsl_mc_device *mc_dev = vdev->mc_dev; 19cc0ee20bSDiana Craciun struct vfio_fsl_mc_irq *mc_irq; 20cc0ee20bSDiana Craciun int irq_count; 21cc0ee20bSDiana Craciun int ret, i; 22cc0ee20bSDiana Craciun 23cc0ee20bSDiana Craciun /* Device does not support any interrupt */ 24cc0ee20bSDiana Craciun if (mc_dev->obj_desc.irq_count == 0) 25cc0ee20bSDiana Craciun return 0; 26cc0ee20bSDiana Craciun 27cc0ee20bSDiana Craciun /* interrupts were already allocated for this device */ 28cc0ee20bSDiana Craciun if (vdev->mc_irqs) 29cc0ee20bSDiana Craciun return 0; 30cc0ee20bSDiana Craciun 31cc0ee20bSDiana Craciun irq_count = mc_dev->obj_desc.irq_count; 32cc0ee20bSDiana Craciun 33cc0ee20bSDiana Craciun mc_irq = kcalloc(irq_count, sizeof(*mc_irq), GFP_KERNEL); 34cc0ee20bSDiana Craciun if (!mc_irq) 35cc0ee20bSDiana Craciun return -ENOMEM; 36cc0ee20bSDiana Craciun 37cc0ee20bSDiana Craciun /* Allocate IRQs */ 38cc0ee20bSDiana Craciun ret = fsl_mc_allocate_irqs(mc_dev); 39cc0ee20bSDiana Craciun if (ret) { 40cc0ee20bSDiana Craciun kfree(mc_irq); 41cc0ee20bSDiana Craciun return ret; 42cc0ee20bSDiana Craciun } 43cc0ee20bSDiana Craciun 44cc0ee20bSDiana Craciun for (i = 0; i < irq_count; i++) { 45cc0ee20bSDiana Craciun mc_irq[i].count = 1; 46cc0ee20bSDiana Craciun mc_irq[i].flags = VFIO_IRQ_INFO_EVENTFD; 47cc0ee20bSDiana Craciun } 48cc0ee20bSDiana Craciun 49cc0ee20bSDiana Craciun vdev->mc_irqs = mc_irq; 50cc0ee20bSDiana Craciun 51cc0ee20bSDiana Craciun return 0; 52cc0ee20bSDiana Craciun } 53cc0ee20bSDiana Craciun 54cc0ee20bSDiana Craciun static irqreturn_t vfio_fsl_mc_irq_handler(int irq_num, void *arg) 55cc0ee20bSDiana Craciun { 56cc0ee20bSDiana Craciun struct vfio_fsl_mc_irq *mc_irq = (struct vfio_fsl_mc_irq *)arg; 57cc0ee20bSDiana Craciun 58cc0ee20bSDiana Craciun eventfd_signal(mc_irq->trigger, 1); 59cc0ee20bSDiana Craciun return IRQ_HANDLED; 60cc0ee20bSDiana Craciun } 61cc0ee20bSDiana Craciun 62cc0ee20bSDiana Craciun static int vfio_set_trigger(struct vfio_fsl_mc_device *vdev, 63cc0ee20bSDiana Craciun int index, int fd) 64cc0ee20bSDiana Craciun { 65cc0ee20bSDiana Craciun struct vfio_fsl_mc_irq *irq = &vdev->mc_irqs[index]; 66cc0ee20bSDiana Craciun struct eventfd_ctx *trigger; 67cc0ee20bSDiana Craciun int hwirq; 68cc0ee20bSDiana Craciun int ret; 69cc0ee20bSDiana Craciun 70cc0ee20bSDiana Craciun hwirq = vdev->mc_dev->irqs[index]->msi_desc->irq; 71cc0ee20bSDiana Craciun if (irq->trigger) { 72cc0ee20bSDiana Craciun free_irq(hwirq, irq); 73cc0ee20bSDiana Craciun kfree(irq->name); 74cc0ee20bSDiana Craciun eventfd_ctx_put(irq->trigger); 75cc0ee20bSDiana Craciun irq->trigger = NULL; 76cc0ee20bSDiana Craciun } 77cc0ee20bSDiana Craciun 78cc0ee20bSDiana Craciun if (fd < 0) /* Disable only */ 79cc0ee20bSDiana Craciun return 0; 80cc0ee20bSDiana Craciun 81cc0ee20bSDiana Craciun irq->name = kasprintf(GFP_KERNEL, "vfio-irq[%d](%s)", 82cc0ee20bSDiana Craciun hwirq, dev_name(&vdev->mc_dev->dev)); 83cc0ee20bSDiana Craciun if (!irq->name) 84cc0ee20bSDiana Craciun return -ENOMEM; 85cc0ee20bSDiana Craciun 86cc0ee20bSDiana Craciun trigger = eventfd_ctx_fdget(fd); 87cc0ee20bSDiana Craciun if (IS_ERR(trigger)) { 88cc0ee20bSDiana Craciun kfree(irq->name); 89cc0ee20bSDiana Craciun return PTR_ERR(trigger); 90cc0ee20bSDiana Craciun } 91cc0ee20bSDiana Craciun 92cc0ee20bSDiana Craciun irq->trigger = trigger; 93cc0ee20bSDiana Craciun 94cc0ee20bSDiana Craciun ret = request_irq(hwirq, vfio_fsl_mc_irq_handler, 0, 95cc0ee20bSDiana Craciun irq->name, irq); 96cc0ee20bSDiana Craciun if (ret) { 97cc0ee20bSDiana Craciun kfree(irq->name); 98cc0ee20bSDiana Craciun eventfd_ctx_put(trigger); 99cc0ee20bSDiana Craciun irq->trigger = NULL; 100cc0ee20bSDiana Craciun return ret; 101cc0ee20bSDiana Craciun } 102cc0ee20bSDiana Craciun 103cc0ee20bSDiana Craciun return 0; 104cc0ee20bSDiana Craciun } 105cc0ee20bSDiana Craciun 1062e0d2956SDiana Craciun static int vfio_fsl_mc_set_irq_trigger(struct vfio_fsl_mc_device *vdev, 1072e0d2956SDiana Craciun unsigned int index, unsigned int start, 1082e0d2956SDiana Craciun unsigned int count, u32 flags, 1092e0d2956SDiana Craciun void *data) 1102e0d2956SDiana Craciun { 111cc0ee20bSDiana Craciun struct fsl_mc_device *mc_dev = vdev->mc_dev; 112cc0ee20bSDiana Craciun int ret, hwirq; 113cc0ee20bSDiana Craciun struct vfio_fsl_mc_irq *irq; 114cc0ee20bSDiana Craciun struct device *cont_dev = fsl_mc_cont_dev(&mc_dev->dev); 115cc0ee20bSDiana Craciun struct fsl_mc_device *mc_cont = to_fsl_mc_device(cont_dev); 116cc0ee20bSDiana Craciun 117cc0ee20bSDiana Craciun if (start != 0 || count != 1) 1182e0d2956SDiana Craciun return -EINVAL; 119cc0ee20bSDiana Craciun 120cc0ee20bSDiana Craciun mutex_lock(&vdev->reflck->lock); 121cc0ee20bSDiana Craciun ret = fsl_mc_populate_irq_pool(mc_cont, 122cc0ee20bSDiana Craciun FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS); 123cc0ee20bSDiana Craciun if (ret) 124cc0ee20bSDiana Craciun goto unlock; 125cc0ee20bSDiana Craciun 126cc0ee20bSDiana Craciun ret = vfio_fsl_mc_irqs_allocate(vdev); 127cc0ee20bSDiana Craciun if (ret) 128cc0ee20bSDiana Craciun goto unlock; 129cc0ee20bSDiana Craciun mutex_unlock(&vdev->reflck->lock); 130cc0ee20bSDiana Craciun 131cc0ee20bSDiana Craciun if (!count && (flags & VFIO_IRQ_SET_DATA_NONE)) 132cc0ee20bSDiana Craciun return vfio_set_trigger(vdev, index, -1); 133cc0ee20bSDiana Craciun 134cc0ee20bSDiana Craciun if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { 135cc0ee20bSDiana Craciun s32 fd = *(s32 *)data; 136cc0ee20bSDiana Craciun 137cc0ee20bSDiana Craciun return vfio_set_trigger(vdev, index, fd); 138cc0ee20bSDiana Craciun } 139cc0ee20bSDiana Craciun 140cc0ee20bSDiana Craciun hwirq = vdev->mc_dev->irqs[index]->msi_desc->irq; 141cc0ee20bSDiana Craciun 142cc0ee20bSDiana Craciun irq = &vdev->mc_irqs[index]; 143cc0ee20bSDiana Craciun 144cc0ee20bSDiana Craciun if (flags & VFIO_IRQ_SET_DATA_NONE) { 145cc0ee20bSDiana Craciun vfio_fsl_mc_irq_handler(hwirq, irq); 146cc0ee20bSDiana Craciun 147cc0ee20bSDiana Craciun } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { 148cc0ee20bSDiana Craciun u8 trigger = *(u8 *)data; 149cc0ee20bSDiana Craciun 150cc0ee20bSDiana Craciun if (trigger) 151cc0ee20bSDiana Craciun vfio_fsl_mc_irq_handler(hwirq, irq); 152cc0ee20bSDiana Craciun } 153cc0ee20bSDiana Craciun 154cc0ee20bSDiana Craciun return 0; 155cc0ee20bSDiana Craciun 156cc0ee20bSDiana Craciun unlock: 157cc0ee20bSDiana Craciun mutex_unlock(&vdev->reflck->lock); 158cc0ee20bSDiana Craciun return ret; 159cc0ee20bSDiana Craciun 1602e0d2956SDiana Craciun } 1612e0d2956SDiana Craciun 1622e0d2956SDiana Craciun int vfio_fsl_mc_set_irqs_ioctl(struct vfio_fsl_mc_device *vdev, 1632e0d2956SDiana Craciun u32 flags, unsigned int index, 1642e0d2956SDiana Craciun unsigned int start, unsigned int count, 1652e0d2956SDiana Craciun void *data) 1662e0d2956SDiana Craciun { 1672e0d2956SDiana Craciun if (flags & VFIO_IRQ_SET_ACTION_TRIGGER) 1682e0d2956SDiana Craciun return vfio_fsl_mc_set_irq_trigger(vdev, index, start, 1692e0d2956SDiana Craciun count, flags, data); 1702e0d2956SDiana Craciun else 1712e0d2956SDiana Craciun return -EINVAL; 1722e0d2956SDiana Craciun } 173cc0ee20bSDiana Craciun 174cc0ee20bSDiana Craciun /* Free All IRQs for the given MC object */ 175cc0ee20bSDiana Craciun void vfio_fsl_mc_irqs_cleanup(struct vfio_fsl_mc_device *vdev) 176cc0ee20bSDiana Craciun { 177cc0ee20bSDiana Craciun struct fsl_mc_device *mc_dev = vdev->mc_dev; 178cc0ee20bSDiana Craciun int irq_count = mc_dev->obj_desc.irq_count; 179cc0ee20bSDiana Craciun int i; 180cc0ee20bSDiana Craciun 181cc0ee20bSDiana Craciun /* 182cc0ee20bSDiana Craciun * Device does not support any interrupt or the interrupts 183cc0ee20bSDiana Craciun * were not configured 184cc0ee20bSDiana Craciun */ 185cc0ee20bSDiana Craciun if (!vdev->mc_irqs) 186cc0ee20bSDiana Craciun return; 187cc0ee20bSDiana Craciun 188cc0ee20bSDiana Craciun for (i = 0; i < irq_count; i++) 189cc0ee20bSDiana Craciun vfio_set_trigger(vdev, i, -1); 190cc0ee20bSDiana Craciun 191cc0ee20bSDiana Craciun fsl_mc_free_irqs(mc_dev); 192cc0ee20bSDiana Craciun kfree(vdev->mc_irqs); 193cc0ee20bSDiana Craciun vdev->mc_irqs = NULL; 194cc0ee20bSDiana Craciun } 195