162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2013-2016 Freescale Semiconductor Inc. 462306a36Sopenharmony_ci * Copyright 2019 NXP 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/vfio.h> 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/types.h> 1062306a36Sopenharmony_ci#include <linux/eventfd.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "linux/fsl/mc.h" 1362306a36Sopenharmony_ci#include "vfio_fsl_mc_private.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic int vfio_fsl_mc_irqs_allocate(struct vfio_fsl_mc_device *vdev) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci struct fsl_mc_device *mc_dev = vdev->mc_dev; 1862306a36Sopenharmony_ci struct vfio_fsl_mc_irq *mc_irq; 1962306a36Sopenharmony_ci int irq_count; 2062306a36Sopenharmony_ci int ret, i; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci /* Device does not support any interrupt */ 2362306a36Sopenharmony_ci if (mc_dev->obj_desc.irq_count == 0) 2462306a36Sopenharmony_ci return 0; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci /* interrupts were already allocated for this device */ 2762306a36Sopenharmony_ci if (vdev->mc_irqs) 2862306a36Sopenharmony_ci return 0; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci irq_count = mc_dev->obj_desc.irq_count; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci mc_irq = kcalloc(irq_count, sizeof(*mc_irq), GFP_KERNEL_ACCOUNT); 3362306a36Sopenharmony_ci if (!mc_irq) 3462306a36Sopenharmony_ci return -ENOMEM; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci /* Allocate IRQs */ 3762306a36Sopenharmony_ci ret = fsl_mc_allocate_irqs(mc_dev); 3862306a36Sopenharmony_ci if (ret) { 3962306a36Sopenharmony_ci kfree(mc_irq); 4062306a36Sopenharmony_ci return ret; 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci for (i = 0; i < irq_count; i++) { 4462306a36Sopenharmony_ci mc_irq[i].count = 1; 4562306a36Sopenharmony_ci mc_irq[i].flags = VFIO_IRQ_INFO_EVENTFD; 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci vdev->mc_irqs = mc_irq; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci return 0; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic irqreturn_t vfio_fsl_mc_irq_handler(int irq_num, void *arg) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct vfio_fsl_mc_irq *mc_irq = (struct vfio_fsl_mc_irq *)arg; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci eventfd_signal(mc_irq->trigger, 1); 5862306a36Sopenharmony_ci return IRQ_HANDLED; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic int vfio_set_trigger(struct vfio_fsl_mc_device *vdev, 6262306a36Sopenharmony_ci int index, int fd) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci struct vfio_fsl_mc_irq *irq = &vdev->mc_irqs[index]; 6562306a36Sopenharmony_ci struct eventfd_ctx *trigger; 6662306a36Sopenharmony_ci int hwirq; 6762306a36Sopenharmony_ci int ret; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci hwirq = vdev->mc_dev->irqs[index]->virq; 7062306a36Sopenharmony_ci if (irq->trigger) { 7162306a36Sopenharmony_ci free_irq(hwirq, irq); 7262306a36Sopenharmony_ci kfree(irq->name); 7362306a36Sopenharmony_ci eventfd_ctx_put(irq->trigger); 7462306a36Sopenharmony_ci irq->trigger = NULL; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (fd < 0) /* Disable only */ 7862306a36Sopenharmony_ci return 0; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci irq->name = kasprintf(GFP_KERNEL_ACCOUNT, "vfio-irq[%d](%s)", 8162306a36Sopenharmony_ci hwirq, dev_name(&vdev->mc_dev->dev)); 8262306a36Sopenharmony_ci if (!irq->name) 8362306a36Sopenharmony_ci return -ENOMEM; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci trigger = eventfd_ctx_fdget(fd); 8662306a36Sopenharmony_ci if (IS_ERR(trigger)) { 8762306a36Sopenharmony_ci kfree(irq->name); 8862306a36Sopenharmony_ci return PTR_ERR(trigger); 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci irq->trigger = trigger; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci ret = request_irq(hwirq, vfio_fsl_mc_irq_handler, 0, 9462306a36Sopenharmony_ci irq->name, irq); 9562306a36Sopenharmony_ci if (ret) { 9662306a36Sopenharmony_ci kfree(irq->name); 9762306a36Sopenharmony_ci eventfd_ctx_put(trigger); 9862306a36Sopenharmony_ci irq->trigger = NULL; 9962306a36Sopenharmony_ci return ret; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic int vfio_fsl_mc_set_irq_trigger(struct vfio_fsl_mc_device *vdev, 10662306a36Sopenharmony_ci unsigned int index, unsigned int start, 10762306a36Sopenharmony_ci unsigned int count, u32 flags, 10862306a36Sopenharmony_ci void *data) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct fsl_mc_device *mc_dev = vdev->mc_dev; 11162306a36Sopenharmony_ci int ret, hwirq; 11262306a36Sopenharmony_ci struct vfio_fsl_mc_irq *irq; 11362306a36Sopenharmony_ci struct device *cont_dev = fsl_mc_cont_dev(&mc_dev->dev); 11462306a36Sopenharmony_ci struct fsl_mc_device *mc_cont = to_fsl_mc_device(cont_dev); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (!count && (flags & VFIO_IRQ_SET_DATA_NONE)) 11762306a36Sopenharmony_ci return vfio_set_trigger(vdev, index, -1); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (start != 0 || count != 1) 12062306a36Sopenharmony_ci return -EINVAL; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci mutex_lock(&vdev->vdev.dev_set->lock); 12362306a36Sopenharmony_ci ret = fsl_mc_populate_irq_pool(mc_cont, 12462306a36Sopenharmony_ci FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS); 12562306a36Sopenharmony_ci if (ret) 12662306a36Sopenharmony_ci goto unlock; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci ret = vfio_fsl_mc_irqs_allocate(vdev); 12962306a36Sopenharmony_ci if (ret) 13062306a36Sopenharmony_ci goto unlock; 13162306a36Sopenharmony_ci mutex_unlock(&vdev->vdev.dev_set->lock); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { 13462306a36Sopenharmony_ci s32 fd = *(s32 *)data; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci return vfio_set_trigger(vdev, index, fd); 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci hwirq = vdev->mc_dev->irqs[index]->virq; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci irq = &vdev->mc_irqs[index]; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (flags & VFIO_IRQ_SET_DATA_NONE) { 14462306a36Sopenharmony_ci if (irq->trigger) 14562306a36Sopenharmony_ci eventfd_signal(irq->trigger, 1); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { 14862306a36Sopenharmony_ci u8 trigger = *(u8 *)data; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (trigger && irq->trigger) 15162306a36Sopenharmony_ci eventfd_signal(irq->trigger, 1); 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci return 0; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ciunlock: 15762306a36Sopenharmony_ci mutex_unlock(&vdev->vdev.dev_set->lock); 15862306a36Sopenharmony_ci return ret; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ciint vfio_fsl_mc_set_irqs_ioctl(struct vfio_fsl_mc_device *vdev, 16362306a36Sopenharmony_ci u32 flags, unsigned int index, 16462306a36Sopenharmony_ci unsigned int start, unsigned int count, 16562306a36Sopenharmony_ci void *data) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci if (flags & VFIO_IRQ_SET_ACTION_TRIGGER) 16862306a36Sopenharmony_ci return vfio_fsl_mc_set_irq_trigger(vdev, index, start, 16962306a36Sopenharmony_ci count, flags, data); 17062306a36Sopenharmony_ci else 17162306a36Sopenharmony_ci return -EINVAL; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/* Free All IRQs for the given MC object */ 17562306a36Sopenharmony_civoid vfio_fsl_mc_irqs_cleanup(struct vfio_fsl_mc_device *vdev) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct fsl_mc_device *mc_dev = vdev->mc_dev; 17862306a36Sopenharmony_ci int irq_count = mc_dev->obj_desc.irq_count; 17962306a36Sopenharmony_ci int i; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* 18262306a36Sopenharmony_ci * Device does not support any interrupt or the interrupts 18362306a36Sopenharmony_ci * were not configured 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_ci if (!vdev->mc_irqs) 18662306a36Sopenharmony_ci return; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci for (i = 0; i < irq_count; i++) 18962306a36Sopenharmony_ci vfio_set_trigger(vdev, i, -1); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci fsl_mc_free_irqs(mc_dev); 19262306a36Sopenharmony_ci kfree(vdev->mc_irqs); 19362306a36Sopenharmony_ci vdev->mc_irqs = NULL; 19462306a36Sopenharmony_ci} 195