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