18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2013-2016 Freescale Semiconductor Inc. 48c2ecf20Sopenharmony_ci * Copyright 2019 NXP 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/vfio.h> 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci#include <linux/types.h> 108c2ecf20Sopenharmony_ci#include <linux/eventfd.h> 118c2ecf20Sopenharmony_ci#include <linux/msi.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "linux/fsl/mc.h" 148c2ecf20Sopenharmony_ci#include "vfio_fsl_mc_private.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic int vfio_fsl_mc_irqs_allocate(struct vfio_fsl_mc_device *vdev) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev = vdev->mc_dev; 198c2ecf20Sopenharmony_ci struct vfio_fsl_mc_irq *mc_irq; 208c2ecf20Sopenharmony_ci int irq_count; 218c2ecf20Sopenharmony_ci int ret, i; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci /* Device does not support any interrupt */ 248c2ecf20Sopenharmony_ci if (mc_dev->obj_desc.irq_count == 0) 258c2ecf20Sopenharmony_ci return 0; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci /* interrupts were already allocated for this device */ 288c2ecf20Sopenharmony_ci if (vdev->mc_irqs) 298c2ecf20Sopenharmony_ci return 0; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci irq_count = mc_dev->obj_desc.irq_count; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci mc_irq = kcalloc(irq_count, sizeof(*mc_irq), GFP_KERNEL); 348c2ecf20Sopenharmony_ci if (!mc_irq) 358c2ecf20Sopenharmony_ci return -ENOMEM; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci /* Allocate IRQs */ 388c2ecf20Sopenharmony_ci ret = fsl_mc_allocate_irqs(mc_dev); 398c2ecf20Sopenharmony_ci if (ret) { 408c2ecf20Sopenharmony_ci kfree(mc_irq); 418c2ecf20Sopenharmony_ci return ret; 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci for (i = 0; i < irq_count; i++) { 458c2ecf20Sopenharmony_ci mc_irq[i].count = 1; 468c2ecf20Sopenharmony_ci mc_irq[i].flags = VFIO_IRQ_INFO_EVENTFD; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci vdev->mc_irqs = mc_irq; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci return 0; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic irqreturn_t vfio_fsl_mc_irq_handler(int irq_num, void *arg) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct vfio_fsl_mc_irq *mc_irq = (struct vfio_fsl_mc_irq *)arg; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci eventfd_signal(mc_irq->trigger, 1); 598c2ecf20Sopenharmony_ci return IRQ_HANDLED; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int vfio_set_trigger(struct vfio_fsl_mc_device *vdev, 638c2ecf20Sopenharmony_ci int index, int fd) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct vfio_fsl_mc_irq *irq = &vdev->mc_irqs[index]; 668c2ecf20Sopenharmony_ci struct eventfd_ctx *trigger; 678c2ecf20Sopenharmony_ci int hwirq; 688c2ecf20Sopenharmony_ci int ret; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci hwirq = vdev->mc_dev->irqs[index]->msi_desc->irq; 718c2ecf20Sopenharmony_ci if (irq->trigger) { 728c2ecf20Sopenharmony_ci free_irq(hwirq, irq); 738c2ecf20Sopenharmony_ci kfree(irq->name); 748c2ecf20Sopenharmony_ci eventfd_ctx_put(irq->trigger); 758c2ecf20Sopenharmony_ci irq->trigger = NULL; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (fd < 0) /* Disable only */ 798c2ecf20Sopenharmony_ci return 0; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci irq->name = kasprintf(GFP_KERNEL, "vfio-irq[%d](%s)", 828c2ecf20Sopenharmony_ci hwirq, dev_name(&vdev->mc_dev->dev)); 838c2ecf20Sopenharmony_ci if (!irq->name) 848c2ecf20Sopenharmony_ci return -ENOMEM; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci trigger = eventfd_ctx_fdget(fd); 878c2ecf20Sopenharmony_ci if (IS_ERR(trigger)) { 888c2ecf20Sopenharmony_ci kfree(irq->name); 898c2ecf20Sopenharmony_ci return PTR_ERR(trigger); 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci irq->trigger = trigger; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci ret = request_irq(hwirq, vfio_fsl_mc_irq_handler, 0, 958c2ecf20Sopenharmony_ci irq->name, irq); 968c2ecf20Sopenharmony_ci if (ret) { 978c2ecf20Sopenharmony_ci kfree(irq->name); 988c2ecf20Sopenharmony_ci eventfd_ctx_put(trigger); 998c2ecf20Sopenharmony_ci irq->trigger = NULL; 1008c2ecf20Sopenharmony_ci return ret; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return 0; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic int vfio_fsl_mc_set_irq_trigger(struct vfio_fsl_mc_device *vdev, 1078c2ecf20Sopenharmony_ci unsigned int index, unsigned int start, 1088c2ecf20Sopenharmony_ci unsigned int count, u32 flags, 1098c2ecf20Sopenharmony_ci void *data) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev = vdev->mc_dev; 1128c2ecf20Sopenharmony_ci int ret, hwirq; 1138c2ecf20Sopenharmony_ci struct vfio_fsl_mc_irq *irq; 1148c2ecf20Sopenharmony_ci struct device *cont_dev = fsl_mc_cont_dev(&mc_dev->dev); 1158c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_cont = to_fsl_mc_device(cont_dev); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (!count && (flags & VFIO_IRQ_SET_DATA_NONE)) 1188c2ecf20Sopenharmony_ci return vfio_set_trigger(vdev, index, -1); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (start != 0 || count != 1) 1218c2ecf20Sopenharmony_ci return -EINVAL; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci mutex_lock(&vdev->reflck->lock); 1248c2ecf20Sopenharmony_ci ret = fsl_mc_populate_irq_pool(mc_cont, 1258c2ecf20Sopenharmony_ci FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS); 1268c2ecf20Sopenharmony_ci if (ret) 1278c2ecf20Sopenharmony_ci goto unlock; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci ret = vfio_fsl_mc_irqs_allocate(vdev); 1308c2ecf20Sopenharmony_ci if (ret) 1318c2ecf20Sopenharmony_ci goto unlock; 1328c2ecf20Sopenharmony_ci mutex_unlock(&vdev->reflck->lock); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { 1358c2ecf20Sopenharmony_ci s32 fd = *(s32 *)data; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return vfio_set_trigger(vdev, index, fd); 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci hwirq = vdev->mc_dev->irqs[index]->msi_desc->irq; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci irq = &vdev->mc_irqs[index]; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (flags & VFIO_IRQ_SET_DATA_NONE) { 1458c2ecf20Sopenharmony_ci vfio_fsl_mc_irq_handler(hwirq, irq); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { 1488c2ecf20Sopenharmony_ci u8 trigger = *(u8 *)data; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (trigger) 1518c2ecf20Sopenharmony_ci vfio_fsl_mc_irq_handler(hwirq, irq); 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci return 0; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ciunlock: 1578c2ecf20Sopenharmony_ci mutex_unlock(&vdev->reflck->lock); 1588c2ecf20Sopenharmony_ci return ret; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ciint vfio_fsl_mc_set_irqs_ioctl(struct vfio_fsl_mc_device *vdev, 1638c2ecf20Sopenharmony_ci u32 flags, unsigned int index, 1648c2ecf20Sopenharmony_ci unsigned int start, unsigned int count, 1658c2ecf20Sopenharmony_ci void *data) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci if (flags & VFIO_IRQ_SET_ACTION_TRIGGER) 1688c2ecf20Sopenharmony_ci return vfio_fsl_mc_set_irq_trigger(vdev, index, start, 1698c2ecf20Sopenharmony_ci count, flags, data); 1708c2ecf20Sopenharmony_ci else 1718c2ecf20Sopenharmony_ci return -EINVAL; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/* Free All IRQs for the given MC object */ 1758c2ecf20Sopenharmony_civoid vfio_fsl_mc_irqs_cleanup(struct vfio_fsl_mc_device *vdev) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev = vdev->mc_dev; 1788c2ecf20Sopenharmony_ci int irq_count = mc_dev->obj_desc.irq_count; 1798c2ecf20Sopenharmony_ci int i; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* 1828c2ecf20Sopenharmony_ci * Device does not support any interrupt or the interrupts 1838c2ecf20Sopenharmony_ci * were not configured 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_ci if (!vdev->mc_irqs) 1868c2ecf20Sopenharmony_ci return; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci for (i = 0; i < irq_count; i++) 1898c2ecf20Sopenharmony_ci vfio_set_trigger(vdev, i, -1); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci fsl_mc_free_irqs(mc_dev); 1928c2ecf20Sopenharmony_ci kfree(vdev->mc_irqs); 1938c2ecf20Sopenharmony_ci vdev->mc_irqs = NULL; 1948c2ecf20Sopenharmony_ci} 195