brintos

brintos / linux-shallow public Read only

0
0
Text · 4.1 KiB · 7e7988c Raw
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